Map Provider
This example showcases a context provider and hook to manage multiple MapLibre GL map instances.
Mounted maps:
We will need a plugin to manage map synchronization. The only plugin I could find was mapbox-gl-sync-move
however its a common js library that lacks type declarations. There exists a typed instance of the syncMaps
definition in the issues which can be defined as shown below:
import * as maplibregl from 'maplibre-gl'/** * Sync movements of two maps. * * All interactions that result in movement end up firing * a "move" event. The trick here, though, is to * ensure that movements don't cycle from one map * to the other and back again, because such a cycle * - could cause an infinite loop * - prematurely halts prolonged movements like * double-click zooming, box-zooming, and flying */export default function syncMaps(...maps: maplibregl.Map[]) { // Create all the movement functions, because if they're created every time // they wouldn't be the same and couldn't be removed. let fns: Parameters<maplibregl.Map["on"]>[1][] = []; maps.forEach((map, index) => { // When one map moves, we turn off the movement listeners // on all the maps, move it, then turn the listeners on again fns[index] = () => { off();
const center = map.getCenter(); const zoom = map.getZoom(); const bearing = map.getBearing(); const pitch = map.getPitch();
const clones = maps.filter((o, i) => i !== index); clones.forEach((clone) => { clone.jumpTo({ center: center, zoom: zoom, bearing: bearing, pitch: pitch, }); });
on(); }; });
const on = () => { maps.forEach((map, index) => { map.on("move", fns[index]); }); };
const off = () => { maps.forEach((map, index) => { map.off("move", fns[index]); }); };
on();
return () => { off(); fns = []; maps = []; };}
Save this file as sync.ts
and use it to sync you’re maps.
import { createEffect, createMemo, createSignal, type Component, type JSX } from "solid-js";import { Maplibre, MapsProvider, useMaps } from "solidjs-maplibre-gl";import "maplibre-gl/dist/maplibre-gl.css";import syncMaps from './sync'
const MapCProvider: Component = (props) => { const [maplist, setMapList] = createSignal<maplibregl.Map[]>([]) createEffect(() => { syncMaps(...maplist()); }) return ( <div style={{position: 'relative', height: '100%', "min-height": '500px'}}> <MapsProvider> <MapsProbe /> <Maplibre style={{ position: 'absolute', width: '50%', height: '100%', }} options={{ zoom: 12, center: [-122.43, 37.78], pitch: 30, style:'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json' }} onidle={(e) => { setMapList((m) => { if (m.includes(e.target)) { return m } else { return [...m, e.target] }
}) }} /> <Maplibre style={{ position: 'absolute', left: '50%', width: '50%', height: '100%' }} options={{ zoom: 12, center: [-122.43, 37.78], pitch: 30, style:'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json' }} onidle={(e) => { setMapList((m) => { if (m.includes(e.target)) { return m } else { return [...m, e.target] }
}) }} /> </MapsProvider> </div> );};
export default MapCProvider;
const MapsProbe = () => { const keys = createMemo(() => [...(useMaps()?.maps().keys() ?? [])].join(", "), ); return <p style={mounted}>Mounted maps: <span style={{"font-family":'sans-serif'}}>{keys()}</span></p>;};
const mounted:JSX.CSSProperties = { 'padding': '0.7rem 1rem', background: '#3d3d3dff', "border-top-right-radius": '16px'}