Add V1 selected area map overlay
This commit is contained in:
@@ -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} />
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user