更新 deckLayer 类
This commit is contained in:
@@ -40,7 +40,7 @@ const LayerControl: React.FC = () => {
|
|||||||
return deckLayers.some((dl: any) => dl.id === "waterflowLayer");
|
return deckLayers.some((dl: any) => dl.id === "waterflowLayer");
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}) as Layer[];
|
}) as DeckLayer[];
|
||||||
|
|
||||||
// 合并所有可控制的图层
|
// 合并所有可控制的图层
|
||||||
const allLayers = [...mapLayers, ...deckFlowLayers];
|
const allLayers = [...mapLayers, ...deckFlowLayers];
|
||||||
@@ -55,6 +55,7 @@ const LayerControl: React.FC = () => {
|
|||||||
"valves",
|
"valves",
|
||||||
"scada",
|
"scada",
|
||||||
"waterflow",
|
"waterflow",
|
||||||
|
"contourLayer",
|
||||||
];
|
];
|
||||||
|
|
||||||
// 过滤并排序图层:只显示在 layerOrder 中的图层
|
// 过滤并排序图层:只显示在 layerOrder 中的图层
|
||||||
@@ -81,10 +82,18 @@ const LayerControl: React.FC = () => {
|
|||||||
if (userChangedRef.current.has(layer)) {
|
if (userChangedRef.current.has(layer)) {
|
||||||
visible.set(layer, prevVisibilities.get(layer) ?? true);
|
visible.set(layer, prevVisibilities.get(layer) ?? true);
|
||||||
} else if (layer instanceof DeckLayer) {
|
} else if (layer instanceof DeckLayer) {
|
||||||
// 对于 DeckLayer,获取内部 deck.gl 图层的可见性
|
// 对于 DeckLayer,需要设置内部 deck.gl 图层的可见性
|
||||||
const waterflowVisible =
|
const deckLayers = layer.getDeckLayers();
|
||||||
layer.getDeckLayerVisible("waterflowLayer");
|
deckLayers.forEach((deckLayer: any) => {
|
||||||
visible.set(layer, waterflowVisible ?? true);
|
if (
|
||||||
|
deckLayer &&
|
||||||
|
(deckLayer.id === "waterflowLayer" ||
|
||||||
|
deckLayer.id === "contourLayer")
|
||||||
|
) {
|
||||||
|
const visible = layer.getDeckLayerVisible(deckLayer.id);
|
||||||
|
layer.setDeckLayerVisible(deckLayer.id, !visible);
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// 对于普通 OpenLayers 图层
|
// 对于普通 OpenLayers 图层
|
||||||
visible.set(layer, layer.getVisible());
|
visible.set(layer, layer.getVisible());
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import { parseColor } from "@utils/parseColor";
|
|||||||
import { VectorTile } from "ol";
|
import { VectorTile } from "ol";
|
||||||
import { useNotification } from "@refinedev/core";
|
import { useNotification } from "@refinedev/core";
|
||||||
import { config } from "@/config/config";
|
import { config } from "@/config/config";
|
||||||
import { constructNow, min } from "date-fns";
|
import { DeckLayer } from "@utils/layers";
|
||||||
|
|
||||||
interface StyleConfig {
|
interface StyleConfig {
|
||||||
property: string;
|
property: string;
|
||||||
@@ -190,6 +190,7 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
|||||||
pipeText,
|
pipeText,
|
||||||
setShowJunctionText,
|
setShowJunctionText,
|
||||||
setShowPipeText,
|
setShowPipeText,
|
||||||
|
setShowContourLayer,
|
||||||
setJunctionText,
|
setJunctionText,
|
||||||
setPipeText,
|
setPipeText,
|
||||||
} = data;
|
} = data;
|
||||||
@@ -443,6 +444,7 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
|||||||
}
|
}
|
||||||
if (junctionStyleConfigState)
|
if (junctionStyleConfigState)
|
||||||
applyLayerStyle(junctionStyleConfigState, breaks);
|
applyLayerStyle(junctionStyleConfigState, breaks);
|
||||||
|
updateContourLayerStyle(breaks, junctionStyleConfigState?.styleConfig);
|
||||||
} else if (
|
} else if (
|
||||||
layerType === "pipes" &&
|
layerType === "pipes" &&
|
||||||
currentPipeCalData &&
|
currentPipeCalData &&
|
||||||
@@ -1426,6 +1428,110 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// 更新 ContourLayer 的样式,并显示在地图上
|
||||||
|
const updateContourLayerStyle = (breaks: any, styleConfig: any) => {
|
||||||
|
if (!map) return;
|
||||||
|
// 查找包含 contourLayer 的 DeckLayer
|
||||||
|
const deckLayerWrapper = map
|
||||||
|
.getLayers()
|
||||||
|
.getArray()
|
||||||
|
.find((layer) => {
|
||||||
|
if (layer instanceof DeckLayer) {
|
||||||
|
const deckLayers = layer.getDeckLayers();
|
||||||
|
// 检查是否包含 contourLayer
|
||||||
|
return deckLayers.some((dl: any) => dl.id === "contourLayer");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}) as DeckLayer | undefined;
|
||||||
|
|
||||||
|
if (!deckLayerWrapper) return;
|
||||||
|
|
||||||
|
// 计算颜色
|
||||||
|
const segmentCount = breaks.length - 1;
|
||||||
|
if (segmentCount <= 0) return;
|
||||||
|
|
||||||
|
const thresholdColor = () => {
|
||||||
|
let colors: string[] = [];
|
||||||
|
if (styleConfig.colorType === "single") {
|
||||||
|
const c = SINGLE_COLOR_PALETTES[styleConfig.singlePaletteIndex].color;
|
||||||
|
colors = Array(segmentCount).fill(c);
|
||||||
|
} else if (styleConfig.colorType === "gradient") {
|
||||||
|
const { start, end } =
|
||||||
|
GRADIENT_PALETTES[styleConfig.gradientPaletteIndex];
|
||||||
|
const startColor = parseColor(start);
|
||||||
|
const endColor = parseColor(end);
|
||||||
|
for (let i = 0; i < segmentCount; i++) {
|
||||||
|
const ratio = segmentCount > 1 ? i / (segmentCount - 1) : 1;
|
||||||
|
const r = Math.round(
|
||||||
|
startColor.r + (endColor.r - startColor.r) * ratio
|
||||||
|
);
|
||||||
|
const g = Math.round(
|
||||||
|
startColor.g + (endColor.g - startColor.g) * ratio
|
||||||
|
);
|
||||||
|
const b = Math.round(
|
||||||
|
startColor.b + (endColor.b - startColor.b) * ratio
|
||||||
|
);
|
||||||
|
colors.push(`rgba(${r}, ${g}, ${b}, 1)`);
|
||||||
|
}
|
||||||
|
} else if (styleConfig.colorType === "rainbow") {
|
||||||
|
const baseColors =
|
||||||
|
RAINBOW_PALETTES[styleConfig.rainbowPaletteIndex].colors;
|
||||||
|
colors = Array.from(
|
||||||
|
{ length: segmentCount },
|
||||||
|
(_, i) => baseColors[i % baseColors.length]
|
||||||
|
);
|
||||||
|
} else if (styleConfig.colorType === "custom") {
|
||||||
|
const custom = styleConfig.customColors || [];
|
||||||
|
const result = [...custom];
|
||||||
|
const reverseRainbowColors = RAINBOW_PALETTES[1].colors;
|
||||||
|
while (result.length < segmentCount) {
|
||||||
|
result.push(
|
||||||
|
reverseRainbowColors[
|
||||||
|
(result.length - custom.length) % reverseRainbowColors.length
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
colors = result.slice(0, segmentCount);
|
||||||
|
}
|
||||||
|
return colors;
|
||||||
|
};
|
||||||
|
|
||||||
|
const colors = thresholdColor();
|
||||||
|
// 构建 contours 配置
|
||||||
|
const contours: any[] = [];
|
||||||
|
for (let i = 0; i < segmentCount; i++) {
|
||||||
|
const start = breaks[i];
|
||||||
|
const end = breaks[i + 1];
|
||||||
|
const colorStr = colors[i];
|
||||||
|
try {
|
||||||
|
const c = parseColor(colorStr);
|
||||||
|
contours.push({
|
||||||
|
threshold: [start, end],
|
||||||
|
color: [c.r, c.g, c.b],
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Color parse error", colorStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 更新 DeckLayer
|
||||||
|
const deck = (deckLayerWrapper as any).deck;
|
||||||
|
if (deck && deck.props && deck.props.layers) {
|
||||||
|
const currentLayers = deck.props.layers;
|
||||||
|
const newLayers = currentLayers.map((layer: any) => {
|
||||||
|
if (layer.id === "contourLayer") {
|
||||||
|
return layer.clone({
|
||||||
|
contours: contours,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return layer;
|
||||||
|
});
|
||||||
|
console.log(newLayers);
|
||||||
|
deck.setProps({ layers: newLayers });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示 contourLayer
|
||||||
|
// if (setShowContourLayer) setShowContourLayer(true);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -241,13 +241,13 @@ const Timeline: React.FC<TimelineProps> = ({
|
|||||||
// 播放控制
|
// 播放控制
|
||||||
const handlePlay = useCallback(() => {
|
const handlePlay = useCallback(() => {
|
||||||
if (!isPlaying) {
|
if (!isPlaying) {
|
||||||
if (junctionText === "" && pipeText === "") {
|
// if (junctionText === "" && pipeText === "") {
|
||||||
open?.({
|
// open?.({
|
||||||
type: "error",
|
// type: "error",
|
||||||
message: "请至少设定并应用一个图层的样式。",
|
// message: "请至少设定并应用一个图层的样式。",
|
||||||
});
|
// });
|
||||||
// return;
|
// return;
|
||||||
}
|
// }
|
||||||
setIsPlaying(true);
|
setIsPlaying(true);
|
||||||
|
|
||||||
intervalRef.current = setInterval(() => {
|
intervalRef.current = setInterval(() => {
|
||||||
@@ -367,13 +367,13 @@ const Timeline: React.FC<TimelineProps> = ({
|
|||||||
// 检查至少一个属性有值
|
// 检查至少一个属性有值
|
||||||
const junctionProperties = junctionText;
|
const junctionProperties = junctionText;
|
||||||
const pipeProperties = pipeText;
|
const pipeProperties = pipeText;
|
||||||
if (junctionProperties === "" && pipeProperties === "") {
|
// if (junctionProperties === "" && pipeProperties === "") {
|
||||||
open?.({
|
// open?.({
|
||||||
type: "error",
|
// type: "error",
|
||||||
message: "请至少设定并应用一个图层的样式。",
|
// message: "请至少设定并应用一个图层的样式。",
|
||||||
});
|
// });
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
fetchFrameData(
|
fetchFrameData(
|
||||||
currentTimeToDate(selectedDate, currentTime),
|
currentTimeToDate(selectedDate, currentTime),
|
||||||
junctionText,
|
junctionText,
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ interface DataContextType {
|
|||||||
showPipeText?: boolean; // 是否显示管道文本
|
showPipeText?: boolean; // 是否显示管道文本
|
||||||
setShowJunctionText?: React.Dispatch<React.SetStateAction<boolean>>;
|
setShowJunctionText?: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
setShowPipeText?: React.Dispatch<React.SetStateAction<boolean>>;
|
setShowPipeText?: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
setShowContourLayer?: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
junctionText: string;
|
junctionText: string;
|
||||||
pipeText: string;
|
pipeText: string;
|
||||||
setJunctionText?: React.Dispatch<React.SetStateAction<string>>;
|
setJunctionText?: React.Dispatch<React.SetStateAction<string>>;
|
||||||
@@ -86,7 +87,7 @@ export const useData = () => {
|
|||||||
const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
||||||
const mapRef = useRef<HTMLDivElement | null>(null);
|
const mapRef = useRef<HTMLDivElement | null>(null);
|
||||||
const deckRef = useRef<Deck | null>(null);
|
const deckRef = useRef<Deck | null>(null);
|
||||||
const deckFlowRef = useRef<Deck | null>(null);
|
const deckLayerRef = useRef<DeckLayer | null>(null);
|
||||||
|
|
||||||
const [map, setMap] = useState<OlMap>();
|
const [map, setMap] = useState<OlMap>();
|
||||||
// currentCalData 用于存储当前计算结果
|
// currentCalData 用于存储当前计算结果
|
||||||
@@ -111,10 +112,10 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
|||||||
const [showPipeText, setShowPipeText] = useState(false); // 控制管道文本显示
|
const [showPipeText, setShowPipeText] = useState(false); // 控制管道文本显示
|
||||||
const [showJunctionTextLayer, setShowJunctionTextLayer] = useState(true); // 控制节点文本图层显示
|
const [showJunctionTextLayer, setShowJunctionTextLayer] = useState(true); // 控制节点文本图层显示
|
||||||
const [showPipeTextLayer, setShowPipeTextLayer] = useState(true); // 控制管道文本图层显示
|
const [showPipeTextLayer, setShowPipeTextLayer] = useState(true); // 控制管道文本图层显示
|
||||||
|
const [showContourLayer, setShowContourLayer] = useState(true); // 控制等高线图层显示
|
||||||
const [junctionText, setJunctionText] = useState("pressure");
|
const [junctionText, setJunctionText] = useState("pressure");
|
||||||
const [pipeText, setPipeText] = useState("flow");
|
const [pipeText, setPipeText] = useState("flow");
|
||||||
const flowAnimation = useRef(false); // 添加动画控制标志
|
const flowAnimation = useRef(false); // 添加动画控制标志
|
||||||
const waterflowUserVisible = useRef<boolean>(true); // 用户设置的水流图层可见性
|
|
||||||
const [currentZoom, setCurrentZoom] = useState(11); // 当前缩放级别
|
const [currentZoom, setCurrentZoom] = useState(11); // 当前缩放级别
|
||||||
|
|
||||||
// 防抖更新函数
|
// 防抖更新函数
|
||||||
@@ -571,6 +572,7 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
|||||||
controls: [],
|
controls: [],
|
||||||
});
|
});
|
||||||
setMap(map);
|
setMap(map);
|
||||||
|
|
||||||
// 恢复上次视图;如果没有则适配 MAP_EXTENT
|
// 恢复上次视图;如果没有则适配 MAP_EXTENT
|
||||||
try {
|
try {
|
||||||
const stored = localStorage.getItem(MAP_VIEW_STORAGE_KEY);
|
const stored = localStorage.getItem(MAP_VIEW_STORAGE_KEY);
|
||||||
@@ -650,37 +652,13 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
|||||||
layers: [],
|
layers: [],
|
||||||
});
|
});
|
||||||
deckRef.current = deck;
|
deckRef.current = deck;
|
||||||
const deckLayer = new DeckLayer(deck);
|
const deckLayer = new DeckLayer(deck, {
|
||||||
// deckLayer.setZIndex(1000); // 确保在最上层
|
name: "deckLayer",
|
||||||
|
value: "deckLayer",
|
||||||
|
});
|
||||||
|
deckLayerRef.current = deckLayer;
|
||||||
map.addLayer(deckLayer);
|
map.addLayer(deckLayer);
|
||||||
|
|
||||||
// 初始化水流动画的 deck.gl
|
|
||||||
const deckFlow = new Deck({
|
|
||||||
initialViewState: {
|
|
||||||
longitude: 0,
|
|
||||||
latitude: 0,
|
|
||||||
zoom: 1,
|
|
||||||
},
|
|
||||||
canvas: "deck-flow-canvas",
|
|
||||||
controller: false,
|
|
||||||
layers: [],
|
|
||||||
});
|
|
||||||
deckFlowRef.current = deckFlow;
|
|
||||||
const deckFlowLayer = new DeckLayer(deckFlow, {
|
|
||||||
name: "水流动画",
|
|
||||||
value: "waterflow",
|
|
||||||
type: "animation",
|
|
||||||
});
|
|
||||||
// 初始化用户可见性状态(默认为 true)
|
|
||||||
deckFlowLayer.initUserVisibility("waterflowLayer", true);
|
|
||||||
// 设置可见性变化回调,同步更新 waterflowUserVisible
|
|
||||||
deckFlowLayer.setVisibilityChangeCallback((layerId, visible) => {
|
|
||||||
if (layerId === "waterflowLayer") {
|
|
||||||
waterflowUserVisible.current = visible;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
map.addLayer(deckFlowLayer);
|
|
||||||
|
|
||||||
// 清理函数
|
// 清理函数
|
||||||
return () => {
|
return () => {
|
||||||
junctionsLayer.un("change:visible", handleJunctionVisibilityChange);
|
junctionsLayer.un("change:visible", handleJunctionVisibilityChange);
|
||||||
@@ -688,16 +666,18 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
|||||||
map.setTarget(undefined);
|
map.setTarget(undefined);
|
||||||
map.dispose();
|
map.dispose();
|
||||||
deck.finalize();
|
deck.finalize();
|
||||||
deckFlow.finalize();
|
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 当数据变化时,更新 deck.gl 图层
|
// 当数据变化时,更新 deck.gl 图层
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const deck = deckRef.current;
|
const deckLayer = deckLayerRef.current;
|
||||||
if (!deck) return; // 如果 deck 实例还未创建,则退出
|
if (!deckLayer) return; // 如果 deck 实例还未创建,则退出
|
||||||
const newLayers = [
|
if (!junctionData.length) return;
|
||||||
new TextLayer({
|
if (!pipeData.length) return;
|
||||||
|
console.log(pipeData);
|
||||||
|
console.log(pipeText);
|
||||||
|
const junctionTextLayer = new TextLayer({
|
||||||
id: "junctionTextLayer",
|
id: "junctionTextLayer",
|
||||||
zIndex: 10,
|
zIndex: 10,
|
||||||
data: showJunctionText ? junctionData : [],
|
data: showJunctionText ? junctionData : [],
|
||||||
@@ -712,8 +692,7 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
|||||||
getTextAnchor: "middle",
|
getTextAnchor: "middle",
|
||||||
getAlignmentBaseline: "center",
|
getAlignmentBaseline: "center",
|
||||||
getPixelOffset: [0, -10],
|
getPixelOffset: [0, -10],
|
||||||
visible:
|
visible: showJunctionTextLayer && currentZoom >= 15 && currentZoom <= 24,
|
||||||
showJunctionTextLayer && currentZoom >= 15 && currentZoom <= 24,
|
|
||||||
extensions: [new CollisionFilterExtension()],
|
extensions: [new CollisionFilterExtension()],
|
||||||
collisionTestProps: {
|
collisionTestProps: {
|
||||||
sizeScale: 3, // 增加碰撞检测的尺寸以提供更大间距
|
sizeScale: 3, // 增加碰撞检测的尺寸以提供更大间距
|
||||||
@@ -727,8 +706,8 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
|||||||
},
|
},
|
||||||
// outlineWidth: 10,
|
// outlineWidth: 10,
|
||||||
// outlineColor: [242, 244, 246, 255],
|
// outlineColor: [242, 244, 246, 255],
|
||||||
}),
|
});
|
||||||
new TextLayer({
|
const pipeTextLayer = new TextLayer({
|
||||||
id: "pipeTextLayer",
|
id: "pipeTextLayer",
|
||||||
zIndex: 10,
|
zIndex: 10,
|
||||||
data: showPipeText ? pipeData : [],
|
data: showPipeText ? pipeData : [],
|
||||||
@@ -757,16 +736,49 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
|||||||
},
|
},
|
||||||
// outlineWidth: 10,
|
// outlineWidth: 10,
|
||||||
// outlineColor: [242, 244, 246, 255],
|
// outlineColor: [242, 244, 246, 255],
|
||||||
}),
|
});
|
||||||
];
|
if (deckLayer.getDeckLayerById("junctionTextLayer")) {
|
||||||
deck.setProps({ layers: newLayers });
|
// 传入完整 layer 实例以保证 clone/替换时保留 layer 类型和方法
|
||||||
|
deckLayer.updateDeckLayer("junctionTextLayer", junctionTextLayer);
|
||||||
|
} else {
|
||||||
|
deckLayer.addDeckLayer(junctionTextLayer);
|
||||||
|
}
|
||||||
|
if (deckLayer.getDeckLayerById("pipeTextLayer")) {
|
||||||
|
deckLayer.updateDeckLayer("pipeTextLayer", pipeTextLayer);
|
||||||
|
} else {
|
||||||
|
deckLayer.addDeckLayer(pipeTextLayer);
|
||||||
|
}
|
||||||
|
console.log(deckLayer.getDeckLayers());
|
||||||
|
}, [
|
||||||
|
junctionData,
|
||||||
|
pipeData,
|
||||||
|
currentZoom,
|
||||||
|
showJunctionText,
|
||||||
|
showPipeText,
|
||||||
|
showJunctionTextLayer,
|
||||||
|
showPipeTextLayer,
|
||||||
|
showContourLayer,
|
||||||
|
junctionText,
|
||||||
|
pipeText,
|
||||||
|
]);
|
||||||
|
// 控制流动动画开关
|
||||||
|
useEffect(() => {
|
||||||
|
if (pipeText === "flow" && currentPipeCalData.length > 0) {
|
||||||
|
flowAnimation.current = true;
|
||||||
|
} else {
|
||||||
|
flowAnimation.current = false;
|
||||||
|
}
|
||||||
|
const deckLayer = deckLayerRef.current;
|
||||||
|
if (!deckLayer) return; // 如果 deck 实例还未创建,则退出
|
||||||
|
|
||||||
|
let animationFrameId: number; // 保存 requestAnimationFrame 的 ID
|
||||||
|
|
||||||
// 动画循环
|
// 动画循环
|
||||||
const animate = () => {
|
const animate = () => {
|
||||||
if (!deck || !flowAnimation.current) return; // 添加检查,防止空数据或停止旧循环
|
if (!deckRef.current || !flowAnimation.current) return; // 添加检查,防止空数据或停止旧循环
|
||||||
// 动画总时长(秒)
|
// 动画总时长(秒)
|
||||||
if (pipeData.length === 0) {
|
if (pipeData.length === 0) {
|
||||||
requestAnimationFrame(animate);
|
animationFrameId = requestAnimationFrame(animate);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const animationDuration = 10;
|
const animationDuration = 10;
|
||||||
@@ -787,119 +799,31 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
|||||||
getColor: [0, 220, 255],
|
getColor: [0, 220, 255],
|
||||||
opacity: 0.8,
|
opacity: 0.8,
|
||||||
visible:
|
visible:
|
||||||
waterflowUserVisible.current &&
|
flowAnimation.current && currentZoom >= 12 && currentZoom <= 24,
|
||||||
flowAnimation.current &&
|
|
||||||
currentZoom >= 12 &&
|
|
||||||
currentZoom <= 24,
|
|
||||||
widthMinPixels: 5,
|
widthMinPixels: 5,
|
||||||
jointRounded: true, // 拐角变圆
|
jointRounded: true, // 拐角变圆
|
||||||
// capRounded: true, // 端点变圆
|
// capRounded: true, // 端点变圆
|
||||||
trailLength: 2, // 水流尾迹淡出时间
|
trailLength: 2, // 水流尾迹淡出时间
|
||||||
currentTime: currentTime,
|
currentTime: currentTime,
|
||||||
});
|
});
|
||||||
|
if (deckLayer.getDeckLayerById("waterflowLayer")) {
|
||||||
// 获取当前除 waterflowLayer 之外的所有图层
|
deckLayer.updateDeckLayer("waterflowLayer", waterflowLayer);
|
||||||
const otherLayers = deck.props.layers.filter(
|
|
||||||
(layer: any) => layer && layer.id !== "waterflowLayer"
|
|
||||||
);
|
|
||||||
|
|
||||||
deck.setProps({
|
|
||||||
layers: [...otherLayers, waterflowLayer],
|
|
||||||
});
|
|
||||||
|
|
||||||
// 继续请求动画帧,每帧执行一次函数
|
|
||||||
requestAnimationFrame(animate);
|
|
||||||
};
|
|
||||||
animate();
|
|
||||||
}, [
|
|
||||||
flowAnimation,
|
|
||||||
junctionData,
|
|
||||||
pipeData,
|
|
||||||
currentZoom,
|
|
||||||
showJunctionText,
|
|
||||||
showPipeText,
|
|
||||||
showJunctionTextLayer,
|
|
||||||
showPipeTextLayer,
|
|
||||||
junctionText,
|
|
||||||
pipeText,
|
|
||||||
]);
|
|
||||||
// 控制流动动画开关
|
|
||||||
useEffect(() => {
|
|
||||||
if (pipeText === "flow" && currentPipeCalData.length > 0) {
|
|
||||||
flowAnimation.current = true;
|
|
||||||
} else {
|
} else {
|
||||||
flowAnimation.current = false;
|
deckLayer.addDeckLayer(waterflowLayer);
|
||||||
}
|
}
|
||||||
}, [currentPipeCalData, pipeText]);
|
// 继续请求动画帧,每帧执行一次函数
|
||||||
|
animationFrameId = requestAnimationFrame(animate);
|
||||||
// 水流动画循环
|
|
||||||
useEffect(() => {
|
|
||||||
const deckFlow = deckFlowRef.current;
|
|
||||||
if (!deckFlow || !flowAnimation.current || pipeData.length === 0) {
|
|
||||||
// 如果不需要动画,清空图层
|
|
||||||
if (deckFlow) {
|
|
||||||
deckFlow.setProps({ layers: [] });
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let animationId: number;
|
|
||||||
const animate = () => {
|
|
||||||
if (!deckFlow || !flowAnimation.current) return;
|
|
||||||
if (pipeData.length === 0) {
|
|
||||||
animationId = requestAnimationFrame(animate);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 动画总时长(秒)
|
|
||||||
const animationDuration = 10;
|
|
||||||
// 缓冲时间(秒)
|
|
||||||
const bufferTime = 2;
|
|
||||||
// 完整循环周期
|
|
||||||
const loopLength = animationDuration + bufferTime;
|
|
||||||
// 确保时间范围与你的时间戳数据匹配
|
|
||||||
const currentTime = (Date.now() / 1000) % loopLength; // (0,12) 之间循环
|
|
||||||
|
|
||||||
const waterflowLayer = new TripsLayer({
|
|
||||||
id: "waterflowLayer",
|
|
||||||
data: pipeData,
|
|
||||||
getPath: (d) => d.path,
|
|
||||||
getTimestamps: (d) => {
|
|
||||||
return d.timestamps; // 这些应该是与 currentTime 匹配的数值
|
|
||||||
},
|
|
||||||
getColor: [0, 220, 255],
|
|
||||||
opacity: 0.8,
|
|
||||||
visible:
|
|
||||||
waterflowUserVisible.current &&
|
|
||||||
flowAnimation.current &&
|
|
||||||
currentZoom >= 12 &&
|
|
||||||
currentZoom <= 24,
|
|
||||||
widthMinPixels: 5,
|
|
||||||
jointRounded: true, // 拐角变圆
|
|
||||||
// capRounded: true, // 端点变圆
|
|
||||||
trailLength: 2, // 水流尾迹淡出时间
|
|
||||||
currentTime: currentTime,
|
|
||||||
});
|
|
||||||
|
|
||||||
deckFlow.setProps({
|
|
||||||
layers: [waterflowLayer],
|
|
||||||
});
|
|
||||||
|
|
||||||
// 继续请求动画帧,每帧执行一次函数
|
|
||||||
animationId = requestAnimationFrame(animate);
|
|
||||||
};
|
};
|
||||||
animate();
|
animate();
|
||||||
|
|
||||||
// 清理函数
|
// 清理函数:取消动画帧
|
||||||
return () => {
|
return () => {
|
||||||
if (animationId) {
|
if (animationFrameId) {
|
||||||
cancelAnimationFrame(animationId);
|
cancelAnimationFrame(animationFrameId);
|
||||||
}
|
|
||||||
if (deckFlow) {
|
|
||||||
deckFlow.setProps({ layers: [] });
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [flowAnimation, pipeData, currentZoom]);
|
}, [currentZoom, currentPipeCalData, pipeText, pipeData.length]);
|
||||||
|
|
||||||
// 计算值更新时,更新 junctionData 和 pipeData
|
// 计算值更新时,更新 junctionData 和 pipeData
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const junctionProperties = junctionText;
|
const junctionProperties = junctionText;
|
||||||
@@ -965,6 +889,7 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
|||||||
setCurrentPipeCalData,
|
setCurrentPipeCalData,
|
||||||
setShowJunctionText,
|
setShowJunctionText,
|
||||||
setShowPipeText,
|
setShowPipeText,
|
||||||
|
setShowContourLayer,
|
||||||
setJunctionText,
|
setJunctionText,
|
||||||
setPipeText,
|
setPipeText,
|
||||||
showJunctionText,
|
showJunctionText,
|
||||||
@@ -980,7 +905,6 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
|||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
<canvas id="deck-canvas" />
|
<canvas id="deck-canvas" />
|
||||||
<canvas id="deck-flow-canvas" />
|
|
||||||
</MapContext.Provider>
|
</MapContext.Provider>
|
||||||
</DataContext.Provider>
|
</DataContext.Provider>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -67,6 +67,14 @@ export class DeckLayer extends Layer {
|
|||||||
// 添加图层
|
// 添加图层
|
||||||
addDeckLayer(layer: any): void {
|
addDeckLayer(layer: any): void {
|
||||||
const currentLayers = this.getDeckLayers();
|
const currentLayers = this.getDeckLayers();
|
||||||
|
// 如果已有同 id 图层,则替换保持顺序;否则追加
|
||||||
|
const idx = currentLayers.findIndex((l: any) => l && l.id === layer.id);
|
||||||
|
if (idx >= 0) {
|
||||||
|
const copy = [...currentLayers];
|
||||||
|
copy[idx] = layer;
|
||||||
|
this.deck.setProps({ layers: copy });
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.deck.setProps({ layers: [...currentLayers, layer] });
|
this.deck.setProps({ layers: [...currentLayers, layer] });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,15 +93,41 @@ export class DeckLayer extends Layer {
|
|||||||
return layers.find((layer: any) => layer && layer.id === layerId);
|
return layers.find((layer: any) => layer && layer.id === layerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新特定图层
|
// 更新特定图层:支持传入一个新的 Layer 实例或一个 props 对象
|
||||||
updateDeckLayer(layerId: string, props: any): void {
|
// - 如果传入的是 Layer 实例,则直接替换同 id 的图层为该实例
|
||||||
|
// - 如果传入的是 props(普通对象),则基于原图层调用 clone(props)
|
||||||
|
updateDeckLayer(layerId: string, layerOrProps: any): void {
|
||||||
const layers = this.getDeckLayers();
|
const layers = this.getDeckLayers();
|
||||||
const updatedLayers = layers.map((layer: any) => {
|
const updatedLayers = layers.map((layer: any) => {
|
||||||
if (layer && layer.id === layerId) {
|
if (!layer || layer.id !== layerId) return layer;
|
||||||
return layer.clone(props);
|
|
||||||
|
// 如果传入的是一个 deck.gl Layer 实例(通常包含 id 和 props)
|
||||||
|
if (
|
||||||
|
layerOrProps &&
|
||||||
|
typeof layerOrProps === "object" &&
|
||||||
|
layerOrProps.id !== undefined &&
|
||||||
|
typeof layerOrProps.clone === "function"
|
||||||
|
) {
|
||||||
|
// 替换为新的 layer 实例
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
return layer;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.deck.setProps({ layers: updatedLayers });
|
this.deck.setProps({ layers: updatedLayers });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +147,16 @@ export class DeckLayer extends Layer {
|
|||||||
// 存储用户设置的可见性
|
// 存储用户设置的可见性
|
||||||
this.userVisibility.set(layerId, visible);
|
this.userVisibility.set(layerId, visible);
|
||||||
// 更新图层(注意:实际的 visible 可能还受其他条件控制)
|
// 更新图层(注意:实际的 visible 可能还受其他条件控制)
|
||||||
|
// 优先尝试使用传入的 layer 实例替换,否则使用 clone({ visible }) 来保留图层类型
|
||||||
|
const found = this.getDeckLayerById(layerId);
|
||||||
|
if (!found) return;
|
||||||
|
try {
|
||||||
|
// 使用 clone 来确保保持同类型实例
|
||||||
|
this.updateDeckLayer(layerId, { ...found.props, visible });
|
||||||
|
} catch (err) {
|
||||||
|
// 降级:直接替换属性
|
||||||
this.updateDeckLayer(layerId, { visible });
|
this.updateDeckLayer(layerId, { visible });
|
||||||
|
}
|
||||||
// 触发回调通知外部
|
// 触发回调通知外部
|
||||||
if (this.onVisibilityChange) {
|
if (this.onVisibilityChange) {
|
||||||
this.onVisibilityChange(layerId, visible);
|
this.onVisibilityChange(layerId, visible);
|
||||||
|
|||||||
Reference in New Issue
Block a user