Commit a1b84834fc442964d221b678a1166d46810c02c5

Authored by Vladyslav_Prykhodko
1 parent 140ae29f

Update trip-animation widget

@@ -130,7 +130,7 @@ @@ -130,7 +130,7 @@
130 "controllerScript": " self.onInit = function() {\n var $scope = self.ctx.$scope;\n $scope.self = self;\n }\n \n \n self.actionSources = function () {\n return {\n 'tooltipAction': {\n name: 'widget-action.tooltip-tag-action',\n multiple: false\n }\n }\n };\n \n self.getSettingsSchema = function() {\n return TbTripAnimationWidget.getSettingsSchema();\n}\n", 130 "controllerScript": " self.onInit = function() {\n var $scope = self.ctx.$scope;\n $scope.self = self;\n }\n \n \n self.actionSources = function () {\n return {\n 'tooltipAction': {\n name: 'widget-action.tooltip-tag-action',\n multiple: false\n }\n }\n };\n \n self.getSettingsSchema = function() {\n return TbTripAnimationWidget.getSettingsSchema();\n}\n",
131 "settingsSchema": "", 131 "settingsSchema": "",
132 "dataKeySettingsSchema": "{}", 132 "dataKeySettingsSchema": "{}",
133 - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"entityAliasId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var gpsData = [\\n37.771210000, -122.510960000,\\n 37.771990000, -122.497070000,\\n 37.772730000, -122.480740000,\\n 37.773360000, -122.466870000,\\n 37.774270000, -122.458520000,\\n 37.771980000, -122.454110000,\\n 37.768250000, -122.453380000,\\n 37.765920000, -122.456810000,\\n 37.765930000, -122.467680000,\\n 37.765500000, -122.477180000,\\n 37.765300000, -122.481660000,\\n 37.764780000, -122.493350000,\\n 37.764120000, -122.508360000,\\n 37.766410000, -122.510260000,\\n 37.770010000, -122.510830000,\\n 37.770980000, -122.510930000\\n];\\n let value = gpsData.indexOf(prevValue); \\nreturn gpsData[(value == -1 ? 0 : value + 2)];\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var gpsData = [\\n37.771210000, -122.510960000,\\n 37.771990000, -122.497070000,\\n 37.772730000, -122.480740000,\\n 37.773360000, -122.466870000,\\n 37.774270000, -122.458520000,\\n 37.771980000, -122.454110000,\\n 37.768250000, -122.453380000,\\n 37.765920000, -122.456810000,\\n 37.765930000, -122.467680000,\\n 37.765500000, -122.477180000,\\n 37.765300000, -122.481660000,\\n 37.764780000, -122.493350000,\\n 37.764120000, -122.508360000,\\n 37.766410000, -122.510260000,\\n 37.770010000, -122.510830000,\\n 37.770980000, -122.510930000\\n];\\n let value = gpsData.indexOf(prevValue); \\nreturn gpsData[(value == -1 ? 1 : value + 2)];\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"history\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":500}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"mapProvider\":\"OpenStreetMap.Mapnik\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showLabel\":true,\"label\":\"${entityName}\",\"showTooltip\":true,\"tooltipColor\":\"#fff\",\"tooltipFontColor\":\"#000\",\"tooltipOpacity\":1,\"tooltipPattern\":\"<b>${entityName}</b><br/><br/><b>Latitude:</b> ${latitude:7}<br/><b>Longitude:</b> ${longitude:7}<br/><b>End Time:</b> ${maxTime}<br/><b>Start Time:</b> ${minTime}\",\"strokeWeight\":2,\"strokeOpacity\":1,\"pointSize\":10,\"markerImageSize\":34,\"rotationAngle\":180,\"provider\":\"openstreet-map\",\"normalizationStep\":1000,\"polKeyName\":\"coordinates\",\"decoratorSymbol\":\"arrowHead\",\"decoratorSymbolSize\":10,\"decoratorCustomColor\":\"#000\",\"decoratorOffset\":\"20px\",\"endDecoratorOffset\":\"20px\",\"decoratorRepeat\":\"20px\",\"polygonTooltipPattern\":\"<b>${entityName}</b><br/><br/><b>TimeStamp:</b> ${ts:7}\",\"polygonOpacity\":0.5,\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"pointTooltipOnRightPanel\":true,\"autocloseTooltip\":true},\"title\":\"Trip Animation\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":false,\"showLegend\":false,\"actions\":{},\"legendConfig\":{\"position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false},\"displayTimewindow\":true}" 133 + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"entityAliasId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var gpsData = [\\n37.771210000, -122.510960000,\\n 37.771990000, -122.497070000,\\n 37.772730000, -122.480740000,\\n 37.773360000, -122.466870000,\\n 37.774270000, -122.458520000,\\n 37.771980000, -122.454110000,\\n 37.768250000, -122.453380000,\\n 37.765920000, -122.456810000,\\n 37.765930000, -122.467680000,\\n 37.765500000, -122.477180000,\\n 37.765300000, -122.481660000,\\n 37.764780000, -122.493350000,\\n 37.764120000, -122.508360000,\\n 37.766410000, -122.510260000,\\n 37.770010000, -122.510830000,\\n 37.770980000, -122.510930000\\n];\\n let value = gpsData.indexOf(prevValue); \\nreturn gpsData[(value == -1 ? 0 : (value + 2) % gpsData.length)];\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var gpsData = [\\n37.771210000, -122.510960000,\\n 37.771990000, -122.497070000,\\n 37.772730000, -122.480740000,\\n 37.773360000, -122.466870000,\\n 37.774270000, -122.458520000,\\n 37.771980000, -122.454110000,\\n 37.768250000, -122.453380000,\\n 37.765920000, -122.456810000,\\n 37.765930000, -122.467680000,\\n 37.765500000, -122.477180000,\\n 37.765300000, -122.481660000,\\n 37.764780000, -122.493350000,\\n 37.764120000, -122.508360000,\\n 37.766410000, -122.510260000,\\n 37.770010000, -122.510830000,\\n 37.770980000, -122.510930000\\n];\\n let value = gpsData.indexOf(prevValue); \\nreturn gpsData[(value == -1 ? 1 : (value + 2) % gpsData.length)];\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"history\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":500}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"mapProvider\":\"OpenStreetMap.Mapnik\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showLabel\":true,\"label\":\"${entityName}\",\"showTooltip\":true,\"tooltipColor\":\"#fff\",\"tooltipFontColor\":\"#000\",\"tooltipOpacity\":1,\"tooltipPattern\":\"<b>${entityName}</b><br/><br/><b>Latitude:</b> ${latitude:7}<br/><b>Longitude:</b> ${longitude:7}<br/><b>End Time:</b> ${maxTime}<br/><b>Start Time:</b> ${minTime}\",\"strokeWeight\":2,\"strokeOpacity\":1,\"pointSize\":10,\"markerImageSize\":34,\"rotationAngle\":180,\"provider\":\"openstreet-map\",\"normalizationStep\":1000,\"polKeyName\":\"coordinates\",\"decoratorSymbol\":\"arrowHead\",\"decoratorSymbolSize\":10,\"decoratorCustomColor\":\"#000\",\"decoratorOffset\":\"20px\",\"endDecoratorOffset\":\"20px\",\"decoratorRepeat\":\"20px\",\"polygonTooltipPattern\":\"<b>${entityName}</b><br/><br/><b>TimeStamp:</b> ${ts:7}\",\"polygonOpacity\":0.5,\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"pointTooltipOnRightPanel\":true,\"autocloseTooltip\":true},\"title\":\"Trip Animation\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":false,\"showLegend\":false,\"actions\":{},\"legendConfig\":{\"position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false},\"displayTimewindow\":true}"
134 } 134 }
135 }, 135 },
136 { 136 {
@@ -150,4 +150,4 @@ @@ -150,4 +150,4 @@
150 } 150 }
151 } 151 }
152 ] 152 ]
153 -}  
  153 +}
@@ -7969,14 +7969,6 @@ @@ -7969,14 +7969,6 @@
7969 "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.6.0.tgz", 7969 "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.6.0.tgz",
7970 "integrity": "sha512-CPkhyqWUKZKFJ6K8umN5/D2wrJ2+/8UIpXppY7QDnUZW5bZL5+SEI2J7GBpwh4LIupOKqbNSQXgqmrEJopHVNQ==" 7970 "integrity": "sha512-CPkhyqWUKZKFJ6K8umN5/D2wrJ2+/8UIpXppY7QDnUZW5bZL5+SEI2J7GBpwh4LIupOKqbNSQXgqmrEJopHVNQ=="
7971 }, 7971 },
7972 - "leaflet-geometryutil": {  
7973 - "version": "0.9.3",  
7974 - "resolved": "https://registry.npmjs.org/leaflet-geometryutil/-/leaflet-geometryutil-0.9.3.tgz",  
7975 - "integrity": "sha512-Wi6YvfNx/Xu9q35AEfXpsUXmIFLen/MO+C2qimxHRnjyeyOxBhdcZa6kSiReaOX0cGK7yQInqrzz0dkIqZ8Dpg==",  
7976 - "requires": {  
7977 - "leaflet": ">=0.7.0"  
7978 - }  
7979 - },  
7980 "leaflet-polylinedecorator": { 7972 "leaflet-polylinedecorator": {
7981 "version": "1.6.0", 7973 "version": "1.6.0",
7982 "resolved": "https://registry.npmjs.org/leaflet-polylinedecorator/-/leaflet-polylinedecorator-1.6.0.tgz", 7974 "resolved": "https://registry.npmjs.org/leaflet-polylinedecorator/-/leaflet-polylinedecorator-1.6.0.tgz",
@@ -58,7 +58,6 @@ @@ -58,7 +58,6 @@
58 "jstree-bootstrap-theme": "^1.0.1", 58 "jstree-bootstrap-theme": "^1.0.1",
59 "jszip": "^3.4.0", 59 "jszip": "^3.4.0",
60 "leaflet": "^1.6.0", 60 "leaflet": "^1.6.0",
61 - "leaflet-geometryutil": "^0.9.3",  
62 "leaflet-polylinedecorator": "^1.6.0", 61 "leaflet-polylinedecorator": "^1.6.0",
63 "leaflet-providers": "^1.9.1", 62 "leaflet-providers": "^1.9.1",
64 "leaflet.gridlayer.googlemutant": "0.8.0", 63 "leaflet.gridlayer.googlemutant": "0.8.0",
@@ -56,6 +56,19 @@ export function getRatio(firsMoment: number, secondMoment: number, intermediateM @@ -56,6 +56,19 @@ export function getRatio(firsMoment: number, secondMoment: number, intermediateM
56 return (intermediateMoment - firsMoment) / (secondMoment - firsMoment); 56 return (intermediateMoment - firsMoment) / (secondMoment - firsMoment);
57 } 57 }
58 58
  59 +export function interpolateOnLineSegment(
  60 + pointA: FormattedData,
  61 + oointB: FormattedData,
  62 + latKeyName: string,
  63 + lngKeyName: string,
  64 + ratio: number
  65 +): { [key: string]: number } {
  66 + return {
  67 + [latKeyName]: (pointA[latKeyName] + (oointB[latKeyName] - pointA[latKeyName]) * ratio),
  68 + [lngKeyName]: (pointA[lngKeyName] + (oointB[lngKeyName] - pointA[lngKeyName]) * ratio)
  69 + };
  70 +}
  71 +
59 export function findAngle(startPoint: FormattedData, endPoint: FormattedData, latKeyName: string, lngKeyName: string): number { 72 export function findAngle(startPoint: FormattedData, endPoint: FormattedData, latKeyName: string, lngKeyName: string): number {
60 let angle = -Math.atan2(endPoint[latKeyName] - startPoint[latKeyName], endPoint[lngKeyName] - startPoint[lngKeyName]); 73 let angle = -Math.atan2(endPoint[latKeyName] - startPoint[latKeyName], endPoint[lngKeyName] - startPoint[lngKeyName]);
61 angle = angle * 180 / Math.PI; 74 angle = angle * 180 / Math.PI;
@@ -841,52 +841,57 @@ export const pathSchema = @@ -841,52 +841,57 @@ export const pathSchema =
841 }; 841 };
842 842
843 export const pointSchema = 843 export const pointSchema =
844 -{ 844 + {
845 schema: { 845 schema: {
846 - title: 'Trip Animation Path Configuration',  
847 - type: 'object',  
848 - properties: {  
849 - showPoints: {  
850 - title: 'Show points',  
851 - type: 'boolean',  
852 - default: false  
853 - },  
854 - pointColor: {  
855 - title: 'Point color',  
856 - type: 'string'  
857 - },  
858 - pointSize: {  
859 - title: 'Point size (px)',  
860 - type: 'number',  
861 - default: 10  
862 - },  
863 - usePointAsAnchor: {  
864 - title: 'Use point as anchor',  
865 - type: 'boolean',  
866 - default: false  
867 - },  
868 - pointAsAnchorFunction: {  
869 - title: 'Point as anchor function: f(data, dsData, dsIndex)',  
870 - type: 'string'  
871 - },  
872 - pointTooltipOnRightPanel: {  
873 - title: 'Independant point tooltip',  
874 - type: 'boolean',  
875 - default: true  
876 - }, 846 + title: 'Trip Animation Path Configuration',
  847 + type: 'object',
  848 + properties: {
  849 + showPoints: {
  850 + title: 'Show points',
  851 + type: 'boolean',
  852 + default: false
877 }, 853 },
878 - required: [] 854 + pointColor: {
  855 + title: 'Point color',
  856 + type: 'string'
  857 + },
  858 + pointSize: {
  859 + title: 'Point size (px)',
  860 + type: 'number',
  861 + default: 10
  862 + },
  863 + // usePointAsAnchor: {
  864 + // title: 'Use point as anchor',
  865 + // type: 'boolean',
  866 + // default: false
  867 + // },
  868 + // pointAsAnchorFunction: {
  869 + // title: 'Point as anchor function: f(data, dsData, dsIndex)',
  870 + // type: 'string'
  871 + // },
  872 + pointTooltipOnRightPanel: {
  873 + title: 'Independant point tooltip',
  874 + type: 'boolean',
  875 + default: true
  876 + },
  877 + },
  878 + required: []
879 }, 879 },
880 form: [ 880 form: [
881 - 'showPoints', {  
882 - key: 'pointColor',  
883 - type: 'color'  
884 - }, 'pointSize', 'usePointAsAnchor', {  
885 - key: 'pointAsAnchorFunction',  
886 - type: 'javascript'  
887 - }, 'pointTooltipOnRightPanel', 881 + 'showPoints',
  882 + {
  883 + key: 'pointColor',
  884 + type: 'color'
  885 + },
  886 + 'pointSize',
  887 + // 'usePointAsAnchor',
  888 + // {
  889 + // key: 'pointAsAnchorFunction',
  890 + // type: 'javascript'
  891 + // },
  892 + 'pointTooltipOnRightPanel',
888 ] 893 ]
889 -}; 894 + };
890 895
891 export const mapProviderSchema = 896 export const mapProviderSchema =
892 { 897 {
@@ -32,6 +32,10 @@ @@ -32,6 +32,10 @@
32 [ngStyle]="{'background-color': settings.tooltipColor, 'opacity': settings.tooltipOpacity, 'color': settings.tooltipFontColor}"> 32 [ngStyle]="{'background-color': settings.tooltipColor, 'opacity': settings.tooltipOpacity, 'color': settings.tooltipFontColor}">
33 </div> 33 </div>
34 </div> 34 </div>
35 - <tb-history-selector *ngIf="historicalData" [settings]="settings" [intervals]="intervals" [anchors]="anchors" [useAnchors]="useAnchors"  
36 - (timeUpdated)="timeUpdated($event)"></tb-history-selector>  
37 -</div>  
  35 + <tb-history-selector *ngIf="historicalData"
  36 + [settings]="settings"
  37 + [minTime]="minTime"
  38 + [maxTime]="maxTime"
  39 + [step]="normalizationStep"
  40 + (timeUpdated)="timeUpdated($event)"></tb-history-selector>
  41 +</div>
@@ -14,10 +14,8 @@ @@ -14,10 +14,8 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import L from 'leaflet';  
18 import _ from 'lodash'; 17 import _ from 'lodash';
19 import tinycolor from 'tinycolor2'; 18 import tinycolor from 'tinycolor2';
20 -import { interpolateOnPointSegment } from 'leaflet-geometryutil';  
21 19
22 import { AfterViewInit, ChangeDetectorRef, Component, Input, OnInit, SecurityContext, ViewChild } from '@angular/core'; 20 import { AfterViewInit, ChangeDetectorRef, Component, Input, OnInit, SecurityContext, ViewChild } from '@angular/core';
23 import { MapWidgetController, TbMapWidgetV2 } from '../lib/maps/map-widget2'; 21 import { MapWidgetController, TbMapWidgetV2 } from '../lib/maps/map-widget2';
@@ -26,9 +24,17 @@ import { addCondition, addGroupInfo, addToSchema, initSchema } from '@app/core/s @@ -26,9 +24,17 @@ import { addCondition, addGroupInfo, addToSchema, initSchema } from '@app/core/s
26 import { mapPolygonSchema, pathSchema, pointSchema, tripAnimationSchema } from '../lib/maps/schemes'; 24 import { mapPolygonSchema, pathSchema, pointSchema, tripAnimationSchema } from '../lib/maps/schemes';
27 import { DomSanitizer } from '@angular/platform-browser'; 25 import { DomSanitizer } from '@angular/platform-browser';
28 import { WidgetContext } from '@app/modules/home/models/widget-component.models'; 26 import { WidgetContext } from '@app/modules/home/models/widget-component.models';
29 -import { findAngle, getRatio, parseArray, parseWithTranslation, safeExecute } from '../lib/maps/maps-utils'; 27 +import {
  28 + findAngle,
  29 + getRatio,
  30 + interpolateOnLineSegment,
  31 + parseArray,
  32 + parseWithTranslation,
  33 + safeExecute
  34 +} from '../lib/maps/maps-utils';
30 import { JsonSettingsSchema, WidgetConfig } from '@shared/models/widget.models'; 35 import { JsonSettingsSchema, WidgetConfig } from '@shared/models/widget.models';
31 import moment from 'moment'; 36 import moment from 'moment';
  37 +import { isUndefined } from '@core/utils';
32 38
33 39
34 @Component({ 40 @Component({
@@ -46,20 +52,21 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -46,20 +52,21 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
46 @ViewChild('map') mapContainer; 52 @ViewChild('map') mapContainer;
47 53
48 mapWidget: MapWidgetController; 54 mapWidget: MapWidgetController;
49 - historicalData;  
50 - intervals;  
51 - normalizationStep = 1000;  
52 - interpolatedData = []; 55 + historicalData: FormattedData[][];
  56 + normalizationStep: number;
  57 + interpolatedTimeData = [];
53 widgetConfig: WidgetConfig; 58 widgetConfig: WidgetConfig;
54 settings; 59 settings;
55 mainTooltip = ''; 60 mainTooltip = '';
56 visibleTooltip = false; 61 visibleTooltip = false;
57 - activeTrip; 62 + activeTrip: FormattedData;
58 label; 63 label;
59 - minTime;  
60 - maxTime; 64 + minTime: number;
  65 + minTimeFormat: string;
  66 + maxTime: number;
  67 + maxTimeFormat: string;
61 anchors = []; 68 anchors = [];
62 - useAnchors = false; 69 + useAnchors: boolean;
63 70
64 static getSettingsSchema(): JsonSettingsSchema { 71 static getSettingsSchema(): JsonSettingsSchema {
65 const schema = initSchema(); 72 const schema = initSchema();
@@ -95,7 +102,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -95,7 +102,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
95 this.historicalData = parseArray(this.ctx.data); 102 this.historicalData = parseArray(this.ctx.data);
96 this.activeTrip = this.historicalData[0][0]; 103 this.activeTrip = this.historicalData[0][0];
97 this.calculateIntervals(); 104 this.calculateIntervals();
98 - this.timeUpdated(this.intervals[0]); 105 + this.timeUpdated(this.minTime);
99 this.mapWidget.map.map?.invalidateSize(); 106 this.mapWidget.map.map?.invalidateSize();
100 this.cd.detectChanges(); 107 this.cd.detectChanges();
101 } 108 }
@@ -108,23 +115,37 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -108,23 +115,37 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
108 } 115 }
109 116
110 timeUpdated(time: number) { 117 timeUpdated(time: number) {
111 - const currentPosition = this.interpolatedData.map(dataSource => dataSource[time]); 118 + const currentPosition = this.interpolatedTimeData.map(dataSource => dataSource[time]);
  119 + if(isUndefined(currentPosition[0])){
  120 + const timePoints = Object.keys(this.interpolatedTimeData[0]).map(item => parseInt(item, 10));
  121 + for (let i = 1; i < timePoints.length; i++) {
  122 + if (timePoints[i - 1] < time && timePoints[i] > time) {
  123 + const beforePosition = this.interpolatedTimeData[0][timePoints[i - 1]];
  124 + const afterPosition = this.interpolatedTimeData[0][timePoints[i]];
  125 + const ratio = getRatio(timePoints[i - 1], timePoints[i], time);
  126 + currentPosition[0] = {
  127 + ...beforePosition,
  128 + time,
  129 + ...interpolateOnLineSegment(beforePosition, afterPosition, this.settings.latKeyName, this.settings.lngKeyName, ratio)
  130 + }
  131 + break;
  132 + }
  133 + }
  134 + }
112 this.activeTrip = currentPosition[0]; 135 this.activeTrip = currentPosition[0];
113 - this.minTime = moment(this.intervals[this.intervals.length - 1]).format('YYYY-MM-DD HH:mm:ss')  
114 - this.maxTime = moment(this.intervals[0]).format('YYYY-MM-DD HH:mm:ss')  
115 this.calcLabel(); 136 this.calcLabel();
116 this.calcTooltip(); 137 this.calcTooltip();
117 if (this.mapWidget) { 138 if (this.mapWidget) {
118 - this.mapWidget.map.updatePolylines(this.interpolatedData.map(ds => _.values(ds))); 139 + this.mapWidget.map.updatePolylines(this.interpolatedTimeData.map(ds => _.values(ds)));
119 if (this.settings.showPolygon) { 140 if (this.settings.showPolygon) {
120 - this.mapWidget.map.updatePolygons(this.interpolatedData); 141 + this.mapWidget.map.updatePolygons(this.interpolatedTimeData);
121 } 142 }
122 if (this.settings.showPoints) { 143 if (this.settings.showPoints) {
123 - this.mapWidget.map.updatePoints(this.historicalData[0], this.calcTooltip);  
124 - this.anchors = this.historicalData[0]  
125 - .filter(data =>  
126 - this.settings.usePointAsAnchor ||  
127 - safeExecute(this.settings.pointAsAnchorFunction, [this.historicalData, data, data.dsIndex])).map(data => data.time); 144 + this.mapWidget.map.updatePoints(this.interpolatedTimeData, this.calcTooltip);
  145 + // this.anchors = this.interpolatedTimeData
  146 + // .filter(data =>
  147 + // this.settings.usePointAsAnchor ||
  148 + // safeExecute(this.settings.pointAsAnchorFunction, [this.interpolatedTimeData, data, data.dsIndex])).map(data => data.time);
128 } 149 }
129 this.mapWidget.map.updateMarkers(currentPosition); 150 this.mapWidget.map.updateMarkers(currentPosition);
130 } 151 }
@@ -135,12 +156,11 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -135,12 +156,11 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
135 156
136 calculateIntervals() { 157 calculateIntervals() {
137 this.historicalData.forEach((dataSource, index) => { 158 this.historicalData.forEach((dataSource, index) => {
138 - this.intervals = [];  
139 - for (let time = dataSource[0]?.time; time < dataSource[dataSource.length - 1]?.time; time += this.normalizationStep) {  
140 - this.intervals.push(time);  
141 - }  
142 - this.intervals.push(dataSource[dataSource.length - 1]?.time);  
143 - this.interpolatedData[index] = this.interpolateArray(dataSource, this.intervals); 159 + this.minTime = dataSource[0]?.time || Infinity;
  160 + this.minTimeFormat = this.minTime !== Infinity ? moment(this.minTime).format('YYYY-MM-DD HH:mm:ss') : '';
  161 + this.maxTime = dataSource[dataSource.length - 1]?.time || -Infinity;
  162 + this.maxTimeFormat = this.maxTime !== -Infinity ? moment(this.maxTime).format('YYYY-MM-DD HH:mm:ss') : '';
  163 + this.interpolatedTimeData[index] = this.interpolateArray(dataSource);
144 }); 164 });
145 165
146 } 166 }
@@ -149,9 +169,13 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -149,9 +169,13 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
149 if (!point) { 169 if (!point) {
150 point = this.activeTrip; 170 point = this.activeTrip;
151 } 171 }
152 - const data = { ...point, maxTime: this.maxTime, minTime: this.minTime } 172 + const data = {
  173 + ...this.activeTrip,
  174 + maxTime: this.maxTimeFormat,
  175 + minTime: this.minTimeFormat
  176 + }
153 const tooltipPattern: string = this.settings.useTooltipFunction ? 177 const tooltipPattern: string = this.settings.useTooltipFunction ?
154 - safeExecute(this.settings.tooolTipFunction, [data, this.historicalData, 0]) : this.settings.tooltipPattern; 178 + safeExecute(this.settings.tooolTipFunction, [data, this.historicalData, point.dsIndex]) : this.settings.tooltipPattern;
155 const tooltipText = parseWithTranslation.parseTemplate(tooltipPattern, data, true); 179 const tooltipText = parseWithTranslation.parseTemplate(tooltipPattern, data, true);
156 if (setTooltip) { 180 if (setTooltip) {
157 this.mainTooltip = this.sanitizer.sanitize( 181 this.mainTooltip = this.sanitizer.sanitize(
@@ -162,34 +186,37 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -162,34 +186,37 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
162 } 186 }
163 187
164 calcLabel() { 188 calcLabel() {
165 - const data = { ...this.activeTrip, maxTime: this.maxTime, minTime: this.minTime } 189 + const data = {
  190 + ...this.activeTrip,
  191 + maxTime: this.maxTimeFormat,
  192 + minTime: this.minTimeFormat
  193 + }
166 const labelText: string = this.settings.useLabelFunction ? 194 const labelText: string = this.settings.useLabelFunction ?
167 - safeExecute(this.settings.labelFunction, [data, this.historicalData, 0]) : this.settings.label; 195 + safeExecute(this.settings.labelFunction, [data, this.historicalData, data.dsIndex]) : this.settings.label;
168 this.label = (parseWithTranslation.parseTemplate(labelText, data, true)); 196 this.label = (parseWithTranslation.parseTemplate(labelText, data, true));
169 } 197 }
170 198
171 - interpolateArray(originData, interpolatedIntervals) { 199 + interpolateArray(originData: FormattedData[]) {
172 const result = {}; 200 const result = {};
173 - for (let i = 1, j = 0; i < originData.length && j < interpolatedIntervals.length;) {  
174 - const currentTime = interpolatedIntervals[j];  
175 - while (originData[i].time < currentTime) i++;  
176 - const before = originData[i - 1];  
177 - const after = originData[i];  
178 - const interpolation = interpolateOnPointSegment(  
179 - new L.Point(before[this.settings.latKeyName], before[this.settings.lngKeyName]),  
180 - new L.Point(after[this.settings.latKeyName], after[this.settings.lngKeyName]),  
181 - getRatio(before.time, after.time, currentTime));  
182 - result[currentTime] = ({  
183 - ...originData[i],  
184 - rotationAngle: findAngle(before, after, this.settings.latKeyName, this.settings.lngKeyName) + this.settings.rotationAngle,  
185 - latitude: interpolation.x,  
186 - longitude: interpolation.y  
187 - });  
188 - j++; 201 + const latKeyName = this.settings.latKeyName;
  202 + const lngKeyName = this.settings.lngKeyName;
  203 + for (let i = 0; i < originData.length; i++) {
  204 + const currentTime = originData[i].time;
  205 + const normalizeTime = this.minTime + Math.ceil((currentTime - this.minTime) / this.normalizationStep) * this.normalizationStep;
  206 + if (i !== originData.length - 1) {
  207 + result[normalizeTime] = {
  208 + ...originData[i],
  209 + rotationAngle: this.settings.rotationAngle + findAngle(originData[i], originData[i + 1], latKeyName, lngKeyName)
  210 + };
  211 + } else {
  212 + result[normalizeTime] = {
  213 + ...originData[i],
  214 + rotationAngle: findAngle(originData[i - 1], originData[i], latKeyName, lngKeyName) + this.settings.rotationAngle
  215 + };
  216 + }
189 } 217 }
190 return result; 218 return result;
191 } 219 }
192 } 220 }
193 221
194 export let TbTripAnimationWidget = TripAnimationComponent; 222 export let TbTripAnimationWidget = TripAnimationComponent;
195 -  
@@ -27,8 +27,8 @@ @@ -27,8 +27,8 @@
27 <mat-slider [(ngModel)]="index" [min]="minTimeIndex" [max]="maxTimeIndex" (change)="changeIndex()"> 27 <mat-slider [(ngModel)]="index" [min]="minTimeIndex" [max]="maxTimeIndex" (change)="changeIndex()">
28 </mat-slider> 28 </mat-slider>
29 <div class="panel-timer"> 29 <div class="panel-timer">
30 - <span *ngIf="this.intervals[this.index]">{{ this.intervals[this.index] | date:'medium'}}</span>  
31 - <span *ngIf="!this.intervals[this.index]">{{ "widget.no-data-found" | translate}}</span> 30 + <span *ngIf="this.currentTime">{{ this.currentTime | date:'medium'}}</span>
  31 + <span *ngIf="!this.currentTime">{{ "widget.no-data-found" | translate}}</span>
32 </div> 32 </div>
33 </div> 33 </div>
34 <button mat-icon-button class="mat-icon-button" aria-label="Next" (click)="moveNext()"> 34 <button mat-icon-button class="mat-icon-button" aria-label="Next" (click)="moveNext()">
@@ -47,8 +47,9 @@ @@ -47,8 +47,9 @@
47 pause_circle_outline 47 pause_circle_outline
48 </mat-icon> 48 </mat-icon>
49 </button> 49 </button>
50 - <mat-select matInput [(ngModel)]="speed" (selectionChange)="reeneble()" class="speed-select" 50 + <mat-select [(ngModel)]="speed" (selectionChange)="reeneble()" class="speed-select"
51 aria-label="Speed selector"> 51 aria-label="Speed selector">
52 <mat-option [value]="speedValue" *ngFor="let speedValue of speeds">{{speedValue}} </mat-option> 52 <mat-option [value]="speedValue" *ngFor="let speedValue of speeds">{{speedValue}} </mat-option>
53 </mat-select> 53 </mat-select>
54 - </div>  
  54 + </div>
  55 +</div>
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { Component, OnInit, OnChanges, Input, Output, EventEmitter, ChangeDetectorRef } from '@angular/core'; 17 +import { ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
18 import { interval } from 'rxjs'; 18 import { interval } from 'rxjs';
19 import { filter } from 'rxjs/operators'; 19 import { filter } from 'rxjs/operators';
20 import { HistorySelectSettings } from '@app/modules/home/components/widget/lib/maps/map-models'; 20 import { HistorySelectSettings } from '@app/modules/home/components/widget/lib/maps/map-models';
@@ -27,13 +27,12 @@ import { HistorySelectSettings } from '@app/modules/home/components/widget/lib/m @@ -27,13 +27,12 @@ import { HistorySelectSettings } from '@app/modules/home/components/widget/lib/m
27 export class HistorySelectorComponent implements OnInit, OnChanges { 27 export class HistorySelectorComponent implements OnInit, OnChanges {
28 28
29 @Input() settings: HistorySelectSettings 29 @Input() settings: HistorySelectSettings
30 - @Input() intervals = [];  
31 - @Input() anchors = [];  
32 - @Input() useAnchors = false; 30 + @Input() minTime: number;
  31 + @Input() maxTime: number;
  32 + @Input() step = 1000;
33 33
34 @Output() timeUpdated: EventEmitter<number> = new EventEmitter(); 34 @Output() timeUpdated: EventEmitter<number> = new EventEmitter();
35 35
36 - animationTime;  
37 minTimeIndex = 0; 36 minTimeIndex = 0;
38 maxTimeIndex = 0; 37 maxTimeIndex = 0;
39 speed = 1; 38 speed = 1;
@@ -41,6 +40,7 @@ export class HistorySelectorComponent implements OnInit, OnChanges { @@ -41,6 +40,7 @@ export class HistorySelectorComponent implements OnInit, OnChanges {
41 playing = false; 40 playing = false;
42 interval; 41 interval;
43 speeds = [1, 5, 10, 25]; 42 speeds = [1, 5, 10, 25];
  43 + currentTime = null;
44 44
45 45
46 constructor(private cd: ChangeDetectorRef) { } 46 constructor(private cd: ChangeDetectorRef) { }
@@ -49,7 +49,8 @@ export class HistorySelectorComponent implements OnInit, OnChanges { @@ -49,7 +49,8 @@ export class HistorySelectorComponent implements OnInit, OnChanges {
49 } 49 }
50 50
51 ngOnChanges() { 51 ngOnChanges() {
52 - this.maxTimeIndex = this.intervals?.length - 1; 52 + this.maxTimeIndex = Math.ceil((this.maxTime - this.minTime) / this.step);
  53 + this.currentTime = this.minTime === Infinity ? null : this.minTime;
53 } 54 }
54 55
55 play() { 56 play() {
@@ -59,17 +60,18 @@ export class HistorySelectorComponent implements OnInit, OnChanges { @@ -59,17 +60,18 @@ export class HistorySelectorComponent implements OnInit, OnChanges {
59 .pipe( 60 .pipe(
60 filter(() => this.playing)).subscribe(() => { 61 filter(() => this.playing)).subscribe(() => {
61 this.index++; 62 this.index++;
62 - if (this.index < this.maxTimeIndex) { 63 + this.currentTime = this.minTime + this.index * this.step;
  64 + if (this.index <= this.maxTimeIndex) {
63 this.cd.detectChanges(); 65 this.cd.detectChanges();
64 - this.timeUpdated.emit(this.intervals[this.index]); 66 + this.timeUpdated.emit(this.currentTime);
65 } 67 }
66 else { 68 else {
67 this.interval.complete(); 69 this.interval.complete();
68 } 70 }
69 }, err => { 71 }, err => {
70 - console.log(err); 72 + console.error(err);
71 }, () => { 73 }, () => {
72 - this.index = this.minTimeIndex; 74 + this.currentTime = this.index = this.minTimeIndex;
73 this.playing = false; 75 this.playing = false;
74 this.interval = null; 76 this.interval = null;
75 this.cd.detectChanges(); 77 this.cd.detectChanges();
@@ -87,30 +89,21 @@ export class HistorySelectorComponent implements OnInit, OnChanges { @@ -87,30 +89,21 @@ export class HistorySelectorComponent implements OnInit, OnChanges {
87 89
88 pause() { 90 pause() {
89 this.playing = false; 91 this.playing = false;
  92 + this.currentTime = this.minTime + this.index * this.step;
90 this.cd.detectChanges(); 93 this.cd.detectChanges();
91 - this.timeUpdated.emit(this.intervals[this.index]); 94 + this.timeUpdated.emit(this.currentTime);
92 } 95 }
93 96
94 moveNext() { 97 moveNext() {
95 - if (this.index < this.maxTimeIndex) {  
96 - if (this.useAnchors) {  
97 - const anchorIndex = this.findIndex(this.intervals[this.index], this.anchors)+1;  
98 - this.index = this.findIndex(this.anchors[anchorIndex], this.intervals);  
99 - }  
100 - else  
101 - this.index++; 98 + if (this.index <= this.maxTimeIndex) {
  99 + this.index++;
102 } 100 }
103 this.pause(); 101 this.pause();
104 } 102 }
105 103
106 movePrev() { 104 movePrev() {
107 if (this.index > this.minTimeIndex) { 105 if (this.index > this.minTimeIndex) {
108 - if (this.useAnchors) {  
109 - const anchorIndex = this.findIndex(this.intervals[this.index], this.anchors) - 1;  
110 - this.index = this.findIndex(this.anchors[anchorIndex], this.intervals);  
111 - }  
112 - else  
113 - this.index--; 106 + this.index--;
114 } 107 }
115 this.pause(); 108 this.pause();
116 } 109 }
@@ -125,15 +118,8 @@ export class HistorySelectorComponent implements OnInit, OnChanges { @@ -125,15 +118,8 @@ export class HistorySelectorComponent implements OnInit, OnChanges {
125 this.pause(); 118 this.pause();
126 } 119 }
127 120
128 - findIndex(value, array: any[]) {  
129 - let i = 0;  
130 - while (array[i] < value) {  
131 - i++;  
132 - };  
133 - return i;  
134 - }  
135 -  
136 changeIndex() { 121 changeIndex() {
137 - this.timeUpdated.emit(this.intervals[this.index]); 122 + this.currentTime = this.minTime + this.index * this.step;
  123 + this.timeUpdated.emit(this.currentTime);
138 } 124 }
139 } 125 }