完成页面的基础配置
This commit is contained in:
382
src/app/OlMap/Controls/DrawPanel.tsx
Normal file
382
src/app/OlMap/Controls/DrawPanel.tsx
Normal file
@@ -0,0 +1,382 @@
|
||||
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">
|
||||
<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;
|
||||
Reference in New Issue
Block a user