新增等值线图样式自定义功能

This commit is contained in:
JIANG
2025-12-19 11:43:33 +08:00
parent 9953fc5bd8
commit ac966242e7
4 changed files with 119 additions and 69 deletions

View File

@@ -1,6 +1,5 @@
import React, { useState, useEffect, useCallback } from "react"; import React, { useState, useEffect, useCallback } from "react";
import { useData, useMap } from "../MapComponent"; import { useData, useMap } from "../MapComponent";
import { Layer } from "ol/layer";
import { Checkbox, FormControlLabel } from "@mui/material"; import { Checkbox, FormControlLabel } from "@mui/material";
import WebGLVectorTileLayer from "ol/layer/WebGLVectorTile"; import WebGLVectorTileLayer from "ol/layer/WebGLVectorTile";
import VectorLayer from "ol/layer/Vector"; import VectorLayer from "ol/layer/Vector";

View File

@@ -193,7 +193,7 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
setWaterflowLayerAvailable, setWaterflowLayerAvailable,
setJunctionText, setJunctionText,
setPipeText, setPipeText,
deckLayer, setContours,
} = data; } = data;
const { open } = useNotification(); const { open } = useNotification();
@@ -444,8 +444,10 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
breaks.push(max_val); breaks.push(max_val);
breaks.sort((a, b) => a - b); breaks.sort((a, b) => a - b);
} }
if (junctionStyleConfigState) if (junctionStyleConfigState) {
applyLayerStyle(junctionStyleConfigState, breaks); applyLayerStyle(junctionStyleConfigState, breaks);
applyContourLayerStyle(junctionStyleConfigState, breaks);
}
} else if ( } else if (
layerType === "pipes" && layerType === "pipes" &&
currentPipeCalData && currentPipeCalData &&
@@ -661,6 +663,67 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
saveLayerStyle(renderLayer.get("value"), legendConfig); saveLayerStyle(renderLayer.get("value"), legendConfig);
}, 100); }, 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(() => { const resetStyle = useCallback(() => {
if (!selectedRenderLayer) return; if (!selectedRenderLayer) return;
@@ -679,6 +742,7 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
setApplyJunctionStyle(false); setApplyJunctionStyle(false);
if (setShowJunctionTextLayer) setShowJunctionTextLayer(false); if (setShowJunctionTextLayer) setShowJunctionTextLayer(false);
if (setJunctionText) setJunctionText(""); if (setJunctionText) setJunctionText("");
setContours && setContours([]);
setContourLayerAvailable && setContourLayerAvailable(false); setContourLayerAvailable && setContourLayerAvailable(false);
} else if (layerId === "pipes") { } else if (layerId === "pipes") {
setApplyPipeStyle(false); setApplyPipeStyle(false);

View File

@@ -62,7 +62,8 @@ interface DataContextType {
pipeText: string; pipeText: string;
setJunctionText?: React.Dispatch<React.SetStateAction<string>>; setJunctionText?: React.Dispatch<React.SetStateAction<string>>;
setPipeText?: React.Dispatch<React.SetStateAction<string>>; setPipeText?: React.Dispatch<React.SetStateAction<string>>;
deckLayer?: DeckLayer; // DeckLayer 实例 setContours?: React.Dispatch<React.SetStateAction<any[]>>;
deckLayer?: DeckLayer;
} }
// 跨组件传递 // 跨组件传递
@@ -122,6 +123,7 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
const [showContourLayer, setShowContourLayer] = useState(false); // 控制等高线图层显示 const [showContourLayer, setShowContourLayer] = useState(false); // 控制等高线图层显示
const [junctionText, setJunctionText] = useState("pressure"); const [junctionText, setJunctionText] = useState("pressure");
const [pipeText, setPipeText] = useState("flow"); const [pipeText, setPipeText] = useState("flow");
const [contours, setContours] = useState<any[]>([]);
const flowAnimation = useRef(false); // 添加动画控制标志 const flowAnimation = useRef(false); // 添加动画控制标志
const [isContourLayerAvailable, setContourLayerAvailable] = useState(false); // 控制等高线图层显示 const [isContourLayerAvailable, setContourLayerAvailable] = useState(false); // 控制等高线图层显示
const [isWaterflowLayerAvailable, setWaterflowLayerAvailable] = const [isWaterflowLayerAvailable, setWaterflowLayerAvailable] =
@@ -773,7 +775,6 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
// outlineColor: [255, 255, 255, 220], // outlineColor: [255, 255, 255, 220],
}); });
const ALPHA = 102;
const contourLayer = new ContourLayer({ const contourLayer = new ContourLayer({
id: "junctionContourLayer", id: "junctionContourLayer",
name: "等值线", name: "等值线",
@@ -781,15 +782,7 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
aggregation: "MEAN", aggregation: "MEAN",
cellSize: 600, cellSize: 600,
strokeWidth: 0, strokeWidth: 0,
contours: [ contours: 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 },
],
getPosition: (d) => d.position, getPosition: (d) => d.position,
getWeight: (d: any) => getWeight: (d: any) =>
(d[junctionText] as number) < 0 ? 0 : (d[junctionText] as number), (d[junctionText] as number) < 0 ? 0 : (d[junctionText] as number),
@@ -821,7 +814,11 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
showContourLayer, showContourLayer,
junctionText, junctionText,
pipeText, pipeText,
contours,
]); ]);
useEffect(() => {
console.log("Contour Layer Available:", isContourLayerAvailable);
}, [isContourLayerAvailable]);
// 控制流动动画开关 // 控制流动动画开关
useEffect(() => { useEffect(() => {
if (pipeText === "flow" && currentPipeCalData.length > 0) { if (pipeText === "flow" && currentPipeCalData.length > 0) {
@@ -879,11 +876,11 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
trailLength: 2, // 水流尾迹淡出时间 trailLength: 2, // 水流尾迹淡出时间
currentTime: currentTime, currentTime: currentTime,
}); });
if (deckLayer.getDeckLayerById("waterflowLayer")) { // if (deckLayer.getDeckLayerById("waterflowLayer")) {
deckLayer.updateDeckLayer("waterflowLayer", waterflowLayer); // deckLayer.updateDeckLayer("waterflowLayer", waterflowLayer);
} else { // } else {
deckLayer.addDeckLayer(waterflowLayer); // deckLayer.addDeckLayer(waterflowLayer);
} // }
// 继续请求动画帧,每帧执行一次函数 // 继续请求动画帧,每帧执行一次函数
animationFrameId = requestAnimationFrame(animate); animationFrameId = requestAnimationFrame(animate);
}; };
@@ -917,41 +914,45 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
const linkMap: Map<string, any> = new Map( const linkMap: Map<string, any> = new Map(
currentPipeCalData.map((r: any) => [r.ID, r]) currentPipeCalData.map((r: any) => [r.ID, r])
); );
if (nodeMap.size > 0) {
// 更新junctionData // 更新junctionData
setJunctionDataState((prev: any[]) => setJunctionDataState((prev: any[]) =>
prev.map((j) => { prev.map((j) => {
const record = nodeMap.get(j.id); const record = nodeMap.get(j.id);
if (record) { if (record) {
return { return {
...j, ...j,
[junctionProperties]: record.value, [junctionProperties]: record.value,
}; };
} }
return j; return j;
}) })
); );
}
// 更新pipeData if (linkMap.size > 0) {
setPipeDataState((prev: any[]) => // 更新pipeData
prev.map((p) => { setPipeDataState((prev: any[]) =>
const record = linkMap.get(p.id); prev.map((p) => {
if (record) { const record = linkMap.get(p.id);
return { if (record) {
...p, return {
flowFlag: pipeProperties === "flow" && record.value < 0 ? -1 : 1, ...p,
path: flowFlag: pipeProperties === "flow" && record.value < 0 ? -1 : 1,
pipeProperties === "flow" && record.value < 0 && p.flowFlag > 0 path:
? [...p.path].reverse() pipeProperties === "flow" && record.value < 0 && p.flowFlag > 0
: p.path, ? [...p.path].reverse()
// 流量数值 : p.path,
[pipeProperties]: // 流量数值
pipeProperties === "flow" ? Math.abs(record.value) : record.value, [pipeProperties]:
}; pipeProperties === "flow"
} ? Math.abs(record.value)
return p; : record.value,
}) };
); }
return p;
})
);
}
}, [currentJunctionCalData, currentPipeCalData]); }, [currentJunctionCalData, currentPipeCalData]);
return ( return (
<> <>
@@ -981,6 +982,7 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
setPipeText, setPipeText,
junctionText, junctionText,
pipeText, pipeText,
setContours,
deckLayer, deckLayer,
}} }}
> >

View File

@@ -111,21 +111,6 @@ export class DeckLayer extends Layer {
// 替换为新的 layer 实例 // 替换为新的 layer 实例
return layerOrProps; 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 }); this.deck.setProps({ layers: updatedLayers });