import Emitter from "@finevis/emitter"; import "@amap/amap-jsapi-types"; import { IMapState } from "@store"; import { getOverlayPaths } from "@utils"; import { BaseOverlayEditor, RectangleEditor, PolygonEditor, PolylineEditor, CircleEditor, } from "./editors"; import { registerHotkey } from "../utils/hotkeys"; import { PolygonOptions, PolylineOptions, SelectedOptions } from "./constants"; import { Command, EventTypes, OverlayTypes } from "../types/enum"; let uuid = 0; const getUuid = () => ++uuid + ""; type AMapOverlayEditor = | AMap.RectangleEditor | AMap.PolygonEditor | AMap.PolylineEditor | AMap.CircleEditor; type OverlayTemp = { type: OverlayTypes; target: AMap.MapOverlay; }; export class MapEditor extends Emitter { 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, }); } update(mapState: IMapState) { if (this._editorStatus != null) { this._finishEditOverlay(); } const { command } = mapState; switch (command) { case Command.CreateOverlay: this._createOverlay(mapState); break; case Command.SelectOverlay: this._selectOverlays(mapState.selectedIds); break; case Command.DeleteOverlays: this._deleteOverlays(); } } 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 ); } _createOverlay(mapState: IMapState) { const { overlayType } = mapState; this._currentOverlayEditor = this.getEditorByType(overlayType!); this._currentOverlayEditor?.create(); this._editorStatus = "creating"; } _selectOverlays(ids?: string[]) { if (!ids?.length) 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); }); } _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 = getUuid(); 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"; } }