Browse Source

UI: Add JS modules support to map widgets JS functions.

pull/12184/head
Igor Kulikov 2 years ago
parent
commit
a0dcd7cf30
  1. 15
      ui-ngx/src/app/core/utils.ts
  2. 11
      ui-ngx/src/app/modules/home/components/widget/lib/maps/circle.ts
  3. 5
      ui-ngx/src/app/modules/home/components/widget/lib/maps/common-maps-utils.ts
  4. 10
      ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts
  5. 78
      ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts
  6. 142
      ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts
  7. 12
      ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts
  8. 16
      ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts
  9. 11
      ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts
  10. 22
      ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts
  11. 4
      ui-ngx/src/app/modules/home/components/widget/lib/settings/map/circle-settings.component.html
  12. 1
      ui-ngx/src/app/modules/home/components/widget/lib/settings/map/marker-clustering-settings.component.html
  13. 5
      ui-ngx/src/app/modules/home/components/widget/lib/settings/map/markers-settings.component.html
  14. 4
      ui-ngx/src/app/modules/home/components/widget/lib/settings/map/polygon-settings.component.html
  15. 1
      ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-common-settings.component.html
  16. 2
      ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-marker-settings.component.html
  17. 1
      ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-path-settings.component.html
  18. 2
      ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-point-settings.component.html
  19. 123
      ui-ngx/src/app/modules/home/components/widget/lib/trip-animation/trip-animation.component.ts

15
ui-ngx/src/app/core/utils.ts

@ -711,21 +711,6 @@ export function safeExecuteTbFunction<T extends GenericFunction>(func: CompiledT
return res; return res;
} }
export function safeExecute(func: (...args: any[]) => any, params = []) {
let res = null;
if (func && typeof (func) === 'function') {
try {
res = func(...params);
}
catch (err) {
console.log('error in external function:', err);
res = null;
}
}
return res;
}
export function padValue(val: any, dec: number): string { export function padValue(val: any, dec: number): string {
let strVal; let strVal;
let n; let n;

11
ui-ngx/src/app/modules/home/components/widget/lib/maps/circle.ts

@ -16,14 +16,11 @@
import L, { LeafletMouseEvent } from 'leaflet'; import L, { LeafletMouseEvent } from 'leaflet';
import { CircleData, WidgetCircleSettings } from '@home/components/widget/lib/maps/map-models'; import { CircleData, WidgetCircleSettings } from '@home/components/widget/lib/maps/map-models';
import { import { functionValueCalculator, parseWithTranslation } from '@home/components/widget/lib/maps/common-maps-utils';
functionValueCalculator,
parseWithTranslation
} from '@home/components/widget/lib/maps/common-maps-utils';
import LeafletMap from '@home/components/widget/lib/maps/leaflet-map'; import LeafletMap from '@home/components/widget/lib/maps/leaflet-map';
import { createTooltip } from '@home/components/widget/lib/maps/maps-utils'; import { createTooltip } from '@home/components/widget/lib/maps/maps-utils';
import { FormattedData } from '@shared/models/widget.models'; import { FormattedData } from '@shared/models/widget.models';
import { fillDataPattern, processDataPattern, safeExecute } from '@core/utils'; import { fillDataPattern, processDataPattern, safeExecuteTbFunction } from '@core/utils';
export class Circle { export class Circle {
@ -94,7 +91,7 @@ export class Circle {
if (this.settings.showCircleLabel) { if (this.settings.showCircleLabel) {
if (!this.map.circleLabelText || this.settings.useCircleLabelFunction) { if (!this.map.circleLabelText || this.settings.useCircleLabelFunction) {
const pattern = this.settings.useCircleLabelFunction ? const pattern = this.settings.useCircleLabelFunction ?
safeExecute(this.settings.parsedCircleLabelFunction, safeExecuteTbFunction(this.settings.parsedCircleLabelFunction,
[this.data, this.dataSources, this.data.dsIndex]) : this.settings.circleLabel; [this.data, this.dataSources, this.data.dsIndex]) : this.settings.circleLabel;
this.map.circleLabelText = parseWithTranslation.prepareProcessPattern(pattern, true); this.map.circleLabelText = parseWithTranslation.prepareProcessPattern(pattern, true);
this.map.replaceInfoTooltipCircle = processDataPattern(this.map.circleLabelText, this.data); this.map.replaceInfoTooltipCircle = processDataPattern(this.map.circleLabelText, this.data);
@ -109,7 +106,7 @@ export class Circle {
private updateTooltip() { private updateTooltip() {
const pattern = this.settings.useCircleTooltipFunction ? const pattern = this.settings.useCircleTooltipFunction ?
safeExecute(this.settings.parsedCircleTooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) : safeExecuteTbFunction(this.settings.parsedCircleTooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) :
this.settings.circleTooltipPattern; this.settings.circleTooltipPattern;
this.tooltip.setContent(parseWithTranslation.parseTemplate(pattern, this.data, true)); this.tooltip.setContent(parseWithTranslation.parseTemplate(pattern, this.data, true));
} }

5
ui-ngx/src/app/modules/home/components/widget/lib/maps/common-maps-utils.ts

@ -29,6 +29,7 @@ import { map } from 'rxjs/operators';
import { FormattedData } from '@shared/models/widget.models'; import { FormattedData } from '@shared/models/widget.models';
import L from 'leaflet'; import L from 'leaflet';
import { ImagePipe } from '@shared/pipe/image.pipe'; import { ImagePipe } from '@shared/pipe/image.pipe';
import { CompiledTbFunction, GenericFunction } from '@shared/models/js-function.models';
export function getRatio(firsMoment: number, secondMoment: number, intermediateMoment: number): number { export function getRatio(firsMoment: number, secondMoment: number, intermediateMoment: number): number {
return (intermediateMoment - firsMoment) / (secondMoment - firsMoment); return (intermediateMoment - firsMoment) / (secondMoment - firsMoment);
@ -257,11 +258,11 @@ export const parseWithTranslation = {
} }
}; };
export function functionValueCalculator<T>(useFunction: boolean, func: (...args: any[]) => any, params = [], defaultValue: T): T { export function functionValueCalculator<T>(useFunction: boolean, func: CompiledTbFunction<GenericFunction>, params = [], defaultValue: T): T {
let res: T; let res: T;
if (useFunction && isDefined(func) && isFunction(func)) { if (useFunction && isDefined(func) && isFunction(func)) {
try { try {
res = func(...params); res = func.execute(...params);
if (!isDefinedAndNotNull(res) || res === '') { if (!isDefinedAndNotNull(res) || res === '') {
res = defaultValue; res = defaultValue;
} }

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

@ -54,7 +54,7 @@ import {
isNotEmptyStr, isNotEmptyStr,
isString, isString,
mergeFormattedData, mergeFormattedData,
safeExecute safeExecuteTbFunction
} from '@core/utils'; } from '@core/utils';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { import {
@ -63,9 +63,9 @@ import {
} from '@home/components/widget/lib/maps/dialogs/select-entity-dialog.component'; } from '@home/components/widget/lib/maps/dialogs/select-entity-dialog.component';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { FormattedData, ReplaceInfo } from '@shared/models/widget.models'; import { FormattedData, ReplaceInfo } from '@shared/models/widget.models';
import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance;
import { ImagePipe } from '@shared/pipe/image.pipe'; import { ImagePipe } from '@shared/pipe/image.pipe';
import { take, tap } from 'rxjs/operators'; import { take, tap } from 'rxjs/operators';
import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance;
export default abstract class LeafletMap { export default abstract class LeafletMap {
@ -149,7 +149,7 @@ export default abstract class LeafletMap {
const childCount = cluster.getChildCount(); const childCount = cluster.getChildCount();
const formattedData = cluster.getAllChildMarkers().map(clusterMarker => clusterMarker.options.tbMarkerData); const formattedData = cluster.getAllChildMarkers().map(clusterMarker => clusterMarker.options.tbMarkerData);
const markerColor = markerClusteringSettings.clusterMarkerFunction const markerColor = markerClusteringSettings.clusterMarkerFunction
? safeExecute(markerClusteringSettings.parsedClusterMarkerFunction, ? safeExecuteTbFunction(markerClusteringSettings.parsedClusterMarkerFunction,
[formattedData, childCount]) [formattedData, childCount])
: null; : null;
if (isDefinedAndNotNull(markerColor) && tinycolor(markerColor).isValid()) { if (isDefinedAndNotNull(markerColor) && tinycolor(markerColor).isValid()) {
@ -899,7 +899,7 @@ export default abstract class LeafletMap {
rawMarkers.forEach(data => { rawMarkers.forEach(data => {
if (data.rotationAngle || data.rotationAngle === 0) { if (data.rotationAngle || data.rotationAngle === 0) {
const currentImage: MarkerImageInfo = this.options.useMarkerImageFunction ? const currentImage: MarkerImageInfo = this.options.useMarkerImageFunction ?
safeExecute(this.options.parsedMarkerImageFunction, safeExecuteTbFunction(this.options.parsedMarkerImageFunction,
[data, this.options.markerImages, markersData, data.dsIndex]) : this.options.currentImage; [data, this.options.markerImages, markersData, data.dsIndex]) : this.options.currentImage;
const imageUrl$ = const imageUrl$ =
currentImage currentImage
@ -1042,7 +1042,7 @@ export default abstract class LeafletMap {
if (!!this.extractPosition(pdata)) { if (!!this.extractPosition(pdata)) {
const dsData = pointsData.map(ds => ds[tsIndex]); const dsData = pointsData.map(ds => ds[tsIndex]);
if (this.options.useColorPointFunction) { if (this.options.useColorPointFunction) {
pointColor = safeExecute(this.options.parsedColorPointFunction, [pdata, dsData, pdata.dsIndex]); pointColor = safeExecuteTbFunction(this.options.parsedColorPointFunction, [pdata, dsData, pdata.dsIndex]);
} }
const point = L.circleMarker(this.convertPosition(pdata, dsData), { const point = L.circleMarker(this.convertPosition(pdata, dsData), {
color: pointColor, color: pointColor,

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

@ -18,6 +18,7 @@ import { Datasource, FormattedData } from '@app/shared/models/widget.models';
import tinycolor from 'tinycolor2'; import tinycolor from 'tinycolor2';
import { BaseIconOptions, Icon } from 'leaflet'; import { BaseIconOptions, Icon } from 'leaflet';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { CompiledTbFunction, TbFunction } from '@shared/models/js-function.models';
export const DEFAULT_MAP_PAGE_SIZE = 16384; export const DEFAULT_MAP_PAGE_SIZE = 16384;
export const DEFAULT_ZOOM_LEVEL = 8; export const DEFAULT_ZOOM_LEVEL = 8;
@ -273,7 +274,7 @@ export interface TripAnimationCommonSettings {
} }
export interface WidgetTripAnimationCommonSettings extends TripAnimationCommonSettings { export interface WidgetTripAnimationCommonSettings extends TripAnimationCommonSettings {
parsedTooltipFunction: GenericFunction; parsedTooltipFunction: CompiledTbFunction<GenericFunction>;
} }
export const defaultTripAnimationCommonSettings: TripAnimationCommonSettings = { export const defaultTripAnimationCommonSettings: TripAnimationCommonSettings = {
@ -305,35 +306,35 @@ export const showTooltipActionTranslationMap = new Map<ShowTooltipAction, string
export interface MarkersSettings { export interface MarkersSettings {
markerOffsetX: number; markerOffsetX: number;
markerOffsetY: number; markerOffsetY: number;
posFunction?: string; posFunction?: TbFunction;
draggableMarker: boolean; draggableMarker: boolean;
showLabel: boolean; showLabel: boolean;
useLabelFunction: boolean; useLabelFunction: boolean;
label?: string; label?: string;
labelFunction?: string; labelFunction?: TbFunction;
showTooltip: boolean; showTooltip: boolean;
showTooltipAction: ShowTooltipAction; showTooltipAction: ShowTooltipAction;
autocloseTooltip: boolean; autocloseTooltip: boolean;
useTooltipFunction: boolean; useTooltipFunction: boolean;
tooltipPattern?: string; tooltipPattern?: string;
tooltipFunction?: string; tooltipFunction?: TbFunction;
tooltipOffsetX: number; tooltipOffsetX: number;
tooltipOffsetY: number; tooltipOffsetY: number;
color?: string; color?: string;
useColorFunction: boolean; useColorFunction: boolean;
colorFunction?: string; colorFunction?: TbFunction;
useMarkerImageFunction: boolean; useMarkerImageFunction: boolean;
markerImage?: string; markerImage?: string;
markerImageSize?: number; markerImageSize?: number;
markerImageFunction?: string; markerImageFunction?: TbFunction;
markerImages?: string[]; markerImages?: string[];
} }
export interface WidgetMarkersSettings extends MarkersSettings, WidgetToolipSettings { export interface WidgetMarkersSettings extends MarkersSettings, WidgetToolipSettings {
parsedLabelFunction: GenericFunction; parsedLabelFunction: CompiledTbFunction<GenericFunction>;
parsedTooltipFunction: GenericFunction; parsedTooltipFunction: CompiledTbFunction<GenericFunction>;
parsedColorFunction: GenericFunction; parsedColorFunction: CompiledTbFunction<GenericFunction>;
parsedMarkerImageFunction: MarkerImageFunction; parsedMarkerImageFunction: CompiledTbFunction<MarkerImageFunction>;
markerClick: { [name: string]: actionsHandler }; markerClick: { [name: string]: actionsHandler };
currentImage: MarkerImageInfo; currentImage: MarkerImageInfo;
tinyColor: tinycolor.Instance; tinyColor: tinycolor.Instance;
@ -382,8 +383,7 @@ export interface TripAnimationMarkerSettings {
} }
export interface WidgetTripAnimationMarkerSettings extends TripAnimationMarkerSettings { export interface WidgetTripAnimationMarkerSettings extends TripAnimationMarkerSettings {
parsedLabelFunction: GenericFunction; parsedLabelFunction: CompiledTbFunction<GenericFunction>;
parsedMarkerImageFunction: MarkerImageFunction;
} }
export const defaultTripAnimationMarkersSettings: TripAnimationMarkerSettings = { export const defaultTripAnimationMarkersSettings: TripAnimationMarkerSettings = {
@ -406,29 +406,29 @@ export interface PolygonSettings {
showPolygonLabel: boolean; showPolygonLabel: boolean;
usePolygonLabelFunction: boolean; usePolygonLabelFunction: boolean;
polygonLabel?: string; polygonLabel?: string;
polygonLabelFunction?: string; polygonLabelFunction?: TbFunction;
showPolygonTooltip: boolean; showPolygonTooltip: boolean;
showPolygonTooltipAction: ShowTooltipAction; showPolygonTooltipAction: ShowTooltipAction;
autoClosePolygonTooltip: boolean; autoClosePolygonTooltip: boolean;
usePolygonTooltipFunction: boolean; usePolygonTooltipFunction: boolean;
polygonTooltipPattern?: string; polygonTooltipPattern?: string;
polygonTooltipFunction?: string; polygonTooltipFunction?: TbFunction;
polygonColor?: string; polygonColor?: string;
polygonOpacity?: number; polygonOpacity?: number;
usePolygonColorFunction: boolean; usePolygonColorFunction: boolean;
polygonColorFunction?: string; polygonColorFunction?: TbFunction;
polygonStrokeColor?: string; polygonStrokeColor?: string;
polygonStrokeOpacity?: number; polygonStrokeOpacity?: number;
polygonStrokeWeight?: number; polygonStrokeWeight?: number;
usePolygonStrokeColorFunction: boolean; usePolygonStrokeColorFunction: boolean;
polygonStrokeColorFunction?: string; polygonStrokeColorFunction?: TbFunction;
} }
export interface WidgetPolygonSettings extends PolygonSettings, WidgetToolipSettings { export interface WidgetPolygonSettings extends PolygonSettings, WidgetToolipSettings {
parsedPolygonLabelFunction: GenericFunction; parsedPolygonLabelFunction: CompiledTbFunction<GenericFunction>;
parsedPolygonTooltipFunction: GenericFunction; parsedPolygonTooltipFunction: CompiledTbFunction<GenericFunction>;
parsedPolygonColorFunction: GenericFunction; parsedPolygonColorFunction: CompiledTbFunction<GenericFunction>;
parsedPolygonStrokeColorFunction: GenericFunction; parsedPolygonStrokeColorFunction: CompiledTbFunction<GenericFunction>;
polygonClick: { [name: string]: actionsHandler }; polygonClick: { [name: string]: actionsHandler };
} }
@ -464,29 +464,29 @@ export interface CircleSettings {
showCircleLabel: boolean; showCircleLabel: boolean;
useCircleLabelFunction: boolean; useCircleLabelFunction: boolean;
circleLabel?: string; circleLabel?: string;
circleLabelFunction?: string; circleLabelFunction?: TbFunction;
showCircleTooltip: boolean; showCircleTooltip: boolean;
showCircleTooltipAction: ShowTooltipAction; showCircleTooltipAction: ShowTooltipAction;
autoCloseCircleTooltip: boolean; autoCloseCircleTooltip: boolean;
useCircleTooltipFunction: boolean; useCircleTooltipFunction: boolean;
circleTooltipPattern?: string; circleTooltipPattern?: string;
circleTooltipFunction?: string; circleTooltipFunction?: TbFunction;
circleFillColor?: string; circleFillColor?: string;
circleFillColorOpacity?: number; circleFillColorOpacity?: number;
useCircleFillColorFunction: boolean; useCircleFillColorFunction: boolean;
circleFillColorFunction?: string; circleFillColorFunction?: TbFunction;
circleStrokeColor?: string; circleStrokeColor?: string;
circleStrokeOpacity?: number; circleStrokeOpacity?: number;
circleStrokeWeight?: number; circleStrokeWeight?: number;
useCircleStrokeColorFunction: boolean; useCircleStrokeColorFunction: boolean;
circleStrokeColorFunction?: string; circleStrokeColorFunction?: TbFunction;
} }
export interface WidgetCircleSettings extends CircleSettings, WidgetToolipSettings { export interface WidgetCircleSettings extends CircleSettings, WidgetToolipSettings {
parsedCircleLabelFunction: GenericFunction; parsedCircleLabelFunction: CompiledTbFunction<GenericFunction>;
parsedCircleTooltipFunction: GenericFunction; parsedCircleTooltipFunction: CompiledTbFunction<GenericFunction>;
parsedCircleFillColorFunction: GenericFunction; parsedCircleFillColorFunction: CompiledTbFunction<GenericFunction>;
parsedCircleStrokeColorFunction: GenericFunction; parsedCircleStrokeColorFunction: CompiledTbFunction<GenericFunction>;
circleClick: { [name: string]: actionsHandler }; circleClick: { [name: string]: actionsHandler };
} }
@ -530,13 +530,13 @@ export const polylineDecoratorSymbolTranslationMap = new Map<PolylineDecoratorSy
export interface PolylineSettings { export interface PolylineSettings {
useStrokeWeightFunction?: boolean; useStrokeWeightFunction?: boolean;
strokeWeight: number; strokeWeight: number;
strokeWeightFunction?: string; strokeWeightFunction?: TbFunction;
useStrokeOpacityFunction?: boolean; useStrokeOpacityFunction?: boolean;
strokeOpacity: number; strokeOpacity: number;
strokeOpacityFunction?: string; strokeOpacityFunction?: TbFunction;
useColorFunction?: boolean; useColorFunction?: boolean;
color?: string; color?: string;
colorFunction?: string; colorFunction?: TbFunction;
usePolylineDecorator?: boolean; usePolylineDecorator?: boolean;
decoratorSymbol?: PolylineDecoratorSymbol; decoratorSymbol?: PolylineDecoratorSymbol;
decoratorSymbolSize?: number; decoratorSymbolSize?: number;
@ -548,9 +548,9 @@ export interface PolylineSettings {
} }
export interface WidgetPolylineSettings extends PolylineSettings { export interface WidgetPolylineSettings extends PolylineSettings {
parsedColorFunction: GenericFunction; parsedColorFunction: CompiledTbFunction<GenericFunction>;
parsedStrokeOpacityFunction: GenericFunction; parsedStrokeOpacityFunction: CompiledTbFunction<GenericFunction>;
parsedStrokeWeightFunction: GenericFunction; parsedStrokeWeightFunction: CompiledTbFunction<GenericFunction>;
} }
export const defaultRouteMapSettings: PolylineSettings = { export const defaultRouteMapSettings: PolylineSettings = {
@ -578,7 +578,7 @@ export interface PointsSettings {
showPoints?: boolean; showPoints?: boolean;
pointColor?: string; pointColor?: string;
useColorPointFunction?: false; useColorPointFunction?: false;
colorPointFunction?: string; colorPointFunction?: TbFunction;
pointSize?: number; pointSize?: number;
usePointAsAnchor?: false; usePointAsAnchor?: false;
pointAsAnchorFunction?: string; pointAsAnchorFunction?: string;
@ -586,8 +586,8 @@ export interface PointsSettings {
} }
export interface WidgetPointsSettings extends PointsSettings { export interface WidgetPointsSettings extends PointsSettings {
parsedColorPointFunction: GenericFunction; parsedColorPointFunction: CompiledTbFunction<GenericFunction>;
parsedPointAsAnchorFunction: GenericFunction; parsedPointAsAnchorFunction: CompiledTbFunction<GenericFunction>;
} }
export const defaultTripAnimationPointSettings: PointsSettings = { export const defaultTripAnimationPointSettings: PointsSettings = {
@ -612,11 +612,11 @@ export interface MarkerClusteringSettings {
chunkedLoading: boolean; chunkedLoading: boolean;
removeOutsideVisibleBounds: boolean; removeOutsideVisibleBounds: boolean;
useIconCreateFunction: boolean; useIconCreateFunction: boolean;
clusterMarkerFunction?: string; clusterMarkerFunction?: TbFunction;
} }
export interface WidgetMarkerClusteringSettings extends MarkerClusteringSettings { export interface WidgetMarkerClusteringSettings extends MarkerClusteringSettings {
parsedClusterMarkerFunction?: GenericFunction; parsedClusterMarkerFunction?: CompiledTbFunction<GenericFunction>;
} }
export const defaultMarkerClusteringSettings: MarkerClusteringSettings = { export const defaultMarkerClusteringSettings: MarkerClusteringSettings = {

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

@ -30,9 +30,9 @@ import { TranslateService } from '@ngx-translate/core';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { EntityDataPageLink } from '@shared/models/query/query.models'; import { EntityDataPageLink } from '@shared/models/query/query.models';
import { providerClass } from '@home/components/widget/lib/maps/providers/public-api'; import { providerClass } from '@home/components/widget/lib/maps/providers/public-api';
import { isDefined, isDefinedAndNotNull, parseFunction } from '@core/utils'; import { isDefined, isDefinedAndNotNull, parseFunction, parseTbFunction } from '@core/utils';
import L from 'leaflet'; import L from 'leaflet';
import { forkJoin, Observable, of } from 'rxjs'; import { firstValueFrom, forkJoin, from, Observable, of } from 'rxjs';
import { AttributeService } from '@core/http/attribute.service'; import { AttributeService } from '@core/http/attribute.service';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models'; import { AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models';
@ -40,12 +40,18 @@ import { AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/tel
// @dynamic // @dynamic
export class MapWidgetController implements MapWidgetInterface { export class MapWidgetController implements MapWidgetInterface {
private updatePending = false;
private latestUpdatePending = false;
private resizePending = false;
private destroyed = false;
constructor( constructor(
public mapProvider: MapProviders, public mapProvider: MapProviders,
private drawRoutes: boolean, private drawRoutes: boolean,
public ctx: WidgetContext, public ctx: WidgetContext,
$element: HTMLElement, $element: HTMLElement,
isEdit?: boolean isEdit = false,
mapLoaded?: (map: LeafletMap) => void
) { ) {
if (this.map) { if (this.map) {
this.map.map.remove(); this.map.map.remove();
@ -56,37 +62,53 @@ export class MapWidgetController implements MapWidgetInterface {
if (!$element) { if (!$element) {
$element = ctx.$container[0]; $element = ctx.$container[0];
} }
this.settings = this.initSettings(ctx.settings, isEdit); from(this.initSettings(ctx.settings, isEdit)).subscribe(settings => {
this.settings.tooltipAction = this.getDescriptors('tooltipAction'); if (!this.destroyed) {
this.settings.markerClick = this.getDescriptors('markerClick'); this.settings = settings;
this.settings.polygonClick = this.getDescriptors('polygonClick'); this.settings.tooltipAction = this.getDescriptors('tooltipAction');
this.settings.circleClick = this.getDescriptors('circleClick'); this.settings.markerClick = this.getDescriptors('markerClick');
this.settings.polygonClick = this.getDescriptors('polygonClick');
this.settings.circleClick = this.getDescriptors('circleClick');
const MapClass = providerClass[this.provider]; const MapClass = providerClass[this.provider];
if (!MapClass) { if (!MapClass) {
return; return;
} }
parseWithTranslation.setTranslate(this.translate); parseWithTranslation.setTranslate(this.translate);
this.map = new MapClass(this.ctx, $element, this.settings); this.map = new MapClass(this.ctx, $element, this.settings);
(this.ctx as any).mapInstance = this.map; (this.ctx as any).mapInstance = this.map;
this.map.saveMarkerLocation = this.setMarkerLocation.bind(this); this.map.saveMarkerLocation = this.setMarkerLocation.bind(this);
this.map.savePolygonLocation = this.savePolygonLocation.bind(this); this.map.savePolygonLocation = this.savePolygonLocation.bind(this);
this.map.saveLocation = this.saveLocation.bind(this); this.map.saveLocation = this.saveLocation.bind(this);
let pageSize = this.settings.mapPageSize; let pageSize = this.settings.mapPageSize;
if (isDefinedAndNotNull(this.ctx.widgetConfig.pageSize)) { if (isDefinedAndNotNull(this.ctx.widgetConfig.pageSize)) {
pageSize = Math.max(pageSize, this.ctx.widgetConfig.pageSize); pageSize = Math.max(pageSize, this.ctx.widgetConfig.pageSize);
} }
this.pageLink = { this.pageLink = {
page: 0, page: 0,
pageSize, pageSize,
textSearch: null, textSearch: null,
dynamic: true dynamic: true
}; };
this.map.setLoading(true); this.map.setLoading(true);
this.ctx.defaultSubscription.paginatedDataSubscriptionUpdated.subscribe(() => { this.ctx.defaultSubscription.paginatedDataSubscriptionUpdated.subscribe(() => {
this.map.resetState(); this.map.resetState();
});
this.ctx.defaultSubscription.subscribeAllForPaginatedData(this.pageLink, null);
if (this.updatePending) {
this.update();
}
if (this.latestUpdatePending) {
this.latestDataUpdate();
}
if (this.resizePending) {
this.resize();
}
if (mapLoaded) {
mapLoaded(this.map);
}
}
}); });
this.ctx.defaultSubscription.subscribeAllForPaginatedData(this.pageLink, null);
} }
map: LeafletMap; map: LeafletMap;
@ -233,27 +255,27 @@ export class MapWidgetController implements MapWidgetInterface {
} }
} }
initSettings(settings: UnitedMapSettings, isEditMap?: boolean): WidgetUnitedMapSettings { async initSettings(settings: UnitedMapSettings, isEditMap?: boolean): Promise<WidgetUnitedMapSettings> {
const functionParams = ['data', 'dsData', 'dsIndex']; const functionParams = ['data', 'dsData', 'dsIndex'];
this.provider = settings.provider || this.mapProvider; this.provider = settings.provider || this.mapProvider;
const parsedOptions: Partial<WidgetUnitedMapSettings> = { const parsedOptions: Partial<WidgetUnitedMapSettings> = {
provider: this.provider, provider: this.provider,
parsedLabelFunction: parseFunction(settings.labelFunction, functionParams), parsedLabelFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.labelFunction, functionParams)),
parsedTooltipFunction: parseFunction(settings.tooltipFunction, functionParams), parsedTooltipFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.tooltipFunction, functionParams)),
parsedColorFunction: parseFunction(settings.colorFunction, functionParams), parsedColorFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.colorFunction, functionParams)),
parsedColorPointFunction: parseFunction(settings.colorPointFunction, functionParams), parsedColorPointFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.colorPointFunction, functionParams)),
parsedStrokeOpacityFunction: parseFunction(settings.strokeOpacityFunction, functionParams), parsedStrokeOpacityFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.strokeOpacityFunction, functionParams)),
parsedStrokeWeightFunction: parseFunction(settings.strokeWeightFunction, functionParams), parsedStrokeWeightFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.strokeWeightFunction, functionParams)),
parsedPolygonLabelFunction: parseFunction(settings.polygonLabelFunction, functionParams), parsedPolygonLabelFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.polygonLabelFunction, functionParams)),
parsedPolygonColorFunction: parseFunction(settings.polygonColorFunction, functionParams), parsedPolygonColorFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.polygonColorFunction, functionParams)),
parsedPolygonStrokeColorFunction: parseFunction(settings.polygonStrokeColorFunction, functionParams), parsedPolygonStrokeColorFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.polygonStrokeColorFunction, functionParams)),
parsedPolygonTooltipFunction: parseFunction(settings.polygonTooltipFunction, functionParams), parsedPolygonTooltipFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.polygonTooltipFunction, functionParams)),
parsedCircleLabelFunction: parseFunction(settings.circleLabelFunction, functionParams), parsedCircleLabelFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.circleLabelFunction, functionParams)),
parsedCircleStrokeColorFunction: parseFunction(settings.circleStrokeColorFunction, functionParams), parsedCircleStrokeColorFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.circleStrokeColorFunction, functionParams)),
parsedCircleFillColorFunction: parseFunction(settings.circleFillColorFunction, functionParams), parsedCircleFillColorFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.circleFillColorFunction, functionParams)),
parsedCircleTooltipFunction: parseFunction(settings.circleTooltipFunction, functionParams), parsedCircleTooltipFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.circleTooltipFunction, functionParams)),
parsedMarkerImageFunction: parseFunction(settings.markerImageFunction, ['data', 'images', 'dsData', 'dsIndex']), parsedMarkerImageFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.markerImageFunction, ['data', 'images', 'dsData', 'dsIndex'])),
parsedClusterMarkerFunction: parseFunction(settings.clusterMarkerFunction, ['data', 'childCount']), parsedClusterMarkerFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.clusterMarkerFunction, ['data', 'childCount'])),
// labelColor: this.ctx.widgetConfig.color, // labelColor: this.ctx.widgetConfig.color,
// polygonLabelColor: this.ctx.widgetConfig.color, // polygonLabelColor: this.ctx.widgetConfig.color,
polygonKeyName: (settings as any).polKeyName ? (settings as any).polKeyName : settings.polygonKeyName, polygonKeyName: (settings as any).polKeyName ? (settings as any).polKeyName : settings.polygonKeyName,
@ -277,20 +299,36 @@ export class MapWidgetController implements MapWidgetInterface {
} }
update() { update() {
if (this.map) {
this.updatePending = false;
this.map.updateData(this.drawRoutes); this.map.updateData(this.drawRoutes);
this.map.setLoading(false); this.map.setLoading(false);
} else {
this.updatePending = true;
}
} }
latestDataUpdate() { latestDataUpdate() {
this.map.updateData(this.drawRoutes); if (this.map) {
this.latestUpdatePending = false;
this.map.updateData(this.drawRoutes);
} else {
this.latestUpdatePending = true;
}
} }
resize() { resize() {
this.map.onResize(); if (this.map) {
this.map?.invalidateSize(); this.resizePending = false;
this.map.onResize();
this.map.invalidateSize();
} else {
this.resizePending = true;
}
} }
destroy() { destroy() {
this.destroyed = true;
if (this.map) { if (this.map) {
this.map.remove(); this.map.remove();
} }

12
ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts

@ -15,13 +15,11 @@
/// ///
import L from 'leaflet'; import L from 'leaflet';
import { import { GenericFunction, ShowTooltipAction, WidgetToolipSettings } from './map-models';
GenericFunction,
ShowTooltipAction, WidgetToolipSettings
} from './map-models';
import { Datasource, FormattedData } from '@app/shared/models/widget.models'; import { Datasource, FormattedData } from '@app/shared/models/widget.models';
import { fillDataPattern, isDefinedAndNotNull, isString, processDataPattern, safeExecute } from '@core/utils'; import { fillDataPattern, isDefinedAndNotNull, isString, processDataPattern, safeExecuteTbFunction } from '@core/utils';
import { parseWithTranslation } from '@home/components/widget/lib/maps/common-maps-utils'; import { parseWithTranslation } from '@home/components/widget/lib/maps/common-maps-utils';
import { CompiledTbFunction } from '@shared/models/js-function.models';
export function createTooltip(target: L.Layer, export function createTooltip(target: L.Layer,
settings: Partial<WidgetToolipSettings>, settings: Partial<WidgetToolipSettings>,
@ -90,7 +88,7 @@ export function isJSON(data: string): boolean {
export interface LabelSettings { export interface LabelSettings {
showLabel: boolean; showLabel: boolean;
useLabelFunction: boolean; useLabelFunction: boolean;
parsedLabelFunction: GenericFunction; parsedLabelFunction: CompiledTbFunction<GenericFunction>;
label: string; label: string;
} }
@ -98,7 +96,7 @@ export function entitiesParseName(entities: FormattedData[], labelSettings: Labe
const div = document.createElement('div'); const div = document.createElement('div');
for (const entity of entities) { for (const entity of entities) {
if (labelSettings?.showLabel) { if (labelSettings?.showLabel) {
const pattern = labelSettings.useLabelFunction ? safeExecute(labelSettings.parsedLabelFunction, const pattern = labelSettings.useLabelFunction ? safeExecuteTbFunction(labelSettings.parsedLabelFunction,
[entity, entities, entity.dsIndex]) : labelSettings.label; [entity, entities, entity.dsIndex]) : labelSettings.label;
const markerLabelText = parseWithTranslation.prepareProcessPattern(pattern, true); const markerLabelText = parseWithTranslation.prepareProcessPattern(pattern, true);
const replaceInfoLabelMarker = processDataPattern(pattern, entity); const replaceInfoLabelMarker = processDataPattern(pattern, entity);

16
ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts

@ -19,7 +19,13 @@ import { MarkerIconInfo, MarkerIconReadyFunction, MarkerImageInfo, WidgetMarkers
import { bindPopupActions, createTooltip } from './maps-utils'; import { bindPopupActions, createTooltip } from './maps-utils';
import { loadImageWithAspect, parseWithTranslation } from './common-maps-utils'; import { loadImageWithAspect, parseWithTranslation } from './common-maps-utils';
import tinycolor from 'tinycolor2'; import tinycolor from 'tinycolor2';
import { fillDataPattern, isDefined, isDefinedAndNotNull, processDataPattern, safeExecute } from '@core/utils'; import {
fillDataPattern,
isDefined,
isDefinedAndNotNull,
processDataPattern,
safeExecuteTbFunction
} from '@core/utils';
import LeafletMap from './leaflet-map'; import LeafletMap from './leaflet-map';
import { FormattedData } from '@shared/models/widget.models'; import { FormattedData } from '@shared/models/widget.models';
import { ImagePipe } from '@shared/pipe/image.pipe'; import { ImagePipe } from '@shared/pipe/image.pipe';
@ -101,7 +107,7 @@ export class Marker {
updateMarkerTooltip(data: FormattedData) { updateMarkerTooltip(data: FormattedData) {
if (!this.map.markerTooltipText || this.settings.useTooltipFunction) { if (!this.map.markerTooltipText || this.settings.useTooltipFunction) {
const pattern = this.settings.useTooltipFunction ? const pattern = this.settings.useTooltipFunction ?
safeExecute(this.settings.parsedTooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) : this.settings.tooltipPattern; safeExecuteTbFunction(this.settings.parsedTooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) : this.settings.tooltipPattern;
this.map.markerTooltipText = parseWithTranslation.prepareProcessPattern(pattern, true); this.map.markerTooltipText = parseWithTranslation.prepareProcessPattern(pattern, true);
this.map.replaceInfoTooltipMarker = processDataPattern(this.map.markerTooltipText, data); this.map.replaceInfoTooltipMarker = processDataPattern(this.map.markerTooltipText, data);
} }
@ -123,7 +129,7 @@ export class Marker {
if (settings.showLabel) { if (settings.showLabel) {
if (!this.map.markerLabelText || settings.useLabelFunction) { if (!this.map.markerLabelText || settings.useLabelFunction) {
const pattern = settings.useLabelFunction ? const pattern = settings.useLabelFunction ?
safeExecute(settings.parsedLabelFunction, [this.data, this.dataSources, this.data.dsIndex]) : settings.label; safeExecuteTbFunction(settings.parsedLabelFunction, [this.data, this.dataSources, this.data.dsIndex]) : settings.label;
this.map.markerLabelText = parseWithTranslation.prepareProcessPattern(pattern, true); this.map.markerLabelText = parseWithTranslation.prepareProcessPattern(pattern, true);
this.map.replaceInfoLabelMarker = processDataPattern(this.map.markerLabelText, this.data); this.map.replaceInfoLabelMarker = processDataPattern(this.map.markerLabelText, this.data);
} }
@ -165,11 +171,11 @@ export class Marker {
return; return;
} }
const currentImage: MarkerImageInfo = this.settings.useMarkerImageFunction ? const currentImage: MarkerImageInfo = this.settings.useMarkerImageFunction ?
safeExecute(this.settings.parsedMarkerImageFunction, safeExecuteTbFunction(this.settings.parsedMarkerImageFunction,
[this.data, this.settings.markerImages, this.dataSources, this.data.dsIndex]) : this.settings.currentImage; [this.data, this.settings.markerImages, this.dataSources, this.data.dsIndex]) : this.settings.currentImage;
let currentColor = this.settings.tinyColor; let currentColor = this.settings.tinyColor;
if (this.settings.useColorFunction) { if (this.settings.useColorFunction) {
const functionColor = safeExecute(this.settings.parsedColorFunction, const functionColor = safeExecuteTbFunction(this.settings.parsedColorFunction,
[this.data, this.dataSources, this.data.dsIndex]); [this.data, this.dataSources, this.data.dsIndex]);
if (isDefinedAndNotNull(functionColor)) { if (isDefinedAndNotNull(functionColor)) {
currentColor = tinycolor(functionColor); currentColor = tinycolor(functionColor);

11
ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts

@ -16,13 +16,10 @@
import L, { LatLngExpression, LeafletMouseEvent } from 'leaflet'; import L, { LatLngExpression, LeafletMouseEvent } from 'leaflet';
import { createTooltip, isCutPolygon } from './maps-utils'; import { createTooltip, isCutPolygon } from './maps-utils';
import { import { functionValueCalculator, parseWithTranslation } from './common-maps-utils';
functionValueCalculator,
parseWithTranslation
} from './common-maps-utils';
import { WidgetPolygonSettings } from './map-models'; import { WidgetPolygonSettings } from './map-models';
import { FormattedData } from '@shared/models/widget.models'; import { FormattedData } from '@shared/models/widget.models';
import { fillDataPattern, processDataPattern, safeExecute } from '@core/utils'; import { fillDataPattern, processDataPattern, safeExecuteTbFunction } from '@core/utils';
import LeafletMap from '@home/components/widget/lib/maps/leaflet-map'; import LeafletMap from '@home/components/widget/lib/maps/leaflet-map';
export class Polygon { export class Polygon {
@ -92,7 +89,7 @@ export class Polygon {
updateTooltip(data: FormattedData) { updateTooltip(data: FormattedData) {
const pattern = this.settings.usePolygonTooltipFunction ? const pattern = this.settings.usePolygonTooltipFunction ?
safeExecute(this.settings.parsedPolygonTooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) : safeExecuteTbFunction(this.settings.parsedPolygonTooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) :
this.settings.polygonTooltipPattern; this.settings.polygonTooltipPattern;
this.tooltip.setContent(parseWithTranslation.parseTemplate(pattern, data, true)); this.tooltip.setContent(parseWithTranslation.parseTemplate(pattern, data, true));
} }
@ -102,7 +99,7 @@ export class Polygon {
if (settings.showPolygonLabel) { if (settings.showPolygonLabel) {
if (!this.map.polygonLabelText || settings.usePolygonLabelFunction) { if (!this.map.polygonLabelText || settings.usePolygonLabelFunction) {
const pattern = settings.usePolygonLabelFunction ? const pattern = settings.usePolygonLabelFunction ?
safeExecute(settings.parsedPolygonLabelFunction, safeExecuteTbFunction(settings.parsedPolygonLabelFunction,
[this.data, this.dataSources, this.data.dsIndex]) : settings.polygonLabel; [this.data, this.dataSources, this.data.dsIndex]) : settings.polygonLabel;
this.map.polygonLabelText = parseWithTranslation.prepareProcessPattern(pattern, true); this.map.polygonLabelText = parseWithTranslation.prepareProcessPattern(pattern, true);
this.map.replaceInfoLabelPolygon = processDataPattern(this.map.polygonLabelText, this.data); this.map.replaceInfoLabelPolygon = processDataPattern(this.map.polygonLabelText, this.data);

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

@ -23,16 +23,17 @@ import {
PosFunction, PosFunction,
WidgetUnitedMapSettings WidgetUnitedMapSettings
} from '../map-models'; } from '../map-models';
import { Observable, of, ReplaySubject, switchMap } from 'rxjs'; import { forkJoin, Observable, of, ReplaySubject, switchMap } from 'rxjs';
import { catchError } from 'rxjs/operators'; import { catchError } from 'rxjs/operators';
import { calculateNewPointCoordinate, loadImageWithAspect } from '@home/components/widget/lib/maps/common-maps-utils'; import { calculateNewPointCoordinate, loadImageWithAspect } from '@home/components/widget/lib/maps/common-maps-utils';
import { WidgetContext } from '@home/models/widget-component.models'; import { WidgetContext } from '@home/models/widget-component.models';
import { DataSet, DatasourceType, FormattedData, widgetType } from '@shared/models/widget.models'; import { DataSet, DatasourceType, FormattedData, widgetType } from '@shared/models/widget.models';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { WidgetSubscriptionOptions } from '@core/api/widget-api.models'; import { WidgetSubscriptionOptions } from '@core/api/widget-api.models';
import { isDefinedAndNotNull, isEmptyStr, isNotEmptyStr, parseFunction } from '@core/utils'; import { isDefinedAndNotNull, isEmptyStr, isNotEmptyStr, parseFunction, parseTbFunction } from '@core/utils';
import { EntityDataPageLink } from '@shared/models/query/query.models'; import { EntityDataPageLink } from '@shared/models/query/query.models';
import { ImagePipe } from '@shared/pipe/image.pipe'; import { ImagePipe } from '@shared/pipe/image.pipe';
import { CompiledTbFunction } from '@shared/models/js-function.models';
const maxZoom = 4; // ? const maxZoom = 4; // ?
@ -43,13 +44,20 @@ export class ImageMap extends LeafletMap {
width = 0; width = 0;
height = 0; height = 0;
imageUrl: string; imageUrl: string;
posFunction: PosFunction; posFunction: CompiledTbFunction<PosFunction>;
constructor(ctx: WidgetContext, $container: HTMLElement, options: WidgetUnitedMapSettings) { constructor(ctx: WidgetContext, $container: HTMLElement, options: WidgetUnitedMapSettings) {
super(ctx, $container, options); super(ctx, $container, options);
this.posFunction = parseFunction(options.posFunction,
['origXPos', 'origYPos', 'data', 'dsData', 'dsIndex', 'aspect']) as PosFunction; const initData = {
this.mapImage(options).subscribe((mapImage) => { posFunction: parseTbFunction<PosFunction>(this.ctx.http, options.posFunction,
['origXPos', 'origYPos', 'data', 'dsData', 'dsIndex', 'aspect']),
mapImage: this.mapImage(options)
};
forkJoin(initData).subscribe(inited => {
this.posFunction = inited.posFunction;
const mapImage = inited.mapImage;
this.imageUrl = mapImage.imageUrl; this.imageUrl = mapImage.imageUrl;
this.aspect = mapImage.aspect; this.aspect = mapImage.aspect;
if (mapImage.update) { if (mapImage.update) {
@ -272,7 +280,7 @@ export class ImageMap extends LeafletMap {
convertPosition(data: FormattedData, dsData: FormattedData[]): L.LatLng { convertPosition(data: FormattedData, dsData: FormattedData[]): L.LatLng {
const position = this.extractPosition(data); const position = this.extractPosition(data);
if (position) { if (position) {
const converted = this.posFunction(position.x, position.y, data, dsData, data.dsIndex, this.aspect) || {x: 0, y: 0}; const converted = this.posFunction.execute(position.x, position.y, data, dsData, data.dsIndex, this.aspect) || {x: 0, y: 0};
return this.positionToLatLng(converted); return this.positionToLatLng(converted);
} else { } else {
return null; return null;

4
ui-ngx/src/app/modules/home/components/widget/lib/settings/map/circle-settings.component.html

@ -62,6 +62,7 @@
</tb-html> </tb-html>
<tb-js-func [class.!hidden]="!circleSettingsFormGroup.get('useCircleLabelFunction').value" <tb-js-func [class.!hidden]="!circleSettingsFormGroup.get('useCircleLabelFunction').value"
formControlName="circleLabelFunction" formControlName="circleLabelFunction"
withModules
[globalVariables]="functionScopeVariables" [globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']" [functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.circle-label-function' | translate }}" functionTitle="{{ 'widgets.maps.circle-label-function' | translate }}"
@ -105,6 +106,7 @@
</tb-html> </tb-html>
<tb-js-func [class.!hidden]="!circleSettingsFormGroup.get('useCircleTooltipFunction').value" <tb-js-func [class.!hidden]="!circleSettingsFormGroup.get('useCircleTooltipFunction').value"
formControlName="circleTooltipFunction" formControlName="circleTooltipFunction"
withModules
[globalVariables]="functionScopeVariables" [globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']" [functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.circle-tooltip-function' | translate }}" functionTitle="{{ 'widgets.maps.circle-tooltip-function' | translate }}"
@ -140,6 +142,7 @@
</mat-expansion-panel-header> </mat-expansion-panel-header>
<ng-template matExpansionPanelContent> <ng-template matExpansionPanelContent>
<tb-js-func formControlName="circleFillColorFunction" <tb-js-func formControlName="circleFillColorFunction"
withModules
[globalVariables]="functionScopeVariables" [globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']" [functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.circle-fill-color-function' | translate }}" functionTitle="{{ 'widgets.maps.circle-fill-color-function' | translate }}"
@ -179,6 +182,7 @@
</mat-expansion-panel-header> </mat-expansion-panel-header>
<ng-template matExpansionPanelContent> <ng-template matExpansionPanelContent>
<tb-js-func formControlName="circleStrokeColorFunction" <tb-js-func formControlName="circleStrokeColorFunction"
withModules
[globalVariables]="functionScopeVariables" [globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']" [functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.circle-stroke-color-function' | translate }}" functionTitle="{{ 'widgets.maps.circle-stroke-color-function' | translate }}"

1
ui-ngx/src/app/modules/home/components/widget/lib/settings/map/marker-clustering-settings.component.html

@ -78,6 +78,7 @@
</mat-expansion-panel-header> </mat-expansion-panel-header>
<ng-template matExpansionPanelContent> <ng-template matExpansionPanelContent>
<tb-js-func formControlName="clusterMarkerFunction" <tb-js-func formControlName="clusterMarkerFunction"
withModules
[globalVariables]="functionScopeVariables" [globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'childCount']" [functionArgs]="['data', 'childCount']"
functionTitle="{{ 'widgets.maps.marker-color-function' | translate }}" functionTitle="{{ 'widgets.maps.marker-color-function' | translate }}"

5
ui-ngx/src/app/modules/home/components/widget/lib/settings/map/markers-settings.component.html

@ -30,6 +30,7 @@
</section> </section>
<tb-js-func [class.!hidden]="provider !== mapProvider.image" <tb-js-func [class.!hidden]="provider !== mapProvider.image"
formControlName="posFunction" formControlName="posFunction"
withModules
minHeight="100px" minHeight="100px"
[globalVariables]="functionScopeVariables" [globalVariables]="functionScopeVariables"
[functionArgs]="['origXPos', 'origYPos', 'data', 'dsData', 'dsIndex', 'aspect']" [functionArgs]="['origXPos', 'origYPos', 'data', 'dsData', 'dsIndex', 'aspect']"
@ -63,6 +64,7 @@
</tb-html> </tb-html>
<tb-js-func [class.!hidden]="!markersSettingsFormGroup.get('useLabelFunction').value" <tb-js-func [class.!hidden]="!markersSettingsFormGroup.get('useLabelFunction').value"
formControlName="labelFunction" formControlName="labelFunction"
withModules
[globalVariables]="functionScopeVariables" [globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']" [functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.label-function' | translate }}" functionTitle="{{ 'widgets.maps.label-function' | translate }}"
@ -106,6 +108,7 @@
</tb-html> </tb-html>
<tb-js-func [class.!hidden]="!markersSettingsFormGroup.get('useTooltipFunction').value" <tb-js-func [class.!hidden]="!markersSettingsFormGroup.get('useTooltipFunction').value"
formControlName="tooltipFunction" formControlName="tooltipFunction"
withModules
[globalVariables]="functionScopeVariables" [globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']" [functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.tooltip-function' | translate }}" functionTitle="{{ 'widgets.maps.tooltip-function' | translate }}"
@ -144,6 +147,7 @@
</mat-expansion-panel-header> </mat-expansion-panel-header>
<ng-template matExpansionPanelContent> <ng-template matExpansionPanelContent>
<tb-js-func formControlName="colorFunction" <tb-js-func formControlName="colorFunction"
withModules
[globalVariables]="functionScopeVariables" [globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']" [functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.color-function' | translate }}" functionTitle="{{ 'widgets.maps.color-function' | translate }}"
@ -177,6 +181,7 @@
</mat-form-field> </mat-form-field>
<tb-js-func [class.!hidden]="!markersSettingsFormGroup.get('useMarkerImageFunction').value" <tb-js-func [class.!hidden]="!markersSettingsFormGroup.get('useMarkerImageFunction').value"
formControlName="markerImageFunction" formControlName="markerImageFunction"
withModules
[globalVariables]="functionScopeVariables" [globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'images', 'dsData', 'dsIndex']" [functionArgs]="['data', 'images', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.marker-image-function' | translate }}" functionTitle="{{ 'widgets.maps.marker-image-function' | translate }}"

4
ui-ngx/src/app/modules/home/components/widget/lib/settings/map/polygon-settings.component.html

@ -62,6 +62,7 @@
</tb-html> </tb-html>
<tb-js-func [class.!hidden]="!polygonSettingsFormGroup.get('usePolygonLabelFunction').value" <tb-js-func [class.!hidden]="!polygonSettingsFormGroup.get('usePolygonLabelFunction').value"
formControlName="polygonLabelFunction" formControlName="polygonLabelFunction"
withModules
[globalVariables]="functionScopeVariables" [globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']" [functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.polygon-label-function' | translate }}" functionTitle="{{ 'widgets.maps.polygon-label-function' | translate }}"
@ -105,6 +106,7 @@
</tb-html> </tb-html>
<tb-js-func [class.!hidden]="!polygonSettingsFormGroup.get('usePolygonTooltipFunction').value" <tb-js-func [class.!hidden]="!polygonSettingsFormGroup.get('usePolygonTooltipFunction').value"
formControlName="polygonTooltipFunction" formControlName="polygonTooltipFunction"
withModules
[globalVariables]="functionScopeVariables" [globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']" [functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.polygon-tooltip-function' | translate }}" functionTitle="{{ 'widgets.maps.polygon-tooltip-function' | translate }}"
@ -140,6 +142,7 @@
</mat-expansion-panel-header> </mat-expansion-panel-header>
<ng-template matExpansionPanelContent> <ng-template matExpansionPanelContent>
<tb-js-func formControlName="polygonColorFunction" <tb-js-func formControlName="polygonColorFunction"
withModules
[globalVariables]="functionScopeVariables" [globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']" [functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.polygon-color-function' | translate }}" functionTitle="{{ 'widgets.maps.polygon-color-function' | translate }}"
@ -179,6 +182,7 @@
</mat-expansion-panel-header> </mat-expansion-panel-header>
<ng-template matExpansionPanelContent> <ng-template matExpansionPanelContent>
<tb-js-func formControlName="polygonStrokeColorFunction" <tb-js-func formControlName="polygonStrokeColorFunction"
withModules
[globalVariables]="functionScopeVariables" [globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']" [functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.polygon-stroke-color-function' | translate }}" functionTitle="{{ 'widgets.maps.polygon-stroke-color-function' | translate }}"

1
ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-common-settings.component.html

@ -81,6 +81,7 @@
</tb-html> </tb-html>
<tb-js-func [class.!hidden]="!tripAnimationCommonSettingsFormGroup.get('useTooltipFunction').value" <tb-js-func [class.!hidden]="!tripAnimationCommonSettingsFormGroup.get('useTooltipFunction').value"
formControlName="tooltipFunction" formControlName="tooltipFunction"
withModules
[globalVariables]="functionScopeVariables" [globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']" [functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.tooltip-function' | translate }}" functionTitle="{{ 'widgets.maps.tooltip-function' | translate }}"

2
ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-marker-settings.component.html

@ -46,6 +46,7 @@
</tb-html> </tb-html>
<tb-js-func [class.!hidden]="!tripAnimationMarkerSettingsFormGroup.get('useLabelFunction').value" <tb-js-func [class.!hidden]="!tripAnimationMarkerSettingsFormGroup.get('useLabelFunction').value"
formControlName="labelFunction" formControlName="labelFunction"
withModules
[globalVariables]="functionScopeVariables" [globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']" [functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.label-function' | translate }}" functionTitle="{{ 'widgets.maps.label-function' | translate }}"
@ -78,6 +79,7 @@
<input matInput type="number" min="1" formControlName="markerImageSize"> <input matInput type="number" min="1" formControlName="markerImageSize">
</mat-form-field> </mat-form-field>
<tb-js-func [class.!hidden]="!tripAnimationMarkerSettingsFormGroup.get('useMarkerImageFunction').value" <tb-js-func [class.!hidden]="!tripAnimationMarkerSettingsFormGroup.get('useMarkerImageFunction').value"
withModules
formControlName="markerImageFunction" formControlName="markerImageFunction"
[globalVariables]="functionScopeVariables" [globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'images', 'dsData', 'dsIndex']" [functionArgs]="['data', 'images', 'dsData', 'dsIndex']"

1
ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-path-settings.component.html

@ -47,6 +47,7 @@
</mat-expansion-panel-header> </mat-expansion-panel-header>
<ng-template matExpansionPanelContent> <ng-template matExpansionPanelContent>
<tb-js-func formControlName="colorFunction" <tb-js-func formControlName="colorFunction"
withModules
[globalVariables]="functionScopeVariables" [globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']" [functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.path-color-function' | translate }}" functionTitle="{{ 'widgets.maps.path-color-function' | translate }}"

2
ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-point-settings.component.html

@ -55,6 +55,7 @@
</mat-expansion-panel-header> </mat-expansion-panel-header>
<ng-template matExpansionPanelContent> <ng-template matExpansionPanelContent>
<tb-js-func formControlName="colorPointFunction" <tb-js-func formControlName="colorPointFunction"
withModules
[globalVariables]="functionScopeVariables" [globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']" [functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.point-color-function' | translate }}" functionTitle="{{ 'widgets.maps.point-color-function' | translate }}"
@ -75,6 +76,7 @@
</mat-expansion-panel-header> </mat-expansion-panel-header>
<ng-template matExpansionPanelContent> <ng-template matExpansionPanelContent>
<tb-js-func formControlName="pointAsAnchorFunction" <tb-js-func formControlName="pointAsAnchorFunction"
withModules
[globalVariables]="functionScopeVariables" [globalVariables]="functionScopeVariables"
[functionArgs]="['data', 'dsData', 'dsIndex']" [functionArgs]="['data', 'dsData', 'dsIndex']"
functionTitle="{{ 'widgets.maps.point-as-anchor-function' | translate }}" functionTitle="{{ 'widgets.maps.point-as-anchor-function' | translate }}"

123
ui-ngx/src/app/modules/home/components/widget/lib/trip-animation/trip-animation.component.ts

@ -48,10 +48,11 @@ import {
isDefined, isDefined,
isUndefined, isUndefined,
mergeFormattedData, mergeFormattedData,
parseFunction, parseTbFunction,
safeExecute safeExecuteTbFunction
} from '@core/utils'; } from '@core/utils';
import { MapWidgetInterface } from '@home/components/widget/lib/maps/map-widget.interface'; import { MapWidgetInterface } from '@home/components/widget/lib/maps/map-widget.interface';
import { firstValueFrom, from } from 'rxjs';
interface DataMap { interface DataMap {
[key: string]: FormattedData; [key: string]: FormattedData;
@ -67,6 +68,10 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
private mapResize$: ResizeObserver; private mapResize$: ResizeObserver;
private initialized = false;
private updatePending = false;
private mapWidgetUpdatePending = false;
constructor(private cd: ChangeDetectorRef, private sanitizer: DomSanitizer) { } constructor(private cd: ChangeDetectorRef, private sanitizer: DomSanitizer) { }
@Input() ctx: WidgetContext; @Input() ctx: WidgetContext;
@ -100,38 +105,33 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
...this.ctx.settings ...this.ctx.settings
}; };
this.useAnchors = this.settings.showPoints && this.settings.usePointAsAnchor; this.useAnchors = this.settings.showPoints && this.settings.usePointAsAnchor;
this.settings.parsedPointAsAnchorFunction = parseFunction(this.settings.pointAsAnchorFunction, ['data', 'dsData', 'dsIndex']);
this.settings.parsedTooltipFunction = parseFunction(this.settings.tooltipFunction, ['data', 'dsData', 'dsIndex']);
this.settings.parsedLabelFunction = parseFunction(this.settings.labelFunction, ['data', 'dsData', 'dsIndex']);
this.settings.parsedColorPointFunction = parseFunction(this.settings.colorPointFunction, ['data', 'dsData', 'dsIndex']);
this.normalizationStep = this.settings.normalizationStep; this.normalizationStep = this.settings.normalizationStep;
const subscription = this.ctx.defaultSubscription; const subscription = this.ctx.defaultSubscription;
subscription.callbacks.onDataUpdated = () => { subscription.callbacks.onDataUpdated = () => {
this.historicalData = formattedDataArrayFromDatasourceData(this.ctx.data).map( this.update();
item => this.clearIncorrectFirsLastDatapoint(item)).filter(arr => arr.length);
this.interpolatedTimeData.length = 0;
this.formattedInterpolatedTimeData.length = 0;
const prevMinTime = this.minTime;
const prevMaxTime = this.maxTime;
this.calculateIntervals();
const currentTime = this.calculateCurrentTime(prevMinTime, prevMaxTime);
if (currentTime !== this.currentTime) {
this.timeUpdated(currentTime);
}
this.mapWidget.map.map?.invalidateSize();
this.mapWidget.map.setLoading(false);
this.cd.detectChanges();
}; };
subscription.callbacks.onLatestDataUpdated = () => { subscription.callbacks.onLatestDataUpdated = () => {
this.formattedLatestData = formattedDataFormDatasourceData(this.ctx.latestData); this.latestDataUpdate();
this.updateCurrentData();
}; };
from(this.initializeFunctions()).subscribe(() => {
this.initialized = true;
if (this.updatePending) {
this.updateCurrentData();
}
});
} }
ngAfterViewInit() { ngAfterViewInit() {
import('@home/components/widget/lib/maps/map-widget2').then( import('@home/components/widget/lib/maps/map-widget2').then(
(mod) => { (mod) => {
this.mapWidget = new mod.MapWidgetController(MapProviders.openstreet, false, this.ctx, this.mapContainer.nativeElement); this.mapWidget = new mod.MapWidgetController(MapProviders.openstreet, false, this.ctx, this.mapContainer.nativeElement, false,
() => {
if (this.mapWidgetUpdatePending) {
this.updateMapWidget();
}
}
);
this.mapResize$ = new ResizeObserver(() => { this.mapResize$ = new ResizeObserver(() => {
this.mapWidget.resize(); this.mapWidget.resize();
}); });
@ -177,22 +177,65 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
this.updateCurrentData(); this.updateCurrentData();
} }
private updateCurrentData() { private async initializeFunctions(): Promise<void> {
let currentPosition = this.formattedCurrentPosition; this.settings.parsedPointAsAnchorFunction = await firstValueFrom(parseTbFunction(this.ctx.http, this.settings.pointAsAnchorFunction, ['data', 'dsData', 'dsIndex']));
if (this.formattedLatestData.length) { this.settings.parsedTooltipFunction = await firstValueFrom(parseTbFunction(this.ctx.http, this.settings.tooltipFunction, ['data', 'dsData', 'dsIndex']));
currentPosition = mergeFormattedData(this.formattedCurrentPosition, this.formattedLatestData); this.settings.parsedLabelFunction = await firstValueFrom(parseTbFunction(this.ctx.http, this.settings.labelFunction, ['data', 'dsData', 'dsIndex']));
this.settings.parsedColorPointFunction = await firstValueFrom(parseTbFunction(this.ctx.http, this.settings.colorPointFunction, ['data', 'dsData', 'dsIndex']));
}
private update() {
this.historicalData = formattedDataArrayFromDatasourceData(this.ctx.data).map(
item => this.clearIncorrectFirsLastDatapoint(item)).filter(arr => arr.length);
this.interpolatedTimeData.length = 0;
this.formattedInterpolatedTimeData.length = 0;
const prevMinTime = this.minTime;
const prevMaxTime = this.maxTime;
this.calculateIntervals();
const currentTime = this.calculateCurrentTime(prevMinTime, prevMaxTime);
if (currentTime !== this.currentTime) {
this.timeUpdated(currentTime);
} }
this.calcLabel(currentPosition); this.updateMapWidget();
this.calcMainTooltip(currentPosition); }
if (this.mapWidget && this.mapWidget.map && this.mapWidget.map.map) {
this.mapWidget.map.updateFromData(true, currentPosition, this.formattedInterpolatedTimeData, (trip) => { private latestDataUpdate() {
this.activeTrip = trip; this.formattedLatestData = formattedDataFormDatasourceData(this.ctx.latestData);
this.timeUpdated(this.currentTime); this.updateCurrentData();
this.cd.markForCheck(); }
});
if (this.settings.showPoints) { private updateMapWidget() {
this.mapWidget.map.updatePoints(this.formattedInterpolatedTimeData, this.calcTooltip); if (this.mapWidget?.map) {
this.mapWidgetUpdatePending = false;
this.mapWidget.map.map?.invalidateSize();
this.mapWidget.map.setLoading(false);
this.cd.detectChanges();
} else {
this.mapWidgetUpdatePending = true;
}
}
private updateCurrentData() {
if (this.initialized) {
this.updatePending = false;
let currentPosition = this.formattedCurrentPosition;
if (this.formattedLatestData.length) {
currentPosition = mergeFormattedData(this.formattedCurrentPosition, this.formattedLatestData);
} }
this.calcLabel(currentPosition);
this.calcMainTooltip(currentPosition);
if (this.mapWidget?.map?.map) {
this.mapWidget.map.updateFromData(true, currentPosition, this.formattedInterpolatedTimeData, (trip) => {
this.activeTrip = trip;
this.timeUpdated(this.currentTime);
this.cd.markForCheck();
});
if (this.settings.showPoints) {
this.mapWidget.map.updatePoints(this.formattedInterpolatedTimeData, this.calcTooltip);
}
}
} else {
this.updatePending = true;
} }
} }
@ -235,7 +278,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
if (this.useAnchors) { if (this.useAnchors) {
const anchorDate = Object.entries(_.union(this.interpolatedTimeData)[0]); const anchorDate = Object.entries(_.union(this.interpolatedTimeData)[0]);
this.anchors = anchorDate this.anchors = anchorDate
.filter((data: [string, FormattedData], tsIndex) => safeExecute(this.settings.parsedPointAsAnchorFunction, [data[1], .filter((data: [string, FormattedData], tsIndex) => safeExecuteTbFunction(this.settings.parsedPointAsAnchorFunction, [data[1],
this.formattedInterpolatedTimeData.map(ds => ds[tsIndex]), data[1].dsIndex])) this.formattedInterpolatedTimeData.map(ds => ds[tsIndex]), data[1].dsIndex]))
.map(data => parseInt(data[0], 10)); .map(data => parseInt(data[0], 10));
} }
@ -244,7 +287,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
calcTooltip = (point: FormattedData, points: FormattedData[]): string => { calcTooltip = (point: FormattedData, points: FormattedData[]): string => {
const data = point ? point : this.activeTrip; const data = point ? point : this.activeTrip;
const tooltipPattern: string = this.settings.useTooltipFunction ? const tooltipPattern: string = this.settings.useTooltipFunction ?
safeExecute(this.settings.parsedTooltipFunction, safeExecuteTbFunction(this.settings.parsedTooltipFunction,
[data, points, point.dsIndex]) : this.settings.tooltipPattern; [data, points, point.dsIndex]) : this.settings.tooltipPattern;
return parseWithTranslation.parseTemplate(tooltipPattern, data, true); return parseWithTranslation.parseTemplate(tooltipPattern, data, true);
} }
@ -261,7 +304,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
if (this.activeTrip) { if (this.activeTrip) {
const data = points[this.activeTrip.dsIndex]; const data = points[this.activeTrip.dsIndex];
const labelText: string = this.settings.useLabelFunction ? const labelText: string = this.settings.useLabelFunction ?
safeExecute(this.settings.parsedLabelFunction, [data, points, data.dsIndex]) : this.settings.label; safeExecuteTbFunction(this.settings.parsedLabelFunction, [data, points, data.dsIndex]) : this.settings.label;
this.label = this.sanitizer.bypassSecurityTrustHtml(parseWithTranslation.parseTemplate(labelText, data, true)); this.label = this.sanitizer.bypassSecurityTrustHtml(parseWithTranslation.parseTemplate(labelText, data, true));
} }
} }

Loading…
Cancel
Save