|
|
|
import Emitter from "@finevis/emitter";
|
|
|
|
import "@amap/amap-jsapi-types";
|
|
|
|
|
|
|
|
import { getOverlayPaths, getUID, registerHotkey } from "@utils";
|
|
|
|
|
|
|
|
import {
|
|
|
|
BaseOverlayEditor,
|
|
|
|
RectangleEditor,
|
|
|
|
PolygonEditor,
|
|
|
|
PolylineEditor,
|
|
|
|
CircleEditor,
|
|
|
|
} from "./editors";
|
|
|
|
|
|
|
|
import { IMapOptions, IMapEditor } from "./type";
|
|
|
|
|
|
|
|
import { PolygonOptions, PolylineOptions, SelectedOptions } from "./constants";
|
|
|
|
|
|
|
|
import { EventTypes, OverlayTypes } from "../types/enum";
|
|
|
|
|
|
|
|
type AMapOverlayEditor =
|
|
|
|
| AMap.RectangleEditor
|
|
|
|
| AMap.PolygonEditor
|
|
|
|
| AMap.PolylineEditor
|
|
|
|
| AMap.CircleEditor;
|
|
|
|
|
|
|
|
type OverlayTemp = {
|
|
|
|
type: OverlayTypes;
|
|
|
|
target: AMap.MapOverlay;
|
|
|
|
};
|
|
|
|
|
|
|
|
const getOverlayOptions = (type: OverlayTypes) =>
|
|
|
|
type === OverlayTypes.Polyline ? PolylineOptions : PolygonOptions;
|
|
|
|
export class MapEditor extends Emitter implements IMapEditor {
|
|
|
|
dom: HTMLDivElement;
|
|
|
|
private _map: AMap.Map | undefined;
|
|
|
|
private overlayEditors: BaseOverlayEditor<AMapOverlayEditor>[] = [];
|
|
|
|
private overlayMap: Record<string, OverlayTemp> = {};
|
|
|
|
private currentOverlayEditor:
|
|
|
|
| BaseOverlayEditor<AMapOverlayEditor>
|
|
|
|
| undefined;
|
|
|
|
private selectedIds: string[] = [];
|
|
|
|
private editorStatus: "editing" | "creating" | null = null;
|
|
|
|
constructor(dom: HTMLDivElement) {
|
|
|
|
super();
|
|
|
|
this.dom = dom;
|
|
|
|
}
|
|
|
|
|
|
|
|
get map() {
|
|
|
|
return this._map!;
|
|
|
|
}
|
|
|
|
|
|
|
|
async init() {
|
|
|
|
await AMapLoader.load({
|
|
|
|
key: "a4171ad2d7df42823b4de7d25c8c35ee",
|
|
|
|
version: "2.0",
|
|
|
|
plugins: [
|
|
|
|
"AMap.RectangleEditor",
|
|
|
|
"AMap.PolylineEditor",
|
|
|
|
"AMap.PolygonEditor",
|
|
|
|
"AMap.CircleEditor",
|
|
|
|
"AMap.PlaceSearch",
|
|
|
|
"AMap.AutoComplete",
|
|
|
|
],
|
|
|
|
});
|
|
|
|
this._map = new AMap.Map(this.dom);
|
|
|
|
this.initEditors();
|
|
|
|
// Space的key是空字符串, 这就离谱.
|
|
|
|
registerHotkey(" ", { callback: this.finishEditOverlay.bind(this) });
|
|
|
|
registerHotkey("e", {
|
|
|
|
callback: this.editSelectedOverlay.bind(this),
|
|
|
|
alt: true,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
initEditors() {
|
|
|
|
const { map } = this;
|
|
|
|
this.overlayEditors = [
|
|
|
|
new RectangleEditor(map),
|
|
|
|
new PolygonEditor(map),
|
|
|
|
new PolylineEditor(map),
|
|
|
|
new CircleEditor(map),
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
getEditorByType(overlayType: OverlayTypes) {
|
|
|
|
return this.overlayEditors.find(
|
|
|
|
(editor) => editor.getType() === overlayType
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
importProject(options: IMapOptions) {
|
|
|
|
this.clearOverlays();
|
|
|
|
const { rectangles, polygons, polylines, circles } = options;
|
|
|
|
const rectangleEditor = this.getEditorByType(OverlayTypes.Rectangle);
|
|
|
|
const polygonEditor = this.getEditorByType(OverlayTypes.Polygon);
|
|
|
|
const polylineEditor = this.getEditorByType(OverlayTypes.Polyline);
|
|
|
|
const circleEditor = this.getEditorByType(OverlayTypes.Circle);
|
|
|
|
|
|
|
|
const addOverlay = (
|
|
|
|
id: string,
|
|
|
|
target: AMap.MapOverlay,
|
|
|
|
type: OverlayTypes
|
|
|
|
) => {
|
|
|
|
this.overlayMap[id] = {
|
|
|
|
type,
|
|
|
|
target,
|
|
|
|
};
|
|
|
|
target.setOptions(
|
|
|
|
type === OverlayTypes.Polyline ? PolylineOptions : PolygonOptions
|
|
|
|
);
|
|
|
|
this.map.add(target);
|
|
|
|
};
|
|
|
|
|
|
|
|
rectangles.forEach((rect) =>
|
|
|
|
addOverlay(rect.id, rectangleEditor!.build(rect), OverlayTypes.Rectangle)
|
|
|
|
);
|
|
|
|
polygons.forEach((polygon) =>
|
|
|
|
addOverlay(
|
|
|
|
polygon.id,
|
|
|
|
polygonEditor!.build(polygon),
|
|
|
|
OverlayTypes.Polygon
|
|
|
|
)
|
|
|
|
);
|
|
|
|
polylines.forEach((polyline) =>
|
|
|
|
addOverlay(
|
|
|
|
polyline.id,
|
|
|
|
polylineEditor!.build(polyline),
|
|
|
|
OverlayTypes.Polyline
|
|
|
|
)
|
|
|
|
);
|
|
|
|
circles.forEach((circle) =>
|
|
|
|
addOverlay(circle.id, circleEditor!.build(circle), OverlayTypes.Circle)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
importGeoJSON(geojson: GeoJSON.FeatureCollection): void {
|
|
|
|
throw new Error("Method not implemented.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// 清空所有覆盖物
|
|
|
|
clearOverlays() {
|
|
|
|
for (let id in this.overlayMap) {
|
|
|
|
this.map.remove(this.overlayMap[id].target);
|
|
|
|
}
|
|
|
|
this.overlayMap = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
createOverlay(type: OverlayTypes) {
|
|
|
|
this.currentOverlayEditor = this.getEditorByType(type!);
|
|
|
|
this.currentOverlayEditor?.create();
|
|
|
|
this.editorStatus = "creating";
|
|
|
|
}
|
|
|
|
|
|
|
|
selectOverlays(ids?: string[]) {
|
|
|
|
this.selectedIds?.forEach((id) => {
|
|
|
|
const { target, type } = this.overlayMap[id];
|
|
|
|
target.setOptions(getOverlayOptions(type));
|
|
|
|
});
|
|
|
|
if (ids == null) return;
|
|
|
|
this.selectedIds = ids;
|
|
|
|
ids.forEach((id) => {
|
|
|
|
const { target } = this.overlayMap[id];
|
|
|
|
target.setOptions(SelectedOptions);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
deleteOverlays() {
|
|
|
|
if (this.selectedIds?.length === 0) return;
|
|
|
|
this.selectedIds.forEach((id) => {
|
|
|
|
const { target } = this.overlayMap[id];
|
|
|
|
this.map.remove(target);
|
|
|
|
delete this.overlayMap[id];
|
|
|
|
});
|
|
|
|
this.selectedIds = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
finishEditOverlay() {
|
|
|
|
if (this.currentOverlayEditor == null) return;
|
|
|
|
const target = this.currentOverlayEditor.finish();
|
|
|
|
if (target == null) return;
|
|
|
|
const type = this.currentOverlayEditor.getType();
|
|
|
|
const isCreatingOverlay = this.editorStatus === "creating";
|
|
|
|
let id = getUID();
|
|
|
|
if (isCreatingOverlay) {
|
|
|
|
this.overlayMap[id] = { type, target };
|
|
|
|
} else {
|
|
|
|
id = this.selectedIds[0];
|
|
|
|
}
|
|
|
|
const evt: any = { id, type };
|
|
|
|
if (type === OverlayTypes.Circle) {
|
|
|
|
const circle = target as AMap.Circle;
|
|
|
|
const { lng, lat } = circle.getCenter();
|
|
|
|
evt.lngLat = [lng, lat];
|
|
|
|
evt.radius = circle.getRadius();
|
|
|
|
} else {
|
|
|
|
evt.path = getOverlayPaths(target, type);
|
|
|
|
}
|
|
|
|
target.setOptions(
|
|
|
|
type === OverlayTypes.Polyline ? PolylineOptions : PolygonOptions
|
|
|
|
);
|
|
|
|
this.emit(EventTypes.FinishEditOverlay, evt);
|
|
|
|
this.editorStatus = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
editSelectedOverlay() {
|
|
|
|
if (this.selectedIds.length !== 1) return;
|
|
|
|
const [id] = this.selectedIds;
|
|
|
|
const { target, type } = this.overlayMap[id];
|
|
|
|
this.currentOverlayEditor = this.getEditorByType(type);
|
|
|
|
this.currentOverlayEditor?.edit(target);
|
|
|
|
this.editorStatus = "editing";
|
|
|
|
}
|
|
|
|
}
|