feat: stream municipality vectors by viewport
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import {
|
||||
VECTOR_VIEWPORT_DEBOUNCE_MS,
|
||||
VECTOR_VIEWPORT_FEATURE_LIMIT,
|
||||
VECTOR_VIEWPORT_FEATURE_THRESHOLD,
|
||||
VECTOR_VIEWPORT_MIN_ZOOM,
|
||||
} from '../config/vectorDelivery'
|
||||
import { formatError } from '../lib/formatError'
|
||||
import { datasetsApi } from '../services/api/datasets'
|
||||
import type { DatasetCreateResponse, MapViewportState } from '../types'
|
||||
|
||||
interface ViewportVectorLayerOptions {
|
||||
selectedProjectId: string | null
|
||||
selectedDataset: DatasetCreateResponse | null
|
||||
featureCount: number | null
|
||||
isVectorDatasetType: (datasetType: string) => boolean
|
||||
}
|
||||
|
||||
export function useViewportVectorLayer({
|
||||
selectedProjectId,
|
||||
selectedDataset,
|
||||
featureCount,
|
||||
isVectorDatasetType,
|
||||
}: ViewportVectorLayerOptions) {
|
||||
const [viewport, setViewport] = useState<MapViewportState | null>(null)
|
||||
const [data, setData] = useState<GeoJSON.FeatureCollection | null>(null)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [truncated, setTruncated] = useState(false)
|
||||
const [loadedFeatureCount, setLoadedFeatureCount] = useState(0)
|
||||
const requestSequence = useRef(0)
|
||||
|
||||
const enabled = Boolean(
|
||||
selectedProjectId &&
|
||||
selectedDataset &&
|
||||
isVectorDatasetType(selectedDataset.dataset_type) &&
|
||||
(featureCount ?? 0) > VECTOR_VIEWPORT_FEATURE_THRESHOLD,
|
||||
)
|
||||
const zoom = viewport?.zoom ?? null
|
||||
const zoomRequired = enabled && (zoom === null || zoom < VECTOR_VIEWPORT_MIN_ZOOM)
|
||||
|
||||
useEffect(() => {
|
||||
requestSequence.current += 1
|
||||
setData(null)
|
||||
setLoading(false)
|
||||
setError(null)
|
||||
setTruncated(false)
|
||||
setLoadedFeatureCount(0)
|
||||
}, [enabled, selectedDataset?.id, selectedProjectId])
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled || !selectedProjectId || !selectedDataset || !viewport) {
|
||||
return
|
||||
}
|
||||
if (viewport.zoom < VECTOR_VIEWPORT_MIN_ZOOM) {
|
||||
requestSequence.current += 1
|
||||
setData(null)
|
||||
setLoading(false)
|
||||
setError(null)
|
||||
setTruncated(false)
|
||||
setLoadedFeatureCount(0)
|
||||
return
|
||||
}
|
||||
|
||||
const sequence = requestSequence.current + 1
|
||||
requestSequence.current = sequence
|
||||
const timer = window.setTimeout(async () => {
|
||||
setLoading(true)
|
||||
setError(null)
|
||||
try {
|
||||
const response = await datasetsApi.selectVectorFeatures(selectedProjectId, selectedDataset.id, {
|
||||
bbox: viewport.bbox,
|
||||
limit: VECTOR_VIEWPORT_FEATURE_LIMIT,
|
||||
})
|
||||
if (requestSequence.current !== sequence) {
|
||||
return
|
||||
}
|
||||
setData(response.geojson)
|
||||
setLoadedFeatureCount(response.feature_count)
|
||||
setTruncated(response.truncated)
|
||||
} catch (requestError) {
|
||||
if (requestSequence.current !== sequence) {
|
||||
return
|
||||
}
|
||||
setData(null)
|
||||
setLoadedFeatureCount(0)
|
||||
setTruncated(false)
|
||||
setError(formatError(requestError, 'Unable to load visible vector features'))
|
||||
} finally {
|
||||
if (requestSequence.current === sequence) {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
}, VECTOR_VIEWPORT_DEBOUNCE_MS)
|
||||
|
||||
return () => {
|
||||
window.clearTimeout(timer)
|
||||
}
|
||||
}, [enabled, selectedDataset, selectedProjectId, viewport])
|
||||
|
||||
const statusMessage = useMemo(() => {
|
||||
if (!enabled) {
|
||||
return null
|
||||
}
|
||||
if (zoomRequired) {
|
||||
return `Zoom in to level ${VECTOR_VIEWPORT_MIN_ZOOM} to load buildings from PostGIS.`
|
||||
}
|
||||
if (loading) {
|
||||
return 'Loading visible features from PostGIS...'
|
||||
}
|
||||
if (error) {
|
||||
return error
|
||||
}
|
||||
if (truncated) {
|
||||
return `${loadedFeatureCount.toLocaleString()} visible features loaded; zoom in further because this view exceeds the ${VECTOR_VIEWPORT_FEATURE_LIMIT.toLocaleString()} feature limit.`
|
||||
}
|
||||
return `${loadedFeatureCount.toLocaleString()} visible of ${(featureCount ?? 0).toLocaleString()} total features loaded from PostGIS.`
|
||||
}, [enabled, error, featureCount, loadedFeatureCount, loading, truncated, zoomRequired])
|
||||
|
||||
return {
|
||||
enabled,
|
||||
data,
|
||||
loading,
|
||||
error,
|
||||
truncated,
|
||||
loadedFeatureCount,
|
||||
featureLimit: VECTOR_VIEWPORT_FEATURE_LIMIT,
|
||||
minZoom: VECTOR_VIEWPORT_MIN_ZOOM,
|
||||
zoom,
|
||||
zoomRequired,
|
||||
statusMessage,
|
||||
setViewport,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user