Vladimir Atanasov
Vladimir Atanasov

Reputation: 189

Mapbox style changes/breaks on zoom when a layer is added

I have a mapbox map, initialized with the outdoors-v9 style (tried other styles, same behavior). When I add a layer to the map - a marker or a geojson source and zoom the map, the style changes or breaks, I'm not sure which.

This is the map before the zoom enter image description here

and after the zoom enter image description here

here are the functions that init the map and add markers

mapboxgl.accessToken = "pk.*******";


buildMap: function() {
  const _self = this;
  _self.map = new mapboxgl.Map({
    container: "map",
    style: "mapbox://styles/mapbox/outdoors-v9",
    center: [-95.712891, 37.09024],
    zoom: 3
  });

  _self.map.on('load', function() {
    _self.map.addSource('route', {
      'type': 'geojson',
      'data': {
            "type": "FeatureCollection",
            "features": []
      } 
    });

    _self.map.addLayer({
        'id': 'route',
        'source': 'route',
        'type': 'line',
        'layout': {
          'line-join': 'round',
          'line-cap': 'round'
        },
        'paint': {
          'line-color': '#47576A',
          'line-width': 3
        }
      });
  });
}

...

const coords = [addressData.longitude, addressData.latitude];

const marker = new mapboxgl.Marker().setLngLat(coords).addTo(this.map);

I am using Vue.js to render the map. Mapbox version v0.45.0

Any help or leads are highly appreciated

Upvotes: 4

Views: 2416

Answers (6)

cweitat
cweitat

Reputation: 1247

Short and quick answer.

Explanation is similar to @mlb's answer. So you freeze the object to prevent the map from disorientated and for any actions done to the map, call back the data with an extra Object key which in case is 'wrapper'.

<template><MglMap :accessToken="..." :mapStyle="..." @load="onMapLoaded" /></template>
    
<script>  
methods: {  
 onMapLoaded(event) {
   this.mapboxEvent = Object.freeze({wrapper: event.map});
 },
 panMap(event) {
   this.mapboxEvent.wrapper.panTo([lng, lat], {duration: 1000, zoom: 14});
 }
}
</script>

Upvotes: 0

mlb
mlb

Reputation: 681

After hours spent on this problem, here is my working solution to access map instance from a store (thanks to https://github.com/vuejs/vue/issues/2637#issuecomment-331913620):

const state = reactive({
  map: Object.freeze({ wrapper: /* PUT THE MAP INSTANCE HERE */ });
});

Here is an example with Vue Composition Api:

index.js

import { reactive, computed } from "@vue/composition-api";

export const state = reactive({
  map: null
});

export const setMap = (map) => {
  state.map = Object.freeze({ wrapper: map});
};

export const getMap = computed(() => state.map.wrapper);

export const initMap = (event) => {
  setMap(event.map);

  // now you can access to map instance from the "getMap" getter!
  getMap.value.addSource("satellite-source", {
    type: "raster",
    url: "mapbox://mapbox.satellite",
  }); 
  getMap.value.addLayer({
    id: "satellite-layer",
    type: "raster",
    source: "satellite-source"
  });
};

App.vue

<template>
  <MglMap :accessToken="..." :mapStyle="..." @load="onMapLoaded" />
</template>


<script>
import { defineComponent } from "@vue/composition-api";
import { MglMap } from "vue-mapbox";
import { initMap } from "./index.js";

export default defineComponent({
  components: {
    MglMap
  },
  setup() {
    const onMapLoaded = (event) => {
      initMap(event);
    }

    return { onMapLoaded };
  }
});
</script>

Upvotes: 1

Vinayak Kulkarni
Vinayak Kulkarni

Reputation: 291

Vue data() properties are reactive, they have getters and setters, so, when loading map object or adding vector tiles layer (geojson), Vue tries to add getters & setters to the map & map.layers which causes vue & vue-dev-tools to crash and mess up the map.

If you enable any raster layer, it would work successfully because raster tiles are loaded via the mapbox.css whereas vector tiles being geojson, are added to the map object.

Easiest solution would be to define a non-reactive variable in vue and then re-use it everywhere.

// edit: A correct/recommended way to set non-reactive data: GitHub link

Upvotes: 3

Bruno Fa&#250;ndez
Bruno Fa&#250;ndez

Reputation: 126

I just faced this issue and realized that I didn't follow the documentation exactly as it was described (jumped right on to coding without reading properly). And the documentation says:

Storing Map object

Take note that it's generally bad idea to add to Vuex or component's data anything but primitive types and plain objects. Vue adds getters and setters to every property, so if you add Map object to Vuex store or component data, it may lead to weird bugs. If you want to store map object, store it as non-reactive property like in example below.

The problem was that I had also registered "map" inside the "data" object of my Vue component. But in the example code it's not declared in data, only in the "create" function.

https://soal.github.io/vue-mapbox/guide/basemap.html#map-loading

Upvotes: 1

Jakub Juszczak
Jakub Juszczak

Reputation: 7827

I've got the same error. This happens if you either put the map or the marker on an reactive vue.js instance.

Upvotes: 0

Vladimir Atanasov
Vladimir Atanasov

Reputation: 189

Seems the issue was related with the fact that I'm pushing the marker instance to an observable (a vuejs data field). After pushing the marker instance to an array, the issue disappeared. This comment doesn't really answer why this happens, but hope it helps someone else that might face the same issue

Upvotes: 1

Related Questions