Changing map-style causes layers to disappear
Here is an example of the problem (click "Change Style" button): https://codesandbox.io/p/sandbox/3hm8nm (it's using a test api key). The red route line will disappear. Along with this console warning:
Unable to perform style diff: Unimplemented: setSprite.. Rebuilding the style from scratch.
One would expect that changing the map style would not affect anything except the map style. This seems to be a problem with the underlying behaviour of the mapbox API. However one would expect that the Vue component should be able to handle this occurrance. If one has to manually load the layers back onto the map every time the style is changed (using the map instance directly) then there is really no point to having the <MapboxLayer> component at all.
<script setup>
import { ref } from "vue";
import {
MapboxMap,
MapboxSource,
MapboxLayer,
} from "@studiometa/vue-mapbox-gl";
import "mapbox-gl/dist/mapbox-gl.css";
const mapCenter = ref([-1.159535, 51.08822]);
const mapStyle = ref("mapbox://styles/mapbox/streets-v11");
const Change = () => {
console.log(mapStyle.value);
if (mapStyle.value === "mapbox://styles/mapbox/streets-v11")
mapStyle.value = "mapbox://styles/mapbox/satellite-v9";
else mapStyle.value = "mapbox://styles/mapbox/streets-v11";
};
const route = ref({
id: "route",
type: "geojson",
data: {
type: "Feature",
properties: {},
geometry: {
type: "LineString",
coordinates: [
[-1.158535, 51.08822],
[-1.158546, 51.08838],
[-1.1585, 51.08878],
[-1.158, 51.08978],
],
},
},
});
const layer = ref({
id: "layer",
type: "line",
source: "route",
layout: {
"line-join": "round",
"line-cap": "round",
},
paint: {
"line-color": "#F00",
"line-width": 8,
},
});
</script>
<template>
<div>Demo Map</div>
<div><button @click="Change">Change Style</button></div>
<MapboxMap
style="height: 400px"
access-token="..."
:map-style="mapStyle"
:center="mapCenter"
:zoom="15"
>
<MapboxSource id="route" :options="route" />
<MapboxLayer id="layer" :options="layer" />
</MapboxMap>
</template>
Note that one "fix" i've found for is to add mapStyle as a key for MapboxMap for example:
<template>
<div>Demo Map</div>
<div><button @click="Change">Change Style</button></div>
<MapboxMap
:key="mapStyle"
style="height: 400px"
access-token="..."
:map-style="mapStyle"
:center="mapCenter"
:zoom="15"
>
<MapboxSource id="route" :options="route" />
<MapboxLayer id="layer" :options="layer" />
</MapboxMap>
</template>
It is a "known bug" of Mapbox, when the style of a map is changed, any custom layer needs to be readded to the map. See https://github.com/mapbox/mapbox-gl-js/issues/10534 and https://github.com/mapbox/mapbox-gl-js/issues/8326 and this paragraph in the documentation:
Switch styles at runtime
You can also change the style any time after initializing the map using the Map setStyle method. If you added any layers at runtime, you will need to re-add layers after the new style is loaded.
I had a quick look at how this could be addressed in the components directly, but it is not really simple as the setStyle() method is not async while the change of style is. We could use the styledata event to re-add the layers after the style change is finished, but for now it does not seem reliable enough to have a single and simple solution.
Your workaround is the simplest for now.
I wonder if it would be easier to get rid of the MapboxSource and MapboxLayer components completely and just feed that data MapboxMap via props. Then it would easier to redraw all sources layers when the map was changed.