From 305dbb6279166c080d0e6abeb8529ad738b46e97 Mon Sep 17 00:00:00 2001 From: Daniel Aschwanden Date: Fri, 26 Jan 2024 13:47:52 +0100 Subject: [PATCH 1/2] feat(map): Enrich LineString start/endpoints --- package.json | 2 +- public/index.html | 2 +- src/views/map/FeatureDetails.tsx | 19 +- src/views/map/Map.tsx | 143 +++++--- src/views/map/controls/DrawControl.tsx | 40 ++- src/views/map/style.ts | 456 ++++++++++++++++++++++++- yarn.lock | 14 +- 7 files changed, 600 insertions(+), 76 deletions(-) diff --git a/package.json b/package.json index 13a8e76d..3581b800 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "@fortawesome/free-regular-svg-icons": "^6.5.1", "@fortawesome/free-solid-svg-icons": "^6.5.1", "@fortawesome/react-fontawesome": "^0.2.0", - "@mapbox/mapbox-gl-draw": "~1.3.0", + "@mapbox/mapbox-gl-draw": "^1.3.0", "@svgr/webpack": "^8.1.0", "@testing-library/dom": "^9.3.4", "@turf/bearing": "^6.5.0", diff --git a/public/index.html b/public/index.html index 7220d2ff..2b64f0da 100644 --- a/public/index.html +++ b/public/index.html @@ -6,7 +6,7 @@ + content="default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; style-src-attr 'self'; child-src 'none'; worker-src 'self' blob:; script-src-elem 'self' https://api.mapbox.com; connect-src 'self' ws://* wss://* https://*;" /> diff --git a/src/views/map/FeatureDetails.tsx b/src/views/map/FeatureDetails.tsx index a205ff9c..c19fa49e 100644 --- a/src/views/map/FeatureDetails.tsx +++ b/src/views/map/FeatureDetails.tsx @@ -22,6 +22,7 @@ function FeatureDetail(props: { onUpdate: (e: any) => void, feature: Feature | u const [iconRotation, setIconRotation] = useState(feature && feature.properties && (feature.properties.iconRotation)); const [name, setName] = useState((feature && feature.properties && feature.properties.name)); const [icon, setIcon] = useState((feature && feature.properties && feature.properties.icon)); + const [iconEnd, setIconEnd] = useState((feature && feature.properties && feature.properties.iconEnd)); const [color, setColor] = useState((feature && feature.properties && feature.properties.color)); const [kind, setKind] = useState((feature && feature.properties && ((feature.geometry.type === "LineString" || feature.geometry.type === "MultiLineString") ? feature.properties.lineType : feature.properties.zoneType))); @@ -44,6 +45,7 @@ function FeatureDetail(props: { onUpdate: (e: any) => void, feature: Feature | u if (feature !== undefined) { let properties: GeoJsonProperties = Object.assign({}, feature.properties, { "icon": icon, + "iconEnd": iconEnd, "color": color, "name": name, "lineType": feature.geometry.type === "LineString" || feature.geometry.type === "MultiLineString" ? kind : undefined, @@ -55,7 +57,7 @@ function FeatureDetail(props: { onUpdate: (e: any) => void, feature: Feature | u onUpdate({ features: [feature], action: "featureDetail" }); } return () => onUpdate({ features: [feature] }); - }, [onUpdate, feature, name, iconRotation, color, icon, kind]); + }, [onUpdate, feature, name, iconRotation, iconEnd, color, icon, kind]); let selectableTypes: typeof LineTypes | typeof ZoneTypes | undefined = undefined; @@ -75,11 +77,17 @@ function FeatureDetail(props: { onUpdate: (e: any) => void, feature: Feature | u else { setIcon(""); } + if (t && t.iconEnd) { + setIconEnd(t.iconEnd.name); + } + else { + setIconEnd(undefined); + } }, [setIcon, selectableTypes]) return (
-

Eigenschaften

+ < h3 className='title is-size-5' > Eigenschaften {feature && feature.geometry.type === "Point" ?
@@ -163,6 +171,7 @@ interface TypesType { description: string; color?: string; icon?: BabsIcon; + iconEnd?: BabsIcon; } interface SelectableTypes { [key: string]: TypesType } @@ -190,13 +199,13 @@ const LineTypes: SelectableTypes = { name: "RutschgebietGespiegelt", description: "Rutschgebiet (umgekehrt)", color: Colors.Red, icon: undefined, }, "begehbar": { - name: "begehbar", description: "Strasse erschwert befahrbar / begehbar", color: Colors.Red, icon: Schaeden.Beschaedigung, + name: "begehbar", description: "Strasse erschwert befahrbar / begehbar", color: Colors.Red, icon: Schaeden.Beschaedigung, iconEnd: Schaeden.Beschaedigung, }, "schwerBegehbar": { - name: "schwerBegehbar", description: "Strasse nicht befahrbar / schwer Begehbar", color: Colors.Red, icon: Schaeden.Teilzerstoerung, + name: "schwerBegehbar", description: "Strasse nicht befahrbar / schwer Begehbar", color: Colors.Red, icon: Schaeden.Teilzerstoerung, iconEnd: Schaeden.Teilzerstoerung, }, "unpassierbar": { - name: "unpassierbar", description: "Strasse unpassierbar / gesperrt", color: Colors.Red, icon: Schaeden.Totalzerstoerung, + name: "unpassierbar", description: "Strasse unpassierbar / gesperrt", color: Colors.Red, icon: Schaeden.Totalzerstoerung, iconEnd: Schaeden.Totalzerstoerung, }, "beabsichtigteErkundung": { name: "beabsichtigteErkundung", description: "Beabsichtigte Erkundung", color: Colors.Blue, icon: Others.Verschiebung, diff --git a/src/views/map/Map.tsx b/src/views/map/Map.tsx index 84b5f560..2330d1c4 100644 --- a/src/views/map/Map.tsx +++ b/src/views/map/Map.tsx @@ -2,32 +2,72 @@ import MapboxDraw from '@mapbox/mapbox-gl-draw'; import "@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css"; +import bearing from '@turf/bearing'; +import { point } from "@turf/helpers"; import DefaultMaker from 'assets/marker.svg'; import { AllIcons, LinePatterns, ZonePatterns } from 'components/BabsIcons'; -import { Feature, FeatureCollection, Geometry, GeoJsonProperties } from 'geojson'; +import { Feature, FeatureCollection, GeoJsonProperties, Geometry } from 'geojson'; import hat from 'hat'; import { isEqual, unionBy } from 'lodash'; import isEmpty from 'lodash/isEmpty'; import maplibregl from 'maplibre-gl'; import 'maplibre-gl/dist/maplibre-gl.css'; import { memo, useCallback, useEffect, useRef, useState } from 'react'; -import { FullscreenControl, Map, MapRef, NavigationControl, ScaleControl } from 'react-map-gl/maplibre'; +import { FullscreenControl, Layer, Map, MapRef, NavigationControl, ScaleControl, Source } from 'react-map-gl/maplibre'; import { useParams } from 'react-router-dom'; import Notification from 'utils/Notification'; import useLocalStorage from 'utils/useLocalStorage'; +import FeatureDetail from './FeatureDetails'; import './control-panel.css'; import DrawControl from './controls/DrawControl'; import ExportControl from './controls/ExportControl'; -import FeatureDetailControlPanel from './controls/FeatureDetailControl'; +import FeatureDetailControl from './controls/FeatureDetailControl'; import StyleSwitcherControl from './controls/StyleSwitcherControl'; -import FeatureDetail from './FeatureDetails'; -import style from './style'; +import { drawStyle } from './style'; const modes = { ...MapboxDraw.modes, // 'draw_point': BabsPointMode }; +const enrichFeature = (f: Feature): Feature[] => { + + if (f === undefined) { + return [] + } + + let features: Feature[] = []; + + if (f.geometry.type === "LineString") { + + if (f.properties?.iconEnd !== undefined && f.properties?.iconEnd !== "") { + // let startPoint = point(f.geometry.coordinates[0]); + // startPoint.id = f.id + ":end"; + // startPoint.properties = { + // parent: f.id, + // icon: f.properties.icon, + // iconRotation: bearing(point(f.geometry.coordinates[0]), point(f.geometry.coordinates[1])) + 90, + // } + + if (f.geometry.coordinates?.length < 2) { + return features + } + + let endPoint = point(f.geometry.coordinates.slice(-1)[0]); + endPoint.properties = { + parent: f.id, + icon: f.properties.iconEnd ?? f.properties.icon, + iconRotation: bearing(f.geometry.coordinates.slice(-1)[0], point(f.geometry.coordinates.slice(-2)[0])) + 90, + }; + endPoint.id = f.id + ":end"; + console.log(endPoint); + features.push(endPoint); + } + } + + return features +} + function MapComponent() { const [draw, setDraw] = useState(); @@ -45,10 +85,11 @@ function MapComponent() { const { incidentId } = useParams(); const [features, setFeatures] = useLocalStorage(`map-incident-${incidentId}`, { "type": "FeatureCollection", "features": [], }); - // const [features, setFeatures] = useState({ "type": "FeatureCollection", "features": [] }); + const [enrichedFeatures, setEnrichedFeatures] = useState({ "type": "FeatureCollection", "features": [] }); - const onCreate = useCallback((e: { features: Feature[]; }) => { + const onCreate = useCallback((e: any) => { + console.log("[onCreate]", e) setFeatures(curFeatureCollection => { console.log("[update]: current features created", e) @@ -67,7 +108,9 @@ function MapComponent() { }); }, [setFeatures]); - const onUpdate = useCallback((e: { features: Feature[]; }) => { + const onUpdate = useCallback((e: any) => { + console.log("[onUpdate]", e) + setFeatures(curFeatureCollection => { // an update creates a deleted feature with the old properties and adds a new one with the new properties const newFeatureCollection = { ...curFeatureCollection }; @@ -83,15 +126,12 @@ function MapComponent() { let cur: Feature | undefined = curFeatureCollection.features.find(c => c.id === f.id) // make sure the old element is not identical to the current if (cur && isEqual(cur, f) && isEqual(cur?.properties, f?.properties)) { - console.log("[update]: current ", cur) - console.log("[update]: identical") return; } // if we found the old one and it got changed, close it if (cur && cur.properties) { cur.properties['deletedAt'] = new Date(); - console.log("[update] storing deleted feature", cur) modifiedFeatures.push(cur); } @@ -104,16 +144,15 @@ function MapComponent() { modifiedFeatures.push(f); } }); - console.log("[update] modified features", modifiedFeatures) - newFeatureCollection.features = [...curFeatureCollection.features, ...modifiedFeatures]; - console.log("[update] resulting feature collection", newFeatureCollection) return newFeatureCollection; }); }, [setFeatures]); - const onDelete = useCallback((e: { features: Feature[]; }) => { + const onDelete = useCallback((e: any) => { + console.log("[onDelete]", e); + setFeatures(curFeatureCollection => { console.log("[delete]: current features updated", e) @@ -125,7 +164,7 @@ function MapComponent() { let cur: Feature | undefined = curFeatureCollection.features.find(c => c.id === f.id) if (cur && cur.properties) { cur.properties['deletedAt'] = new Date(); - newFeatureCollection.features.push(cur); + newFeatureCollection.features.push(f); } } }); @@ -135,45 +174,55 @@ function MapComponent() { }); }, [setFeatures]); - const onCombine = useCallback((e: { createdFeatures: Feature[]; deletedFeatures: Feature[]; }) => { - console.log("onCombine", e); - setFeatures(curFeatureCollection => { - const createdFeatures: Feature[] = e.createdFeatures; - const deletedFeatures: Feature[] = e.deletedFeatures; - deletedFeatures.forEach(f => { if (f.properties) { f.properties['deletedAt'] = Date.now() } }) - createdFeatures.forEach(f => { if (f.properties) { f.properties['createdAt'] = Date.now() } }) + // const onCombine = useCallback((e: { createdFeatures: Feature[]; deletedFeatures: Feature[]; }) => { + // console.log("onCombine", e); + // setFeatures(curFeatureCollection => { + // const createdFeatures: Feature[] = e.createdFeatures; + // const deletedFeatures: Feature[] = e.deletedFeatures; + // deletedFeatures.forEach(f => { if (f.properties) { f.properties['deletedAt'] = Date.now() } }) + // createdFeatures.forEach(f => { if (f.properties) { f.properties['createdAt'] = Date.now() } }) - const newFeatureCollection = { ...curFeatureCollection }; + // const newFeatureCollection = { ...curFeatureCollection }; - // newFeatureCollection.features = pullAllBy(curFeatureCollection.features, deletedFeatures, 'id'); - newFeatureCollection.features = unionBy(createdFeatures, newFeatureCollection.features, 'id'); - return newFeatureCollection; - }); - }, [setFeatures]); + // // newFeatureCollection.features = pullAllBy(curFeatureCollection.features, deletedFeatures, 'id'); + // newFeatureCollection.features = unionBy(createdFeatures, newFeatureCollection.features, 'id'); + // return newFeatureCollection; + // }); + // }, [setFeatures]); const onSelectionChange = useCallback((e: { features: Feature[]; }) => { + console.log("[onSelectionChange]", e) const features: Feature[] = e.features; - if (features.length === 1) { - setSelectedFeature(features[0].id); + if (features.length >= 1) { + const feature = features[0]; + // always work on the parent feature + if (feature.properties?.parent) { + setSelectedFeature(feature.properties.parent); + draw?.changeMode('simple_select', { featureIds: [feature.properties.parent] }) + return + } + setSelectedFeature(feature.id); } else { setSelectedFeature(undefined); } - }, [setSelectedFeature]); + }, [setSelectedFeature, draw]); useEffect(() => { if (!mapRef || !isMapLoaded || !draw || !features || isEmpty(features.features)) { return; } - - console.log("current features", features) let filteredFC: FeatureCollection = { "type": "FeatureCollection", "features": [] }; + filteredFC.features = Object.assign([], features.features.filter(f => f.properties?.deletedAt === undefined)) - console.log("applied features", filteredFC) + + let enrichedFC: FeatureCollection = { "type": "FeatureCollection", "features": [] }; + enrichedFC.features = Object.assign([], features.features.filter(f => f.properties?.deletedAt === undefined).filter(f => f.id !== selectedFeature).flatMap(f => enrichFeature(f))) + setEnrichedFeatures(enrichedFC) draw.set(filteredFC); - }, [draw, mapRef, features, isMapLoaded]); + }, [draw, mapRef, features, isMapLoaded, selectedFeature, setEnrichedFeatures]); const onMapLoad = useCallback(() => { // Add the default marker @@ -221,18 +270,28 @@ function MapComponent() { {/* */} + + + @@ -257,9 +316,9 @@ function MapComponent() { ]} options={{ eventListeners: { onChange: () => { onMapLoad(); return true } } }} /> - f.id === selectedFeature).shift()}> + f.id === selectedFeature).shift()}> f.id === selectedFeature).shift()} /> - +
diff --git a/src/views/map/controls/DrawControl.tsx b/src/views/map/controls/DrawControl.tsx index b0a19cc6..913b7a1e 100644 --- a/src/views/map/controls/DrawControl.tsx +++ b/src/views/map/controls/DrawControl.tsx @@ -7,11 +7,11 @@ import type { ControlPosition } from "react-map-gl/maplibre"; type DrawControlProps = ConstructorParameters[0] & { position?: ControlPosition; setDraw: Dispatch> - onCreate: (evt: any) => void; - onUpdate: (evt: any) => void; - onDelete: (evt: any) => void; - onCombine: (evt: any) => void; - onSelectionChange: (evt: any) => void; + onCreate: (e: any) => void; + onUpdate: (e: any) => void; + onDelete: (e: any) => void; + onCombine: (e: any) => void; + onSelectionChange: (e: any) => void; }; function DrawControl(props: DrawControlProps) { @@ -24,12 +24,15 @@ function DrawControl(props: DrawControlProps) { useControl( ({ map }) => { - map.on("draw.create", props.onCreate); - map.on("draw.update", props.onUpdate); - map.on("draw.combine", props.onCombine); - map.on("draw.uncombine", props.onCombine); - map.on("draw.delete", props.onDelete); - map.on("draw.selectionchange", props.onSelectionChange) + + // map.on("draw.create", (e) => console.log("onCreate:", e)); + // map.on("draw.update", (e) => console.log("onUpdate:", e)); + map.on("draw.create", (e) => props.onCreate(e)); + map.on("draw.update", (e) => props.onUpdate(e)); + map.on("draw.combine", (e) => props.onCombine(e)); + map.on("draw.uncombine", (e) => props.onCombine(e)); + map.on("draw.delete", (e) => props.onDelete(e)); + map.on("draw.selectionchange", (e) => props.onSelectionChange(e)) const draw = new MapboxDraw(props); setDraw(draw); @@ -37,11 +40,12 @@ function DrawControl(props: DrawControlProps) { return draw; }, ({ map }) => { - map.off("draw.create", props.onCreate); - map.off("draw.update", props.onUpdate); - map.off("draw.combine", props.onCombine); - map.off("draw.uncombine", props.onCombine); - map.off("draw.delete", props.onDelete); + map.off("draw.create", (e) => props.onCreate(e)); + map.off("draw.update", (e) => props.onUpdate(e)); + map.off("draw.combine", (e) => props.onCombine(e)); + map.off("draw.uncombine", (e) => props.onCombine(e)); + map.off("draw.delete", (e) => props.onDelete(e)); + map.off("draw.selectionchange", (e) => props.onSelectionChange(e)) }, { position: props.position @@ -54,9 +58,9 @@ function DrawControl(props: DrawControlProps) { DrawControl.defaultProps = { // eslint-disable-next-line @typescript-eslint/no-empty-function - onCreate: () => { }, + onCreate: (e: any) => console.log("onCreate:", e), // eslint-disable-next-line @typescript-eslint/no-empty-function - onUpdate: () => { }, + onUpdate: (e: any) => console.log("onCreate:", e), // eslint-disable-next-line @typescript-eslint/no-empty-function onDelete: () => { }, // eslint-disable-next-line @typescript-eslint/no-empty-function diff --git a/src/views/map/style.ts b/src/views/map/style.ts index b2a3c715..cc0da34c 100644 --- a/src/views/map/style.ts +++ b/src/views/map/style.ts @@ -1,5 +1,5 @@ -export const style = [ +export const drawStyle = [ { 'id': 'gl-draw-polygon-no-fill-pattern', 'type': 'fill', @@ -464,4 +464,456 @@ export const style = [ } ]; -export default style; \ No newline at end of file +export const displayStyle = [ + + { + 'id': 'gl-polygon-special-fill-pattern', + 'type': 'fill', + 'filter': ['all', + ['==', 'active', 'false'], + ['==', '$type', 'Polygon'], + ['has', 'zoneType'], + ['in', 'zoneType', 'Brandzone', 'Zerstoerung'], + ['!=', 'mode', 'static'] + ], + 'paint': { + 'fill-pattern': ['match', ['get', 'zoneType'], 'Brandzone', 'PatternBrandzone', 'Zerstoerung', 'PatternZerstoert', 'PatternBrandzone'], + 'fill-opacity': 0.9 + } + }, + { + 'id': 'gl-polygon-fill-inactive', + 'type': 'fill', + 'filter': ['all', + ['==', 'active', 'false'], + ['==', '$type', 'Polygon'], + ['!in', 'zoneType', 'Brandzone', 'Zerstoerung', 'Schadengebiet', 'Einsatzraum'], + ['!=', 'mode', 'static'] + ], + 'paint': { + 'fill-color': ['coalesce', ['get', 'color'], '#000000'], + 'fill-outline-color': ['coalesce', ['get', 'color'], '#000000'], + 'fill-opacity': 0.5 + } + }, + { + 'id': 'gl-polygon-fill-active', + 'type': 'fill', + 'filter': ['all', ['==', 'active', 'true'], ['==', '$type', 'Polygon']], + 'paint': { + 'fill-color': '#fbb03b', + 'fill-outline-color': '#fbb03b', + 'fill-opacity': 0.3 + } + }, + { + 'id': 'gl-polygon-midpoint', + 'type': 'circle', + 'filter': ['all', + ['==', '$type', 'Point'], + ['==', 'meta', 'midpoint']], + 'paint': { + 'circle-radius': 4, + 'circle-color': '#fbb03b' + } + }, + { + 'id': 'gl-polygon-stroke-inactive', + 'type': 'line', + 'filter': ['all', + ['==', 'active', 'false'], + ['==', '$type', 'Polygon'], + ['!=', 'mode', 'static'] + ], + 'layout': { + 'line-cap': 'round', + 'line-join': 'round' + }, + 'paint': { + 'line-color': ['coalesce', ['get', 'color'], '#000000'], + 'line-width': 2 + } + }, + { + 'id': 'gl-polygon-stroke-active', + 'type': 'line', + 'filter': ['all', ['==', 'active', 'true'], ['==', '$type', 'Polygon']], + 'layout': { + 'line-cap': 'round', + 'line-join': 'round' + }, + 'paint': { + 'line-color': '#fbb03b', + 'line-dasharray': [0.2, 2], + 'line-width': 2 + } + }, + { + 'id': 'gl-line-inactive', + 'type': 'line', + 'filter': ['all', + ['==', 'active', 'false'], + ['==', '$type', 'LineString'], + ['!has', 'lineType'], + ['!=', 'mode', 'static'] + ], + 'layout': { + 'line-cap': 'round', + 'line-join': 'round' + }, + 'paint': { + 'line-color': ['coalesce', ['get', 'color'], '#000000'], + 'line-opacity': 0.7, + 'line-width': 2, + } + }, + { + 'id': 'gl-line-inactive-normalLine', + 'type': 'line', + 'filter': ['all', + ['==', 'active', 'false'], + ['==', '$type', 'LineString'], + ['in', 'lineType', '', 'normal'], + ['!=', 'mode', 'static'] + ], + 'layout': { + 'line-cap': 'round', + 'line-join': 'round' + }, + 'paint': { + 'line-color': ['coalesce', ['get', 'color'], '#000000'], + 'line-opacity': 0.7, + 'line-width': 2, + } + }, + { + 'id': 'gl-line-inactive-pattern', + 'type': 'line', + 'filter': ['all', + ['==', 'active', 'false'], + ['==', '$type', 'LineString'], + ['in', 'lineType', 'unpassierbar', 'beabsichtigteErkundung', 'durchgeführteErkundung', 'Rutschgebiet', 'RutschgebietGespiegelt', 'rettungsAchse'], + ['!=', 'mode', 'static'] + ], + 'layout': { + 'line-cap': 'round', + 'line-join': 'round' + }, + 'paint': { + 'line-pattern': ['match', ['get', 'lineType'], 'unpassierbar', 'PatternLineUnpassierbar', 'beabsichtigteErkundung', 'PatternLineBeabsichtigteErkundung', 'durchgeführteErkundung', 'PatternLineErkundung', 'Rutschgebiet', 'PatternLineRutschgebiet', 'RutschgebietGespiegelt', 'PatternLineRutschgebietGespiegelt', 'PatternLineUnpassierbar', 'rettungsAchse', 'PatternLineRettungsachse'], + 'line-opacity': 0.7, + 'line-width': ['interpolate', ['exponential', 1], ['zoom'], 12, 2, 19, 22], + } + }, + { + 'id': 'gl-line-inactive-solidlines', + 'type': 'line', + 'filter': ['all', + ['==', 'active', 'false'], + ['==', '$type', 'LineString'], + ['in', 'lineType', 'schwerBegehbar', 'durchgeführteVerschiebung', 'durchgeführterEinsatz'], + ['!=', 'mode', 'static'] + ], + 'layout': { + 'line-cap': 'round', + 'line-join': 'round' + }, + 'paint': { + 'line-color': ['coalesce', ['get', 'color'], '#000000'], + 'line-opacity': 0.7, + 'line-width': 2, + } + }, + { + 'id': 'gl-line-inactive-dashlines', + 'type': 'line', + 'filter': ['all', + ['==', 'active', 'false'], + ['==', '$type', 'LineString'], + ['in', 'lineType', 'begehbar', 'beabsichtigteVerschiebung', 'beabsichtigterEinsatz'], + ['!=', 'mode', 'static'] + ], + 'layout': { + 'line-cap': 'round', + 'line-join': 'round' + }, + 'paint': { + 'line-color': ['coalesce', ['get', 'color'], '#000000'], + 'line-dasharray': [6, 4], + 'line-width': 2, + } + }, + { + 'id': 'gl-line-symbol', + 'type': 'symbol', + 'filter': ['all', + ['==', 'active', 'false'], + ['==', '$type', 'LineString'], + ['==', 'meta', 'feature'], + ['has', 'icon'], + ['!has', 'iconRotation'], + ['!=', 'mode', 'static'] + ], + 'layout': { + 'icon-image': ["get", "icon"], + 'icon-allow-overlap': true, + 'icon-size': ['interpolate', ['linear'], ['zoom'], 12, 0.1, 17, 1], + } + }, + { + 'id': 'gl-line-symbol-active', + 'type': 'symbol', + 'filter': ['all', + ['==', 'active', 'false'], + ['==', '$type', 'LineString'], + ['==', 'meta', 'feature'], + ['has', 'iconRotation'], + ['has', 'icon'], + ['!=', 'mode', 'static'] + ], + 'layout': { + 'icon-image': ["get", "icon"], + 'icon-size': ['interpolate', ['linear'], ['zoom'], 12, 0.1, 17, 1], + 'icon-allow-overlap': true, + 'icon-rotation-alignment': 'map', + 'icon-pitch-alignment': 'map', + 'icon-rotate': ['coalesce', ["get", "iconRotation"], 0], + } + }, + { + 'id': 'gl-line-active', + 'type': 'line', + 'filter': ['all', + ['==', '$type', 'LineString'], + ['==', 'active', 'true'] + ], + 'layout': { + 'line-cap': 'round', + 'line-join': 'round' + }, + 'paint': { + 'line-color': '#fbb03b', + 'line-dasharray': [0.2, 2], + 'line-width': 2 + } + }, + { + 'id': 'gl-polygon-and-line-vertex-stroke-inactive', + 'type': 'circle', + 'filter': ['all', + ['==', 'meta', 'vertex'], + ['==', '$type', 'Point'], + ['!=', 'mode', 'static'] + ], + 'paint': { + 'circle-radius': 5, + 'circle-color': '#fff' + } + }, + { + 'id': 'gl-polygon-and-line-vertex-inactive', + 'type': 'circle', + 'filter': ['all', + ['==', 'meta', 'vertex'], + ['==', '$type', 'Point'], + ['!=', 'mode', 'static'] + ], + 'paint': { + 'circle-radius': 3, + 'circle-color': '#fbb03b' + } + }, + { + 'id': 'gl-point-icon', + 'type': 'symbol', + 'filter': ['all', + ['==', '$type', 'Point'], + ['==', 'meta', 'feature'], + ['has', 'icon'], + ['!has', 'iconRotation'], + ], + 'layout': { + 'icon-image': ['coalesce', ["get", "icon"], 'default_marker'], + 'icon-pitch-alignment': 'viewport', + 'icon-allow-overlap': true, + 'icon-size': ['interpolate', ['linear'], ['zoom'], 12, 0.1, 17, 1], + } + }, + { + 'id': 'gl-point-icon-rotation', + 'type': 'symbol', + 'filter': ['all', + ['==', '$type', 'Point'], + ['==', 'meta', 'feature'], + ['has', 'icon'], + ['has', 'iconRotation'], + ], + 'layout': { + 'icon-image': ['coalesce', ["get", "icon"], 'default_marker'], + 'icon-allow-overlap': true, + 'icon-size': ['interpolate', ['linear'], ['zoom'], 12, 0.1, 17, 1], + 'icon-rotation-alignment': 'map', + 'icon-pitch-alignment': 'map', + 'icon-rotate': ['coalesce', ["get", "iconRotation"], 0] + } + }, + { + 'id': 'gl-text-special-placement-points-center', + 'type': 'symbol', + 'filter': ['all', + ['==', 'meta', 'feature'], + ['==', '$type', 'Point'], + ['has', 'name'], + ['has', 'icon'], + ['in', 'icon', 'EingesperrteAbgeschnittene', 'Obdachlose'], + ['!=', 'mode', 'static'] + ], + 'layout': { + 'text-field': ["coalesce", ["get", "name"], ""], + 'text-font': ["Frutiger Neue Condensed Bold"], + 'text-anchor': 'center', + 'text-offset': [0, 0], + 'text-ignore-placement': true, + 'text-size': ['interpolate', ['linear'], ['zoom'], 12, 4, 17, 22] + }, + 'paint': { + 'text-color': '#ff0000', + } + }, + { + 'id': 'gl-text-special-placement-points-right', + 'type': 'symbol', + 'filter': ['all', + ['==', 'meta', 'feature'], + ['==', '$type', 'Point'], + ['has', 'name'], + ['has', 'icon'], + ['in', 'icon', 'Tote', 'Vermisste', 'Verletzte'], + ['!=', 'mode', 'static'] + ], + 'layout': { + 'text-field': ["coalesce", ["get", "name"], ""], + 'text-font': ["Frutiger Neue Condensed Bold"], + 'text-anchor': 'right', + 'text-offset': [2.25, 0.25], + 'text-ignore-placement': true, + 'text-size': ['interpolate', ['linear'], ['zoom'], 12, 4, 17, 22] + }, + 'paint': { + 'text-color': '#ff0000', + } + }, + { + 'id': 'gl-text-name-point', + 'type': 'symbol', + 'filter': ['all', + ['==', 'meta', 'feature'], + ['has', 'name'], + ['!in', 'icon', 'EingesperrteAbgeschnittene', 'Obdachlose', 'Tote', 'Vermisste', 'Verletzte'], + ['==', '$type', 'Point'], + ['!=', 'mode', 'static'] + ], + 'layout': { + 'text-field': ["coalesce", ["get", "name"], ""], + 'text-font': ["Frutiger Neue Condensed Bold"], + 'text-anchor': 'center', + 'text-offset': [0, 1.75], + 'text-ignore-placement': true, + 'text-size': ['interpolate', ['linear'], ['zoom'], 11, 2, 17, 16] + }, + 'paint': { + 'text-color': ['coalesce', ['get', 'color'], '#000000'], + } + }, + { + 'id': 'gl-text-name-Polygon', + 'type': 'symbol', + 'filter': ['all', + ['==', 'meta', 'feature'], + ['has', 'name'], + ['==', '$type', 'Polygon'], + ['!=', 'mode', 'static'] + ], + 'layout': { + 'text-field': ["coalesce", ["get", "name"], ""], + 'text-font': ["Frutiger Neue Condensed Bold"], + 'text-size': ['interpolate', ['linear'], ['zoom'], 12, 2, 17, 20], + 'symbol-placement': 'line', + 'text-offset': [0, 0.5], + 'text-ignore-placement': true, + 'text-anchor': 'center' + }, + 'paint': { + 'text-color': ['coalesce', ['get', 'color'], '#000000'], + 'text-halo-color': '#fff' + } + }, + { + 'id': 'gl-text-name-LineString', + 'type': 'symbol', + 'filter': ['all', + ['==', 'meta', 'feature'], + ['has', 'name'], + ['==', '$type', 'LineString'], + ['!=', 'mode', 'static'] + ], + 'layout': { + 'text-field': ["coalesce", ["get", "name"], ""], + 'text-font': ["Frutiger Neue Condensed Bold"], + 'text-size': ['interpolate', ['linear'], ['zoom'], 12, 2, 17, 20], + 'symbol-placement': 'line-center', + 'text-offset': [0, 1], + 'text-ignore-placement': true, + 'text-anchor': 'center' + }, + 'paint': { + 'text-color': ['coalesce', ['get', 'color'], '#000000'], + 'text-halo-color': '#fff' + } + }, + { + 'id': 'gl-point-inactive', + 'type': 'circle', + 'filter': ['all', + ['==', 'active', 'false'], + ['==', '$type', 'Point'], + ['==', 'meta', 'feature'], + ['!has', 'icon'], + ['!=', 'mode', 'static'] + ], + 'paint': { + 'circle-radius': 5, + 'circle-color': '#0055ff' + }, + }, + { + 'id': 'gl-point-stroke-active', + 'type': 'circle', + 'filter': ['all', + ['==', '$type', 'Point'], + ['==', 'active', 'true'], + ['!has', 'icon'], + ['!=', 'meta', 'midpoint'] + ], + 'paint': { + 'circle-radius': 7, + 'circle-color': '#fff' + } + }, + { + 'id': 'gl-point-active', + 'type': 'circle', + 'filter': ['all', + ['==', '$type', 'Point'], + ['!=', 'meta', 'midpoint'], + ['!has', 'icon'], + ['==', 'active', 'true']], + 'paint': { + 'circle-radius': 5, + 'circle-color': '#fbb03b' + } + } +]; + + +export default drawStyle; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index a1355af3..d0d2c538 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2567,7 +2567,7 @@ __metadata: languageName: node linkType: hard -"@mapbox/geojson-extent@npm:^1.0.0": +"@mapbox/geojson-extent@npm:^1.0.1": version: 1.0.1 resolution: "@mapbox/geojson-extent@npm:1.0.1" dependencies: @@ -2616,18 +2616,18 @@ __metadata: languageName: node linkType: hard -"@mapbox/mapbox-gl-draw@npm:~1.3.0": - version: 1.3.0 - resolution: "@mapbox/mapbox-gl-draw@npm:1.3.0" +"@mapbox/mapbox-gl-draw@npm:^1.3.0": + version: 1.4.3 + resolution: "@mapbox/mapbox-gl-draw@npm:1.4.3" dependencies: "@mapbox/geojson-area": ^0.2.2 - "@mapbox/geojson-extent": ^1.0.0 + "@mapbox/geojson-extent": ^1.0.1 "@mapbox/geojson-normalize": ^0.0.1 "@mapbox/point-geometry": ^0.1.0 hat: 0.0.3 lodash.isequal: ^4.5.0 xtend: ^4.0.2 - checksum: 51d32271d9f9941268ca99b45de05bfd3a21080a2a7456eb4996985ce01ba78760a44501753d38cbc0ea1950bddc0927b5d2ea9559cea45320a1bfb2b3c38a40 + checksum: 5426173bd10f8b181567f73ebbb285175f6e05b09015ba9fba24aea5b056591f5326195091b37cd9df239be1e688ce72ed29f318047b52e130213d640bf269bf languageName: node linkType: hard @@ -13654,7 +13654,7 @@ __metadata: "@fortawesome/free-regular-svg-icons": ^6.5.1 "@fortawesome/free-solid-svg-icons": ^6.5.1 "@fortawesome/react-fontawesome": ^0.2.0 - "@mapbox/mapbox-gl-draw": ~1.3.0 + "@mapbox/mapbox-gl-draw": ^1.3.0 "@svgr/webpack": ^8.1.0 "@testing-library/dom": ^9.3.4 "@testing-library/jest-dom": ^6.3.0 From 1370647f1cce9d41e4a89c5e0039068962e3ac75 Mon Sep 17 00:00:00 2001 From: Daniel Aschwanden Date: Sun, 28 Jan 2024 20:37:11 +0100 Subject: [PATCH 2/2] feat(map): rework layer enrichment --- src/components/map/EnrichedLayerFeatures.tsx | 134 +++++++++++++++++++ src/views/map/FeatureDetails.tsx | 37 ++--- src/views/map/Map.tsx | 93 ++----------- 3 files changed, 156 insertions(+), 108 deletions(-) create mode 100644 src/components/map/EnrichedLayerFeatures.tsx diff --git a/src/components/map/EnrichedLayerFeatures.tsx b/src/components/map/EnrichedLayerFeatures.tsx new file mode 100644 index 00000000..0c7dc8fc --- /dev/null +++ b/src/components/map/EnrichedLayerFeatures.tsx @@ -0,0 +1,134 @@ +import bearing from '@turf/bearing'; +import { point } from '@turf/helpers'; +import { BabsIcon, Schaeden, Others } from 'components/BabsIcons'; +import { Feature, FeatureCollection, GeoJsonProperties, Geometry } from 'geojson'; +import { Layer, Source } from "react-map-gl"; + +const enrichFeature = (f: Feature): Feature[] => { + + if (f === undefined) { + return [] + } + + let features: Feature[] = []; + + if (f.geometry.type === "LineString") { + let enrich: EnrichLineConfig | undefined = EnrichLineStringMap[f.properties?.lineType] + if (enrich !== undefined) { + if (enrich.iconStart) { + let startPoint = point(f.geometry.coordinates[0]); + startPoint.id = f.id + ":start"; + startPoint.properties = { + parent: f.id, + icon: enrich.iconStart.name, + iconRotation: bearing(point(f.geometry.coordinates[0]), point(f.geometry.coordinates[1])) + enrich.iconRotation, + } + features.push(startPoint) + } + + if (enrich.iconEnd) { + let endPoint = point(f.geometry.coordinates.slice(-1)[0]); + endPoint.id = f.id + ":end"; + endPoint.properties = { + parent: f.id, + icon: enrich.iconEnd.name, + iconRotation: bearing(f.geometry.coordinates.slice(-1)[0], point(f.geometry.coordinates.slice(-2)[0])) + enrich.iconRotation, + }; + features.push(endPoint); + } + } + } + + return features +} + +type EnrichLineConfig = { + iconStart?: BabsIcon; + iconEnd?: BabsIcon; + iconRotation: number; +} + +const EnrichLineStringMap: { [key: string]: EnrichLineConfig } = { + "begehbar": { + iconStart: Schaeden.Beschaedigung, + iconEnd: Schaeden.Beschaedigung, + iconRotation: 90, + }, + "schwerBegehbar": { + iconStart: Schaeden.Teilzerstoerung, + iconEnd: Schaeden.Teilzerstoerung, + iconRotation: 90, + }, + "unpassierbar": { + iconStart: Schaeden.Totalzerstoerung, + iconEnd: Schaeden.Totalzerstoerung, + iconRotation: 90, + }, + "beabsichtigteErkundung": { + iconStart: undefined, + iconEnd: Others.Verschiebung, + iconRotation: 90, + }, + "durchgeführteErkundung": { + iconStart: undefined, + iconEnd: Others.Verschiebung, + iconRotation: 90, + }, + "beabsichtigteVerschiebung": { + iconStart: undefined, + iconEnd: Others.Verschiebung, + iconRotation: 90, + }, + "rettungsAchse": { + iconStart: undefined, + iconEnd: Others.Verschiebung, + iconRotation: 90, + }, + "durchgeführteVerschiebung": { + iconStart: undefined, + iconEnd: Others.Verschiebung, + iconRotation: 90, + }, + "beabsichtigterEinsatz": { + iconStart: undefined, + iconEnd: Others.Einsatz, + iconRotation: 90, + }, + "durchgeführterEinsatz": { + iconStart: undefined, + iconEnd: Others.Einsatz, + iconRotation: 90, + }, +} + + + +const EnrichedSymbolSource = (props: EnrichedFeaturesProps) => { + let enrichedFC: FeatureCollection = { "type": "FeatureCollection", "features": [] }; + enrichedFC.features = Object.assign([], props.featureCollection.features.filter(f => f.properties?.deletedAt === undefined).filter(f => f.id !== props.selectedFeature).flatMap(f => enrichFeature(f))) + + return + + +} + +export const EnrichedFeaturesSource = (props: EnrichedFeaturesProps) => { + + return <> + + +} + +interface EnrichedFeaturesProps { + featureCollection: FeatureCollection; + selectedFeature: string | number | undefined +} + +export default EnrichedFeaturesSource; \ No newline at end of file diff --git a/src/views/map/FeatureDetails.tsx b/src/views/map/FeatureDetails.tsx index c19fa49e..33708dbb 100644 --- a/src/views/map/FeatureDetails.tsx +++ b/src/views/map/FeatureDetails.tsx @@ -1,6 +1,6 @@ import bearing from "@turf/bearing"; import { LineString, point } from "@turf/helpers"; -import { BabsIcon, IconGroups, Others, Schaeden } from "components/BabsIcons"; +import { BabsIcon, IconGroups, } from "components/BabsIcons"; import { Feature, GeoJsonProperties } from "geojson"; import { isEmpty, isUndefined, omitBy } from "lodash"; import { SetStateAction, memo, useCallback, useEffect, useState } from "react"; @@ -22,7 +22,6 @@ function FeatureDetail(props: { onUpdate: (e: any) => void, feature: Feature | u const [iconRotation, setIconRotation] = useState(feature && feature.properties && (feature.properties.iconRotation)); const [name, setName] = useState((feature && feature.properties && feature.properties.name)); const [icon, setIcon] = useState((feature && feature.properties && feature.properties.icon)); - const [iconEnd, setIconEnd] = useState((feature && feature.properties && feature.properties.iconEnd)); const [color, setColor] = useState((feature && feature.properties && feature.properties.color)); const [kind, setKind] = useState((feature && feature.properties && ((feature.geometry.type === "LineString" || feature.geometry.type === "MultiLineString") ? feature.properties.lineType : feature.properties.zoneType))); @@ -45,7 +44,6 @@ function FeatureDetail(props: { onUpdate: (e: any) => void, feature: Feature | u if (feature !== undefined) { let properties: GeoJsonProperties = Object.assign({}, feature.properties, { "icon": icon, - "iconEnd": iconEnd, "color": color, "name": name, "lineType": feature.geometry.type === "LineString" || feature.geometry.type === "MultiLineString" ? kind : undefined, @@ -57,7 +55,7 @@ function FeatureDetail(props: { onUpdate: (e: any) => void, feature: Feature | u onUpdate({ features: [feature], action: "featureDetail" }); } return () => onUpdate({ features: [feature] }); - }, [onUpdate, feature, name, iconRotation, iconEnd, color, icon, kind]); + }, [onUpdate, feature, name, iconRotation, color, icon, kind]); let selectableTypes: typeof LineTypes | typeof ZoneTypes | undefined = undefined; @@ -77,12 +75,6 @@ function FeatureDetail(props: { onUpdate: (e: any) => void, feature: Feature | u else { setIcon(""); } - if (t && t.iconEnd) { - setIconEnd(t.iconEnd.name); - } - else { - setIconEnd(undefined); - } }, [setIcon, selectableTypes]) return ( @@ -171,7 +163,6 @@ interface TypesType { description: string; color?: string; icon?: BabsIcon; - iconEnd?: BabsIcon; } interface SelectableTypes { [key: string]: TypesType } @@ -193,40 +184,40 @@ const ZoneTypes: SelectableTypes = { const LineTypes: SelectableTypes = { "Rutschgebiet": { - name: "Rutschgebiet", description: "Rutschgebiet", color: Colors.Red, icon: undefined, + name: "Rutschgebiet", description: "Rutschgebiet", color: Colors.Red, }, "RutschgebietGespiegelt": { - name: "RutschgebietGespiegelt", description: "Rutschgebiet (umgekehrt)", color: Colors.Red, icon: undefined, + name: "RutschgebietGespiegelt", description: "Rutschgebiet (umgekehrt)", color: Colors.Red, }, "begehbar": { - name: "begehbar", description: "Strasse erschwert befahrbar / begehbar", color: Colors.Red, icon: Schaeden.Beschaedigung, iconEnd: Schaeden.Beschaedigung, + name: "begehbar", description: "Strasse erschwert befahrbar / begehbar", color: Colors.Red, }, "schwerBegehbar": { - name: "schwerBegehbar", description: "Strasse nicht befahrbar / schwer Begehbar", color: Colors.Red, icon: Schaeden.Teilzerstoerung, iconEnd: Schaeden.Teilzerstoerung, + name: "schwerBegehbar", description: "Strasse nicht befahrbar / schwer Begehbar", color: Colors.Red, }, "unpassierbar": { - name: "unpassierbar", description: "Strasse unpassierbar / gesperrt", color: Colors.Red, icon: Schaeden.Totalzerstoerung, iconEnd: Schaeden.Totalzerstoerung, + name: "unpassierbar", description: "Strasse unpassierbar / gesperrt", color: Colors.Red, }, "beabsichtigteErkundung": { - name: "beabsichtigteErkundung", description: "Beabsichtigte Erkundung", color: Colors.Blue, icon: Others.Verschiebung, + name: "beabsichtigteErkundung", description: "Beabsichtigte Erkundung", color: Colors.Blue, }, "durchgeführteErkundung": { - name: "durchgeführteErkundung", description: "Durchgeführte Erkundung", color: Colors.Blue, icon: Others.Verschiebung, + name: "durchgeführteErkundung", description: "Durchgeführte Erkundung", color: Colors.Blue, }, "beabsichtigteVerschiebung": { - name: "beabsichtigteVerschiebung", description: "Beabsichtigte Verschiebung", color: Colors.Blue, icon: Others.Verschiebung, + name: "beabsichtigteVerschiebung", description: "Beabsichtigte Verschiebung", color: Colors.Blue, }, "rettungsAchse": { - name: "rettungsAchse", description: "Rettungs Achse", color: Colors.Blue, icon: Others.Verschiebung, + name: "rettungsAchse", description: "Rettungs Achse", color: Colors.Blue, }, "durchgeführteVerschiebung": { - name: "durchgeführteVerschiebung", description: "Durchgeführte Verschiebung", color: Colors.Blue, icon: Others.Verschiebung, + name: "durchgeführteVerschiebung", description: "Durchgeführte Verschiebung", color: Colors.Blue, }, "beabsichtigterEinsatz": { - name: "beabsichtigterEinsatz", description: "Beabsichtigter Einsatz", color: Colors.Blue, icon: Others.Einsatz, + name: "beabsichtigterEinsatz", description: "Beabsichtigter Einsatz", color: Colors.Blue, }, "durchgeführterEinsatz": { - name: "durchgeführterEinsatz", description: "Durchgeführter Einsatz", color: Colors.Blue, icon: Others.Einsatz, + name: "durchgeführterEinsatz", description: "Durchgeführter Einsatz", color: Colors.Blue, }, }; diff --git a/src/views/map/Map.tsx b/src/views/map/Map.tsx index 2330d1c4..cdf89b46 100644 --- a/src/views/map/Map.tsx +++ b/src/views/map/Map.tsx @@ -2,8 +2,6 @@ import MapboxDraw from '@mapbox/mapbox-gl-draw'; import "@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css"; -import bearing from '@turf/bearing'; -import { point } from "@turf/helpers"; import DefaultMaker from 'assets/marker.svg'; import { AllIcons, LinePatterns, ZonePatterns } from 'components/BabsIcons'; import { Feature, FeatureCollection, GeoJsonProperties, Geometry } from 'geojson'; @@ -13,7 +11,7 @@ import isEmpty from 'lodash/isEmpty'; import maplibregl from 'maplibre-gl'; import 'maplibre-gl/dist/maplibre-gl.css'; import { memo, useCallback, useEffect, useRef, useState } from 'react'; -import { FullscreenControl, Layer, Map, MapRef, NavigationControl, ScaleControl, Source } from 'react-map-gl/maplibre'; +import { FullscreenControl, Map, MapRef, NavigationControl, ScaleControl } from 'react-map-gl/maplibre'; import { useParams } from 'react-router-dom'; import Notification from 'utils/Notification'; import useLocalStorage from 'utils/useLocalStorage'; @@ -24,50 +22,13 @@ import ExportControl from './controls/ExportControl'; import FeatureDetailControl from './controls/FeatureDetailControl'; import StyleSwitcherControl from './controls/StyleSwitcherControl'; import { drawStyle } from './style'; +import EnrichedLayerFeatures from 'components/map/EnrichedLayerFeatures'; const modes = { ...MapboxDraw.modes, // 'draw_point': BabsPointMode }; -const enrichFeature = (f: Feature): Feature[] => { - - if (f === undefined) { - return [] - } - - let features: Feature[] = []; - - if (f.geometry.type === "LineString") { - - if (f.properties?.iconEnd !== undefined && f.properties?.iconEnd !== "") { - // let startPoint = point(f.geometry.coordinates[0]); - // startPoint.id = f.id + ":end"; - // startPoint.properties = { - // parent: f.id, - // icon: f.properties.icon, - // iconRotation: bearing(point(f.geometry.coordinates[0]), point(f.geometry.coordinates[1])) + 90, - // } - - if (f.geometry.coordinates?.length < 2) { - return features - } - - let endPoint = point(f.geometry.coordinates.slice(-1)[0]); - endPoint.properties = { - parent: f.id, - icon: f.properties.iconEnd ?? f.properties.icon, - iconRotation: bearing(f.geometry.coordinates.slice(-1)[0], point(f.geometry.coordinates.slice(-2)[0])) + 90, - }; - endPoint.id = f.id + ":end"; - console.log(endPoint); - features.push(endPoint); - } - } - - return features -} - function MapComponent() { const [draw, setDraw] = useState(); @@ -85,14 +46,9 @@ function MapComponent() { const { incidentId } = useParams(); const [features, setFeatures] = useLocalStorage(`map-incident-${incidentId}`, { "type": "FeatureCollection", "features": [], }); - const [enrichedFeatures, setEnrichedFeatures] = useState({ "type": "FeatureCollection", "features": [] }); - const onCreate = useCallback((e: any) => { - console.log("[onCreate]", e) setFeatures(curFeatureCollection => { - console.log("[update]: current features created", e) - const newFeatureCollection = { ...curFeatureCollection }; const createdFeatures: Feature[] = e.features; createdFeatures.forEach(f => { @@ -101,8 +57,6 @@ function MapComponent() { newFeatureCollection.features.push(f); } }) - - console.log("Creating feature", createdFeatures) newFeatureCollection.features = unionBy(newFeatureCollection.features, curFeatureCollection.features, 'id'); return newFeatureCollection; }); @@ -119,7 +73,6 @@ function MapComponent() { const updatedFeatures: Feature[] = e.features; const modifiedFeatures: Feature[] = []; updatedFeatures.forEach(f => { - console.log("[update]: current features updated", e) if (f.properties) { // fetch the old element @@ -139,8 +92,6 @@ function MapComponent() { f.id = hat(); f.properties['createdAt'] = new Date(); f.properties['achestorID'] = cur?.id; - console.log("[update] storing added feature", f) - modifiedFeatures.push(f); } }); @@ -148,14 +99,13 @@ function MapComponent() { return newFeatureCollection; }); + }, [setFeatures]); const onDelete = useCallback((e: any) => { console.log("[onDelete]", e); setFeatures(curFeatureCollection => { - console.log("[delete]: current features updated", e) - const newFeatureCollection = { ...curFeatureCollection }; const deletedFeatures: Feature[] = e.features; deletedFeatures.forEach(f => { @@ -172,24 +122,9 @@ function MapComponent() { return newFeatureCollection; }); - }, [setFeatures]); - - // const onCombine = useCallback((e: { createdFeatures: Feature[]; deletedFeatures: Feature[]; }) => { - // console.log("onCombine", e); - // setFeatures(curFeatureCollection => { - // const createdFeatures: Feature[] = e.createdFeatures; - // const deletedFeatures: Feature[] = e.deletedFeatures; - // deletedFeatures.forEach(f => { if (f.properties) { f.properties['deletedAt'] = Date.now() } }) - // createdFeatures.forEach(f => { if (f.properties) { f.properties['createdAt'] = Date.now() } }) - - // const newFeatureCollection = { ...curFeatureCollection }; - - // // newFeatureCollection.features = pullAllBy(curFeatureCollection.features, deletedFeatures, 'id'); - // newFeatureCollection.features = unionBy(createdFeatures, newFeatureCollection.features, 'id'); - // return newFeatureCollection; - // }); - // }, [setFeatures]); + setSelectedFeature(undefined); + }, [setFeatures, setSelectedFeature]); const onSelectionChange = useCallback((e: { features: Feature[]; }) => { console.log("[onSelectionChange]", e) @@ -217,12 +152,8 @@ function MapComponent() { filteredFC.features = Object.assign([], features.features.filter(f => f.properties?.deletedAt === undefined)) - let enrichedFC: FeatureCollection = { "type": "FeatureCollection", "features": [] }; - enrichedFC.features = Object.assign([], features.features.filter(f => f.properties?.deletedAt === undefined).filter(f => f.id !== selectedFeature).flatMap(f => enrichFeature(f))) - setEnrichedFeatures(enrichedFC) - draw.set(filteredFC); - }, [draw, mapRef, features, isMapLoaded, selectedFeature, setEnrichedFeatures]); + }, [draw, mapRef, features, isMapLoaded, selectedFeature]); const onMapLoad = useCallback(() => { // Add the default marker @@ -270,16 +201,6 @@ function MapComponent() { {/* */} - - - + +