Showing
9 changed files
with
169 additions
and
142 deletions
@@ -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 | } |