import { useMemo, useState } from 'react'; import DeckGL from '@deck.gl/react'; import { TileLayer } from '@deck.gl/geo-layers'; import { BitmapLayer, ScatterplotLayer } from '@deck.gl/layers'; import CircularProgress from '@mui/material/CircularProgress'; import { useFilters } from '../../context/FilterContext'; import { GeoJSONFeature, SEVERITY_COLORS, SEVERITY_RGB } from '../../types'; const INITIAL_VIEW_STATE = { longitude: -119.4, latitude: 36.6, zoom: 5.3, pitch: 0, bearing: 0, }; interface HoverInfo { x: number; y: number; feature: GeoJSONFeature; } interface PickInfo { x: number; y: number; object?: GeoJSONFeature; } export default function MapCanvas() { const { data, loading, selected, setSelected } = useFilters(); const [hover, setHover] = useState(null); const features = data?.geojson.features ?? []; const layers = useMemo(() => { const basemap = new TileLayer({ id: 'carto-dark-basemap', data: 'https://basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png', minZoom: 0, maxZoom: 19, tileSize: 256, renderSubLayers: (props: { tile: { bbox: { west: number; south: number; east: number; north: number } }; data: unknown; }) => { const { west, south, east, north } = props.tile.bbox; return new BitmapLayer(props, { data: undefined, image: props.data, bounds: [west, south, east, north], }); }, }); const points = new ScatterplotLayer({ id: 'ssam-conflicts', data: features, pickable: true, stroked: true, radiusUnits: 'pixels', lineWidthUnits: 'pixels', getPosition: (f: GeoJSONFeature) => f.geometry.coordinates, getRadius: (f: GeoJSONFeature) => 4 + f.properties.severity_index * 10, getFillColor: (f: GeoJSONFeature) => { const [r, g, b] = SEVERITY_RGB[f.properties.severity_label] ?? [128, 128, 128]; return [r, g, b, 200]; }, getLineColor: (f: GeoJSONFeature) => selected && f.properties.id === selected.id ? [255, 255, 255, 255] : [0, 0, 0, 120], getLineWidth: (f: GeoJSONFeature) => (selected && f.properties.id === selected.id ? 3 : 1), onHover: (info: PickInfo) => setHover(info.object ? { x: info.x, y: info.y, feature: info.object } : null), onClick: (info: PickInfo) => { const feature = info.object; if (!feature) return; const [lng, lat] = feature.geometry.coordinates; setSelected({ ...feature.properties, lng, lat }); }, updateTriggers: { getLineColor: [selected?.id], getLineWidth: [selected?.id], }, }); return [basemap, points]; }, [features, selected, setSelected]); return (
isDragging ? 'grabbing' : isHovering ? 'pointer' : 'grab' } /> {hover && (
{hover.feature.properties.street_name} {hover.feature.properties.county} County {hover.feature.properties.severity_label} · index {hover.feature.properties.severity_index.toFixed(2)} TTC {hover.feature.properties.min_ttc.toFixed(1)}s · PET {hover.feature.properties.min_pet.toFixed(1)}s
)} {loading && (
)}
© OpenStreetMap · © CARTO
); }