diff --git a/ui-ngx/package-lock.json b/ui-ngx/package-lock.json index a5565bb937..94142b8bbf 100644 --- a/ui-ngx/package-lock.json +++ b/ui-ngx/package-lock.json @@ -1900,7 +1900,8 @@ "@types/lodash": { "version": "4.14.149", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz", - "integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==" + "integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==", + "dev": true }, "@types/minimatch": { "version": "3.0.3", @@ -8263,6 +8264,14 @@ "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.6.0.tgz", "integrity": "sha512-CPkhyqWUKZKFJ6K8umN5/D2wrJ2+/8UIpXppY7QDnUZW5bZL5+SEI2J7GBpwh4LIupOKqbNSQXgqmrEJopHVNQ==" }, + "leaflet-geometryutil": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/leaflet-geometryutil/-/leaflet-geometryutil-0.9.3.tgz", + "integrity": "sha512-Wi6YvfNx/Xu9q35AEfXpsUXmIFLen/MO+C2qimxHRnjyeyOxBhdcZa6kSiReaOX0cGK7yQInqrzz0dkIqZ8Dpg==", + "requires": { + "leaflet": "1.6.0" + } + }, "leaflet-providers": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/leaflet-providers/-/leaflet-providers-1.9.1.tgz", diff --git a/ui-ngx/package.json b/ui-ngx/package.json index 39d60903e1..0d9fe74b06 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -57,6 +57,7 @@ "jstree": "^3.3.9", "jstree-bootstrap-theme": "^1.0.1", "leaflet": "^1.6.0", + "leaflet-geometryutil": "^0.9.3", "leaflet-providers": "^1.9.1", "leaflet.gridlayer.googlemutant": "^0.8.0", "leaflet.markercluster": "^1.4.1", diff --git a/ui-ngx/src/app/core/utils.ts b/ui-ngx/src/app/core/utils.ts index 063a830bbb..a1c4c8024e 100644 --- a/ui-ngx/src/app/core/utils.ts +++ b/ui-ngx/src/app/core/utils.ts @@ -451,13 +451,13 @@ export function aspectCache(imageUrl: string): Observable { export function parseArray(input: any[]): any[] { let alliases: any = _(input).groupBy(el => el?.datasource?.aliasName).values().value(); - console.log("alliases", alliases) return alliases.map((alliasArray, dsIndex) => alliasArray[0].data.map((el, i) => { const obj = { aliasName: alliasArray[0]?.datasource?.aliasName, $datasource: alliasArray[0]?.datasource, - dsIndex: dsIndex + dsIndex: dsIndex, + time: el[0] }; alliasArray.forEach(el => { obj[el?.dataKey?.label] = el?.data[i][1]; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts index 59486f4c1d..3ba7c80b08 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts @@ -73,6 +73,10 @@ export class MapWidgetController implements MapWidgetInterface { this.map.updateMarkers(parseData(this.data)); } + public updateHistoryData(dataSources){ + dataSources.map() + } + onDataUpdated() { } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts index 9a250c8d5e..5034fbe8f1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts @@ -1,8 +1,9 @@ import L from 'leaflet'; +import { interpolateOnPointSegment } from 'leaflet-geometryutil'; import _ from 'lodash'; export function createTooltip(target, settings, targetArgs?) { - var popup = L.popup(); + const popup = L.popup(); popup.setContent(''); target.bindPopup(popup, { autoClose: settings.autocloseTooltip, closeOnClick: false }); if (settings.displayTooltipAction == 'hover') { @@ -21,3 +22,27 @@ export function createTooltip(target, settings, targetArgs?) { dsIndex: settings.dsIndex }; } + + +export function interpolateArray(originData, interpolatedIntervals) { + + const getRatio = (firsMoment, secondMoment, intermediateMoment) => { + return (intermediateMoment - firsMoment) / (secondMoment - firsMoment); + }; + + const result = {}; + + for (let i = 1, j = 0; i < originData.length, j < interpolatedIntervals.length;) { + const currentTime = interpolatedIntervals[j]; + while (originData[i].time < currentTime) i++; + const before = originData[i - 1]; + const after = originData[i]; + result[currentTime] = (interpolateOnPointSegment( + new L.Point(before.latitude, before.longitude), + new L.Point(after.latitude, after.longitude), + getRatio(before.time, after.time, currentTime))); + j++; + } + + return result; +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.html b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.html index a0de5d5f37..a1a7dbb30a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.html @@ -1,2 +1,4 @@
- \ No newline at end of file +
{{historicalData?.lenth}}
+ diff --git a/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts index 271c517a3d..6903bbfa65 100644 --- a/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts @@ -1,7 +1,8 @@ -import { Component, OnInit, Input, ViewChild, AfterViewInit } from '@angular/core'; +import { Component, OnInit, Input, ViewChild, AfterViewInit, ChangeDetectorRef } from '@angular/core'; import { MapWidgetController } from '../lib/maps/map-widget2'; import { MapProviders } from '../lib/maps/map-models'; import { parseArray } from '@app/core/utils'; +import { interpolateArray } from '../lib/maps/maps-utils'; @Component({ selector: 'trip-animation', @@ -15,18 +16,50 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @ViewChild('map') mapContainer; mapWidget: MapWidgetController; - historicalData + historicalData; + intervals; + normalizationStep = 500; + interpolatedData = []; - constructor() { } + + constructor(private cd: ChangeDetectorRef) { } ngOnInit(): void { - console.log(this.ctx); - this.historicalData = parseArray(this.ctx.data); - console.log("TripAnimationComponent -> ngOnInit -> this.historicalData",this.ctx.data, this.historicalData) + let subscription = this.ctx.subscriptions[Object.keys(this.ctx.subscriptions)[0]]; + if (subscription) subscription.callbacks.onDataUpdated = (updated) => { + this.historicalData = parseArray(this.ctx.data); + this.historicalData.forEach(el => { + console.log("TripAnimationComponent -> if -> el", el) + el.longitude += (Math.random() - 0.5) + el.latitude += (Math.random() - 0.5) + }); + this.calculateIntervals(); + this.cd.detectChanges(); + } } ngAfterViewInit() { this.mapWidget = new MapWidgetController(MapProviders.openstreet, false, this.ctx, this.mapContainer.nativeElement); this.mapWidget.data } + + timeUpdated(time) { + //this.mapWidget.ma + const currentPosition = this.interpolatedData.map(dataSource=>dataSource[time]); + console.log("TripAnimationComponent -> timeUpdated -> currentPosition", currentPosition) + } + + calculateIntervals() { + this.historicalData.forEach((dataSource, index) => { + this.intervals = []; + for (let time = dataSource[0]?.time; time < dataSource[dataSource.length - 1]?.time; time += this.normalizationStep) { + this.intervals.push(time); + } + this.intervals.push(dataSource[dataSource.length - 1]?.time); + this.interpolatedData[index] = interpolateArray(dataSource, this.intervals); + console.log("TripAnimationComponent -> calculateIntervals -> this.intervals", this.intervals) + + }); + } + } diff --git a/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.html b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.html index 5a64d88bf2..985ef29c49 100644 --- a/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.html +++ b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.html @@ -6,34 +6,41 @@ - + + - - - + -->
{{ animationTime | date:'medium'}} {{ "widget.no-data-found" | translate}} -
\ No newline at end of file + + +
{{index}}
\ No newline at end of file diff --git a/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.scss b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.scss index 6ca4906263..a8fa1c71fb 100644 --- a/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.scss +++ b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.scss @@ -13,125 +13,152 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - .trip-animation-widget { +.trip-animation-widget { + position: relative; + width: 100%; + height: 100%; + font-size: 16px; + line-height: 24px; + .trip-animation-label-container { + height: 24px; + } + + .trip-animation-container { position: relative; + z-index: 1; + flex: 1; width: 100%; - height: 100%; - font-size: 16px; - line-height: 24px; - - .trip-animation-label-container { - height: 24px; - } - - .trip-animation-container { - position: relative; + + #trip-animation-map { z-index: 1; - flex: 1; width: 100%; - - #trip-animation-map { - z-index: 1; - width: 100%; - height: 100%; - - .pointsLayerMarkerIcon { - border-radius: 50%; - } + height: 100%; + + .pointsLayerMarkerIcon { + border-radius: 50%; } - - .trip-animation-info-panel { - position: absolute; + } + + .trip-animation-info-panel { + position: absolute; + top: 0; + right: 0; + z-index: 2; + pointer-events: none; + + .md-button { top: 0; - right: 0; - z-index: 2; - pointer-events: none; - - .md-button { - top: 0; - left: 0; - width: 32px; - min-width: 32px; - height: 32px; - min-height: 32px; - padding: 0 0 2px; - margin: 2px; - line-height: 24px; - - ng-md-icon { - width: 24px; - height: 24px; - - svg { - width: inherit; - height: inherit; - } + left: 0; + width: 32px; + min-width: 32px; + height: 32px; + min-height: 32px; + padding: 0 0 2px; + margin: 2px; + line-height: 24px; + + ng-md-icon { + width: 24px; + height: 24px; + + svg { + width: inherit; + height: inherit; } } } - - .trip-animation-tooltip { - position: absolute; - top: 38px; - right: 0; - z-index: 2; - padding: 10px; - background-color: #fff; - transition: .3s ease-in-out; - - &-hidden { - transform: translateX(110%); - } + } + + .trip-animation-tooltip { + position: absolute; + top: 38px; + right: 0; + z-index: 2; + padding: 10px; + background-color: #fff; + transition: 0.3s ease-in-out; + + &-hidden { + transform: translateX(110%); } } - - .trip-animation-control-panel { - position: relative; - box-sizing: border-box; - width: 100%; - padding-bottom: 16px; - padding-left: 10px; - - md-slider-container { - md-slider { - min-width: 80px; - } - - button.md-button.md-icon-button { - width: 44px; - min-width: 44px; - height: 44px; - min-height: 44px; - margin: 0; - line-height: 28px; - - md-icon { - width: 28px; - height: 28px; - font-size: 28px; - - svg { - width: inherit; - height: inherit; - } + } + + .trip-animation-control-panel { + position: relative; + box-sizing: border-box; + width: 100%; + padding-bottom: 16px; + padding-left: 10px; + + md-slider-container { + md-slider { + min-width: 80px; + } + + button.md-button.md-icon-button { + width: 44px; + min-width: 44px; + height: 44px; + min-height: 44px; + margin: 0; + line-height: 28px; + + md-icon { + width: 28px; + height: 28px; + font-size: 28px; + + svg { + width: inherit; + height: inherit; } } - - md-select { - margin: 0; - } } - - .panel-timer { - max-width: none; - padding-right: 250px; - padding-left: 90px; - margin-top: -20px; - font-size: 12px; - font-weight: 500; - text-align: center; + + md-select { + margin: 0; } } + + .panel-timer { + max-width: none; + padding-right: 250px; + padding-left: 90px; + margin-top: -20px; + font-size: 12px; + font-weight: 500; + text-align: center; + } + } +} + +.playpause { + label { + display: block; + box-sizing: border-box; + + width: 0; + height: 74px; + + cursor: pointer; + + border-color: transparent transparent transparent #202020; + transition: 100ms all ease; + will-change: border-width; + + // paused state + border-style: double; + border-width: 0px 0 0px 60px; + } + input[type="checkbox"] { + visibility: hidden; + + &:checked + label { + // play state + border-style: solid; + border-width: 37px 0 37px 60px; + } } - \ No newline at end of file +} diff --git a/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts index 8d1c686382..8ae52c9611 100644 --- a/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts +++ b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts @@ -1,22 +1,161 @@ -import { Component, OnInit, Input } from '@angular/core'; +import { Component, OnInit, OnChanges, Input, Output, EventEmitter, ChangeDetectorRef } from '@angular/core'; +import { interval } from 'rxjs'; +import { filter, tap } from 'rxjs/operators'; @Component({ selector: 'tb-history-selector', templateUrl: './history-selector.component.html', styleUrls: ['./history-selector.component.scss'] }) -export class HistorySelectorComponent implements OnInit { +export class HistorySelectorComponent implements OnInit, OnChanges { @Input() settings + @Input() intervals = []; - animationTime + @Output() onTimeUpdated = new EventEmitter(); + animationTime; + minTimeIndex = 0; + maxTimeIndex = 0; + speed = 1; + index = 0; + playing = false; + interval; - constructor() { } + + constructor(private cd: ChangeDetectorRef) { } ngOnInit(): void { - console.log(this.settings); - + } + + ngOnChanges() { + this.maxTimeIndex = this.intervals?.length - 1; + } + + play() { + this.playing = true; + this.interval = interval(1000 / this.speed) + .pipe( + filter(() => this.playing), + tap(() => this.index++)).subscribe(() => { + if (this.index < this.maxTimeIndex) { + this.cd.detectChanges(); + this.onTimeUpdated.emit(this.intervals[this.index]); + } + else { + this.interval.complete(); + } + }, err => { + console.log(err); + }, () => { + this.index = this.minTimeIndex; + this.playing = false; + }) + } + + pause() { + this.playing = false; + } + /* + setSpeed() { + if (this.interval) this.interval.unsubscribe(); + this.interval = interval(1000 / this.speed) + .pipe( + takeWhile(() => this.index < this.maxTimeIndex), + filter(() => this.play), + tap(() => this.index++)) + .subscribe(() => { + console.log(this.intervals); + + this.onTimeUpdated.emit(this.intervals[this.index]); + + }, err => { + console.log(err); + }, () => { + this.index = this.minTimeIndex; + this.play = false; + }) + }*/ + + + + + /* + + playMove(play) { + if (play && this.isPlaying) return; + if (play || this.isPlaying) this.isPlaying = true; + if (this.isPlaying) { + moveInc(1); + this.timeout = $timeout(function () { + this.playMove(); + }, 1000 / this.speed) + } + }; + + moveNext() { + this.stopPlay(); + if (this.staticSettings.usePointAsAnchor) { + let newIndex = this.maxTimeIndex; + for (let index = this.index + 1; index < this.maxTimeIndex; index++) { + if (this.trips.some(function (trip) { + return calculateCurrentDate(trip.timeRange, index).hasAnchor; + })) { + newIndex = index; + break; + } + } + this.moveToIndex(newIndex); + } else moveInc(1); + }; + + movePrev() { + this.stopPlay(); + if (this.staticSettings.usePointAsAnchor) { + let newIndex = this.minTimeIndex; + for (let index = this.index - 1; index > this.minTimeIndex; index--) { + if (this.trips.some(function (trip) { + return calculateCurrentDate(trip.timeRange, index).hasAnchor; + })) { + newIndex = index; + break; + } + } + this.moveToIndex(newIndex); + } else moveInc(-1); + }; + + moveStart = function () { + this.stopPlay(); + this.moveToIndex(this.minTimeIndex); + }; + + moveEnd = function () { + this.stopPlay(); + this.moveToIndex(this.maxTimeIndex); + }; + + stopPlay = function () { + if (this.isPlaying) { + this.isPlaying = false; + $timeout.cancel(this.timeout); + } + }; + + moveInc(inc) { + let newIndex = this.index + inc; + this.moveToIndex(newIndex); + } + + moveToIndex(newIndex) { + if (newIndex > this.maxTimeIndex || newIndex < this.minTimeIndex) return; + this.index = newIndex; + this.animationTime = this.minTime + this.index * this.staticSettings.normalizationStep; + recalculateTrips(); + } + */ + recalculateTrips() { + } }