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[] = []; private overlayMap: Record = {}; private currentOverlayEditor: | BaseOverlayEditor | 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"; } }