Commit 423a491e8003e4a53b3271664a868c02fdffcaf7

Authored by Mrkartoshka
1 parent 8750b26f

Editable Polygons without proper typings

... ... @@ -58,6 +58,7 @@
58 58 "jstree-bootstrap-theme": "^1.0.1",
59 59 "jszip": "^3.4.0",
60 60 "leaflet": "^1.6.0",
  61 + "leaflet-editable": "^1.2.0",
61 62 "leaflet-polylinedecorator": "^1.6.0",
62 63 "leaflet-providers": "^1.10.1",
63 64 "leaflet.gridlayer.googlemutant": "0.10.0",
... ...
... ... @@ -43,6 +43,8 @@ import { createTooltip, parseArray, safeExecute } from '@home/components/widget/
43 43 import { WidgetContext } from '@home/models/widget-component.models';
44 44 import { DatasourceData } from '@shared/models/widget.models';
45 45 import { deepClone, isDefinedAndNotNull } from '@core/utils';
  46 +import {newArray} from "@angular/compiler/src/util";
  47 +import {isArray} from "rxjs/internal-compatibility";
46 48
47 49 export default abstract class LeafletMap {
48 50
... ... @@ -168,6 +170,75 @@ export default abstract class LeafletMap {
168 170 }
169 171 }
170 172
  173 + addPolygonControl() {
  174 + if (this.options.editablePolygon) {
  175 + let mousePositionOnMap: L.LatLng[];
  176 + let addPolygon: L.Control;
  177 + this.map.on('mousemove', (e: L.LeafletMouseEvent) => {
  178 + let latlng1 = e.latlng;
  179 + let latlng2 = L.latLng(e.latlng.lat, e.latlng.lng + 10);
  180 + let latlng3 = L.latLng(e.latlng.lat-10, e.latlng.lng);
  181 + mousePositionOnMap = [latlng1,latlng2, latlng3 ];
  182 + });
  183 + const dragListener = (e: L.DragEndEvent) => {
  184 + if (e.type === 'dragend' && mousePositionOnMap) {
  185 + const icon = new L.Icon.Default();
  186 + icon.options.shadowSize = [0, 0];
  187 + const newPolygon = L.polygon(mousePositionOnMap).addTo(this.map);
  188 + const datasourcesList = document.createElement('div');
  189 + const customLatLng = {coordinates: this.convertToPolygonFormat(mousePositionOnMap)};
  190 + this.datasources.forEach(ds => {
  191 + const dsItem = document.createElement('p');
  192 + dsItem.appendChild(document.createTextNode(ds.entityName));
  193 + dsItem.setAttribute('style', 'font-size: 14px');
  194 + dsItem.onclick = () => {
  195 + const updatedEnttity = { ...ds, ...customLatLng };
  196 + this.savePolygonLocation(updatedEnttity).subscribe(() => {
  197 + this.map.removeLayer(newPolygon);
  198 + this.deletePolygon(ds.entityName);
  199 + // this.createPolygon(ds, this.datasources, this.options);
  200 + });
  201 + }
  202 + datasourcesList.append(dsItem);
  203 + });
  204 + const deleteBtn = document.createElement('a');
  205 + deleteBtn.appendChild(document.createTextNode('Delete position'));
  206 + deleteBtn.setAttribute('color', 'red');
  207 + deleteBtn.onclick = () => {
  208 + this.map.removeLayer(newPolygon);
  209 + }
  210 + datasourcesList.append(deleteBtn);
  211 + const popup = L.popup();
  212 + popup.setContent(datasourcesList);
  213 + newPolygon.bindPopup(popup).openPopup();
  214 + }
  215 + addPolygon.setPosition('topright')
  216 + }
  217 + L.Control.AddPolygon = L.Control.extend({
  218 + onAdd() {
  219 + const img = L.DomUtil.create('img') as any;
  220 + img.src = `assets/add_polygon.svg`;
  221 + img.style.width = '32px';
  222 + img.style.height = '32px';
  223 + img.title = 'Drag and drop to add Polygon';
  224 + img.onclick = this.dragPolygonVertex;
  225 + img.draggable = true;
  226 + const draggableImg = new L.Draggable(img);
  227 + draggableImg.enable();
  228 + draggableImg.on('dragend', dragListener)
  229 + return img;
  230 + },
  231 + onRemove() {
  232 + },
  233 + dragPolygonVertex: this.dragPolygonVertex
  234 + } as any);
  235 + L.control.addPolygon = (opts) => {
  236 + return new L.Control.AddPolygon(opts);
  237 + }
  238 + addPolygon = L.control.addPolygon({ position: 'topright' }).addTo(this.map);
  239 + }
  240 + }
  241 +
171 242 public setMap(map: L.Map) {
172 243 this.map = map;
173 244 if (this.options.useDefaultCenterPosition) {
... ... @@ -178,6 +249,9 @@ export default abstract class LeafletMap {
178 249 if (this.options.draggableMarker) {
179 250 this.addMarkerControl();
180 251 }
  252 + if (this.options.editablePolygon) {
  253 + this.addPolygonControl();
  254 + }
181 255 this.map$.next(this.map);
182 256 }
183 257
... ... @@ -189,6 +263,10 @@ export default abstract class LeafletMap {
189 263 return of(null);
190 264 }
191 265
  266 + public savePolygonLocation(_e: FormattedData, coordinates?: Array<[number, number]>): Observable<any> {
  267 + return of(null);
  268 + }
  269 +
192 270 createLatLng(lat: number, lng: number): L.LatLng {
193 271 return L.latLng(lat, lng);
194 272 }
... ... @@ -255,10 +333,16 @@ export default abstract class LeafletMap {
255 333 return L.latLng(lat, lng) as L.LatLng;
256 334 }
257 335
258   - convertPositionPolygon(expression: Array<[number, number]>): L.LatLngExpression[] {
259   - return expression.map((el) => {
260   - return el.length === 2 && !el.some(isNaN) ? el : null
261   - }).filter(el => !!el)
  336 + convertPositionPolygon(expression: Array<[number, number]> | Array<Array<[number, number]>>) {
  337 + return (expression as Array<any>).map((el) => {
  338 + if (el.length === 2 && !el.some(isNaN)) {
  339 + return el;
  340 + } else if (isArray(el) && el.length) {
  341 + return this.convertPositionPolygon(el);
  342 + } else {
  343 + return null;
  344 + }
  345 + }).filter(el => !!el)
262 346 }
263 347
264 348 convertToCustomFormat(position: L.LatLng): object {
... ... @@ -268,6 +352,26 @@ export default abstract class LeafletMap {
268 352 }
269 353 }
270 354
  355 + convertToPolygonFormat(points: Array<any>): Array<any> {
  356 + if (points.length) {
  357 + return points.map(point=> {
  358 + if (point.length) {
  359 + return this.convertToPolygonFormat(point);
  360 + } else {
  361 + return [point.lat, point.lng];
  362 + }
  363 + })
  364 + } else {
  365 + return []
  366 + }
  367 + }
  368 +
  369 + convertPolygonToCustomFormat(expression: Array<Array<any>>): object {
  370 + return {
  371 + [this.options.polygonKeyName] : this.convertToPolygonFormat(expression)
  372 + }
  373 + }
  374 +
271 375 updateData(data: DatasourceData[], formattedData: FormattedData[], drawRoutes: boolean, showPolygon: boolean) {
272 376 this.ready$.subscribe(() => {
273 377 if (drawRoutes) {
... ... @@ -394,6 +498,15 @@ export default abstract class LeafletMap {
394 498 }
395 499 }
396 500
  501 + deletePolygon(key: string) {
  502 + let polygon = this.polygons.get(key)?.leafletPoly;
  503 + if (polygon) {
  504 + this.map.removeLayer(polygon);
  505 + this.polygons.delete(key);
  506 + polygon = null;
  507 + }
  508 + }
  509 +
397 510 updatePoints(pointsData: FormattedData[], getTooltip: (point: FormattedData, setTooltip?: boolean) => string) {
398 511 this.map$.subscribe(map => {
399 512 if (this.points) {
... ... @@ -509,9 +622,14 @@ export default abstract class LeafletMap {
509 622 });
510 623 }
511 624
512   - createPolygon(polyData: FormattedData, dataSources: FormattedData[], settings: PolygonSettings, updateBounds = true) {
  625 + dragPolygonVertex = (e?, data = {} as FormattedData) => {
  626 + if (e == undefined || (e.type !== 'editable:vertex:dragend' && e.type !== 'editable:vertex:deleted')) return;
  627 + this.savePolygonLocation({ ...data, ...this.convertPolygonToCustomFormat(e.layer._latlngs) }).subscribe();
  628 + }
  629 +
  630 + createPolygon(polyData: FormattedData, dataSources: FormattedData[], settings: PolygonSettings, updateBounds = true) {
513 631 this.ready$.subscribe(() => {
514   - const polygon = new Polygon(this.map, polyData, dataSources, settings);
  632 + const polygon = new Polygon(this.map, polyData, dataSources, settings, this.dragPolygonVertex);
515 633 if (updateBounds) {
516 634 const bounds = polygon.leafletPoly.getBounds();
517 635 this.fitBounds(bounds);
... ...
... ... @@ -34,6 +34,7 @@ export type PosFuncton = (origXPos, origYPos) => { x, y };
34 34
35 35 export type MapSettings = {
36 36 draggableMarker: boolean;
  37 + editablePolygon: boolean;
37 38 initCallback?: () => any;
38 39 posFunction: PosFuncton;
39 40 defaultZoomLevel?: number;
... ... @@ -140,6 +141,7 @@ export type PolygonSettings = {
140 141 usePolygonColorFunction: boolean;
141 142 polygonTooltipFunction: GenericFunction;
142 143 polygonColorFunction?: GenericFunction;
  144 + editablePolygon: boolean;
143 145 }
144 146
145 147 export type PolylineSettings = {
... ... @@ -268,6 +270,7 @@ export const defaultSettings: any = {
268 270 credentials: '',
269 271 markerClusteringSetting: null,
270 272 draggableMarker: false,
  273 + editablePolygon: false,
271 274 fitMapBounds: true,
272 275 mapPageSize: DEFAULT_MAP_PAGE_SIZE
273 276 };
... ...
... ... @@ -79,6 +79,7 @@ export class MapWidgetController implements MapWidgetInterface {
79 79 this.map = new MapClass(this.ctx, $element, this.settings);
80 80 (this.ctx as any).mapInstance = this.map;
81 81 this.map.saveMarkerLocation = this.setMarkerLocation;
  82 + this.map.savePolygonLocation = this.savePolygonLocation;
82 83 this.pageLink = {
83 84 page: 0,
84 85 pageSize: this.settings.mapPageSize,
... ... @@ -239,6 +240,56 @@ export class MapWidgetController implements MapWidgetInterface {
239 240 }
240 241 }
241 242
  243 + savePolygonLocation = (e: FormattedData, coordinates?: Array<any>) => {
  244 + const attributeService = this.ctx.$injector.get(AttributeService);
  245 +
  246 + const entityId: EntityId = {
  247 + entityType: e.$datasource.entityType,
  248 + id: e.$datasource.entityId
  249 + };
  250 + const attributes = [];
  251 + const timeseries = [];
  252 +
  253 + const coordinatesProperties = this.settings.polygonKeyName;
  254 + e.$datasource.dataKeys.forEach(key => {
  255 + let value;
  256 + if (coordinatesProperties == key.name) {
  257 + value = {
  258 + key: key.name,
  259 + value: isDefined(coordinates) ? coordinates : e[key.name]
  260 + };
  261 + }
  262 + if (value) {
  263 + if (key.type === DataKeyType.attribute) {
  264 + attributes.push(value)
  265 + }
  266 + if (key.type === DataKeyType.timeseries) {
  267 + timeseries.push(value)
  268 + }
  269 + }
  270 + });
  271 + const observables: Observable<any>[] = [];
  272 + if (timeseries.length) {
  273 + observables.push(attributeService.saveEntityTimeseries(
  274 + entityId,
  275 + LatestTelemetry.LATEST_TELEMETRY,
  276 + timeseries
  277 + ));
  278 + }
  279 + if (attributes.length) {
  280 + observables.push(attributeService.saveEntityAttributes(
  281 + entityId,
  282 + AttributeScope.SERVER_SCOPE,
  283 + attributes
  284 + ));
  285 + }
  286 + if (observables.length) {
  287 + return forkJoin(observables);
  288 + } else {
  289 + return of(null);
  290 + }
  291 + }
  292 +
242 293 initSettings(settings: UnitedMapSettings, isEditMap?: boolean): UnitedMapSettings {
243 294 const functionParams = ['data', 'dsData', 'dsIndex'];
244 295 this.provider = settings.provider || this.mapProvider;
... ... @@ -270,6 +321,9 @@ export class MapWidgetController implements MapWidgetInterface {
270 321 if (isEditMap && !settings.hasOwnProperty('draggableMarker')) {
271 322 settings.draggableMarker = true;
272 323 }
  324 + if (isEditMap && !settings.hasOwnProperty('editablePolygon')) {
  325 + settings.editablePolygon = true;
  326 + }
273 327 return { ...defaultSettings, ...settings, ...customOptions, }
274 328 }
275 329
... ...
... ... @@ -22,13 +22,13 @@
22 22 background-repeat: no-repeat;
23 23 }
24 24
25   -.leaflet-div-icon,
26   -.tb-marker-label,
27   -.tb-marker-label:before {
28   - border: none;
29   - background: none;
30   - box-shadow: none;
31   -}
  25 +//.leaflet-div-icon,
  26 +//.tb-marker-label,
  27 +//.tb-marker-label:before {
  28 +// border: none;
  29 +// background: none;
  30 +// box-shadow: none;
  31 +//}
32 32
33 33 .leaflet-container{
34 34 background-color: white;
... ...
... ... @@ -14,7 +14,8 @@
14 14 /// limitations under the License.
15 15 ///
16 16
17   -import L, { LatLngExpression, LeafletMouseEvent } from 'leaflet';
  17 +import L, { LatLngExpression, LeafletMouseEvent} from 'leaflet';
  18 +import "leaflet-editable/src/Leaflet.Editable";
18 19 import { createTooltip, parseWithTranslation, safeExecute } from './maps-utils';
19 20 import { FormattedData, PolygonSettings } from './map-models';
20 21
... ... @@ -25,11 +26,10 @@ export class Polygon {
25 26 data: FormattedData;
26 27 dataSources: FormattedData[];
27 28
28   - constructor(public map, polyData: FormattedData, dataSources: FormattedData[], private settings: PolygonSettings) {
  29 + constructor(public map, polyData: FormattedData, dataSources: FormattedData[], private settings: PolygonSettings, onDragendListener?) {
29 30 this.dataSources = dataSources;
30 31 this.data = polyData;
31 32 const polygonColor = this.getPolygonColor(settings);
32   -
33 33 this.leafletPoly = L.polygon(polyData[this.settings.polygonKeyName], {
34 34 fill: true,
35 35 fillColor: polygonColor,
... ... @@ -38,6 +38,14 @@ export class Polygon {
38 38 fillOpacity: settings.polygonOpacity,
39 39 opacity: settings.polygonStrokeOpacity
40 40 }).addTo(this.map);
  41 + if (settings.editablePolygon) {
  42 + this.leafletPoly.enableEdit(this.map);
  43 + if (onDragendListener) {
  44 + this.leafletPoly.on("editable:vertex:dragend", e => onDragendListener(e, this.data));
  45 + this.leafletPoly.on("editable:vertex:deleted", e => onDragendListener(e, this.data));
  46 + }
  47 + }
  48 +
41 49
42 50 if (settings.showPolygonTooltip) {
43 51 this.tooltip = createTooltip(this.leafletPoly, settings, polyData.$datasource);
... ... @@ -64,7 +72,13 @@ export class Polygon {
64 72 updatePolygon(data: FormattedData, dataSources: FormattedData[], settings: PolygonSettings) {
65 73 this.data = data;
66 74 this.dataSources = dataSources;
  75 + if (settings.editablePolygon) {
  76 + this.leafletPoly.disableEdit();
  77 + }
67 78 this.leafletPoly.setLatLngs(data[this.settings.polygonKeyName]);
  79 + if (settings.editablePolygon) {
  80 + this.leafletPoly.enableEdit(this.map);
  81 + }
68 82 if (settings.showPolygonTooltip)
69 83 this.updateTooltip(this.data);
70 84 this.updatePolygonColor(settings);
... ...
... ... @@ -36,7 +36,7 @@ export class GoogleMap extends LeafletMap {
36 36 super(ctx, $container, options);
37 37 this.resource = ctx.$injector.get(ResourcesService);
38 38 this.loadGoogle(() => {
39   - const map = L.map($container, {attributionControl: false}).setView(options?.defaultCenterPosition, options?.defaultZoomLevel);
  39 + const map = L.map($container, {attributionControl: false, editable: !!options.editablePolygon}).setView(options?.defaultCenterPosition, options?.defaultZoomLevel);
40 40 (L.gridLayer as any).googleMutant({
41 41 type: options?.gmDefaultMapType || 'roadmap'
42 42 }).addTo(map);
... ...
... ... @@ -22,7 +22,7 @@ import { WidgetContext } from '@home/models/widget-component.models';
22 22 export class HEREMap extends LeafletMap {
23 23 constructor(ctx: WidgetContext, $container, options: UnitedMapSettings) {
24 24 super(ctx, $container, options);
25   - const map = L.map($container).setView(options?.defaultCenterPosition, options?.defaultZoomLevel);
  25 + const map = L.map($container, {editable: !!options.editablePolygon}).setView(options?.defaultCenterPosition, options?.defaultZoomLevel);
26 26 const tileLayer = (L.tileLayer as any).provider(options.mapProviderHere || 'HERE.normalDay', options.credentials);
27 27 tileLayer.addTo(map);
28 28 super.setMap(map);
... ...
... ... @@ -24,7 +24,9 @@ import { WidgetContext } from '@home/models/widget-component.models';
24 24 import { DataSet, DatasourceType, widgetType } from '@shared/models/widget.models';
25 25 import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
26 26 import { WidgetSubscriptionOptions } from '@core/api/widget-api.models';
27   -import { isDefinedAndNotNull } from '@core/utils';
  27 +import {isDefinedAndNotNull, isNumber} from '@core/utils';
  28 +import "leaflet-editable/src/Leaflet.Editable";
  29 +import {isArray} from "rxjs/internal-compatibility";
28 30
29 31 const maxZoom = 4;// ?
30 32
... ... @@ -196,14 +198,15 @@ export class ImageMap extends LeafletMap {
196 198 initMap(updateImage?: boolean) {
197 199 if (!this.map && this.aspect > 0) {
198 200 const center = this.pointToLatLng(this.width / 2, this.height / 2);
199   - this.map = L.map(this.$container, {
  201 + this.map = L.map(this.$container, {
200 202 minZoom: 1,
201 203 maxZoom,
202 204 scrollWheelZoom: !this.options.disableScrollZooming,
203 205 center,
204 206 zoom: 1,
205 207 crs: L.CRS.Simple,
206   - attributionControl: false
  208 + attributionControl: false,
  209 + editable: !!this.options.editablePolygon
207 210 });
208 211 this.updateBounds(updateImage);
209 212 }
... ... @@ -221,14 +224,17 @@ export class ImageMap extends LeafletMap {
221 224 expression.y * this.height);
222 225 }
223 226
224   - convertPositionPolygon(expression: Array<[number, number]>): L.LatLngExpression[] {
225   - return expression.map((el) => {
  227 + convertPositionPolygon(expression: Array<[number, number]> | Array<Array<[number, number]>>) {
  228 + return (expression as Array<any>).map((el) => {
226 229 if (el.length === 2 && !el.some(isNaN)) {
227 230 return this.pointToLatLng(
228 231 el[0] * this.width,
229 232 el[1] * this.height)
  233 + } else if (isArray(el) && el.length) {
  234 + return this.convertPositionPolygon(el);
  235 + } else {
  236 + return null;
230 237 }
231   - return null;
232 238 }).filter(el => !!el)
233 239 }
234 240
... ... @@ -247,4 +253,25 @@ export class ImageMap extends LeafletMap {
247 253 [this.options.yPosKeyName]: calculateNewPointCoordinate(point.y, this.height)
248 254 }
249 255 }
  256 +
  257 + convertToPolygonFormat(points: Array<any>): Array<any> {
  258 + if (points.length) {
  259 + return points.map(point=> {
  260 + if (point.length) {
  261 + return this.convertToPolygonFormat(point);
  262 + } else {
  263 + let pos = this.latLngToPoint(point);
  264 + return [calculateNewPointCoordinate(pos.x, this.width), calculateNewPointCoordinate(pos.y, this.height)];
  265 + }
  266 + })
  267 + } else {
  268 + return []
  269 + }
  270 + }
  271 +
  272 + convertPolygonToCustomFormat(expression: Array<Array<any>>): object {
  273 + return {
  274 + [this.options.polygonKeyName] : this.convertToPolygonFormat(expression)
  275 + }
  276 + }
250 277 }
... ...
... ... @@ -22,7 +22,7 @@ import { WidgetContext } from '@home/models/widget-component.models';
22 22 export class OpenStreetMap extends LeafletMap {
23 23 constructor(ctx: WidgetContext, $container, options: UnitedMapSettings) {
24 24 super(ctx, $container, options);
25   - const map = L.map($container).setView(options?.defaultCenterPosition, options?.defaultZoomLevel);
  25 + const map = new L.Map($container, {editable: !!options.editablePolygon}).setView(options?.defaultCenterPosition, options?.defaultZoomLevel);
26 26 let tileLayer;
27 27 if (options.useCustomProvider)
28 28 tileLayer = L.tileLayer(options.customProviderTileUrl);
... ...
... ... @@ -24,7 +24,7 @@ export class TencentMap extends LeafletMap {
24 24 constructor(ctx: WidgetContext, $container, options: UnitedMapSettings) {
25 25 super(ctx, $container, options);
26 26 const txUrl = 'http://rt{s}.map.gtimg.com/realtimerender?z={z}&x={x}&y={y}&type=vector&style=0';
27   - const map = L.map($container).setView(options?.defaultCenterPosition, options?.defaultZoomLevel);
  27 + const map = L.map($container, {editable: !!options.editablePolygon}).setView(options?.defaultCenterPosition, options?.defaultZoomLevel);
28 28 const txLayer = L.tileLayer(txUrl, {
29 29 subdomains: '0123',
30 30 tms: true,
... ...
  1 +<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48">
  2 + <polygon points="2,46 46,46 24,2" style="fill:transparent;stroke:black;stroke-width:2"/>
  3 +</svg>
... ...
... ... @@ -20,9 +20,12 @@ declare module 'leaflet' {
20 20
21 21 namespace Control {
22 22 class AddMarker extends L.Control { }
  23 + class AddPolygon extends L.Control { }
23 24 }
24 25
25 26 namespace control {
26 27 function addMarker(options): Control.AddMarker;
  28 + function addPolygon(options): Control.AddPolygon;
27 29 }
28   -}
\ No newline at end of file
  30 +
  31 +}
... ...
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import * as Leaflet from 'leaflet';
  18 +
  19 +declare module 'leaflet' {
  20 +
  21 + /**
  22 + * Make geometries editable in Leaflet.
  23 + *
  24 + * This is not a plug and play UI, and will not. This is a minimal, lightweight, and fully extendable API to
  25 + * control editing of geometries. So you can easily build your own UI with your own needs and choices.
  26 + */
  27 + interface EditableStatic {
  28 + new (map: Map, options: EditOptions): Editable;
  29 + }
  30 +
  31 + /**
  32 + * Options to pass to L.Editable when instanciating.
  33 + */
  34 + interface EditOptions extends Leaflet.MapOptions{
  35 + /**
  36 + * Class to be used when creating a new Polyline.
  37 + */
  38 + polylineClass?: object;
  39 +
  40 + /**
  41 + * Class to be used when creating a new Polygon.
  42 + */
  43 + polygonClass?: object;
  44 +
  45 + /**
  46 + * Class to be used when creating a new Marker.
  47 + */
  48 + markerClass?: object;
  49 +
  50 + /**
  51 + * CSS class to be added to the map container while drawing.
  52 + */
  53 + drawingCSSClass?: string;
  54 +
  55 + /**
  56 + * Layer used to store edit tools (vertex, line guide…).
  57 + */
  58 + editLayer?: LayerGroup<Leaflet.Layer>;
  59 +
  60 + /**
  61 + * Default layer used to store drawn features (marker, polyline…).
  62 + */
  63 + featuresLayer?: LayerGroup<Polyline|Polygon|Marker>;
  64 +
  65 + /**
  66 + * Class to be used as vertex, for path editing.
  67 + */
  68 + vertexMarkerClass?: object;
  69 +
  70 + /**
  71 + * Class to be used as middle vertex, pulled by the user to create a new point in the middle of a path.
  72 + */
  73 + middleMarkerClass?: object;
  74 +
  75 + /**
  76 + * Class to be used as Polyline editor.
  77 + */
  78 + polylineEditorClass?: object;
  79 +
  80 + /**
  81 + * Class to be used as Polygon editor.
  82 + */
  83 + polygonEditorClass?: object;
  84 +
  85 + /**
  86 + * Class to be used as Marker editor.
  87 + */
  88 + markerEditorClass?: object;
  89 +
  90 + /**
  91 + * Options to be passed to the line guides.
  92 + */
  93 + lineGuideOptions?: object;
  94 +
  95 + /**
  96 + * Set this to true if you don't want middle markers.
  97 + */
  98 + skipMiddleMarkers?: boolean;
  99 + }
  100 +
  101 + /**
  102 + * Make geometries editable in Leaflet.
  103 + *
  104 + * This is not a plug and play UI, and will not. This is a minimal, lightweight, and fully extendable API to
  105 + * control editing of geometries. So you can easily build your own UI with your own needs and choices.
  106 + */
  107 + interface Editable extends Leaflet.Evented {
  108 + /**
  109 + * Options to pass to L.Editable when instanciating.
  110 + */
  111 + options: EditOptions;
  112 +
  113 + currentPolygon: Polyline|Polygon|Marker;
  114 +
  115 + /**
  116 + * Start drawing a polyline. If latlng is given, a first point will be added. In any case, continuing on user
  117 + * click. If options is given, it will be passed to the polyline class constructor.
  118 + */
  119 + startPolyline(latLng?: LatLng, options?: PolylineOptions): Polyline;
  120 +
  121 + /**
  122 + * Start drawing a polygon. If latlng is given, a first point will be added. In any case, continuing on user
  123 + * click. If options is given, it will be passed to the polygon class constructor.
  124 + */
  125 + startPolygon(latLng?: LatLng, options?: PolylineOptions): Polygon;
  126 +
  127 + /**
  128 + * Start adding a marker. If latlng is given, the marker will be shown first at this point. In any case, it
  129 + * will follow the user mouse, and will have a final latlng on next click (or touch). If options is given,
  130 + * it will be passed to the marker class constructor.
  131 + */
  132 + startMarker(latLng?: LatLng, options?: MarkerOptions): Marker;
  133 +
  134 + /**
  135 + * When you need to stop any ongoing drawing, without needing to know which editor is active.
  136 + */
  137 + stopDrawing(): void;
  138 +
  139 + /**
  140 + * When you need to commit any ongoing drawing, without needing to know which editor is active.
  141 + */
  142 + commitDrawing(): void;
  143 + }
  144 +
  145 + let Editable: EditableStatic;
  146 +
  147 + /**
  148 + * EditableMixin is included to L.Polyline, L.Polygon and L.Marker. It adds the following methods to them.
  149 + *
  150 + * When editing is enabled, the editor is accessible on the instance with the editor property.
  151 + */
  152 + interface EditableMixin {
  153 + /**
  154 + * Enable editing, by creating an editor if not existing, and then calling enable on it.
  155 + */
  156 + enableEdit(map: L.Map): any;
  157 +
  158 + /**
  159 + * Disable editing, also remove the editor property reference.
  160 + */
  161 + disableEdit(): void;
  162 +
  163 + /**
  164 + * Enable or disable editing, according to current status.
  165 + */
  166 + toggleEdit(): void;
  167 +
  168 + /**
  169 + * Return true if current instance has an editor attached, and this editor is enabled.
  170 + */
  171 + editEnabled(): boolean;
  172 + }
  173 +
  174 + interface Map {
  175 + /**
  176 + * Whether to create a L.Editable instance at map init or not.
  177 + */
  178 + editable: boolean;
  179 +
  180 + /**
  181 + * Options to pass to L.Editable when instanciating.
  182 + */
  183 + editOptions: EditOptions;
  184 +
  185 + /**
  186 + * L.Editable plugin instance.
  187 + */
  188 + editTools: Editable;
  189 + }
  190 +
  191 + // tslint:disable-next-line:no-empty-interface
  192 + interface Polyline extends EditableMixin {}
  193 +
  194 + namespace Map {
  195 + interface MapOptions {
  196 + /**
  197 + * Whether to create a L.Editable instance at map init or not.
  198 + */
  199 + editable?: boolean;
  200 +
  201 + /**
  202 + * Options to pass to L.Editable when instanciating.
  203 + */
  204 + editOptions?: EditOptions;
  205 + }
  206 + }
  207 +
  208 + /**
  209 + * When editing a feature (marker, polyline…), an editor is attached to it. This editor basically knows
  210 + * how to handle the edition.
  211 + */
  212 + interface BaseEditor {
  213 + /**
  214 + * Set up the drawing tools for the feature to be editable.
  215 + */
  216 + enable(): MarkerEditor|PolylineEditor|PolygonEditor;
  217 +
  218 + /**
  219 + * Remove editing tools.
  220 + */
  221 + disable(): MarkerEditor|PolylineEditor|PolygonEditor;
  222 + }
  223 +
  224 + /**
  225 + * Inherit from L.Editable.BaseEditor.
  226 + * Inherited by L.Editable.PolylineEditor and L.Editable.PolygonEditor.
  227 + */
  228 + interface PathEditor extends BaseEditor {
  229 + /**
  230 + * Rebuild edit elements (vertex, middlemarker, etc.).
  231 + */
  232 + reset(): void;
  233 + }
  234 +
  235 + /**
  236 + * Inherit from L.Editable.PathEditor.
  237 + */
  238 + interface PolylineEditor extends PathEditor {
  239 + /**
  240 + * Set up drawing tools to continue the line forward.
  241 + */
  242 + continueForward(): void;
  243 +
  244 + /**
  245 + * Set up drawing tools to continue the line backward.
  246 + */
  247 + continueBackward(): void;
  248 + }
  249 +
  250 + /**
  251 + * Inherit from L.Editable.PathEditor.
  252 + */
  253 + interface PolygonEditor extends PathEditor {
  254 + /**
  255 + * Set up drawing tools for creating a new hole on the polygon. If the latlng param is given, a first
  256 + * point is created.
  257 + */
  258 + newHole(latlng: LatLng): void;
  259 + }
  260 +
  261 + /**
  262 + * Inherit from L.Editable.BaseEditor.
  263 + */
  264 + // tslint:disable-next-line:no-empty-interface
  265 + interface MarkerEditor extends BaseEditor {}
  266 +
  267 + interface Marker extends EditableMixin, MarkerEditor {}
  268 +
  269 + interface Polyline extends EditableMixin, PolylineEditor {}
  270 +
  271 + interface Polygon extends EditableMixin, PolygonEditor {}
  272 +}
... ...
... ... @@ -20,7 +20,8 @@
20 20 "src/typings/jquery.flot.typings.d.ts",
21 21 "src/typings/jquery.jstree.typings.d.ts",
22 22 "src/typings/split.js.typings.d.ts",
23   - "src/typings/add-marker.d.ts"
  23 + "src/typings/add-marker.d.ts",
  24 + "src/typings/leaflet-editable.d.ts"
24 25 ],
25 26 "paths": {
26 27 "@app/*": ["src/app/*"],
... ...