vue-mapbox
vue-mapbox copied to clipboard
Way to add hover popup to a geoJSON layer element
Looking for a way to add popup without any specific coordinates and change coordinates on hovering an element on a map. Trying to achieve something similar as this Mapbox example.
Haven't found a good way to do it using the mgl-popup component yet, here's what I have:
<template lang="pug">
.continer.map-wrapper
mgl-map.map-container(
:accessToken="accessToken",
:mapStyle.sync="mapStyle",
@load="mapLoaded")
mgl-navigation-control(position="top-right")
mgl-geojson-layer(
type="circle",
:sourceId="geoJsonSource.properties.id"
:layerId="geoJsonSource.properties.id"
:source.sync="geoJsonSource.data",
:paint="paint")
mgl-popup(
ref="popup",
:coordinates="popupCoordinates",
:closeButton="closeButton",
:closeOnClick="closeOnClick",
anchor="bottom")
.map-popup-content {{ popupContent }}
</template>
<script>
import {
MglMap,
MglNavigationControl,
MglGeojsonLayer,
MglPopup
} from 'vue-mapbox';
export default {
name: 'ClusterMap',
components: {
MglMap,
MglNavigationControl,
MglGeojsonLayer,
},
data: () => ({
accessToken: MAP_ACCESS_TOKEN,
mapStyle: MAP_STYLE,
popupCoordinates: [0,0],
popupContent: null,
closeButton: false,
closeOnClick: false,
geoJsonSource: {
properties: { id: 'geopoints' },
data: { //...some GeoJSON object }
}
paint: {
'circle-color': '#11b4da',
'circle-radius': 4,
'circle-stroke-width': 1,
'circle-stroke-color': '#fff',
},
}),
methods: {
mapLoaded(e) {
e.map.on('mouseenter', 'geopoints', (event) => {
e.map.getCanvas().style.cursor = 'pointer';
const coordinates = event.features[0].geometry.coordinates.slice();
const content = event.features[0].properties.name;
// Ensure that if the map is zoomed out such that multiple
// copies of the feature are visible, the popup appears
// over the copy being pointed to.
while (Math.abs(event.lngLat.lng - coordinates[0]) > 180) {
coordinates[0] += event.lngLat.lng > coordinates[0] ? 360 : -360;
}
this.popupCoordinates = coordinates;
this.popupContent = content;
this.$refs.popup.popup.addTo(e.map); // not the best way to do this
});
e.map.on('mouseleave', 'geopoints', () => {
e.map.getCanvas().style.cursor = '';
this.$refs.popup.popup.remove(); // not the best way to do this
});
},
},
};
</script>
<style lang="sass" scoped>
.map-wrapper
width: 100%
height: 100%
.map-container
width: 100%
height: 600px
</style>
Will produce:

Also having a similar issue to this one https://github.com/soal/vue-mapbox/issues/55. And for some reason two popup elements are added to the map.
<div data-v-75ee5279="" id="map-6544415584706778" class="map-container mapboxgl-map">
....
<div class="mapboxgl-popup mapboxgl-popup-anchor-bottom"
style="transform: translate(-50%, -100%) translate(722px, 360px);">
<div class="mapboxgl-popup-tip"></div>
<div class="mapboxgl-popup-content"></div>
</div>
<div class="mapboxgl-popup mapboxgl-popup-anchor-bottom"
style="transform: translate(-50%, -100%) translate(1062px, 345px);">
<div class="mapboxgl-popup-tip"></div>
<div class="mapboxgl-popup-content">
<div data-v-75ee5279="" class="map-popup-content">Test Place</div>
</div>
</div>
</div>
Any ideas? Thanks
I'm having the same issue, debugging a little I find out that the method $_deferredMount on Popup.vue is called twice, creating two Popups in DOM, but when it is destroyed the remove method just remove one popup element.
I see two workarounds to fix this issue:
1 - add a parent class to map element when the popup is open and a css to hide all popups when this class is not present.
2 - manually remove all mapboxgl-popup elements with javascript on mouseleave
@luizotcarvalho Thank you for the info.
Version 0.2 is coming at the end of the week. Component registration mechanism will be changed completely, $_deferredMount will be removed and I think this issue will be fixed. Stay tuned! =)
I did something similar to @luizotcarvalho recommendations. Here's how I do it:
/* my GeoPopup component */
<template lang="pug">
mgl-popup(
ref="popup",
:coordinates="getCoordinates",
:closeButton="closeButton",
:closeOnClick="closeOnClick")
.map-popup-content.has-text-centered
strong {{ content}}
</template>
<script>
import { MglPopup } from 'vue-mapbox';
export default {
name: 'GeoPopup',
components: {
MglPopup,
},
props: {
coordinates: {
type: Array,
default: undefined, /* this will not initialise the popup when you add it */
},
content: {
type: Object,
default() {
return {};
},
},
show: {
type: Boolean,
default: true,
},
},
data: () => ({
popupContent: null,
closeButton: false,
closeOnClick: false,
}),
watch: {
show() {
if (!this.show) {
if (this.$refs.popup) { this.$refs.popup.popup.remove(); }
} else if (this.$refs.popup) { this.$refs.popup.popup.addTo(this.$store.map); }
},
},
computed: {
getCoordinates() {
// Here we can do stuff with the coordinates if needed
// ...
return this.coordinates;
},
},
methods: {
},
};
</script>
<style scoped>
</style>
And then I use the show parameter to show the popup or remove it from map. Not the best solution, but works for now. Looking forward to the new release! Cheers