Add V1 selected area map overlay
GeoIntel CI / docs-smoke (push) Has been cancelled
GeoIntel CI / contract-smoke (push) Has been cancelled

This commit is contained in:
Codex
2026-06-17 00:21:01 +02:00
parent 97be9d9293
commit 47cd2c6c1e
12 changed files with 315 additions and 21 deletions
+93 -3
View File
@@ -4,8 +4,11 @@ import 'maplibre-gl/dist/maplibre-gl.css'
interface GeoMapProps {
data: GeoJSON.FeatureCollection | null
areaData?: GeoJSON.FeatureCollection | null
visible?: boolean
opacity?: number
areaVisible?: boolean
areaOpacity?: number
onFeatureSelect?: (feature: GeoJSON.Feature | null) => void
}
@@ -43,7 +46,20 @@ function collectCoordinates(featureCollection: GeoJSON.FeatureCollection): mapli
]
}
function GeoMap({ data, visible = true, opacity = 0.4, onFeatureSelect }: GeoMapProps): JSX.Element {
function mergeFeatureCollections(collections: Array<GeoJSON.FeatureCollection | null | undefined>): GeoJSON.FeatureCollection | null {
const features = collections.flatMap((collection) => collection?.features ?? [])
return features.length > 0 ? { type: 'FeatureCollection', features } : null
}
function GeoMap({
data,
areaData = null,
visible = true,
opacity = 0.4,
areaVisible = true,
areaOpacity = 0.18,
onFeatureSelect,
}: GeoMapProps): JSX.Element {
const containerRef = useRef<HTMLDivElement | null>(null)
const mapRef = useRef<maplibregl.Map | null>(null)
const onFeatureSelectRef = useRef<GeoMapProps['onFeatureSelect']>(onFeatureSelect)
@@ -65,12 +81,13 @@ function GeoMap({ data, visible = true, opacity = 0.4, onFeatureSelect }: GeoMap
})
map.addControl(new maplibregl.NavigationControl(), 'top-right')
map.on('click', (event) => {
if (!map.getLayer('dataset-fill') || !map.getLayer('dataset-line')) {
const layers = ['dataset-fill', 'dataset-line', 'area-fill', 'area-line'].filter((layerId) => map.getLayer(layerId))
if (layers.length === 0) {
onFeatureSelectRef.current?.(null)
return
}
const features = map.queryRenderedFeatures(event.point, {
layers: ['dataset-fill', 'dataset-line'],
layers,
})
if (features.length === 0) {
onFeatureSelectRef.current?.(null)
@@ -159,6 +176,63 @@ function GeoMap({ data, visible = true, opacity = 0.4, onFeatureSelect }: GeoMap
}
}, [data])
useEffect(() => {
const map = mapRef.current
if (!map) {
return
}
if (map.getSource('area')) {
if (areaData) {
;(map.getSource('area') as maplibregl.GeoJSONSource).setData(areaData)
} else {
if (map.getLayer('area-fill')) {
map.removeLayer('area-fill')
}
if (map.getLayer('area-line')) {
map.removeLayer('area-line')
}
map.removeSource('area')
return
}
} else if (areaData) {
map.addSource('area', { type: 'geojson', data: areaData })
map.addLayer(
{
id: 'area-fill',
type: 'fill',
source: 'area',
paint: {
'fill-color': '#0f766e',
'fill-opacity': 0.18,
},
},
map.getLayer('dataset-fill') ? 'dataset-fill' : undefined,
)
map.addLayer(
{
id: 'area-line',
type: 'line',
source: 'area',
paint: {
'line-color': '#0f766e',
'line-width': 3,
'line-dasharray': [2, 1],
},
},
map.getLayer('dataset-line') ? 'dataset-line' : undefined,
)
}
const activeCollection = mergeFeatureCollections([areaData, data])
if (activeCollection) {
const bounds = collectCoordinates(activeCollection)
if (bounds) {
map.fitBounds(bounds, { padding: 40 })
}
}
}, [areaData, data])
useEffect(() => {
const map = mapRef.current
if (!map) {
@@ -175,6 +249,22 @@ function GeoMap({ data, visible = true, opacity = 0.4, onFeatureSelect }: GeoMap
}
}, [visible, opacity, data])
useEffect(() => {
const map = mapRef.current
if (!map) {
return
}
const visibility = areaVisible ? 'visible' : 'none'
if (map.getLayer('area-fill')) {
map.setLayoutProperty('area-fill', 'visibility', visibility)
map.setPaintProperty('area-fill', 'fill-opacity', areaOpacity)
}
if (map.getLayer('area-line')) {
map.setLayoutProperty('area-line', 'visibility', visibility)
map.setPaintProperty('area-line', 'line-opacity', areaVisible ? 1 : 0)
}
}, [areaVisible, areaOpacity, areaData])
return <div className="map-container" ref={containerRef} />
}