From 4b257aa95979afc2b7b366bae201d1c31e1d1db8 Mon Sep 17 00:00:00 2001 From: JIANG Date: Mon, 29 Dec 2025 11:17:43 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=9B=BA=E6=9C=89=E5=B1=9E?= =?UTF-8?q?=E6=80=A7=E7=9A=84=E6=A0=B7=E5=BC=8F=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/OlMap/Controls/StyleEditorPanel.tsx | 110 ++++++++++++++++---- src/app/OlMap/MapComponent.tsx | 51 ++++++++- 2 files changed, 138 insertions(+), 23 deletions(-) diff --git a/src/app/OlMap/Controls/StyleEditorPanel.tsx b/src/app/OlMap/Controls/StyleEditorPanel.tsx index fc47330..9b4f667 100644 --- a/src/app/OlMap/Controls/StyleEditorPanel.tsx +++ b/src/app/OlMap/Controls/StyleEditorPanel.tsx @@ -195,6 +195,8 @@ const StyleEditorPanel: React.FC = ({ setJunctionText, setPipeText, setContours, + diameterRange, + elevationRange, } = data; const { open } = useNotification(); @@ -398,10 +400,15 @@ const StyleEditorPanel: React.FC = ({ layerType: "junctions" | "pipes", styleConfig: any ) => { + const isElevation = + layerType === "junctions" && styleConfig.property === "elevation"; + const isDiameter = + layerType === "pipes" && styleConfig.property === "diameter"; + if ( layerType === "junctions" && - currentJunctionCalData && - currentJunctionCalData.length > 0 + ((currentJunctionCalData && currentJunctionCalData.length > 0) || + (isElevation && elevationRange)) ) { // 应用节点样式 let junctionStyleConfigState = layerStyleStates.find( @@ -411,6 +418,14 @@ const StyleEditorPanel: React.FC = ({ // 更新节点数据属性 const segments = junctionStyleConfigState?.styleConfig.segments ?? 5; let breaks: number[] = []; + + const dataValues = + isElevation && elevationRange + ? [elevationRange[0], elevationRange[1]] + : currentJunctionCalData?.map((d: any) => d.value) || []; + + if (dataValues.length === 0) return; + if ( junctionStyleConfigState?.styleConfig.classificationMethod === "custom_breaks" @@ -428,7 +443,7 @@ const StyleEditorPanel: React.FC = ({ breaks.push(breaks[breaks.length - 1] ?? 0); } else { const calc = calculateClassification( - currentJunctionCalData.map((d) => d.value), + dataValues, segments, styleConfig.classificationMethod ); @@ -439,12 +454,14 @@ const StyleEditorPanel: React.FC = ({ return; } // 计算最大最小值,判断是否包含并插入 breaks - const data = currentJunctionCalData.map((d) => d.value); const min_val = Math.max( - data.reduce((min, val) => Math.min(min, val), Infinity), + dataValues.reduce((min, val) => Math.min(min, val), Infinity), 0 ); - const max_val = data.reduce((max, val) => Math.max(max, val), -Infinity); + const max_val = dataValues.reduce( + (max, val) => Math.max(max, val), + -Infinity + ); if (breaks.includes(min_val) === false) { breaks.push(min_val); breaks.sort((a, b) => a - b); @@ -459,8 +476,8 @@ const StyleEditorPanel: React.FC = ({ } } else if ( layerType === "pipes" && - currentPipeCalData && - currentPipeCalData.length > 0 + ((currentPipeCalData && currentPipeCalData.length > 0) || + (isDiameter && diameterRange)) ) { // 应用管道样式 let pipeStyleConfigState = layerStyleStates.find( @@ -469,6 +486,14 @@ const StyleEditorPanel: React.FC = ({ // 更新管道数据属性 const segments = pipeStyleConfigState?.styleConfig.segments ?? 5; let breaks: number[] = []; + + const dataValues = + isDiameter && diameterRange + ? [diameterRange[0], diameterRange[1]] + : currentPipeCalData?.map((d: any) => d.value) || []; + + if (dataValues.length === 0) return; + if ( pipeStyleConfigState?.styleConfig.classificationMethod === "custom_breaks" @@ -485,7 +510,7 @@ const StyleEditorPanel: React.FC = ({ breaks.push(breaks[breaks.length - 1] ?? 0); } else { const calc = calculateClassification( - currentPipeCalData.map((d) => d.value), + dataValues, segments, styleConfig.classificationMethod ); @@ -496,12 +521,14 @@ const StyleEditorPanel: React.FC = ({ return; } // 计算最大最小值,判断是否包含并插入 breaks - const data = currentPipeCalData.map((d) => d.value); const min_val = Math.max( - data.reduce((min, val) => Math.min(min, val), Infinity), + dataValues.reduce((min, val) => Math.min(min, val), Infinity), 0 ); - const max_val = data.reduce((max, val) => Math.max(max, val), -Infinity); + const max_val = dataValues.reduce( + (max, val) => Math.max(max, val), + -Infinity + ); if (breaks.includes(min_val) === false) { breaks.push(min_val); breaks.sort((a, b) => a - b); @@ -883,16 +910,25 @@ const StyleEditorPanel: React.FC = ({ prevStyleUpdateTriggerRef.current = styleUpdateTrigger; const updateJunctionStyle = () => { - if (!currentJunctionCalData) return; const junctionStyleConfigState = layerStyleStates.find( (s) => s.layerId === "junctions" ); + const isElevation = + junctionStyleConfigState?.styleConfig.property === "elevation"; + // setStyle() 会清除渲染器缓存,这是闪烁的主要原因 WebGLVectorTile.js:114-118 // 尝试考虑使用 updateStyleVariables() 更新 applyClassificationStyle( "junctions", junctionStyleConfigState?.styleConfig ); + + if (isElevation) { + removeVectorTileSourceLoadedEvent("junctions"); + return; + } + + if (!currentJunctionCalData) return; // 更新现有的 VectorTileSource updateVectorTileSource(junctionText, currentJunctionCalData); // 移除旧的监听器,并添加新的监听器 @@ -904,11 +940,20 @@ const StyleEditorPanel: React.FC = ({ ); }; const updatePipeStyle = () => { - if (!currentPipeCalData) return; const pipeStyleConfigState = layerStyleStates.find( (s) => s.layerId === "pipes" ); + const isDiameter = + pipeStyleConfigState?.styleConfig.property === "diameter"; + applyClassificationStyle("pipes", pipeStyleConfigState?.styleConfig); + + if (isDiameter) { + removeVectorTileSourceLoadedEvent("pipes"); + return; + } + + if (!currentPipeCalData) return; // 更新现有的 VectorTileSource updateVectorTileSource(pipeText, currentPipeCalData); // 移除旧的监听器,并添加新的监听器 @@ -923,14 +968,21 @@ const StyleEditorPanel: React.FC = ({ } return; } + + const isElevation = junctionText === "elevation"; + const isDiameter = pipeText === "diameter"; + if ( applyJunctionStyle && - currentJunctionCalData && - currentJunctionCalData.length > 0 + ((currentJunctionCalData && currentJunctionCalData.length > 0) || + isElevation) ) { updateJunctionStyle(); } - if (applyPipeStyle && currentPipeCalData && currentPipeCalData.length > 0) { + if ( + applyPipeStyle && + ((currentPipeCalData && currentPipeCalData.length > 0) || isDiameter) + ) { updatePipeStyle(); } if (!applyJunctionStyle) { @@ -945,6 +997,8 @@ const StyleEditorPanel: React.FC = ({ applyPipeStyle, currentJunctionCalData, currentPipeCalData, + elevationRange, + diameterRange, ]); // 获取地图中的矢量图层,用于选择图层选项 @@ -1018,10 +1072,21 @@ const StyleEditorPanel: React.FC = ({ const selectedLayerId = selectedRenderLayer?.get("value"); let dataArr: number[] = []; - if (selectedLayerId === "junctions" && currentJunctionCalData) - dataArr = currentJunctionCalData.map((d) => d.value); - else if (selectedLayerId === "pipes" && currentPipeCalData) - dataArr = currentPipeCalData.map((d) => d.value); + + const isElevation = + selectedLayerId === "junctions" && styleConfig.property === "elevation"; + const isDiameter = + selectedLayerId === "pipes" && styleConfig.property === "diameter"; + + if (isElevation && elevationRange) { + dataArr = [elevationRange[0], elevationRange[1]]; + } else if (isDiameter && diameterRange) { + dataArr = [diameterRange[0], diameterRange[1]]; + } else if (selectedLayerId === "junctions" && currentJunctionCalData) { + dataArr = currentJunctionCalData.map((d: any) => d.value); + } else if (selectedLayerId === "pipes" && currentPipeCalData) { + dataArr = currentPipeCalData.map((d: any) => d.value); + } let defaultBreaks: number[] = Array.from({ length: numBreaks }, () => 0); if (dataArr && dataArr.length > 0) { @@ -1041,9 +1106,12 @@ const StyleEditorPanel: React.FC = ({ }, [ styleConfig.classificationMethod, styleConfig.segments, + styleConfig.property, selectedRenderLayer, currentJunctionCalData, currentPipeCalData, + elevationRange, + diameterRange, ]); // 初始化或调整自定义颜色数组长度 diff --git a/src/app/OlMap/MapComponent.tsx b/src/app/OlMap/MapComponent.tsx index fdd2fc3..497e7ce 100644 --- a/src/app/OlMap/MapComponent.tsx +++ b/src/app/OlMap/MapComponent.tsx @@ -63,6 +63,8 @@ interface DataContextType { setPipeText?: React.Dispatch>; setContours?: React.Dispatch>; deckLayer?: DeckLayer; + diameterRange?: [number, number]; + elevationRange?: [number, number]; } // 跨组件传递 @@ -130,6 +132,13 @@ const MapComponent: React.FC = ({ children }) => { const [showWaterflowLayer, setShowWaterflowLayer] = useState(false); // 控制等高线图层显示 const [currentZoom, setCurrentZoom] = useState(11); // 当前缩放级别 + const [diameterRange, setDiameterRange] = useState< + [number, number] | undefined + >(); + const [elevationRange, setElevationRange] = useState< + [number, number] | undefined + >(); + // 防抖更新函数 const debouncedUpdateData = useRef( debounce(() => { @@ -155,6 +164,24 @@ const MapComponent: React.FC = ({ children }) => { }); if (uniqueNewData.length > 0) { setJunctionDataState((prev) => prev.concat(uniqueNewData)); + setElevationRange((prev) => { + const elevations = uniqueNewData + .map((d) => d.elevation) + .filter((v) => typeof v === "number"); + if (elevations.length === 0) return prev; + + let newMin = elevations[0]; + let newMax = elevations[0]; + for (let i = 1; i < elevations.length; i++) { + if (elevations[i] < newMin) newMin = elevations[i]; + if (elevations[i] > newMax) newMax = elevations[i]; + } + + if (!prev) { + return [newMin, newMax]; + } + return [Math.min(prev[0], newMin), Math.max(prev[1], newMax)]; + }); } }; const setPipeData = (newData: any[]) => { @@ -168,6 +195,24 @@ const MapComponent: React.FC = ({ children }) => { }); if (uniqueNewData.length > 0) { setPipeDataState((prev) => prev.concat(uniqueNewData)); + setDiameterRange((prev) => { + const diameters = uniqueNewData + .map((d) => d.diameter) + .filter((v) => typeof v === "number"); + if (diameters.length === 0) return prev; + + let newMin = diameters[0]; + let newMax = diameters[0]; + for (let i = 1; i < diameters.length; i++) { + if (diameters[i] < newMin) newMin = diameters[i]; + if (diameters[i] > newMax) newMax = diameters[i]; + } + + if (!prev) { + return [newMin, newMax]; + } + return [Math.min(prev[0], newMin), Math.max(prev[1], newMax)]; + }); } }; // 配置地图数据源、图层和样式 @@ -295,8 +340,8 @@ const MapComponent: React.FC = ({ children }) => { // { name: "需求量", value: "demand" }, { name: "高程", value: "elevation" }, // 计算属性 - { name: "实际需求量", value: "actualdemand" }, - { name: "水头", value: "head" }, + { name: "实际需水量", value: "actual_demand" }, + { name: "水头", value: "total_head" }, { name: "压力", value: "pressure" }, { name: "水质", value: "quality" }, ], @@ -985,6 +1030,8 @@ const MapComponent: React.FC = ({ children }) => { pipeText, setContours, deckLayer, + diameterRange, + elevationRange, }} >