|
|
@ -32,7 +32,11 @@ import L from 'leaflet'; |
|
|
import { forkJoin, Observable, of } from 'rxjs'; |
|
|
import { forkJoin, Observable, of } from 'rxjs'; |
|
|
import { switchMap } from 'rxjs/operators'; |
|
|
import { switchMap } from 'rxjs/operators'; |
|
|
import '@home/components/widget/lib/maps/leaflet/leaflet-tb'; |
|
|
import '@home/components/widget/lib/maps/leaflet/leaflet-tb'; |
|
|
import { MapDataLayerType, TbMapDataLayer, } from '@home/components/widget/lib/maps/data-layer/map-data-layer'; |
|
|
import { |
|
|
|
|
|
MapDataLayerType, |
|
|
|
|
|
TbDataLayerItem, |
|
|
|
|
|
TbMapDataLayer, |
|
|
|
|
|
} from '@home/components/widget/lib/maps/data-layer/map-data-layer'; |
|
|
import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; |
|
|
import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; |
|
|
import { FormattedData, WidgetActionDescriptor, widgetType } from '@shared/models/widget.models'; |
|
|
import { FormattedData, WidgetActionDescriptor, widgetType } from '@shared/models/widget.models'; |
|
|
import { EntityDataPageLink } from '@shared/models/query/query.models'; |
|
|
import { EntityDataPageLink } from '@shared/models/query/query.models'; |
|
|
@ -44,6 +48,9 @@ import { AttributeService } from '@core/http/attribute.service'; |
|
|
import { AttributeData, AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models'; |
|
|
import { AttributeData, AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models'; |
|
|
import { EntityId } from '@shared/models/id/entity-id'; |
|
|
import { EntityId } from '@shared/models/id/entity-id'; |
|
|
import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance; |
|
|
import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance; |
|
|
|
|
|
import TooltipPositioningSide = JQueryTooltipster.TooltipPositioningSide; |
|
|
|
|
|
|
|
|
|
|
|
type TooltipInstancesData = {root: HTMLElement, instances: ITooltipsterInstance[]}; |
|
|
|
|
|
|
|
|
export abstract class TbMap<S extends BaseMapSettings> { |
|
|
export abstract class TbMap<S extends BaseMapSettings> { |
|
|
|
|
|
|
|
|
@ -59,10 +66,14 @@ export abstract class TbMap<S extends BaseMapSettings> { |
|
|
protected dataLayers: TbMapDataLayer<any,any>[]; |
|
|
protected dataLayers: TbMapDataLayer<any,any>[]; |
|
|
protected dsData: FormattedData<TbMapDatasource>[]; |
|
|
protected dsData: FormattedData<TbMapDatasource>[]; |
|
|
|
|
|
|
|
|
|
|
|
protected selectedDataItem: TbDataLayerItem; |
|
|
|
|
|
|
|
|
protected mapElement: HTMLElement; |
|
|
protected mapElement: HTMLElement; |
|
|
|
|
|
|
|
|
protected sidebar: L.TB.SidebarControl; |
|
|
protected sidebar: L.TB.SidebarControl; |
|
|
|
|
|
|
|
|
|
|
|
protected editToolbar: L.TB.BottomToolbarControl; |
|
|
|
|
|
|
|
|
private readonly mapResize$: ResizeObserver; |
|
|
private readonly mapResize$: ResizeObserver; |
|
|
|
|
|
|
|
|
private readonly tooltipActions: { [name: string]: MapActionHandler }; |
|
|
private readonly tooltipActions: { [name: string]: MapActionHandler }; |
|
|
@ -70,7 +81,7 @@ export abstract class TbMap<S extends BaseMapSettings> { |
|
|
private readonly polygonClickActions: { [name: string]: MapActionHandler }; |
|
|
private readonly polygonClickActions: { [name: string]: MapActionHandler }; |
|
|
private readonly circleClickActions: { [name: string]: MapActionHandler }; |
|
|
private readonly circleClickActions: { [name: string]: MapActionHandler }; |
|
|
|
|
|
|
|
|
private tooltipInstances: ITooltipsterInstance[] = []; |
|
|
private tooltipInstances: TooltipInstancesData[] = []; |
|
|
|
|
|
|
|
|
protected constructor(protected ctx: WidgetContext, |
|
|
protected constructor(protected ctx: WidgetContext, |
|
|
protected inputSettings: DeepPartial<S>, |
|
|
protected inputSettings: DeepPartial<S>, |
|
|
@ -133,7 +144,7 @@ export abstract class TbMap<S extends BaseMapSettings> { |
|
|
} |
|
|
} |
|
|
this.setupDataLayers(); |
|
|
this.setupDataLayers(); |
|
|
this.setupEditMode(); |
|
|
this.setupEditMode(); |
|
|
this.createdControlButtonTooltip(); |
|
|
this.createdControlButtonTooltip(this.mapElement, ['topleft', 'bottomleft'].includes(this.settings.controlsPosition) ? 'right' : 'left'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private setupDataLayers() { |
|
|
private setupDataLayers() { |
|
|
@ -231,21 +242,36 @@ export abstract class TbMap<S extends BaseMapSettings> { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private setupEditMode() { |
|
|
private setupEditMode() { |
|
|
const dragEnabled = this.dataLayers.some(dl => dl.isDragEnabled()); |
|
|
this.editToolbar = L.TB.bottomToolbar({ |
|
|
if (dragEnabled) { |
|
|
mapElement: $(this.mapElement), |
|
|
//this.map.pm.enableGlobalDragMode();
|
|
|
closeTitle: this.ctx.translate.instant('action.cancel'), |
|
|
} |
|
|
onClose: () => { |
|
|
|
|
|
this.deselectItem(); |
|
|
|
|
|
} |
|
|
|
|
|
}).addTo(this.map); |
|
|
|
|
|
|
|
|
|
|
|
this.map.on('click', () => { |
|
|
|
|
|
this.deselectItem(); |
|
|
|
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private createdControlButtonTooltip() { |
|
|
private createdControlButtonTooltip(root: HTMLElement, side: TooltipPositioningSide) { |
|
|
import('tooltipster').then(() => { |
|
|
import('tooltipster').then(() => { |
|
|
|
|
|
let tooltipData = this.tooltipInstances.find(d => d.root === root); |
|
|
|
|
|
if (!tooltipData) { |
|
|
|
|
|
tooltipData = { |
|
|
|
|
|
root, |
|
|
|
|
|
instances: [] |
|
|
|
|
|
} |
|
|
|
|
|
this.tooltipInstances.push(tooltipData); |
|
|
|
|
|
} |
|
|
if ($.tooltipster) { |
|
|
if ($.tooltipster) { |
|
|
this.tooltipInstances.forEach((instance) => { |
|
|
tooltipData.instances.forEach((instance) => { |
|
|
instance.destroy(); |
|
|
instance.destroy(); |
|
|
}); |
|
|
}); |
|
|
this.tooltipInstances = []; |
|
|
tooltipData.instances = []; |
|
|
} |
|
|
} |
|
|
$(this.mapElement) |
|
|
$(root) |
|
|
.find('a[role="button"]:not(.leaflet-pm-action)') |
|
|
.find('a[role="button"]:not(.leaflet-pm-action)') |
|
|
.each((_index, element) => { |
|
|
.each((_index, element) => { |
|
|
let title: string; |
|
|
let title: string; |
|
|
@ -267,7 +293,7 @@ export abstract class TbMap<S extends BaseMapSettings> { |
|
|
scroll: true, |
|
|
scroll: true, |
|
|
mouseleave: true |
|
|
mouseleave: true |
|
|
}, |
|
|
}, |
|
|
side: ['topleft', 'bottomleft'].includes(this.settings.controlsPosition) ? 'right' : 'left', |
|
|
side, |
|
|
distance: 2, |
|
|
distance: 2, |
|
|
trackOrigin: true, |
|
|
trackOrigin: true, |
|
|
functionBefore: (_instance, helper) => { |
|
|
functionBefore: (_instance, helper) => { |
|
|
@ -277,7 +303,14 @@ export abstract class TbMap<S extends BaseMapSettings> { |
|
|
}, |
|
|
}, |
|
|
} |
|
|
} |
|
|
); |
|
|
); |
|
|
this.tooltipInstances.push(tooltip.tooltipster('instance')); |
|
|
const instance = tooltip.tooltipster('instance'); |
|
|
|
|
|
tooltipData.instances.push(instance); |
|
|
|
|
|
instance.on('destroyed', () => { |
|
|
|
|
|
const index = tooltipData.instances.indexOf(instance); |
|
|
|
|
|
if (index > -1) { |
|
|
|
|
|
tooltipData.instances.splice(index, 1); |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
@ -385,36 +418,64 @@ export abstract class TbMap<S extends BaseMapSettings> { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public markerClick(marker: L.Layer, datasource: TbMapDatasource): void { |
|
|
public markerClick(marker: TbDataLayerItem, datasource: TbMapDatasource): void { |
|
|
if (Object.keys(this.markerClickActions).length) { |
|
|
if (Object.keys(this.markerClickActions).length) { |
|
|
marker.on('click', (event: L.LeafletMouseEvent) => { |
|
|
marker.getLayer().on('click', (event: L.LeafletMouseEvent) => { |
|
|
for (const action in this.markerClickActions) { |
|
|
if (!marker.isEditing()) { |
|
|
this.markerClickActions[action](event.originalEvent, datasource); |
|
|
for (const action in this.markerClickActions) { |
|
|
|
|
|
this.markerClickActions[action](event.originalEvent, datasource); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public polygonClick(polygon: L.Layer, datasource: TbMapDatasource): void { |
|
|
public polygonClick(polygon: TbDataLayerItem, datasource: TbMapDatasource): void { |
|
|
if (Object.keys(this.polygonClickActions).length) { |
|
|
if (Object.keys(this.polygonClickActions).length) { |
|
|
polygon.on('click', (event: L.LeafletMouseEvent) => { |
|
|
polygon.getLayer().on('click', (event: L.LeafletMouseEvent) => { |
|
|
for (const action in this.polygonClickActions) { |
|
|
if (!polygon.isEditing()) { |
|
|
this.polygonClickActions[action](event.originalEvent, datasource); |
|
|
for (const action in this.polygonClickActions) { |
|
|
|
|
|
this.polygonClickActions[action](event.originalEvent, datasource); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public circleClick(circle: L.Layer, datasource: TbMapDatasource): void { |
|
|
public circleClick(circle: TbDataLayerItem, datasource: TbMapDatasource): void { |
|
|
if (Object.keys(this.circleClickActions).length) { |
|
|
if (Object.keys(this.circleClickActions).length) { |
|
|
circle.on('click', (event: L.LeafletMouseEvent) => { |
|
|
circle.getLayer().on('click', (event: L.LeafletMouseEvent) => { |
|
|
for (const action in this.circleClickActions) { |
|
|
if (!circle.isEditing()) { |
|
|
this.circleClickActions[action](event.originalEvent, datasource); |
|
|
for (const action in this.circleClickActions) { |
|
|
|
|
|
this.circleClickActions[action](event.originalEvent, datasource); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public selectItem(item: TbDataLayerItem): void { |
|
|
|
|
|
if (this.selectedDataItem) { |
|
|
|
|
|
this.selectedDataItem.deselect(); |
|
|
|
|
|
this.selectedDataItem = null; |
|
|
|
|
|
this.editToolbar.close(); |
|
|
|
|
|
} |
|
|
|
|
|
this.selectedDataItem = item; |
|
|
|
|
|
if (this.selectedDataItem) { |
|
|
|
|
|
const buttons = this.selectedDataItem.select(); |
|
|
|
|
|
this.editToolbar.open(buttons); |
|
|
|
|
|
this.createdControlButtonTooltip(this.editToolbar.container, 'top'); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public deselectItem(): void { |
|
|
|
|
|
this.selectItem(null); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public getSelectedDataItem(): TbDataLayerItem { |
|
|
|
|
|
return this.selectedDataItem; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
public saveItemData(datasource: TbMapDatasource, data: DataKeyValuePair[]): Observable<any> { |
|
|
public saveItemData(datasource: TbMapDatasource, data: DataKeyValuePair[]): Observable<any> { |
|
|
const attributeService = this.ctx.$injector.get(AttributeService); |
|
|
const attributeService = this.ctx.$injector.get(AttributeService); |
|
|
const attributes: AttributeData[] = []; |
|
|
const attributes: AttributeData[] = []; |
|
|
@ -466,8 +527,10 @@ export abstract class TbMap<S extends BaseMapSettings> { |
|
|
if (this.map) { |
|
|
if (this.map) { |
|
|
this.map.remove(); |
|
|
this.map.remove(); |
|
|
} |
|
|
} |
|
|
this.tooltipInstances.forEach((instance) => { |
|
|
this.tooltipInstances.forEach((data) => { |
|
|
instance.destroy(); |
|
|
data.instances.forEach(instance => { |
|
|
|
|
|
instance.destroy(); |
|
|
|
|
|
}) |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|