Merge branch 'vvlladd28-feature/map/circle'

This commit is contained in:
Igor Kulikov 2022-02-10 16:43:38 +02:00
commit 6e70e2e2c2
11 changed files with 810 additions and 95 deletions

File diff suppressed because one or more lines are too long

View 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);
}
}

View File

@ -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);
}
}
}

View File

@ -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',

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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'
}
]
};

View File

@ -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",