From 8b8b242d3e9fab16eb46bfc5177b5785009c660a Mon Sep 17 00:00:00 2001 From: popovspiridon99 Date: Tue, 7 Oct 2025 12:21:05 +0900 Subject: [PATCH] MapComponent interaction fixes; --- client/src/components/map/MapComponent.tsx | 440 +++++++++------------ 1 file changed, 186 insertions(+), 254 deletions(-) diff --git a/client/src/components/map/MapComponent.tsx b/client/src/components/map/MapComponent.tsx index b4ebaef..5915b2d 100644 --- a/client/src/components/map/MapComponent.tsx +++ b/client/src/components/map/MapComponent.tsx @@ -1,4 +1,4 @@ -import { CSSProperties, useEffect, useRef, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import 'ol/ol.css' import { Modify } from 'ol/interaction' import { ImageStatic, Vector as VectorSource } from 'ol/source' @@ -38,11 +38,14 @@ import { IRegion } from '../../interfaces/fuel' import { useAppStore } from '../../store/app' import { getDistrictData, getRegionData, setDistrictsData, setRegionsData } from '../../store/regions' import { ArrowLeft24Regular } from '@fluentui/react-icons' +import View from 'ol/View' +import { Style } from 'ol/style' const swrOptions: SWRConfiguration = { revalidateOnFocus: false } +// City settings mockup function getCitySettings() { return { city_id: 0, @@ -90,8 +93,8 @@ const MapComponent = ({ // Map const mapElement = useRef(null) - const tooltipRef = useRef(null) + // Get type roles useSWR(`/gis/type-roles`, (url) => fetcher(url, BASE_URL.ems).then(res => { if (Array.isArray(res)) { setTypeRoles(id, res) @@ -99,8 +102,22 @@ const MapComponent = ({ return res }), swrOptions) + // Bounds: region const { data: regionBoundsData } = useSWR(`/gis/bounds/region`, (url) => fetcher(url, BASE_URL.ems), swrOptions) + // Map init + useEffect(() => { + map?.setTarget(mapElement.current as HTMLDivElement) + + if (drawingLayerSource) { + const modify = new Modify({ source: drawingLayerSource }) + map?.addInteraction(modify) + } + + loadFeatures(id) + }, []) + + // First step: On region bounds loaded useEffect(() => { if (regionsLayer && regionBoundsData) { if (Array.isArray(regionBoundsData)) { @@ -113,20 +130,55 @@ const MapComponent = ({ regionsLayer.getSource()?.addFeature(feature) }) + } + } - if (map) { - const extent = regionsLayer.getSource()?.getExtent() - - if (extent) { - map.getView().fit(fromExtent(extent)) - } - } + return () => { + if (regionsLayer) { + regionsLayer.getSource()?.clear() } } }, [regionBoundsData]) useEffect(() => { - if (selectedDistrict && selectedYear) { + if (selectedRegion === null && regionBoundsData) { + if (map) { + const extent = regionsLayer.getSource()?.getExtent() + + if (extent) { + map?.setView(new View({ + extent: extent, + showFullExtent: true, + })) + + map.getView().fit(fromExtent(extent), { padding: [100, 100, 100, 100] }) + } + + regionsLayer.getSource()?.forEachFeature((feature) => { + if (feature.getProperties()['entity_id'] !== selectedRegion) { + feature.setStyle() + } + }) + + map.addInteraction(regionSelect) + + } + } + }, [regionBoundsData, selectedRegion, map, regionsLayer]) + + useEffect(() => { + if (selectedRegion && map) { + regionsLayer.getSource()?.forEachFeature((feature) => { + if (feature.getProperties()['entity_id'] !== selectedRegion) { + feature.setStyle(new Style()) + } + }) + } + }, [selectedRegion, map]) + + // Last step: once selected scheme + useEffect(() => { + if (selectedDistrict && selectedYear && districtBoundLayer) { const bounds = new VectorSource({ url: `${BASE_URL.ems}/gis/bounds/city/${selectedDistrict}`, format: new GeoJSON(), @@ -134,25 +186,22 @@ const MapComponent = ({ districtBoundLayer.setSource(bounds) + // bounds.on('featuresloadend', function () { - // map?.setView(new View({ - // extent: bounds.getExtent() - // })) + map?.setView(new View({ + extent: bounds.getExtent() + })) }) } - }, [selectedDistrict, selectedYear]) - useEffect(() => { - map?.setTarget(mapElement.current as HTMLDivElement) - - if (drawingLayerSource) { - const modify = new Modify({ source: drawingLayerSource }) - map?.addInteraction(modify) + return () => { + if (districtBoundLayer) { + districtBoundLayer.getSource()?.clear() + } } + }, [selectedDistrict, selectedYear, districtBoundLayer]) - loadFeatures(id) - }, []) - + // Edit Mode: add interaction on tool change useEffect(() => { if (currentTool) { if (draw) map?.removeInteraction(draw) @@ -183,19 +232,13 @@ const MapComponent = ({ } }, [satMapsProvider, satLayer]) + // Upload map overlay const submitOverlay = async (file: File | null, polygonExtent: Extent | undefined, rectCoords: IRectCoords | undefined) => { await GisService.uploadOverlay(file, polygonExtent, rectCoords).then(res => { console.log(res) }) } - const mapControlsStyle: CSSProperties = { - borderRadius: '4px', - zIndex: '1', - backgroundColor: colorScheme === 'light' ? '#F0F0F0CC' : '#000000CC', - backdropFilter: 'blur(8px)', - } - // const { data: nodes } = useSWR('/nodes/all', () => fetcher('/nodes/all', BASE_URL.ems), { revalidateOnFocus: false }) // useEffect(() => { @@ -250,16 +293,12 @@ const MapComponent = ({ if (currentObjectId) { if (figuresLayer) { // Reset styles and apply highlight to matching features + console.log(currentObjectId) figuresLayer.getSource()?.getFeatures().forEach((feature: Feature) => { - if (currentObjectId == feature.get('object_id')) { + if (Array.isArray(feature.get('object_id')) ? feature.get('object_id')[0] === currentObjectId : currentObjectId === feature.get('object_id')) { feature.setStyle(highlightStyleRed) - const geometry = feature.getGeometry() - - if (geometry) { - map?.getView().fit(geometry as SimpleGeometry, { duration: 500, maxZoom: 18 }) - } - + zoomToFeature(id, feature) } else { feature.setStyle(undefined) // Reset to default style } @@ -269,20 +308,17 @@ const MapComponent = ({ if (linesLayer) { // Reset styles and apply highlight to matching features linesLayer.getSource()?.getFeatures().forEach((feature: Feature) => { - if (currentObjectId == feature.get('object_id')) { + if (Array.isArray(feature.get('object_id')) ? feature.get('object_id')[0] === currentObjectId : currentObjectId === feature.get('object_id')) { feature.setStyle(highlightStyleRed) - const geometry = feature.getGeometry() - if (geometry) { - map?.getView().fit(geometry as SimpleGeometry, { duration: 500, maxZoom: 18 }) - } + zoomToFeature(id, feature) } else { feature.setStyle(undefined) // Reset to default style } }) } } - }, [currentObjectId]) + }, [currentObjectId, figuresLayer, linesLayer]) const { data: regionsData } = useSWR(`/general/regions`, (url) => fetcher(url, BASE_URL.ems).then(res => { setRegionsData(res) @@ -337,13 +373,15 @@ const MapComponent = ({ useEffect(() => { if (selectedDistrict === null) { setSelectedYear(id, null) + + map?.addInteraction(districtSelect) } if (selectedRegion === null) { setSelectedYear(id, null) setSelectedDistrict(id, null) } - }, [selectedDistrict, selectedRegion, id]) + }, [selectedDistrict, selectedRegion, id, map]) const [leftPaneHidden, setLeftPaneHidden] = useState(false) @@ -397,13 +435,6 @@ const MapComponent = ({ }, [figuresData, linesData, selectedDistrict, selectedYear, districtBoundLayer]) - useEffect(() => { - if (!selectedRegion) { - setSelectedRegion(id, null) - setSelectedYear(id, null) - } - }, [selectedRegion, selectedDistrict, id]) - useEffect(() => { if (selectedDistrict && districtData) { const settings = getCitySettings() @@ -423,12 +454,7 @@ const MapComponent = ({ const center = [settings.offset_x + (wk), settings.offset_y - (hk)] - const extent = [ - center[0] - (wk), - center[1] - (hk), - center[0] + (wk), - center[1] + (hk), - ] + const extent = [center[0] - (wk), center[1] - (hk), center[0] + (wk), center[1] + (hk)] // Set up the initial image layer with the extent const imageSource = new ImageStatic({ @@ -443,34 +469,6 @@ const MapComponent = ({ } }, [selectedDistrict, districtData, staticMapLayer]) - - // Light/dark theme - useEffect(() => { - if (baseLayer) { - baseLayer.on('prerender', function (e) { - if (colorScheme === 'dark') { - if (e.context) { - const context = e.context as CanvasRenderingContext2D - context.filter = 'grayscale(80%) invert(100%) hue-rotate(180deg) ' - context.globalCompositeOperation = 'source-over' - } - } else { - if (e.context) { - const context = e.context as CanvasRenderingContext2D - context.filter = 'none' - } - } - }) - - baseLayer.on('postrender', function (e) { - if (e.context) { - const context = e.context as CanvasRenderingContext2D - context.filter = 'none' - } - }) - } - }, [colorScheme]) - useEffect(() => { if (map) { if (mode === 'print') { @@ -530,15 +528,15 @@ const MapComponent = ({ if (feature) { regionSelect.getFeatures().push(feature) + map?.setView(new View({ + extent: feature?.getGeometry()?.getExtent(), + showFullExtent: true, + })) } zoomToFeature(id, feature) - } - }, [selectedRegion, selectedDistrict]) - - // zoom on district select - useEffect(() => { - if (selectedDistrict) { + } else if (selectedDistrict) { + // zoom on district select const feature = getFeatureByEntityId(selectedDistrict, districtsLayer) if (feature) { @@ -547,9 +545,18 @@ const MapComponent = ({ regionsLayer.setOpacity(0) } + map?.setView(new View({ + extent: feature?.getGeometry()?.getExtent(), + showFullExtent: true, + })) + zoomToFeature(id, feature) + } else if (!selectedRegion) { + setSelectedRegion(id, null) + setSelectedYear(id, null) } - }, [selectedDistrict]) + }, [selectedRegion, selectedDistrict, id]) + useEffect(() => { if (selectedYear) { @@ -560,15 +567,6 @@ const MapComponent = ({ return (
- - {statusText && active && !selectedYear && - -
- {/* {statusText} */} -
-
- } - {active && @@ -600,64 +598,6 @@ const MapComponent = ({ : null} - {/* item.id === selectedRegion)?.name ?? "" - : "" - } - onOptionSelect={(_ev, data) => { - if (data.optionValue) { - setSelectedRegion(id, Number(data.optionValue)); - } else { - setSelectedRegion(id, null); - } - }} - style={{ minWidth: 'auto' }} - > - {regionsData - ? regionsData.map((item: { name: string; id: number }) => ( - - )) - : null} - */} - - {/* item.id === selectedDistrict)?.name + - " - " + - districtsData?.find((item: { id: number }) => item.id === selectedDistrict)?.district_name - : "" - } - onOptionSelect={(_ev, data) => { - if (data.optionValue) { - setSelectedDistrict(id, Number(data.optionValue)); - } else { - setSelectedDistrict(id, null); - } - }} - style={{ minWidth: 'auto' }} - > - {districtsData - ? districtsData.map( - (item: { name: string; id: number; district_name: string }) => ( - - ) - ) - : null} - */} - -
-
-
-
-
- {selectedRegion && selectedDistrict && selectedYear && - //
- // - // - // - //
- "" - } - - - - - - - {!!selectedRegion && !!selectedDistrict && !!selectedYear && -
-
- -
-
- {selectedDistrict && selectedYear && } -
-
- -
- {selectedRegion && selectedDistrict && selectedYear && mode === 'edit' && - - } +
+
+
+
+ + + + + {!!selectedRegion && !!selectedDistrict && !!selectedYear && - +
+ +
+
+ {selectedDistrict && selectedYear && } +
+
+ +
+ + + {!!selectedRegion && !!selectedDistrict && !!selectedYear && + + } +
+
+
+ +
e.preventDefault()} onDrop={(e) => handleImageDrop(e, id)}> +
+ {statusText && active && !selectedYear && + +
+ {/* {statusText} */} +
+
+ }
+ {(linesValidating || figuresValidating) && (