Commit 3d52420d17b4fbc9ff7e24416ed9e13167756272

Authored by Igor Kulikov
1 parent 3bc534ec

Fixed map functions. Update map helps.

... ... @@ -25,7 +25,7 @@ import {
25 25 SubscriptionTimewindow
26 26 } from '@shared/models/time/time.models';
27 27 import { UtilsService } from '@core/services/utils.service';
28   -import { deepClone, isNumeric } from '@core/utils';
  28 +import { deepClone, isNumber, isNumeric } from '@core/utils';
29 29 import Timeout = NodeJS.Timeout;
30 30
31 31 export declare type onAggregatedData = (data: SubscriptionData, detectChanges: boolean) => void;
... ... @@ -92,20 +92,36 @@ declare type AggFunction = (aggData: AggData, value?: any) => void;
92 92
93 93 const avg: AggFunction = (aggData: AggData, value?: any) => {
94 94 aggData.count++;
95   - aggData.sum += value;
96   - aggData.aggValue = aggData.sum / aggData.count;
  95 + if (isNumber(value)) {
  96 + aggData.sum += value;
  97 + aggData.aggValue = aggData.sum / aggData.count;
  98 + } else {
  99 + aggData.aggValue = value;
  100 + }
97 101 };
98 102
99 103 const min: AggFunction = (aggData: AggData, value?: any) => {
100   - aggData.aggValue = Math.min(aggData.aggValue, value);
  104 + if (isNumber(value)) {
  105 + aggData.aggValue = Math.min(aggData.aggValue, value);
  106 + } else {
  107 + aggData.aggValue = value;
  108 + }
101 109 };
102 110
103 111 const max: AggFunction = (aggData: AggData, value?: any) => {
104   - aggData.aggValue = Math.max(aggData.aggValue, value);
  112 + if (isNumber(value)) {
  113 + aggData.aggValue = Math.max(aggData.aggValue, value);
  114 + } else {
  115 + aggData.aggValue = value;
  116 + }
105 117 };
106 118
107 119 const sum: AggFunction = (aggData: AggData, value?: any) => {
108   - aggData.aggValue = aggData.aggValue + value;
  120 + if (isNumber(value)) {
  121 + aggData.aggValue = aggData.aggValue + value;
  122 + } else {
  123 + aggData.aggValue = value;
  124 + }
109 125 };
110 126
111 127 const count: AggFunction = (aggData: AggData) => {
... ... @@ -409,7 +425,7 @@ export class DataAggregator {
409 425 }
410 426
411 427 private convertValue(val: string): any {
412   - if (!this.noAggregation || val && isNumeric(val) && Number(val).toString() === val) {
  428 + if (val && isNumeric(val) && (!this.noAggregation || this.noAggregation && Number(val).toString() === val)) {
413 429 return Number(val);
414 430 }
415 431 return val;
... ...
... ... @@ -16,7 +16,7 @@
16 16
17 17 import { FormattedData, MapProviders, ReplaceInfo } from '@home/components/widget/lib/maps/map-models';
18 18 import {
19   - createLabelFromDatasource, deepClone,
  19 + createLabelFromDatasource,
20 20 hashCode,
21 21 isDefined,
22 22 isDefinedAndNotNull,
... ... @@ -127,7 +127,6 @@ export function aspectCache(imageUrl: string): Observable<number> {
127 127
128 128 export type TranslateFunc = (key: string, defaultTranslation?: string) => string;
129 129
130   -const varsRegex = /\${([^}]*)}/g;
131 130 const linkActionRegex = /<link-act name=['"]([^['"]*)['"]>([^<]*)<\/link-act>/g;
132 131 const buttonActionRegex = /<button-act name=['"]([^['"]*)['"]>([^<]*)<\/button-act>/g;
133 132
... ... @@ -304,7 +303,8 @@ export const parseWithTranslation = {
304 303 if (this.translateFn) {
305 304 return this.translateFn(key, defaultTranslation);
306 305 } else {
307   - throw console.error('Translate not assigned');
  306 + console.error('Translate not assigned');
  307 + throw Error('Translate not assigned');
308 308 }
309 309 },
310 310 parseTemplate(template: string, data: object, forceTranslate = false): string {
... ... @@ -334,7 +334,7 @@ export function parseData(input: DatasourceData[], dataIndex?: number): Formatte
334 334 if (!obj.hasOwnProperty(el.dataKey.label) || el.data[dataIndex][1] !== '') {
335 335 obj[el.dataKey.label] = el.data[dataIndex][1];
336 336 obj[el.dataKey.label + '|ts'] = el.data[dataIndex][0];
337   - if (el.dataKey.label === 'type') {
  337 + if (el.dataKey.label.toLowerCase() === 'type') {
338 338 obj.deviceType = el.data[dataIndex][1];
339 339 }
340 340 }
... ... @@ -360,28 +360,35 @@ export function flatData(input: FormattedData[]): FormattedData {
360 360 }
361 361
362 362 export function parseArray(input: DatasourceData[]): FormattedData[][] {
363   - return _(input).groupBy(el => el?.datasource?.entityName)
364   - .values().value().map((entityArray) =>
365   - entityArray[0].data.map((el, i) => {
366   - const obj: FormattedData = {
367   - entityName: entityArray[0]?.datasource?.entityName,
368   - entityId: entityArray[0]?.datasource?.entityId,
369   - entityType: entityArray[0]?.datasource?.entityType,
370   - $datasource: entityArray[0]?.datasource,
371   - dsIndex: i,
372   - time: el[0],
373   - deviceType: null
374   - };
375   - entityArray.filter(e => e.data.length && e.data[i]).forEach(entity => {
376   - obj[entity?.dataKey?.label] = entity?.data[i][1];
377   - obj[entity?.dataKey?.label + '|ts'] = entity?.data[0][0];
378   - if (entity?.dataKey?.label === 'type') {
379   - obj.deviceType = entity?.data[0][1];
  363 + return _(input).groupBy(el => el.datasource.entityName)
  364 + .values().value().map((entityArray, dsIndex) => {
  365 + const timeDataMap: {[time: number]: FormattedData} = {};
  366 + entityArray.filter(e => e.data.length).forEach(entity => {
  367 + entity.data.forEach(tsData => {
  368 + const time = tsData[0];
  369 + const value = tsData[1];
  370 + let data = timeDataMap[time];
  371 + if (!data) {
  372 + data = {
  373 + entityName: entity.datasource.entityName,
  374 + entityId: entity.datasource.entityId,
  375 + entityType: entity.datasource.entityType,
  376 + $datasource: entity.datasource,
  377 + dsIndex,
  378 + time,
  379 + deviceType: null
  380 + };
  381 + timeDataMap[time] = data;
  382 + }
  383 + data[entity.dataKey.label] = value;
  384 + data[entity.dataKey.label + '|ts'] = time;
  385 + if (entity.dataKey.label.toLowerCase() === 'type') {
  386 + data.deviceType = value;
380 387 }
381 388 });
382   - return obj;
383   - })
384   - );
  389 + });
  390 + return _.values(timeDataMap);
  391 + });
385 392 }
386 393
387 394 export function parseFunction(source: any, params: string[] = ['def']): (...args: any[]) => any {
... ...
... ... @@ -474,9 +474,10 @@ export default abstract class LeafletMap {
474 474 this.showPolygon = showPolygon;
475 475 if (this.map) {
476 476 const data = this.ctx.data;
477   - const formattedData = parseData(this.ctx.data);
  477 + const formattedData = parseData(data);
478 478 if (drawRoutes) {
479   - this.updatePolylines(parseArray(data), false);
  479 + const polyData = parseArray(data);
  480 + this.updatePolylines(polyData, formattedData, false);
480 481 }
481 482 if (showPolygon) {
482 483 this.updatePolygons(formattedData, false);
... ... @@ -633,7 +634,8 @@ export default abstract class LeafletMap {
633 634 return polygon;
634 635 }
635 636
636   - updatePoints(pointsData: FormattedData[][], getTooltip: (point: FormattedData) => string) {
  637 + updatePoints(pointsData: FormattedData[][],
  638 + getTooltip: (point: FormattedData, points: FormattedData[]) => string) {
637 639 if (pointsData.length) {
638 640 if (this.points) {
639 641 this.map.removeLayer(this.points);
... ... @@ -642,21 +644,25 @@ export default abstract class LeafletMap {
642 644 }
643 645 let pointColor = this.options.pointColor;
644 646 for (const pointsList of pointsData) {
645   - pointsList.filter(pdata => !!this.convertPosition(pdata)).forEach(data => {
646   - if (this.options.useColorPointFunction) {
647   - pointColor = safeExecute(this.options.colorPointFunction, [data, pointsData, data.dsIndex]);
648   - }
649   - const point = L.circleMarker(this.convertPosition(data), {
650   - color: pointColor,
651   - radius: this.options.pointSize
652   - });
653   - if (!this.options.pointTooltipOnRightPanel) {
654   - point.on('click', () => getTooltip(data));
655   - } else {
656   - createTooltip(point, this.options, data.$datasource, getTooltip(data));
  647 + for (let tsIndex = 0; tsIndex < pointsList.length; tsIndex++) {
  648 + const pdata = pointsList[tsIndex];
  649 + if (!!this.convertPosition(pdata)) {
  650 + const dsData = pointsData.map(ds => ds[tsIndex]);
  651 + if (this.options.useColorPointFunction) {
  652 + pointColor = safeExecute(this.options.colorPointFunction, [pdata, dsData, pdata.dsIndex]);
  653 + }
  654 + const point = L.circleMarker(this.convertPosition(pdata), {
  655 + color: pointColor,
  656 + radius: this.options.pointSize
  657 + });
  658 + if (!this.options.pointTooltipOnRightPanel) {
  659 + point.on('click', () => getTooltip(pdata, dsData));
  660 + } else {
  661 + createTooltip(point, this.options, pdata.$datasource, getTooltip(pdata, dsData));
  662 + }
  663 + this.points.addLayer(point);
657 664 }
658   - this.points.addLayer(point);
659   - });
  665 + }
660 666 }
661 667 if (pointsData.length) {
662 668 this.map.addLayer(this.points);
... ... @@ -665,15 +671,15 @@ export default abstract class LeafletMap {
665 671
666 672 // Polyline
667 673
668   - updatePolylines(polyData: FormattedData[][], updateBounds = true, activePolyline?: FormattedData) {
  674 + updatePolylines(polyData: FormattedData[][], dsData: FormattedData[], updateBounds = true) {
669 675 const keys: string[] = [];
670   - polyData.forEach((dataSource: FormattedData[]) => {
671   - const data = activePolyline || dataSource[0];
672   - if (dataSource.length && data.entityName === dataSource[0].entityName) {
  676 + polyData.forEach((tsData: FormattedData[], index) => {
  677 + const data = dsData[index];
  678 + if (tsData.length && data.entityName === tsData[0].entityName) {
673 679 if (this.polylines.get(data.entityName)) {
674   - this.updatePolyline(data, dataSource, this.options, updateBounds);
  680 + this.updatePolyline(data, tsData, dsData, this.options, updateBounds);
675 681 } else {
676   - this.createPolyline(data, dataSource, this.options, updateBounds);
  682 + this.createPolyline(data, tsData, dsData, this.options, updateBounds);
677 683 }
678 684 keys.push(data.entityName);
679 685 }
... ... @@ -689,9 +695,9 @@ export default abstract class LeafletMap {
689 695 });
690 696 }
691 697
692   - createPolyline(data: FormattedData, dataSources: FormattedData[], settings: PolylineSettings, updateBounds = true) {
  698 + createPolyline(data: FormattedData, tsData: FormattedData[], dsData: FormattedData[], settings: PolylineSettings, updateBounds = true) {
693 699 const poly = new Polyline(this.map,
694   - dataSources.map(el => this.convertPosition(el)).filter(el => !!el), data, dataSources, settings);
  700 + tsData.map(el => this.convertPosition(el)).filter(el => !!el), data, dsData, settings);
695 701 if (updateBounds) {
696 702 const bounds = poly.leafletPoly.getBounds();
697 703 this.fitBounds(bounds);
... ... @@ -699,10 +705,10 @@ export default abstract class LeafletMap {
699 705 this.polylines.set(data.entityName, poly);
700 706 }
701 707
702   - updatePolyline(data: FormattedData, dataSources: FormattedData[], settings: PolylineSettings, updateBounds = true) {
  708 + updatePolyline(data: FormattedData, tsData: FormattedData[], dsData: FormattedData[], settings: PolylineSettings, updateBounds = true) {
703 709 const poly = this.polylines.get(data.entityName);
704 710 const oldBounds = poly.leafletPoly.getBounds();
705   - poly.updatePolyline(dataSources.map(el => this.convertPosition(el)).filter(el => !!el), data, dataSources, settings);
  711 + poly.updatePolyline(tsData.map(el => this.convertPosition(el)).filter(el => !!el), data, dsData, settings);
706 712 const newBounds = poly.leafletPoly.getBounds();
707 713 if (updateBounds && oldBounds.toBBoxString() !== newBounds.toBBoxString()) {
708 714 this.fitBounds(newBounds);
... ...
... ... @@ -866,7 +866,7 @@ export const pathSchema =
866 866 'useColorFunction',
867 867 {
868 868 key: 'colorFunction',
869   - helpId: 'widget/lib/map/trip_path_color_fn',
  869 + helpId: 'widget/lib/map/path_color_fn',
870 870 type: 'javascript',
871 871 condition: 'model.useColorFunction === true'
872 872 },
... ... @@ -961,7 +961,7 @@ export const pointSchema =
961 961 'useColorPointFunction',
962 962 {
963 963 key: 'colorPointFunction',
964   - helpId: 'widget/lib/map/trip_path_point_color_fn',
  964 + helpId: 'widget/lib/map/path_point_color_fn',
965 965 type: 'javascript',
966 966 condition: 'model.useColorPointFunction === true'
967 967 },
... ... @@ -1150,7 +1150,7 @@ export const tripAnimationSchema = {
1150 1150 {
1151 1151 key: 'labelFunction',
1152 1152 type: 'javascript',
1153   - helpId: 'widget/lib/map/trip_label_fn',
  1153 + helpId: 'widget/lib/map/label_fn',
1154 1154 condition: 'model.showLabel === true && model.useLabelFunction === true'
1155 1155 },
1156 1156 'showTooltip',
... ... @@ -1184,7 +1184,7 @@ export const tripAnimationSchema = {
1184 1184 {
1185 1185 key: 'tooltipFunction',
1186 1186 type: 'javascript',
1187   - helpId: 'widget/lib/map/trip_tooltip_fn',
  1187 + helpId: 'widget/lib/map/tooltip_fn',
1188 1188 condition: 'model.showTooltip === true && model.useTooltipFunction === true'
1189 1189 },
1190 1190 'rotationAngle',
... ... @@ -1201,7 +1201,7 @@ export const tripAnimationSchema = {
1201 1201 {
1202 1202 key: 'markerImageFunction',
1203 1203 type: 'javascript',
1204   - helpId: 'widget/lib/map/trip_marker_image_fn',
  1204 + helpId: 'widget/lib/map/marker_image_fn',
1205 1205 condition: 'model.useMarkerImageFunction === true'
1206 1206 },
1207 1207 {
... ...
... ... @@ -16,9 +16,7 @@
16 16
17 17 -->
18 18 <div class="trip-animation-widget">
19   - <div class="trip-animation-label-container" *ngIf="settings.showLabel">
20   - {{label}}
21   - </div>
  19 + <div class="trip-animation-label-container" *ngIf="settings.showLabel" [innerHTML]="label"></div>
22 20 <div class="trip-animation-container" fxLayout="column">
23 21 <div class="map" #map></div>
24 22 <div class="trip-animation-info-panel" fxLayout="row">
... ...
... ... @@ -30,7 +30,7 @@ import {
30 30 import { FormattedData, MapProviders, TripAnimationSettings } from '@home/components/widget/lib/maps/map-models';
31 31 import { addCondition, addGroupInfo, addToSchema, initSchema } from '@app/core/schema-utils';
32 32 import { mapPolygonSchema, pathSchema, pointSchema, tripAnimationSchema } from '@home/components/widget/lib/maps/schemes';
33   -import { DomSanitizer } from '@angular/platform-browser';
  33 +import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
34 34 import { WidgetContext } from '@app/modules/home/models/widget-component.models';
35 35 import {
36 36 findAngle, getProviderSchema,
... ... @@ -70,13 +70,14 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
70 70 mapWidget: MapWidgetInterface;
71 71 historicalData: FormattedData[][];
72 72 normalizationStep: number;
73   - interpolatedTimeData = [];
  73 + interpolatedTimeData: {[time: number]: FormattedData}[] = [];
  74 + formattedInterpolatedTimeData: FormattedData[][] = [];
74 75 widgetConfig: WidgetConfig;
75 76 settings: TripAnimationSettings;
76 77 mainTooltips = [];
77 78 visibleTooltip = false;
78 79 activeTrip: FormattedData;
79   - label: string;
  80 + label: SafeHtml;
80 81 minTime: number;
81 82 maxTime: number;
82 83 anchors: number[] = [];
... ... @@ -117,6 +118,8 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
117 118 const subscription = this.ctx.defaultSubscription;
118 119 subscription.callbacks.onDataUpdated = () => {
119 120 this.historicalData = parseArray(this.ctx.data).filter(arr => arr.length);
  121 + this.interpolatedTimeData.length = 0;
  122 + this.formattedInterpolatedTimeData.length = 0;
120 123 if (this.historicalData.length) {
121 124 this.calculateIntervals();
122 125 this.timeUpdated(this.minTime);
... ... @@ -146,6 +149,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
146 149
147 150 timeUpdated(time: number) {
148 151 this.currentTime = time;
  152 + // get point for each datasource associated with time
149 153 const currentPosition = this.interpolatedTimeData
150 154 .map(dataSource => dataSource[time]);
151 155 for (let j = 0; j < this.interpolatedTimeData.length; j++) {
... ... @@ -171,20 +175,20 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
171 175 currentPosition[j] = this.calculateLastPoints(this.interpolatedTimeData[j], time);
172 176 }
173 177 }
174   - this.calcLabel();
  178 + this.calcLabel(currentPosition);
175 179 this.calcMainTooltip(currentPosition);
176 180 if (this.mapWidget && this.mapWidget.map && this.mapWidget.map.map) {
177   - const formattedInterpolatedTimeData = this.interpolatedTimeData.map(ds => _.values(ds));
178   - this.mapWidget.map.updatePolylines(formattedInterpolatedTimeData, true);
  181 + this.mapWidget.map.updatePolylines(this.formattedInterpolatedTimeData, currentPosition, true);
179 182 if (this.settings.showPolygon) {
180   - this.mapWidget.map.updatePolygons(this.interpolatedTimeData);
  183 + this.mapWidget.map.updatePolygons(currentPosition);
181 184 }
182 185 if (this.settings.showPoints) {
183   - this.mapWidget.map.updatePoints(formattedInterpolatedTimeData.map(ds => _.union(ds)), this.calcTooltip);
  186 + this.mapWidget.map.updatePoints(this.formattedInterpolatedTimeData, this.calcTooltip);
184 187 }
185 188 this.mapWidget.map.updateMarkers(currentPosition, true, (trip) => {
186 189 this.activeTrip = trip;
187 190 this.timeUpdated(this.currentTime);
  191 + this.cd.markForCheck();
188 192 });
189 193 }
190 194 }
... ... @@ -215,41 +219,44 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
215 219 this.maxTime = dataSource[dataSource.length - 1]?.time || -Infinity;
216 220 this.interpolatedTimeData[index] = this.interpolateArray(dataSource);
217 221 });
  222 + this.formattedInterpolatedTimeData = this.interpolatedTimeData.map(ds => _.values(ds));
218 223 if (!this.activeTrip) {
219 224 this.activeTrip = this.interpolatedTimeData.map(dataSource => dataSource[this.minTime]).filter(ds => ds)[0];
220 225 }
221 226 if (this.useAnchors) {
222 227 const anchorDate = Object.entries(_.union(this.interpolatedTimeData)[0]);
223 228 this.anchors = anchorDate
224   - .filter((data: [string, FormattedData]) => safeExecute(this.settings.pointAsAnchorFunction, [data[1], anchorDate, data[1].dsIndex]))
  229 + .filter((data: [string, FormattedData], tsIndex) => safeExecute(this.settings.pointAsAnchorFunction, [data[1],
  230 + this.formattedInterpolatedTimeData.map(ds => ds[tsIndex]), data[1].dsIndex]))
225 231 .map(data => parseInt(data[0], 10));
226 232 }
227 233 }
228 234
229   - calcTooltip = (point: FormattedData): string => {
  235 + calcTooltip = (point: FormattedData, points: FormattedData[]): string => {
230 236 const data = point ? point : this.activeTrip;
231 237 const tooltipPattern: string = this.settings.useTooltipFunction ?
232   - safeExecute(this.settings.tooltipFunction, [data, this.historicalData, point.dsIndex]) : this.settings.tooltipPattern;
  238 + safeExecute(this.settings.tooltipFunction,
  239 + [data, points, point.dsIndex]) : this.settings.tooltipPattern;
233 240 return parseWithTranslation.parseTemplate(tooltipPattern, data, true);
234 241 }
235 242
236 243 private calcMainTooltip(points: FormattedData[]): void {
237 244 const tooltips = [];
238 245 for (const point of points) {
239   - tooltips.push(this.sanitizer.sanitize(SecurityContext.HTML, this.calcTooltip(point)));
  246 + tooltips.push(this.sanitizer.sanitize(SecurityContext.HTML, this.calcTooltip(point, points)));
240 247 }
241 248 this.mainTooltips = tooltips;
242 249 }
243 250
244   - calcLabel() {
245   - const data = this.activeTrip;
  251 + calcLabel(points: FormattedData[]) {
  252 + const data = points[this.activeTrip.dsIndex];
246 253 const labelText: string = this.settings.useLabelFunction ?
247   - safeExecute(this.settings.labelFunction, [data, this.historicalData, data.dsIndex]) : this.settings.label;
248   - this.label = (parseWithTranslation.parseTemplate(labelText, data, true));
  254 + safeExecute(this.settings.labelFunction, [data, points, data.dsIndex]) : this.settings.label;
  255 + this.label = this.sanitizer.bypassSecurityTrustHtml(parseWithTranslation.parseTemplate(labelText, data, true));
249 256 }
250 257
251   - interpolateArray(originData: FormattedData[]) {
252   - const result = {};
  258 + interpolateArray(originData: FormattedData[]): {[time: number]: FormattedData} {
  259 + const result: {[time: number]: FormattedData} = {};
253 260 const latKeyName = this.settings.latKeyName;
254 261 const lngKeyName = this.settings.lngKeyName;
255 262 for (const data of originData) {
... ...
1   - <li><b>data:</b> <code><a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData</a></code> - A <a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData</a> object associated with marker.<br/>
  1 + <li><b>data:</b> <code><a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData</a></code> - A <a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData</a> object associated with marker or data point of the route.<br/>
2 2 Represents basic entity properties (ex. <code>entityId</code>, <code>entityName</code>)<br/>and provides access to other entity attributes/timeseries declared in widget datasource configuration.
3 3 </li>
4   - <li><b>dsData:</b> <code><a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData[]</a></code> - All available widget data (entities) as array of <a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData</a> objects<br/>
  4 + <li><b>dsData:</b> <code><a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData[]</a></code> - All available data associated with markers or routes data points as array of <a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData</a> objects<br/>
5 5 resolved from configured datasources. Each object represents basic entity properties (ex. <code>entityId</code>, <code>entityName</code>)<br/>
6 6 and provides access to other entity attributes/timeseries declared in widget datasource configuration.
7 7 </li>
8   - <li><b>dsIndex</b> <code>number</code> - index of the current marker data in <code>dsData</code> array.<br>
  8 + <li><b>dsIndex</b> <code>number</code> - index of the current marker data or route data point in <code>dsData</code> array.<br>
9 9 <strong>Note: </strong> The <code>data</code> argument is equivalent to <code>dsData[dsIndex]</code> expression.
10 10 </li>
... ...
ui-ngx/src/assets/help/en_US/widget/lib/map/path_color_fn.md renamed from ui-ngx/src/assets/help/en_US/widget/lib/map/trip_path_color_fn.md
... ... @@ -10,7 +10,7 @@ A JavaScript function used to compute color of the trip path.
10 10 **Parameters:**
11 11
12 12 <ul>
13   - {% include widget/lib/map/trip_fn_args %}
  13 + {% include widget/lib/map/map_fn_args %}
14 14 </ul>
15 15
16 16 **Returns:**
... ...
ui-ngx/src/assets/help/en_US/widget/lib/map/path_point_color_fn.md renamed from ui-ngx/src/assets/help/en_US/widget/lib/map/trip_path_point_color_fn.md
... ... @@ -10,7 +10,7 @@ A JavaScript function used to compute color of the trip path point.
10 10 **Parameters:**
11 11
12 12 <ul>
13   - {% include widget/lib/map/trip_fn_args %}
  13 + {% include widget/lib/map/map_fn_args %}
14 14 </ul>
15 15
16 16 **Returns:**
... ...
... ... @@ -5,7 +5,7 @@
5 5
6 6 *function (data, dsData, dsIndex): string*
7 7
8   -A JavaScript function used to compute text or HTML code to be displayed in the marker tooltip.
  8 +A JavaScript function used to compute text or HTML code to be displayed in the marker, point or polygon tooltip.
9 9
10 10 **Parameters:**
11 11
... ... @@ -15,7 +15,7 @@ A JavaScript function used to compute text or HTML code to be displayed in the m
15 15
16 16 **Returns:**
17 17
18   -Should return string value presenting text or HTML for the marker tooltip.
  18 +Should return string value presenting text or HTML for the tooltip.
19 19
20 20 <div class="divider"></div>
21 21
... ...
1   - <li><b>data:</b> <code><a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData</a></code> - A <a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData</a> object associated with the point of the trip.<br/>
2   - Represents basic entity properties (ex. <code>entityId</code>, <code>entityName</code>)<br/>and provides access to other entity attributes/timeseries declared in widget datasource configuration.
3   - </li>
4   - <li><b>dsData:</b> <code><a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData[][]</a></code> - two-dimensional array of <a href="https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts#L108" target="_blank">FormattedData</a> objects presenting<br/>
5   - array of trips (entities with timeseries data) resolved from configured datasources.<br/>
6   - Each element of array is particular entity trip data presented as array of <b>FormattedData</b> object (trip point).<br/>
7   - Each object represents basic entity properties (ex. <code>entityId</code>, <code>entityName</code>)<br/>
8   - and provides access to other entity attributes/timeseries declared in widget datasource configuration.
9   - </li>
10   - <li><b>dsIndex</b> <code>number</code> - index of the current trip data in <code>dsData</code> array.
11   - </li>
1   -#### Trip animation widget label function
2   -
3   -<div class="divider"></div>
4   -<br/>
5   -
6   -*function (data, dsData, dsIndex): string*
7   -
8   -A JavaScript function used to compute text or HTML code of the marker label.
9   -
10   -**Parameters:**
11   -
12   -<ul>
13   - {% include widget/lib/map/trip_fn_args %}
14   -</ul>
15   -
16   -**Returns:**
17   -
18   -Should return string value presenting text or HTML of the marker label.
19   -
20   -<div class="divider"></div>
21   -
22   -##### Examples
23   -
24   -* Display styled label with corresponding latest telemetry data for `energy meter` or `thermometer` device types:
25   -
26   -```javascript
27   -var deviceType = data['Type'];
28   -if (typeof deviceType !== undefined) {
29   - if (deviceType == "energy meter") {
30   - return '<span style="color:orange;">${entityName}, ${energy:2} kWt</span>';
31   - } else if (deviceType == "thermometer") {
32   - return '<span style="color:blue;">${entityName}, ${temperature:2} °C</span>';
33   - }
34   -}
35   -return data.entityName;
36   -{:copy-code}
37   -```
38   -
39   -<br>
40   -<br>
1   -#### Marker image function
2   -
3   -<div class="divider"></div>
4   -<br/>
5   -
6   -*function (data, images, dsData, dsIndex): {url: string, size: number}*
7   -
8   -A JavaScript function used to compute marker image.
9   -
10   -**Parameters:**
11   -
12   -<ul>
13   - {% include widget/lib/map/trip_fn_args %}
14   -</ul>
15   -
16   -**Returns:**
17   -
18   -Should return marker image data having the following structure:
19   -
20   -```typescript
21   -{
22   - url: string,
23   - size: number
24   -}
25   -```
26   -
27   -- *url* - marker image url;
28   -- *size* - marker image size;
29   -
30   -In case no data is returned, default marker image will be used.
31   -
32   -<div class="divider"></div>
33   -
34   -##### Examples
35   -
36   -<ul>
37   -<li>
38   -Calculate image url depending on <code>temperature</code> telemetry value for <code>thermometer</code> device type.<br>
39   -Let's assume 4 images are defined in <b>Marker images</b> section. Each image corresponds to particular temperature level:
40   -</li>
41   -</ul>
42   -
43   -```javascript
44   -var type = data['Type'];
45   -if (type == 'thermometer') {
46   - var res = {
47   - url: images[0],
48   - size: 40
49   - }
50   - var temperature = data['temperature'];
51   - if (typeof temperature !== undefined) {
52   - var percent = (temperature + 60)/120;
53   - var index = Math.min(3, Math.floor(4 * percent));
54   - res.url = images[index];
55   - }
56   - return res;
57   -}
58   -{:copy-code}
59   -```
60   -
61   -<br>
62   -<br>
... ... @@ -10,7 +10,7 @@ A JavaScript function evaluating whether to use trip point as time anchor used i
10 10 **Parameters:**
11 11
12 12 <ul>
13   - {% include widget/lib/map/trip_fn_args %}
  13 + {% include widget/lib/map/map_fn_args %}
14 14 </ul>
15 15
16 16 **Returns:**
... ...
1   -#### Trip animation widget tooltip function
2   -
3   -<div class="divider"></div>
4   -<br/>
5   -
6   -*function (data, dsData, dsIndex): string*
7   -
8   -A JavaScript function used to compute text or HTML code to be displayed in the marker tooltip.
9   -
10   -**Parameters:**
11   -
12   -<ul>
13   - {% include widget/lib/map/trip_fn_args %}
14   -</ul>
15   -
16   -**Returns:**
17   -
18   -Should return string value presenting text or HTML for the marker tooltip.
19   -
20   -<div class="divider"></div>
21   -
22   -##### Examples
23   -
24   -* Display details with corresponding telemetry data for `thermostat` device type:
25   -
26   -```javascript
27   -var deviceType = data['Type'];
28   -if (typeof deviceType !== undefined) {
29   - if (deviceType == "energy meter") {
30   - return '<b>${entityName}</b><br/><b>Energy:</b> ${energy:2} kWt<br/>';
31   - } else if (deviceType == "thermometer") {
32   - return '<b>${entityName}</b><br/><b>Temperature:</b> ${temperature:2} °C<br/>';
33   - }
34   -}
35   -return data.entityName;
36   -{:copy-code}
37   -```
38   -
39   -<br>
40   -<br>