新增固有属性的样式计算

This commit is contained in:
JIANG
2025-12-29 11:17:43 +08:00
parent 187e8e93b4
commit 4b257aa959
2 changed files with 138 additions and 23 deletions

View File

@@ -195,6 +195,8 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
setJunctionText, setJunctionText,
setPipeText, setPipeText,
setContours, setContours,
diameterRange,
elevationRange,
} = data; } = data;
const { open } = useNotification(); const { open } = useNotification();
@@ -398,10 +400,15 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
layerType: "junctions" | "pipes", layerType: "junctions" | "pipes",
styleConfig: any styleConfig: any
) => { ) => {
const isElevation =
layerType === "junctions" && styleConfig.property === "elevation";
const isDiameter =
layerType === "pipes" && styleConfig.property === "diameter";
if ( if (
layerType === "junctions" && layerType === "junctions" &&
currentJunctionCalData && ((currentJunctionCalData && currentJunctionCalData.length > 0) ||
currentJunctionCalData.length > 0 (isElevation && elevationRange))
) { ) {
// 应用节点样式 // 应用节点样式
let junctionStyleConfigState = layerStyleStates.find( let junctionStyleConfigState = layerStyleStates.find(
@@ -411,6 +418,14 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
// 更新节点数据属性 // 更新节点数据属性
const segments = junctionStyleConfigState?.styleConfig.segments ?? 5; const segments = junctionStyleConfigState?.styleConfig.segments ?? 5;
let breaks: number[] = []; let breaks: number[] = [];
const dataValues =
isElevation && elevationRange
? [elevationRange[0], elevationRange[1]]
: currentJunctionCalData?.map((d: any) => d.value) || [];
if (dataValues.length === 0) return;
if ( if (
junctionStyleConfigState?.styleConfig.classificationMethod === junctionStyleConfigState?.styleConfig.classificationMethod ===
"custom_breaks" "custom_breaks"
@@ -428,7 +443,7 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
breaks.push(breaks[breaks.length - 1] ?? 0); breaks.push(breaks[breaks.length - 1] ?? 0);
} else { } else {
const calc = calculateClassification( const calc = calculateClassification(
currentJunctionCalData.map((d) => d.value), dataValues,
segments, segments,
styleConfig.classificationMethod styleConfig.classificationMethod
); );
@@ -439,12 +454,14 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
return; return;
} }
// 计算最大最小值,判断是否包含并插入 breaks // 计算最大最小值,判断是否包含并插入 breaks
const data = currentJunctionCalData.map((d) => d.value);
const min_val = Math.max( const min_val = Math.max(
data.reduce((min, val) => Math.min(min, val), Infinity), dataValues.reduce((min, val) => Math.min(min, val), Infinity),
0 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) { if (breaks.includes(min_val) === false) {
breaks.push(min_val); breaks.push(min_val);
breaks.sort((a, b) => a - b); breaks.sort((a, b) => a - b);
@@ -459,8 +476,8 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
} }
} else if ( } else if (
layerType === "pipes" && layerType === "pipes" &&
currentPipeCalData && ((currentPipeCalData && currentPipeCalData.length > 0) ||
currentPipeCalData.length > 0 (isDiameter && diameterRange))
) { ) {
// 应用管道样式 // 应用管道样式
let pipeStyleConfigState = layerStyleStates.find( let pipeStyleConfigState = layerStyleStates.find(
@@ -469,6 +486,14 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
// 更新管道数据属性 // 更新管道数据属性
const segments = pipeStyleConfigState?.styleConfig.segments ?? 5; const segments = pipeStyleConfigState?.styleConfig.segments ?? 5;
let breaks: number[] = []; let breaks: number[] = [];
const dataValues =
isDiameter && diameterRange
? [diameterRange[0], diameterRange[1]]
: currentPipeCalData?.map((d: any) => d.value) || [];
if (dataValues.length === 0) return;
if ( if (
pipeStyleConfigState?.styleConfig.classificationMethod === pipeStyleConfigState?.styleConfig.classificationMethod ===
"custom_breaks" "custom_breaks"
@@ -485,7 +510,7 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
breaks.push(breaks[breaks.length - 1] ?? 0); breaks.push(breaks[breaks.length - 1] ?? 0);
} else { } else {
const calc = calculateClassification( const calc = calculateClassification(
currentPipeCalData.map((d) => d.value), dataValues,
segments, segments,
styleConfig.classificationMethod styleConfig.classificationMethod
); );
@@ -496,12 +521,14 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
return; return;
} }
// 计算最大最小值,判断是否包含并插入 breaks // 计算最大最小值,判断是否包含并插入 breaks
const data = currentPipeCalData.map((d) => d.value);
const min_val = Math.max( const min_val = Math.max(
data.reduce((min, val) => Math.min(min, val), Infinity), dataValues.reduce((min, val) => Math.min(min, val), Infinity),
0 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) { if (breaks.includes(min_val) === false) {
breaks.push(min_val); breaks.push(min_val);
breaks.sort((a, b) => a - b); breaks.sort((a, b) => a - b);
@@ -883,16 +910,25 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
prevStyleUpdateTriggerRef.current = styleUpdateTrigger; prevStyleUpdateTriggerRef.current = styleUpdateTrigger;
const updateJunctionStyle = () => { const updateJunctionStyle = () => {
if (!currentJunctionCalData) return;
const junctionStyleConfigState = layerStyleStates.find( const junctionStyleConfigState = layerStyleStates.find(
(s) => s.layerId === "junctions" (s) => s.layerId === "junctions"
); );
const isElevation =
junctionStyleConfigState?.styleConfig.property === "elevation";
// setStyle() 会清除渲染器缓存,这是闪烁的主要原因 WebGLVectorTile.js:114-118 // setStyle() 会清除渲染器缓存,这是闪烁的主要原因 WebGLVectorTile.js:114-118
// 尝试考虑使用 updateStyleVariables() 更新 // 尝试考虑使用 updateStyleVariables() 更新
applyClassificationStyle( applyClassificationStyle(
"junctions", "junctions",
junctionStyleConfigState?.styleConfig junctionStyleConfigState?.styleConfig
); );
if (isElevation) {
removeVectorTileSourceLoadedEvent("junctions");
return;
}
if (!currentJunctionCalData) return;
// 更新现有的 VectorTileSource // 更新现有的 VectorTileSource
updateVectorTileSource(junctionText, currentJunctionCalData); updateVectorTileSource(junctionText, currentJunctionCalData);
// 移除旧的监听器,并添加新的监听器 // 移除旧的监听器,并添加新的监听器
@@ -904,11 +940,20 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
); );
}; };
const updatePipeStyle = () => { const updatePipeStyle = () => {
if (!currentPipeCalData) return;
const pipeStyleConfigState = layerStyleStates.find( const pipeStyleConfigState = layerStyleStates.find(
(s) => s.layerId === "pipes" (s) => s.layerId === "pipes"
); );
const isDiameter =
pipeStyleConfigState?.styleConfig.property === "diameter";
applyClassificationStyle("pipes", pipeStyleConfigState?.styleConfig); applyClassificationStyle("pipes", pipeStyleConfigState?.styleConfig);
if (isDiameter) {
removeVectorTileSourceLoadedEvent("pipes");
return;
}
if (!currentPipeCalData) return;
// 更新现有的 VectorTileSource // 更新现有的 VectorTileSource
updateVectorTileSource(pipeText, currentPipeCalData); updateVectorTileSource(pipeText, currentPipeCalData);
// 移除旧的监听器,并添加新的监听器 // 移除旧的监听器,并添加新的监听器
@@ -923,14 +968,21 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
} }
return; return;
} }
const isElevation = junctionText === "elevation";
const isDiameter = pipeText === "diameter";
if ( if (
applyJunctionStyle && applyJunctionStyle &&
currentJunctionCalData && ((currentJunctionCalData && currentJunctionCalData.length > 0) ||
currentJunctionCalData.length > 0 isElevation)
) { ) {
updateJunctionStyle(); updateJunctionStyle();
} }
if (applyPipeStyle && currentPipeCalData && currentPipeCalData.length > 0) { if (
applyPipeStyle &&
((currentPipeCalData && currentPipeCalData.length > 0) || isDiameter)
) {
updatePipeStyle(); updatePipeStyle();
} }
if (!applyJunctionStyle) { if (!applyJunctionStyle) {
@@ -945,6 +997,8 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
applyPipeStyle, applyPipeStyle,
currentJunctionCalData, currentJunctionCalData,
currentPipeCalData, currentPipeCalData,
elevationRange,
diameterRange,
]); ]);
// 获取地图中的矢量图层,用于选择图层选项 // 获取地图中的矢量图层,用于选择图层选项
@@ -1018,10 +1072,21 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
const selectedLayerId = selectedRenderLayer?.get("value"); const selectedLayerId = selectedRenderLayer?.get("value");
let dataArr: number[] = []; let dataArr: number[] = [];
if (selectedLayerId === "junctions" && currentJunctionCalData)
dataArr = currentJunctionCalData.map((d) => d.value); const isElevation =
else if (selectedLayerId === "pipes" && currentPipeCalData) selectedLayerId === "junctions" && styleConfig.property === "elevation";
dataArr = currentPipeCalData.map((d) => d.value); 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); let defaultBreaks: number[] = Array.from({ length: numBreaks }, () => 0);
if (dataArr && dataArr.length > 0) { if (dataArr && dataArr.length > 0) {
@@ -1041,9 +1106,12 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
}, [ }, [
styleConfig.classificationMethod, styleConfig.classificationMethod,
styleConfig.segments, styleConfig.segments,
styleConfig.property,
selectedRenderLayer, selectedRenderLayer,
currentJunctionCalData, currentJunctionCalData,
currentPipeCalData, currentPipeCalData,
elevationRange,
diameterRange,
]); ]);
// 初始化或调整自定义颜色数组长度 // 初始化或调整自定义颜色数组长度

View File

@@ -63,6 +63,8 @@ interface DataContextType {
setPipeText?: React.Dispatch<React.SetStateAction<string>>; setPipeText?: React.Dispatch<React.SetStateAction<string>>;
setContours?: React.Dispatch<React.SetStateAction<any[]>>; setContours?: React.Dispatch<React.SetStateAction<any[]>>;
deckLayer?: DeckLayer; deckLayer?: DeckLayer;
diameterRange?: [number, number];
elevationRange?: [number, number];
} }
// 跨组件传递 // 跨组件传递
@@ -130,6 +132,13 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
const [showWaterflowLayer, setShowWaterflowLayer] = useState(false); // 控制等高线图层显示 const [showWaterflowLayer, setShowWaterflowLayer] = useState(false); // 控制等高线图层显示
const [currentZoom, setCurrentZoom] = useState(11); // 当前缩放级别 const [currentZoom, setCurrentZoom] = useState(11); // 当前缩放级别
const [diameterRange, setDiameterRange] = useState<
[number, number] | undefined
>();
const [elevationRange, setElevationRange] = useState<
[number, number] | undefined
>();
// 防抖更新函数 // 防抖更新函数
const debouncedUpdateData = useRef( const debouncedUpdateData = useRef(
debounce(() => { debounce(() => {
@@ -155,6 +164,24 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
}); });
if (uniqueNewData.length > 0) { if (uniqueNewData.length > 0) {
setJunctionDataState((prev) => prev.concat(uniqueNewData)); 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[]) => { const setPipeData = (newData: any[]) => {
@@ -168,6 +195,24 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
}); });
if (uniqueNewData.length > 0) { if (uniqueNewData.length > 0) {
setPipeDataState((prev) => prev.concat(uniqueNewData)); 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<MapComponentProps> = ({ children }) => {
// { name: "需求量", value: "demand" }, // { name: "需求量", value: "demand" },
{ name: "高程", value: "elevation" }, { name: "高程", value: "elevation" },
// 计算属性 // 计算属性
{ name: "实际需量", value: "actualdemand" }, { name: "实际需量", value: "actual_demand" },
{ name: "水头", value: "head" }, { name: "水头", value: "total_head" },
{ name: "压力", value: "pressure" }, { name: "压力", value: "pressure" },
{ name: "水质", value: "quality" }, { name: "水质", value: "quality" },
], ],
@@ -985,6 +1030,8 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
pipeText, pipeText,
setContours, setContours,
deckLayer, deckLayer,
diameterRange,
elevationRange,
}} }}
> >
<MapContext.Provider value={map}> <MapContext.Provider value={map}>