Commit 3d52420d17b4fbc9ff7e24416ed9e13167756272
1 parent
3bc534ec
Fixed map functions. Update map helps.
Showing
15 changed files
with
126 additions
and
245 deletions
... | ... | @@ -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
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
... | ... | @@ -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 | ... | ... |
ui-ngx/src/assets/help/en_US/widget/lib/map/trip_fn_args.md
deleted
100644 → 0
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> |
ui-ngx/src/assets/help/en_US/widget/lib/map/trip_label_fn.md
deleted
100644 → 0
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> |
ui-ngx/src/assets/help/en_US/widget/lib/map/trip_marker_image_fn.md
deleted
100644 → 0
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> |
ui-ngx/src/assets/help/en_US/widget/lib/map/trip_tooltip_fn.md
deleted
100644 → 0
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> |