feat: stream municipality vectors by viewport
This commit is contained in:
@@ -3,6 +3,7 @@ import maplibregl from 'maplibre-gl'
|
||||
import 'maplibre-gl/dist/maplibre-gl.css'
|
||||
import { PRIMARY_FOCUS_CENTER } from '../config/primaryFocus'
|
||||
import { featureCollectionBounds } from '../lib/geojsonBounds'
|
||||
import type { MapViewportState } from '../types'
|
||||
|
||||
interface GeoMapProps {
|
||||
data: GeoJSON.FeatureCollection | null
|
||||
@@ -16,8 +17,10 @@ interface GeoMapProps {
|
||||
opacity?: number
|
||||
areaVisible?: boolean
|
||||
areaOpacity?: number
|
||||
fitDataOnChange?: boolean
|
||||
onFeatureSelect?: (feature: GeoJSON.Feature | null) => void
|
||||
onMapCoordinateSelect?: (coordinate: [number, number]) => void
|
||||
onViewportChange?: (viewport: MapViewportState) => void
|
||||
}
|
||||
|
||||
const EMPTY_FEATURE_COLLECTION: GeoJSON.FeatureCollection = {
|
||||
@@ -108,14 +111,18 @@ function GeoMap({
|
||||
opacity = 0.4,
|
||||
areaVisible = true,
|
||||
areaOpacity = 0.18,
|
||||
fitDataOnChange = true,
|
||||
onFeatureSelect,
|
||||
onMapCoordinateSelect,
|
||||
onViewportChange,
|
||||
}: GeoMapProps): JSX.Element {
|
||||
const containerRef = useRef<HTMLDivElement | null>(null)
|
||||
const mapRef = useRef<maplibregl.Map | null>(null)
|
||||
const onFeatureSelectRef = useRef<GeoMapProps['onFeatureSelect']>(onFeatureSelect)
|
||||
const onMapCoordinateSelectRef = useRef<GeoMapProps['onMapCoordinateSelect']>(onMapCoordinateSelect)
|
||||
const onViewportChangeRef = useRef<GeoMapProps['onViewportChange']>(onViewportChange)
|
||||
const bboxSelectionModeRef = useRef(bboxSelectionMode)
|
||||
const lastFittedAreaRef = useRef<GeoJSON.FeatureCollection | null>(null)
|
||||
const [mapStyleReady, setMapStyleReady] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -126,6 +133,10 @@ function GeoMap({
|
||||
onMapCoordinateSelectRef.current = onMapCoordinateSelect
|
||||
}, [onMapCoordinateSelect])
|
||||
|
||||
useEffect(() => {
|
||||
onViewportChangeRef.current = onViewportChange
|
||||
}, [onViewportChange])
|
||||
|
||||
useEffect(() => {
|
||||
bboxSelectionModeRef.current = bboxSelectionMode
|
||||
if (mapRef.current) {
|
||||
@@ -152,9 +163,24 @@ function GeoMap({
|
||||
}),
|
||||
'bottom-right',
|
||||
)
|
||||
const emitViewport = () => {
|
||||
const bounds = map.getBounds()
|
||||
onViewportChangeRef.current?.({
|
||||
bbox: {
|
||||
min_x: bounds.getWest(),
|
||||
min_y: bounds.getSouth(),
|
||||
max_x: bounds.getEast(),
|
||||
max_y: bounds.getNorth(),
|
||||
crs: 'EPSG:4326',
|
||||
},
|
||||
zoom: map.getZoom(),
|
||||
})
|
||||
}
|
||||
map.on('load', () => {
|
||||
setMapStyleReady(true)
|
||||
emitViewport()
|
||||
})
|
||||
map.on('moveend', emitViewport)
|
||||
map.on('click', (event) => {
|
||||
if (bboxSelectionModeRef.current) {
|
||||
onMapCoordinateSelectRef.current?.([event.lngLat.lng, event.lngLat.lat])
|
||||
@@ -270,7 +296,7 @@ function GeoMap({
|
||||
})
|
||||
}
|
||||
|
||||
if (data) {
|
||||
if (data && fitDataOnChange) {
|
||||
const collection = data
|
||||
if (collection.type === 'FeatureCollection' && collection.features.length > 0) {
|
||||
const bounds = collectCoordinates(collection)
|
||||
@@ -279,7 +305,7 @@ function GeoMap({
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [data, mapStyleReady])
|
||||
}, [data, fitDataOnChange, mapStyleReady])
|
||||
|
||||
useEffect(() => {
|
||||
const map = mapRef.current
|
||||
@@ -329,14 +355,16 @@ function GeoMap({
|
||||
)
|
||||
}
|
||||
|
||||
const activeCollection = mergeFeatureCollections([areaData, data])
|
||||
if (activeCollection) {
|
||||
const activeCollection = fitDataOnChange ? mergeFeatureCollections([areaData, data]) : areaData
|
||||
const shouldFitArea = fitDataOnChange || lastFittedAreaRef.current !== areaData
|
||||
if (activeCollection && shouldFitArea) {
|
||||
const bounds = collectCoordinates(activeCollection)
|
||||
if (bounds) {
|
||||
map.fitBounds(bounds, { padding: 40 })
|
||||
}
|
||||
}
|
||||
}, [areaData, data, mapStyleReady])
|
||||
lastFittedAreaRef.current = areaData
|
||||
}, [areaData, data, fitDataOnChange, mapStyleReady])
|
||||
|
||||
useEffect(() => {
|
||||
const map = mapRef.current
|
||||
|
||||
Reference in New Issue
Block a user