WIP on trip animation widget
This commit is contained in:
parent
5d33da4b09
commit
be770518d7
@ -38,7 +38,6 @@
|
||||
"@ngx-share/core": "^7.1.4",
|
||||
"@ngx-translate/core": "^12.1.1",
|
||||
"@ngx-translate/http-loader": "^4.0.0",
|
||||
"@types/lodash": "^4.14.149",
|
||||
"ace-builds": "^1.4.8",
|
||||
"angular-gridster2": "^9.0.1",
|
||||
"angular2-hotkeys": "^2.1.5",
|
||||
@ -105,6 +104,7 @@
|
||||
"@types/js-beautify": "^1.8.1",
|
||||
"@types/jstree": "^3.3.39",
|
||||
"@types/leaflet": "^1.5.9",
|
||||
"@types/lodash": "^4.14.149",
|
||||
"@types/raphael": "^2.1.30",
|
||||
"@types/react": "^16.9.20",
|
||||
"@types/react-dom": "^16.9.5",
|
||||
|
||||
@ -447,3 +447,73 @@ export function aspectCache(imageUrl: string): Observable<number> {
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
};
|
||||
alliasArray.forEach(el => {
|
||||
obj[el?.dataKey?.label] = el?.data[i][1];
|
||||
obj[el?.dataKey?.label + '|ts'] = el?.data[0][0];
|
||||
if (el?.dataKey?.label == 'type') {
|
||||
obj['deviceType'] = el?.data[0][1];
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export function parseData(input: any[]): any[] {
|
||||
return _(input).groupBy(el => el?.datasource?.aliasName).values().value().map((alliasArray, i) => {
|
||||
const obj = {
|
||||
aliasName: alliasArray[0]?.datasource?.aliasName,
|
||||
entityName: alliasArray[0]?.datasource?.entityName,
|
||||
$datasource: alliasArray[0]?.datasource,
|
||||
dsIndex: i
|
||||
};
|
||||
alliasArray.forEach(el => {
|
||||
obj[el?.dataKey?.label] = el?.data[0][1];
|
||||
obj[el?.dataKey?.label + '|ts'] = el?.data[0][0];
|
||||
if (el?.dataKey?.label == 'type') {
|
||||
obj['deviceType'] = el?.data[0][1];
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
|
||||
export function safeExecute(func: Function, params = []) {
|
||||
let res = null;
|
||||
if (func && typeof (func) == "function") {
|
||||
try {
|
||||
res = func(...params);
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
res = null;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
export function parseFunction(source: string, params: string[] = []): Function {
|
||||
let res = null;
|
||||
if (source?.length) {
|
||||
try {
|
||||
res = new Function(...params, source);
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
res = null;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@ -14,10 +14,9 @@ import {
|
||||
} from './schemes';
|
||||
import { MapWidgetStaticInterface, MapWidgetInterface } from './map-widget.interface';
|
||||
import { OpenStreetMap, TencentMap, GoogleMap, HEREMap, ImageMap } from './providers';
|
||||
import { parseData, parseArray, parseFunction } from './maps-utils';
|
||||
import { parseFunction, parseArray, parseData } from '@app/core/utils';
|
||||
|
||||
export let TbMapWidgetV2: MapWidgetStaticInterface;
|
||||
TbMapWidgetV2 = class TbMapWidgetV2 implements MapWidgetInterface {
|
||||
export class MapWidgetController implements MapWidgetInterface {
|
||||
|
||||
map: LeafletMap;
|
||||
provider: MapProviders;
|
||||
@ -201,6 +200,9 @@ TbMapWidgetV2 = class TbMapWidgetV2 implements MapWidgetInterface {
|
||||
}
|
||||
}
|
||||
|
||||
export let TbMapWidgetV2: MapWidgetStaticInterface = MapWidgetController;
|
||||
|
||||
|
||||
const providerSets = {
|
||||
'openstreet-map': {
|
||||
MapClass: OpenStreetMap,
|
||||
|
||||
@ -21,71 +21,3 @@ export function createTooltip(target, settings, targetArgs?) {
|
||||
dsIndex: settings.dsIndex
|
||||
};
|
||||
}
|
||||
|
||||
export function parseArray(input: any[]): any[] {
|
||||
let alliases: any = _(input).groupBy(el => el?.datasource?.aliasName).values().value();
|
||||
return alliases.map((alliasArray, dsIndex) =>
|
||||
alliasArray[0].data.map((el, i) => {
|
||||
const obj = {
|
||||
aliasName: alliasArray[0]?.datasource?.aliasName,
|
||||
$datasource: alliasArray[0]?.datasource,
|
||||
dsIndex: dsIndex
|
||||
};
|
||||
alliasArray.forEach(el => {
|
||||
obj[el?.dataKey?.label] = el?.data[i][1];
|
||||
obj[el?.dataKey?.label + '|ts'] = el?.data[0][0];
|
||||
if (el?.dataKey?.label == 'type') {
|
||||
obj['deviceType'] = el?.data[0][1];
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export function parseData(input: any[]): any[] {
|
||||
return _(input).groupBy(el => el?.datasource?.aliasName).values().value().map((alliasArray, i) => {
|
||||
const obj = {
|
||||
aliasName: alliasArray[0]?.datasource?.aliasName,
|
||||
entityName: alliasArray[0]?.datasource?.entityName,
|
||||
$datasource: alliasArray[0]?.datasource,
|
||||
dsIndex: i
|
||||
};
|
||||
alliasArray.forEach(el => {
|
||||
obj[el?.dataKey?.label] = el?.data[0][1];
|
||||
obj[el?.dataKey?.label + '|ts'] = el?.data[0][0];
|
||||
if (el?.dataKey?.label == 'type') {
|
||||
obj['deviceType'] = el?.data[0][1];
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
|
||||
export function safeExecute(func: Function, params = []) {
|
||||
let res = null;
|
||||
if (func && typeof (func) == "function") {
|
||||
try {
|
||||
res = func(...params);
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
res = null;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
export function parseFunction(source: string, params: string[] = []): Function {
|
||||
let res = null;
|
||||
if (source?.length) {
|
||||
try {
|
||||
res = new Function(...params, source);
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
res = null;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
import L from 'leaflet';
|
||||
import { createTooltip, safeExecute, parseFunction } from './maps-utils';
|
||||
import { MarkerSettings } from './map-models';
|
||||
import { aspectCache } from '@app/core/utils';
|
||||
import { aspectCache, safeExecute, parseFunction } from '@app/core/utils';
|
||||
import { createTooltip } from './maps-utils';
|
||||
|
||||
export class Marker {
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import L from 'leaflet';
|
||||
import { safeExecute } from './maps-utils';
|
||||
import { safeExecute } from '@app/core/utils';
|
||||
|
||||
export class Polyline {
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ export class GoogleMap extends LeafletMap {
|
||||
this.loadGoogle(() => {
|
||||
const map = L.map($container).setView(options?.defaultCenterPosition, options?.defaultZoomLevel);
|
||||
var roads = (L.gridLayer as any).googleMutant({
|
||||
type: options?.gmDefaultMapType || 'roadmap' // valid values are 'roadmap', 'satellite', 'terrain' and 'hybrid'
|
||||
type: options?.gmDefaultMapType || 'roadmap'
|
||||
}).addTo(map);
|
||||
super.setMap(map);
|
||||
}, options.credentials.apiKey);
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
<div class="map" #map ></div>
|
||||
<tb-history-selector [settings]="ctx.settings"></tb-history-selector>
|
||||
@ -0,0 +1,6 @@
|
||||
|
||||
|
||||
.map{
|
||||
width: 100%;
|
||||
height: calc(100% - 100px);
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
import { Component, OnInit, Input, ViewChild, AfterViewInit } from '@angular/core';
|
||||
import { MapWidgetController } from '../lib/maps/map-widget2';
|
||||
import { MapProviders } from '../lib/maps/map-models';
|
||||
import { parseArray } from '@app/core/utils';
|
||||
|
||||
@Component({
|
||||
selector: 'trip-animation',
|
||||
templateUrl: './trip-animation.component.html',
|
||||
styleUrls: ['./trip-animation.component.scss']
|
||||
})
|
||||
export class TripAnimationComponent implements OnInit, AfterViewInit {
|
||||
|
||||
@Input() ctx;
|
||||
|
||||
@ViewChild('map') mapContainer;
|
||||
|
||||
mapWidget: MapWidgetController;
|
||||
historicalData
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
console.log(this.ctx);
|
||||
this.historicalData = parseArray(this.ctx.data);
|
||||
console.log("TripAnimationComponent -> ngOnInit -> this.historicalData",this.ctx.data, this.historicalData)
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.mapWidget = new MapWidgetController(MapProviders.openstreet, false, this.ctx, this.mapContainer.nativeElement);
|
||||
this.mapWidget.data
|
||||
}
|
||||
}
|
||||
@ -31,6 +31,7 @@ import {
|
||||
DateRangeNavigatorWidgetComponent
|
||||
} from '@home/components/widget/lib/date-range-navigator/date-range-navigator.component';
|
||||
import { MultipleInputWidgetComponent } from './lib/multiple-input-widget.component';
|
||||
import { TripAnimationComponent } from './trip-animation/trip-animation.component';
|
||||
|
||||
@NgModule({
|
||||
declarations:
|
||||
@ -43,7 +44,8 @@ import { MultipleInputWidgetComponent } from './lib/multiple-input-widget.compon
|
||||
EntitiesHierarchyWidgetComponent,
|
||||
DateRangeNavigatorWidgetComponent,
|
||||
DateRangeNavigatorPanelComponent,
|
||||
MultipleInputWidgetComponent
|
||||
MultipleInputWidgetComponent,
|
||||
TripAnimationComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
@ -58,7 +60,8 @@ import { MultipleInputWidgetComponent } from './lib/multiple-input-widget.compon
|
||||
EntitiesHierarchyWidgetComponent,
|
||||
RpcWidgetsModule,
|
||||
DateRangeNavigatorWidgetComponent,
|
||||
MultipleInputWidgetComponent
|
||||
MultipleInputWidgetComponent,
|
||||
TripAnimationComponent
|
||||
],
|
||||
providers: [
|
||||
CustomDialogService
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
<div class="trip-animation-control-panel">
|
||||
<div>
|
||||
<button mat-icon-button class="mat-icon-button" aria-label="Start" ng-click="moveStart()">
|
||||
<mat-icon class="material-icons" ng-style="{'color': staticSettings.buttonColor}">fast_rewind</mat-icon>
|
||||
</button>
|
||||
<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>
|
||||
</button>
|
||||
<!-- <mat-slider ng-model="index" [min]="minTimeIndex" [max]="maxTimeIndex" (change)="recalculateTrips()">
|
||||
</mat-slider>-->
|
||||
<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>
|
||||
</button>
|
||||
<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>
|
||||
</button>
|
||||
<button mat-icon-button class="mat-icon-button" aria-label="Play" ng-click="playMove(true)"
|
||||
ng-disabled="isPlaying">
|
||||
<mat-icon class="material-icons"
|
||||
ng-style="{'color': isPlaying ? staticSettings.disabledButtonColor : staticSettings.buttonColor}">
|
||||
play_circle_outline
|
||||
</mat-icon>
|
||||
</button>
|
||||
<!-- <mat-select ng-model="speed" aria-label="Speed selector">
|
||||
<mat-option ng-value="speed" flex="1" ng-repeat="speed in speeds track by $index">{{ speed}}
|
||||
</mat-option>
|
||||
</mat-select>-->
|
||||
<button mat-icon-button class="mat-icon-button" aria-label="Stop playing" ng-click="stopPlay()"
|
||||
ng-disabled="!isPlaying">
|
||||
<mat-icon class="material-icons"
|
||||
ng-style="{'color': isPlaying ? staticSettings.buttonColor : staticSettings.disabledButtonColor}">
|
||||
pause_circle_outline
|
||||
</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="panel-timer">
|
||||
<span *ngIf="animationTime">{{ animationTime | date:'medium'}}</span>
|
||||
<span *ngIf="!animationTime">{{ "widget.no-data-found" | translate}}</span>
|
||||
</div>
|
||||
@ -0,0 +1,137 @@
|
||||
/**
|
||||
* Copyright © 2016-2020 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
.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%;
|
||||
|
||||
#trip-animation-map {
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.pointsLayerMarkerIcon {
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.trip-animation-info-panel {
|
||||
position: absolute;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.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-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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-history-selector',
|
||||
templateUrl: './history-selector.component.html',
|
||||
styleUrls: ['./history-selector.component.scss']
|
||||
})
|
||||
export class HistorySelectorComponent implements OnInit {
|
||||
|
||||
@Input() settings
|
||||
|
||||
animationTime
|
||||
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
console.log(this.settings);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -127,6 +127,7 @@ import { NavTreeComponent } from '@shared/components/nav-tree.component';
|
||||
import { LedLightComponent } from '@shared/components/led-light.component';
|
||||
import { TbJsonToStringDirective } from "@shared/components/directives/tb-json-to-string.directive";
|
||||
import { JsonObjectEditDialogComponent } from "@shared/components/dialog/json-object-edit-dialog.component";
|
||||
import { HistorySelectorComponent } from './components/time/history-selector/history-selector.component';
|
||||
|
||||
@NgModule({
|
||||
providers: [
|
||||
@ -209,7 +210,8 @@ import { JsonObjectEditDialogComponent } from "@shared/components/dialog/json-ob
|
||||
TbJsonPipe,
|
||||
KeyboardShortcutPipe,
|
||||
TbJsonToStringDirective,
|
||||
JsonObjectEditDialogComponent
|
||||
JsonObjectEditDialogComponent,
|
||||
HistorySelectorComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
@ -367,7 +369,8 @@ import { JsonObjectEditDialogComponent } from "@shared/components/dialog/json-ob
|
||||
TbJsonPipe,
|
||||
KeyboardShortcutPipe,
|
||||
TranslateModule,
|
||||
JsonObjectEditDialogComponent
|
||||
JsonObjectEditDialogComponent,
|
||||
HistorySelectorComponent
|
||||
]
|
||||
})
|
||||
export class SharedModule { }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user