import { useEffect, useRef, useState } from 'react' import type { Map } from 'maplibre-gl' import { useMapLibreMap } from '../maplibre/maplibreContext' import { safeRemoveLayers } from '../maplibre/geoUtils' import styles from './OverlayControls.module.css' interface OverlayDef { id: string label: string tiles: string[] tileSize: 256 | 512 opacity: number } const OVERLAYS: OverlayDef[] = [ { id: 'streets', label: 'Streets', tiles: ['https://server.arcgisonline.com/ArcGIS/rest/services/Reference/World_Transportation/MapServer/tile/{z}/{y}/{x}'], tileSize: 256, opacity: 0.75, }, { id: 'labels', label: 'City names', tiles: ['https://a.basemaps.cartocdn.com/rastertiles/voyager_only_labels/{z}/{x}/{y}.png'], tileSize: 256, opacity: 1.0, }, { id: 'borders', label: 'Borders', tiles: ['https://server.arcgisonline.com/ArcGIS/rest/services/Reference/World_Boundaries_and_Places/MapServer/tile/{z}/{y}/{x}'], tileSize: 256, opacity: 0.85, }, ] function addOverlay(map: Map, overlay: OverlayDef) { const srcId = `overlay-src-${overlay.id}` const lyrId = `overlay-lyr-${overlay.id}` map.addSource(srcId, { type: 'raster', tiles: overlay.tiles, tileSize: overlay.tileSize }) map.addLayer({ id: lyrId, type: 'raster', source: srcId, paint: { 'raster-opacity': overlay.opacity } }) } function removeOverlay(map: Map, overlay: OverlayDef) { safeRemoveLayers(map, [`overlay-lyr-${overlay.id}`], [`overlay-src-${overlay.id}`], ) } export function OverlayControls() { const map = useMapLibreMap() const [active, setActive] = useState>(new Set()) const activeRef = useRef(active) activeRef.current = active function toggle(overlay: OverlayDef) { setActive((prev) => { const next = new Set(prev) if (next.has(overlay.id)) { removeOverlay(map, overlay) next.delete(overlay.id) } else { addOverlay(map, overlay) next.add(overlay.id) } return next }) } // Clean up all active overlays on unmount useEffect(() => { return () => { for (const id of activeRef.current) { const overlay = OVERLAYS.find(o => o.id === id) if (overlay) removeOverlay(map, overlay) } } }, [map]) return (
{OVERLAYS.map((o) => ( ))}
) }