From bf6edf26621d36f403bfb69749598afcdbe35e72 Mon Sep 17 00:00:00 2001 From: JIANG Date: Fri, 6 Mar 2026 14:09:26 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E8=8A=82=E7=82=B9=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=E5=8F=98=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DMALeakDetectionPanel.tsx | 161 ++++++++++++------ 1 file changed, 105 insertions(+), 56 deletions(-) diff --git a/src/components/olmap/DMALeakDetection/DMALeakDetectionPanel.tsx b/src/components/olmap/DMALeakDetection/DMALeakDetectionPanel.tsx index 575d649..75cb5a0 100644 --- a/src/components/olmap/DMALeakDetection/DMALeakDetectionPanel.tsx +++ b/src/components/olmap/DMALeakDetection/DMALeakDetectionPanel.tsx @@ -17,11 +17,10 @@ import { ChevronRight, FormatListBulleted, } from "@mui/icons-material"; -import { Circle as CircleStyle, Fill, Stroke, Style } from "ol/style"; -import VectorLayer from "ol/layer/Vector"; -import VectorSource from "ol/source/Vector"; -import Feature from "ol/Feature"; -import { queryFeaturesByIds } from "@/utils/mapQueryService"; +import WebGLVectorTileLayer from "ol/layer/WebGLVectorTile"; +import VectorTileSource from "ol/source/VectorTile"; +import { VectorTile } from "ol"; +import { FlatStyleLike } from "ol/style/flat"; import { useMap } from "@app/OlMap/MapComponent"; import StyleLegend from "@app/OlMap/Controls/StyleLegend"; import AnalysisParameters from "./AnalysisParameters"; @@ -29,6 +28,7 @@ import SchemeQuery from "./SchemeQuery"; import RecognitionResults from "./RecognitionResults"; import { getAreaColor } from "./utils"; import { LeakageResultDetail } from "./types"; +import { config } from "@/config/config"; const TabPanel = ({ value, @@ -44,7 +44,7 @@ const TabPanel = ({ ); - +const DMA_AREA_INDEX_PROPERTY = "dma_area_index"; const DMALeakDetectionPanel: React.FC = () => { const map = useMap(); @@ -52,7 +52,6 @@ const DMALeakDetectionPanel: React.FC = () => { const [tab, setTab] = useState(0); const [result, setResult] = useState(null); const [loadedResult, setLoadedResult] = useState(null); - const [nodeLayer, setNodeLayer] = useState | null>(null); const drawerWidth = 450; const panelTitle = "DMA漏损识别"; @@ -70,55 +69,6 @@ const DMALeakDetectionPanel: React.FC = () => { [activeAreas.length], ); - useEffect(() => { - if (!map) return; - const layer = new VectorLayer({ - source: new VectorSource(), - maxZoom: 24, - minZoom: 12, - properties: { - name: "DMA漏损节点着色", - value: "dma_leak_nodes", - }, - style: (feature) => { - const areaId = feature.get("__areaId"); - return new Style({ - image: new CircleStyle({ - radius: 4.5, - fill: new Fill({ color: getAreaColor(areaId) }), - stroke: new Stroke({ color: "#ffffff", width: 1.2 }), - }), - }); - }, - }); - map.addLayer(layer); - setNodeLayer(layer); - return () => { - map.removeLayer(layer); - }; - }, [map]); - - useEffect(() => { - if (!nodeLayer) return; - const source = nodeLayer.getSource(); - if (!source) return; - source.clear(); - if (!loadedResult) return; - - const nodeAreaMap = loadedResult.node_area_map || {}; - const nodeIds = Object.keys(nodeAreaMap); - if (nodeIds.length === 0) return; - - queryFeaturesByIds(nodeIds, "geo_junctions_mat").then((features) => { - if (!features?.length) return; - features.forEach((feature) => { - const nodeId = String(feature.get("id") ?? ""); - feature.set("__areaId", nodeAreaMap[nodeId] ?? ""); - }); - source.addFeatures(features as Feature[]); - }); - }, [loadedResult, nodeLayer]); - const handleAnalysisResult = useCallback((res: LeakageResultDetail) => { setResult(res); }, []); @@ -129,6 +79,105 @@ const DMALeakDetectionPanel: React.FC = () => { setTab(2); }, []); + useEffect(() => { + if (!map) return; + const junctionLayer = map + .getAllLayers() + .find( + (layer) => + layer instanceof WebGLVectorTileLayer && layer.get("value") === "junctions", + ) as WebGLVectorTileLayer | undefined; + if (!junctionLayer) return; + const source = junctionLayer.getSource() as VectorTileSource; + if (!source) return; + + if (!loadedResult || !loadedResult.node_area_map) { + junctionLayer.setStyle(config.MAP_DEFAULT_STYLE as FlatStyleLike); + return; + } + + const fallbackAreaIds = Array.from( + new Set(Object.values(loadedResult.node_area_map || {}).map(String)), + ); + const areaIds = (loadedResult.areas || []).length + ? loadedResult.areas.map((area) => String(area.area_id)) + : fallbackAreaIds; + if (areaIds.length === 0) { + junctionLayer.setStyle(config.MAP_DEFAULT_STYLE as FlatStyleLike); + return; + } + + const areaIdToIndex = new Map(); + areaIds.forEach((areaId, index) => { + areaIdToIndex.set(areaId, index + 1); + }); + + const nodeAreaIndexMap = new Map(); + Object.entries(loadedResult.node_area_map || {}).forEach(([nodeId, areaId]) => { + const idx = areaIdToIndex.get(String(areaId)); + if (idx !== undefined) { + nodeAreaIndexMap.set(String(nodeId), idx); + } + }); + + const applyFeatureAreaIndex = (renderFeature: any) => { + const featureId = String(renderFeature.get("id") ?? ""); + const areaIndex = nodeAreaIndexMap.get(featureId); + if (areaIndex !== undefined) { + renderFeature.properties_[DMA_AREA_INDEX_PROPERTY] = areaIndex; + } + }; + + const sourceTiles = (source as any).sourceTiles_; + if (sourceTiles) { + Object.values(sourceTiles).forEach((vectorTile: any) => { + const renderFeatures = vectorTile.getFeatures(); + if (!renderFeatures || renderFeatures.length === 0) return; + renderFeatures.forEach((renderFeature: any) => { + applyFeatureAreaIndex(renderFeature); + }); + }); + } + + const listener = (event: any) => { + try { + if (event.tile instanceof VectorTile) { + const renderFeatures = event.tile.getFeatures(); + if (!renderFeatures || renderFeatures.length === 0) return; + renderFeatures.forEach((renderFeature: any) => { + applyFeatureAreaIndex(renderFeature); + }); + } + } catch (error) { + console.error("Error applying DMA area mapping:", error); + } + }; + source.on("tileloadend", listener); + + const fillCases: any[] = []; + areaIds.forEach((areaId, index) => { + fillCases.push( + ["==", ["get", DMA_AREA_INDEX_PROPERTY], index + 1], + getAreaColor(areaId), + ); + }); + const defaultFillColor = String(config.MAP_DEFAULT_STYLE["circle-fill-color"]); + const defaultStrokeColor = String( + config.MAP_DEFAULT_STYLE["circle-stroke-color"], + ); + const dmaStyle: FlatStyleLike = { + ...config.MAP_DEFAULT_STYLE, + "circle-fill-color": ["case", ...fillCases, defaultFillColor], + "circle-stroke-color": ["case", ...fillCases, defaultStrokeColor], + }; + junctionLayer.setStyle(dmaStyle); + + return () => { + source.un("tileloadend", listener); + junctionLayer.setStyle(config.MAP_DEFAULT_STYLE as FlatStyleLike); + }; + }, [map, loadedResult]); + return ( <> {!open && (