class Point implements L.LatLngLiteral { lat: number = 0.00; lng: number = 0.00; } enum OverlayType { POINT = 0, CIRCLE = 1, POLYGON = 2, } interface Overlay { name: string; desc: string; points: Point[]; options: any; } class OverlayData implements Overlay { name: string; desc: string; points: Point[]; options: any; type: OverlayType; constructor(type: OverlayType, name: string, desc: string, points: Point[], options: any) { this.type = type; this.name = name; this.desc = desc; this.points = points; this.options = options; } } abstract class OverlayBase implements Overlay { name: string; desc: string; points: Point[]; options: any; protected self: any; constructor(name: string, desc: string, points: Point[], options: any) { this.name = name; this.desc = desc; this.points = points; this.options = options; } abstract add(map: L.Map): void; abstract remove(map: L.Map): void; abstract menuItem: Node | null; static listAdd(self: OverlayBase, listName: string) { const list = document.getElementById(listName); if (list) { const li = document.createElement("li"); const a = document.createElement("a"); if (li && a) { a.innerText = self.name; a.href = "#"; a.onclick = (e: any) => { //show EditOverlayModal with this overlay's data }; li.appendChild(a); list.appendChild(li); self.menuItem = li; } } } static listRemove(self: OverlayBase, listName: string) { const list = document.getElementById(listName); if (list && self.menuItem) { list.removeChild(self.menuItem); } } } class Marker extends OverlayBase { menuItem: Node | null = null; constructor(name: string, desc: string, point: Point, options: any) { super(name, desc, [ point ], options); this.self = L.marker(point); this.self.bindPopup(`

${name}

${desc}

`); } add(map: L.Map) { this.self.addTo(map); OverlayBase.listAdd(this, "markers-list"); } remove(map: L.Map) { this.self.removeFrom(map); OverlayBase.listRemove(this, "markers-list"); } } class Circle extends OverlayBase { menuItem: Node | null = null; constructor(name: string, desc: string, point: Point, options: any) { super(name, desc, [ point ], options); this.self = L.circle(point, options); this.self.bindPopup(`

${name}

${desc}

`); } add(map: L.Map) { this.self.addTo(map); OverlayBase.listAdd(this, "circles-list"); } remove(map: L.Map) { this.self.removeFrom(map); OverlayBase.listRemove(this, "circles-list"); } } class Polygon extends OverlayBase { menuItem: Node | null = null; constructor(name: string, desc: string, points: Point[], options: any) { super(name, desc, points, options); this.self = L.polygon(points, options); this.self.bindPopup(`

${name}

${desc}

`); } add(map: L.Map) { this.self.addTo(map); OverlayBase.listAdd(this, "polygons-list"); } remove(map: L.Map) { this.self.removeFrom(map); OverlayBase.listRemove(this, "polygons-list"); } } class Polyline extends OverlayBase { menuItem: Node | null = null; constructor() { super("", "", [ ], {}); this.self = L.polyline([]); } insertPoint(pt: Point): void { this.self.addLatLng(pt); this.points.push(pt); } clearPoints(): void { this.points = []; this.self.setLatLngs([]); } numPoints(): number { return this.self.getLatLngs().length; } add(map: L.Map) { this.self.addTo(map); } remove(map: L.Map) { this.self.removeFrom(map); } } class OverlayState { markers: Marker[]; circles: Circle[]; polygons: Polygon[]; polyline: Polyline; constructor() { this.markers = []; this.circles = []; this.polygons = []; this.polyline = new Polyline(); } static load(): OverlayState { const store = localStorage.getItem("overlay_state"); if (store) { const model = JSON.parse(store); return { markers: model.markers.map((m: OverlayData) => OverlayState.fromData(m)), circles: model.circles.map((c: OverlayData) => OverlayState.fromData(c)), polygons: model.polygons.map((p: OverlayData) => OverlayState.fromData(p)), polyline: new Polyline(), } as OverlayState } else { return new OverlayState(); } } static save(overlayState: OverlayState): void { localStorage.setItem("overlay_state", JSON.stringify({ markers: overlayState.markers.map((m: OverlayBase) => OverlayState.toData(m)), circles: overlayState.circles.map((c: OverlayBase) => OverlayState.toData(c)), polygons: overlayState.polygons.map((p: OverlayBase) => OverlayState.toData(p)), })); } static clear(overlayState: OverlayState, map: L.Map): OverlayState { overlayState.markers.forEach((m: Marker) => m.remove(map)); overlayState.circles.forEach((c: Circle) => c.remove(map)); overlayState.polygons.forEach((p: Polygon) => p.remove(map)); return new OverlayState(); } private static toData(source: OverlayBase): OverlayData { let type = OverlayType.POINT; if (source.points.length > 1) { type = OverlayType.POLYGON; } else if (source.options.radius) { type = OverlayType.CIRCLE; } return new OverlayData(type, source.name, source.desc, source.points, source.options); } private static fromData(data: OverlayData): OverlayBase { switch(data.type) { case OverlayType.POINT: return new Marker(data.name, data.desc, data.points[0], data.options); case OverlayType.CIRCLE: return new Circle(data.name, data.desc, data.points[0], data.options); case OverlayType.POLYGON: return new Polygon(data.name, data.desc, data.points, data.options); } } }