Merge pull request #5616 from vvlladd28/improvement/map-widgets/marker-placement

[3.3.3] UI: Improvement add/edit location marker/polygon in maps widgets
This commit is contained in:
Igor Kulikov 2021-11-26 13:44:17 +02:00 committed by GitHub
commit 715eefa6d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 759 additions and 446 deletions

View File

@ -830,6 +830,7 @@
<exclude>src/.browserslistrc</exclude>
<exclude>**/yarn.lock</exclude>
<exclude>**/*.raw</exclude>
<exclude>**/*.patch</exclude>
<exclude>**/apache/cassandra/io/**</exclude>
<exclude>.run/**</exclude>
</excludes>

View File

@ -79,6 +79,7 @@
"src/app/modules/home/components/widget/lib/maps/markers.scss",
"node_modules/leaflet.markercluster/dist/MarkerCluster.css",
"node_modules/leaflet.markercluster/dist/MarkerCluster.Default.css",
"node_modules/@geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css",
"node_modules/prismjs/themes/prism.css",
"node_modules/prismjs/plugins/line-numbers/prism-line-numbers.css"
],

View File

@ -28,6 +28,7 @@
"@date-io/date-fns": "^2.10.11",
"@flowjs/flow.js": "^2.14.1",
"@flowjs/ngx-flow": "~0.4.6",
"@geoman-io/leaflet-geoman-free": "^2.11.3",
"@juggle/resize-observer": "^3.3.1",
"@mat-datetimepicker/core": "~6.0.2",
"@material-ui/core": "^4.11.4",
@ -57,11 +58,10 @@
"jstree-bootstrap-theme": "^1.0.1",
"jszip": "^3.6.0",
"leaflet": "^1.7.1",
"leaflet-editable": "^1.2.0",
"leaflet-polylinedecorator": "^1.6.0",
"leaflet-providers": "^1.12.0",
"leaflet.gridlayer.googlemutant": "0.10.2",
"leaflet.markercluster": "^1.5.0",
"leaflet-providers": "^1.13.0",
"leaflet.gridlayer.googlemutant": "^0.13.4",
"leaflet.markercluster": "^1.5.3",
"material-design-icons": "^3.0.1",
"messageformat": "^2.3.0",
"moment": "^2.29.1",
@ -113,10 +113,11 @@
"@types/jquery": "^3.5.2",
"@types/js-beautify": "^1.13.1",
"@types/jstree": "^3.3.40",
"@types/leaflet": "1.5.17",
"@types/leaflet-editable": "^1.2.1",
"@types/leaflet-polylinedecorator": "^1.6.0",
"@types/leaflet.markercluster": "^1.4.4",
"@types/leaflet": "^1.7.6",
"@types/leaflet-polylinedecorator": "^1.6.1",
"@types/leaflet-providers": "^1.2.1",
"@types/leaflet.gridlayer.googlemutant": "^0.4.6",
"@types/leaflet.markercluster": "^1.4.6",
"@types/lodash": "^4.14.170",
"@types/moment-timezone": "^0.5.30",
"@types/mousetrap": "1.6.3",

View File

@ -0,0 +1,54 @@
<!--
Copyright © 2016-2021 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.
-->
<form #addEntityForm="ngForm" [formGroup]="selectEntityFormGroup" (ngSubmit)="save()">
<mat-toolbar fxLayout="row" color="primary">
<h2 translate>widgets.maps.select-entity</h2>
<span fxFlex></span>
<button mat-icon-button [matDialogClose]="null" type="button">
<mat-icon class="material-icons">close</mat-icon>
</button>
</mat-toolbar>
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar>
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
<div mat-dialog-content>
<mat-form-field class="mat-block" style="min-width: 280px">
<mat-label translate>entity.entity</mat-label>
<mat-select formControlName="entity">
<mat-option *ngFor="let entity of data.entities" [value]="entity">
{{ entity.entityName }}
</mat-option>
</mat-select>
</mat-form-field>
<div class="tb-hint" translate>widgets.maps.select-entity-hint</div>
</div>
<div mat-dialog-actions fxLayout="row" fxLayoutAlign="end center">
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"
[matDialogClose]="null"
cdkFocusInitial>
{{ 'action.cancel' | translate }}
</button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || addEntityForm.invalid || !addEntityForm.dirty">
{{ 'action.add' | translate }}
</button>
</div>
</form>

View File

@ -0,0 +1,21 @@
/**
* Copyright © 2016-2021 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.
*/
:host {
.tb-hint {
margin-top: -1.25em;
max-width: fit-content;
}
}

View File

@ -0,0 +1,56 @@
///
/// Copyright © 2016-2021 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 { Component, Inject } from '@angular/core';
import { DialogComponent } from '@shared/components/dialog.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { Router } from '@angular/router';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { FormattedData } from '@home/components/widget/lib/maps/map-models';
export interface SelectEntityDialogData {
entities: FormattedData[];
}
@Component({
selector: 'tb-select-entity-dialog',
templateUrl: './select-entity-dialog.component.html',
styleUrls: ['./select-entity-dialog.component.scss']
})
export class SelectEntityDialogComponent extends DialogComponent<SelectEntityDialogComponent, FormattedData> {
selectEntityFormGroup: FormGroup;
constructor(protected store: Store<AppState>,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: SelectEntityDialogData,
public dialogRef: MatDialogRef<SelectEntityDialogComponent, FormattedData>,
public fb: FormBuilder) {
super(store, router, dialogRef);
this.selectEntityFormGroup = this.fb.group(
{
entity: ['', Validators.required]
}
);
}
save(): void {
this.dialogRef.close(this.selectEntityFormGroup.value.entity);
}
}

View File

@ -14,23 +14,15 @@
/// limitations under the License.
///
import L, {
FeatureGroup,
Icon,
LatLngBounds,
LatLngTuple,
markerClusterGroup,
MarkerClusterGroup,
MarkerClusterGroupOptions, Projection
} from 'leaflet';
import L, { FeatureGroup, Icon, LatLngBounds, LatLngTuple, Projection } from 'leaflet';
import tinycolor from 'tinycolor2';
import 'leaflet-providers';
import 'leaflet.markercluster/dist/leaflet.markercluster';
import { MarkerClusterGroup, MarkerClusterGroupOptions } from 'leaflet.markercluster/dist/leaflet.markercluster';
import '@geoman-io/leaflet-geoman-free';
import {
defaultSettings,
FormattedData,
MapProviders,
MapSettings,
MarkerSettings,
PolygonSettings,
@ -39,12 +31,10 @@ import {
UnitedMapSettings
} from './map-models';
import { Marker } from './markers';
import { Observable, of } from 'rxjs';
import { forkJoin, Observable, of } from 'rxjs';
import { Polyline } from './polyline';
import { Polygon } from './polygon';
import {
createTooltip,
} from '@home/components/widget/lib/maps/maps-utils';
import { createTooltip } from '@home/components/widget/lib/maps/maps-utils';
import {
checkLngLat,
createLoadingDiv,
@ -54,6 +44,15 @@ import {
} from '@home/components/widget/lib/maps/common-maps-utils';
import { WidgetContext } from '@home/models/widget-component.models';
import { deepClone, isDefinedAndNotNull, isEmptyStr, isString } from '@core/utils';
import { TranslateService } from '@ngx-translate/core';
import {
SelectEntityDialogComponent,
SelectEntityDialogData
} from '@home/components/widget/lib/maps/dialogs/select-entity-dialog.component';
import { MatDialog } from '@angular/material/dialog';
import { AttributeService } from '@core/http/attribute.service';
import { EntityId } from '@shared/models/id/entity-id';
import { AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models';
export default abstract class LeafletMap {
@ -78,20 +77,20 @@ export default abstract class LeafletMap {
drawRoutes: boolean;
showPolygon: boolean;
updatePending = false;
editPolygons = false;
selectedEntity: FormattedData;
addMarkers: L.Marker[] = [];
addPolygons: L.Polygon[] = [];
// tslint:disable-next-line:no-string-literal
southWest = new L.LatLng(-Projection.SphericalMercator['MAX_LATITUDE'], -180);
// tslint:disable-next-line:no-string-literal
northEast = new L.LatLng(Projection.SphericalMercator['MAX_LATITUDE'], 180);
mousePositionOnMap: L.LatLng;
addMarker: L.Control;
addPolygon: L.Control;
protected constructor(public ctx: WidgetContext,
public $container: HTMLElement,
options: UnitedMapSettings) {
this.options = options;
this.editPolygons = options.showPolygon && options.editablePolygon;
}
public initSettings(options: MapSettings) {
@ -102,16 +101,28 @@ export default abstract class LeafletMap {
removeOutsideVisibleBounds,
animate,
chunkedLoading,
spiderfyOnMaxZoom,
maxClusterRadius,
maxZoom }: MapSettings = options;
if (useClusterMarkers) {
// disabled marker cluster icon
(L as any).MarkerCluster = (L as any).MarkerCluster.extend({
options: { pmIgnore: true, ...L.Icon.prototype.options }
});
const clusteringSettings: MarkerClusterGroupOptions = {
spiderfyOnMaxZoom: false,
spiderfyOnMaxZoom,
zoomToBoundsOnClick: zoomOnClick,
showCoverageOnHover,
removeOutsideVisibleBounds,
animate,
chunkedLoading
chunkedLoading,
pmIgnore: true,
spiderLegPolylineOptions: {
pmIgnore: true
},
polygonOptions: {
pmIgnore: true
}
};
if (maxClusterRadius && maxClusterRadius > 0) {
clusteringSettings.maxClusterRadius = Math.floor(maxClusterRadius);
@ -119,183 +130,165 @@ export default abstract class LeafletMap {
if (maxZoom && maxZoom >= 0 && maxZoom < 19) {
clusteringSettings.disableClusteringAtZoom = Math.floor(maxZoom);
}
this.markersCluster = markerClusterGroup(clusteringSettings);
this.markersCluster = new MarkerClusterGroup(clusteringSettings);
}
}
addMarkerControl() {
if (this.options.draggableMarker) {
this.map.on('mousemove', (e: L.LeafletMouseEvent) => {
this.mousePositionOnMap = e.latlng;
});
const dragListener = (e: L.DragEndEvent) => {
if (e.type === 'dragend' && this.mousePositionOnMap) {
const icon = L.icon({
iconRetinaUrl: 'marker-icon-2x.png',
iconUrl: 'marker-icon.png',
shadowUrl: 'marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
tooltipAnchor: [16, -28],
shadowSize: [41, 41]
});
const customLatLng = this.convertToCustomFormat(this.mousePositionOnMap);
this.mousePositionOnMap.lat = customLatLng[this.options.latKeyName];
this.mousePositionOnMap.lng = customLatLng[this.options.lngKeyName];
const newMarker = L.marker(this.mousePositionOnMap, { icon }).addTo(this.map);
this.addMarkers.push(newMarker);
const datasourcesList = document.createElement('div');
const header = document.createElement('p');
header.appendChild(document.createTextNode('Select entity:'));
header.setAttribute('style', 'font-size: 14px; margin: 8px 0');
datasourcesList.appendChild(header);
this.datasources.forEach(ds => {
const dsItem = document.createElement('p');
dsItem.appendChild(document.createTextNode(ds.entityName));
dsItem.setAttribute('style', 'font-size: 14px; margin: 8px 0; cursor: pointer');
dsItem.onclick = () => {
const updatedEnttity = { ...ds, ...customLatLng };
this.saveMarkerLocation(updatedEnttity).subscribe(() => {
this.map.removeLayer(newMarker);
const markerIndex = this.addMarkers.indexOf(newMarker);
if (markerIndex > -1) {
this.addMarkers.splice(markerIndex, 1);
}
this.deleteMarker(ds.entityName);
this.createMarker(ds.entityName, updatedEnttity, this.datasources, this.options);
});
};
datasourcesList.appendChild(dsItem);
});
datasourcesList.appendChild(document.createElement('br'));
const deleteBtn = document.createElement('a');
deleteBtn.appendChild(document.createTextNode('Discard changes'));
deleteBtn.onclick = () => {
this.map.removeLayer(newMarker);
const markerIndex = this.addMarkers.indexOf(newMarker);
if (markerIndex > -1) {
this.addMarkers.splice(markerIndex, 1);
}
};
datasourcesList.appendChild(deleteBtn);
const popup = L.popup();
popup.setContent(datasourcesList);
newMarker.bindPopup(popup).openPopup();
}
this.addMarker.setPosition('topright');
};
const addMarker = L.Control.extend({
onAdd() {
const img = L.DomUtil.create('img') as any;
img.src = `assets/add_location.svg`;
img.style.width = '32px';
img.style.height = '32px';
img.title = 'Drag and drop to add marker';
img.onclick = this.dragMarker;
img.draggable = true;
const draggableImg = new L.Draggable(img);
draggableImg.enable();
draggableImg.on('dragend', dragListener);
return img;
},
onRemove() {
},
dragMarker: this.dragMarker
} as any);
const addMarkerControl = (opts) => {
return new addMarker(opts);
};
this.addMarker = addMarkerControl({ position: 'topright' }).addTo(this.map);
}
private selectEntityWithoutLocationDialog(shapes: L.PM.SUPPORTED_SHAPES): Observable<FormattedData> {
let entities;
switch (shapes) {
case 'Polygon':
case 'Rectangle':
entities = this.datasources.filter(pData => !this.isValidPolygonPosition(pData));
break;
case 'Marker':
entities = this.datasources.filter(mData => !this.convertPosition(mData));
break;
default:
return of(null);
}
if (entities.length === 1) {
return of(entities[0]);
}
const dialog = this.ctx.$injector.get(MatDialog);
return dialog.open<SelectEntityDialogComponent, SelectEntityDialogData, FormattedData>(SelectEntityDialogComponent,
{
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
entities
}
}).afterClosed();
}
addPolygonControl() {
if (this.options.showPolygon && this.options.editablePolygon) {
this.map.on('mousemove', (e: L.LeafletMouseEvent) => {
this.mousePositionOnMap = e.latlng;
private selectEntityWithoutLocation(type: string) {
this.selectEntityWithoutLocationDialog(type.substring(2)).subscribe((data) => {
if (data !== null) {
this.selectedEntity = data;
this.toggleDrawMode(type);
} else {
this.map.pm.Toolbar.toggleButton(type, false);
}
});
}
private toggleDrawMode(type: string) {
this.map.pm.Draw[type].toggle();
}
addEditControl() {
// Customize edit marker
if (this.options.draggableMarker) {
const actions = [{
text: L.PM.Utils.getTranslation('actions.cancel'),
onClick: () => this.toggleDrawMode('tbMarker')
}];
this.map.pm.Toolbar.copyDrawControl('Marker', {
name: 'tbMarker',
afterClick: () => this.selectEntityWithoutLocation('tbMarker'),
disabled: true,
actions
});
}
// Customize edit polygon
if (this.editPolygons) {
const rectangleActions = [
{
text: L.PM.Utils.getTranslation('actions.cancel'),
onClick: () => this.toggleDrawMode('tbRectangle')
}
];
const polygonActions = [
'finish' as const,
'removeLastVertex' as const,
{
text: L.PM.Utils.getTranslation('actions.cancel'),
onClick: () => this.toggleDrawMode('tbPolygon')
}
];
this.map.pm.Toolbar.copyDrawControl('Rectangle', {
name: 'tbRectangle',
afterClick: () => this.selectEntityWithoutLocation('tbRectangle'),
disabled: true,
actions: rectangleActions
});
this.map.pm.Toolbar.copyDrawControl('Polygon', {
name: 'tbPolygon',
afterClick: () => this.selectEntityWithoutLocation('tbPolygon'),
disabled: true,
actions: polygonActions
});
}
const translateService = this.ctx.$injector.get(TranslateService);
this.map.pm.setLang('en', translateService.instant('widgets.maps'), 'en');
this.map.pm.addControls({
position: 'topleft',
drawMarker: false,
drawCircle: false,
drawCircleMarker: false,
drawRectangle: false,
drawPolyline: false,
drawPolygon: false,
editMode: this.editPolygons,
cutPolygon: this.editPolygons,
rotateMode: this.editPolygons
});
const dragListener = (e: L.DragEndEvent) => {
if (e.type === 'dragend') {
const polygonOffset = this.options.provider === MapProviders.image ? 10 : 0.01;
const convert = this.convertToCustomFormat(this.mousePositionOnMap, polygonOffset);
this.mousePositionOnMap.lat = convert[this.options.latKeyName];
this.mousePositionOnMap.lng = convert[this.options.lngKeyName];
const latlng1 = this.mousePositionOnMap;
const latlng2 = L.latLng(this.mousePositionOnMap.lat, this.mousePositionOnMap.lng + polygonOffset);
const latlng3 = L.latLng(this.mousePositionOnMap.lat - polygonOffset, this.mousePositionOnMap.lng);
const polygonPoints = [latlng1, latlng2, latlng3];
const newPolygon = L.polygon(polygonPoints).addTo(this.map);
this.addPolygons.push(newPolygon);
const datasourcesList = document.createElement('div');
const customLatLng = {[this.options.polygonKeyName]: this.convertToPolygonFormat(polygonPoints)};
const header = document.createElement('p');
header.appendChild(document.createTextNode('Select entity:'));
header.setAttribute('style', 'font-size: 14px; margin: 8px 0');
datasourcesList.appendChild(header);
this.datasources.forEach(ds => {
const dsItem = document.createElement('p');
dsItem.appendChild(document.createTextNode(ds.entityName));
dsItem.setAttribute('style', 'font-size: 14px; margin: 8px 0; cursor: pointer');
dsItem.onclick = () => {
const updatedEnttity = { ...ds, ...customLatLng };
this.savePolygonLocation(updatedEnttity).subscribe(() => {
this.map.removeLayer(newPolygon);
const polygonIndex = this.addPolygons.indexOf(newPolygon);
if (polygonIndex > -1) {
this.addPolygons.splice(polygonIndex, 1);
}
this.deletePolygon(ds.entityName);
});
};
datasourcesList.appendChild(dsItem);
this.map.on('pm:create', (e) => {
if (e.shape === 'tbMarker') {
// @ts-ignore
this.saveLocation(this.selectedEntity, this.convertToCustomFormat(e.layer.getLatLng())).subscribe(() => {
});
} else if (e.shape === 'tbRectangle' || e.shape === 'tbPolygon') {
// @ts-ignore
this.saveLocation(this.selectedEntity, this.convertPolygonToCustomFormat(e.layer.getLatLngs()[0])).subscribe(() => {
});
datasourcesList.appendChild(document.createElement('br'));
const deleteBtn = document.createElement('a');
deleteBtn.appendChild(document.createTextNode('Discard changes'));
deleteBtn.onclick = () => {
this.map.removeLayer(newPolygon);
const polygonIndex = this.addPolygons.indexOf(newPolygon);
if (polygonIndex > -1) {
this.addPolygons.splice(polygonIndex, 1);
}
};
datasourcesList.appendChild(deleteBtn);
const popup = L.popup();
popup.setContent(datasourcesList);
newPolygon.bindPopup(popup).openPopup();
}
this.addPolygon.setPosition('topright');
};
const 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);
const addPolygonControl = (opts) => {
return new addPolygon(opts);
};
this.addPolygon = addPolygonControl({ position: 'topright' }).addTo(this.map);
// @ts-ignore
e.layer._pmTempLayer = true;
e.layer.remove();
});
this.map.on('pm:cut', (e) => {
// @ts-ignore
e.originalLayer.setLatLngs(e.layer.getLatLngs());
e.originalLayer.addTo(this.map);
// @ts-ignore
e.originalLayer._pmTempLayer = false;
const iterator = this.polygons.values();
let result = iterator.next();
while (!result.done && e.originalLayer !== result.value.leafletPoly) {
result = iterator.next();
}
// @ts-ignore
e.layer._pmTempLayer = true;
e.layer.remove();
});
this.map.on('pm:remove', (e) => {
if (e.shape === 'Marker') {
const iterator = this.markers.values();
let result = iterator.next();
while (!result.done && e.layer !== result.value.leafletMarker) {
result = iterator.next();
}
this.saveLocation(result.value.data, this.convertToCustomFormat(null)).subscribe(() => {});
} else if (e.shape === 'Polygon') {
const iterator = this.polygons.values();
let result = iterator.next();
while (!result.done && e.layer !== result.value.leafletPoly) {
result = iterator.next();
}
this.saveLocation(result.value.data, this.convertPolygonToCustomFormat(null)).subscribe(() => {});
}
});
}
}
public setLoading(loading: boolean) {
if (this.loading !== loading) {
@ -337,11 +330,10 @@ export default abstract class LeafletMap {
if (this.options.disableScrollZooming) {
this.map.scrollWheelZoom.disable();
}
if (this.options.draggableMarker) {
this.addMarkerControl();
}
if (this.options.editablePolygon) {
this.addPolygonControl();
if (this.options.draggableMarker || this.editPolygons) {
this.addEditControl();
} else {
this.map.pm.disableDraw();
}
if (this.options.useClusterMarkers) {
this.map.addLayer(this.markersCluster);
@ -352,12 +344,53 @@ export default abstract class LeafletMap {
}
}
public saveMarkerLocation(datasource: FormattedData, lat?: number, lng?: number): Observable<any> {
return of(null);
}
private saveLocation(e: FormattedData, values: {[key: string]: any}): Observable<any> {
const attributeService = this.ctx.$injector.get(AttributeService);
const attributes = [];
const timeseries = [];
public savePolygonLocation(datasource: FormattedData, coordinates?: Array<[number, number]>): Observable<any> {
return of(null);
const entityId: EntityId = {
entityType: e.$datasource.entityType,
id: e.$datasource.entityId
};
for (const dataKeyName of Object.keys(values)) {
for (const key of e.$datasource.dataKeys) {
if (dataKeyName === key.name) {
const value = {
key: key.name,
value: values[dataKeyName]
};
if (key.type === DataKeyType.attribute) {
attributes.push(value);
} else if (key.type === DataKeyType.timeseries) {
timeseries.push(value);
}
break;
}
}
}
const observables: Observable<any>[] = [];
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);
}
}
createLatLng(lat: number, lng: number): L.LatLng {
@ -441,7 +474,7 @@ export default abstract class LeafletMap {
}
convertToCustomFormat(position: L.LatLng, offset = 0): object {
position = checkLngLat(position, this.southWest, this.northEast, offset);
position = position ? checkLngLat(position, this.southWest, this.northEast, offset) : {lat: null, lng: null} as L.LatLng;
return {
[this.options.latKeyName]: position.lat,
@ -455,17 +488,18 @@ export default abstract class LeafletMap {
if (point.length) {
return this.convertToPolygonFormat(point);
} else {
return [point.lat, point.lng];
const convertPoint = checkLngLat(point, this.southWest, this.northEast);
return [convertPoint.lat, convertPoint.lng];
}
});
} else {
return [];
}
return [];
}
convertPolygonToCustomFormat(expression: any[][]): object {
convertPolygonToCustomFormat(expression: any[][]): {[key: string]: any} {
const coordinate = expression ? this.convertToPolygonFormat(expression) : null;
return {
[this.options.polygonKeyName] : this.convertToPolygonFormat(expression)
[this.options.polygonKeyName]: coordinate
};
}
@ -484,7 +518,15 @@ export default abstract class LeafletMap {
}
this.updateMarkers(formattedData, false);
this.updateBoundsInternal();
if (this.options.draggableMarker || this.options.editablePolygon) {
if (this.options.draggableMarker) {
const foundEntityWithoutLocation = formattedData.some(mData => !this.convertPosition(mData));
this.map.pm.Toolbar.setButtonDisabled('tbMarker', !foundEntityWithoutLocation);
this.datasources = formattedData;
}
if (this.editPolygons) {
const foundEntityWithoutPolygon = formattedData.some(pData => !this.isValidPolygonPosition(pData));
this.map.pm.Toolbar.setButtonDisabled('tbPolygon', !foundEntityWithoutPolygon);
this.map.pm.Toolbar.setButtonDisabled('tbRectangle', !foundEntityWithoutPolygon);
this.datasources = formattedData;
}
} else {
@ -577,10 +619,10 @@ export default abstract class LeafletMap {
}
dragMarker = (e, data = {} as FormattedData) => {
if (e.type !== 'dragend') {
if (e === undefined) {
return;
}
this.saveMarkerLocation({ ...data, ...this.convertToCustomFormat(e.target._latlng) }).subscribe();
this.saveLocation(data, this.convertToCustomFormat(e.target._latlng)).subscribe();
}
private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings,
@ -728,11 +770,15 @@ export default abstract class LeafletMap {
// Polygon
isValidPolygonPosition(data: FormattedData): boolean {
return data && isDefinedAndNotNull(data[this.options.polygonKeyName]) && !isEmptyStr(data[this.options.polygonKeyName]);
}
updatePolygons(polyData: FormattedData[], updateBounds = true) {
const keys: string[] = [];
this.polygonsData = deepClone(polyData);
polyData.forEach((data: FormattedData) => {
if (data && isDefinedAndNotNull(data[this.options.polygonKeyName]) && !isEmptyStr(data[this.options.polygonKeyName])) {
if (this.isValidPolygonPosition(data)) {
if (isString((data[this.options.polygonKeyName]))) {
data[this.options.polygonKeyName] = JSON.parse(data[this.options.polygonKeyName]);
}
@ -758,15 +804,14 @@ export default abstract class LeafletMap {
}
dragPolygonVertex = (e?, data = {} as FormattedData) => {
if (e === undefined || (e.type !== 'editable:vertex:dragend' && e.type !== 'editable:vertex:deleted')) {
if (e === undefined) {
return;
}
if (this.options.provider !== MapProviders.image) {
for (const key of Object.keys(e.layer._latlngs[0])) {
e.layer._latlngs[0][key] = checkLngLat(e.layer._latlngs[0][key], this.southWest, this.northEast);
}
let coordinates = e.layer.getLatLngs();
if (coordinates.length === 1) {
coordinates = coordinates[0];
}
this.savePolygonLocation({ ...data, ...this.convertPolygonToCustomFormat(e.layer._latlngs) }).subscribe();
this.saveLocation(data, this.convertPolygonToCustomFormat(coordinates)).subscribe(() => {});
}
createPolygon(polyData: FormattedData, dataSources: FormattedData[], settings: PolygonSettings, updateBounds = true) {

View File

@ -57,6 +57,7 @@ export type MapSettings = {
showCoverageOnHover: boolean,
animate: boolean,
maxClusterRadius: number,
spiderfyOnMaxZoom: boolean,
chunkedLoading: boolean,
removeOutsideVisibleBounds: boolean,
useCustomProvider: boolean,

View File

@ -14,41 +14,23 @@
/// limitations under the License.
///
import {
defaultSettings,
FormattedData,
hereProviders,
MapProviders,
UnitedMapSettings
} from './map-models';
import { defaultSettings, hereProviders, MapProviders, UnitedMapSettings } from './map-models';
import LeafletMap from './leaflet-map';
import {
commonMapSettingsSchema,
mapPolygonSchema,
mapProviderSchema,
markerClusteringSettingsSchema,
markerClusteringSettingsSchemaLeaflet,
routeMapSettingsSchema
} from './schemes';
import { MapWidgetInterface, MapWidgetStaticInterface } from './map-widget.interface';
import {
addCondition,
addGroupInfo,
addToSchema,
initSchema,
mergeSchemes
} from '@core/schema-utils';
import { addCondition, addGroupInfo, addToSchema, initSchema, mergeSchemes } from '@core/schema-utils';
import { WidgetContext } from '@app/modules/home/models/widget-component.models';
import { getDefCenterPosition, getProviderSchema, parseFunction, parseWithTranslation } from './common-maps-utils';
import { Datasource, DatasourceData, JsonSettingsSchema, WidgetActionDescriptor } from '@shared/models/widget.models';
import { EntityId } from '@shared/models/id/entity-id';
import { AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models';
import { AttributeService } from '@core/http/attribute.service';
import { TranslateService } from '@ngx-translate/core';
import { UtilsService } from '@core/services/utils.service';
import { EntityDataPageLink } from '@shared/models/query/query.models';
import { isDefined } from '@core/utils';
import { forkJoin, Observable, of } from 'rxjs';
import { providerClass } from '@home/components/widget/lib/maps/providers';
// @dynamic
@ -81,8 +63,6 @@ export class MapWidgetController implements MapWidgetInterface {
}
parseWithTranslation.setTranslate(this.translate);
this.map = new MapClass(this.ctx, $element, this.settings);
this.map.saveMarkerLocation = this.setMarkerLocation;
this.map.savePolygonLocation = this.savePolygonLocation;
this.pageLink = {
page: 0,
pageSize: this.settings.mapPageSize,
@ -178,112 +158,6 @@ export class MapWidgetController implements MapWidgetInterface {
}, entityName, null, entityLabel);
}
setMarkerLocation = (e: FormattedData, lat?: number, lng?: number) => {
const attributeService = this.ctx.$injector.get(AttributeService);
const entityId: EntityId = {
entityType: e.$datasource.entityType,
id: e.$datasource.entityId
};
const attributes = [];
const timeseries = [];
const latProperties = [this.settings.latKeyName, this.settings.xPosKeyName];
const lngProperties = [this.settings.lngKeyName, this.settings.yPosKeyName];
e.$datasource.dataKeys.forEach(key => {
let value;
if (latProperties.includes(key.name)) {
value = {
key: key.name,
value: isDefined(lat) ? lat : e[key.name]
};
} else if (lngProperties.includes(key.name)) {
value = {
key: key.name,
value: isDefined(lng) ? lng : e[key.name]
};
}
if (value) {
if (key.type === DataKeyType.attribute) {
attributes.push(value);
}
if (key.type === DataKeyType.timeseries) {
timeseries.push(value);
}
}
});
const observables: Observable<any>[] = [];
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);
}
}
savePolygonLocation = (e: FormattedData, coordinates?: Array<any>) => {
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<any>[] = [];
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;

View File

@ -29,6 +29,10 @@
box-shadow: none;
}
.leaflet-container{
.leaflet-container {
background-color: white;
}
.leaflet-buttons-control-button.pm-disabled {
pointer-events: none;
}

View File

@ -42,9 +42,7 @@ export class Marker {
constructor(private map: LeafletMap, private location: L.LatLng, public settings: MarkerSettings,
data?: FormattedData, dataSources?, onDragendListener?) {
this.setDataSources(data, dataSources);
this.leafletMarker = L.marker(location, {
draggable: settings.draggableMarker
});
this.leafletMarker = L.marker(location, {pmIgnore: !settings.draggableMarker});
this.markerOffset = [
isDefined(settings.markerOffsetX) ? settings.markerOffsetX : 0.5,
@ -72,8 +70,8 @@ export class Marker {
});
}
if (onDragendListener) {
this.leafletMarker.on('dragend', (e) => onDragendListener(e, this.data));
if (settings.draggableMarker && onDragendListener) {
this.leafletMarker.on('pm:dragend', (e) => onDragendListener(e, this.data));
}
}
@ -199,19 +197,24 @@ export class Marker {
createColoredMarkerIcon(color: tinycolor.Instance): { size: number[], icon: Icon } {
return {
size: [21, 34],
icon: L.icon({
iconUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + color.toHex(),
iconSize: [21, 34],
iconAnchor: [21 * this.markerOffset[0], 34 * this.markerOffset[1]],
popupAnchor: [0, -34],
shadowUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_shadow',
shadowSize: [40, 37],
shadowAnchor: [12, 35]
size: [21, 34],
icon: L.icon({
iconUrl: this.createColorIconURI(color),
iconSize: [21, 34],
iconAnchor: [21 * this.markerOffset[0], 34 * this.markerOffset[1]],
popupAnchor: [0, -34],
shadowUrl: 'assets/shadow.png',
shadowSize: [40, 37],
shadowAnchor: [12, 35]
})
};
}
createColorIconURI(color: tinycolor.Instance): string {
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="-191.35 -351.18 1083.58 1730.46"><path fill-rule="evenodd" clip-rule="evenodd" fill="#${color.toHex()}" stroke="#000" stroke-width="37" stroke-miterlimit="10" d="M351.833 1360.78c-38.766-190.3-107.116-348.665-189.903-495.44C100.523 756.469 29.386 655.978-36.434 550.404c-21.972-35.244-40.934-72.477-62.047-109.054-42.216-73.137-76.444-157.935-74.269-267.932 2.125-107.473 33.208-193.685 78.03-264.173C-21-206.69 102.481-301.745 268.164-326.724c135.466-20.425 262.475 14.082 352.543 66.747 73.6 43.038 130.596 100.528 173.92 168.28 45.22 70.716 76.36 154.26 78.971 263.233 1.337 55.83-7.805 107.532-20.684 150.417-13.034 43.41-33.996 79.695-52.646 118.455-36.406 75.659-82.049 144.981-127.855 214.345-136.437 206.606-264.496 417.31-320.58 706.028z"/><circle fill-rule="evenodd" clip-rule="evenodd" cx="352.891" cy="225.779" r="183.332"/></svg>`;
return 'data:image/svg+xml;base64,' + btoa(svg);
}
removeMarker() {
/* this.map$.subscribe(map =>
this.leafletMarker.addTo(map))*/

View File

@ -17,7 +17,6 @@
import L, { LatLngExpression, LeafletMouseEvent } from 'leaflet';
import { createTooltip } from './maps-utils';
import { functionValueCalculator, parseWithTranslation, safeExecute } from './common-maps-utils';
import 'leaflet-editable/src/Leaflet.Editable';
import { FormattedData, PolygonSettings } from './map-models';
export class Polygon {
@ -37,14 +36,12 @@ export class Polygon {
color: settings.polygonStrokeColor,
weight: settings.polygonStrokeWeight,
fillOpacity: settings.polygonOpacity,
opacity: settings.polygonStrokeOpacity
opacity: settings.polygonStrokeOpacity,
pmIgnore: !settings.editablePolygon
}).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.editablePolygon && onDragendListener) {
this.leafletPoly.on('pm:edit', (e) => onDragendListener(e, this.data));
}
@ -73,13 +70,7 @@ 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);
}

View File

@ -14,7 +14,8 @@
/// limitations under the License.
///
import L, { PolylineDecoratorOptions } from 'leaflet';
// @ts-ignore
import L, { PolylineDecorator, PolylineDecoratorOptions, Symbol } from 'leaflet';
import 'leaflet-polylinedecorator';
import { FormattedData, PolylineSettings } from './map-models';
@ -23,7 +24,7 @@ import { functionValueCalculator } from '@home/components/widget/lib/maps/common
export class Polyline {
leafletPoly: L.Polyline;
polylineDecorator: L.PolylineDecorator;
polylineDecorator: PolylineDecorator;
dataSources: FormattedData[];
data: FormattedData;
@ -36,7 +37,7 @@ export class Polyline {
).addTo(this.map);
if (settings.usePolylineDecorator) {
this.polylineDecorator = L.polylineDecorator(this.leafletPoly, this.getDecoratorSettings(settings)).addTo(this.map);
this.polylineDecorator = new PolylineDecorator(this.leafletPoly, this.getDecoratorSettings(settings)).addTo(this.map);
}
}
@ -47,7 +48,7 @@ export class Polyline {
offset: settings.decoratorOffset,
endOffset: settings.endDecoratorOffset,
repeat: settings.decoratorRepeat,
symbol: L.Symbol[settings.decoratorSymbol]({
symbol: Symbol[settings.decoratorSymbol]({
pixelSize: settings.decoratorSymbolSize,
polygon: false,
pathOptions: {
@ -78,7 +79,8 @@ export class Polyline {
opacity: functionValueCalculator(settings.useStrokeOpacityFunction, settings.strokeOpacityFunction,
[this.data, this.dataSources, this.data.dsIndex], settings.strokeOpacity),
weight: functionValueCalculator(settings.useStrokeWeightFunction, settings.strokeWeightFunction,
[this.data, this.dataSources, this.data.dsIndex], settings.strokeWeight)
[this.data, this.dataSources, this.data.dsIndex], settings.strokeWeight),
pmIgnore: true
};
}

View File

@ -38,7 +38,6 @@ export class GoogleMap extends LeafletMap {
this.loadGoogle(() => {
const map = L.map($container, {
attributionControl: false,
editable: !!options.editablePolygon,
tap: L.Browser.safari && L.Browser.mobile
}).setView(options?.defaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL);
(L.gridLayer as any).googleMutant({

View File

@ -23,7 +23,6 @@ export class HEREMap extends LeafletMap {
constructor(ctx: WidgetContext, $container, options: UnitedMapSettings) {
super(ctx, $container, options);
const map = L.map($container, {
editable: !!options.editablePolygon,
tap: L.Browser.safari && L.Browser.mobile
}).setView(options?.defaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL);
const tileLayer = (L.tileLayer as any).provider(options.mapProviderHere || 'HERE.normalDay', options.credentials);

View File

@ -22,7 +22,6 @@ import { filter, map, mergeMap } from 'rxjs/operators';
import {
aspectCache,
calculateNewPointCoordinate,
checkLngLat,
parseFunction
} from '@home/components/widget/lib/maps/common-maps-utils';
import { WidgetContext } from '@home/models/widget-component.models';
@ -221,7 +220,6 @@ export class ImageMap extends LeafletMap {
zoom: 1,
crs: L.CRS.Simple,
attributionControl: false,
editable: !!this.options.editablePolygon,
tap: L.Browser.safari && L.Browser.mobile
});
this.updateBounds(updateImage);
@ -263,7 +261,13 @@ export class ImageMap extends LeafletMap {
return L.CRS.Simple.latLngToPoint(latLng, maxZoom - 1);
}
convertToCustomFormat(position: L.LatLng, offset = 0, width = this.width, height = this.height): object {
convertToCustomFormat(position: L.LatLng, offset = 0, width = this.width, height = this.height): {[key: string]: any} {
if (!position) {
return {
[this.options.xPosKeyName]: null,
[this.options.yPosKeyName]: null
};
}
const point = this.latLngToPoint(position);
const customX = calculateNewPointCoordinate(point.x, width);
const customY = calculateNewPointCoordinate(point.y, height);
@ -279,13 +283,9 @@ export class ImageMap extends LeafletMap {
point.y = height;
}
const customLatLng = checkLngLat(this.pointToLatLng(point.x, point.y), this.southWest, this.northEast, offset);
return {
[this.options.xPosKeyName]: customX,
[this.options.yPosKeyName]: customY,
[this.options.latKeyName]: customLatLng.lat,
[this.options.lngKeyName]: customLatLng.lng
[this.options.yPosKeyName]: customY
};
}
@ -304,9 +304,10 @@ export class ImageMap extends LeafletMap {
}
}
convertPolygonToCustomFormat(expression: any[][]): object {
convertPolygonToCustomFormat(expression: any[][]): {[key: string]: any} {
const coordinate = expression ? this.convertToPolygonFormat(expression) : null;
return {
[this.options.polygonKeyName] : this.convertToPolygonFormat(expression)
[this.options.polygonKeyName]: coordinate
};
}
}

View File

@ -23,7 +23,6 @@ export class OpenStreetMap extends LeafletMap {
constructor(ctx: WidgetContext, $container, options: UnitedMapSettings) {
super(ctx, $container, options);
const map = L.map($container, {
editable: !!options.editablePolygon,
tap: L.Browser.safari && L.Browser.mobile
}).setView(options?.defaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL);
let tileLayer;

View File

@ -25,7 +25,6 @@ export class TencentMap extends LeafletMap {
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, {
editable: !!options.editablePolygon,
tap: L.Browser.safari && L.Browser.mobile
}).setView(options?.defaultCenterPosition, options?.defaultZoomLevel || DEFAULT_ZOOM_LEVEL);
const txLayer = L.tileLayer(txUrl, {

View File

@ -728,6 +728,11 @@ export const markerClusteringSettingsSchemaLeaflet =
type: 'number',
default: 80
},
spiderfyOnMaxZoom: {
title: 'Spiderfy at the max zoom level (to see all cluster markers)',
type: 'boolean',
default: false
},
chunkedLoading: {
title: 'Use chunks for adding markers so that the page does not freeze',
type: 'boolean',
@ -747,6 +752,7 @@ export const markerClusteringSettingsSchemaLeaflet =
'showCoverageOnHover',
'animate',
'maxClusterRadius',
'spiderfyOnMaxZoom',
'chunkedLoading',
'removeOutsideVisibleBounds'
]

View File

@ -100,7 +100,10 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
addGroupInfo(schema, 'Path Settings');
addToSchema(schema, addCondition(pointSchema, 'model.showPoints === true', ['showPoints']));
addGroupInfo(schema, 'Path Points Settings');
addToSchema(schema, addCondition(mapPolygonSchema, 'model.showPolygon === true', ['showPolygon']));
const mapPolygonSchemaWithoutEdit = mapPolygonSchema;
delete mapPolygonSchemaWithoutEdit.schema.properties.editablePolygon;
mapPolygonSchemaWithoutEdit.form.splice(mapPolygonSchemaWithoutEdit.form.indexOf('editablePolygon'), 1);
addToSchema(schema, addCondition(mapPolygonSchemaWithoutEdit, 'model.showPolygon === true', ['showPolygon']));
addGroupInfo(schema, 'Polygon Settings');
return schema;
}
@ -115,6 +118,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
rotationAngle: 0
};
this.settings = { ...settings, ...this.ctx.settings };
this.settings.editablePolygon = false;
this.useAnchors = this.settings.showPoints && this.settings.usePointAsAnchor;
this.settings.pointAsAnchorFunction = parseFunction(this.settings.pointAsAnchorFunction, ['data', 'dsData', 'dsIndex']);
this.settings.tooltipFunction = parseFunction(this.settings.tooltipFunction, ['data', 'dsData', 'dsIndex']);

View File

@ -41,6 +41,7 @@ import { EdgesOverviewWidgetComponent } from '@home/components/widget/lib/edges-
import { JsonInputWidgetComponent } from '@home/components/widget/lib/json-input-widget.component';
import { QrCodeWidgetComponent } from '@home/components/widget/lib/qrcode-widget.component';
import { MarkdownWidgetComponent } from '@home/components/widget/lib/markdown-widget.component';
import { SelectEntityDialogComponent } from './lib/maps/dialogs/select-entity-dialog.component';
@NgModule({
declarations:
@ -62,7 +63,8 @@ import { MarkdownWidgetComponent } from '@home/components/widget/lib/markdown-wi
NavigationCardsWidgetComponent,
NavigationCardWidgetComponent,
QrCodeWidgetComponent,
MarkdownWidgetComponent
MarkdownWidgetComponent,
SelectEntityDialogComponent
],
imports: [
CommonModule,

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path d="M24 4c-7.72 0-14 6.28-14 14 0 10.5 14 26 14 26s14-15.5 14-26c0-7.72-6.28-14-14-14zm8 16h-6v6h-4v-6h-6v-4h6v-6h4v6h6v4z"/></svg>

Before

Width:  |  Height:  |  Size: 219 B

View File

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48">
<polygon points="2,46 46,46 24,2" style="fill:transparent;stroke:black;stroke-width:2"/>
</svg>

Before

Width:  |  Height:  |  Size: 182 B

View File

@ -3238,7 +3238,40 @@
"update-timeseries": "Update timeseries",
"value": "Value"
},
"invalid-qr-code-text": "Invalid input text for QR code. Input should have a string type"
"invalid-qr-code-text": "Invalid input text for QR code. Input should have a string type",
"maps": {
"select-entity": "Select entity",
"select-entity-hint": "Hint: after selection click at the map to set position",
"tooltips": {
"placeMarker": "Click to place marker",
"firstVertex": "Click to place first point",
"continueLine": "Click to continue drawing",
"finishLine": "Click any existing marker to finish",
"finishPoly": "Click first marker to finish and save",
"finishRect": "Click to finish and save",
"startCircle": "Click to place circle center",
"finishCircle": "Click to finish circle",
"placeCircleMarker": "Click to place circle marker"
},
"actions": {
"finish": "Finish",
"cancel": "Cancel",
"removeLastVertex": "Remove last point"
},
"buttonTitles": {
"drawMarkerButton": "Create marker",
"drawPolyButton": "Create polygon",
"drawLineButton": "Create Polyline",
"drawCircleButton": "Create Circle",
"drawRectButton": "Create rectangle",
"editButton": "Edit mode",
"dragButton": "Drag-drop mode",
"cutButton": "Cut polygon area",
"deleteButton": "Remove",
"drawCircleMarkerButton": "Create circle marker",
"rotateButton": "Rotate polygon"
}
}
},
"icon": {
"icon": "Icon",

Binary file not shown.

After

Width:  |  Height:  |  Size: 712 B

View File

@ -3,7 +3,7 @@
"compilerOptions": {
"outDir": "../out-tsc/app",
"types": ["node", "jquery", "flot", "tooltipster", "tinycolor2", "js-beautify",
"react", "react-dom", "jstree", "raphael", "canvas-gauges", "leaflet", "leaflet.markercluster", "leaflet-editable"]
"react", "react-dom", "jstree", "raphael", "canvas-gauges"]
},
"angularCompilerOptions": {
"fullTemplateTypeCheck": true

View File

@ -17,15 +17,13 @@
import * as L from 'leaflet';
declare module 'leaflet' {
namespace Control {
class AddMarker extends L.Control { }
class AddPolygon extends L.Control { }
namespace PM {
interface PMMapToolbar {
toggleButton(
name: string,
status: boolean,
disableOthers?: boolean
): void;
}
namespace control {
function addMarker(options): Control.AddMarker;
function addPolygon(options): Control.AddPolygon;
}
}
}

View File

@ -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"
"node_modules/@geoman-io/leaflet-geoman-free/dist/leaflet-geoman.d.ts",
"src/typings/leaflet-geoman-extend.d.ts"
],
"paths": {
"@app/*": ["src/app/*"],

View File

@ -1422,6 +1422,18 @@
"@types/flowjs" "2.13.3"
tslib "^1.9.0"
"@geoman-io/leaflet-geoman-free@^2.11.3":
version "2.11.3"
resolved "https://registry.yarnpkg.com/@geoman-io/leaflet-geoman-free/-/leaflet-geoman-free-2.11.3.tgz#480164ab76c2b2a885003e0c111284f3c3160a36"
integrity sha512-LsiurEgKEHBcTnAVl8h7EfS5V/doCuxePzPE9SnfrhtJBN7IzP6UwkEo35Agwko+BnIuw/o2bE4F7irvKwQzjw==
dependencies:
"@turf/boolean-contains" "6.3.0"
"@turf/kinks" "6.3.0"
"@turf/line-intersect" "6.3.0"
"@turf/line-split" "6.3.0"
lodash "4.17.21"
polygon-clipping "0.15.3"
"@istanbuljs/schema@^0.1.2":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd"
@ -1688,6 +1700,167 @@
resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.1.1.tgz#3348564048e7a2d7398c935d466c0414ebb6a669"
integrity sha512-Z6DoceYb/1xSg5+e+ZlPZ9v0N16ZvZ+wYMraFue4HYrE4ttONKtsvruIRf6t9TBR0YvSOfi1hUU0fJfBLCDYow==
"@turf/bbox@*", "@turf/bbox@^6.3.0":
version "6.5.0"
resolved "https://registry.yarnpkg.com/@turf/bbox/-/bbox-6.5.0.tgz#bec30a744019eae420dac9ea46fb75caa44d8dc5"
integrity sha512-RBbLaao5hXTYyyg577iuMtDB8ehxMlUqHEJiMs8jT1GHkFhr6sYre3lmLsPeYEi/ZKj5TP5tt7fkzNdJ4GIVyw==
dependencies:
"@turf/helpers" "^6.5.0"
"@turf/meta" "^6.5.0"
"@turf/bearing@^6.5.0":
version "6.5.0"
resolved "https://registry.yarnpkg.com/@turf/bearing/-/bearing-6.5.0.tgz#462a053c6c644434bdb636b39f8f43fb0cd857b0"
integrity sha512-dxINYhIEMzgDOztyMZc20I7ssYVNEpSv04VbMo5YPQsqa80KO3TFvbuCahMsCAW5z8Tncc8dwBlEFrmRjJG33A==
dependencies:
"@turf/helpers" "^6.5.0"
"@turf/invariant" "^6.5.0"
"@turf/boolean-contains@6.3.0":
version "6.3.0"
resolved "https://registry.yarnpkg.com/@turf/boolean-contains/-/boolean-contains-6.3.0.tgz#fe4fc359e408c8c3c89e7fb159c9d31fde48779a"
integrity sha512-1MW7B5G5tIu1lnAv3pXyFzl75wfBYnbA2GhwHDb4okIXMhloy/r5uIqAZHo0fOXykKVJS/gIfA/MioKIftoTug==
dependencies:
"@turf/bbox" "^6.3.0"
"@turf/boolean-point-in-polygon" "^6.3.0"
"@turf/boolean-point-on-line" "^6.3.0"
"@turf/helpers" "^6.3.0"
"@turf/invariant" "^6.3.0"
"@turf/boolean-point-in-polygon@^6.3.0":
version "6.5.0"
resolved "https://registry.yarnpkg.com/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-6.5.0.tgz#6d2e9c89de4cd2e4365004c1e51490b7795a63cf"
integrity sha512-DtSuVFB26SI+hj0SjrvXowGTUCHlgevPAIsukssW6BG5MlNSBQAo70wpICBNJL6RjukXg8d2eXaAWuD/CqL00A==
dependencies:
"@turf/helpers" "^6.5.0"
"@turf/invariant" "^6.5.0"
"@turf/boolean-point-on-line@^6.3.0":
version "6.5.0"
resolved "https://registry.yarnpkg.com/@turf/boolean-point-on-line/-/boolean-point-on-line-6.5.0.tgz#a8efa7bad88760676f395afb9980746bc5b376e9"
integrity sha512-A1BbuQ0LceLHvq7F/P7w3QvfpmZqbmViIUPHdNLvZimFNLo4e6IQunmzbe+8aSStH9QRZm3VOflyvNeXvvpZEQ==
dependencies:
"@turf/helpers" "^6.5.0"
"@turf/invariant" "^6.5.0"
"@turf/destination@^6.5.0":
version "6.5.0"
resolved "https://registry.yarnpkg.com/@turf/destination/-/destination-6.5.0.tgz#30a84702f9677d076130e0440d3223ae503fdae1"
integrity sha512-4cnWQlNC8d1tItOz9B4pmJdWpXqS0vEvv65bI/Pj/genJnsL7evI0/Xw42RvEGROS481MPiU80xzvwxEvhQiMQ==
dependencies:
"@turf/helpers" "^6.5.0"
"@turf/invariant" "^6.5.0"
"@turf/distance@^6.5.0":
version "6.5.0"
resolved "https://registry.yarnpkg.com/@turf/distance/-/distance-6.5.0.tgz#21f04d5f86e864d54e2abde16f35c15b4f36149a"
integrity sha512-xzykSLfoURec5qvQJcfifw/1mJa+5UwByZZ5TZ8iaqjGYN0vomhV9aiSLeYdUGtYRESZ+DYC/OzY+4RclZYgMg==
dependencies:
"@turf/helpers" "^6.5.0"
"@turf/invariant" "^6.5.0"
"@turf/helpers@6.x", "@turf/helpers@^6.3.0", "@turf/helpers@^6.5.0":
version "6.5.0"
resolved "https://registry.yarnpkg.com/@turf/helpers/-/helpers-6.5.0.tgz#f79af094bd6b8ce7ed2bd3e089a8493ee6cae82e"
integrity sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw==
"@turf/invariant@^6.3.0", "@turf/invariant@^6.5.0":
version "6.5.0"
resolved "https://registry.yarnpkg.com/@turf/invariant/-/invariant-6.5.0.tgz#970afc988023e39c7ccab2341bd06979ddc7463f"
integrity sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg==
dependencies:
"@turf/helpers" "^6.5.0"
"@turf/kinks@6.3.0":
version "6.3.0"
resolved "https://registry.yarnpkg.com/@turf/kinks/-/kinks-6.3.0.tgz#a16b4ccc5a5aae139d43e36271e0a0494fdb4bf7"
integrity sha512-BLWvbl2/fa4SeJzVMbleT6Vo1cmzwmzRfxL2xxMei2jmf6JSvqDoMJFwIHGXrLZXvhOCb1b2C+MhBfhtc7kYkQ==
dependencies:
"@turf/helpers" "^6.3.0"
"@turf/line-intersect@6.3.0":
version "6.3.0"
resolved "https://registry.yarnpkg.com/@turf/line-intersect/-/line-intersect-6.3.0.tgz#726a50edc66bb7b5e798b052b103fb0da4d1c4f4"
integrity sha512-3naxR7XpkPd2vst3Mw6DFry4C9m3o0/f2n/xu5UAyxb88Ie4m2k+1eqkhzMMx/0L+E6iThWpLx7DASM6q6o9ow==
dependencies:
"@turf/helpers" "^6.3.0"
"@turf/invariant" "^6.3.0"
"@turf/line-segment" "^6.3.0"
"@turf/meta" "^6.3.0"
geojson-rbush "3.x"
"@turf/line-intersect@^6.3.0", "@turf/line-intersect@^6.5.0":
version "6.5.0"
resolved "https://registry.yarnpkg.com/@turf/line-intersect/-/line-intersect-6.5.0.tgz#dea48348b30c093715d2195d2dd7524aee4cf020"
integrity sha512-CS6R1tZvVQD390G9Ea4pmpM6mJGPWoL82jD46y0q1KSor9s6HupMIo1kY4Ny+AEYQl9jd21V3Scz20eldpbTVA==
dependencies:
"@turf/helpers" "^6.5.0"
"@turf/invariant" "^6.5.0"
"@turf/line-segment" "^6.5.0"
"@turf/meta" "^6.5.0"
geojson-rbush "3.x"
"@turf/line-segment@^6.3.0", "@turf/line-segment@^6.5.0":
version "6.5.0"
resolved "https://registry.yarnpkg.com/@turf/line-segment/-/line-segment-6.5.0.tgz#ee73f3ffcb7c956203b64ed966d96af380a4dd65"
integrity sha512-jI625Ho4jSuJESNq66Mmi290ZJ5pPZiQZruPVpmHkUw257Pew0alMmb6YrqYNnLUuiVVONxAAKXUVeeUGtycfw==
dependencies:
"@turf/helpers" "^6.5.0"
"@turf/invariant" "^6.5.0"
"@turf/meta" "^6.5.0"
"@turf/line-split@6.3.0":
version "6.3.0"
resolved "https://registry.yarnpkg.com/@turf/line-split/-/line-split-6.3.0.tgz#ee218f66cd65ce84eafc4956c24083663f6082ea"
integrity sha512-Q0nUJ0vczy11piyEz0FaKScFwSQtb1HJ2RPEMCw1coUJhTCB02KBWQLImhYqwsD3uLg+H/fxaJ1Gva6EPWoDNQ==
dependencies:
"@turf/bbox" "^6.3.0"
"@turf/helpers" "^6.3.0"
"@turf/invariant" "^6.3.0"
"@turf/line-intersect" "^6.3.0"
"@turf/line-segment" "^6.3.0"
"@turf/meta" "^6.3.0"
"@turf/nearest-point-on-line" "^6.3.0"
"@turf/square" "^6.3.0"
"@turf/truncate" "^6.3.0"
geojson-rbush "3.x"
"@turf/meta@6.x", "@turf/meta@^6.3.0", "@turf/meta@^6.5.0":
version "6.5.0"
resolved "https://registry.yarnpkg.com/@turf/meta/-/meta-6.5.0.tgz#b725c3653c9f432133eaa04d3421f7e51e0418ca"
integrity sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA==
dependencies:
"@turf/helpers" "^6.5.0"
"@turf/nearest-point-on-line@^6.3.0":
version "6.5.0"
resolved "https://registry.yarnpkg.com/@turf/nearest-point-on-line/-/nearest-point-on-line-6.5.0.tgz#8e1cd2cdc0b5acaf4c8d8b3b33bb008d3cb99e7b"
integrity sha512-WthrvddddvmymnC+Vf7BrkHGbDOUu6Z3/6bFYUGv1kxw8tiZ6n83/VG6kHz4poHOfS0RaNflzXSkmCi64fLBlg==
dependencies:
"@turf/bearing" "^6.5.0"
"@turf/destination" "^6.5.0"
"@turf/distance" "^6.5.0"
"@turf/helpers" "^6.5.0"
"@turf/invariant" "^6.5.0"
"@turf/line-intersect" "^6.5.0"
"@turf/meta" "^6.5.0"
"@turf/square@^6.3.0":
version "6.5.0"
resolved "https://registry.yarnpkg.com/@turf/square/-/square-6.5.0.tgz#ab43eef99d39c36157ab5b80416bbeba1f6b2122"
integrity sha512-BM2UyWDmiuHCadVhHXKIx5CQQbNCpOxB6S/aCNOCLbhCeypKX5Q0Aosc5YcmCJgkwO5BERCC6Ee7NMbNB2vHmQ==
dependencies:
"@turf/distance" "^6.5.0"
"@turf/helpers" "^6.5.0"
"@turf/truncate@^6.3.0":
version "6.5.0"
resolved "https://registry.yarnpkg.com/@turf/truncate/-/truncate-6.5.0.tgz#c3a16cad959f1be1c5156157d5555c64b19185d8"
integrity sha512-pFxg71pLk+eJj134Z9yUoRhIi8vqnnKvCYwdT4x/DQl/19RVdq1tV3yqOT3gcTQNfniteylL5qV1uTBDV5sgrg==
dependencies:
"@turf/helpers" "^6.5.0"
"@turf/meta" "^6.5.0"
"@types/canvas-gauges@^2.1.2":
version "2.1.2"
resolved "https://registry.yarnpkg.com/@types/canvas-gauges/-/canvas-gauges-2.1.2.tgz#fb9ece324cb15ae137791ad21eb2db70e11a7210"
@ -1725,6 +1898,11 @@
resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.7.tgz#c8fa532b60a0042219cdf173ca21a975ef0666ad"
integrity sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ==
"@types/geojson@7946.0.8":
version "7946.0.8"
resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.8.tgz#30744afdb385e2945e22f3b033f897f76b1f12ca"
integrity sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==
"@types/glob@^7.1.1":
version "7.1.3"
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183"
@ -1791,34 +1969,48 @@
dependencies:
"@types/jquery" "*"
"@types/leaflet-editable@^1.2.1":
"@types/leaflet-polylinedecorator@^1.6.1":
version "1.6.1"
resolved "https://registry.yarnpkg.com/@types/leaflet-polylinedecorator/-/leaflet-polylinedecorator-1.6.1.tgz#b6522f9dae52146bf73da249e4bedfbab200c6e4"
integrity sha512-9etweJ2U4SWqcV/AR3i0NdWJByeMn6+zMUNlO6jVbpL8UI6qrMKybu8v9/s6UR4oXvsV4lZT6vzAsNAAMq5Ssg==
dependencies:
"@types/leaflet" "*"
"@types/leaflet-providers@^1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@types/leaflet-editable/-/leaflet-editable-1.2.1.tgz#12f1bd1d9af7beafbac256216062e97fe2ee4d55"
integrity sha512-7Oms1HgulWiclkI0s1XLmr1yRylNoJX8sVUfAv9+28JzwWbKbLcQ6//vhFEOmoMlBQyL5veogKpUUb5qeF+Qyg==
resolved "https://registry.yarnpkg.com/@types/leaflet-providers/-/leaflet-providers-1.2.1.tgz#620669b828959740a2d8572e0c0288a2382d3564"
integrity sha512-uNyuXiNV2q3fmgNjQji2P6RjQISmL40bbOL91/3OAwiE3XhkLKPmSAtAcfe11MAIz45iEjdFZJWppq9QyfnPIw==
dependencies:
"@types/leaflet" "*"
"@types/leaflet-polylinedecorator@^1.6.0":
version "1.6.0"
resolved "https://registry.yarnpkg.com/@types/leaflet-polylinedecorator/-/leaflet-polylinedecorator-1.6.0.tgz#1572131ffedb3154c6e18e682d2fb700e203af19"
integrity sha512-Z2BXZDjKEqHclwrAmhYdF1RwyFfa/NFxsoF79sitzaj5D/4YWHp/zDRcUZar5cQFKRgK66AYEIF7nKVuMzUGdw==
"@types/leaflet.gridlayer.googlemutant@^0.4.6":
version "0.4.6"
resolved "https://registry.yarnpkg.com/@types/leaflet.gridlayer.googlemutant/-/leaflet.gridlayer.googlemutant-0.4.6.tgz#86d3ba9d432dec29b4796e37d815c233680e7fcb"
integrity sha512-L0J7NadcZp5bcKQrv4DVlsEbQ90xLsOKScckAMnxoghh/wogk0GVkauYOYHBKeKDkx9qkMRzTf8oO+fKeYD7oQ==
dependencies:
"@types/leaflet" "*"
"@types/leaflet.markercluster@^1.4.4":
version "1.4.4"
resolved "https://registry.yarnpkg.com/@types/leaflet.markercluster/-/leaflet.markercluster-1.4.4.tgz#4b4772c86182923e920061a0c25cb3e53543ad35"
integrity sha512-BQAilNWlBpYl4+PrsJXLOh4vyv7KfWi5kh3Fclg5y4gEeNeXKqhS6y1zzBB4+wcTuVUnMWfm2G0MfqA4yA5A5A==
"@types/leaflet.markercluster@^1.4.6":
version "1.4.6"
resolved "https://registry.yarnpkg.com/@types/leaflet.markercluster/-/leaflet.markercluster-1.4.6.tgz#1159460b374ba5e329cb678d0e427f99dca75be5"
integrity sha512-MD+bUDzxHznY0zOlSBUAMNQUGB2+xpJPKrR2MNEoBAAKa3QTKJJySBtCqWyGLvYNNO+Cdyc2c64aF2IFwe4fcQ==
dependencies:
"@types/leaflet" "*"
"@types/leaflet@*", "@types/leaflet@1.5.17":
"@types/leaflet@*":
version "1.5.17"
resolved "https://registry.yarnpkg.com/@types/leaflet/-/leaflet-1.5.17.tgz#b2153dc12c344e6896a93ffc6b61ac79da251e5b"
integrity sha512-2XYq9k6kNjhNI7PaTz8Rdxcc8Vzwu97OaS9CtcrTxnTSxFUGwjlGjTDvhTLJU+JRSfZ4lBwGcl0SjZHALdVr6g==
dependencies:
"@types/geojson" "*"
"@types/leaflet@^1.7.6":
version "1.7.6"
resolved "https://registry.yarnpkg.com/@types/leaflet/-/leaflet-1.7.6.tgz#6580f4babb648972c5af3abc3d66866753fa9311"
integrity sha512-Emkz3V08QnlelSbpT46OEAx+TBZYTOX2r1yM7W+hWg5+djHtQ1GbEXBDRLaqQDOYcDI51Ss0ayoqoKD4CtLUDA==
dependencies:
"@types/geojson" "*"
"@types/lodash@^4.14.170":
version "4.14.170"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.170.tgz#0d67711d4bf7f4ca5147e9091b847479b87925d6"
@ -4858,6 +5050,17 @@ gensync@^1.0.0-beta.2:
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
geojson-rbush@3.x:
version "3.2.0"
resolved "https://registry.yarnpkg.com/geojson-rbush/-/geojson-rbush-3.2.0.tgz#8b543cf0d56f99b78faf1da52bb66acad6dfc290"
integrity sha512-oVltQTXolxvsz1sZnutlSuLDEcQAKYC/uXt9zDzJJ6bu0W+baTI8LZBaTup5afzibEH4N3jlq2p+a152wlBJ7w==
dependencies:
"@turf/bbox" "*"
"@turf/helpers" "6.x"
"@turf/meta" "6.x"
"@types/geojson" "7946.0.8"
rbush "^3.0.1"
get-caller-file@^2.0.1, get-caller-file@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
@ -6224,11 +6427,6 @@ klona@^2.0.4:
resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0"
integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==
leaflet-editable@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/leaflet-editable/-/leaflet-editable-1.2.0.tgz#a3a01001764ba58ea923381ee6a1c814708a0b84"
integrity sha512-wG11JwpL8zqIbypTop6xCRGagMuWw68ihYu4uqrqc5Ep0wnEJeyob7NB2Rt5t74Oih4rwJ3OfwaGbzdowOGfYQ==
leaflet-polylinedecorator@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/leaflet-polylinedecorator/-/leaflet-polylinedecorator-1.6.0.tgz#9ef79fd1b5302d67b72efe959a8ecd2553f27266"
@ -6236,25 +6434,25 @@ leaflet-polylinedecorator@^1.6.0:
dependencies:
leaflet-rotatedmarker "^0.2.0"
leaflet-providers@^1.12.0:
version "1.12.0"
resolved "https://registry.yarnpkg.com/leaflet-providers/-/leaflet-providers-1.12.0.tgz#bf407f580d9564480e2346bc1e6412ef696624cf"
integrity sha512-pU/mR4B+NbayBGCg5/88dmRq7t1EGiNPhsVGV3yqHuDn594vIwus4CiPVW0RtiKJNKg8Vf1pILAbFl0i+yk+lQ==
leaflet-providers@^1.13.0:
version "1.13.0"
resolved "https://registry.yarnpkg.com/leaflet-providers/-/leaflet-providers-1.13.0.tgz#10c843a23d5823a65096d40ad53f27029e13434b"
integrity sha512-f/sN5wdgBbVA2jcCYzScIfYNxKdn2wBJP9bu+5cRX9Xj6g8Bt1G9Sr8WgJAt/ckIFIc3LVVxCBNFpSCfTuUElg==
leaflet-rotatedmarker@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/leaflet-rotatedmarker/-/leaflet-rotatedmarker-0.2.0.tgz#4467f49f98d1bfd56959bd9c6705203dd2601277"
integrity sha1-RGf0n5jRv9VpWb2cZwUgPdJgEnc=
leaflet.gridlayer.googlemutant@0.10.2:
version "0.10.2"
resolved "https://registry.yarnpkg.com/leaflet.gridlayer.googlemutant/-/leaflet.gridlayer.googlemutant-0.10.2.tgz#3c5351db4230beac1b1ea1f774d9288cfb0b6283"
integrity sha512-r3le0W8izKmF2aeCCYp6P+dLQvPadV/vpJkres0ltDHiWac6qt3fQPNWjQl+8WCsCmcGTb1y5bmHOx0Yj6HA7g==
leaflet.gridlayer.googlemutant@0.13.4:
version "0.13.4"
resolved "https://registry.yarnpkg.com/leaflet.gridlayer.googlemutant/-/leaflet.gridlayer.googlemutant-0.13.4.tgz#0add37d240c70c999e1f1d341208e6fea2372c40"
integrity sha512-oC6xUSFJ9HP4WIupXakgiYckdBHuHQeSaxTXsVlcvcpfsuYoJ/HFIrz1bmK4Qr/qKO4fY1MDM6AoewU7Bph8ZQ==
leaflet.markercluster@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/leaflet.markercluster/-/leaflet.markercluster-1.5.0.tgz#54db42485da32fc3d92c7ae22d0d7982879e0b67"
integrity sha512-Fvf/cq4o806mJL50n+fZW9+QALDDLPvt7vuAjlD2vfnxx3srMDs2vWINJze4nKYJYRY45OC6tM/669C3pLwMCA==
leaflet.markercluster@^1.5.3:
version "1.5.3"
resolved "https://registry.yarnpkg.com/leaflet.markercluster/-/leaflet.markercluster-1.5.3.tgz#9cdb52a4eab92671832e1ef9899669e80efc4056"
integrity sha512-vPTw/Bndq7eQHjLBVlWpnGeLa3t+3zGiuM7fJwCkiMFq+nmRuG3RI3f7f4N4TDX7T4NpbAXpR2+NTRSEGfCSeA==
leaflet@^1.7.1:
version "1.7.1"
@ -6365,7 +6563,7 @@ lodash.uniq@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
lodash@^4.0.1, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@~4.17.21:
lodash@4.17.21, lodash@^4.0.1, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@~4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@ -7651,6 +7849,13 @@ pnp-webpack-plugin@1.6.4:
dependencies:
ts-pnp "^1.1.6"
polygon-clipping@0.15.3:
version "0.15.3"
resolved "https://registry.yarnpkg.com/polygon-clipping/-/polygon-clipping-0.15.3.tgz#0215840438470ba2e9e6593625e4ea5c1087b4b7"
integrity sha512-ho0Xx5DLkgxRx/+n4O74XyJ67DcyN3Tu9bGYKsnTukGAW6ssnuak6Mwcyb1wHy9MZc9xsUWqIoiazkZB5weECg==
dependencies:
splaytree "^3.1.0"
popper.js@1.16.1-lts:
version "1.16.1-lts"
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1-lts.tgz#cf6847b807da3799d80ee3d6d2f90df8a3f50b05"
@ -8191,6 +8396,11 @@ querystringify@^2.1.1:
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
quickselect@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/quickselect/-/quickselect-2.0.0.tgz#f19680a486a5eefb581303e023e98faaf25dd018"
integrity sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==
raf@^3.4.0, raf@^3.4.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
@ -8243,6 +8453,13 @@ raw-loader@4.0.2:
loader-utils "^2.0.0"
schema-utils "^3.0.0"
rbush@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/rbush/-/rbush-3.0.1.tgz#5fafa8a79b3b9afdfe5008403a720cc1de882ecf"
integrity sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==
dependencies:
quickselect "^2.0.0"
rc-align@^4.0.0:
version "4.0.8"
resolved "https://registry.yarnpkg.com/rc-align/-/rc-align-4.0.8.tgz#276c3f5dfadf0de4bb95392cb81568c9e947a668"
@ -9298,6 +9515,11 @@ speed-measure-webpack-plugin@1.4.2:
dependencies:
chalk "^4.1.0"
splaytree@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/splaytree/-/splaytree-3.1.0.tgz#17d4a0108a6da3627579690b7b847241e18ddec8"
integrity sha512-gvUGR7xnOy0fLKTCxDeUZYgU/I1Tdf8M/lM1Qrf8L2TIOR5ipZjGk02uYcdv0o2x7WjVRgpm3iS2clLyuVAt0Q==
split-string@^3.0.1, split-string@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"