<template>
<div id="popup" class="ol-map-popup max-width">
<a href="#" id="popup-closer" class="ol-map-popup-closer"></a>
<div id="popup-content">
<div class="text-weight-bold" v-html="title ?? $t('Unnamed feature')" />
<div v-if="featureProperties">
<div v-for="o of featureProperties" :key="o.key">
<div v-html="`${o.key} : ${o.value ?? ''}`" />
</div>
</div>
<div>
<q-btn flat dense @click="showHistory">{{ $t('History') }}</q-btn>
</div>
<div v-if="hasFeatureStatsButton">
<q-btn flat dense @click="showFeatureStats">{{ $t('Statistics') }}</q-btn>
</div>
<a v-if="options && options.api2Link" @click="$emit('children', feature)">{{ options.api2Link }}</a>
</div>
</div>
</template>
<script>
/**
* Popup component for displaying properties of a map feature.
*
* @component
* @name OlMapPopup
* @example
* <OlMapPopup />
*/
import Overlay from "ol/Overlay"
export default {
name: "OlMapPopup",
props: {
options: { type: Object },
},
data() {
return {
title: null,
map: null,
overlay: null,
container: null,
feature: null,
featureProperties: null,
parent: null
}
},
/**
* Initializes the popup.
*/
async mounted() {
await this.$nextTick();
let closer = document.getElementById('popup-closer');
this.container = document.getElementById('popup');
closer.onclick = (e) => {
this.hide();
closer.blur();
return false;
};
},
methods: {
showHistory() {
this.parent.showHistory();
},
showFeatureStats() {
this.parent.showFeatureStats();
},
/**
* Checks should we display feature stats button
*
* @returns {boolean} True if the feature has a history button, false otherwise.
*/
hasFeatureStatsButton() {
return this.parent && this.parent.selectedTitle && this.feature.get("custom_geometry_id") && this.feature.getGeometry().getType() != "Point";
},
/**
* Hides the popup.
*/
hide() {
if (this.overlay) this.overlay.setPosition(undefined);
this.$mitt.off('escKeyPressed');
},
/**
* Shows the popup.
*
* @param {Object} map - The map to show the popup on.
* @param {string} title - The title of the popup.
* @param {Array} coordinates - The coordinates of the popup.
* @param {Object} feature - The feature to show the popup for.
* @param {Object} additionalProps - Additional properties to show in the popup.
*/
show(map, title, coordinates, feature, parent, additionalProps) {
this.$mitt.on('escKeyPressed', () => {
this.hide();
});
this.title = title;
this.map = map;
this.feature = feature;
this.featureProperties = [];
this.parent = parent;
let skipProperty = ["color_code", "geometry", "geometry_type_id", "Name", "name"];
// workaround: feature.getProperties() does not work in v-for
for (let key in this.feature.getProperties()) {
// if key is not in skipProperty
if (!skipProperty.find(x => x == key)) {
let value = this.feature.get(key);
if (value instanceof Array) {
for (let v of value) {
if (v.name && v.name.toLowerCase() == "name") {
this.title = v.value;
} else if (v.name != null) {
let prop = this.$store.catalogs.properties.find(x => x.id == v.property_id);
if (prop == null || !prop.indicator_id || prop.numerical) {
this.featureProperties.push({ key: v.name, value: this.formatValue(v.value) });
} else {
this.featureProperties.push({ key: v.name, value: this.$store.catalogs.descriptions.find(x => x.indicator_id == prop.indicator_id && x.value == v.value).label });
}
}
}
} else if (value != null) {
this.featureProperties.push({ key: key, value: this.formatValue(value) });
}
}
}
for (let key in additionalProps) {
this.featureProperties.push({ key: key, value: additionalProps[key] });
}
if (!this.overlay) {
this.overlay = new Overlay({
zIndex: 9999,
element: this.container,
autoPan: {
animation: {
duration: 250,
},
},
stopEvent: true,
className: 'ol-map-popup',
});
this.map.addOverlay(this.overlay);
this.container.style.visibility = 'visible';
this.overlay.getElement().addEventListener('click', (event) => {
event.stopPropagation();
});
this.overlay.getElement().addEventListener('contextmenu', (event) => {
event.stopPropagation();
});
}
this.overlay.setPosition(coordinates);
this.$store.menuDisabled = true;
}
}
}
</script>
<style scoped>
.ol-map-popup {
position: absolute;
background-color: white;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
/* padding: 15px; */
border-radius: 5px;
border: 1px solid #cccccc;
bottom: 12px;
left: -50px;
padding: 5px 25px 5px 5px;
visibility: hidden;
white-space: nowrap;
max-width: 300px;
}
.ol-map-popup:after,
.ol-map-popup:before {
top: 100%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
.ol-map-popup:after {
border-top-color: white;
border-width: 10px;
left: 48px;
margin-left: -10px;
}
.ol-map-popup:before {
border-top-color: #cccccc;
border-width: 11px;
left: 48px;
margin-left: -11px;
}
.ol-map-popup-closer {
text-decoration: none;
position: absolute;
top: 2px;
right: 5px;
z-index: 999;
}
.ol-map-popup-closer:after {
content: "✖";
}
</style>