Commit 5535c33d0eabd9e0b3c10e8094c584126e5f8b1d
1 parent
b9db41bd
WIP on history control and route interpolation
Showing
10 changed files
with
378 additions
and
131 deletions
@@ -1900,7 +1900,8 @@ | @@ -1900,7 +1900,8 @@ | ||
1900 | "@types/lodash": { | 1900 | "@types/lodash": { |
1901 | "version": "4.14.149", | 1901 | "version": "4.14.149", |
1902 | "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz", | 1902 | "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz", |
1903 | - "integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==" | 1903 | + "integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==", |
1904 | + "dev": true | ||
1904 | }, | 1905 | }, |
1905 | "@types/minimatch": { | 1906 | "@types/minimatch": { |
1906 | "version": "3.0.3", | 1907 | "version": "3.0.3", |
@@ -8263,6 +8264,14 @@ | @@ -8263,6 +8264,14 @@ | ||
8263 | "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.6.0.tgz", | 8264 | "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.6.0.tgz", |
8264 | "integrity": "sha512-CPkhyqWUKZKFJ6K8umN5/D2wrJ2+/8UIpXppY7QDnUZW5bZL5+SEI2J7GBpwh4LIupOKqbNSQXgqmrEJopHVNQ==" | 8265 | "integrity": "sha512-CPkhyqWUKZKFJ6K8umN5/D2wrJ2+/8UIpXppY7QDnUZW5bZL5+SEI2J7GBpwh4LIupOKqbNSQXgqmrEJopHVNQ==" |
8265 | }, | 8266 | }, |
8267 | + "leaflet-geometryutil": { | ||
8268 | + "version": "0.9.3", | ||
8269 | + "resolved": "https://registry.npmjs.org/leaflet-geometryutil/-/leaflet-geometryutil-0.9.3.tgz", | ||
8270 | + "integrity": "sha512-Wi6YvfNx/Xu9q35AEfXpsUXmIFLen/MO+C2qimxHRnjyeyOxBhdcZa6kSiReaOX0cGK7yQInqrzz0dkIqZ8Dpg==", | ||
8271 | + "requires": { | ||
8272 | + "leaflet": "1.6.0" | ||
8273 | + } | ||
8274 | + }, | ||
8266 | "leaflet-providers": { | 8275 | "leaflet-providers": { |
8267 | "version": "1.9.1", | 8276 | "version": "1.9.1", |
8268 | "resolved": "https://registry.npmjs.org/leaflet-providers/-/leaflet-providers-1.9.1.tgz", | 8277 | "resolved": "https://registry.npmjs.org/leaflet-providers/-/leaflet-providers-1.9.1.tgz", |
@@ -57,6 +57,7 @@ | @@ -57,6 +57,7 @@ | ||
57 | "jstree": "^3.3.9", | 57 | "jstree": "^3.3.9", |
58 | "jstree-bootstrap-theme": "^1.0.1", | 58 | "jstree-bootstrap-theme": "^1.0.1", |
59 | "leaflet": "^1.6.0", | 59 | "leaflet": "^1.6.0", |
60 | + "leaflet-geometryutil": "^0.9.3", | ||
60 | "leaflet-providers": "^1.9.1", | 61 | "leaflet-providers": "^1.9.1", |
61 | "leaflet.gridlayer.googlemutant": "^0.8.0", | 62 | "leaflet.gridlayer.googlemutant": "^0.8.0", |
62 | "leaflet.markercluster": "^1.4.1", | 63 | "leaflet.markercluster": "^1.4.1", |
@@ -451,13 +451,13 @@ export function aspectCache(imageUrl: string): Observable<number> { | @@ -451,13 +451,13 @@ export function aspectCache(imageUrl: string): Observable<number> { | ||
451 | 451 | ||
452 | export function parseArray(input: any[]): any[] { | 452 | export function parseArray(input: any[]): any[] { |
453 | let alliases: any = _(input).groupBy(el => el?.datasource?.aliasName).values().value(); | 453 | let alliases: any = _(input).groupBy(el => el?.datasource?.aliasName).values().value(); |
454 | - console.log("alliases", alliases) | ||
455 | return alliases.map((alliasArray, dsIndex) => | 454 | return alliases.map((alliasArray, dsIndex) => |
456 | alliasArray[0].data.map((el, i) => { | 455 | alliasArray[0].data.map((el, i) => { |
457 | const obj = { | 456 | const obj = { |
458 | aliasName: alliasArray[0]?.datasource?.aliasName, | 457 | aliasName: alliasArray[0]?.datasource?.aliasName, |
459 | $datasource: alliasArray[0]?.datasource, | 458 | $datasource: alliasArray[0]?.datasource, |
460 | - dsIndex: dsIndex | 459 | + dsIndex: dsIndex, |
460 | + time: el[0] | ||
461 | }; | 461 | }; |
462 | alliasArray.forEach(el => { | 462 | alliasArray.forEach(el => { |
463 | obj[el?.dataKey?.label] = el?.data[i][1]; | 463 | obj[el?.dataKey?.label] = el?.data[i][1]; |
@@ -73,6 +73,10 @@ export class MapWidgetController implements MapWidgetInterface { | @@ -73,6 +73,10 @@ export class MapWidgetController implements MapWidgetInterface { | ||
73 | this.map.updateMarkers(parseData(this.data)); | 73 | this.map.updateMarkers(parseData(this.data)); |
74 | } | 74 | } |
75 | 75 | ||
76 | + public updateHistoryData(dataSources){ | ||
77 | + dataSources.map() | ||
78 | + } | ||
79 | + | ||
76 | onDataUpdated() { | 80 | onDataUpdated() { |
77 | } | 81 | } |
78 | 82 |
1 | import L from 'leaflet'; | 1 | import L from 'leaflet'; |
2 | +import { interpolateOnPointSegment } from 'leaflet-geometryutil'; | ||
2 | import _ from 'lodash'; | 3 | import _ from 'lodash'; |
3 | 4 | ||
4 | export function createTooltip(target, settings, targetArgs?) { | 5 | export function createTooltip(target, settings, targetArgs?) { |
5 | - var popup = L.popup(); | 6 | + const popup = L.popup(); |
6 | popup.setContent(''); | 7 | popup.setContent(''); |
7 | target.bindPopup(popup, { autoClose: settings.autocloseTooltip, closeOnClick: false }); | 8 | target.bindPopup(popup, { autoClose: settings.autocloseTooltip, closeOnClick: false }); |
8 | if (settings.displayTooltipAction == 'hover') { | 9 | if (settings.displayTooltipAction == 'hover') { |
@@ -21,3 +22,27 @@ export function createTooltip(target, settings, targetArgs?) { | @@ -21,3 +22,27 @@ export function createTooltip(target, settings, targetArgs?) { | ||
21 | dsIndex: settings.dsIndex | 22 | dsIndex: settings.dsIndex |
22 | }; | 23 | }; |
23 | } | 24 | } |
25 | + | ||
26 | + | ||
27 | +export function interpolateArray(originData, interpolatedIntervals) { | ||
28 | + | ||
29 | + const getRatio = (firsMoment, secondMoment, intermediateMoment) => { | ||
30 | + return (intermediateMoment - firsMoment) / (secondMoment - firsMoment); | ||
31 | + }; | ||
32 | + | ||
33 | + const result = {}; | ||
34 | + | ||
35 | + for (let i = 1, j = 0; i < originData.length, j < interpolatedIntervals.length;) { | ||
36 | + const currentTime = interpolatedIntervals[j]; | ||
37 | + while (originData[i].time < currentTime) i++; | ||
38 | + const before = originData[i - 1]; | ||
39 | + const after = originData[i]; | ||
40 | + result[currentTime] = (interpolateOnPointSegment( | ||
41 | + new L.Point(before.latitude, before.longitude), | ||
42 | + new L.Point(after.latitude, after.longitude), | ||
43 | + getRatio(before.time, after.time, currentTime))); | ||
44 | + j++; | ||
45 | + } | ||
46 | + | ||
47 | + return result; | ||
48 | +}; |
1 | <div class="map" #map ></div> | 1 | <div class="map" #map ></div> |
2 | -<tb-history-selector [settings]="ctx.settings"></tb-history-selector> | ||
2 | +<div>{{historicalData?.lenth}}</div> | ||
3 | +<tb-history-selector *ngIf="historicalData" [settings]="ctx.settings" [intervals]="intervals" | ||
4 | + (onTimeUpdated)="timeUpdated($event)"></tb-history-selector> |
1 | -import { Component, OnInit, Input, ViewChild, AfterViewInit } from '@angular/core'; | 1 | +import { Component, OnInit, Input, ViewChild, AfterViewInit, ChangeDetectorRef } from '@angular/core'; |
2 | import { MapWidgetController } from '../lib/maps/map-widget2'; | 2 | import { MapWidgetController } from '../lib/maps/map-widget2'; |
3 | import { MapProviders } from '../lib/maps/map-models'; | 3 | import { MapProviders } from '../lib/maps/map-models'; |
4 | import { parseArray } from '@app/core/utils'; | 4 | import { parseArray } from '@app/core/utils'; |
5 | +import { interpolateArray } from '../lib/maps/maps-utils'; | ||
5 | 6 | ||
6 | @Component({ | 7 | @Component({ |
7 | selector: 'trip-animation', | 8 | selector: 'trip-animation', |
@@ -15,18 +16,50 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { | @@ -15,18 +16,50 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { | ||
15 | @ViewChild('map') mapContainer; | 16 | @ViewChild('map') mapContainer; |
16 | 17 | ||
17 | mapWidget: MapWidgetController; | 18 | mapWidget: MapWidgetController; |
18 | - historicalData | 19 | + historicalData; |
20 | + intervals; | ||
21 | + normalizationStep = 500; | ||
22 | + interpolatedData = []; | ||
19 | 23 | ||
20 | - constructor() { } | 24 | + |
25 | + constructor(private cd: ChangeDetectorRef) { } | ||
21 | 26 | ||
22 | ngOnInit(): void { | 27 | ngOnInit(): void { |
23 | - console.log(this.ctx); | ||
24 | - this.historicalData = parseArray(this.ctx.data); | ||
25 | - console.log("TripAnimationComponent -> ngOnInit -> this.historicalData",this.ctx.data, this.historicalData) | 28 | + let subscription = this.ctx.subscriptions[Object.keys(this.ctx.subscriptions)[0]]; |
29 | + if (subscription) subscription.callbacks.onDataUpdated = (updated) => { | ||
30 | + this.historicalData = parseArray(this.ctx.data); | ||
31 | + this.historicalData.forEach(el => { | ||
32 | + console.log("TripAnimationComponent -> if -> el", el) | ||
33 | + el.longitude += (Math.random() - 0.5) | ||
34 | + el.latitude += (Math.random() - 0.5) | ||
35 | + }); | ||
36 | + this.calculateIntervals(); | ||
37 | + this.cd.detectChanges(); | ||
38 | + } | ||
26 | } | 39 | } |
27 | 40 | ||
28 | ngAfterViewInit() { | 41 | ngAfterViewInit() { |
29 | this.mapWidget = new MapWidgetController(MapProviders.openstreet, false, this.ctx, this.mapContainer.nativeElement); | 42 | this.mapWidget = new MapWidgetController(MapProviders.openstreet, false, this.ctx, this.mapContainer.nativeElement); |
30 | this.mapWidget.data | 43 | this.mapWidget.data |
31 | } | 44 | } |
45 | + | ||
46 | + timeUpdated(time) { | ||
47 | + //this.mapWidget.ma | ||
48 | + const currentPosition = this.interpolatedData.map(dataSource=>dataSource[time]); | ||
49 | + console.log("TripAnimationComponent -> timeUpdated -> currentPosition", currentPosition) | ||
50 | + } | ||
51 | + | ||
52 | + calculateIntervals() { | ||
53 | + this.historicalData.forEach((dataSource, index) => { | ||
54 | + this.intervals = []; | ||
55 | + for (let time = dataSource[0]?.time; time < dataSource[dataSource.length - 1]?.time; time += this.normalizationStep) { | ||
56 | + this.intervals.push(time); | ||
57 | + } | ||
58 | + this.intervals.push(dataSource[dataSource.length - 1]?.time); | ||
59 | + this.interpolatedData[index] = interpolateArray(dataSource, this.intervals); | ||
60 | + console.log("TripAnimationComponent -> calculateIntervals -> this.intervals", this.intervals) | ||
61 | + | ||
62 | + }); | ||
63 | + } | ||
64 | + | ||
32 | } | 65 | } |
@@ -6,34 +6,41 @@ | @@ -6,34 +6,41 @@ | ||
6 | <button mat-icon-button class="mat-icon-button" aria-label="Previous" ng-click="movePrev()"> | 6 | <button mat-icon-button class="mat-icon-button" aria-label="Previous" ng-click="movePrev()"> |
7 | <mat-icon class="material-icons" ng-style="{'color': staticSettings.buttonColor}">skip_previous</mat-icon> | 7 | <mat-icon class="material-icons" ng-style="{'color': staticSettings.buttonColor}">skip_previous</mat-icon> |
8 | </button> | 8 | </button> |
9 | - <!-- <mat-slider ng-model="index" [min]="minTimeIndex" [max]="maxTimeIndex" (change)="recalculateTrips()"> | ||
10 | - </mat-slider>--> | 9 | + <mat-slider [(ngModel)]="index" [min]="minTimeIndex" [max]="maxTimeIndex" (change)="recalculateTrips()"> |
10 | + </mat-slider> | ||
11 | <button mat-icon-button class="mat-icon-button" aria-label="Next" ng-click="moveNext()"> | 11 | <button mat-icon-button class="mat-icon-button" aria-label="Next" ng-click="moveNext()"> |
12 | <mat-icon class="material-icons" ng-style="{'color': staticSettings.buttonColor}">skip_next</mat-icon> | 12 | <mat-icon class="material-icons" ng-style="{'color': staticSettings.buttonColor}">skip_next</mat-icon> |
13 | </button> | 13 | </button> |
14 | <button mat-icon-button class="mat-icon-button" aria-label="End" ng-click="moveEnd()"> | 14 | <button mat-icon-button class="mat-icon-button" aria-label="End" ng-click="moveEnd()"> |
15 | <mat-icon class="material-icons" ng-style="{'color': staticSettings.buttonColor}">fast_forward</mat-icon> | 15 | <mat-icon class="material-icons" ng-style="{'color': staticSettings.buttonColor}">fast_forward</mat-icon> |
16 | </button> | 16 | </button> |
17 | - <button mat-icon-button class="mat-icon-button" aria-label="Play" ng-click="playMove(true)" | ||
18 | - ng-disabled="isPlaying"> | ||
19 | - <mat-icon class="material-icons" | ||
20 | - ng-style="{'color': isPlaying ? staticSettings.disabledButtonColor : staticSettings.buttonColor}"> | 17 | + <!--<div class="play-pause"> |
18 | + <input type="checkbox" [(ngModel)]="play" value="" id="playPauseCheckbox" name="playPauseCheckbox" /> | ||
19 | + <label for="playPauseCheckbox"></label> | ||
20 | + </div>--> | ||
21 | + <button mat-icon-button class="mat-icon-button" aria-label="Play" > | ||
22 | + <mat-icon (click)="play()" *ngIf="!playing" class="material-icons" ng-style="{'color': settings.buttonColor}"> | ||
21 | play_circle_outline | 23 | play_circle_outline |
22 | </mat-icon> | 24 | </mat-icon> |
25 | + <mat-icon (click)="pause()" *ngIf="playing" class="material-icons" ng-style="{'color': settings.buttonColor}"> | ||
26 | + pause_circle_outline | ||
27 | + </mat-icon> | ||
23 | </button> | 28 | </button> |
24 | - <!-- <mat-select ng-model="speed" aria-label="Speed selector"> | 29 | + <!-- <mat-select [(ngModel)]="speed" aria-label="Speed selector"> |
25 | <mat-option ng-value="speed" flex="1" ng-repeat="speed in speeds track by $index">{{ speed}} | 30 | <mat-option ng-value="speed" flex="1" ng-repeat="speed in speeds track by $index">{{ speed}} |
26 | </mat-option> | 31 | </mat-option> |
27 | </mat-select>--> | 32 | </mat-select>--> |
28 | - <button mat-icon-button class="mat-icon-button" aria-label="Stop playing" ng-click="stopPlay()" | 33 | + <!-- <button mat-icon-button class="mat-icon-button" aria-label="Stop playing" ng-click="stopPlay()" |
29 | ng-disabled="!isPlaying"> | 34 | ng-disabled="!isPlaying"> |
30 | <mat-icon class="material-icons" | 35 | <mat-icon class="material-icons" |
31 | ng-style="{'color': isPlaying ? staticSettings.buttonColor : staticSettings.disabledButtonColor}"> | 36 | ng-style="{'color': isPlaying ? staticSettings.buttonColor : staticSettings.disabledButtonColor}"> |
32 | pause_circle_outline | 37 | pause_circle_outline |
33 | </mat-icon> | 38 | </mat-icon> |
34 | - </button> | 39 | + </button>--> |
35 | </div> | 40 | </div> |
36 | <div class="panel-timer"> | 41 | <div class="panel-timer"> |
37 | <span *ngIf="animationTime">{{ animationTime | date:'medium'}}</span> | 42 | <span *ngIf="animationTime">{{ animationTime | date:'medium'}}</span> |
38 | <span *ngIf="!animationTime">{{ "widget.no-data-found" | translate}}</span> | 43 | <span *ngIf="!animationTime">{{ "widget.no-data-found" | translate}}</span> |
39 | - </div> | ||
44 | + </div> | ||
45 | + | ||
46 | + <div>{{index}}</div> |
@@ -13,125 +13,152 @@ | @@ -13,125 +13,152 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | - .trip-animation-widget { | 16 | +.trip-animation-widget { |
17 | + position: relative; | ||
18 | + width: 100%; | ||
19 | + height: 100%; | ||
20 | + font-size: 16px; | ||
21 | + line-height: 24px; | ||
17 | 22 | ||
23 | + .trip-animation-label-container { | ||
24 | + height: 24px; | ||
25 | + } | ||
26 | + | ||
27 | + .trip-animation-container { | ||
18 | position: relative; | 28 | position: relative; |
29 | + z-index: 1; | ||
30 | + flex: 1; | ||
19 | width: 100%; | 31 | width: 100%; |
20 | - height: 100%; | ||
21 | - font-size: 16px; | ||
22 | - line-height: 24px; | ||
23 | - | ||
24 | - .trip-animation-label-container { | ||
25 | - height: 24px; | ||
26 | - } | ||
27 | - | ||
28 | - .trip-animation-container { | ||
29 | - position: relative; | 32 | + |
33 | + #trip-animation-map { | ||
30 | z-index: 1; | 34 | z-index: 1; |
31 | - flex: 1; | ||
32 | width: 100%; | 35 | width: 100%; |
33 | - | ||
34 | - #trip-animation-map { | ||
35 | - z-index: 1; | ||
36 | - width: 100%; | ||
37 | - height: 100%; | ||
38 | - | ||
39 | - .pointsLayerMarkerIcon { | ||
40 | - border-radius: 50%; | ||
41 | - } | 36 | + height: 100%; |
37 | + | ||
38 | + .pointsLayerMarkerIcon { | ||
39 | + border-radius: 50%; | ||
42 | } | 40 | } |
43 | - | ||
44 | - .trip-animation-info-panel { | ||
45 | - position: absolute; | 41 | + } |
42 | + | ||
43 | + .trip-animation-info-panel { | ||
44 | + position: absolute; | ||
45 | + top: 0; | ||
46 | + right: 0; | ||
47 | + z-index: 2; | ||
48 | + pointer-events: none; | ||
49 | + | ||
50 | + .md-button { | ||
46 | top: 0; | 51 | top: 0; |
47 | - right: 0; | ||
48 | - z-index: 2; | ||
49 | - pointer-events: none; | ||
50 | - | ||
51 | - .md-button { | ||
52 | - top: 0; | ||
53 | - left: 0; | ||
54 | - width: 32px; | ||
55 | - min-width: 32px; | ||
56 | - height: 32px; | ||
57 | - min-height: 32px; | ||
58 | - padding: 0 0 2px; | ||
59 | - margin: 2px; | ||
60 | - line-height: 24px; | ||
61 | - | ||
62 | - ng-md-icon { | ||
63 | - width: 24px; | ||
64 | - height: 24px; | ||
65 | - | ||
66 | - svg { | ||
67 | - width: inherit; | ||
68 | - height: inherit; | ||
69 | - } | 52 | + left: 0; |
53 | + width: 32px; | ||
54 | + min-width: 32px; | ||
55 | + height: 32px; | ||
56 | + min-height: 32px; | ||
57 | + padding: 0 0 2px; | ||
58 | + margin: 2px; | ||
59 | + line-height: 24px; | ||
60 | + | ||
61 | + ng-md-icon { | ||
62 | + width: 24px; | ||
63 | + height: 24px; | ||
64 | + | ||
65 | + svg { | ||
66 | + width: inherit; | ||
67 | + height: inherit; | ||
70 | } | 68 | } |
71 | } | 69 | } |
72 | } | 70 | } |
73 | - | ||
74 | - .trip-animation-tooltip { | ||
75 | - position: absolute; | ||
76 | - top: 38px; | ||
77 | - right: 0; | ||
78 | - z-index: 2; | ||
79 | - padding: 10px; | ||
80 | - background-color: #fff; | ||
81 | - transition: .3s ease-in-out; | ||
82 | - | ||
83 | - &-hidden { | ||
84 | - transform: translateX(110%); | ||
85 | - } | 71 | + } |
72 | + | ||
73 | + .trip-animation-tooltip { | ||
74 | + position: absolute; | ||
75 | + top: 38px; | ||
76 | + right: 0; | ||
77 | + z-index: 2; | ||
78 | + padding: 10px; | ||
79 | + background-color: #fff; | ||
80 | + transition: 0.3s ease-in-out; | ||
81 | + | ||
82 | + &-hidden { | ||
83 | + transform: translateX(110%); | ||
86 | } | 84 | } |
87 | } | 85 | } |
88 | - | ||
89 | - .trip-animation-control-panel { | ||
90 | - position: relative; | ||
91 | - box-sizing: border-box; | ||
92 | - width: 100%; | ||
93 | - padding-bottom: 16px; | ||
94 | - padding-left: 10px; | ||
95 | - | ||
96 | - md-slider-container { | ||
97 | - md-slider { | ||
98 | - min-width: 80px; | ||
99 | - } | ||
100 | - | ||
101 | - button.md-button.md-icon-button { | ||
102 | - width: 44px; | ||
103 | - min-width: 44px; | ||
104 | - height: 44px; | ||
105 | - min-height: 44px; | ||
106 | - margin: 0; | ||
107 | - line-height: 28px; | ||
108 | - | ||
109 | - md-icon { | ||
110 | - width: 28px; | ||
111 | - height: 28px; | ||
112 | - font-size: 28px; | ||
113 | - | ||
114 | - svg { | ||
115 | - width: inherit; | ||
116 | - height: inherit; | ||
117 | - } | 86 | + } |
87 | + | ||
88 | + .trip-animation-control-panel { | ||
89 | + position: relative; | ||
90 | + box-sizing: border-box; | ||
91 | + width: 100%; | ||
92 | + padding-bottom: 16px; | ||
93 | + padding-left: 10px; | ||
94 | + | ||
95 | + md-slider-container { | ||
96 | + md-slider { | ||
97 | + min-width: 80px; | ||
98 | + } | ||
99 | + | ||
100 | + button.md-button.md-icon-button { | ||
101 | + width: 44px; | ||
102 | + min-width: 44px; | ||
103 | + height: 44px; | ||
104 | + min-height: 44px; | ||
105 | + margin: 0; | ||
106 | + line-height: 28px; | ||
107 | + | ||
108 | + md-icon { | ||
109 | + width: 28px; | ||
110 | + height: 28px; | ||
111 | + font-size: 28px; | ||
112 | + | ||
113 | + svg { | ||
114 | + width: inherit; | ||
115 | + height: inherit; | ||
118 | } | 116 | } |
119 | } | 117 | } |
120 | - | ||
121 | - md-select { | ||
122 | - margin: 0; | ||
123 | - } | ||
124 | } | 118 | } |
125 | - | ||
126 | - .panel-timer { | ||
127 | - max-width: none; | ||
128 | - padding-right: 250px; | ||
129 | - padding-left: 90px; | ||
130 | - margin-top: -20px; | ||
131 | - font-size: 12px; | ||
132 | - font-weight: 500; | ||
133 | - text-align: center; | 119 | + |
120 | + md-select { | ||
121 | + margin: 0; | ||
134 | } | 122 | } |
135 | } | 123 | } |
124 | + | ||
125 | + .panel-timer { | ||
126 | + max-width: none; | ||
127 | + padding-right: 250px; | ||
128 | + padding-left: 90px; | ||
129 | + margin-top: -20px; | ||
130 | + font-size: 12px; | ||
131 | + font-weight: 500; | ||
132 | + text-align: center; | ||
133 | + } | ||
134 | + } | ||
135 | +} | ||
136 | + | ||
137 | +.playpause { | ||
138 | + label { | ||
139 | + display: block; | ||
140 | + box-sizing: border-box; | ||
141 | + | ||
142 | + width: 0; | ||
143 | + height: 74px; | ||
144 | + | ||
145 | + cursor: pointer; | ||
146 | + | ||
147 | + border-color: transparent transparent transparent #202020; | ||
148 | + transition: 100ms all ease; | ||
149 | + will-change: border-width; | ||
150 | + | ||
151 | + // paused state | ||
152 | + border-style: double; | ||
153 | + border-width: 0px 0 0px 60px; | ||
154 | + } | ||
155 | + input[type="checkbox"] { | ||
156 | + visibility: hidden; | ||
157 | + | ||
158 | + &:checked + label { | ||
159 | + // play state | ||
160 | + border-style: solid; | ||
161 | + border-width: 37px 0 37px 60px; | ||
162 | + } | ||
136 | } | 163 | } |
137 | - | ||
164 | +} |
1 | -import { Component, OnInit, Input } from '@angular/core'; | 1 | +import { Component, OnInit, OnChanges, Input, Output, EventEmitter, ChangeDetectorRef } from '@angular/core'; |
2 | +import { interval } from 'rxjs'; | ||
3 | +import { filter, tap } from 'rxjs/operators'; | ||
2 | 4 | ||
3 | @Component({ | 5 | @Component({ |
4 | selector: 'tb-history-selector', | 6 | selector: 'tb-history-selector', |
5 | templateUrl: './history-selector.component.html', | 7 | templateUrl: './history-selector.component.html', |
6 | styleUrls: ['./history-selector.component.scss'] | 8 | styleUrls: ['./history-selector.component.scss'] |
7 | }) | 9 | }) |
8 | -export class HistorySelectorComponent implements OnInit { | 10 | +export class HistorySelectorComponent implements OnInit, OnChanges { |
9 | 11 | ||
10 | @Input() settings | 12 | @Input() settings |
13 | + @Input() intervals = []; | ||
11 | 14 | ||
12 | - animationTime | 15 | + @Output() onTimeUpdated = new EventEmitter(); |
13 | 16 | ||
17 | + animationTime; | ||
18 | + minTimeIndex = 0; | ||
19 | + maxTimeIndex = 0; | ||
20 | + speed = 1; | ||
21 | + index = 0; | ||
22 | + playing = false; | ||
23 | + interval; | ||
14 | 24 | ||
15 | - constructor() { } | 25 | + |
26 | + constructor(private cd: ChangeDetectorRef) { } | ||
16 | 27 | ||
17 | ngOnInit(): void { | 28 | ngOnInit(): void { |
18 | - console.log(this.settings); | ||
19 | - | 29 | + } |
30 | + | ||
31 | + ngOnChanges() { | ||
32 | + this.maxTimeIndex = this.intervals?.length - 1; | ||
33 | + } | ||
34 | + | ||
35 | + play() { | ||
36 | + this.playing = true; | ||
37 | + this.interval = interval(1000 / this.speed) | ||
38 | + .pipe( | ||
39 | + filter(() => this.playing), | ||
40 | + tap(() => this.index++)).subscribe(() => { | ||
41 | + if (this.index < this.maxTimeIndex) { | ||
42 | + this.cd.detectChanges(); | ||
43 | + this.onTimeUpdated.emit(this.intervals[this.index]); | ||
44 | + } | ||
45 | + else { | ||
46 | + this.interval.complete(); | ||
47 | + } | ||
48 | + }, err => { | ||
49 | + console.log(err); | ||
50 | + }, () => { | ||
51 | + this.index = this.minTimeIndex; | ||
52 | + this.playing = false; | ||
53 | + }) | ||
54 | + } | ||
55 | + | ||
56 | + pause() { | ||
57 | + this.playing = false; | ||
58 | + } | ||
59 | + /* | ||
60 | + setSpeed() { | ||
61 | + if (this.interval) this.interval.unsubscribe(); | ||
62 | + this.interval = interval(1000 / this.speed) | ||
63 | + .pipe( | ||
64 | + takeWhile(() => this.index < this.maxTimeIndex), | ||
65 | + filter(() => this.play), | ||
66 | + tap(() => this.index++)) | ||
67 | + .subscribe(() => { | ||
68 | + console.log(this.intervals); | ||
69 | + | ||
70 | + this.onTimeUpdated.emit(this.intervals[this.index]); | ||
71 | + | ||
72 | + }, err => { | ||
73 | + console.log(err); | ||
74 | + }, () => { | ||
75 | + this.index = this.minTimeIndex; | ||
76 | + this.play = false; | ||
77 | + }) | ||
78 | + }*/ | ||
79 | + | ||
80 | + | ||
81 | + | ||
82 | + | ||
83 | + /* | ||
84 | + | ||
85 | + playMove(play) { | ||
86 | + if (play && this.isPlaying) return; | ||
87 | + if (play || this.isPlaying) this.isPlaying = true; | ||
88 | + if (this.isPlaying) { | ||
89 | + moveInc(1); | ||
90 | + this.timeout = $timeout(function () { | ||
91 | + this.playMove(); | ||
92 | + }, 1000 / this.speed) | ||
93 | + } | ||
94 | + }; | ||
95 | + | ||
96 | + moveNext() { | ||
97 | + this.stopPlay(); | ||
98 | + if (this.staticSettings.usePointAsAnchor) { | ||
99 | + let newIndex = this.maxTimeIndex; | ||
100 | + for (let index = this.index + 1; index < this.maxTimeIndex; index++) { | ||
101 | + if (this.trips.some(function (trip) { | ||
102 | + return calculateCurrentDate(trip.timeRange, index).hasAnchor; | ||
103 | + })) { | ||
104 | + newIndex = index; | ||
105 | + break; | ||
106 | + } | ||
107 | + } | ||
108 | + this.moveToIndex(newIndex); | ||
109 | + } else moveInc(1); | ||
110 | + }; | ||
111 | + | ||
112 | + movePrev() { | ||
113 | + this.stopPlay(); | ||
114 | + if (this.staticSettings.usePointAsAnchor) { | ||
115 | + let newIndex = this.minTimeIndex; | ||
116 | + for (let index = this.index - 1; index > this.minTimeIndex; index--) { | ||
117 | + if (this.trips.some(function (trip) { | ||
118 | + return calculateCurrentDate(trip.timeRange, index).hasAnchor; | ||
119 | + })) { | ||
120 | + newIndex = index; | ||
121 | + break; | ||
122 | + } | ||
123 | + } | ||
124 | + this.moveToIndex(newIndex); | ||
125 | + } else moveInc(-1); | ||
126 | + }; | ||
127 | + | ||
128 | + moveStart = function () { | ||
129 | + this.stopPlay(); | ||
130 | + this.moveToIndex(this.minTimeIndex); | ||
131 | + }; | ||
132 | + | ||
133 | + moveEnd = function () { | ||
134 | + this.stopPlay(); | ||
135 | + this.moveToIndex(this.maxTimeIndex); | ||
136 | + }; | ||
137 | + | ||
138 | + stopPlay = function () { | ||
139 | + if (this.isPlaying) { | ||
140 | + this.isPlaying = false; | ||
141 | + $timeout.cancel(this.timeout); | ||
142 | + } | ||
143 | + }; | ||
144 | + | ||
145 | + moveInc(inc) { | ||
146 | + let newIndex = this.index + inc; | ||
147 | + this.moveToIndex(newIndex); | ||
148 | + } | ||
149 | + | ||
150 | + moveToIndex(newIndex) { | ||
151 | + if (newIndex > this.maxTimeIndex || newIndex < this.minTimeIndex) return; | ||
152 | + this.index = newIndex; | ||
153 | + this.animationTime = this.minTime + this.index * this.staticSettings.normalizationStep; | ||
154 | + recalculateTrips(); | ||
155 | + } | ||
156 | + */ | ||
157 | + recalculateTrips() { | ||
158 | + | ||
20 | } | 159 | } |
21 | 160 | ||
22 | } | 161 | } |