Files
TJWaterServer/src/app/OlMap/Controls/DrawPanel.tsx

383 lines
11 KiB
TypeScript

import React, { useState, useEffect, useCallback, useRef } from "react";
import ToolbarButton from "@components/olmap/common/ToolbarButton";
// 导入Material-UI图标
import BackHandOutlinedIcon from "@mui/icons-material/BackHandOutlined";
import BorderColorOutlinedIcon from "@mui/icons-material/BorderColorOutlined";
import MoreHorizOutlinedIcon from "@mui/icons-material/MoreHorizOutlined";
import TimelineIcon from "@mui/icons-material/Timeline";
import CircleOutlinedIcon from "@mui/icons-material/CircleOutlined";
import CheckBoxOutlineBlankIcon from "@mui/icons-material/CheckBoxOutlineBlank";
import GestureIcon from "@mui/icons-material/Gesture";
import UndoIcon from "@mui/icons-material/Undo";
import RedoIcon from "@mui/icons-material/Redo";
import DeleteIcon from "@mui/icons-material/Delete";
import SaveIcon from "@mui/icons-material/Save";
// 导入OpenLayers绘图相关模块
import Draw, {
DrawEvent,
createBox,
GeometryFunction,
} from "ol/interaction/Draw";
import VectorSource from "ol/source/Vector";
import VectorLayer from "ol/layer/Vector";
import { Style, Stroke, Fill, Circle } from "ol/style";
import { Geometry } from "ol/geom";
import Feature from "ol/Feature";
import { Type as GeometryType } from "ol/geom/Geometry";
import { useMap } from "../MapComponent";
const DrawPanel: React.FC = () => {
const map = useMap();
const [activeTool, setActiveTool] = useState<string>("pan");
const [drawLayer, setDrawLayer] = useState<VectorLayer<VectorSource> | null>(
null
);
const [drawnFeatures, setDrawnFeatures] = useState<Feature<Geometry>[]>([]);
const [historyStack, setHistoryStack] = useState<Feature<Geometry>[][]>([]);
const [historyIndex, setHistoryIndex] = useState<number>(-1);
const drawInteractionRef = useRef<Draw | null>(null);
// 创建并添加绘图图层
useEffect(() => {
if (!map) return;
const drawSource = new VectorSource();
const drawVectorLayer = new VectorLayer({
source: drawSource,
style: new Style({
stroke: new Stroke({
color: `rgba(255, 152, 0, 0.9)`,
width: 2,
}),
fill: new Fill({
color: `rgba(255, 152, 0, 0.3)`,
}),
image: new Circle({
radius: 7,
stroke: new Stroke({
color: `rgba(255, 152, 0, 0.9)`,
width: 2,
}),
fill: new Fill({
color: `rgba(255, 152, 0, 0.3)`,
}),
}),
}),
});
map.addLayer(drawVectorLayer);
setDrawLayer(drawVectorLayer);
return () => {
if (drawInteractionRef.current && map) {
map.removeInteraction(drawInteractionRef.current);
drawInteractionRef.current = null;
}
map.removeLayer(drawVectorLayer);
};
}, [map, drawInteractionRef]);
// 保存到历史记录
const saveToHistory = useCallback(
(features: Feature<Geometry>[]) => {
setHistoryStack((prevStack) => {
const newHistory = prevStack.slice(0, historyIndex + 1);
newHistory.push([...features]);
setHistoryIndex(newHistory.length - 1);
return newHistory;
});
},
[historyIndex]
);
// 添加绘图交互
const addDrawInteraction = (
type: GeometryType,
geometryFunction?: GeometryFunction
) => {
if (!drawLayer) return;
if (!map) return;
// 清除现有的绘图交互
if (drawInteractionRef.current && map) {
map.removeInteraction(drawInteractionRef.current);
}
const source = drawLayer.getSource();
if (!source) return;
const drawOptions: {
source: VectorSource;
type: GeometryType;
style: Style;
geometryFunction?: GeometryFunction;
} = {
source: source,
type: type,
style: new Style({
stroke: new Stroke({
color: `rgba(255, 152, 0, 0.9)`,
width: 2,
}),
fill: new Fill({
color: `rgba(255, 152, 0, 0.3)`,
}),
image: new Circle({
radius: 7,
stroke: new Stroke({
color: `rgba(255, 152, 0, 0.9)`,
width: 2,
}),
fill: new Fill({
color: `rgba(255, 152, 0, 0.3)`,
}),
}),
}),
};
// 如果有几何函数,添加它
if (geometryFunction) {
drawOptions.geometryFunction = geometryFunction;
}
const draw = new Draw(drawOptions);
// 绘图完成事件
draw.on("drawend", (event: DrawEvent) => {
const feature = event.feature;
const currentFeatures = [...drawnFeatures, feature];
setDrawnFeatures(currentFeatures);
saveToHistory(currentFeatures);
});
map.addInteraction(draw);
drawInteractionRef.current = draw;
};
// 处理工具点击
const handleToolClick = (tool: string) => {
// 如果点击的是当前激活的工具,则取消激活
// console.log("当前激活的工具:", activeTool);
// console.log("点击的工具:", tool);
if (activeTool === tool) {
setActiveTool("");
if (drawInteractionRef.current && map) {
map.removeInteraction(drawInteractionRef.current);
drawInteractionRef.current = null;
}
return;
}
if (
tool !== "undo" &&
tool !== "redo" &&
tool !== "delete" &&
tool !== "save"
) {
setActiveTool(tool);
}
// 根据工具类型处理不同的交互
switch (tool) {
case "pan":
// 平移地图,移除所有绘图交互
if (drawInteractionRef.current && map) {
map.removeInteraction(drawInteractionRef.current);
drawInteractionRef.current = null;
}
break;
case "select":
// 选定要素,移除所有绘图交互
if (drawInteractionRef.current && map) {
map.removeInteraction(drawInteractionRef.current);
drawInteractionRef.current = null;
}
break;
case "edit":
// 编辑要素,移除所有绘图交互
if (drawInteractionRef.current && map) {
map.removeInteraction(drawInteractionRef.current);
drawInteractionRef.current = null;
}
break;
case "point":
addDrawInteraction("Point");
break;
case "line":
addDrawInteraction("LineString");
break;
case "circle":
addDrawInteraction("Circle");
break;
case "box":
// 使用矩形绘制函数
addDrawInteraction("Circle", createBox());
break;
case "polygon":
addDrawInteraction("Polygon");
break;
case "undo":
handleUndo();
break;
case "redo":
handleRedo();
break;
case "delete":
handleDelete();
break;
case "save":
handleSave();
break;
default:
if (drawInteractionRef.current && map) {
map.removeInteraction(drawInteractionRef.current);
drawInteractionRef.current = null;
}
}
};
// 撤销功能
const handleUndo = () => {
if (historyIndex > 0) {
const newIndex = historyIndex - 1;
const previousFeatures = historyStack[newIndex];
updateDrawLayer(previousFeatures);
setDrawnFeatures(previousFeatures);
setHistoryIndex(newIndex);
}
};
// 重做功能
const handleRedo = () => {
if (historyIndex < historyStack.length - 1) {
const newIndex = historyIndex + 1;
const nextFeatures = historyStack[newIndex];
updateDrawLayer(nextFeatures);
setDrawnFeatures(nextFeatures);
setHistoryIndex(newIndex);
}
};
// 删除所有绘制的要素
const handleDelete = () => {
if (!drawLayer) return;
const source = drawLayer.getSource();
if (source) {
source.clear();
const emptyFeatures: Feature<Geometry>[] = [];
setDrawnFeatures(emptyFeatures);
saveToHistory(emptyFeatures);
}
};
// 保存绘制的要素
const handleSave = () => {};
// 更新绘图图层
const updateDrawLayer = (features: Feature<Geometry>[]) => {
if (!drawLayer) return;
const source = drawLayer.getSource();
if (source) {
source.clear();
source.addFeatures(features);
}
};
// 初始化历史记录
useEffect(() => {
// 初始化空的历史记录
if (historyStack.length === 0) {
saveToHistory([]);
}
}, [historyStack.length, saveToHistory]);
// 判断按钮是否应该禁用
const isUndoDisabled = historyIndex <= 0;
const isRedoDisabled = historyIndex >= historyStack.length - 1;
const isDeleteDisabled = drawnFeatures.length === 0;
const isSaveDisabled = drawnFeatures.length === 0;
return (
<div className="absolute top-20 left-4 bg-white p-1 rounded-xl shadow-lg flex flex-col opacity-85 hover:opacity-100 transition-opacity z-10">
<div className="flex">
<ToolbarButton
icon={<BackHandOutlinedIcon />}
name="平移地图"
isActive={activeTool === "pan"}
onClick={() => handleToolClick("pan")}
/>
<ToolbarButton
icon={<BorderColorOutlinedIcon />}
name="矢量编辑"
isActive={activeTool === "edit"}
onClick={() => handleToolClick("edit")}
/>
<ToolbarButton
icon={<MoreHorizOutlinedIcon />}
name="绘制点"
isActive={activeTool === "point"}
onClick={() => handleToolClick("point")}
/>
<ToolbarButton
icon={<TimelineIcon />}
name="绘制线"
isActive={activeTool === "line"}
onClick={() => handleToolClick("line")}
/>
<ToolbarButton
icon={<CircleOutlinedIcon />}
name="绘制圆"
isActive={activeTool === "circle"}
onClick={() => handleToolClick("circle")}
/>
<ToolbarButton
icon={<CheckBoxOutlineBlankIcon />}
name="绘制框"
isActive={activeTool === "box"}
onClick={() => handleToolClick("box")}
/>
<ToolbarButton
icon={<GestureIcon />}
name="绘制多边形"
isActive={activeTool === "polygon"}
onClick={() => handleToolClick("polygon")}
/>
</div>
<div className="flex mt-1 border-t-1 pt-1">
<ToolbarButton
icon={<UndoIcon />}
name="撤销"
isActive={false}
onClick={() => handleToolClick("undo")}
disabled={isUndoDisabled}
/>
<ToolbarButton
icon={<RedoIcon />}
name="重做"
isActive={false}
onClick={() => handleToolClick("redo")}
disabled={isRedoDisabled}
/>
<ToolbarButton
icon={<DeleteIcon />}
name="删除"
isActive={false}
onClick={() => handleToolClick("delete")}
disabled={isDeleteDisabled}
/>
{/* <ToolbarButton
icon={<SaveIcon />}
name="保存"
isActive={false}
onClick={() => handleToolClick("save")}
disabled={isSaveDisabled}
/> */}
</div>
</div>
);
};
export default DrawPanel;