Working with multiple deck.gl layers
You can render multiple deck.gl layers on the MapGL JS API map simultaneously and manage them:
- Add layers to the map and remove them.
- Change the order of rendering layers and display layers so that they do not overlap each other. To change the layer order, specify the layer index in the array.
- Show and hide layers.
To work with multiple layers in a React project:
- Configure the map provider.
- Create the Deck provider.
- Initialize the providers.
- Create a context for layer management.
1. Configure map provider
-
Initialize the MapGL JS API map following the Working with React instruction.
-
Before adding a layer to the map, the map style must be loaded. Otherwise, it overwrites the previous style and removes the added layers. Use the
styleloadevent handler, which is emitted every time after a new style is set: for more information, see the Modifying current map style in runtime instruction. Add a style readiness flag to theMapProvider:const MapContext = React.createContext({
mapInstance: undefined,
setMapInstance: () => {},
styleLoaded: false,
});
const MapProvider = (props) => {
const [mapInstance, setMapInstance] = React.useState(undefined);
// Adding a style readiness flag
const [styleLoaded, setStyleLoaded] = React.useState(false);
// Subscribing to the style readiness event on map load
React.useEffect(() => {
if (!mapInstance) {
return;
}
// Callback for handling the style readiness event
const onStyleHasLoaded = () => {
setStyleLoaded(true);
};
mapInstance.on('styleload', onStyleHasLoaded);
return () => {
mapInstance.off('styleload', onStyleHasLoaded);
};
}, [mapInstance]);
// Adding the styleLoaded flag to the context
return (
<MapContext.Provider value={{ mapInstance, setMapInstance, styleLoaded }}>
{props.children}
</MapContext.Provider>
);
};
To improve performance, you can move the style loading flag to a separate context.
2. Create Deck provider
Create a separate provider for the Deck instance so that you can access it and manage layers from anywhere in the application:
const DeckContext = React.createContext({ deckInstance: undefined });
let deckGLInstance = undefined;
const DeckProvider: FC<PropsWithChildren> = ({ children }) => {
const [deckInstance, setDeckInstance] = React.useState(undefined);
useMapEffect(({ mapInstance }) => {
if (deckGLInstance) {
return;
}
// Initializing Deck when the map loads
const deckGLInstance = Deck2gisLayer.initDeck(mapInstance, DeckGL, { antialiasing: 'msaa' });
deckGLInstance = deckGLInstance;
setDeckInstance(deckGLInstance);
}, []);
return <DeckContext.Provider value={{ deckInstance }}>{children}</DeckContext.Provider>;
};
3. Initialize providers
Initialize the providers in the root component of the application:
const App = () => {
return (
<MapProvider>
<DeckProvider>
{/* The rest of the application */}
</DeckProvider>
</MapProvider>
);
};
The DeckProvider is nested inside the MapProvider so that the Deck instance has access to the map.
4. Create context for layer management
To manage deck.gl layers, create a context that stores the list of layers and provides functions to manage them.
The algorithm of layer management consists of the following steps:
- Adding layers to the context with the specified rendering order.
- Adding layers to the map after the style is loaded.
- Updating the set of layers using the
addLayer()andremoveLayer()functions. - Removing the previous set of layers from the map (cleanup in
useMapEffect) and rendering the new set. The layer instance is not removed and can be reused. - Repeating the cycle on each change to the set of layers or their order.
Context example:
const deck2GisLayerContext = React.createContext({
addLayer: () => {},
removeLayer: () => {},
});
const Deck2gisLayerContextProvider = ({ children }) => {
// The array stores each layer and its index that determines the rendering order
// A layer with a higher index is rendered above a layer with a lower index
const [deck2gisLayers, setDeck2gisLayers] = React.useState([]);
const addLayer = React.useCallback((deck2gisLayer, layerIndex) => {
// The layer array is sorted by index
setDeck2gisLayers((layers) =>
[...layers, { layerIndex, layer: deck2gisLayer }].sort(
(a, b) => b.layerIndex - a.layerIndex,
),
);
}, []);
const removeLayer = React.useCallback((layer) => {
setDeck2gisLayers((layers) => layers.filter((l) => l.layer !== layer));
}, []);
const { styleLoaded } = useMapContext();
useMapEffect(
({ mapInstance }) => {
if (!styleLoaded) {
return;
}
deck2gisLayers.forEach(({ layer }) => {
if (!mapInstance.hasLayer(layer.id)) {
mapInstance.addLayer(layer);
}
});
return () => {
deck2gisLayers.forEach(({ layer }) => {
mapInstance.removeLayer(layer.id);
});
};
},
[deck2gisLayers, styleLoaded],
);
const value = React.useMemo(() => ({ addLayer, removeLayer }), [addLayer, removeLayer]);
return <deck2GisLayerContext.Provider value={value}>{children}</deck2GisLayerContext.Provider>;
};
Usage example
The example includes additional code that simulates interaction with the backend of the application. The way you obtain data for layers depends on your implementation.