diff --git a/src/app/OlMap/Controls/BaseLayers.tsx b/src/app/OlMap/Controls/BaseLayers.tsx
index 393d936..6dfc9e6 100644
--- a/src/app/OlMap/Controls/BaseLayers.tsx
+++ b/src/app/OlMap/Controls/BaseLayers.tsx
@@ -214,7 +214,7 @@ const BaseLayers: React.FC = () => {
{isExpanded && (
{
@@ -104,8 +108,10 @@ const StyleEditorPanel: React.FC = () => {
return
Loading...
; // 或其他占位符
}
const {
- junctionData,
- pipeData,
+ currentJunctionCalData,
+ currentPipeCalData,
+ junctionText,
+ pipeText,
setShowJunctionText,
setShowPipeText,
setJunctionText,
@@ -114,6 +120,8 @@ const StyleEditorPanel: React.FC = () => {
const [applyJunctionStyle, setApplyJunctionStyle] = useState(false);
const [applyPipeStyle, setApplyPipeStyle] = useState(false);
+ const [styleUpdateTrigger, setStyleUpdateTrigger] = useState(0); // 用于触发样式更新的状态
+ const prevStyleUpdateTriggerRef = useRef
(0);
const [renderLayers, setRenderLayers] = useState([]);
const [selectedRenderLayer, setSelectedRenderLayer] =
@@ -131,7 +139,7 @@ const StyleEditorPanel: React.FC = () => {
classificationMethod: "pretty_breaks",
segments: 5,
minSize: 4,
- maxSize: 15,
+ maxSize: 12,
minStrokeWidth: 2,
maxStrokeWidth: 6,
fixedStrokeWidth: 3,
@@ -142,17 +150,6 @@ const StyleEditorPanel: React.FC = () => {
opacity: 0.9,
adjustWidthByProperty: true,
});
- const [legendStyleConfig, setLegendStyleConfig] = useState(
- {
- layerName: "",
- layerId: "",
- property: "",
- colors: [],
- type: "point",
- dimensions: [],
- breaks: [],
- }
- );
// 样式状态管理 - 存储多个图层的样式状态
const [layerStyleStates, setLayerStyleStates] = useState(
[]
@@ -185,41 +182,141 @@ const StyleEditorPanel: React.FC = () => {
},
[gradientPaletteIndex, parseColor]
);
- // 应用分类样式
- const setStyleState = (layer: any) => {
- if (
- layer.get("value") !== undefined &&
- styleConfig.property !== undefined
- ) {
+ // 保存当前图层的样式状态
+ const saveLayerStyle = useCallback(
+ (layerId?: string, legendConfig?: LegendStyleConfig) => {
+ if (!selectedRenderLayer || !styleConfig.property) {
+ console.warn("无法保存样式:缺少必要的图层或样式配置");
+ return;
+ }
+ if (!layerId) return; // 如果没有传入 layerId,则不保存
+ const layerName = selectedRenderLayer.get("name") || `图层${layerId}`;
+ // 如果没有传入图例配置,则创建一个默认的空配置
+ const finalLegendConfig: LegendStyleConfig = legendConfig || {
+ layerId,
+ layerName,
+ property: styleConfig.property,
+ colors: [],
+ type: "point",
+ dimensions: [],
+ breaks: [],
+ };
+ const newStyleState: LayerStyleState = {
+ layerId,
+ layerName,
+ styleConfig: { ...styleConfig },
+ legendConfig: { ...finalLegendConfig },
+ isActive: true,
+ };
+
+ setLayerStyleStates((prev) => {
+ // 检查是否已存在该图层的样式状态
+ const existingIndex = prev.findIndex(
+ (state) => state.layerId === layerId
+ );
+
+ if (existingIndex !== -1) {
+ // 更新已存在的状态
+ const updated = [...prev];
+ updated[existingIndex] = newStyleState;
+ return updated;
+ } else {
+ // 添加新的状态
+ return [...prev, newStyleState];
+ }
+ });
+ },
+ [selectedRenderLayer, styleConfig]
+ );
+ // 设置分类样式参数,触发样式应用
+ const setStyleState = () => {
+ if (!selectedRenderLayer) return;
+ const layerId = selectedRenderLayer.get("value");
+ const property = styleConfig.property;
+ if (layerId !== undefined && property !== undefined) {
// 更新文字标签设置
- if (layer.get("value") === "junctions") {
+ if (layerId === "junctions") {
if (setJunctionText && setShowJunctionText) {
- setJunctionText(styleConfig.property);
+ setJunctionText(property);
setShowJunctionText(styleConfig.showLabels);
setApplyJunctionStyle(true);
+ saveLayerStyle(layerId);
}
}
- if (layer.get("value") === "pipes") {
+ if (layerId === "pipes") {
+ console.log(styleConfig);
if (setPipeText && setShowPipeText) {
- setPipeText(styleConfig.property);
+ setPipeText(property);
setShowPipeText(styleConfig.showLabels);
setApplyPipeStyle(true);
+ saveLayerStyle(layerId);
}
}
+ // 触发样式更新
+ setStyleUpdateTrigger((prev) => prev + 1);
}
};
- const applyStyle = (layerId: string, breaks?: number[]) => {
+ // 计算分类样式,并应用到对应图层
+ const applyClassificationStyle = (
+ layerType: "junctions" | "pipes",
+ styleConfig: any
+ ) => {
+ if (
+ layerType === "junctions" &&
+ currentJunctionCalData &&
+ currentJunctionCalData.length > 0
+ ) {
+ // 应用节点样式
+ let junctionStyleConfigState = layerStyleStates.find(
+ (s) => s.layerId === "junctions"
+ );
+
+ // 更新节点数据属性
+ const segments = junctionStyleConfigState?.styleConfig.segments ?? 5;
+ const breaks = calculateClassification(
+ currentJunctionCalData.map((d) => d.value),
+ segments,
+ styleConfig.classificationMethod
+ );
+ if (breaks.length === 0) {
+ console.warn("计算的 breaks 为空,无法应用样式");
+ return;
+ }
+ if (junctionStyleConfigState)
+ applyLayerStyle(junctionStyleConfigState, breaks);
+ } else if (
+ layerType === "pipes" &&
+ currentPipeCalData &&
+ currentPipeCalData.length > 0
+ ) {
+ // 应用管道样式
+ let pipeStyleConfigState = layerStyleStates.find(
+ (s) => s.layerId === "pipes"
+ );
+ // 更新管道数据属性
+ const segments = pipeStyleConfigState?.styleConfig.segments ?? 5;
+ const breaks = calculateClassification(
+ currentPipeCalData.map((d) => d.value),
+ segments,
+ styleConfig.classificationMethod
+ );
+ if (pipeStyleConfigState) applyLayerStyle(pipeStyleConfigState, breaks);
+ }
+ };
+ // 应用样式函数,传入 breaks 数据
+ const applyLayerStyle = (
+ layerStyleConfig: LayerStyleState,
+ breaks?: number[]
+ ) => {
// 使用传入的 breaks 数据
if (!breaks || breaks.length === 0) {
console.warn("没有有效的 breaks 数据");
return;
}
- const styleConfig = layerStyleStates.find(
- (s) => s.layerId === layerId
- )?.styleConfig;
- const selectedRenderLayer = renderLayers.find(
- (l) => l.get("id") === layerId
- );
+ const styleConfig = layerStyleConfig.styleConfig;
+ const selectedRenderLayer = renderLayers.filter((layer) => {
+ return layer.get("value") === layerStyleConfig.layerId;
+ })[0];
if (!selectedRenderLayer || !styleConfig?.property) return;
const layerType: string = selectedRenderLayer?.get("type");
const source = selectedRenderLayer.getSource();
@@ -257,26 +354,14 @@ const StyleEditorPanel: React.FC = () => {
(styleConfig.maxSize - styleConfig.minSize) * ratio
);
});
- // 创建图例配置对象
- const legendConfig: LegendStyleConfig = {
- layerName: selectedRenderLayer.get("name"),
- layerId: selectedRenderLayer.get("value"),
- property: selectedProperty.name,
- colors: colors,
- type: layerType,
- dimensions: dimensions,
- breaks: breaks,
- };
- // 更新图例配置
- setLegendStyleConfig(legendConfig);
// 动态生成颜色条件表达式
- const generateColorConditions = (): any[] => {
+ const generateColorConditions = (property: string): any[] => {
const conditions: any[] = ["case"];
for (let i = 0; i < breaks.length; i++) {
// 添加条件:属性值 <= 当前断点
- conditions.push(["<=", ["get", styleConfig.property], breaks[i]]);
+ conditions.push(["<=", ["get", property], breaks[i]]);
// 添加对应的颜色值
const colorObj = parseColor(colors[i]);
const color = `rgba(${colorObj.r}, ${colorObj.g}, ${colorObj.b}, ${styleConfig.opacity})`;
@@ -289,14 +374,12 @@ const StyleEditorPanel: React.FC = () => {
return conditions;
};
// 动态生成尺寸条件表达式
- const generateDimensionConditions = (): any[] => {
+ const generateDimensionConditions = (property: string): any[] => {
const conditions: any[] = ["case"];
-
for (let i = 0; i < breaks.length; i++) {
- conditions.push(["<=", ["get", styleConfig.property], breaks[i]]);
+ conditions.push(["<=", ["get", property], breaks[i]]);
conditions.push(dimensions[i]);
}
-
conditions.push(dimensions[dimensions.length - 1]);
return conditions;
};
@@ -305,25 +388,29 @@ const StyleEditorPanel: React.FC = () => {
// 根据图层类型设置不同的样式属性
if (layerType === "linestring") {
- dynamicStyle["stroke-color"] = generateColorConditions();
- dynamicStyle["stroke-width"] = generateDimensionConditions();
+ dynamicStyle["stroke-color"] = generateColorConditions(
+ styleConfig.property
+ );
+ dynamicStyle["stroke-width"] = generateDimensionConditions(
+ styleConfig.property
+ );
} else if (layerType === "point") {
- dynamicStyle["circle-fill-color"] = generateColorConditions();
- dynamicStyle["circle-radius"] = generateDimensionConditions();
- dynamicStyle["circle-stroke-color"] = generateColorConditions();
+ dynamicStyle["circle-fill-color"] = generateColorConditions(
+ styleConfig.property
+ );
+ dynamicStyle["circle-radius"] = generateDimensionConditions(
+ styleConfig.property
+ );
+ dynamicStyle["circle-stroke-color"] = generateColorConditions(
+ styleConfig.property
+ );
dynamicStyle["circle-stroke-width"] = 2;
- } else {
- // 面要素
- dynamicStyle["fill-color"] = generateColorConditions();
- dynamicStyle["stroke-color"] = generateColorConditions();
- dynamicStyle["stroke-width"] = generateDimensionConditions();
}
selectedRenderLayer.setStyle(dynamicStyle);
- // console.log(map?.getAllLayers());
// 创建图例配置对象
- const finalLegendConfig: LegendStyleConfig = {
+ const legendConfig: LegendStyleConfig = {
layerName: selectedRenderLayer.get("name"),
layerId: selectedRenderLayer.get("value"),
property: selectedProperty.name,
@@ -332,28 +419,29 @@ const StyleEditorPanel: React.FC = () => {
dimensions: dimensions,
breaks: breaks,
};
-
- // 更新图例配置
- setLegendStyleConfig(finalLegendConfig);
-
// 自动保存样式状态,直接传入图例配置
setTimeout(() => {
- saveLayerStyle(finalLegendConfig);
+ saveLayerStyle(selectedRenderLayer.get("value"), legendConfig);
}, 100);
};
// 重置样式
const resetStyle = useCallback(() => {
if (!selectedRenderLayer) return;
-
// 重置 WebGL 图层样式
const defaultFlatStyle: FlatStyleLike = {
- "stroke-width": 2,
- "stroke-color": `rgba(51, 153, 204, 0.9)`,
- "fill-color": `rgba(51, 153, 204, 0.5)`,
- "circle-radius": 7,
- "circle-stroke-width": 2,
- "circle-stroke-color": `rgba(51, 153, 204, 0.9)`,
- "circle-fill-color": `rgba(51, 153, 204, 0.5)`,
+ "stroke-width": 3,
+ "stroke-color": "rgba(51, 153, 204, 0.9)",
+ "circle-fill-color": "rgba(255,255,255,0.4)",
+ "circle-stroke-color": "rgba(255,255,255,0.9)",
+ "circle-radius": [
+ "interpolate",
+ ["linear"],
+ ["zoom"],
+ 12,
+ 1, // 在缩放级别 12 时,圆形半径为 1px
+ 24,
+ 12, // 在缩放级别 24 时,圆形半径为 12px
+ ],
};
selectedRenderLayer.setStyle(defaultFlatStyle);
@@ -366,85 +454,190 @@ const StyleEditorPanel: React.FC = () => {
// 重置样式应用状态
if (layerId === "junctions") {
setApplyJunctionStyle(false);
+ if (setShowJunctionText) setShowJunctionText(false);
+ if (setJunctionText) setJunctionText("");
} else if (layerId === "pipes") {
setApplyPipeStyle(false);
+ if (setShowPipeText) setShowPipeText(false);
+ if (setPipeText) setPipeText("");
}
}
}, [selectedRenderLayer]);
+ // 更新当前 VectorTileSource 中的所有缓冲要素属性
+ const updateVectorTileSource = (property: string, data: any[]) => {
+ if (!map) return;
+ const vectorTileSources = map
+ .getAllLayers()
+ .filter((layer) => layer instanceof WebGLVectorTileLayer)
+ .map((layer) => layer.getSource() as VectorTileSource)
+ .filter((source) => source);
+
+ if (!vectorTileSources.length) return;
+
+ // 创建 id 到 value 的映射
+ const dataMap = new Map();
+ data.forEach((d: any) => {
+ dataMap.set(d.ID, d.value || 0);
+ });
+
+ // 直接遍历所有瓦片和要素,无需分批处理
+ vectorTileSources.forEach((vectorTileSource) => {
+ const sourceTiles = vectorTileSource.sourceTiles_;
+
+ Object.values(sourceTiles).forEach((vectorTile) => {
+ const renderFeatures = vectorTile.getFeatures();
+ if (!renderFeatures || renderFeatures.length === 0) return;
+ // 直接更新要素属性
+ renderFeatures.forEach((renderFeature) => {
+ const featureId = renderFeature.get("id");
+ const value = dataMap.get(featureId);
+ if (value !== undefined) {
+ (renderFeature as any).properties_[property] = value;
+ }
+ });
+ });
+ });
+ };
+ // 新增事件,监听 VectorTileSource 的 tileloadend 事件,为新增瓦片数据动态更新要素属性
+ const [tileLoadListeners, setTileLoadListeners] = useState<
+ Map void>
+ >(new Map());
+ const attachVectorTileSourceLoadedEvent = (
+ layerId: string,
+ property: string,
+ data: any[]
+ ) => {
+ if (!map) return;
+ const vectorTileSource = map
+ .getAllLayers()
+ .filter((layer) => layer.get("value") === layerId)
+ .map((layer) => layer.getSource() as VectorTileSource)
+ .filter((source) => source)[0];
+ if (!vectorTileSource) return;
+ // 创建 id 到 value 的映射
+ const dataMap = new Map();
+ data.forEach((d: any) => {
+ dataMap.set(d.ID, d.value || 0);
+ });
+ // 新增监听器并保存
+ const newListeners = new Map void>();
+
+ 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) => {
+ const featureId = renderFeature.get("id");
+ const value = dataMap.get(featureId);
+ if (value !== undefined) {
+ (renderFeature as any).properties_[property] = value;
+ }
+ });
+ }
+ } catch (error) {
+ console.error("Error processing tile load event:", error);
+ }
+ };
+
+ vectorTileSource.on("tileloadend", listener);
+ newListeners.set(vectorTileSource, listener);
+ setTileLoadListeners(newListeners);
+ };
+ // 新增函数:取消对应 layerId 已添加的 on 事件
+ const removeVectorTileSourceLoadedEvent = (layerId: string) => {
+ if (!map) return;
+ const vectorTileSource = map
+ .getAllLayers()
+ .filter((layer) => layer.get("value") === layerId)
+ .map((layer) => layer.getSource() as VectorTileSource)
+ .filter((source) => source)[0];
+ if (!vectorTileSource) return;
+ const listener = tileLoadListeners.get(vectorTileSource);
+ if (listener) {
+ vectorTileSource.un("tileloadend", listener);
+ setTileLoadListeners((prev) => {
+ const newMap = new Map(prev);
+ newMap.delete(vectorTileSource);
+ return newMap;
+ });
+ }
+ };
+
+ // 监听数据变化,重新应用样式。由样式应用按钮触发,或由数据变化触发
useEffect(() => {
- if (applyJunctionStyle && junctionData.length > 0) {
- // 应用节点样式
+ // 判断此次触发是否由用户点击“应用”按钮引起
+ const isUserTrigger =
+ styleUpdateTrigger !== prevStyleUpdateTriggerRef.current;
+ // 更新 prevStyleUpdateTriggerRef
+ prevStyleUpdateTriggerRef.current = styleUpdateTrigger;
+
+ const updateJunctionStyle = () => {
+ if (!currentJunctionCalData) return;
const junctionStyleConfigState = layerStyleStates.find(
(s) => s.layerId === "junctions"
);
- if (!junctionStyleConfigState) return;
- const segments = junctionStyleConfigState?.styleConfig.segments;
- const breaks = calculateClassification(
- junctionData,
- segments,
- styleConfig.classificationMethod
+ applyClassificationStyle(
+ "junctions",
+ junctionStyleConfigState?.styleConfig
);
- applyStyle(junctionStyleConfigState.layerId, breaks);
- }
- if (applyPipeStyle && pipeData.length > 0) {
- // 应用管道样式
+ // 更新现有的 VectorTileSource
+ updateVectorTileSource(junctionText, currentJunctionCalData);
+ // 移除旧的监听器,并添加新的监听器
+ removeVectorTileSourceLoadedEvent("junctions");
+ attachVectorTileSourceLoadedEvent(
+ "junctions",
+ junctionText,
+ currentJunctionCalData
+ );
+ };
+ const updatePipeStyle = () => {
+ if (!currentPipeCalData) return;
const pipeStyleConfigState = layerStyleStates.find(
(s) => s.layerId === "pipes"
);
- if (!pipeStyleConfigState) return;
- const segments = pipeStyleConfigState?.styleConfig.segments;
- const breaks = calculateClassification(
- pipeData,
- segments,
- styleConfig.classificationMethod
- );
- applyStyle(pipeStyleConfigState.layerId, breaks);
- }
- }, [junctionData, pipeData, applyJunctionStyle, applyPipeStyle]);
- // 样式状态管理功能
- // 保存当前图层的样式状态
- const saveLayerStyle = useCallback(
- (overrideLegendConfig?: LegendStyleConfig) => {
- if (!selectedRenderLayer || !styleConfig.property) {
- console.warn("无法保存样式:缺少必要的图层或样式配置");
- return;
+ applyClassificationStyle("pipes", pipeStyleConfigState?.styleConfig);
+ // 更新现有的 VectorTileSource
+ updateVectorTileSource(pipeText, currentPipeCalData);
+ // 移除旧的监听器,并添加新的监听器
+ removeVectorTileSourceLoadedEvent("pipes");
+ attachVectorTileSourceLoadedEvent("pipes", pipeText, currentPipeCalData);
+ };
+ if (isUserTrigger) {
+ if (selectedRenderLayer?.get("value") === "junctions") {
+ updateJunctionStyle();
+ } else if (selectedRenderLayer?.get("value") === "pipes") {
+ updatePipeStyle();
}
+ return;
+ }
+ if (
+ applyJunctionStyle &&
+ currentJunctionCalData &&
+ currentJunctionCalData.length > 0
+ ) {
+ updateJunctionStyle();
+ }
+ if (applyPipeStyle && currentPipeCalData && currentPipeCalData.length > 0) {
+ updatePipeStyle();
+ }
+ if (!applyJunctionStyle) {
+ removeVectorTileSourceLoadedEvent("junctions");
+ }
+ if (!applyPipeStyle) {
+ removeVectorTileSourceLoadedEvent("pipes");
+ }
+ }, [
+ styleUpdateTrigger,
+ applyJunctionStyle,
+ applyPipeStyle,
+ currentJunctionCalData,
+ currentPipeCalData,
+ ]);
- const layerId = selectedRenderLayer.get("value");
- const layerName = selectedRenderLayer.get("name") || `图层${layerId}`;
-
- // 使用传入的图例配置,或者使用当前状态的图例配置
- const finalLegendConfig = overrideLegendConfig || legendStyleConfig;
-
- const newStyleState: LayerStyleState = {
- layerId,
- layerName,
- styleConfig: { ...styleConfig },
- legendConfig: { ...finalLegendConfig },
- isActive: true,
- };
-
- setLayerStyleStates((prev) => {
- // 检查是否已存在该图层的样式状态
- const existingIndex = prev.findIndex(
- (state) => state.layerId === layerId
- );
-
- if (existingIndex !== -1) {
- // 更新已存在的状态
- const updated = [...prev];
- updated[existingIndex] = newStyleState;
- return updated;
- } else {
- // 添加新的状态
- return [...prev, newStyleState];
- }
- });
- },
- [selectedRenderLayer, styleConfig, legendStyleConfig]
- );
- // 获取地图中的矢量图层
+ // 获取地图中的矢量图层,用于选择图层选项
useEffect(() => {
if (!map) return;
@@ -461,18 +654,16 @@ const StyleEditorPanel: React.FC = () => {
updateVisibleLayers();
}, [map]);
- // 获取选中图层的属性
+ // 获取选中图层的属性,并检查是否有已缓存的样式状态
useEffect(() => {
// 如果没有矢量图层或没有选中图层,清空属性列表
if (!renderLayers || renderLayers.length === 0) {
setAvailableProperties([]);
- // console.log("没有可用的矢量图层");
return;
}
// 如果没有选中图层,清空属性列表
if (!selectedRenderLayer) {
setAvailableProperties([]);
- // console.log("没有选中的图层");
return;
}
@@ -491,11 +682,8 @@ const StyleEditorPanel: React.FC = () => {
const cachedStyleState = layerStyleStates.find(
(state) => state.layerId === layerId
);
-
if (cachedStyleState) {
setStyleConfig(cachedStyleState.styleConfig);
- setLegendStyleConfig(cachedStyleState.legendConfig);
- // console.log(`已自动恢复图层 ${cachedStyleState.layerName} 的样式状态`);
}
}, [renderLayers, selectedRenderLayer, map, renderLayers, layerStyleStates]);
// 同步属性状态
@@ -643,8 +831,8 @@ const StyleEditorPanel: React.FC = () => {
minSize: value as number,
}))
}
- min={5}
- max={15}
+ min={2}
+ max={8}
step={1}
size="small"
/>
@@ -661,8 +849,8 @@ const StyleEditorPanel: React.FC = () => {
maxSize: value as number,
}))
}
- min={20}
- max={30}
+ min={10}
+ max={16}
step={1}
size="small"
/>
@@ -966,7 +1154,7 @@ const StyleEditorPanel: React.FC = () => {
variant="contained"
color="primary"
onClick={() => {
- setStyleState(selectedRenderLayer);
+ setStyleState();
}}
disabled={!selectedRenderLayer || !styleConfig.property}
startIcon={}
@@ -989,7 +1177,7 @@ const StyleEditorPanel: React.FC = () => {
{/* 显示多图层图例 */}
{getActiveLegendConfigs().length > 0 && (
-