1. Introduction

Because the project needs, need to mapbox map theme style switch. Using the setStyle method, all dependencies and layers on the map are cleared. I was dumbfounded 😮.

Reason 2.

When the original setStyle method is updated, it will not keep the original dependencies and layers, and will render with the latest map style.

3. Solutions

First edition scheme:

Steps:

  1. Record existing rendered dependencies and layers in the map;
  2. Monitor map style switch;
  3. Map style switch;
  4. When the map theme style is updated, trigger the map style loading completion callback;
  5. Manually reload the original dependencies and layers into the map instance.

But, you think this is the end? No, there are many more pits 😭.

Step 1:

The styleData event does not work at all. It does not monitor when the style switch is loaded. It has many triggers and cannot monitor the style alone. No way, I searched and searched and finally found an undisclosed event style.load. Mapbox-gl “: “^2.2.0

While style.load events are not officially recommended, hopefully future versions of Mapbox-GL will make this easier with promise or async methods. When you use, pay attention to the version.

On hole 2:

If you use the setStyle method for the same map style repeatedly, the map instance will die, causing all the layers on the map to be cleared. Method, you need to cache the incoming map style and determine whether the current incoming map style is different from the last one. If the operation is not equal, continue the operation. If the operation is equal, close and return. Do not continue the operation.

Therefore, based on the above problems, our final solution is as follows:

Second edition programme:

Steps:

  1. Closure processing, the incoming map style cache;
  2. Record existing rendered dependencies and layers in the map;
  3. Monitor map style toggles and bind map style load eventsstyle.load;
  4. Map style switch;
  5. Compare the passed style to the last style. If the style is the same, the function returns. If the style is different, the operation continues.
  6. When the map theme style is updated, trigger the map style loading completion callback;
  7. Manually reload the original dependencies and layers into the map instance.
  8. Uninstall the map style update eventstyle.loadTo prevent pollution;

The following code (based on typescript) :

import deepEqual from 'deep-equal' interface ISetMapStyle { map: mapboxgl.Map style: string | mapboxgl.Style layersList: string[] sourcesList: string[] } const setMapStyleFn = function (defaultStyle: string | mapboxgl.Style) { let prevStyle = defaultStyle return (props: ISetMapStyle) => { const { map, style, sourcesList, layersList } = props if (! map || ! style || ! layersList || ! sourcesList) { return } if (deepEqual(prevStyle, style)) { return } prevStyle = style const mapStyle = map.getStyle(); if (! mapStyle) { return } const layers = (mapStyle.layers || []).filter((layer) => layersList.includes(layer.id)); const sources = Object.keys((mapStyle.sources || {})).filter((key) => { return sourcesList.includes(key) }).reduce((prev, result) => { prev[result] = (mapStyle.sources || {})[result]; return prev; }, {}); const handleStyle = () => { Object.keys(sources).forEach((key) => { const existing = map.getSource(key); if (! existing) { map.addSource(key, sources[key]); }}); layers.forEach((layer) => { const existing = map.getLayer(layer.id); if (! existing) { map.addLayer(layer) } }); map.off('style.load', handleStyle); } map.on('style.load', handleStyle); map.setStyle(style, { diff: true }); } } export default setMapStyleFnCopy the code

Use 4.

Here’s an example of pseudocode based on react:

import setMapStyleFn from './index' const map = mapServer; Const style = 'mapbox://styles/ XXXXXXX 'const style1 = 'mapbox://styles/xxxxxxx2' const style1 = 'mapbox://styles/xxxxxxx2 setMapStyle = setMapStyleFn(style) const onClick = () => { setMapStyle({ map, style: style1, layersList: ['map-fill-layer', 'map-circle-layer'], sourcesList: ['map-fill-source', 'map-circle-source'],})} <button onClick={onClick}> </button>Copy the code