Merge branch 'vvlladd28-feature/map/circle'
This commit is contained in:
commit
6e70e2e2c2
File diff suppressed because one or more lines are too long
164
ui-ngx/src/app/modules/home/components/widget/lib/maps/circle.ts
Normal file
164
ui-ngx/src/app/modules/home/components/widget/lib/maps/circle.ts
Normal file
@ -0,0 +1,164 @@
|
||||
///
|
||||
/// Copyright © 2016-2022 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 L, { LeafletMouseEvent } from 'leaflet';
|
||||
import { CircleData, FormattedData, UnitedMapSettings } from '@home/components/widget/lib/maps/map-models';
|
||||
import {
|
||||
fillPattern,
|
||||
functionValueCalculator,
|
||||
parseWithTranslation,
|
||||
processPattern,
|
||||
safeExecute
|
||||
} from '@home/components/widget/lib/maps/common-maps-utils';
|
||||
import LeafletMap from '@home/components/widget/lib/maps/leaflet-map';
|
||||
import { createTooltip } from '@home/components/widget/lib/maps/maps-utils';
|
||||
|
||||
export class Circle {
|
||||
|
||||
private editing = false;
|
||||
private circleData: CircleData;
|
||||
|
||||
leafletCircle: L.Circle;
|
||||
tooltip: L.Popup;
|
||||
|
||||
constructor(private map: LeafletMap,
|
||||
public data: FormattedData,
|
||||
public dataSources: FormattedData[],
|
||||
private settings: UnitedMapSettings,
|
||||
private onDragendListener?) {
|
||||
this.circleData = this.map.convertToCircleFormat(JSON.parse(data[this.settings.circleKeyName]));
|
||||
const centerPosition = this.createCenterPosition();
|
||||
const circleFillColor = this.getFillColor();
|
||||
const circleStrokeColor = this.getStrokeColor();
|
||||
this.leafletCircle = L.circle(centerPosition, {
|
||||
radius: this.circleData.radius,
|
||||
fillColor: circleFillColor,
|
||||
color: circleStrokeColor,
|
||||
weight: settings.circleStrokeWeight,
|
||||
fillOpacity: settings.circleFillColorOpacity,
|
||||
opacity: settings.circleStrokeOpacity,
|
||||
pmIgnore: !settings.editableCircle,
|
||||
snapIgnore: !settings.snappable
|
||||
}).addTo(this.map.map);
|
||||
|
||||
if (settings.showCircleLabel) {
|
||||
this.updateLabel();
|
||||
}
|
||||
|
||||
if (settings.showCircleTooltip) {
|
||||
this.tooltip = createTooltip(this.leafletCircle, settings, data.$datasource,
|
||||
settings.autoCloseCircleTooltip, settings.showCircleTooltipAction);
|
||||
this.updateTooltip();
|
||||
}
|
||||
|
||||
this.createEventListeners();
|
||||
}
|
||||
|
||||
private createEventListeners() {
|
||||
if (this.settings.editableCircle && this.onDragendListener) {
|
||||
// Change center position (call in drag drop mode)
|
||||
this.leafletCircle.on('pm:dragstart', () => this.editing = true);
|
||||
this.leafletCircle.on('pm:dragend', () => this.editing = false);
|
||||
// Change radius (call in edit mode)
|
||||
this.leafletCircle.on('pm:markerdragstart', () => this.editing = true);
|
||||
this.leafletCircle.on('pm:markerdragend', () => this.editing = false);
|
||||
this.leafletCircle.on('pm:edit', (e) => this.onDragendListener(e, this.data));
|
||||
}
|
||||
|
||||
if (this.settings.circleClick) {
|
||||
this.leafletCircle.on('click', (event: LeafletMouseEvent) => {
|
||||
for (const action in this.settings.circleClick) {
|
||||
if (typeof (this.settings.circleClick[action]) === 'function') {
|
||||
this.settings.circleClick[action](event.originalEvent, this.data.$datasource);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private updateLabel() {
|
||||
this.leafletCircle.unbindTooltip();
|
||||
if (this.settings.showCircleLabel) {
|
||||
if (!this.map.circleLabelText || this.settings.useCircleLabelFunction) {
|
||||
const pattern = this.settings.useCircleLabelFunction ?
|
||||
safeExecute(this.settings.circleLabelFunction, [this.data, this.dataSources, this.data.dsIndex]) : this.settings.circleLabel;
|
||||
this.map.circleLabelText = parseWithTranslation.prepareProcessPattern(pattern, true);
|
||||
this.map.replaceInfoTooltipCircle = processPattern(this.map.circleLabelText, this.data);
|
||||
}
|
||||
const circleLabelText = fillPattern(this.map.circleLabelText, this.map.replaceInfoTooltipCircle, this.data);
|
||||
this.leafletCircle.bindTooltip(`<div style="color: ${this.settings.labelColor};"><b>${circleLabelText}</b></div>`,
|
||||
{ className: 'tb-polygon-label', permanent: true, sticky: true, direction: 'center'})
|
||||
.openTooltip(this.leafletCircle.getLatLng());
|
||||
}
|
||||
}
|
||||
|
||||
private updateTooltip() {
|
||||
const pattern = this.settings.useCircleTooltipFunction ?
|
||||
safeExecute(this.settings.circleTooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) :
|
||||
this.settings.circleTooltipPattern;
|
||||
this.tooltip.setContent(parseWithTranslation.parseTemplate(pattern, this.data, true));
|
||||
}
|
||||
|
||||
private updateCircleColor() {
|
||||
const circleFillColor = this.getFillColor();
|
||||
const circleStrokeColor = this.getStrokeColor();
|
||||
const style: L.PathOptions = {
|
||||
fillColor: circleFillColor,
|
||||
color: circleStrokeColor
|
||||
};
|
||||
this.leafletCircle.setStyle(style);
|
||||
}
|
||||
|
||||
updateCircle(data: FormattedData, dataSources: FormattedData[]) {
|
||||
if (this.editing) {
|
||||
return;
|
||||
}
|
||||
this.data = data;
|
||||
this.dataSources = dataSources;
|
||||
this.circleData = this.map.convertToCircleFormat(JSON.parse(data[this.settings.circleKeyName]));
|
||||
const newCenterPosition = this.createCenterPosition();
|
||||
if (!this.leafletCircle.getLatLng().equals(newCenterPosition) && !this.editing) {
|
||||
this.leafletCircle.setLatLng(newCenterPosition);
|
||||
}
|
||||
if (this.leafletCircle.getRadius() !== this.circleData.radius) {
|
||||
this.leafletCircle.setRadius(this.circleData.radius);
|
||||
}
|
||||
if (this.settings.showCircleLabel) {
|
||||
this.updateLabel();
|
||||
}
|
||||
if (this.settings.showCircleTooltip) {
|
||||
this.updateTooltip();
|
||||
}
|
||||
if (this.settings.useCircleStrokeColorFunction || this.settings.useCircleFillColorFunction) {
|
||||
this.updateCircleColor();
|
||||
}
|
||||
}
|
||||
|
||||
private createCenterPosition(): L.LatLng {
|
||||
return new L.LatLng(this.circleData.latitude, this.circleData.longitude);
|
||||
}
|
||||
|
||||
private getFillColor(): string | null {
|
||||
return functionValueCalculator(this.settings.useCircleFillColorFunction, this.settings.circleFillColorFunction,
|
||||
[this.data, this.dataSources, this.data.dsIndex], this.settings.circleFillColor);
|
||||
}
|
||||
|
||||
private getStrokeColor(): string | null {
|
||||
return functionValueCalculator(this.settings.useCircleStrokeColorFunction, this.settings.circleStrokeColorFunction,
|
||||
[this.data, this.dataSources, this.data.dsIndex], this.settings.circleStrokeColor);
|
||||
}
|
||||
|
||||
}
|
||||
@ -21,6 +21,7 @@ import { MarkerClusterGroup, MarkerClusterGroupOptions } from 'leaflet.markerclu
|
||||
import '@geoman-io/leaflet-geoman-free';
|
||||
|
||||
import {
|
||||
CircleData,
|
||||
defaultSettings,
|
||||
FormattedData,
|
||||
MapSettings,
|
||||
@ -36,7 +37,8 @@ import { Marker } from './markers';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { Polyline } from './polyline';
|
||||
import { Polygon } from './polygon';
|
||||
import { createTooltip } from '@home/components/widget/lib/maps/maps-utils';
|
||||
import { Circle } from './circle';
|
||||
import { createTooltip, isCutPolygon, isJSON } from '@home/components/widget/lib/maps/maps-utils';
|
||||
import {
|
||||
checkLngLat,
|
||||
createLoadingDiv,
|
||||
@ -45,19 +47,21 @@ import {
|
||||
safeExecute
|
||||
} 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 { deepClone, isDefinedAndNotNull, isNotEmptyStr, 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 ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance;
|
||||
|
||||
export default abstract class LeafletMap {
|
||||
|
||||
markers: Map<string, Marker> = new Map();
|
||||
polylines: Map<string, Polyline> = new Map();
|
||||
polygons: Map<string, Polygon> = new Map();
|
||||
circles: Map<string, Circle> = new Map();
|
||||
map: L.Map;
|
||||
options: UnitedMapSettings;
|
||||
bounds: L.LatLngBounds;
|
||||
@ -66,22 +70,25 @@ export default abstract class LeafletMap {
|
||||
points: FeatureGroup;
|
||||
markersData: FormattedData[] = [];
|
||||
polygonsData: FormattedData[] = [];
|
||||
circleData: FormattedData[] = [];
|
||||
defaultMarkerIconInfo: MarkerIconInfo;
|
||||
loadingDiv: JQuery<HTMLElement>;
|
||||
loading = false;
|
||||
replaceInfoLabelMarker: Array<ReplaceInfo> = [];
|
||||
markerLabelText: string;
|
||||
polygonLabelText: string;
|
||||
circleLabelText: string;
|
||||
replaceInfoLabelPolygon: Array<ReplaceInfo> = [];
|
||||
replaceInfoTooltipMarker: Array<ReplaceInfo> = [];
|
||||
replaceInfoTooltipCircle: Array<ReplaceInfo> = [];
|
||||
markerTooltipText: string;
|
||||
drawRoutes: boolean;
|
||||
showPolygon: boolean;
|
||||
updatePending = false;
|
||||
editPolygons = false;
|
||||
editCircle = false;
|
||||
selectedEntity: FormattedData;
|
||||
addMarkers: L.Marker[] = [];
|
||||
addPolygons: L.Polygon[] = [];
|
||||
ignoreUpdateBounds = false;
|
||||
// tslint:disable-next-line:no-string-literal
|
||||
southWest = new L.LatLng(-Projection.SphericalMercator['MAX_LATITUDE'], -180);
|
||||
// tslint:disable-next-line:no-string-literal
|
||||
@ -90,12 +97,14 @@ export default abstract class LeafletMap {
|
||||
saveMarkerLocation: (e: FormattedData, lat?: number, lng?: number) => Observable<any>;
|
||||
savePolygonLocation: (e: FormattedData, coordinates?: Array<any>) => Observable<any>;
|
||||
translateService: TranslateService;
|
||||
tooltipInstances: ITooltipsterInstance[] = [];
|
||||
|
||||
protected constructor(public ctx: WidgetContext,
|
||||
public $container: HTMLElement,
|
||||
options: UnitedMapSettings) {
|
||||
this.options = options;
|
||||
this.editPolygons = options.showPolygon && options.editablePolygon;
|
||||
this.editCircle = options.showCircle && options.editableCircle;
|
||||
L.Icon.Default.imagePath = '/';
|
||||
this.translateService = this.ctx.$injector.get(TranslateService);
|
||||
}
|
||||
@ -151,6 +160,9 @@ export default abstract class LeafletMap {
|
||||
case 'Marker':
|
||||
entities = this.datasources.filter(mData => !this.convertPosition(mData));
|
||||
break;
|
||||
case 'Circle':
|
||||
entities = this.datasources.filter(mData => !this.isValidCircle(mData));
|
||||
break;
|
||||
default:
|
||||
return of(null);
|
||||
}
|
||||
@ -181,6 +193,16 @@ export default abstract class LeafletMap {
|
||||
// @ts-ignore
|
||||
this.map.pm.Draw.tbMarker._hintMarker.setTooltipContent(tooltipText);
|
||||
break;
|
||||
case 'tbCircle':
|
||||
tooltipText = this.translateService.instant('widgets.maps.tooltips.startCircle', {entityName: data.entityName});
|
||||
// @ts-ignore
|
||||
this.map.pm.Draw.tbCircle._hintMarker.setTooltipContent(tooltipText);
|
||||
customTranslation = {
|
||||
tooltips: {
|
||||
finishCircle: this.translateService.instant('widgets.maps.tooltips.finishCircle', {entityName: data.entityName})
|
||||
}
|
||||
};
|
||||
break;
|
||||
case 'tbRectangle':
|
||||
tooltipText = this.translateService.instant('widgets.maps.tooltips.firstVertex', {entityName: data.entityName});
|
||||
// @ts-ignore
|
||||
@ -190,7 +212,6 @@ export default abstract class LeafletMap {
|
||||
finishRect: this.translateService.instant('widgets.maps.tooltips.finishRect', {entityName: data.entityName})
|
||||
}
|
||||
};
|
||||
this.map.pm.setLang('en', customTranslation, 'en');
|
||||
break;
|
||||
case 'tbPolygon':
|
||||
tooltipText = this.translateService.instant('widgets.maps.tooltips.firstVertex', {entityName: data.entityName});
|
||||
@ -202,9 +223,12 @@ export default abstract class LeafletMap {
|
||||
finishPoly: this.translateService.instant('widgets.maps.tooltips.finishPoly', {entityName: data.entityName})
|
||||
}
|
||||
};
|
||||
this.map.pm.setLang('en', customTranslation);
|
||||
break;
|
||||
}
|
||||
if (customTranslation) {
|
||||
this.map.pm.setLang('en', customTranslation, 'en');
|
||||
this.createdControlButtonTooltip();
|
||||
}
|
||||
} else {
|
||||
// @ts-ignore
|
||||
this.map.pm.Toolbar.toggleButton(type, false);
|
||||
@ -265,6 +289,48 @@ export default abstract class LeafletMap {
|
||||
});
|
||||
}
|
||||
|
||||
// Customize edit circle
|
||||
if (this.editCircle && !this.options.hideDrawControlButton) {
|
||||
const actions = [{
|
||||
text: L.PM.Utils.getTranslation('actions.cancel'),
|
||||
onClick: () => this.toggleDrawMode('tbCircle')
|
||||
}];
|
||||
|
||||
this.map.pm.Toolbar.copyDrawControl('Circle', {
|
||||
name: 'tbCircle',
|
||||
afterClick: () => this.selectEntityWithoutLocation('tbCircle'),
|
||||
disabled: true,
|
||||
actions
|
||||
});
|
||||
}
|
||||
|
||||
if (this.editPolygons && !this.options.hideEditControlButton) {
|
||||
this.map.pm.Toolbar.copyDrawControl('cutPolygon', {
|
||||
name: 'tbCut',
|
||||
title: this.translateService.instant('widgets.maps.buttonTitles.cutButton'),
|
||||
block: 'edit',
|
||||
onClick: () => {
|
||||
this.map.pm.setLang('en', {
|
||||
tooltips: {
|
||||
firstVertex: this.translateService.instant('widgets.maps.tooltips.firstVertex-cut'),
|
||||
continueLine: this.translateService.instant('widgets.maps.tooltips.continueLine-cut'),
|
||||
finishPoly: this.translateService.instant('widgets.maps.tooltips.finishPoly-cut')
|
||||
}
|
||||
}, 'en');
|
||||
this.createdControlButtonTooltip();
|
||||
},
|
||||
// @ts-ignore
|
||||
afterClick: (e, ctx) => {
|
||||
this.map.pm.Draw[ctx.button._button.jsClass].toggle({
|
||||
snappable: this.options.snappable,
|
||||
cursorMarker: true,
|
||||
allowSelfIntersection: false,
|
||||
});
|
||||
},
|
||||
});
|
||||
this.map.pm.Toolbar.changeControlOrder(['tbMarker', 'tbRectangle', 'tbPolygon', 'tbCircle', 'editMode', 'dragMode', 'tbCut', 'removalMode', 'rotateMode']);
|
||||
}
|
||||
|
||||
this.map.pm.setLang('en', this.translateService.instant('widgets.maps'), 'en');
|
||||
if (!this.options.hideAllControlButton) {
|
||||
this.map.pm.addControls({
|
||||
@ -277,8 +343,8 @@ export default abstract class LeafletMap {
|
||||
drawPolyline: false,
|
||||
drawPolygon: false,
|
||||
dragMode: !this.options.hideEditControlButton,
|
||||
editMode: this.editPolygons && !this.options.hideEditControlButton,
|
||||
cutPolygon: this.editPolygons && !this.options.hideEditControlButton,
|
||||
editMode: (this.editPolygons || this.editCircle) && !this.options.hideEditControlButton,
|
||||
cutPolygon: false,
|
||||
removalMode: !this.options.hideRemoveControlButton,
|
||||
rotateMode: this.editPolygons && !this.options.hideEditControlButton
|
||||
});
|
||||
@ -286,25 +352,39 @@ export default abstract class LeafletMap {
|
||||
|
||||
if (this.options.initDragMode) {
|
||||
this.map.pm.enableGlobalDragMode();
|
||||
this.ignoreUpdateBounds = true;
|
||||
}
|
||||
|
||||
this.map.on('pm:globaldrawmodetoggled', (e) => this.ignoreUpdateBounds = e.enabled);
|
||||
this.map.on('pm:globaleditmodetoggled', (e) => this.ignoreUpdateBounds = e.enabled);
|
||||
this.map.on('pm:globaldragmodetoggled', (e) => this.ignoreUpdateBounds = e.enabled);
|
||||
this.map.on('pm:globalremovalmodetoggled', (e) => this.ignoreUpdateBounds = e.enabled);
|
||||
this.map.on('pm:globalcutmodetoggled', (e) => this.ignoreUpdateBounds = e.enabled);
|
||||
this.map.on('pm:globalrotatemodetoggled', (e) => this.ignoreUpdateBounds = e.enabled);
|
||||
|
||||
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') {
|
||||
let coordinates;
|
||||
if (e.shape === 'tbRectangle') {
|
||||
switch (e.shape) {
|
||||
case 'tbMarker':
|
||||
// @ts-ignore
|
||||
const bounds: L.LatLngBounds = e.layer.getBounds();
|
||||
coordinates = [bounds.getNorthWest(), bounds.getSouthEast()];
|
||||
} else {
|
||||
this.saveLocation(this.selectedEntity, this.convertToCustomFormat(e.layer.getLatLng())).subscribe(() => {});
|
||||
break;
|
||||
case 'tbRectangle':
|
||||
case 'tbPolygon':
|
||||
let coordinates;
|
||||
if (e.shape === 'tbRectangle') {
|
||||
// @ts-ignore
|
||||
const bounds: L.LatLngBounds = e.layer.getBounds();
|
||||
coordinates = [bounds.getNorthWest(), bounds.getSouthEast()];
|
||||
} else {
|
||||
// @ts-ignore
|
||||
coordinates = e.layer.getLatLngs()[0];
|
||||
}
|
||||
this.saveLocation(this.selectedEntity, this.convertPolygonToCustomFormat(coordinates)).subscribe(() => {});
|
||||
break;
|
||||
case 'tbCircle':
|
||||
// @ts-ignore
|
||||
coordinates = e.layer.getLatLngs()[0];
|
||||
}
|
||||
this.saveLocation(this.selectedEntity, this.convertPolygonToCustomFormat(coordinates)).subscribe(() => {
|
||||
});
|
||||
this.saveLocation(this.selectedEntity, this.convertCircleToCustomFormat(e.layer.getLatLng(), e.layer.getRadius()))
|
||||
.subscribe(() => {});
|
||||
}
|
||||
// @ts-ignore
|
||||
e.layer._pmTempLayer = true;
|
||||
@ -342,6 +422,13 @@ export default abstract class LeafletMap {
|
||||
result = iterator.next();
|
||||
}
|
||||
this.saveLocation(result.value.data, this.convertPolygonToCustomFormat(null)).subscribe(() => {});
|
||||
} else if (e.shape === 'Circle') {
|
||||
const iterator = this.circles.values();
|
||||
let result = iterator.next();
|
||||
while (!result.done && e.layer !== result.value.leafletCircle) {
|
||||
result = iterator.next();
|
||||
}
|
||||
this.saveLocation(result.value.data, this.convertCircleToCustomFormat(null, 0)).subscribe(() => {});
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -386,7 +473,7 @@ export default abstract class LeafletMap {
|
||||
if (this.options.disableScrollZooming) {
|
||||
this.map.scrollWheelZoom.disable();
|
||||
}
|
||||
if (this.options.draggableMarker || this.editPolygons) {
|
||||
if (this.options.draggableMarker || this.editPolygons || this.editCircle) {
|
||||
map.pm.setGlobalOptions({ snappable: false } as L.PM.GlobalOptions);
|
||||
map.pm.applyGlobalOptions();
|
||||
this.addEditControl();
|
||||
@ -405,6 +492,12 @@ export default abstract class LeafletMap {
|
||||
|
||||
private createdControlButtonTooltip() {
|
||||
import('tooltipster').then(() => {
|
||||
if ($.tooltipster) {
|
||||
this.tooltipInstances.forEach((instance) => {
|
||||
instance.destroy();
|
||||
});
|
||||
this.tooltipInstances = [];
|
||||
}
|
||||
$(this.ctx.$container)
|
||||
.find('a[role="button"]:not(.leaflet-pm-action)')
|
||||
.each((index, element) => {
|
||||
@ -416,7 +509,7 @@ export default abstract class LeafletMap {
|
||||
title = element.title;
|
||||
$(element).removeAttr('title');
|
||||
}
|
||||
$(element).tooltipster(
|
||||
const tooltip = $(element).tooltipster(
|
||||
{
|
||||
content: title,
|
||||
theme: 'tooltipster-shadow',
|
||||
@ -432,6 +525,7 @@ export default abstract class LeafletMap {
|
||||
trackOrigin: true
|
||||
}
|
||||
);
|
||||
this.tooltipInstances.push(tooltip.tooltipster('instance'));
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -559,15 +653,18 @@ export default abstract class LeafletMap {
|
||||
if (showPolygon) {
|
||||
this.updatePolygons(formattedData, false);
|
||||
}
|
||||
if (this.options.showCircle) {
|
||||
this.updateCircle(formattedData, false);
|
||||
}
|
||||
this.updateMarkers(formattedData, false);
|
||||
this.updateBoundsInternal();
|
||||
if (this.options.draggableMarker || this.editPolygons) {
|
||||
let foundEntityWithoutLocation = false;
|
||||
if (this.options.draggableMarker || this.editPolygons || this.editCircle) {
|
||||
let foundEntityWithLocation = false;
|
||||
let foundEntityWithoutPolygon = false;
|
||||
let foundEntityWithPolygon = false;
|
||||
let foundEntityWithCircle = false;
|
||||
|
||||
if (this.options.draggableMarker && !this.options.hideDrawControlButton && !this.options.hideAllControlButton) {
|
||||
let foundEntityWithoutLocation = false;
|
||||
for (const mData of formattedData) {
|
||||
const position = this.convertPosition(mData);
|
||||
if (!position) {
|
||||
@ -587,6 +684,7 @@ export default abstract class LeafletMap {
|
||||
}
|
||||
|
||||
if (this.editPolygons && !this.options.hideDrawControlButton && !this.options.hideAllControlButton) {
|
||||
let foundEntityWithoutPolygon = false;
|
||||
for (const pData of formattedData) {
|
||||
const isValidPolygon = this.isValidPolygonPosition(pData);
|
||||
if (!isValidPolygon) {
|
||||
@ -605,22 +703,47 @@ export default abstract class LeafletMap {
|
||||
}
|
||||
this.datasources = formattedData;
|
||||
}
|
||||
|
||||
if (this.editCircle && !this.options.hideDrawControlButton && !this.options.hideAllControlButton) {
|
||||
let foundEntityWithoutCircle = false;
|
||||
for (const cData of formattedData) {
|
||||
const isValidCircle = this.isValidCircle(cData);
|
||||
if (!isValidCircle) {
|
||||
foundEntityWithoutCircle = true;
|
||||
} else if (isValidCircle) {
|
||||
foundEntityWithCircle = true;
|
||||
}
|
||||
if (foundEntityWithoutCircle && foundEntityWithCircle) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// @ts-ignore
|
||||
if (this.map.pm.Toolbar.getButtons().tbCircle.disable !== foundEntityWithoutCircle) {
|
||||
this.map.pm.Toolbar.setButtonDisabled('tbCircle', !foundEntityWithoutCircle);
|
||||
}
|
||||
this.datasources = formattedData;
|
||||
}
|
||||
|
||||
if (!this.options.hideRemoveControlButton && !this.options.hideAllControlButton) {
|
||||
const disabledButton = !foundEntityWithLocation && !foundEntityWithPolygon;
|
||||
const disabledButton = !foundEntityWithLocation && !foundEntityWithPolygon && !foundEntityWithCircle;
|
||||
if (disabledButton && this.map.pm.globalRemovalModeEnabled()) {
|
||||
this.map.pm.toggleGlobalRemovalMode();
|
||||
}
|
||||
this.map.pm.Toolbar.setButtonDisabled('removalMode', disabledButton);
|
||||
}
|
||||
if (!this.options.hideEditControlButton && !this.options.hideAllControlButton) {
|
||||
const disabledButton = !foundEntityWithLocation && !foundEntityWithPolygon;
|
||||
const disabledButton = !foundEntityWithLocation && !foundEntityWithPolygon && !foundEntityWithCircle;
|
||||
// @ts-ignore
|
||||
if (this.map.pm.Toolbar.getButtons().dragMode.disable !== disabledButton) {
|
||||
this.map.pm.Toolbar.setButtonDisabled('dragMode', disabledButton);
|
||||
const foundEntityWithPoly = foundEntityWithPolygon || foundEntityWithCircle;
|
||||
// @ts-ignore
|
||||
if (this.editPolygons && this.map.pm.Toolbar.getButtons().editMode.disable !== foundEntityWithPolygon) {
|
||||
this.map.pm.Toolbar.setButtonDisabled('editMode', !foundEntityWithPolygon);
|
||||
this.map.pm.Toolbar.setButtonDisabled('cutPolygon', !foundEntityWithPolygon);
|
||||
if ((this.editPolygons || this.editCircle) && this.map.pm.Toolbar.getButtons().editMode.disable !== foundEntityWithPoly) {
|
||||
this.map.pm.Toolbar.setButtonDisabled('editMode', !foundEntityWithPoly);
|
||||
}
|
||||
// @ts-ignore
|
||||
if (this.editPolygons && this.map.pm.Toolbar.getButtons().tbCut.disable !== foundEntityWithPolygon) {
|
||||
this.map.pm.Toolbar.setButtonDisabled('tbCut', !foundEntityWithPolygon);
|
||||
this.map.pm.Toolbar.setButtonDisabled('rotateMode', !foundEntityWithPolygon);
|
||||
}
|
||||
}
|
||||
@ -643,6 +766,11 @@ export default abstract class LeafletMap {
|
||||
bounds.extend(polygon.leafletPoly.getBounds());
|
||||
});
|
||||
}
|
||||
if (this.options.showCircle) {
|
||||
this.circles.forEach((polygon) => {
|
||||
bounds.extend(polygon.leafletCircle.getBounds());
|
||||
});
|
||||
}
|
||||
if ((this.options as MarkerSettings).useClusterMarkers && this.markersCluster.getBounds().isValid()) {
|
||||
bounds.extend(this.markersCluster.getBounds());
|
||||
} else {
|
||||
@ -655,7 +783,9 @@ export default abstract class LeafletMap {
|
||||
if (bounds.isValid() && (!this.bounds || !this.bounds.isValid() || !this.bounds.equals(bounds)
|
||||
&& this.options.fitMapBounds ? !mapBounds.contains(bounds) : false)) {
|
||||
this.bounds = bounds;
|
||||
this.fitBounds(bounds);
|
||||
if (!this.ignoreUpdateBounds) {
|
||||
this.fitBounds(bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -797,7 +927,8 @@ export default abstract class LeafletMap {
|
||||
if (!this.options.pointTooltipOnRightPanel) {
|
||||
point.on('click', () => getTooltip(pdata, dsData));
|
||||
} else {
|
||||
createTooltip(point, this.options, pdata.$datasource, getTooltip(pdata, dsData));
|
||||
createTooltip(point, this.options, pdata.$datasource, this.options.autocloseTooltip,
|
||||
this.options.showTooltipAction, getTooltip(pdata, dsData));
|
||||
}
|
||||
this.points.addLayer(point);
|
||||
}
|
||||
@ -868,7 +999,8 @@ export default abstract class LeafletMap {
|
||||
// Polygon
|
||||
|
||||
isValidPolygonPosition(data: FormattedData): boolean {
|
||||
return data && isDefinedAndNotNull(data[this.options.polygonKeyName]) && !isEmptyStr(data[this.options.polygonKeyName]);
|
||||
return data && ((isNotEmptyStr(data[this.options.polygonKeyName]) && !isJSON(data[this.options.polygonKeyName])
|
||||
|| Array.isArray(data[this.options.polygonKeyName])));
|
||||
}
|
||||
|
||||
updatePolygons(polyData: FormattedData[], updateBounds = true) {
|
||||
@ -908,7 +1040,7 @@ export default abstract class LeafletMap {
|
||||
if (coordinates.length === 1) {
|
||||
coordinates = coordinates[0];
|
||||
}
|
||||
if (e.shape === 'Rectangle' && coordinates.length === 1) {
|
||||
if (e.shape === 'Rectangle' && !isCutPolygon(coordinates)) {
|
||||
// @ts-ignore
|
||||
const bounds: L.LatLngBounds = e.layer.getBounds();
|
||||
const boundsArray = [bounds.getNorthWest(), bounds.getNorthEast(), bounds.getSouthWest(), bounds.getSouthEast()];
|
||||
@ -951,5 +1083,88 @@ export default abstract class LeafletMap {
|
||||
this.map.remove();
|
||||
this.map = null;
|
||||
}
|
||||
this.tooltipInstances.forEach((instance) => {
|
||||
instance.destroy();
|
||||
});
|
||||
}
|
||||
|
||||
// Circle
|
||||
isValidCircle(data: FormattedData): boolean {
|
||||
return data && isNotEmptyStr(data[this.options.circleKeyName]) && isJSON(data[this.options.circleKeyName]);
|
||||
}
|
||||
|
||||
convertCircleToCustomFormat(expression: L.LatLng, radius: number): {[key: string]: CircleData} {
|
||||
let circleDara: CircleData = null;
|
||||
if (expression) {
|
||||
const position = checkLngLat(expression, this.southWest, this.northEast);
|
||||
circleDara = {
|
||||
latitude: position.lat,
|
||||
longitude: position.lng,
|
||||
radius
|
||||
};
|
||||
}
|
||||
return {
|
||||
[this.options.circleKeyName]: circleDara
|
||||
};
|
||||
}
|
||||
|
||||
convertToCircleFormat(circle: CircleData): CircleData {
|
||||
const centerPoint = checkLngLat(new L.LatLng(circle.latitude, circle.longitude), this.southWest, this.northEast);
|
||||
circle.latitude = centerPoint.lat;
|
||||
circle.longitude = centerPoint.lng;
|
||||
return circle;
|
||||
}
|
||||
|
||||
dragCircleVertex = (e?, data = {} as FormattedData) => {
|
||||
if (e === undefined) {
|
||||
return;
|
||||
}
|
||||
const center = e.layer.getLatLng();
|
||||
const radius = e.layer.getRadius();
|
||||
this.saveLocation(data, this.convertCircleToCustomFormat(center, radius)).subscribe(() => {});
|
||||
}
|
||||
|
||||
updateCircle(circlesData: FormattedData[], updateBounds = true) {
|
||||
const toDelete = new Set(Array.from(this.circles.keys()));
|
||||
const rawCircles = circlesData.filter(cdata => this.isValidCircle(cdata));
|
||||
rawCircles.forEach(data => {
|
||||
if (this.circles.get(data.entityName)) {
|
||||
this.updatedCircle(data, circlesData, updateBounds);
|
||||
} else {
|
||||
this.createdCircle(data, circlesData, updateBounds);
|
||||
}
|
||||
toDelete.delete(data.entityName);
|
||||
});
|
||||
toDelete.forEach((key) => {
|
||||
this.removeCircle(key);
|
||||
});
|
||||
this.circleData = circlesData;
|
||||
}
|
||||
|
||||
updatedCircle(data: FormattedData, dataSources: FormattedData[], updateBounds = true) {
|
||||
const circle = this.circles.get(data.entityName);
|
||||
const oldBounds = circle.leafletCircle.getBounds();
|
||||
circle.updateCircle(data, dataSources);
|
||||
const newBounds = circle.leafletCircle.getBounds();
|
||||
if (updateBounds && oldBounds.toBBoxString() !== newBounds.toBBoxString()) {
|
||||
this.fitBounds(newBounds);
|
||||
}
|
||||
}
|
||||
|
||||
createdCircle(data: FormattedData, dataSources: FormattedData[], updateBounds = true) {
|
||||
const circle = new Circle(this, data, dataSources, this.options, this.dragCircleVertex);
|
||||
if (updateBounds) {
|
||||
const bounds = circle.leafletCircle.getBounds();
|
||||
this.fitBounds(bounds);
|
||||
}
|
||||
this.circles.set(data.entityName, circle);
|
||||
}
|
||||
|
||||
removeCircle(name: string) {
|
||||
const circle = this.circles.get(name);
|
||||
if (circle) {
|
||||
this.map.removeLayer(circle.leafletCircle);
|
||||
this.circles.delete(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,8 +153,8 @@ export type PolygonSettings = {
|
||||
polygonLabelText: string;
|
||||
usePolygonLabelFunction: boolean;
|
||||
showPolygonTooltip: boolean;
|
||||
autocloseTooltip: boolean;
|
||||
showTooltipAction: string;
|
||||
autoClosePolygonTooltip: boolean;
|
||||
showPolygonTooltipAction: string;
|
||||
tooltipAction: { [name: string]: actionsHandler };
|
||||
polygonTooltipPattern: string;
|
||||
usePolygonTooltipFunction: boolean;
|
||||
@ -168,6 +168,32 @@ export type PolygonSettings = {
|
||||
editablePolygon: boolean;
|
||||
};
|
||||
|
||||
export interface CircleSettings {
|
||||
showCircle: boolean;
|
||||
circleKeyName: string;
|
||||
editableCircle: boolean;
|
||||
showCircleLabel: boolean;
|
||||
useCircleLabelFunction: boolean;
|
||||
circleLabel: string;
|
||||
circleLabelFunction?: GenericFunction;
|
||||
circleFillColor: string;
|
||||
useCircleFillColorFunction: boolean;
|
||||
circleFillColorFunction?: GenericFunction;
|
||||
circleFillColorOpacity: number;
|
||||
circleStrokeColor: string;
|
||||
useCircleStrokeColorFunction: boolean;
|
||||
circleStrokeColorFunction: GenericFunction;
|
||||
circleStrokeOpacity: number;
|
||||
circleStrokeWeight: number;
|
||||
showCircleTooltip: boolean;
|
||||
showCircleTooltipAction: string;
|
||||
autoCloseCircleTooltip: boolean;
|
||||
useCircleTooltipFunction: boolean;
|
||||
circleTooltipPattern: string;
|
||||
circleTooltipFunction?: GenericFunction;
|
||||
circleClick?: { [name: string]: actionsHandler };
|
||||
}
|
||||
|
||||
export type PolylineSettings = {
|
||||
usePolylineDecorator: any;
|
||||
autocloseTooltip: boolean;
|
||||
@ -241,9 +267,10 @@ export interface TripAnimationSettings extends PolygonSettings {
|
||||
|
||||
export type actionsHandler = ($event: Event, datasource: Datasource) => void;
|
||||
|
||||
export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolylineSettings & TripAnimationSettings & EditorSettings;
|
||||
export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolylineSettings
|
||||
& CircleSettings & TripAnimationSettings & EditorSettings;
|
||||
|
||||
export const defaultSettings: any = {
|
||||
export const defaultSettings: Partial<UnitedMapSettings> = {
|
||||
xPosKeyName: 'xPos',
|
||||
yPosKeyName: 'yPos',
|
||||
markerOffsetX: 0.5,
|
||||
@ -252,7 +279,7 @@ export const defaultSettings: any = {
|
||||
tooltipOffsetY: -1,
|
||||
latKeyName: 'latitude',
|
||||
lngKeyName: 'longitude',
|
||||
polygonKeyName: 'coordinates',
|
||||
polygonKeyName: 'perimeter',
|
||||
showLabel: false,
|
||||
label: '${entityName}',
|
||||
showTooltip: false,
|
||||
@ -263,17 +290,18 @@ export const defaultSettings: any = {
|
||||
labelColor: '#000000',
|
||||
color: '#FE7569',
|
||||
showPolygonLabel: false,
|
||||
polygonColor: '#0000ff',
|
||||
polygonStrokeColor: '#fe0001',
|
||||
polygonColor: '#3388ff',
|
||||
polygonStrokeColor: '#3388ff',
|
||||
polygonLabelColor: '#000000',
|
||||
polygonOpacity: 0.5,
|
||||
polygonOpacity: 0.2,
|
||||
polygonStrokeOpacity: 1,
|
||||
polygonStrokeWeight: 1,
|
||||
polygonStrokeWeight: 3,
|
||||
showPolygonTooltipAction: 'click',
|
||||
autoClosePolygonTooltip: true,
|
||||
useLabelFunction: false,
|
||||
markerImages: [],
|
||||
strokeWeight: 2,
|
||||
strokeOpacity: 1.0,
|
||||
initCallback: () => { },
|
||||
disableScrollZooming: false,
|
||||
minZoomLevel: 16,
|
||||
credentials: '',
|
||||
@ -287,9 +315,34 @@ export const defaultSettings: any = {
|
||||
hideAllControlButton: false,
|
||||
hideDrawControlButton: false,
|
||||
hideEditControlButton: false,
|
||||
hideRemoveControlButton: false
|
||||
hideRemoveControlButton: false,
|
||||
showCircle: true,
|
||||
circleKeyName: 'perimeter',
|
||||
editableCircle: false,
|
||||
showCircleLabel: false,
|
||||
useCircleLabelFunction: false,
|
||||
circleLabel: '${entityName}',
|
||||
circleFillColor: '#3388ff',
|
||||
useCircleFillColorFunction: false,
|
||||
circleFillColorOpacity: 0.2,
|
||||
circleStrokeColor: '#3388ff',
|
||||
useCircleStrokeColorFunction: false,
|
||||
circleStrokeOpacity: 1,
|
||||
circleStrokeWeight: 3,
|
||||
showCircleTooltip: false,
|
||||
showCircleTooltipAction: 'click',
|
||||
autoCloseCircleTooltip: true,
|
||||
useCircleTooltipFunction: false
|
||||
};
|
||||
|
||||
export interface CircleData {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
radius: number;
|
||||
}
|
||||
|
||||
export const circleDataKeys: Array<keyof CircleData> = ['latitude', 'longitude', 'radius'];
|
||||
|
||||
export const hereProviders = [
|
||||
'HERE.normalDay',
|
||||
'HERE.normalNight',
|
||||
|
||||
@ -19,6 +19,7 @@ import LeafletMap from './leaflet-map';
|
||||
import {
|
||||
commonMapSettingsSchema,
|
||||
editorSettingSchema,
|
||||
mapCircleSchema,
|
||||
mapPolygonSchema,
|
||||
markerClusteringSettingsSchema,
|
||||
markerClusteringSettingsSchemaLeaflet,
|
||||
@ -63,6 +64,7 @@ export class MapWidgetController implements MapWidgetInterface {
|
||||
this.settings.tooltipAction = this.getDescriptors('tooltipAction');
|
||||
this.settings.markerClick = this.getDescriptors('markerClick');
|
||||
this.settings.polygonClick = this.getDescriptors('polygonClick');
|
||||
this.settings.circleClick = this.getDescriptors('circleClick');
|
||||
|
||||
const MapClass = providerClass[this.provider];
|
||||
if (!MapClass) {
|
||||
@ -107,6 +109,8 @@ export class MapWidgetController implements MapWidgetInterface {
|
||||
addGroupInfo(schema, 'Common Map Settings');
|
||||
addToSchema(schema, addCondition(mapPolygonSchema, 'model.showPolygon === true', ['showPolygon']));
|
||||
addGroupInfo(schema, 'Polygon Settings');
|
||||
addToSchema(schema, addCondition(mapCircleSchema, 'model.showCircle === true', ['showCircle']));
|
||||
addGroupInfo(schema, 'Circle Settings');
|
||||
if (drawRoutes) {
|
||||
addToSchema(schema, routeMapSettingsSchema);
|
||||
addGroupInfo(schema, 'Route Map Settings');
|
||||
@ -132,6 +136,10 @@ export class MapWidgetController implements MapWidgetInterface {
|
||||
name: 'widget-action.polygon-click',
|
||||
multiple: false
|
||||
},
|
||||
circleClick: {
|
||||
name: 'widget-action.circle-click',
|
||||
multiple: false
|
||||
},
|
||||
tooltipAction: {
|
||||
name: 'widget-action.tooltip-tag-action',
|
||||
multiple: true
|
||||
@ -272,6 +280,10 @@ export class MapWidgetController implements MapWidgetInterface {
|
||||
polygonColorFunction: parseFunction(settings.polygonColorFunction, functionParams),
|
||||
polygonStrokeColorFunction: parseFunction(settings.polygonStrokeColorFunction, functionParams),
|
||||
polygonTooltipFunction: parseFunction(settings.polygonTooltipFunction, functionParams),
|
||||
circleLabelFunction: parseFunction(settings.circleLabelFunction, functionParams),
|
||||
circleStrokeColorFunction: parseFunction(settings.circleStrokeColorFunction, functionParams),
|
||||
circleFillColorFunction: parseFunction(settings.circleFillColorFunction, functionParams),
|
||||
circleTooltipFunction: parseFunction(settings.circleTooltipFunction, functionParams),
|
||||
markerImageFunction: parseFunction(settings.markerImageFunction, ['data', 'images', 'dsData', 'dsIndex']),
|
||||
labelColor: this.ctx.widgetConfig.color,
|
||||
polygonLabelColor: this.ctx.widgetConfig.color,
|
||||
@ -305,16 +317,10 @@ export class MapWidgetController implements MapWidgetInterface {
|
||||
}
|
||||
|
||||
destroy() {
|
||||
(this.ctx as any).mapInstance = null;
|
||||
if (this.map) {
|
||||
this.map.remove();
|
||||
}
|
||||
if ($.tooltipster) {
|
||||
const instances = $.tooltipster.instances();
|
||||
instances.forEach((instance) => {
|
||||
instance.destroy();
|
||||
});
|
||||
}
|
||||
(this.ctx as any).mapInstance = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -21,16 +21,22 @@ import { Datasource } from '@app/shared/models/widget.models';
|
||||
export function createTooltip(target: L.Layer,
|
||||
settings: MarkerSettings | PolylineSettings | PolygonSettings,
|
||||
datasource: Datasource,
|
||||
autoClose = false,
|
||||
showTooltipAction = 'click',
|
||||
content?: string | HTMLElement
|
||||
): L.Popup {
|
||||
const popup = L.popup();
|
||||
popup.setContent(content);
|
||||
target.bindPopup(popup, { autoClose: settings.autocloseTooltip, closeOnClick: false });
|
||||
if (settings.showTooltipAction === 'hover') {
|
||||
target.bindPopup(popup, { autoClose, closeOnClick: false });
|
||||
if (showTooltipAction === 'hover') {
|
||||
target.off('click');
|
||||
target.on('mouseover', () => {
|
||||
target.openPopup();
|
||||
});
|
||||
target.on('mousemove', (e) => {
|
||||
// @ts-ignore
|
||||
popup.setLatLng(e.latlng);
|
||||
});
|
||||
target.on('mouseout', () => {
|
||||
target.closePopup();
|
||||
});
|
||||
@ -58,8 +64,17 @@ export function bindPopupActions(popup: L.Popup, settings: MarkerSettings | Poly
|
||||
}
|
||||
|
||||
export function isCutPolygon(data): boolean {
|
||||
if (Array.isArray(data[0]) && Array.isArray(data[0][0])) {
|
||||
if (data.length > 1 && Array.isArray(data[0]) && (Array.isArray(data[0][0]) || data[0][0] instanceof L.LatLng)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isJSON(data: string): boolean {
|
||||
try {
|
||||
const parseData = JSON.parse(data);
|
||||
return !Array.isArray(parseData);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,7 +62,8 @@ export class Marker {
|
||||
this.updateMarkerIcon(settings);
|
||||
|
||||
if (settings.showTooltip) {
|
||||
this.tooltip = createTooltip(this.leafletMarker, settings, data.$datasource);
|
||||
this.tooltip = createTooltip(this.leafletMarker, settings, data.$datasource,
|
||||
settings.autocloseTooltip, settings.showTooltipAction);
|
||||
this.updateMarkerTooltip(data);
|
||||
}
|
||||
|
||||
|
||||
@ -41,7 +41,7 @@ export class Polygon {
|
||||
const polygonColor = this.getPolygonColor(settings);
|
||||
const polygonStrokeColor = this.getPolygonStrokeColor(settings);
|
||||
const polyData = data[this.settings.polygonKeyName];
|
||||
const polyConstructor = isCutPolygon(polyData) || polyData.length > 2 ? L.polygon : L.rectangle;
|
||||
const polyConstructor = isCutPolygon(polyData) || polyData.length !== 2 ? L.polygon : L.rectangle;
|
||||
this.leafletPoly = polyConstructor(polyData, {
|
||||
fill: true,
|
||||
fillColor: polygonColor,
|
||||
@ -53,10 +53,13 @@ export class Polygon {
|
||||
snapIgnore: !settings.snappable
|
||||
}).addTo(this.map);
|
||||
|
||||
this.updateLabel(settings);
|
||||
if (settings.showPolygonLabel) {
|
||||
this.updateLabel(settings);
|
||||
}
|
||||
|
||||
if (settings.showPolygonTooltip) {
|
||||
this.tooltip = createTooltip(this.leafletPoly, settings, data.$datasource);
|
||||
this.tooltip = createTooltip(this.leafletPoly, settings, data.$datasource,
|
||||
settings.autoClosePolygonTooltip, settings.showPolygonTooltipAction);
|
||||
this.updateTooltip(data);
|
||||
}
|
||||
this.createEventListeners();
|
||||
@ -64,6 +67,13 @@ export class Polygon {
|
||||
|
||||
private createEventListeners() {
|
||||
if (this.settings.editablePolygon && this.onDragendListener) {
|
||||
// Change position (call in drag drop mode)
|
||||
this.leafletPoly.on('pm:dragstart', () => this.editing = true);
|
||||
this.leafletPoly.on('pm:dragend', () => this.editing = false);
|
||||
// Rotate (call in rotate mode)
|
||||
this.leafletPoly.on('pm:rotatestart', () => this.editing = true);
|
||||
this.leafletPoly.on('pm:rotateend', () => this.editing = false);
|
||||
// Change size/point (call in edit mode)
|
||||
this.leafletPoly.on('pm:markerdragstart', () => this.editing = true);
|
||||
this.leafletPoly.on('pm:markerdragend', () => this.editing = false);
|
||||
this.leafletPoly.on('pm:edit', (e) => this.onDragendListener(e, this.data));
|
||||
@ -96,8 +106,8 @@ export class Polygon {
|
||||
this.map.polygonLabelText = parseWithTranslation.prepareProcessPattern(pattern, true);
|
||||
this.map.replaceInfoLabelPolygon = processPattern(this.map.polygonLabelText, this.data);
|
||||
}
|
||||
settings.polygonLabelText = fillPattern(this.map.polygonLabelText, this.map.replaceInfoLabelPolygon, this.data);
|
||||
this.leafletPoly.bindTooltip(`<div style="color: ${settings.polygonLabelColor};"><b>${settings.polygonLabelText}</b></div>`,
|
||||
const polygonLabelText = fillPattern(this.map.polygonLabelText, this.map.replaceInfoLabelPolygon, this.data);
|
||||
this.leafletPoly.bindTooltip(`<div style="color: ${settings.polygonLabelColor};"><b>${polygonLabelText}</b></div>`,
|
||||
{ className: 'tb-polygon-label', permanent: true, sticky: true, direction: 'center' })
|
||||
.openTooltip(this.leafletPoly.getBounds().getCenter());
|
||||
}
|
||||
@ -110,7 +120,7 @@ export class Polygon {
|
||||
this.data = data;
|
||||
this.dataSources = dataSources;
|
||||
const polyData = data[this.settings.polygonKeyName];
|
||||
if (isCutPolygon(polyData) || polyData.length > 2) {
|
||||
if (isCutPolygon(polyData) || polyData.length !== 2) {
|
||||
if (this.leafletPoly instanceof L.Rectangle) {
|
||||
this.map.removeLayer(this.leafletPoly);
|
||||
const polygonColor = this.getPolygonColor(settings);
|
||||
@ -124,6 +134,11 @@ export class Polygon {
|
||||
opacity: settings.polygonStrokeOpacity,
|
||||
pmIgnore: !settings.editablePolygon
|
||||
}).addTo(this.map);
|
||||
if (settings.showPolygonTooltip) {
|
||||
this.tooltip = createTooltip(this.leafletPoly, settings, data.$datasource,
|
||||
settings.autoClosePolygonTooltip, settings.showPolygonTooltipAction);
|
||||
}
|
||||
this.createEventListeners();
|
||||
} else {
|
||||
this.leafletPoly.setLatLngs(polyData);
|
||||
}
|
||||
@ -149,12 +164,8 @@ export class Polygon {
|
||||
const polygonColor = this.getPolygonColor(settings);
|
||||
const polygonStrokeColor = this.getPolygonStrokeColor(settings);
|
||||
const style: L.PathOptions = {
|
||||
fill: true,
|
||||
fillColor: polygonColor,
|
||||
color: polygonStrokeColor,
|
||||
weight: settings.polygonStrokeWeight,
|
||||
fillOpacity: settings.polygonOpacity,
|
||||
opacity: settings.polygonStrokeOpacity
|
||||
color: polygonStrokeColor
|
||||
};
|
||||
this.leafletPoly.setStyle(style);
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
|
||||
import L, { LatLngBounds, LatLngLiteral, LatLngTuple } from 'leaflet';
|
||||
import LeafletMap from '../leaflet-map';
|
||||
import { MapImage, PosFuncton, UnitedMapSettings } from '../map-models';
|
||||
import { CircleData, MapImage, PosFuncton, UnitedMapSettings } from '../map-models';
|
||||
import { Observable, ReplaySubject } from 'rxjs';
|
||||
import { filter, map, mergeMap } from 'rxjs/operators';
|
||||
import {
|
||||
@ -201,18 +201,11 @@ export class ImageMap extends LeafletMap {
|
||||
this.map.invalidateSize(false);
|
||||
(this.map as any)._enforcingBounds = false;
|
||||
this.updateMarkers(this.markersData);
|
||||
if (this.options.draggableMarker && this.addMarkers.length) {
|
||||
this.addMarkers.forEach((marker) => {
|
||||
const prevPoint = this.convertToCustomFormat(marker.getLatLng(), null, prevWidth, prevHeight);
|
||||
marker.setLatLng(this.convertPosition(prevPoint));
|
||||
});
|
||||
if (this.options.showPolygon) {
|
||||
this.updatePolygons(this.polygonsData);
|
||||
}
|
||||
this.updatePolygons(this.polygonsData);
|
||||
if (this.options.showPolygon && this.options.editablePolygon && this.addPolygons.length) {
|
||||
this.addPolygons.forEach((polygon) => {
|
||||
const prevPolygonPoint = this.convertToPolygonFormat(polygon.getLatLngs(), prevWidth, prevHeight);
|
||||
polygon.setLatLngs(this.convertPositionPolygon(prevPolygonPoint));
|
||||
});
|
||||
if (this.options.showCircle) {
|
||||
this.updateCircle(this.circleData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -323,4 +316,31 @@ export class ImageMap extends LeafletMap {
|
||||
[this.options.polygonKeyName]: coordinate
|
||||
};
|
||||
}
|
||||
|
||||
convertCircleToCustomFormat(expression: L.LatLng, radius: number, width = this.width,
|
||||
height = this.height): {[key: string]: CircleData} {
|
||||
let circleDara: CircleData = null;
|
||||
if (expression) {
|
||||
const point = this.latLngToPoint(expression);
|
||||
const customX = calculateNewPointCoordinate(point.x, width);
|
||||
const customY = calculateNewPointCoordinate(point.y, height);
|
||||
const customRadius = calculateNewPointCoordinate(radius, width);
|
||||
circleDara = {
|
||||
latitude: customX,
|
||||
longitude: customY,
|
||||
radius: customRadius
|
||||
};
|
||||
}
|
||||
return {
|
||||
[this.options.circleKeyName]: circleDara
|
||||
};
|
||||
}
|
||||
|
||||
convertToCircleFormat(circle: CircleData, width = this.width, height = this.height): CircleData {
|
||||
const centerPoint = this.pointToLatLng(circle.longitude * width, circle.latitude * height);
|
||||
circle.latitude = centerPoint.lat;
|
||||
circle.longitude = centerPoint.lng;
|
||||
circle.radius = circle.radius * width;
|
||||
return circle;
|
||||
}
|
||||
}
|
||||
|
||||
@ -567,7 +567,7 @@ export const mapPolygonSchema =
|
||||
polygonKeyName: {
|
||||
title: 'Polygon key name',
|
||||
type: 'string',
|
||||
default: 'coordinates'
|
||||
default: 'perimeter'
|
||||
},
|
||||
editablePolygon: {
|
||||
title: 'Enable polygon edit',
|
||||
@ -600,7 +600,7 @@ export const mapPolygonSchema =
|
||||
polygonOpacity: {
|
||||
title: 'Polygon opacity',
|
||||
type: 'number',
|
||||
default: 0.5
|
||||
default: 0.2
|
||||
},
|
||||
polygonStrokeColor: {
|
||||
title: 'Stroke color',
|
||||
@ -614,13 +614,23 @@ export const mapPolygonSchema =
|
||||
polygonStrokeWeight: {
|
||||
title: 'Stroke weight',
|
||||
type: 'number',
|
||||
default: 1
|
||||
default: 3
|
||||
},
|
||||
showPolygonTooltip: {
|
||||
title: 'Show polygon tooltip',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
showPolygonTooltipAction: {
|
||||
title: 'Action for displaying polygon tooltip',
|
||||
type: 'string',
|
||||
default: 'click'
|
||||
},
|
||||
autoClosePolygonTooltip: {
|
||||
title: 'Auto-close polygon tooltips',
|
||||
type: 'boolean',
|
||||
default: true
|
||||
},
|
||||
polygonTooltipPattern: {
|
||||
title: 'Tooltip (for ex. \'Text ${keyName} units.\' or <link-act name=\'my-action\'>Link text</link-act>\')',
|
||||
type: 'string',
|
||||
@ -701,6 +711,26 @@ export const mapPolygonSchema =
|
||||
'polygonStrokeOpacity',
|
||||
'polygonStrokeWeight',
|
||||
'showPolygonTooltip',
|
||||
{
|
||||
key: 'showPolygonTooltipAction',
|
||||
type: 'rc-select',
|
||||
multiple: false,
|
||||
items: [
|
||||
{
|
||||
value: 'click',
|
||||
label: 'Show tooltip on click (Default)'
|
||||
},
|
||||
{
|
||||
value: 'hover',
|
||||
label: 'Show tooltip on hover'
|
||||
}
|
||||
],
|
||||
condition: 'model.showPolygonTooltip === true'
|
||||
},
|
||||
{
|
||||
key: 'autoClosePolygonTooltip',
|
||||
condition: 'model.showPolygonTooltip === true'
|
||||
},
|
||||
{
|
||||
key: 'usePolygonTooltipFunction',
|
||||
condition: 'model.showPolygonTooltip === true'
|
||||
@ -1375,3 +1405,199 @@ export const editorSettingSchema =
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const mapCircleSchema =
|
||||
{
|
||||
schema: {
|
||||
title: 'Map Circle Configuration',
|
||||
type: 'object',
|
||||
properties: {
|
||||
showCircle: {
|
||||
title: 'Show circle',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
circleKeyName: {
|
||||
title: 'Circle key name',
|
||||
type: 'string',
|
||||
default: 'perimeter'
|
||||
},
|
||||
editableCircle: {
|
||||
title: 'Enable circle edit',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
showCircleLabel: {
|
||||
title: 'Show circle label',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
circleLabel: {
|
||||
title: 'Circle label (pattern examples: \'${entityName}\', \'${entityName}: (Text ${keyName} units.)\' )',
|
||||
type: 'string',
|
||||
default: '${entityName}'
|
||||
},
|
||||
useCircleLabelFunction: {
|
||||
title: 'Use circle label function',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
circleLabelFunction: {
|
||||
title: 'Circle label function: f(data, dsData, dsIndex)',
|
||||
type: 'string'
|
||||
},
|
||||
circleFillColor: {
|
||||
title: 'Circle fill color',
|
||||
type: 'string'
|
||||
},
|
||||
useCircleFillColorFunction: {
|
||||
title: 'Use circle fill color function',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
circleFillColorFunction: {
|
||||
title: 'Circle fill color function: f(data, dsData, dsIndex)',
|
||||
type: 'string'
|
||||
},
|
||||
circleFillColorOpacity: {
|
||||
title: 'Circle fill color opacity',
|
||||
type: 'number',
|
||||
default: 0.2
|
||||
},
|
||||
circleStrokeColor: {
|
||||
title: 'Circle stroke color',
|
||||
type: 'string'
|
||||
},
|
||||
useCircleStrokeColorFunction: {
|
||||
title: 'Use circle stroke color function',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
circleStrokeColorFunction: {
|
||||
title: 'Circle stroke Color function: f(data, dsData, dsIndex)',
|
||||
type: 'string'
|
||||
},
|
||||
circleStrokeOpacity: {
|
||||
title: 'Circle stroke opacity',
|
||||
type: 'number',
|
||||
default: 1
|
||||
},
|
||||
circleStrokeWeight: {
|
||||
title: 'Circle stroke weight',
|
||||
type: 'number',
|
||||
default: 3
|
||||
},
|
||||
showCircleTooltip: {
|
||||
title: 'Show circle tooltip',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
showCircleTooltipAction: {
|
||||
title: 'Action for displaying circle tooltip',
|
||||
type: 'string',
|
||||
default: 'click'
|
||||
},
|
||||
autoCloseCircleTooltip: {
|
||||
title: 'Auto-close circle tooltips',
|
||||
type: 'boolean',
|
||||
default: true
|
||||
},
|
||||
circleTooltipPattern: {
|
||||
title: 'Tooltip (for ex. \'Text ${keyName} units.\' or <link-act name=\'my-action\'>Link text</link-act>\')',
|
||||
type: 'string',
|
||||
default: '<b>${entityName}</b><br/><br/><b>Temperatur:</b> ${temp:1}'
|
||||
},
|
||||
useCircleTooltipFunction: {
|
||||
title: 'Use circle tooltip function',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
circleTooltipFunction: {
|
||||
title: 'Circle tooltip function: f(data, dsData, dsIndex)',
|
||||
type: 'string'
|
||||
}
|
||||
},
|
||||
required: []
|
||||
},
|
||||
form: [
|
||||
'showCircle',
|
||||
'circleKeyName',
|
||||
'editableCircle',
|
||||
'showCircleLabel',
|
||||
{
|
||||
key: 'useCircleLabelFunction',
|
||||
condition: 'model.showCircleLabel === true'
|
||||
},
|
||||
{
|
||||
key: 'circleLabel',
|
||||
condition: 'model.showCircleLabel === true && model.useCircleLabelFunction !== true'
|
||||
},
|
||||
{
|
||||
key: 'circleLabelFunction',
|
||||
type: 'javascript',
|
||||
helpId: 'widget/lib/map/label_fn',
|
||||
condition: 'model.showCircleLabel === true && model.useCircleLabelFunction === true'
|
||||
},
|
||||
{
|
||||
key: 'circleFillColor',
|
||||
type: 'color'
|
||||
},
|
||||
'useCircleFillColorFunction',
|
||||
{
|
||||
key: 'circleFillColorFunction',
|
||||
helpId: 'widget/lib/map/polygon_color_fn',
|
||||
type: 'javascript',
|
||||
condition: 'model.useCircleFillColorFunction === true'
|
||||
},
|
||||
'circleFillColorOpacity',
|
||||
{
|
||||
key: 'circleStrokeColor',
|
||||
type: 'color'
|
||||
},
|
||||
'useCircleStrokeColorFunction',
|
||||
{
|
||||
key: 'circleStrokeColorFunction',
|
||||
helpId: 'widget/lib/map/polygon_color_fn',
|
||||
type: 'javascript',
|
||||
condition: 'model.useCircleStrokeColorFunction === true'
|
||||
},
|
||||
'circleStrokeOpacity',
|
||||
'circleStrokeWeight',
|
||||
'showCircleTooltip',
|
||||
{
|
||||
key: 'showCircleTooltipAction',
|
||||
type: 'rc-select',
|
||||
multiple: false,
|
||||
items: [
|
||||
{
|
||||
value: 'click',
|
||||
label: 'Show tooltip on click (Default)'
|
||||
},
|
||||
{
|
||||
value: 'hover',
|
||||
label: 'Show tooltip on hover'
|
||||
}
|
||||
],
|
||||
condition: 'model.showCircleTooltip === true'
|
||||
},
|
||||
{
|
||||
key: 'autoCloseCircleTooltip',
|
||||
condition: 'model.showCircleTooltip === true'
|
||||
},
|
||||
{
|
||||
key: 'useCircleTooltipFunction',
|
||||
condition: 'model.showCircleTooltip === true'
|
||||
},
|
||||
{
|
||||
key: 'circleTooltipPattern',
|
||||
type: 'textarea',
|
||||
condition: 'model.showCircleTooltip === true && model.useCircleTooltipFunction !== true'
|
||||
},
|
||||
{
|
||||
key: 'circleTooltipFunction',
|
||||
helpId: 'widget/lib/map/polygon_tooltip_fn',
|
||||
type: 'javascript',
|
||||
condition: 'model.showCircleTooltip === true && model.useCircleTooltipFunction === true'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@ -3324,12 +3324,15 @@
|
||||
"tooltips": {
|
||||
"placeMarker": "Click to place '{{entityName}}' entity",
|
||||
"firstVertex": "Polygon for '{{entityName}}': click to place first point",
|
||||
"firstVertex-cut": "Click to place first point",
|
||||
"continueLine": "Polygon for '{{entityName}}': click to continue drawing",
|
||||
"continueLine-cut": "Click to continue drawing",
|
||||
"finishLine": "Click any existing marker to finish",
|
||||
"finishPoly": "Polygon for '{{entityName}}': click first marker to finish and save",
|
||||
"finishPoly-cut": "Click first marker to finish and save",
|
||||
"finishRect": "Polygon for '{{entityName}}': click to finish and save",
|
||||
"startCircle": "Click to place circle center",
|
||||
"finishCircle": "Click to finish circle",
|
||||
"startCircle": "Circle for '{{entityName}}': click to place circle center",
|
||||
"finishCircle": "Circle for '{{entityName}}': click to finish circle",
|
||||
"placeCircleMarker": "Click to place circle marker"
|
||||
},
|
||||
"actions": {
|
||||
@ -3364,6 +3367,7 @@
|
||||
"row-click": "On row click",
|
||||
"polygon-click": "On polygon click",
|
||||
"marker-click": "On marker click",
|
||||
"circle-click": "On circle click",
|
||||
"tooltip-tag-action": "Tooltip tag action",
|
||||
"node-selected": "On node selected",
|
||||
"element-click": "On HTML element click",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user