From ac966242e779bf857836dfd398114f09531b1d66 Mon Sep 17 00:00:00 2001 From: JIANG Date: Fri, 19 Dec 2025 11:43:33 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AD=89=E5=80=BC=E7=BA=BF?= =?UTF-8?q?=E5=9B=BE=E6=A0=B7=E5=BC=8F=E8=87=AA=E5=AE=9A=E4=B9=89=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/OlMap/Controls/LayerControl.tsx | 1 - src/app/OlMap/Controls/StyleEditorPanel.tsx | 68 ++++++++++++- src/app/OlMap/MapComponent.tsx | 104 ++++++++++---------- src/utils/layers.ts | 15 --- 4 files changed, 119 insertions(+), 69 deletions(-) diff --git a/src/app/OlMap/Controls/LayerControl.tsx b/src/app/OlMap/Controls/LayerControl.tsx index d89e598..fe16d37 100644 --- a/src/app/OlMap/Controls/LayerControl.tsx +++ b/src/app/OlMap/Controls/LayerControl.tsx @@ -1,6 +1,5 @@ import React, { useState, useEffect, useCallback } from "react"; import { useData, useMap } from "../MapComponent"; -import { Layer } from "ol/layer"; import { Checkbox, FormControlLabel } from "@mui/material"; import WebGLVectorTileLayer from "ol/layer/WebGLVectorTile"; import VectorLayer from "ol/layer/Vector"; diff --git a/src/app/OlMap/Controls/StyleEditorPanel.tsx b/src/app/OlMap/Controls/StyleEditorPanel.tsx index 40d2ce9..780b51e 100644 --- a/src/app/OlMap/Controls/StyleEditorPanel.tsx +++ b/src/app/OlMap/Controls/StyleEditorPanel.tsx @@ -193,7 +193,7 @@ const StyleEditorPanel: React.FC = ({ setWaterflowLayerAvailable, setJunctionText, setPipeText, - deckLayer, + setContours, } = data; const { open } = useNotification(); @@ -444,8 +444,10 @@ const StyleEditorPanel: React.FC = ({ breaks.push(max_val); breaks.sort((a, b) => a - b); } - if (junctionStyleConfigState) + if (junctionStyleConfigState) { applyLayerStyle(junctionStyleConfigState, breaks); + applyContourLayerStyle(junctionStyleConfigState, breaks); + } } else if ( layerType === "pipes" && currentPipeCalData && @@ -661,6 +663,67 @@ const StyleEditorPanel: React.FC = ({ saveLayerStyle(renderLayer.get("value"), legendConfig); }, 100); }; + // 应用样式函数,传入 breaks 数据 + const applyContourLayerStyle = ( + layerStyleConfig: LayerStyleState, + breaks?: number[] + ) => { + // 使用传入的 breaks 数据 + if (!breaks || breaks.length === 0) { + console.warn("没有有效的 breaks 数据"); + return; + } + const styleConfig = layerStyleConfig.styleConfig; + + if (!setContours) return; + + const breaksLength = breaks.length; + // 根据 breaks 计算每个分段的颜色 + const colors: string[] = + styleConfig.colorType === "single" + ? // 单一色重复多次 + Array.from({ length: breaksLength }, () => { + return SINGLE_COLOR_PALETTES[styleConfig.singlePaletteIndex].color; + }) + : styleConfig.colorType === "gradient" + ? generateGradientColors(breaksLength) + : styleConfig.colorType === "rainbow" + ? generateRainbowColors(breaksLength) + : (() => { + // 自定义颜色 + const custom = styleConfig.customColors || []; + // 如果自定义颜色数量不足,用反向彩虹色填充 + const result = [...custom]; + const reverseRainbowColors = RAINBOW_PALETTES[1].colors; + while (result.length < breaksLength) { + result.push( + reverseRainbowColors[ + (result.length - custom.length) % reverseRainbowColors.length + ] + ); + } + return result.slice(0, breaksLength); + })(); + + // 构造 ContourLayer 所需的 contours 配置 + const contours = []; + for (let i = 0; i < breaks.length - 1; i++) { + const colorObj = parseColor(colors[i]); + contours.push({ + threshold: [breaks[i], breaks[i + 1]], + color: [ + colorObj.r, + colorObj.g, + colorObj.b, + Math.round(styleConfig.opacity * 255), + ], + strokeWidth: 0, + }); + } + // 应用样式到等值线图层 + setContours(contours); + }; + // 重置样式 const resetStyle = useCallback(() => { if (!selectedRenderLayer) return; @@ -679,6 +742,7 @@ const StyleEditorPanel: React.FC = ({ setApplyJunctionStyle(false); if (setShowJunctionTextLayer) setShowJunctionTextLayer(false); if (setJunctionText) setJunctionText(""); + setContours && setContours([]); setContourLayerAvailable && setContourLayerAvailable(false); } else if (layerId === "pipes") { setApplyPipeStyle(false); diff --git a/src/app/OlMap/MapComponent.tsx b/src/app/OlMap/MapComponent.tsx index e713d6a..be2b13d 100644 --- a/src/app/OlMap/MapComponent.tsx +++ b/src/app/OlMap/MapComponent.tsx @@ -62,7 +62,8 @@ interface DataContextType { pipeText: string; setJunctionText?: React.Dispatch>; setPipeText?: React.Dispatch>; - deckLayer?: DeckLayer; // DeckLayer 实例 + setContours?: React.Dispatch>; + deckLayer?: DeckLayer; } // 跨组件传递 @@ -122,6 +123,7 @@ const MapComponent: React.FC = ({ children }) => { const [showContourLayer, setShowContourLayer] = useState(false); // 控制等高线图层显示 const [junctionText, setJunctionText] = useState("pressure"); const [pipeText, setPipeText] = useState("flow"); + const [contours, setContours] = useState([]); const flowAnimation = useRef(false); // 添加动画控制标志 const [isContourLayerAvailable, setContourLayerAvailable] = useState(false); // 控制等高线图层显示 const [isWaterflowLayerAvailable, setWaterflowLayerAvailable] = @@ -773,7 +775,6 @@ const MapComponent: React.FC = ({ children }) => { // outlineColor: [255, 255, 255, 220], }); - const ALPHA = 102; const contourLayer = new ContourLayer({ id: "junctionContourLayer", name: "等值线", @@ -781,15 +782,7 @@ const MapComponent: React.FC = ({ children }) => { aggregation: "MEAN", cellSize: 600, strokeWidth: 0, - contours: [ - // { threshold: [0, 16], color: [255, 0, 0, ALPHA], strokeWidth: 0 }, - { threshold: [16, 18], color: [255, 0, 0, 0], strokeWidth: 0 }, - { threshold: [18, 20], color: [255, 127, 0, ALPHA], strokeWidth: 0 }, - { threshold: [20, 22], color: [255, 215, 0, ALPHA], strokeWidth: 0 }, - { threshold: [22, 24], color: [199, 224, 0, ALPHA], strokeWidth: 0 }, - { threshold: [24, 26], color: [76, 175, 80, ALPHA], strokeWidth: 0 }, - { threshold: [26, 30], color: [63, 81, 181, ALPHA], strokeWidth: 0 }, - ], + contours: contours, getPosition: (d) => d.position, getWeight: (d: any) => (d[junctionText] as number) < 0 ? 0 : (d[junctionText] as number), @@ -821,7 +814,11 @@ const MapComponent: React.FC = ({ children }) => { showContourLayer, junctionText, pipeText, + contours, ]); + useEffect(() => { + console.log("Contour Layer Available:", isContourLayerAvailable); + }, [isContourLayerAvailable]); // 控制流动动画开关 useEffect(() => { if (pipeText === "flow" && currentPipeCalData.length > 0) { @@ -879,11 +876,11 @@ const MapComponent: React.FC = ({ children }) => { trailLength: 2, // 水流尾迹淡出时间 currentTime: currentTime, }); - if (deckLayer.getDeckLayerById("waterflowLayer")) { - deckLayer.updateDeckLayer("waterflowLayer", waterflowLayer); - } else { - deckLayer.addDeckLayer(waterflowLayer); - } + // if (deckLayer.getDeckLayerById("waterflowLayer")) { + // deckLayer.updateDeckLayer("waterflowLayer", waterflowLayer); + // } else { + // deckLayer.addDeckLayer(waterflowLayer); + // } // 继续请求动画帧,每帧执行一次函数 animationFrameId = requestAnimationFrame(animate); }; @@ -917,41 +914,45 @@ const MapComponent: React.FC = ({ children }) => { const linkMap: Map = new Map( currentPipeCalData.map((r: any) => [r.ID, r]) ); - - // 更新junctionData - setJunctionDataState((prev: any[]) => - prev.map((j) => { - const record = nodeMap.get(j.id); - if (record) { - return { - ...j, - [junctionProperties]: record.value, - }; - } - return j; - }) - ); - - // 更新pipeData - setPipeDataState((prev: any[]) => - prev.map((p) => { - const record = linkMap.get(p.id); - if (record) { - return { - ...p, - flowFlag: pipeProperties === "flow" && record.value < 0 ? -1 : 1, - path: - pipeProperties === "flow" && record.value < 0 && p.flowFlag > 0 - ? [...p.path].reverse() - : p.path, - // 流量数值 - [pipeProperties]: - pipeProperties === "flow" ? Math.abs(record.value) : record.value, - }; - } - return p; - }) - ); + if (nodeMap.size > 0) { + // 更新junctionData + setJunctionDataState((prev: any[]) => + prev.map((j) => { + const record = nodeMap.get(j.id); + if (record) { + return { + ...j, + [junctionProperties]: record.value, + }; + } + return j; + }) + ); + } + if (linkMap.size > 0) { + // 更新pipeData + setPipeDataState((prev: any[]) => + prev.map((p) => { + const record = linkMap.get(p.id); + if (record) { + return { + ...p, + flowFlag: pipeProperties === "flow" && record.value < 0 ? -1 : 1, + path: + pipeProperties === "flow" && record.value < 0 && p.flowFlag > 0 + ? [...p.path].reverse() + : p.path, + // 流量数值 + [pipeProperties]: + pipeProperties === "flow" + ? Math.abs(record.value) + : record.value, + }; + } + return p; + }) + ); + } }, [currentJunctionCalData, currentPipeCalData]); return ( <> @@ -981,6 +982,7 @@ const MapComponent: React.FC = ({ children }) => { setPipeText, junctionText, pipeText, + setContours, deckLayer, }} > diff --git a/src/utils/layers.ts b/src/utils/layers.ts index d28670c..3febb12 100644 --- a/src/utils/layers.ts +++ b/src/utils/layers.ts @@ -111,21 +111,6 @@ export class DeckLayer extends Layer { // 替换为新的 layer 实例 return layerOrProps; } - - // 否则假定传入的是 props 对象,使用现有 layer.clone(props) 创建新实例 - try { - return layer.clone(layerOrProps); - } catch (err) { - // 如果 clone 失败,作为降级策略尝试手动复制 props 到新对象(保留原 layer) - // 这通常不应该发生,但保证不会抛出而破坏整个 layers 列表 - const newLayer = layer.clone - ? layer.clone(layerOrProps) - : { - ...layer, - props: { ...(layer.props || {}), ...(layerOrProps || {}) }, - }; - return newLayer; - } }); this.deck.setProps({ layers: updatedLayers });