From c9490c817116a2a346b1514c1d77f6b2978236a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Prod=27homme?= Date: Thu, 28 Nov 2024 14:07:24 +0100 Subject: [PATCH] feat(client): Highlight the selected location on the map --- .../components/map/layer-manager/index.tsx | 14 +++- .../layer-manager/selected-location-layer.tsx | 72 +++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 client/src/components/map/layer-manager/selected-location-layer.tsx diff --git a/client/src/components/map/layer-manager/index.tsx b/client/src/components/map/layer-manager/index.tsx index bc008f3..d31f606 100644 --- a/client/src/components/map/layer-manager/index.tsx +++ b/client/src/components/map/layer-manager/index.tsx @@ -1,10 +1,11 @@ import { useMemo } from "react"; import { Layer } from "react-map-gl"; -import MaskLayer from "@/components/map/layer-manager/mask-layer"; import useMapLayers from "@/hooks/use-map-layers"; import LayerManagerItem from "./item"; +import MaskLayer from "./mask-layer"; +import SelectedLocationLayer from "./selected-location-layer"; const LayerManager = () => { const [layers] = useMapLayers(); @@ -15,6 +16,13 @@ const LayerManager = () => { */ const positioningLayers = useMemo(() => { return [ + , ...layers.map((layer, index) => { const beforeId = index === 0 ? "data-layers" : `layer-position-${layers[index - 1].id}`; return ( @@ -39,6 +47,10 @@ const LayerManager = () => { const layerManagerItems = useMemo(() => { return [ + , ...layers.map((layer) => { const beforeId = `layer-position-${layer.id}`; const { id, ...settings } = layer; diff --git a/client/src/components/map/layer-manager/selected-location-layer.tsx b/client/src/components/map/layer-manager/selected-location-layer.tsx new file mode 100644 index 0000000..e9a88f4 --- /dev/null +++ b/client/src/components/map/layer-manager/selected-location-layer.tsx @@ -0,0 +1,72 @@ +import { PathStyleExtension } from "@deck.gl/extensions"; +import { GeoJsonLayer } from "@deck.gl/layers"; +import { useContext, useEffect, useMemo } from "react"; + +import useLocation from "@/hooks/use-location"; +import { useLocationGeometry } from "@/hooks/use-location-geometry"; + +import { DeckGLMapboxOverlayContext } from "../deckgl-mapbox-provider"; + +interface SelectedLocationLayerProps { + beforeId: string; +} + +const SelectedLocationLayer = ({ beforeId }: SelectedLocationLayerProps) => { + const [location] = useLocation(); + const { data, isLoading } = useLocationGeometry(location.code.slice(-1)[0]); + const geometry = useMemo(() => { + if (isLoading || data === undefined || data === null || location.code[0] === "SS") { + // We return an empty feature collection so that while the geometry is loading, we don't show + // anything + return { + type: "FeatureCollection", + features: [], + }; + } + + return data; + }, [data, isLoading, location.code]); + + const { addLayer, removeLayer } = useContext(DeckGLMapboxOverlayContext); + + useEffect(() => { + const layers = [ + new GeoJsonLayer({ + id: "selected-location-background-stroke", + beforeId, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + data: geometry, + stroked: true, + filled: false, + getLineColor: [255, 255, 255], + getLineWidth: 3, + lineWidthUnits: "pixels", + }), + new GeoJsonLayer({ + id: "selected-location-top-stroke", + beforeId, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + data: geometry, + stroked: true, + filled: false, + getLineColor: [255, 204, 21], // supernova-yellow-400 + getLineWidth: 2, + lineWidthUnits: "pixels", + getDashArray: [4, 2], + extensions: [new PathStyleExtension({ dash: true, highPrecisionDash: true })], + }), + ]; + + layers.forEach((layer) => addLayer(layer)); + + return () => { + layers.forEach((layer) => removeLayer(layer.id)); + }; + }, [addLayer, beforeId, geometry, removeLayer]); + + return null; +}; + +export default SelectedLocationLayer;