183 lines
5.1 KiB
TypeScript
183 lines
5.1 KiB
TypeScript
import React, { useState, useEffect, useCallback } from "react";
|
|
import { useData, useMap } from "../MapComponent";
|
|
import { Checkbox, FormControlLabel } from "@mui/material";
|
|
import WebGLVectorTileLayer from "ol/layer/WebGLVectorTile";
|
|
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 data = useData();
|
|
if (!data) return;
|
|
const {
|
|
deckLayer,
|
|
isContourLayerAvailable,
|
|
isWaterflowLayerAvailable,
|
|
setShowWaterflowLayer,
|
|
setShowContourLayer,
|
|
} = data;
|
|
const [layerItems, setLayerItems] = useState<LayerItem[]>([]);
|
|
|
|
// 更新图层列表
|
|
const updateLayers = useCallback(() => {
|
|
if (!map || !data) return;
|
|
const { deckLayer } = data;
|
|
|
|
const items: LayerItem[] = [];
|
|
|
|
// 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;
|
|
}
|
|
// 检查可用性
|
|
if (
|
|
(layer.id === "junctionContourLayer" && !isContourLayerAvailable) ||
|
|
(layer.id === "waterflowLayer" && !isWaterflowLayerAvailable)
|
|
) {
|
|
return; // 跳过不可用图层
|
|
}
|
|
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,
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
// 3. 定义图层显示顺序和过滤白名单
|
|
const layerOrder = [
|
|
"junctions",
|
|
"reservoirs",
|
|
"tanks",
|
|
"pipes",
|
|
"pumps",
|
|
"valves",
|
|
"scada",
|
|
"waterflowLayer",
|
|
"junctionContourLayer",
|
|
];
|
|
|
|
// 过滤并排序
|
|
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") {
|
|
setShowWaterflowLayer && setShowWaterflowLayer(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-1300 opacity-85 hover:opacity-100 transition-opacity max-w-xs">
|
|
<div className="ml-3 grid grid-cols-3">
|
|
{layerItems.map((item) => (
|
|
<FormControlLabel
|
|
key={item.id}
|
|
control={
|
|
<Checkbox
|
|
checked={item.visible}
|
|
onChange={(e) => handleVisibilityChange(item, e.target.checked)}
|
|
size="small"
|
|
/>
|
|
}
|
|
label={item.name}
|
|
sx={{
|
|
fontSize: "0.7rem",
|
|
"& .MuiFormControlLabel-label": { fontSize: "0.7rem" },
|
|
}}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default LayerControl;
|