Browse Source

UI: Map layers drag mode.

pull/12723/head
Igor Kulikov 2 years ago
parent
commit
5d3cafc65d
  1. 57
      ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/circles-data-layer.ts
  2. 114
      ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/map-data-layer.ts
  3. 115
      ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/markers-data-layer.ts
  4. 78
      ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/polygons-data-layer.ts
  5. 63
      ui-ngx/src/app/modules/home/components/widget/lib/maps/geo-map.ts
  6. 79
      ui-ngx/src/app/modules/home/components/widget/lib/maps/image-map.ts
  7. 27
      ui-ngx/src/app/modules/home/components/widget/lib/maps/map.scss
  8. 86
      ui-ngx/src/app/modules/home/components/widget/lib/maps/map.ts
  9. 71
      ui-ngx/src/app/modules/home/components/widget/lib/maps/models/map.models.ts
  10. 16
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.html
  11. 27
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.ts
  12. 15
      ui-ngx/src/assets/locale/locale.constant-en_US.json

57
ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/circles-data-layer.ts

@ -31,6 +31,7 @@ class TbCircleDataLayerItem extends TbDataLayerItem<CirclesDataLayerSettings, Tb
private circle: L.Circle;
private circleStyle: L.PathOptions;
private editing = false;
constructor(data: FormattedData<TbMapDatasource>,
dsData: FormattedData<TbMapDatasource>[],
@ -45,7 +46,8 @@ class TbCircleDataLayerItem extends TbDataLayerItem<CirclesDataLayerSettings, Tb
this.circleStyle = this.dataLayer.getShapeStyle(data, dsData);
this.circle = L.circle(center, {
radius: circleData.radius,
...this.circleStyle
...this.circleStyle,
snapIgnore: !this.dataLayer.isSnappable()
});
this.updateLabel(data, dsData);
return this.circle;
@ -76,7 +78,45 @@ class TbCircleDataLayerItem extends TbDataLayerItem<CirclesDataLayerSettings, Tb
this.updateCircleShape(data);
}
protected addItemClass(clazz: string): void {
if ((this.circle as any)._path) {
L.DomUtil.addClass((this.circle as any)._path, clazz);
}
}
protected removeItemClass(clazz: string): void {
if ((this.circle as any)._path) {
L.DomUtil.removeClass((this.circle as any)._path, clazz);
}
}
protected enableDrag(): void {
this.circle.pm.enableLayerDrag();
this.circle.on('pm:dragstart', () => {
this.editing = true;
});
this.circle.on('pm:dragend', () => {
this.saveCircleCoordinates();
this.editing = false;
});
}
protected disableDrag(): void {
this.circle.pm.disableLayerDrag();
this.circle.off('pm:dragstart');
this.circle.off('pm:dragend');
}
private saveCircleCoordinates() {
const center = this.circle.getLatLng();
const radius = this.circle.getRadius();
this.dataLayer.saveCircleCoordinates(this.data, center, radius);
}
private updateCircleShape(data: FormattedData<TbMapDatasource>) {
if (this.editing) {
return;
}
const circleData = this.dataLayer.extractCircleCoordinates(data);
const center = new L.LatLng(circleData.latitude, circleData.longitude);
if (!this.circle.getLatLng().equals(center)) {
@ -117,13 +157,22 @@ export class TbCirclesDataLayer extends TbShapesDataLayer<CirclesDataLayerSettin
}
protected createLayerItem(data: FormattedData<TbMapDatasource>, dsData: FormattedData<TbMapDatasource>[]): TbDataLayerItem<CirclesDataLayerSettings, TbCirclesDataLayer> {
throw new TbCircleDataLayerItem(data, dsData, this.settings, this);
return new TbCircleDataLayerItem(data, dsData, this.settings, this);
}
public extractCircleCoordinates(data: FormattedData<TbMapDatasource>) {
const circleData: TbCircleData = JSON.parse(data[this.settings.circleKey.label]);
return this.map.convertCircleData(circleData);
return this.map.circleDataToCoordinates(circleData);
}
public saveCircleCoordinates(data: FormattedData<TbMapDatasource>, center: L.LatLng, radius: number): void {
const converted = this.map.coordinatesToCircleData(center, radius);
const circleData = [
{
dataKey: this.settings.circleKey,
value: converted
}
];
this.map.saveItemData(data.$datasource, circleData).subscribe();
}
}

114
ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/map-data-layer.ts

@ -17,6 +17,7 @@
import {
DataLayerColorSettings,
DataLayerColorType,
DataLayerEditAction,
DataLayerPatternSettings,
DataLayerPatternType,
DataLayerTooltipTrigger,
@ -63,6 +64,7 @@ export abstract class TbDataLayerItem<S extends MapDataLayerSettings, D extends
this.createEventListeners(data, dsData);
try {
this.dataLayer.getDataLayerContainer().addLayer(this.layer);
this.editModeUpdated();
} catch (e) {
console.warn(e);
}
@ -80,10 +82,46 @@ export abstract class TbDataLayerItem<S extends MapDataLayerSettings, D extends
protected abstract createEventListeners(data: FormattedData<TbMapDatasource>, dsData: FormattedData<TbMapDatasource>[]): void;
protected abstract addItemClass(clazz: string): void;
protected abstract removeItemClass(clazz: string): void;
protected abstract enableDrag(): void;
protected abstract disableDrag(): void;
protected enableEdit(): void {
if (this.dataLayer.isHoverable()) {
this.addItemClass('tb-hoverable');
}
if (this.dataLayer.isDragEnabled()) {
this.enableDrag();
this.addItemClass('tb-draggable');
}
}
protected disableEdit(): void {
if (this.dataLayer.isHoverable()) {
this.removeItemClass('tb-hoverable');
}
if (this.dataLayer.isDragEnabled()) {
this.disableDrag();
this.removeItemClass('tb-draggable');
}
}
public invalidateCoordinates(): void {
this.doInvalidateCoordinates(this.data, this.dataLayer.getMap().getData());
}
public editModeUpdated() {
if (this.dataLayer.isEditMode()) {
this.enableEdit();
} else {
this.disableEdit();
}
}
public update(data: FormattedData<TbMapDatasource>, dsData: FormattedData<TbMapDatasource>[]): void {
this.data = data;
this.doUpdate(data, dsData);
@ -245,6 +283,17 @@ export abstract class TbMapDataLayer<S extends MapDataLayerSettings, D extends T
protected enabled = true;
protected addEnabled = false;
protected dragEnabled = false;
protected editEnabled = false;
protected removeEnabled = false;
protected selectable = false;
protected hoverable = false;
protected snappable = false;
private editMode = false;
public dataLayerLabelProcessor: DataLayerPatternProcessor;
public dataLayerTooltipProcessor: DataLayerPatternProcessor;
@ -256,10 +305,22 @@ export abstract class TbMapDataLayer<S extends MapDataLayerSettings, D extends T
this.groupsState[group] = true;
});
}
if (this.settings.edit?.enabledActions) {
this.addEnabled = this.settings.edit.enabledActions.includes(DataLayerEditAction.add);
this.dragEnabled = this.settings.edit.enabledActions.includes(DataLayerEditAction.move);
this.editEnabled = this.settings.edit.enabledActions.includes(DataLayerEditAction.edit);
this.removeEnabled = this.settings.edit.enabledActions.includes(DataLayerEditAction.remove);
this.selectable = this.removeEnabled || this.editEnabled;
this.hoverable = this.selectable || this.dragEnabled;
}
this.dataLayerContainer = this.createDataLayerContainer();
this.dataLayerLabelProcessor = this.settings.label.show ? new DataLayerPatternProcessor(this, this.settings.label) : null;
this.dataLayerTooltipProcessor = this.settings.tooltip.show ? new DataLayerPatternProcessor(this, this.settings.tooltip): null;
this.map.getMap().addLayer(this.dataLayerContainer);
this.enableEditMode();
}
public setup(): Observable<any> {
@ -291,6 +352,38 @@ export abstract class TbMapDataLayer<S extends MapDataLayerSettings, D extends T
return this.enabled;
}
public isEditMode(): boolean {
return this.editMode;
}
public isAddEnabled(): boolean {
return this.addEnabled;
}
public isDragEnabled(): boolean {
return this.dragEnabled;
}
public isEditEnabled(): boolean {
return this.editEnabled;
}
public isRemoveEnabled(): boolean {
return this.removeEnabled;
}
public isHoverable(): boolean {
return this.hoverable;
}
public isSelectable(): boolean {
return this.selectable;
}
public isSnappable(): boolean {
return this.snappable;
}
public getGroups(): string[] {
return this.settings.groups || [];
}
@ -303,6 +396,7 @@ export abstract class TbMapDataLayer<S extends MapDataLayerSettings, D extends T
this.enabled = enabled;
if (this.enabled) {
this.map.getMap().addLayer(this.dataLayerContainer);
this.updateItemsEditMode();
} else {
this.map.getMap().removeLayer(this.dataLayerContainer);
}
@ -350,7 +444,7 @@ export abstract class TbMapDataLayer<S extends MapDataLayerSettings, D extends T
}
protected createDataLayerContainer(): L.FeatureGroup {
return L.featureGroup();
return L.featureGroup([], {snapIgnore: !this.snappable});
}
protected setupDatasource(datasource: TbMapDatasource): TbMapDatasource {
@ -364,6 +458,24 @@ export abstract class TbMapDataLayer<S extends MapDataLayerSettings, D extends T
return this.map.type();
}
public enableEditMode() {
if (!this.editMode) {
this.editMode = true;
this.updateItemsEditMode();
}
}
public disableEditMode() {
if (this.editMode) {
this.editMode = false;
this.updateItemsEditMode();
}
}
private updateItemsEditMode() {
this.layerItems.forEach(item => item.editModeUpdated());
}
public abstract dataLayerType(): MapDataLayerType;
protected abstract defaultBaseSettings(map: TbMap<any>): Partial<S>;

115
ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/markers-data-layer.ts

@ -60,9 +60,10 @@ import { TbImageMap } from '@home/components/widget/lib/maps/image-map';
class TbMarkerDataLayerItem extends TbDataLayerItem<MarkersDataLayerSettings, TbMarkersDataLayer, L.Marker> {
private location: L.LatLng;
private marker: L.Marker;
private labelOffset: L.PointTuple;
private iconClassList: string[];
private moving = false;
constructor(data: FormattedData<TbMapDatasource>,
dsData: FormattedData<TbMapDatasource>[],
@ -72,8 +73,9 @@ class TbMarkerDataLayerItem extends TbDataLayerItem<MarkersDataLayerSettings, Tb
}
protected create(data: FormattedData<TbMapDatasource>, dsData: FormattedData<TbMapDatasource>[]): L.Marker {
this.location = this.dataLayer.extractLocation(data, dsData);
this.marker = L.marker(this.location, {
this.iconClassList = [];
const location = this.dataLayer.extractLocation(data, dsData);
this.marker = L.marker(location, {
tbMarkerData: data
});
@ -96,26 +98,69 @@ class TbMarkerDataLayerItem extends TbDataLayerItem<MarkersDataLayerSettings, Tb
protected doUpdate(data: FormattedData<TbMapDatasource>, dsData: FormattedData<TbMapDatasource>[]): void {
this.marker.options.tbMarkerData = data;
this.updateMarkerPosition(data, dsData);
this.updateMarkerLocation(data, dsData);
this.updateTooltip(data, dsData);
this.updateMarkerIcon(data, dsData);
}
protected doInvalidateCoordinates(data: FormattedData<TbMapDatasource>, dsData: FormattedData<TbMapDatasource>[]): void {
this.updateMarkerPosition(data, dsData);
this.updateMarkerLocation(data, dsData);
}
private updateMarkerPosition(data: FormattedData<TbMapDatasource>, dsData: FormattedData<TbMapDatasource>[]) {
const position = this.dataLayer.extractLocation(data, dsData);
if (!this.marker.getLatLng().equals(position)) {
this.location = position;
this.marker.setLatLng(position);
protected addItemClass(clazz: string): void {
if (!this.iconClassList.includes(clazz)) {
this.iconClassList.push(clazz);
this.marker.options.icon.options.className = this.updateIconClasses(this.marker.options.icon.options.className);
if ((this.marker as any)._icon) {
L.DomUtil.addClass((this.marker as any)._icon, clazz);
}
}
}
protected removeItemClass(clazz: string): void {
const index = this.iconClassList.indexOf(clazz);
if (index !== -1) {
this.iconClassList.splice(index, 1);
this.marker.options.icon.options.className = this.updateIconClasses(this.marker.options.icon.options.className);
if ((this.marker as any)._icon) {
L.DomUtil.removeClass((this.marker as any)._icon, clazz);
}
}
}
protected enableDrag(): void {
this.marker.options.draggable = true;
this.marker.on('dragstart', () => {
this.moving = true;
});
this.marker.on('dragend', () => {
this.saveMarkerLocation();
this.moving = false;
});
}
protected disableDrag(): void {
this.marker.options.draggable = false;
this.marker.off('dragstart');
this.marker.off('dragend');
}
private saveMarkerLocation() {
const location = this.marker.getLatLng();
this.dataLayer.saveMarkerLocation(this.data, location);
}
private updateMarkerLocation(data: FormattedData<TbMapDatasource>, dsData: FormattedData<TbMapDatasource>[]) {
const location = this.dataLayer.extractLocation(data, dsData);
if (!this.marker.getLatLng().equals(location) && !this.moving) {
this.marker.setLatLng(location);
}
}
private updateMarkerIcon(data: FormattedData<TbMapDatasource>, dsData: FormattedData<TbMapDatasource>[]) {
this.dataLayer.markerIconProcessor.createMarkerIcon(data, dsData).subscribe(
(iconInfo) => {
iconInfo.icon.options.className = this.updateIconClasses(iconInfo.icon.options.className);
this.marker.setIcon(iconInfo.icon);
const anchor = iconInfo.icon.options.iconAnchor;
if (anchor && Array.isArray(anchor)) {
@ -124,9 +169,23 @@ class TbMarkerDataLayerItem extends TbDataLayerItem<MarkersDataLayerSettings, Tb
this.labelOffset = [0, -iconInfo.size[1] * this.dataLayer.markerOffset[1] + 10];
}
this.updateLabel(data, dsData);
this.editModeUpdated();
}
);
}
private updateIconClasses(className: string): string {
const classes: string[] = [];
if (className?.length) {
classes.push(...className.split(' '));
}
this.iconClassList.forEach(clazz => {
if (!classes.includes(clazz)) {
classes.push(clazz);
}
});
return classes.join(' ');
}
}
abstract class MarkerIconProcessor<S> {
@ -390,7 +449,7 @@ export class TbMarkersDataLayer extends TbMapDataLayer<MarkersDataLayerSettings,
}
protected isValidLayerData(layerData: FormattedData<TbMapDatasource>): boolean {
return !!this.extractPosition(layerData);
return !!this.extractLocationData(layerData);
}
protected createLayerItem(data: FormattedData<TbMapDatasource>, dsData: FormattedData<TbMapDatasource>[]): TbMarkerDataLayerItem {
@ -412,13 +471,7 @@ export class TbMarkersDataLayer extends TbMapDataLayer<MarkersDataLayerSettings,
removeOutsideVisibleBounds: this.settings.markerClustering?.lazyLoad,
animate: this.settings.markerClustering?.zoomAnimation,
chunkedLoading: this.settings.markerClustering?.chunkedLoad,
pmIgnore: true,
spiderLegPolylineOptions: {
pmIgnore: true
},
polygonOptions: {
pmIgnore: true
}
snapIgnore: !this.snappable
};
if (this.settings.markerClustering?.useClusterMarkerColorFunction) {
markerClusterOptions.iconCreateFunction = (cluster) => {
@ -463,7 +516,7 @@ export class TbMarkersDataLayer extends TbMapDataLayer<MarkersDataLayerSettings,
return this.markersClusterContainer;
}
private extractPosition(data: FormattedData<TbMapDatasource>): {x: number; y: number} {
private extractLocationData(data: FormattedData<TbMapDatasource>): {x: number; y: number} {
if (data) {
const xKeyVal = data[this.settings.xKey.label];
const yKeyVal = data[this.settings.yKey.label];
@ -524,15 +577,31 @@ export class TbMarkersDataLayer extends TbMapDataLayer<MarkersDataLayerSettings,
}
public extractLocation(data: FormattedData<TbMapDatasource>, dsData: FormattedData<TbMapDatasource>[]): L.LatLng {
let position = this.extractPosition(data);
if (position) {
let locationData = this.extractLocationData(data);
if (locationData) {
if (this.map.type() === MapType.image && this.positionFunction) {
const imageMap = this.map as TbImageMap;
position = this.positionFunction.execute(position.x, position.y, data, dsData, imageMap.getAspect()) || {x: 0, y: 0};
locationData = this.positionFunction.execute(locationData.x, locationData.y, data, dsData, imageMap.getAspect()) || {x: 0, y: 0};
}
return this.map.positionToLatLng(position);
return this.map.locationDataToLatLng(locationData);
} else {
return null;
}
}
public saveMarkerLocation(data: FormattedData<TbMapDatasource>, position: L.LatLng): void {
const converted = this.map.latLngToLocationData(position);
const locationData = [
{
dataKey: this.settings.xKey,
value: converted.x
},
{
dataKey: this.settings.yKey,
value: converted.y
}
];
this.map.saveItemData(data.$datasource, locationData).subscribe();
}
}

78
ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/polygons-data-layer.ts

@ -18,7 +18,7 @@ import {
defaultBasePolygonsDataLayerSettings,
isCutPolygon, isJSON,
PolygonsDataLayerSettings,
TbMapDatasource
TbMapDatasource, TbPolyData, TbPolygonCoordinates, TbPolygonRawCoordinates
} from '@home/components/widget/lib/maps/models/map.models';
import L from 'leaflet';
import { FormattedData } from '@shared/models/widget.models';
@ -33,6 +33,7 @@ class TbPolygonDataLayerItem extends TbDataLayerItem<PolygonsDataLayerSettings,
private polygonContainer: L.FeatureGroup;
private polygon: L.Polygon;
private polygonStyle: L.PathOptions;
private editing = false;
constructor(data: FormattedData<TbMapDatasource>,
dsData: FormattedData<TbMapDatasource>[],
@ -45,8 +46,9 @@ class TbPolygonDataLayerItem extends TbDataLayerItem<PolygonsDataLayerSettings,
const polyData = this.dataLayer.extractPolygonCoordinates(data);
const polyConstructor = isCutPolygon(polyData) || polyData.length !== 2 ? L.polygon : L.rectangle;
this.polygonStyle = this.dataLayer.getShapeStyle(data, dsData);
this.polygon = polyConstructor(polyData, {
...this.polygonStyle
this.polygon = polyConstructor(polyData as (TbPolygonRawCoordinates & L.LatLngTuple[]), {
...this.polygonStyle,
snapIgnore: !this.dataLayer.isSnappable()
});
this.polygonContainer = L.featureGroup();
@ -81,20 +83,69 @@ class TbPolygonDataLayerItem extends TbDataLayerItem<PolygonsDataLayerSettings,
this.updatePolygonShape(data);
}
protected addItemClass(clazz: string): void {
if ((this.polygon as any)._path) {
L.DomUtil.addClass((this.polygon as any)._path, clazz);
}
}
protected removeItemClass(clazz: string): void {
if ((this.polygon as any)._path) {
L.DomUtil.removeClass((this.polygon as any)._path, clazz);
}
}
protected enableDrag(): void {
this.polygon.pm.enableLayerDrag();
this.polygon.on('pm:dragstart', () => {
this.editing = true;
});
this.polygon.on('pm:dragend', () => {
this.savePolygonCoordinates();
this.editing = false;
});
}
protected disableDrag(): void {
this.polygon.pm.disableLayerDrag();
this.polygon.off('pm:dragstart');
this.polygon.off('pm:dragend');
}
private savePolygonCoordinates() {
let coordinates: TbPolygonCoordinates = this.polygon.getLatLngs();
if (coordinates.length === 1) {
coordinates = coordinates[0] as TbPolygonCoordinates;
}
if (this.polygon instanceof L.Rectangle && !isCutPolygon(coordinates)) {
const bounds = this.polygon.getBounds();
const boundsArray = [bounds.getNorthWest(), bounds.getNorthEast(), bounds.getSouthWest(), bounds.getSouthEast()];
if (coordinates.every(point => boundsArray.find(boundPoint => boundPoint.equals(point as L.LatLng)) !== undefined)) {
coordinates = [bounds.getNorthWest(), bounds.getSouthEast()];
}
}
this.dataLayer.savePolygonCoordinates(this.data, coordinates);
}
private updatePolygonShape(data: FormattedData<TbMapDatasource>) {
const polyData = this.dataLayer.extractPolygonCoordinates(data);
if (this.editing) {
return;
}
const polyData = this.dataLayer.extractPolygonCoordinates(data) as TbPolyData;
if (isCutPolygon(polyData) || polyData.length !== 2) {
if (this.polygon instanceof L.Rectangle) {
this.polygonContainer.removeLayer(this.polygon);
this.polygon = L.polygon(polyData, {
...this.polygonStyle
...this.polygonStyle,
snapIgnore: !this.dataLayer.isSnappable()
});
this.polygon.addTo(this.polygonContainer);
this.editModeUpdated();
} else {
this.polygon.setLatLngs(polyData);
}
} else if (polyData.length === 2) {
const bounds = new L.LatLngBounds(polyData);
const bounds = new L.LatLngBounds(polyData as L.LatLngTuple[]);
(this.polygon as L.Rectangle).setBounds(bounds);
}
}
@ -134,11 +185,22 @@ export class TbPolygonsDataLayer extends TbShapesDataLayer<PolygonsDataLayerSett
return new TbPolygonDataLayerItem(data, dsData, this.settings, this);
}
public extractPolygonCoordinates(data: FormattedData<TbMapDatasource>) {
public extractPolygonCoordinates(data: FormattedData<TbMapDatasource>): TbPolygonRawCoordinates {
let rawPolyData = data[this.settings.polygonKey.label];
if (isString(rawPolyData)) {
rawPolyData = JSON.parse(rawPolyData);
}
return this.map.toPolygonCoordinates(rawPolyData);
return this.map.polygonDataToCoordinates(rawPolyData);
}
public savePolygonCoordinates(data: FormattedData<TbMapDatasource>, coordinates: TbPolygonCoordinates): void {
const converted = this.map.coordinatesToPolygonData(coordinates);
const polygonData = [
{
dataKey: this.settings.polygonKey,
value: converted
}
];
this.map.saveItemData(data.$datasource, polygonData).subscribe();
}
}

63
ui-ngx/src/app/modules/home/components/widget/lib/maps/geo-map.ts

@ -15,15 +15,22 @@
///
import {
checkLngLat,
DEFAULT_ZOOM_LEVEL,
defaultGeoMapSettings,
GeoMapSettings, latLngPointToBounds,
MapZoomAction, TbCircleData
GeoMapSettings,
latLngPointToBounds,
MapZoomAction,
TbCircleData,
TbPolygonCoordinate,
TbPolygonCoordinates,
TbPolygonRawCoordinate,
TbPolygonRawCoordinates
} from '@home/components/widget/lib/maps/models/map.models';
import { WidgetContext } from '@home/models/widget-component.models';
import { DeepPartial } from '@shared/models/common';
import { forkJoin, Observable, of } from 'rxjs';
import L, { LatLngBounds, LatLngTuple } from 'leaflet';
import L from 'leaflet';
import { map, tap } from 'rxjs/operators';
import { TbMapLayer } from '@home/components/widget/lib/maps/map-layer';
import { TbMap } from '@home/components/widget/lib/maps/map';
@ -53,7 +60,7 @@ export class TbGeoMap extends TbMap<GeoMapSettings> {
protected onResize(): void {}
protected fitBounds(bounds: LatLngBounds) {
protected fitBounds(bounds: L.LatLngBounds) {
if (bounds.isValid()) {
if (!this.settings.fitMapBounds && this.settings.defaultZoomLevel) {
this.map.setZoom(this.settings.defaultZoomLevel, { animate: false });
@ -115,27 +122,63 @@ export class TbGeoMap extends TbMap<GeoMapSettings> {
);
}
public positionToLatLng(position: {x: number; y: number}): L.LatLng {
public locationDataToLatLng(position: {x: number; y: number}): L.LatLng {
return L.latLng(position.x, position.y) as L.LatLng;
}
public toPolygonCoordinates(expression: (LatLngTuple | LatLngTuple[] | LatLngTuple[][])[]): any {
return (expression).map((el) => {
if (!Array.isArray(el[0]) && el.length === 2) {
public latLngToLocationData(position: L.LatLng): {x: number; y: number} {
position = position ? checkLngLat(position, this.southWest, this.northEast, 0) : {lat: null, lng: null} as L.LatLng;
return {
x: position.lat,
y: position.lng
}
}
public polygonDataToCoordinates(expression: TbPolygonRawCoordinates): TbPolygonRawCoordinates {
return (expression).map((el: TbPolygonRawCoordinate) => {
if (!Array.isArray(el[0]) && !Array.isArray(el[1]) && el.length === 2) {
return el;
} else if (Array.isArray(el) && el.length) {
return this.toPolygonCoordinates(el as LatLngTuple[] | LatLngTuple[][]);
return this.polygonDataToCoordinates(el as TbPolygonRawCoordinates) as TbPolygonRawCoordinate;
} else {
return null;
}
}).filter(el => !!el);
}
public convertCircleData(circle: TbCircleData): TbCircleData {
public coordinatesToPolygonData(coordinates: TbPolygonCoordinates): TbPolygonRawCoordinates {
if (coordinates.length) {
return coordinates.map((point: TbPolygonCoordinate) => {
if (Array.isArray(point)) {
return this.coordinatesToPolygonData(point) as TbPolygonRawCoordinate;
} else {
const convertPoint = checkLngLat(point, this.southWest, this.northEast);
return [convertPoint.lat, convertPoint.lng];
}
});
}
return [];
}
public circleDataToCoordinates(circle: TbCircleData): TbCircleData {
const centerPoint = latLngPointToBounds(new L.LatLng(circle.latitude, circle.longitude), this.southWest, this.northEast);
circle.latitude = centerPoint.lat;
circle.longitude = centerPoint.lng;
return circle;
}
public coordinatesToCircleData(center: L.LatLng, radius: number): TbCircleData {
let circleData: TbCircleData = null;
if (center) {
const position = checkLngLat(center, this.southWest, this.northEast);
circleData = {
latitude: position.lat,
longitude: position.lng,
radius
};
}
return circleData;
}
}

79
ui-ngx/src/app/modules/home/components/widget/lib/maps/image-map.ts

@ -15,6 +15,7 @@
///
import {
calculateNewPointCoordinate,
defaultImageMapSettings,
defaultImageMapSourceSettings,
ImageMapSettings,
@ -22,12 +23,12 @@ import {
ImageSourceType,
loadImageWithAspect,
MapZoomAction,
TbCircleData
TbCircleData, TbPolygonCoordinate, TbPolygonCoordinates, TbPolygonRawCoordinate, TbPolygonRawCoordinates
} from '@home/components/widget/lib/maps/models/map.models';
import { WidgetContext } from '@home/models/widget-component.models';
import { DeepPartial } from '@shared/models/common';
import { Observable, of, ReplaySubject, switchMap } from 'rxjs';
import L, { LatLngBounds, LatLngLiteral, LatLngTuple } from 'leaflet';
import L from 'leaflet';
import { TbMap } from '@home/components/widget/lib/maps/map';
import { ImagePipe } from '@shared/pipe/image.pipe';
import { catchError } from 'rxjs/operators';
@ -115,38 +116,62 @@ export class TbImageMap extends TbMap<ImageMapSettings> {
}
}
protected fitBounds(_bounds: LatLngBounds) {}
protected fitBounds(_bounds: L.LatLngBounds) {}
public positionToLatLng(position: {x: number; y: number}): L.LatLng {
public locationDataToLatLng(position: {x: number; y: number}): L.LatLng {
return this.pointToLatLng(
position.x * this.width,
position.y * this.height);
}
public pointToLatLng(x: number, y: number): L.LatLng {
return L.CRS.Simple.pointToLatLng({ x, y } as L.PointExpression, this.maxZoom - 1);
}
private latLngToPoint(latLng: LatLngLiteral): L.Point {
return L.CRS.Simple.latLngToPoint(latLng, this.maxZoom - 1);
public latLngToLocationData(position: L.LatLng): {x: number; y: number} {
if (!position) {
return {
x: null,
y: null
};
}
const point = this.latLngToPoint(position);
const posX = calculateNewPointCoordinate(point.x, this.width);
const posY = calculateNewPointCoordinate(point.y, this.height);
return {
x: posX,
y: posY
};
}
public toPolygonCoordinates(expression: (LatLngTuple | LatLngTuple[] | LatLngTuple[][])[]): any {
return (expression).map((el) => {
public polygonDataToCoordinates(expression: TbPolygonRawCoordinates): TbPolygonRawCoordinates {
return expression.map((el: TbPolygonRawCoordinate) => {
if (!Array.isArray(el[0]) && !Array.isArray(el[1]) && el.length === 2) {
return this.pointToLatLng(
const latLng = this.pointToLatLng(
el[0] * this.width,
el[1] * this.height
);
return [latLng.lat, latLng.lng] as TbPolygonRawCoordinate;
} else if (Array.isArray(el) && el.length) {
return this.toPolygonCoordinates(el as LatLngTuple[] | LatLngTuple[][]);
return this.polygonDataToCoordinates(el as TbPolygonRawCoordinates) as TbPolygonRawCoordinate;
} else {
return null;
}
}).filter(el => !!el);
}
public convertCircleData(circle: TbCircleData): TbCircleData {
public coordinatesToPolygonData(coordinates: TbPolygonCoordinates): TbPolygonRawCoordinates {
if (coordinates.length) {
return coordinates.map((point: TbPolygonCoordinate) => {
if (Array.isArray(point)) {
return this.coordinatesToPolygonData(point) as TbPolygonRawCoordinate;
} else {
const pos = this.latLngToPoint(point);
return [calculateNewPointCoordinate(pos.x, this.width), calculateNewPointCoordinate(pos.y, this.height)];
}
});
} else {
return [];
}
}
public circleDataToCoordinates(circle: TbCircleData): TbCircleData {
const centerPoint = this.pointToLatLng(circle.latitude * this.width, circle.longitude * this.height);
circle.latitude = centerPoint.lat;
circle.longitude = centerPoint.lng;
@ -154,10 +179,34 @@ export class TbImageMap extends TbMap<ImageMapSettings> {
return circle;
}
public coordinatesToCircleData(center: L.LatLng, radius: number): TbCircleData {
let circleData: TbCircleData = null;
if (center) {
const point = this.latLngToPoint(center);
const posX = calculateNewPointCoordinate(point.x, this.width);
const posY = calculateNewPointCoordinate(point.y, this.height);
const convertedRadius = calculateNewPointCoordinate(radius, this.width);
circleData = {
latitude: posX,
longitude: posY,
radius: convertedRadius
};
}
return circleData;
}
public getAspect(): number {
return this.imageLayerData.aspect;
}
private pointToLatLng(x: number, y: number): L.LatLng {
return L.CRS.Simple.pointToLatLng({ x, y } as L.PointExpression, this.maxZoom - 1);
}
private latLngToPoint(latLng: L.LatLngLiteral): L.Point {
return L.CRS.Simple.latLngToPoint(latLng, this.maxZoom - 1);
}
private doCreateMap(updateImage?: boolean) {
if (!this.map && this.imageLayerData.aspect > 0) {
const center = this.pointToLatLng(this.width / 2, this.height / 2);

27
ui-ngx/src/app/modules/home/components/widget/lib/maps/map.scss

@ -15,6 +15,9 @@
*/
@import '../../../../../../../scss/constants';
//$map-element-hover-color: #307FE5;
$map-element-hover-color: rgba(0,0,0,0.56);
.tb-map-layout {
display: flex;
width: 100%;
@ -104,6 +107,30 @@
&.tb-marker-div-icon {
background: none;
border: none;
&.tb-draggable {
cursor: move;
}
&.tb-hoverable {
svg {
transition: filter 0.2s;
}
&:hover {
svg {
filter: drop-shadow( 0 0 4px $map-element-hover-color);
}
}
}
}
}
img.leaflet-marker-icon, path {
&.tb-draggable {
cursor: move;
}
&.tb-hoverable {
transition: filter 0.2s;
&:hover {
filter: drop-shadow( 0 0 4px $map-element-hover-color);
}
}
}
.tb-cluster-marker-container {

86
ui-ngx/src/app/modules/home/components/widget/lib/maps/map.ts

@ -17,17 +17,18 @@
import {
additionalMapDataSourcesToDatasources,
BaseMapSettings,
DataKeyValuePair,
MapActionHandler,
MapType,
mergeMapDatasources,
parseCenterPosition,
TbCircleData,
TbMapDatasource
TbMapDatasource, TbPolygonCoordinates, TbPolygonRawCoordinates
} from '@home/components/widget/lib/maps/models/map.models';
import { WidgetContext } from '@home/models/widget-component.models';
import { formattedDataFormDatasourceData, isDefinedAndNotNull, mergeDeepIgnoreArray } from '@core/utils';
import { DeepPartial } from '@shared/models/common';
import L, { LatLngBounds, LatLngTuple, LeafletMouseEvent, Projection } from 'leaflet';
import L from 'leaflet';
import { forkJoin, Observable, of } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import '@home/components/widget/lib/maps/leaflet/leaflet-tb';
@ -39,6 +40,9 @@ import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe';
import { TbMarkersDataLayer } from '@home/components/widget/lib/maps/data-layer/markers-data-layer';
import { TbPolygonsDataLayer } from '@home/components/widget/lib/maps/data-layer/polygons-data-layer';
import { TbCirclesDataLayer } from '@home/components/widget/lib/maps/data-layer/circles-data-layer';
import { AttributeService } from '@core/http/attribute.service';
import { AttributeData, AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models';
import { EntityId } from '@shared/models/id/entity-id';
import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance;
export abstract class TbMap<S extends BaseMapSettings> {
@ -49,8 +53,8 @@ export abstract class TbMap<S extends BaseMapSettings> {
protected defaultCenterPosition: [number, number];
protected ignoreUpdateBounds = false;
protected southWest = new L.LatLng(-Projection.SphericalMercator['MAX_LATITUDE'], -180);
protected northEast = new L.LatLng(Projection.SphericalMercator['MAX_LATITUDE'], 180);
protected southWest = new L.LatLng(-L.Projection.SphericalMercator['MAX_LATITUDE'], -180);
protected northEast = new L.LatLng(L.Projection.SphericalMercator['MAX_LATITUDE'], 180);
protected dataLayers: TbMapDataLayer<any,any>[];
protected dsData: FormattedData<TbMapDatasource>[];
@ -128,6 +132,7 @@ export abstract class TbMap<S extends BaseMapSettings> {
this.map.panTo(this.defaultCenterPosition);
}
this.setupDataLayers();
this.setupEditMode();
this.createdControlButtonTooltip();
}
@ -225,6 +230,13 @@ export abstract class TbMap<S extends BaseMapSettings> {
}
}
private setupEditMode() {
const dragEnabled = this.dataLayers.some(dl => dl.isDragEnabled());
if (dragEnabled) {
//this.map.pm.enableGlobalDragMode();
}
}
private createdControlButtonTooltip() {
import('tooltipster').then(() => {
if ($.tooltipster) {
@ -326,7 +338,7 @@ export abstract class TbMap<S extends BaseMapSettings> {
protected abstract onResize(): void;
protected abstract fitBounds(bounds: LatLngBounds): void;
protected abstract fitBounds(bounds: L.LatLngBounds): void;
protected doSetupControls(): Observable<any> {
return of(null);
@ -375,7 +387,7 @@ export abstract class TbMap<S extends BaseMapSettings> {
public markerClick(marker: L.Layer, datasource: TbMapDatasource): void {
if (Object.keys(this.markerClickActions).length) {
marker.on('click', (event: LeafletMouseEvent) => {
marker.on('click', (event: L.LeafletMouseEvent) => {
for (const action in this.markerClickActions) {
this.markerClickActions[action](event.originalEvent, datasource);
}
@ -385,7 +397,7 @@ export abstract class TbMap<S extends BaseMapSettings> {
public polygonClick(polygon: L.Layer, datasource: TbMapDatasource): void {
if (Object.keys(this.polygonClickActions).length) {
polygon.on('click', (event: LeafletMouseEvent) => {
polygon.on('click', (event: L.LeafletMouseEvent) => {
for (const action in this.polygonClickActions) {
this.polygonClickActions[action](event.originalEvent, datasource);
}
@ -395,7 +407,7 @@ export abstract class TbMap<S extends BaseMapSettings> {
public circleClick(circle: L.Layer, datasource: TbMapDatasource): void {
if (Object.keys(this.circleClickActions).length) {
circle.on('click', (event: LeafletMouseEvent) => {
circle.on('click', (event: L.LeafletMouseEvent) => {
for (const action in this.circleClickActions) {
this.circleClickActions[action](event.originalEvent, datasource);
}
@ -403,6 +415,50 @@ export abstract class TbMap<S extends BaseMapSettings> {
}
}
public saveItemData(datasource: TbMapDatasource, data: DataKeyValuePair[]): Observable<any> {
const attributeService = this.ctx.$injector.get(AttributeService);
const attributes: AttributeData[] = [];
const timeseries: AttributeData[] = [];
const entityId: EntityId = {
entityType: datasource.entityType,
id: datasource.entityId
};
data.forEach(pair => {
const key = pair.dataKey;
if (key.type === DataKeyType.attribute) {
attributes.push({
key: key.name,
value: pair.value
});
} else if (key.type === DataKeyType.timeseries) {
timeseries.push({
key: key.name,
value: pair.value
});
}
});
const observables: Observable<any>[] = [];
if (timeseries.length) {
observables.push(attributeService.saveEntityTimeseries(
entityId,
LatestTelemetry.LATEST_TELEMETRY,
timeseries
));
}
if (attributes.length) {
observables.push(attributeService.saveEntityAttributes(
entityId,
AttributeScope.SERVER_SCOPE,
attributes
));
}
if (observables.length) {
return forkJoin(observables);
} else {
return of(null);
}
}
public destroy() {
if (this.mapResize$) {
this.mapResize$.disconnect();
@ -415,10 +471,18 @@ export abstract class TbMap<S extends BaseMapSettings> {
});
}
public abstract positionToLatLng(position: {x: number; y: number}): L.LatLng;
public abstract locationDataToLatLng(position: {x: number; y: number}): L.LatLng;
public abstract latLngToLocationData(position: L.LatLng): {x: number; y: number};
public abstract polygonDataToCoordinates(coordinates: TbPolygonRawCoordinates): TbPolygonRawCoordinates;
public abstract coordinatesToPolygonData(coordinates: TbPolygonCoordinates): TbPolygonRawCoordinates;
public abstract circleDataToCoordinates(circle: TbCircleData): TbCircleData;
public abstract coordinatesToCircleData(center: L.LatLng, radius: number): TbCircleData;
public abstract toPolygonCoordinates(expression: (LatLngTuple | LatLngTuple[] | LatLngTuple[][])[]): any;
public abstract convertCircleData(circle: TbCircleData): TbCircleData;
}

71
ui-ngx/src/app/modules/home/components/widget/lib/maps/models/map.models.ts

@ -19,7 +19,7 @@ import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { guid, hashCode, isDefinedAndNotNull, isNotEmptyStr, isString, mergeDeep } from '@core/utils';
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
import { materialColors } from '@shared/models/material.models';
import L from 'leaflet';
import L, { LatLngExpression } from 'leaflet';
import { TbFunction } from '@shared/models/js-function.models';
import { Observable, Observer, of, switchMap } from 'rxjs';
import { map } from 'rxjs/operators';
@ -89,11 +89,35 @@ export interface DataLayerTooltipSettings extends DataLayerPatternSettings {
offsetY: number;
}
export enum DataLayerEditAction {
add = 'add',
edit = 'edit',
move = 'move',
remove = 'remove'
}
export const dataLayerEditActions = Object.keys(DataLayerEditAction) as DataLayerEditAction[];
export const dataLayerEditActionTranslationMap = new Map<DataLayerEditAction, string>(
[
[DataLayerEditAction.add, 'widgets.maps.data-layer.action-add'],
[DataLayerEditAction.edit, 'widgets.maps.data-layer.action-edit'],
[DataLayerEditAction.move, 'widgets.maps.data-layer.action-move'],
[DataLayerEditAction.remove, 'widgets.maps.data-layer.action-remove']
]
);
export interface DataLayerEditSettings {
enabledActions: DataLayerEditAction[];
snappable: boolean;
}
export interface MapDataLayerSettings extends MapDataSourceSettings {
additionalDataKeys?: DataKey[];
label: DataLayerPatternSettings;
tooltip: DataLayerTooltipSettings;
groups?: string[];
edit: DataLayerEditSettings;
}
export const defaultBaseDataLayerSettings = (mapType: MapType): Partial<MapDataLayerSettings> => ({
@ -112,6 +136,10 @@ export const defaultBaseDataLayerSettings = (mapType: MapType): Partial<MapDataL
: '<b>${entityName}</b><br/><br/><b>X Pos:</b> ${xPos:2}<br/><b>Y Pos:</b> ${yPos:2}<br/><b>Temperature:</b> ${temperature} °C<br/><small>See tooltip settings for details</small>',
offsetX: 0,
offsetY: -1
},
edit: {
enabledActions: [],
snappable: false
}
})
@ -864,12 +892,23 @@ export type ClusterMarkerColorFunction = (data: FormattedData<TbMapDatasource>[]
export type MarkerPositionFunction = (origXPos: number, origYPos: number, data: FormattedData<TbMapDatasource>,
dsData: FormattedData<TbMapDatasource>[], aspect: number) => { x: number, y: number };
export type TbPolygonRawCoordinate = L.LatLngTuple | L.LatLngTuple[] | L.LatLngTuple[][];
export type TbPolygonRawCoordinates = TbPolygonRawCoordinate[];
export type TbPolyData = L.LatLngTuple[] | L.LatLngTuple[][] | L.LatLngTuple[][][];
export type TbPolygonCoordinate = L.LatLng | L.LatLng[] | L.LatLng[][];
export type TbPolygonCoordinates = TbPolygonCoordinate[];
export interface TbCircleData {
latitude: number;
longitude: number;
radius: number;
}
export type DataKeyValuePair = {
dataKey: DataKey;
value: any;
}
export const isJSON = (data: string): boolean => {
try {
const parseData = JSON.parse(data);
@ -892,7 +931,7 @@ export const isValidLongitude = (longitude: any): boolean =>
export const isValidLatLng = (latitude: any, longitude: any): boolean =>
isValidLatitude(latitude) && isValidLongitude(longitude);
export const isCutPolygon = (data): boolean => {
export const isCutPolygon = (data: TbPolygonCoordinates | TbPolygonRawCoordinates): boolean => {
return data.length > 1 && Array.isArray(data[0]) && (Array.isArray(data[0][0]) || data[0][0] instanceof L.LatLng);
}
@ -1069,3 +1108,31 @@ export const processTooltipTemplate = (template: string): string => {
return template;
}
export function calculateNewPointCoordinate(coordinate: number, imageSize: number): number {
let pointCoordinate = coordinate / imageSize;
if (pointCoordinate < 0) {
pointCoordinate = 0;
} else if (pointCoordinate > 1) {
pointCoordinate = 1;
}
return pointCoordinate;
}
export function checkLngLat(point: L.LatLng, southWest: L.LatLng, northEast: L.LatLng, offset = 0): L.LatLng {
const maxLngMap = northEast.lng - offset;
const minLngMap = southWest.lng + offset;
const maxLatMap = northEast.lat - offset;
const minLatMap = southWest.lat + offset;
if (point.lng > maxLngMap) {
point.lng = maxLngMap;
} else if (point.lng < minLngMap) {
point.lng = minLngMap;
}
if (point.lat > maxLatMap) {
point.lat = maxLatMap;
} else if (point.lat < minLatMap) {
point.lat = minLatMap;
}
return point;
}

16
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.html

@ -262,6 +262,22 @@
formControlName="groups">
</tb-string-items-list>
</div>
<div class="tb-form-panel" formGroupName="edit">
<div class="tb-form-panel-title">{{ dataLayerEditTitle | translate }}</div>
<div class="tb-form-row space-between column-lt-md">
<div translate>widgets.maps.data-layer.edit-instruments</div>
<mat-chip-listbox multiple formControlName="enabledActions">
<mat-chip-option *ngFor="let action of dataLayerEditActions" [value]="action">
{{ dataLayerEditActionTranslationMap.get(action) | translate }}
</mat-chip-option>
</mat-chip-listbox>
</div>
<div class="tb-form-row">
<mat-slide-toggle class="mat-slide" formControlName="snappable">
{{ 'widgets.maps.data-layer.enable-snapping' | translate }}
</mat-slide-toggle>
</div>
</div>
<tb-marker-clustering-settings
*ngIf="dataLayerType === 'markers'"
formControlName="markerClustering">

27
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.ts

@ -18,10 +18,12 @@ import { Component, DestroyRef, Inject, ViewEncapsulation } from '@angular/core'
import { DialogComponent } from '@shared/components/dialog.component';
import {
CirclesDataLayerSettings,
DataLayerEditAction, dataLayerEditActions,
dataLayerEditActionTranslationMap,
defaultBaseMapDataLayerSettings,
MapDataLayerSettings,
MapDataLayerType,
MapType,
MapType, mapZoomActions, mapZoomActionTranslationMap,
MarkersDataLayerSettings,
MarkerType,
PolygonsDataLayerSettings,
@ -71,6 +73,10 @@ export class MapDataLayerDialogComponent extends DialogComponent<MapDataLayerDia
datasourceTypes: Array<DatasourceType> = [];
datasourceTypesTranslations = datasourceTypeTranslationMap;
dataLayerEditTitle: string;
dataLayerEditActions: Array<DataLayerEditAction> = [];
dataLayerEditActionTranslationMap = dataLayerEditActionTranslationMap;
dataLayerFormGroup: UntypedFormGroup;
settings = this.data.settings;
@ -111,13 +117,19 @@ export class MapDataLayerDialogComponent extends DialogComponent<MapDataLayerDia
additionalDataKeys: [this.settings.additionalDataKeys, []],
label: [this.settings.label, []],
tooltip: [this.settings.tooltip, []],
groups: [this.settings.groups, []]
groups: [this.settings.groups, []],
edit: this.fb.group({
enabledActions: [this.settings.edit?.enabledActions, []],
snappable: [this.settings.edit?.snappable, []]
})
});
switch (this.dataLayerType) {
case 'markers':
const markersDataLayer = this.settings as MarkersDataLayerSettings;
this.dialogTitle = 'widgets.maps.data-layer.marker.marker-configuration';
this.dataLayerEditTitle = 'widgets.maps.data-layer.marker.edit';
this.dataLayerEditActions = [DataLayerEditAction.add, DataLayerEditAction.move, DataLayerEditAction.remove];
const markersDataLayer = this.settings as MarkersDataLayerSettings;
this.dataLayerFormGroup.addControl('xKey', this.fb.control(markersDataLayer.xKey, Validators.required));
this.dataLayerFormGroup.addControl('yKey', this.fb.control(markersDataLayer.yKey, Validators.required))
this.dataLayerFormGroup.addControl('markerType', this.fb.control(markersDataLayer.markerType, Validators.required));
@ -138,17 +150,20 @@ export class MapDataLayerDialogComponent extends DialogComponent<MapDataLayerDia
break;
case 'polygons':
case 'circles':
this.dataLayerEditActions = dataLayerEditActions;
const shapeDataLayer = this.settings as ShapeDataLayerSettings;
this.dataLayerFormGroup.addControl('fillColor', this.fb.control(shapeDataLayer.fillColor, Validators.required));
this.dataLayerFormGroup.addControl('strokeColor', this.fb.control(shapeDataLayer.strokeColor, Validators.required));
this.dataLayerFormGroup.addControl('strokeWeight', this.fb.control(shapeDataLayer.strokeWeight, [Validators.required, Validators.min(0)]));
if (this.dataLayerType === 'polygons') {
const polygonsDataLayer = this.settings as PolygonsDataLayerSettings;
this.dialogTitle = 'widgets.maps.data-layer.polygon.polygon-configuration';
this.dataLayerEditTitle = 'widgets.maps.data-layer.marker.edit';
const polygonsDataLayer = this.settings as PolygonsDataLayerSettings;
this.dataLayerFormGroup.addControl('polygonKey', this.fb.control(polygonsDataLayer.polygonKey, Validators.required));
} else {
const circlesDataLayer = this.settings as CirclesDataLayerSettings;
this.dialogTitle = 'widgets.maps.data-layer.circle.circle-configuration';
this.dataLayerEditTitle = 'widgets.maps.data-layer.circle.edit';
const circlesDataLayer = this.settings as CirclesDataLayerSettings;
this.dataLayerFormGroup.addControl('circleKey', this.fb.control(circlesDataLayer.circleKey, Validators.required));
}
break;
@ -304,4 +319,6 @@ export class MapDataLayerDialogComponent extends DialogComponent<MapDataLayerDia
}
protected readonly mapProvider = MapProviders;
protected readonly mapZoomActions = mapZoomActions;
protected readonly mapZoomActionTranslationMap = mapZoomActionTranslationMap;
}

15
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -7664,6 +7664,12 @@
"tooltip-offset": "Tooltip offset",
"tooltip-offset-horizontal": "Horizontal",
"tooltip-offset-vertical": "Vertical",
"action-add": "Add",
"action-edit": "Edit",
"action-move": "Move",
"action-remove": "Remove",
"edit-instruments": "Instruments",
"enable-snapping": "Enable snapping to other vertices for precision drawing",
"marker": {
"latitude-key": "Latitude key",
"longitude-key": "Longitude key",
@ -7709,7 +7715,8 @@
"lazy-load": "Use lazy load for adding markers",
"use-cluster-marker-color-function": "Use cluster markers color function",
"marker-color-function": "Marker color function"
}
},
"edit": "Edit marker"
},
"polygon": {
"polygon-key": "Polygon key",
@ -7717,7 +7724,8 @@
"no-polygons": "No polygons configured",
"add-polygon": "Add polygon",
"polygon-configuration": "Polygon configuration",
"remove-polygon": "Remove polygon"
"remove-polygon": "Remove polygon",
"edit": "Edit polygon"
},
"circle": {
"circle-key": "Circle key",
@ -7725,7 +7733,8 @@
"no-circles": "No circles configured",
"add-circle": "Add circle",
"circle-configuration": "Circle configuration",
"remove-circle": "Remove circle"
"remove-circle": "Remove circle",
"edit": "Edit circle"
}
},
"select-entity": "Select entity",

Loading…
Cancel
Save