From be770518d7d509025d0bbba4dc088656f5d03a03 Mon Sep 17 00:00:00 2001 From: Artem Halushko Date: Fri, 6 Mar 2020 19:22:47 +0200 Subject: [PATCH] WIP on trip animation widget --- ui-ngx/package.json | 2 +- ui-ngx/src/app/core/utils.ts | 70 +++++++++ .../components/widget/lib/maps/map-widget2.ts | 8 +- .../components/widget/lib/maps/maps-utils.ts | 68 --------- .../components/widget/lib/maps/markers.ts | 4 +- .../components/widget/lib/maps/polyline.ts | 2 +- .../widget/lib/maps/providers/google-map.ts | 2 +- .../trip-animation.component.html | 2 + .../trip-animation.component.scss | 6 + .../trip-animation.component.ts | 32 ++++ .../widget/widget-components.module.ts | 7 +- .../history-selector.component.html | 39 +++++ .../history-selector.component.scss | 137 ++++++++++++++++++ .../history-selector.component.ts | 22 +++ ui-ngx/src/app/shared/shared.module.ts | 7 +- 15 files changed, 328 insertions(+), 80 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts create mode 100644 ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.html create mode 100644 ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.scss create mode 100644 ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts diff --git a/ui-ngx/package.json b/ui-ngx/package.json index 76c4cb9753..39d60903e1 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -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", diff --git a/ui-ngx/src/app/core/utils.ts b/ui-ngx/src/app/core/utils.ts index 74e62515be..063a830bbb 100644 --- a/ui-ngx/src/app/core/utils.ts +++ b/ui-ngx/src/app/core/utils.ts @@ -446,4 +446,74 @@ export function aspectCache(imageUrl: string): Observable { return aspect; })) } +} + + +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; } \ No newline at end of file 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 9272f96d16..59486f4c1d 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 @@ -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, 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 1c4029c1df..9a250c8d5e 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 @@ -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; -} \ No newline at end of file diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts index 649af2b2e9..6a1e1513e6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts @@ -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 { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polyline.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/polyline.ts index 95561f6d64..d16a2f58bb 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polyline.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/polyline.ts @@ -1,5 +1,5 @@ import L from 'leaflet'; -import { safeExecute } from './maps-utils'; +import { safeExecute } from '@app/core/utils'; export class Polyline { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/google-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/google-map.ts index 6c5c3e002b..e4bf573bdb 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/google-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/google-map.ts @@ -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); 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 new file mode 100644 index 0000000000..a0de5d5f37 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.html @@ -0,0 +1,2 @@ +
+ \ No newline at end of file diff --git a/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.scss b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.scss new file mode 100644 index 0000000000..7447bf4f5a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.scss @@ -0,0 +1,6 @@ + + +.map{ + width: 100%; + height: calc(100% - 100px); +} \ No newline at end of file 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 new file mode 100644 index 0000000000..271c517a3d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts @@ -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 + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts index 9754019025..7b883dc983 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts @@ -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 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 new file mode 100644 index 0000000000..5a64d88bf2 --- /dev/null +++ b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.html @@ -0,0 +1,39 @@ +
+
+ + + + + + + + +
+
+ {{ animationTime | date:'medium'}} + {{ "widget.no-data-found" | translate}} +
\ 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 new file mode 100644 index 0000000000..6ca4906263 --- /dev/null +++ b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.scss @@ -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; + } + } + } + \ 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 new file mode 100644 index 0000000000..8d1c686382 --- /dev/null +++ b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts @@ -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); + + } + +} diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts index 1df4469fb2..e4766e75b5 100644 --- a/ui-ngx/src/app/shared/shared.module.ts +++ b/ui-ngx/src/app/shared/shared.module.ts @@ -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 { }