From 423a491e8003e4a53b3271664a868c02fdffcaf7 Mon Sep 17 00:00:00 2001 From: Mrkartoshka Date: Mon, 10 Aug 2020 22:52:26 +0300 Subject: [PATCH] Editable Polygons without proper typings --- ui-ngx/package.json | 1 + .../components/widget/lib/maps/leaflet-map.ts | 130 ++++++++- .../components/widget/lib/maps/map-models.ts | 3 + .../components/widget/lib/maps/map-widget2.ts | 54 ++++ .../components/widget/lib/maps/markers.scss | 14 +- .../components/widget/lib/maps/polygon.ts | 20 +- .../widget/lib/maps/providers/google-map.ts | 2 +- .../widget/lib/maps/providers/here-map.ts | 2 +- .../widget/lib/maps/providers/image-map.ts | 39 ++- .../lib/maps/providers/openstreet-map.ts | 2 +- .../widget/lib/maps/providers/tencent-map.ts | 2 +- ui-ngx/src/assets/add_polygon.svg | 3 + ui-ngx/src/typings/add-marker.d.ts | 5 +- ui-ngx/src/typings/leadflet-editable.d.ts | 272 ++++++++++++++++++ ui-ngx/tsconfig.json | 3 +- 15 files changed, 524 insertions(+), 28 deletions(-) create mode 100644 ui-ngx/src/assets/add_polygon.svg create mode 100644 ui-ngx/src/typings/leadflet-editable.d.ts diff --git a/ui-ngx/package.json b/ui-ngx/package.json index 9f6b79d24b..dfde146fb6 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -58,6 +58,7 @@ "jstree-bootstrap-theme": "^1.0.1", "jszip": "^3.4.0", "leaflet": "^1.6.0", + "leaflet-editable": "^1.2.0", "leaflet-polylinedecorator": "^1.6.0", "leaflet-providers": "^1.10.1", "leaflet.gridlayer.googlemutant": "0.10.0", diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts index 49fdd19677..b138112e52 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts @@ -43,6 +43,8 @@ import { createTooltip, parseArray, safeExecute } from '@home/components/widget/ import { WidgetContext } from '@home/models/widget-component.models'; import { DatasourceData } from '@shared/models/widget.models'; import { deepClone, isDefinedAndNotNull } from '@core/utils'; +import {newArray} from "@angular/compiler/src/util"; +import {isArray} from "rxjs/internal-compatibility"; export default abstract class LeafletMap { @@ -168,6 +170,75 @@ export default abstract class LeafletMap { } } + addPolygonControl() { + if (this.options.editablePolygon) { + let mousePositionOnMap: L.LatLng[]; + let addPolygon: L.Control; + this.map.on('mousemove', (e: L.LeafletMouseEvent) => { + let latlng1 = e.latlng; + let latlng2 = L.latLng(e.latlng.lat, e.latlng.lng + 10); + let latlng3 = L.latLng(e.latlng.lat-10, e.latlng.lng); + mousePositionOnMap = [latlng1,latlng2, latlng3 ]; + }); + const dragListener = (e: L.DragEndEvent) => { + if (e.type === 'dragend' && mousePositionOnMap) { + const icon = new L.Icon.Default(); + icon.options.shadowSize = [0, 0]; + const newPolygon = L.polygon(mousePositionOnMap).addTo(this.map); + const datasourcesList = document.createElement('div'); + const customLatLng = {coordinates: this.convertToPolygonFormat(mousePositionOnMap)}; + this.datasources.forEach(ds => { + const dsItem = document.createElement('p'); + dsItem.appendChild(document.createTextNode(ds.entityName)); + dsItem.setAttribute('style', 'font-size: 14px'); + dsItem.onclick = () => { + const updatedEnttity = { ...ds, ...customLatLng }; + this.savePolygonLocation(updatedEnttity).subscribe(() => { + this.map.removeLayer(newPolygon); + this.deletePolygon(ds.entityName); + // this.createPolygon(ds, this.datasources, this.options); + }); + } + datasourcesList.append(dsItem); + }); + const deleteBtn = document.createElement('a'); + deleteBtn.appendChild(document.createTextNode('Delete position')); + deleteBtn.setAttribute('color', 'red'); + deleteBtn.onclick = () => { + this.map.removeLayer(newPolygon); + } + datasourcesList.append(deleteBtn); + const popup = L.popup(); + popup.setContent(datasourcesList); + newPolygon.bindPopup(popup).openPopup(); + } + addPolygon.setPosition('topright') + } + L.Control.AddPolygon = L.Control.extend({ + onAdd() { + const img = L.DomUtil.create('img') as any; + img.src = `assets/add_polygon.svg`; + img.style.width = '32px'; + img.style.height = '32px'; + img.title = 'Drag and drop to add Polygon'; + img.onclick = this.dragPolygonVertex; + img.draggable = true; + const draggableImg = new L.Draggable(img); + draggableImg.enable(); + draggableImg.on('dragend', dragListener) + return img; + }, + onRemove() { + }, + dragPolygonVertex: this.dragPolygonVertex + } as any); + L.control.addPolygon = (opts) => { + return new L.Control.AddPolygon(opts); + } + addPolygon = L.control.addPolygon({ position: 'topright' }).addTo(this.map); + } + } + public setMap(map: L.Map) { this.map = map; if (this.options.useDefaultCenterPosition) { @@ -178,6 +249,9 @@ export default abstract class LeafletMap { if (this.options.draggableMarker) { this.addMarkerControl(); } + if (this.options.editablePolygon) { + this.addPolygonControl(); + } this.map$.next(this.map); } @@ -189,6 +263,10 @@ export default abstract class LeafletMap { return of(null); } + public savePolygonLocation(_e: FormattedData, coordinates?: Array<[number, number]>): Observable { + return of(null); + } + createLatLng(lat: number, lng: number): L.LatLng { return L.latLng(lat, lng); } @@ -255,10 +333,16 @@ export default abstract class LeafletMap { return L.latLng(lat, lng) as L.LatLng; } - convertPositionPolygon(expression: Array<[number, number]>): L.LatLngExpression[] { - return expression.map((el) => { - return el.length === 2 && !el.some(isNaN) ? el : null - }).filter(el => !!el) + convertPositionPolygon(expression: Array<[number, number]> | Array>) { + return (expression as Array).map((el) => { + if (el.length === 2 && !el.some(isNaN)) { + return el; + } else if (isArray(el) && el.length) { + return this.convertPositionPolygon(el); + } else { + return null; + } + }).filter(el => !!el) } convertToCustomFormat(position: L.LatLng): object { @@ -268,6 +352,26 @@ export default abstract class LeafletMap { } } + convertToPolygonFormat(points: Array): Array { + if (points.length) { + return points.map(point=> { + if (point.length) { + return this.convertToPolygonFormat(point); + } else { + return [point.lat, point.lng]; + } + }) + } else { + return [] + } + } + + convertPolygonToCustomFormat(expression: Array>): object { + return { + [this.options.polygonKeyName] : this.convertToPolygonFormat(expression) + } + } + updateData(data: DatasourceData[], formattedData: FormattedData[], drawRoutes: boolean, showPolygon: boolean) { this.ready$.subscribe(() => { if (drawRoutes) { @@ -394,6 +498,15 @@ export default abstract class LeafletMap { } } + deletePolygon(key: string) { + let polygon = this.polygons.get(key)?.leafletPoly; + if (polygon) { + this.map.removeLayer(polygon); + this.polygons.delete(key); + polygon = null; + } + } + updatePoints(pointsData: FormattedData[], getTooltip: (point: FormattedData, setTooltip?: boolean) => string) { this.map$.subscribe(map => { if (this.points) { @@ -509,9 +622,14 @@ export default abstract class LeafletMap { }); } - createPolygon(polyData: FormattedData, dataSources: FormattedData[], settings: PolygonSettings, updateBounds = true) { + dragPolygonVertex = (e?, data = {} as FormattedData) => { + if (e == undefined || (e.type !== 'editable:vertex:dragend' && e.type !== 'editable:vertex:deleted')) return; + this.savePolygonLocation({ ...data, ...this.convertPolygonToCustomFormat(e.layer._latlngs) }).subscribe(); + } + + createPolygon(polyData: FormattedData, dataSources: FormattedData[], settings: PolygonSettings, updateBounds = true) { this.ready$.subscribe(() => { - const polygon = new Polygon(this.map, polyData, dataSources, settings); + const polygon = new Polygon(this.map, polyData, dataSources, settings, this.dragPolygonVertex); if (updateBounds) { const bounds = polygon.leafletPoly.getBounds(); this.fitBounds(bounds); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts index 32cf92eeae..b81c0c1907 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts @@ -34,6 +34,7 @@ export type PosFuncton = (origXPos, origYPos) => { x, y }; export type MapSettings = { draggableMarker: boolean; + editablePolygon: boolean; initCallback?: () => any; posFunction: PosFuncton; defaultZoomLevel?: number; @@ -140,6 +141,7 @@ export type PolygonSettings = { usePolygonColorFunction: boolean; polygonTooltipFunction: GenericFunction; polygonColorFunction?: GenericFunction; + editablePolygon: boolean; } export type PolylineSettings = { @@ -268,6 +270,7 @@ export const defaultSettings: any = { credentials: '', markerClusteringSetting: null, draggableMarker: false, + editablePolygon: false, fitMapBounds: true, mapPageSize: DEFAULT_MAP_PAGE_SIZE }; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts index fd06598fb3..78db682403 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts @@ -79,6 +79,7 @@ export class MapWidgetController implements MapWidgetInterface { this.map = new MapClass(this.ctx, $element, this.settings); (this.ctx as any).mapInstance = this.map; this.map.saveMarkerLocation = this.setMarkerLocation; + this.map.savePolygonLocation = this.savePolygonLocation; this.pageLink = { page: 0, pageSize: this.settings.mapPageSize, @@ -239,6 +240,56 @@ export class MapWidgetController implements MapWidgetInterface { } } + savePolygonLocation = (e: FormattedData, coordinates?: Array) => { + const attributeService = this.ctx.$injector.get(AttributeService); + + const entityId: EntityId = { + entityType: e.$datasource.entityType, + id: e.$datasource.entityId + }; + const attributes = []; + const timeseries = []; + + const coordinatesProperties = this.settings.polygonKeyName; + e.$datasource.dataKeys.forEach(key => { + let value; + if (coordinatesProperties == key.name) { + value = { + key: key.name, + value: isDefined(coordinates) ? coordinates : e[key.name] + }; + } + if (value) { + if (key.type === DataKeyType.attribute) { + attributes.push(value) + } + if (key.type === DataKeyType.timeseries) { + timeseries.push(value) + } + } + }); + const observables: Observable[] = []; + if (timeseries.length) { + observables.push(attributeService.saveEntityTimeseries( + entityId, + LatestTelemetry.LATEST_TELEMETRY, + timeseries + )); + } + if (attributes.length) { + observables.push(attributeService.saveEntityAttributes( + entityId, + AttributeScope.SERVER_SCOPE, + attributes + )); + } + if (observables.length) { + return forkJoin(observables); + } else { + return of(null); + } + } + initSettings(settings: UnitedMapSettings, isEditMap?: boolean): UnitedMapSettings { const functionParams = ['data', 'dsData', 'dsIndex']; this.provider = settings.provider || this.mapProvider; @@ -270,6 +321,9 @@ export class MapWidgetController implements MapWidgetInterface { if (isEditMap && !settings.hasOwnProperty('draggableMarker')) { settings.draggableMarker = true; } + if (isEditMap && !settings.hasOwnProperty('editablePolygon')) { + settings.editablePolygon = true; + } return { ...defaultSettings, ...settings, ...customOptions, } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.scss b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.scss index e7cb470ec7..37dfb7781b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.scss @@ -22,13 +22,13 @@ background-repeat: no-repeat; } -.leaflet-div-icon, -.tb-marker-label, -.tb-marker-label:before { - border: none; - background: none; - box-shadow: none; -} +//.leaflet-div-icon, +//.tb-marker-label, +//.tb-marker-label:before { +// border: none; +// background: none; +// box-shadow: none; +//} .leaflet-container{ background-color: white; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts index 2af02b2878..bbc529d832 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts @@ -14,7 +14,8 @@ /// limitations under the License. /// -import L, { LatLngExpression, LeafletMouseEvent } from 'leaflet'; +import L, { LatLngExpression, LeafletMouseEvent} from 'leaflet'; +import "leaflet-editable/src/Leaflet.Editable"; import { createTooltip, parseWithTranslation, safeExecute } from './maps-utils'; import { FormattedData, PolygonSettings } from './map-models'; @@ -25,11 +26,10 @@ export class Polygon { data: FormattedData; dataSources: FormattedData[]; - constructor(public map, polyData: FormattedData, dataSources: FormattedData[], private settings: PolygonSettings) { + constructor(public map, polyData: FormattedData, dataSources: FormattedData[], private settings: PolygonSettings, onDragendListener?) { this.dataSources = dataSources; this.data = polyData; const polygonColor = this.getPolygonColor(settings); - this.leafletPoly = L.polygon(polyData[this.settings.polygonKeyName], { fill: true, fillColor: polygonColor, @@ -38,6 +38,14 @@ export class Polygon { fillOpacity: settings.polygonOpacity, opacity: settings.polygonStrokeOpacity }).addTo(this.map); + if (settings.editablePolygon) { + this.leafletPoly.enableEdit(this.map); + if (onDragendListener) { + this.leafletPoly.on("editable:vertex:dragend", e => onDragendListener(e, this.data)); + this.leafletPoly.on("editable:vertex:deleted", e => onDragendListener(e, this.data)); + } + } + if (settings.showPolygonTooltip) { this.tooltip = createTooltip(this.leafletPoly, settings, polyData.$datasource); @@ -64,7 +72,13 @@ export class Polygon { updatePolygon(data: FormattedData, dataSources: FormattedData[], settings: PolygonSettings) { this.data = data; this.dataSources = dataSources; + if (settings.editablePolygon) { + this.leafletPoly.disableEdit(); + } this.leafletPoly.setLatLngs(data[this.settings.polygonKeyName]); + if (settings.editablePolygon) { + this.leafletPoly.enableEdit(this.map); + } if (settings.showPolygonTooltip) this.updateTooltip(this.data); this.updatePolygonColor(settings); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/google-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/google-map.ts index b0254ee2c2..114ff49ab2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/google-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/google-map.ts @@ -36,7 +36,7 @@ export class GoogleMap extends LeafletMap { super(ctx, $container, options); this.resource = ctx.$injector.get(ResourcesService); this.loadGoogle(() => { - const map = L.map($container, {attributionControl: false}).setView(options?.defaultCenterPosition, options?.defaultZoomLevel); + const map = L.map($container, {attributionControl: false, editable: !!options.editablePolygon}).setView(options?.defaultCenterPosition, options?.defaultZoomLevel); (L.gridLayer as any).googleMutant({ type: options?.gmDefaultMapType || 'roadmap' }).addTo(map); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/here-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/here-map.ts index 17a8f61801..4855bb740c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/here-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/here-map.ts @@ -22,7 +22,7 @@ import { WidgetContext } from '@home/models/widget-component.models'; export class HEREMap extends LeafletMap { constructor(ctx: WidgetContext, $container, options: UnitedMapSettings) { super(ctx, $container, options); - const map = L.map($container).setView(options?.defaultCenterPosition, options?.defaultZoomLevel); + const map = L.map($container, {editable: !!options.editablePolygon}).setView(options?.defaultCenterPosition, options?.defaultZoomLevel); const tileLayer = (L.tileLayer as any).provider(options.mapProviderHere || 'HERE.normalDay', options.credentials); tileLayer.addTo(map); super.setMap(map); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts index b8c0a9c82e..4b2325fcf9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts @@ -24,7 +24,9 @@ import { WidgetContext } from '@home/models/widget-component.models'; import { DataSet, DatasourceType, widgetType } from '@shared/models/widget.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { WidgetSubscriptionOptions } from '@core/api/widget-api.models'; -import { isDefinedAndNotNull } from '@core/utils'; +import {isDefinedAndNotNull, isNumber} from '@core/utils'; +import "leaflet-editable/src/Leaflet.Editable"; +import {isArray} from "rxjs/internal-compatibility"; const maxZoom = 4;// ? @@ -196,14 +198,15 @@ export class ImageMap extends LeafletMap { initMap(updateImage?: boolean) { if (!this.map && this.aspect > 0) { const center = this.pointToLatLng(this.width / 2, this.height / 2); - this.map = L.map(this.$container, { + this.map = L.map(this.$container, { minZoom: 1, maxZoom, scrollWheelZoom: !this.options.disableScrollZooming, center, zoom: 1, crs: L.CRS.Simple, - attributionControl: false + attributionControl: false, + editable: !!this.options.editablePolygon }); this.updateBounds(updateImage); } @@ -221,14 +224,17 @@ export class ImageMap extends LeafletMap { expression.y * this.height); } - convertPositionPolygon(expression: Array<[number, number]>): L.LatLngExpression[] { - return expression.map((el) => { + convertPositionPolygon(expression: Array<[number, number]> | Array>) { + return (expression as Array).map((el) => { if (el.length === 2 && !el.some(isNaN)) { return this.pointToLatLng( el[0] * this.width, el[1] * this.height) + } else if (isArray(el) && el.length) { + return this.convertPositionPolygon(el); + } else { + return null; } - return null; }).filter(el => !!el) } @@ -247,4 +253,25 @@ export class ImageMap extends LeafletMap { [this.options.yPosKeyName]: calculateNewPointCoordinate(point.y, this.height) } } + + convertToPolygonFormat(points: Array): Array { + if (points.length) { + return points.map(point=> { + if (point.length) { + return this.convertToPolygonFormat(point); + } else { + let pos = this.latLngToPoint(point); + return [calculateNewPointCoordinate(pos.x, this.width), calculateNewPointCoordinate(pos.y, this.height)]; + } + }) + } else { + return [] + } + } + + convertPolygonToCustomFormat(expression: Array>): object { + return { + [this.options.polygonKeyName] : this.convertToPolygonFormat(expression) + } + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/openstreet-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/openstreet-map.ts index d1e2379dbd..fe55ff635e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/openstreet-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/openstreet-map.ts @@ -22,7 +22,7 @@ import { WidgetContext } from '@home/models/widget-component.models'; export class OpenStreetMap extends LeafletMap { constructor(ctx: WidgetContext, $container, options: UnitedMapSettings) { super(ctx, $container, options); - const map = L.map($container).setView(options?.defaultCenterPosition, options?.defaultZoomLevel); + const map = new L.Map($container, {editable: !!options.editablePolygon}).setView(options?.defaultCenterPosition, options?.defaultZoomLevel); let tileLayer; if (options.useCustomProvider) tileLayer = L.tileLayer(options.customProviderTileUrl); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/tencent-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/tencent-map.ts index a615de883a..9c62730f06 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/tencent-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/tencent-map.ts @@ -24,7 +24,7 @@ export class TencentMap extends LeafletMap { constructor(ctx: WidgetContext, $container, options: UnitedMapSettings) { super(ctx, $container, options); const txUrl = 'http://rt{s}.map.gtimg.com/realtimerender?z={z}&x={x}&y={y}&type=vector&style=0'; - const map = L.map($container).setView(options?.defaultCenterPosition, options?.defaultZoomLevel); + const map = L.map($container, {editable: !!options.editablePolygon}).setView(options?.defaultCenterPosition, options?.defaultZoomLevel); const txLayer = L.tileLayer(txUrl, { subdomains: '0123', tms: true, diff --git a/ui-ngx/src/assets/add_polygon.svg b/ui-ngx/src/assets/add_polygon.svg new file mode 100644 index 0000000000..098f02c128 --- /dev/null +++ b/ui-ngx/src/assets/add_polygon.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui-ngx/src/typings/add-marker.d.ts b/ui-ngx/src/typings/add-marker.d.ts index 8da38ae76a..565ecdddd4 100644 --- a/ui-ngx/src/typings/add-marker.d.ts +++ b/ui-ngx/src/typings/add-marker.d.ts @@ -20,9 +20,12 @@ declare module 'leaflet' { namespace Control { class AddMarker extends L.Control { } + class AddPolygon extends L.Control { } } namespace control { function addMarker(options): Control.AddMarker; + function addPolygon(options): Control.AddPolygon; } -} \ No newline at end of file + +} diff --git a/ui-ngx/src/typings/leadflet-editable.d.ts b/ui-ngx/src/typings/leadflet-editable.d.ts new file mode 100644 index 0000000000..8ec3a3e92c --- /dev/null +++ b/ui-ngx/src/typings/leadflet-editable.d.ts @@ -0,0 +1,272 @@ +/// +/// Copyright © 2016-2020 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import * as Leaflet from 'leaflet'; + +declare module 'leaflet' { + + /** + * Make geometries editable in Leaflet. + * + * This is not a plug and play UI, and will not. This is a minimal, lightweight, and fully extendable API to + * control editing of geometries. So you can easily build your own UI with your own needs and choices. + */ + interface EditableStatic { + new (map: Map, options: EditOptions): Editable; + } + + /** + * Options to pass to L.Editable when instanciating. + */ + interface EditOptions extends Leaflet.MapOptions{ + /** + * Class to be used when creating a new Polyline. + */ + polylineClass?: object; + + /** + * Class to be used when creating a new Polygon. + */ + polygonClass?: object; + + /** + * Class to be used when creating a new Marker. + */ + markerClass?: object; + + /** + * CSS class to be added to the map container while drawing. + */ + drawingCSSClass?: string; + + /** + * Layer used to store edit tools (vertex, line guide…). + */ + editLayer?: LayerGroup; + + /** + * Default layer used to store drawn features (marker, polyline…). + */ + featuresLayer?: LayerGroup; + + /** + * Class to be used as vertex, for path editing. + */ + vertexMarkerClass?: object; + + /** + * Class to be used as middle vertex, pulled by the user to create a new point in the middle of a path. + */ + middleMarkerClass?: object; + + /** + * Class to be used as Polyline editor. + */ + polylineEditorClass?: object; + + /** + * Class to be used as Polygon editor. + */ + polygonEditorClass?: object; + + /** + * Class to be used as Marker editor. + */ + markerEditorClass?: object; + + /** + * Options to be passed to the line guides. + */ + lineGuideOptions?: object; + + /** + * Set this to true if you don't want middle markers. + */ + skipMiddleMarkers?: boolean; + } + + /** + * Make geometries editable in Leaflet. + * + * This is not a plug and play UI, and will not. This is a minimal, lightweight, and fully extendable API to + * control editing of geometries. So you can easily build your own UI with your own needs and choices. + */ + interface Editable extends Leaflet.Evented { + /** + * Options to pass to L.Editable when instanciating. + */ + options: EditOptions; + + currentPolygon: Polyline|Polygon|Marker; + + /** + * Start drawing a polyline. If latlng is given, a first point will be added. In any case, continuing on user + * click. If options is given, it will be passed to the polyline class constructor. + */ + startPolyline(latLng?: LatLng, options?: PolylineOptions): Polyline; + + /** + * Start drawing a polygon. If latlng is given, a first point will be added. In any case, continuing on user + * click. If options is given, it will be passed to the polygon class constructor. + */ + startPolygon(latLng?: LatLng, options?: PolylineOptions): Polygon; + + /** + * Start adding a marker. If latlng is given, the marker will be shown first at this point. In any case, it + * will follow the user mouse, and will have a final latlng on next click (or touch). If options is given, + * it will be passed to the marker class constructor. + */ + startMarker(latLng?: LatLng, options?: MarkerOptions): Marker; + + /** + * When you need to stop any ongoing drawing, without needing to know which editor is active. + */ + stopDrawing(): void; + + /** + * When you need to commit any ongoing drawing, without needing to know which editor is active. + */ + commitDrawing(): void; + } + + let Editable: EditableStatic; + + /** + * EditableMixin is included to L.Polyline, L.Polygon and L.Marker. It adds the following methods to them. + * + * When editing is enabled, the editor is accessible on the instance with the editor property. + */ + interface EditableMixin { + /** + * Enable editing, by creating an editor if not existing, and then calling enable on it. + */ + enableEdit(map: L.Map): any; + + /** + * Disable editing, also remove the editor property reference. + */ + disableEdit(): void; + + /** + * Enable or disable editing, according to current status. + */ + toggleEdit(): void; + + /** + * Return true if current instance has an editor attached, and this editor is enabled. + */ + editEnabled(): boolean; + } + + interface Map { + /** + * Whether to create a L.Editable instance at map init or not. + */ + editable: boolean; + + /** + * Options to pass to L.Editable when instanciating. + */ + editOptions: EditOptions; + + /** + * L.Editable plugin instance. + */ + editTools: Editable; + } + + // tslint:disable-next-line:no-empty-interface + interface Polyline extends EditableMixin {} + + namespace Map { + interface MapOptions { + /** + * Whether to create a L.Editable instance at map init or not. + */ + editable?: boolean; + + /** + * Options to pass to L.Editable when instanciating. + */ + editOptions?: EditOptions; + } + } + + /** + * When editing a feature (marker, polyline…), an editor is attached to it. This editor basically knows + * how to handle the edition. + */ + interface BaseEditor { + /** + * Set up the drawing tools for the feature to be editable. + */ + enable(): MarkerEditor|PolylineEditor|PolygonEditor; + + /** + * Remove editing tools. + */ + disable(): MarkerEditor|PolylineEditor|PolygonEditor; + } + + /** + * Inherit from L.Editable.BaseEditor. + * Inherited by L.Editable.PolylineEditor and L.Editable.PolygonEditor. + */ + interface PathEditor extends BaseEditor { + /** + * Rebuild edit elements (vertex, middlemarker, etc.). + */ + reset(): void; + } + + /** + * Inherit from L.Editable.PathEditor. + */ + interface PolylineEditor extends PathEditor { + /** + * Set up drawing tools to continue the line forward. + */ + continueForward(): void; + + /** + * Set up drawing tools to continue the line backward. + */ + continueBackward(): void; + } + + /** + * Inherit from L.Editable.PathEditor. + */ + interface PolygonEditor extends PathEditor { + /** + * Set up drawing tools for creating a new hole on the polygon. If the latlng param is given, a first + * point is created. + */ + newHole(latlng: LatLng): void; + } + + /** + * Inherit from L.Editable.BaseEditor. + */ + // tslint:disable-next-line:no-empty-interface + interface MarkerEditor extends BaseEditor {} + + interface Marker extends EditableMixin, MarkerEditor {} + + interface Polyline extends EditableMixin, PolylineEditor {} + + interface Polygon extends EditableMixin, PolygonEditor {} +} diff --git a/ui-ngx/tsconfig.json b/ui-ngx/tsconfig.json index bef693e404..c5eec536a9 100644 --- a/ui-ngx/tsconfig.json +++ b/ui-ngx/tsconfig.json @@ -20,7 +20,8 @@ "src/typings/jquery.flot.typings.d.ts", "src/typings/jquery.jstree.typings.d.ts", "src/typings/split.js.typings.d.ts", - "src/typings/add-marker.d.ts" + "src/typings/add-marker.d.ts", + "src/typings/leaflet-editable.d.ts" ], "paths": { "@app/*": ["src/app/*"],