更新图层控制逻辑
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useMap } from "../MapComponent";
|
||||
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";
|
||||
@@ -7,159 +7,168 @@ import VectorLayer from "ol/layer/Vector";
|
||||
import VectorTileLayer from "ol/layer/VectorTile";
|
||||
import { DeckLayer } from "@utils/layers";
|
||||
|
||||
// 定义统一的图层项接口
|
||||
interface LayerItem {
|
||||
id: string;
|
||||
name: string;
|
||||
visible: boolean;
|
||||
type: "ol" | "deck";
|
||||
layerRef: any; // OpenLayers Layer 实例或 deck.gl layer 对象
|
||||
}
|
||||
|
||||
const LayerControl: React.FC = () => {
|
||||
const map = useMap();
|
||||
const [layers, setLayers] = useState<Layer[]>([]);
|
||||
const [layerVisibilities, setLayerVisibilities] = useState<
|
||||
Map<Layer, boolean>
|
||||
>(new Map());
|
||||
const userChangedRef = React.useRef<Set<Layer>>(new Set());
|
||||
const data = useData();
|
||||
if (!data) return;
|
||||
const {
|
||||
deckLayer,
|
||||
isContourLayerAvailable,
|
||||
isWaterflowLayerAvailable,
|
||||
setShowContourLayer,
|
||||
flowAnimation,
|
||||
} = data;
|
||||
const [layerItems, setLayerItems] = useState<LayerItem[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!map) return;
|
||||
// 更新图层列表
|
||||
const updateLayers = useCallback(() => {
|
||||
if (!map || !data) return;
|
||||
const { deckLayer } = data;
|
||||
|
||||
const updateLayers = () => {
|
||||
const mapLayers = map
|
||||
.getLayers()
|
||||
.getArray()
|
||||
.filter(
|
||||
(layer) =>
|
||||
layer instanceof WebGLVectorTileLayer ||
|
||||
layer instanceof VectorTileLayer ||
|
||||
layer instanceof VectorLayer
|
||||
) as Layer[];
|
||||
const items: LayerItem[] = [];
|
||||
|
||||
// 查找包含 waterflowLayer 的 DeckLayer
|
||||
const deckFlowLayers = map
|
||||
.getLayers()
|
||||
.getArray()
|
||||
.filter((layer) => {
|
||||
if (layer instanceof DeckLayer) {
|
||||
const deckLayers = layer.getDeckLayers();
|
||||
// 检查是否包含 waterflowLayer
|
||||
return deckLayers.some((dl: any) => dl.id === "waterflowLayer");
|
||||
// 1. 获取 OpenLayers 图层
|
||||
const mapLayers = map.getLayers().getArray();
|
||||
mapLayers.forEach((layer) => {
|
||||
// 筛选特定类型的 OpenLayers 图层
|
||||
if (
|
||||
layer instanceof WebGLVectorTileLayer ||
|
||||
layer instanceof VectorTileLayer ||
|
||||
layer instanceof VectorLayer
|
||||
) {
|
||||
const value = layer.get("value");
|
||||
const name = layer.get("name");
|
||||
// 只有设置了 value (作为 ID) 的图层才会被纳入控制
|
||||
if (value) {
|
||||
items.push({
|
||||
id: value,
|
||||
name: name || value,
|
||||
visible: layer.getVisible(),
|
||||
type: "ol",
|
||||
layerRef: layer,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 2. 获取 DeckLayer 中的子图层
|
||||
if (deckLayer && deckLayer instanceof DeckLayer) {
|
||||
const deckLayers = deckLayer.getDeckLayers();
|
||||
deckLayers.forEach((layer: any) => {
|
||||
if (layer && layer.id) {
|
||||
// 仅处理 junctionContourLayer 和 waterflowLayer
|
||||
if (
|
||||
layer.id !== "junctionContourLayer" &&
|
||||
layer.id !== "waterflowLayer"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
return false;
|
||||
}) as DeckLayer[];
|
||||
|
||||
// 合并所有可控制的图层
|
||||
const allLayers = [...mapLayers, ...deckFlowLayers];
|
||||
|
||||
// 定义图层排序顺序
|
||||
const layerOrder = [
|
||||
"junctions",
|
||||
"reservoirs",
|
||||
"tanks",
|
||||
"pipes",
|
||||
"pumps",
|
||||
"valves",
|
||||
"scada",
|
||||
"waterflow",
|
||||
"contourLayer",
|
||||
];
|
||||
|
||||
// 过滤并排序图层:只显示在 layerOrder 中的图层
|
||||
const sortedLayers = allLayers
|
||||
.filter((layer) => {
|
||||
const value = layer.get("value")?.toLowerCase();
|
||||
return layerOrder.includes(value);
|
||||
})
|
||||
.sort((a, b) => {
|
||||
const nameA = a.get("value")?.toLowerCase();
|
||||
const nameB = b.get("value")?.toLowerCase();
|
||||
const indexA = layerOrder.indexOf(nameA);
|
||||
const indexB = layerOrder.indexOf(nameB);
|
||||
|
||||
return indexA - indexB;
|
||||
});
|
||||
|
||||
setLayers(sortedLayers);
|
||||
|
||||
setLayerVisibilities((prevVisibilities) => {
|
||||
const visible = new Map<Layer, boolean>();
|
||||
sortedLayers.forEach((layer) => {
|
||||
// 如果用户刚手动改变了这个图层,使用本地状态
|
||||
if (userChangedRef.current.has(layer)) {
|
||||
visible.set(layer, prevVisibilities.get(layer) ?? true);
|
||||
} else if (layer instanceof DeckLayer) {
|
||||
// 对于 DeckLayer,需要设置内部 deck.gl 图层的可见性
|
||||
const deckLayers = layer.getDeckLayers();
|
||||
deckLayers.forEach((deckLayer: any) => {
|
||||
if (
|
||||
deckLayer &&
|
||||
(deckLayer.id === "waterflowLayer" ||
|
||||
deckLayer.id === "contourLayer")
|
||||
) {
|
||||
const visible = layer.getDeckLayerVisible(deckLayer.id);
|
||||
layer.setDeckLayerVisible(deckLayer.id, !visible);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 对于普通 OpenLayers 图层
|
||||
visible.set(layer, layer.getVisible());
|
||||
// 检查可用性
|
||||
if (
|
||||
(layer.id === "junctionContourLayer" && !isContourLayerAvailable) ||
|
||||
(layer.id === "waterflowLayer" && !isWaterflowLayerAvailable)
|
||||
) {
|
||||
return; // 跳过不可用图层
|
||||
}
|
||||
});
|
||||
return visible;
|
||||
});
|
||||
};
|
||||
|
||||
// 初始更新
|
||||
updateLayers();
|
||||
|
||||
// 监听图层集合的变化
|
||||
const layerCollection = map.getLayers();
|
||||
layerCollection.on("change:length", updateLayers);
|
||||
|
||||
// 设置定时器定期检查 DeckLayer 内部图层的变化
|
||||
const intervalId = setInterval(updateLayers, 500);
|
||||
|
||||
return () => {
|
||||
layerCollection.un("change:length", updateLayers);
|
||||
clearInterval(intervalId);
|
||||
};
|
||||
}, [map]);
|
||||
|
||||
const handleVisibilityChange = (layer: Layer, visible: boolean) => {
|
||||
// 判断图层类型并调用相应的方法
|
||||
if (layer instanceof DeckLayer) {
|
||||
// 对于 DeckLayer,需要设置内部 deck.gl 图层的可见性
|
||||
const deckLayers = layer.getDeckLayers();
|
||||
deckLayers.forEach((deckLayer: any) => {
|
||||
if (deckLayer && deckLayer.id === "waterflowLayer") {
|
||||
layer.setDeckLayerVisible("waterflowLayer", visible);
|
||||
const visible =
|
||||
deckLayer.getDeckLayerVisible(layer.id) ??
|
||||
layer.props?.visible ??
|
||||
true;
|
||||
items.push({
|
||||
id: layer.props.id,
|
||||
name: layer.props.name, // 使用 name 属性作为显示名称
|
||||
visible: visible,
|
||||
type: "deck",
|
||||
layerRef: layer,
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 对于普通 OpenLayers 图层,使用 setVisible 方法
|
||||
layer.setVisible(visible);
|
||||
}
|
||||
|
||||
// 标记这个图层为用户手动改变
|
||||
userChangedRef.current.add(layer);
|
||||
setLayerVisibilities((prev) => new Map(prev).set(layer, visible));
|
||||
// 3. 定义图层显示顺序和过滤白名单
|
||||
const layerOrder = [
|
||||
"junctions",
|
||||
"reservoirs",
|
||||
"tanks",
|
||||
"pipes",
|
||||
"pumps",
|
||||
"valves",
|
||||
"scada",
|
||||
"waterflowLayer",
|
||||
"junctionContourLayer",
|
||||
];
|
||||
|
||||
// 1秒后移除标记,允许定时器更新
|
||||
setTimeout(() => {
|
||||
userChangedRef.current.delete(layer);
|
||||
}, 1000);
|
||||
// 过滤并排序
|
||||
const sortedItems = items
|
||||
.filter((item) => layerOrder.includes(item.id))
|
||||
.sort((a, b) => {
|
||||
const indexA = layerOrder.indexOf(a.id);
|
||||
const indexB = layerOrder.indexOf(b.id);
|
||||
return indexA - indexB;
|
||||
});
|
||||
|
||||
setLayerItems(sortedItems);
|
||||
}, [map, isWaterflowLayerAvailable, isContourLayerAvailable]);
|
||||
|
||||
useEffect(() => {
|
||||
updateLayers();
|
||||
|
||||
if (map) {
|
||||
const layerCollection = map.getLayers();
|
||||
layerCollection.on("change:length", updateLayers);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (map) {
|
||||
map.getLayers().un("change:length", updateLayers);
|
||||
}
|
||||
};
|
||||
}, [map, updateLayers]);
|
||||
|
||||
const handleVisibilityChange = (item: LayerItem, checked: boolean) => {
|
||||
if (item.type === "ol") {
|
||||
item.layerRef.setVisible(checked);
|
||||
} else if (item.type === "deck" && deckLayer) {
|
||||
if (item.id === "junctionContourLayer") {
|
||||
setShowContourLayer && setShowContourLayer(checked);
|
||||
}
|
||||
if (item.id === "waterflowLayer") {
|
||||
if (flowAnimation) flowAnimation.current = checked;
|
||||
}
|
||||
}
|
||||
|
||||
setLayerItems((prev) =>
|
||||
prev.map((i) => (i.id === item.id ? { ...i, visible: checked } : i))
|
||||
);
|
||||
};
|
||||
|
||||
if (!data) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="absolute left-4 bottom-4 bg-white rounded-md drop-shadow-lg z-10 opacity-85 hover:opacity-100 transition-opacity max-w-xs">
|
||||
<div className="ml-3 grid grid-cols-3">
|
||||
{layers.map((layer, index) => (
|
||||
{layerItems.map((item) => (
|
||||
<FormControlLabel
|
||||
key={index}
|
||||
key={item.id}
|
||||
control={
|
||||
<Checkbox
|
||||
checked={layerVisibilities.get(layer) ?? false}
|
||||
onChange={(e) =>
|
||||
handleVisibilityChange(layer, e.target.checked)
|
||||
}
|
||||
checked={item.visible}
|
||||
onChange={(e) => handleVisibilityChange(item, e.target.checked)}
|
||||
size="small"
|
||||
/>
|
||||
}
|
||||
label={layer.get("name") || `Layer ${index + 1}`}
|
||||
label={item.name}
|
||||
sx={{
|
||||
fontSize: "0.7rem",
|
||||
"& .MuiFormControlLabel-label": { fontSize: "0.7rem" },
|
||||
|
||||
Reference in New Issue
Block a user