vue-mapbox icon indicating copy to clipboard operation
vue-mapbox copied to clipboard

Way to add hover popup to a geoJSON layer element

Open aarepuu opened this issue 6 years ago • 3 comments

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:

screen shot 2018-12-09 at 15 51 11

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

aarepuu avatar Dec 09 '18 16:12 aarepuu

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 avatar Feb 08 '19 13:02 luizotcarvalho

@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! =)

soal avatar Feb 08 '19 17:02 soal

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

aarepuu avatar Feb 12 '19 14:02 aarepuu