Browse Source

WIP on history control and route interpolation

pull/2535/head
Artem Halushko 7 years ago
parent
commit
5535c33d0e
  1. 11
      ui-ngx/package-lock.json
  2. 1
      ui-ngx/package.json
  3. 4
      ui-ngx/src/app/core/utils.ts
  4. 4
      ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts
  5. 27
      ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts
  6. 4
      ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.html
  7. 45
      ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts
  8. 27
      ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.html
  9. 235
      ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.scss
  10. 151
      ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts

11
ui-ngx/package-lock.json

@ -1900,7 +1900,8 @@
"@types/lodash": { "@types/lodash": {
"version": "4.14.149", "version": "4.14.149",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz", "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": { "@types/minimatch": {
"version": "3.0.3", "version": "3.0.3",
@ -8263,6 +8264,14 @@
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.6.0.tgz", "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.6.0.tgz",
"integrity": "sha512-CPkhyqWUKZKFJ6K8umN5/D2wrJ2+/8UIpXppY7QDnUZW5bZL5+SEI2J7GBpwh4LIupOKqbNSQXgqmrEJopHVNQ==" "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": { "leaflet-providers": {
"version": "1.9.1", "version": "1.9.1",
"resolved": "https://registry.npmjs.org/leaflet-providers/-/leaflet-providers-1.9.1.tgz", "resolved": "https://registry.npmjs.org/leaflet-providers/-/leaflet-providers-1.9.1.tgz",

1
ui-ngx/package.json

@ -57,6 +57,7 @@
"jstree": "^3.3.9", "jstree": "^3.3.9",
"jstree-bootstrap-theme": "^1.0.1", "jstree-bootstrap-theme": "^1.0.1",
"leaflet": "^1.6.0", "leaflet": "^1.6.0",
"leaflet-geometryutil": "^0.9.3",
"leaflet-providers": "^1.9.1", "leaflet-providers": "^1.9.1",
"leaflet.gridlayer.googlemutant": "^0.8.0", "leaflet.gridlayer.googlemutant": "^0.8.0",
"leaflet.markercluster": "^1.4.1", "leaflet.markercluster": "^1.4.1",

4
ui-ngx/src/app/core/utils.ts

@ -451,13 +451,13 @@ export function aspectCache(imageUrl: string): Observable<number> {
export function parseArray(input: any[]): any[] { export function parseArray(input: any[]): any[] {
let alliases: any = _(input).groupBy(el => el?.datasource?.aliasName).values().value(); let alliases: any = _(input).groupBy(el => el?.datasource?.aliasName).values().value();
console.log("alliases", alliases)
return alliases.map((alliasArray, dsIndex) => return alliases.map((alliasArray, dsIndex) =>
alliasArray[0].data.map((el, i) => { alliasArray[0].data.map((el, i) => {
const obj = { const obj = {
aliasName: alliasArray[0]?.datasource?.aliasName, aliasName: alliasArray[0]?.datasource?.aliasName,
$datasource: alliasArray[0]?.datasource, $datasource: alliasArray[0]?.datasource,
dsIndex: dsIndex dsIndex: dsIndex,
time: el[0]
}; };
alliasArray.forEach(el => { alliasArray.forEach(el => {
obj[el?.dataKey?.label] = el?.data[i][1]; obj[el?.dataKey?.label] = el?.data[i][1];

4
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)); this.map.updateMarkers(parseData(this.data));
} }
public updateHistoryData(dataSources){
dataSources.map()
}
onDataUpdated() { onDataUpdated() {
} }

27
ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts

@ -1,8 +1,9 @@
import L from 'leaflet'; import L from 'leaflet';
import { interpolateOnPointSegment } from 'leaflet-geometryutil';
import _ from 'lodash'; import _ from 'lodash';
export function createTooltip(target, settings, targetArgs?) { export function createTooltip(target, settings, targetArgs?) {
var popup = L.popup(); const popup = L.popup();
popup.setContent(''); popup.setContent('');
target.bindPopup(popup, { autoClose: settings.autocloseTooltip, closeOnClick: false }); target.bindPopup(popup, { autoClose: settings.autocloseTooltip, closeOnClick: false });
if (settings.displayTooltipAction == 'hover') { if (settings.displayTooltipAction == 'hover') {
@ -21,3 +22,27 @@ export function createTooltip(target, settings, targetArgs?) {
dsIndex: settings.dsIndex 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;
};

4
ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.html

@ -1,2 +1,4 @@
<div class="map" #map ></div> <div class="map" #map ></div>
<tb-history-selector [settings]="ctx.settings"></tb-history-selector> <div>{{historicalData?.lenth}}</div>
<tb-history-selector *ngIf="historicalData" [settings]="ctx.settings" [intervals]="intervals"
(onTimeUpdated)="timeUpdated($event)"></tb-history-selector>

45
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 { MapWidgetController } from '../lib/maps/map-widget2';
import { MapProviders } from '../lib/maps/map-models'; import { MapProviders } from '../lib/maps/map-models';
import { parseArray } from '@app/core/utils'; import { parseArray } from '@app/core/utils';
import { interpolateArray } from '../lib/maps/maps-utils';
@Component({ @Component({
selector: 'trip-animation', selector: 'trip-animation',
@ -15,18 +16,50 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
@ViewChild('map') mapContainer; @ViewChild('map') mapContainer;
mapWidget: MapWidgetController; mapWidget: MapWidgetController;
historicalData historicalData;
intervals;
normalizationStep = 500;
interpolatedData = [];
constructor() { }
constructor(private cd: ChangeDetectorRef) { }
ngOnInit(): void { ngOnInit(): void {
console.log(this.ctx); let subscription = this.ctx.subscriptions[Object.keys(this.ctx.subscriptions)[0]];
this.historicalData = parseArray(this.ctx.data); if (subscription) subscription.callbacks.onDataUpdated = (updated) => {
console.log("TripAnimationComponent -> ngOnInit -> this.historicalData",this.ctx.data, this.historicalData) 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() { ngAfterViewInit() {
this.mapWidget = new MapWidgetController(MapProviders.openstreet, false, this.ctx, this.mapContainer.nativeElement); this.mapWidget = new MapWidgetController(MapProviders.openstreet, false, this.ctx, this.mapContainer.nativeElement);
this.mapWidget.data 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)
});
}
} }

27
ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.html

@ -6,34 +6,41 @@
<button mat-icon-button class="mat-icon-button" aria-label="Previous" ng-click="movePrev()"> <button mat-icon-button class="mat-icon-button" aria-label="Previous" ng-click="movePrev()">
<mat-icon class="material-icons" ng-style="{'color': staticSettings.buttonColor}">skip_previous</mat-icon> <mat-icon class="material-icons" ng-style="{'color': staticSettings.buttonColor}">skip_previous</mat-icon>
</button> </button>
<!-- <mat-slider ng-model="index" [min]="minTimeIndex" [max]="maxTimeIndex" (change)="recalculateTrips()"> <mat-slider [(ngModel)]="index" [min]="minTimeIndex" [max]="maxTimeIndex" (change)="recalculateTrips()">
</mat-slider>--> </mat-slider>
<button mat-icon-button class="mat-icon-button" aria-label="Next" ng-click="moveNext()"> <button mat-icon-button class="mat-icon-button" aria-label="Next" ng-click="moveNext()">
<mat-icon class="material-icons" ng-style="{'color': staticSettings.buttonColor}">skip_next</mat-icon> <mat-icon class="material-icons" ng-style="{'color': staticSettings.buttonColor}">skip_next</mat-icon>
</button> </button>
<button mat-icon-button class="mat-icon-button" aria-label="End" ng-click="moveEnd()"> <button mat-icon-button class="mat-icon-button" aria-label="End" ng-click="moveEnd()">
<mat-icon class="material-icons" ng-style="{'color': staticSettings.buttonColor}">fast_forward</mat-icon> <mat-icon class="material-icons" ng-style="{'color': staticSettings.buttonColor}">fast_forward</mat-icon>
</button> </button>
<button mat-icon-button class="mat-icon-button" aria-label="Play" ng-click="playMove(true)" <!--<div class="play-pause">
ng-disabled="isPlaying"> <input type="checkbox" [(ngModel)]="play" value="" id="playPauseCheckbox" name="playPauseCheckbox" />
<mat-icon class="material-icons" <label for="playPauseCheckbox"></label>
ng-style="{'color': isPlaying ? staticSettings.disabledButtonColor : staticSettings.buttonColor}"> </div>-->
<button mat-icon-button class="mat-icon-button" aria-label="Play" >
<mat-icon (click)="play()" *ngIf="!playing" class="material-icons" ng-style="{'color': settings.buttonColor}">
play_circle_outline play_circle_outline
</mat-icon> </mat-icon>
<mat-icon (click)="pause()" *ngIf="playing" class="material-icons" ng-style="{'color': settings.buttonColor}">
pause_circle_outline
</mat-icon>
</button> </button>
<!-- <mat-select ng-model="speed" aria-label="Speed selector"> <!-- <mat-select [(ngModel)]="speed" aria-label="Speed selector">
<mat-option ng-value="speed" flex="1" ng-repeat="speed in speeds track by $index">{{ speed}} <mat-option ng-value="speed" flex="1" ng-repeat="speed in speeds track by $index">{{ speed}}
</mat-option> </mat-option>
</mat-select>--> </mat-select>-->
<button mat-icon-button class="mat-icon-button" aria-label="Stop playing" ng-click="stopPlay()" <!-- <button mat-icon-button class="mat-icon-button" aria-label="Stop playing" ng-click="stopPlay()"
ng-disabled="!isPlaying"> ng-disabled="!isPlaying">
<mat-icon class="material-icons" <mat-icon class="material-icons"
ng-style="{'color': isPlaying ? staticSettings.buttonColor : staticSettings.disabledButtonColor}"> ng-style="{'color': isPlaying ? staticSettings.buttonColor : staticSettings.disabledButtonColor}">
pause_circle_outline pause_circle_outline
</mat-icon> </mat-icon>
</button> </button>-->
</div> </div>
<div class="panel-timer"> <div class="panel-timer">
<span *ngIf="animationTime">{{ animationTime | date:'medium'}}</span> <span *ngIf="animationTime">{{ animationTime | date:'medium'}}</span>
<span *ngIf="!animationTime">{{ "widget.no-data-found" | translate}}</span> <span *ngIf="!animationTime">{{ "widget.no-data-found" | translate}}</span>
</div> </div>
<div>{{index}}</div>

235
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 * See the License for the specific language governing permissions and
* limitations under the License. * 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; position: relative;
z-index: 1;
flex: 1;
width: 100%; width: 100%;
height: 100%;
font-size: 16px; #trip-animation-map {
line-height: 24px;
.trip-animation-label-container {
height: 24px;
}
.trip-animation-container {
position: relative;
z-index: 1; z-index: 1;
flex: 1;
width: 100%; width: 100%;
height: 100%;
#trip-animation-map {
z-index: 1; .pointsLayerMarkerIcon {
width: 100%; 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; top: 0;
right: 0; left: 0;
z-index: 2; width: 32px;
pointer-events: none; min-width: 32px;
height: 32px;
.md-button { min-height: 32px;
top: 0; padding: 0 0 2px;
left: 0; margin: 2px;
width: 32px; line-height: 24px;
min-width: 32px;
height: 32px; ng-md-icon {
min-height: 32px; width: 24px;
padding: 0 0 2px; height: 24px;
margin: 2px;
line-height: 24px; svg {
width: inherit;
ng-md-icon { height: inherit;
width: 24px;
height: 24px;
svg {
width: inherit;
height: inherit;
}
} }
} }
} }
}
.trip-animation-tooltip {
position: absolute; .trip-animation-tooltip {
top: 38px; position: absolute;
right: 0; top: 38px;
z-index: 2; right: 0;
padding: 10px; z-index: 2;
background-color: #fff; padding: 10px;
transition: .3s ease-in-out; background-color: #fff;
transition: 0.3s ease-in-out;
&-hidden {
transform: translateX(110%); &-hidden {
} transform: translateX(110%);
} }
} }
}
.trip-animation-control-panel {
position: relative; .trip-animation-control-panel {
box-sizing: border-box; position: relative;
width: 100%; box-sizing: border-box;
padding-bottom: 16px; width: 100%;
padding-left: 10px; padding-bottom: 16px;
padding-left: 10px;
md-slider-container {
md-slider { md-slider-container {
min-width: 80px; md-slider {
} min-width: 80px;
}
button.md-button.md-icon-button {
width: 44px; button.md-button.md-icon-button {
min-width: 44px; width: 44px;
height: 44px; min-width: 44px;
min-height: 44px; height: 44px;
margin: 0; min-height: 44px;
line-height: 28px; margin: 0;
line-height: 28px;
md-icon {
width: 28px; md-icon {
height: 28px; width: 28px;
font-size: 28px; height: 28px;
font-size: 28px;
svg {
width: inherit; svg {
height: inherit; width: inherit;
} height: inherit;
} }
} }
md-select {
margin: 0;
}
} }
.panel-timer { md-select {
max-width: none; margin: 0;
padding-right: 250px;
padding-left: 90px;
margin-top: -20px;
font-size: 12px;
font-weight: 500;
text-align: center;
} }
} }
.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;
}
} }
}

151
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({ @Component({
selector: 'tb-history-selector', selector: 'tb-history-selector',
templateUrl: './history-selector.component.html', templateUrl: './history-selector.component.html',
styleUrls: ['./history-selector.component.scss'] styleUrls: ['./history-selector.component.scss']
}) })
export class HistorySelectorComponent implements OnInit { export class HistorySelectorComponent implements OnInit, OnChanges {
@Input() settings @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 { 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() {
} }
} }

Loading…
Cancel
Save