Commit be770518d7d509025d0bbba4dc088656f5d03a03

Authored by Artem Halushko
1 parent 5d33da4b

WIP on trip animation widget

... ... @@ -38,7 +38,6 @@
38 38 "@ngx-share/core": "^7.1.4",
39 39 "@ngx-translate/core": "^12.1.1",
40 40 "@ngx-translate/http-loader": "^4.0.0",
41   - "@types/lodash": "^4.14.149",
42 41 "ace-builds": "^1.4.8",
43 42 "angular-gridster2": "^9.0.1",
44 43 "angular2-hotkeys": "^2.1.5",
... ... @@ -105,6 +104,7 @@
105 104 "@types/js-beautify": "^1.8.1",
106 105 "@types/jstree": "^3.3.39",
107 106 "@types/leaflet": "^1.5.9",
  107 + "@types/lodash": "^4.14.149",
108 108 "@types/raphael": "^2.1.30",
109 109 "@types/react": "^16.9.20",
110 110 "@types/react-dom": "^16.9.5",
... ...
... ... @@ -446,4 +446,74 @@ export function aspectCache(imageUrl: string): Observable<number> {
446 446 return aspect;
447 447 }))
448 448 }
  449 +}
  450 +
  451 +
  452 +export function parseArray(input: any[]): any[] {
  453 + let alliases: any = _(input).groupBy(el => el?.datasource?.aliasName).values().value();
  454 + console.log("alliases", alliases)
  455 + return alliases.map((alliasArray, dsIndex) =>
  456 + alliasArray[0].data.map((el, i) => {
  457 + const obj = {
  458 + aliasName: alliasArray[0]?.datasource?.aliasName,
  459 + $datasource: alliasArray[0]?.datasource,
  460 + dsIndex: dsIndex
  461 + };
  462 + alliasArray.forEach(el => {
  463 + obj[el?.dataKey?.label] = el?.data[i][1];
  464 + obj[el?.dataKey?.label + '|ts'] = el?.data[0][0];
  465 + if (el?.dataKey?.label == 'type') {
  466 + obj['deviceType'] = el?.data[0][1];
  467 + }
  468 + });
  469 + return obj;
  470 + })
  471 + );
  472 +}
  473 +
  474 +export function parseData(input: any[]): any[] {
  475 + return _(input).groupBy(el => el?.datasource?.aliasName).values().value().map((alliasArray, i) => {
  476 + const obj = {
  477 + aliasName: alliasArray[0]?.datasource?.aliasName,
  478 + entityName: alliasArray[0]?.datasource?.entityName,
  479 + $datasource: alliasArray[0]?.datasource,
  480 + dsIndex: i
  481 + };
  482 + alliasArray.forEach(el => {
  483 + obj[el?.dataKey?.label] = el?.data[0][1];
  484 + obj[el?.dataKey?.label + '|ts'] = el?.data[0][0];
  485 + if (el?.dataKey?.label == 'type') {
  486 + obj['deviceType'] = el?.data[0][1];
  487 + }
  488 + });
  489 + return obj;
  490 + });
  491 +}
  492 +
  493 +export function safeExecute(func: Function, params = []) {
  494 + let res = null;
  495 + if (func && typeof (func) == "function") {
  496 + try {
  497 + res = func(...params);
  498 + }
  499 + catch (err) {
  500 + console.error(err);
  501 + res = null;
  502 + }
  503 + }
  504 + return res;
  505 +}
  506 +
  507 +export function parseFunction(source: string, params: string[] = []): Function {
  508 + let res = null;
  509 + if (source?.length) {
  510 + try {
  511 + res = new Function(...params, source);
  512 + }
  513 + catch (err) {
  514 + console.error(err);
  515 + res = null;
  516 + }
  517 + }
  518 + return res;
449 519 }
\ No newline at end of file
... ...
... ... @@ -14,10 +14,9 @@ import {
14 14 } from './schemes';
15 15 import { MapWidgetStaticInterface, MapWidgetInterface } from './map-widget.interface';
16 16 import { OpenStreetMap, TencentMap, GoogleMap, HEREMap, ImageMap } from './providers';
17   -import { parseData, parseArray, parseFunction } from './maps-utils';
  17 +import { parseFunction, parseArray, parseData } from '@app/core/utils';
18 18
19   -export let TbMapWidgetV2: MapWidgetStaticInterface;
20   -TbMapWidgetV2 = class TbMapWidgetV2 implements MapWidgetInterface {
  19 +export class MapWidgetController implements MapWidgetInterface {
21 20
22 21 map: LeafletMap;
23 22 provider: MapProviders;
... ... @@ -201,6 +200,9 @@ TbMapWidgetV2 = class TbMapWidgetV2 implements MapWidgetInterface {
201 200 }
202 201 }
203 202
  203 +export let TbMapWidgetV2: MapWidgetStaticInterface = MapWidgetController;
  204 +
  205 +
204 206 const providerSets = {
205 207 'openstreet-map': {
206 208 MapClass: OpenStreetMap,
... ...
... ... @@ -21,71 +21,3 @@ export function createTooltip(target, settings, targetArgs?) {
21 21 dsIndex: settings.dsIndex
22 22 };
23 23 }
24   -
25   -export function parseArray(input: any[]): any[] {
26   - let alliases: any = _(input).groupBy(el => el?.datasource?.aliasName).values().value();
27   - return alliases.map((alliasArray, dsIndex) =>
28   - alliasArray[0].data.map((el, i) => {
29   - const obj = {
30   - aliasName: alliasArray[0]?.datasource?.aliasName,
31   - $datasource: alliasArray[0]?.datasource,
32   - dsIndex: dsIndex
33   - };
34   - alliasArray.forEach(el => {
35   - obj[el?.dataKey?.label] = el?.data[i][1];
36   - obj[el?.dataKey?.label + '|ts'] = el?.data[0][0];
37   - if (el?.dataKey?.label == 'type') {
38   - obj['deviceType'] = el?.data[0][1];
39   - }
40   - });
41   - return obj;
42   - })
43   - );
44   -}
45   -
46   -export function parseData(input: any[]): any[] {
47   - return _(input).groupBy(el => el?.datasource?.aliasName).values().value().map((alliasArray, i) => {
48   - const obj = {
49   - aliasName: alliasArray[0]?.datasource?.aliasName,
50   - entityName: alliasArray[0]?.datasource?.entityName,
51   - $datasource: alliasArray[0]?.datasource,
52   - dsIndex: i
53   - };
54   - alliasArray.forEach(el => {
55   - obj[el?.dataKey?.label] = el?.data[0][1];
56   - obj[el?.dataKey?.label + '|ts'] = el?.data[0][0];
57   - if (el?.dataKey?.label == 'type') {
58   - obj['deviceType'] = el?.data[0][1];
59   - }
60   - });
61   - return obj;
62   - });
63   -}
64   -
65   -export function safeExecute(func: Function, params = []) {
66   - let res = null;
67   - if (func && typeof (func) == "function") {
68   - try {
69   - res = func(...params);
70   - }
71   - catch (err) {
72   - console.error(err);
73   - res = null;
74   - }
75   - }
76   - return res;
77   -}
78   -
79   -export function parseFunction(source: string, params: string[] = []): Function {
80   - let res = null;
81   - if (source?.length) {
82   - try {
83   - res = new Function(...params, source);
84   - }
85   - catch (err) {
86   - console.error(err);
87   - res = null;
88   - }
89   - }
90   - return res;
91   -}
\ No newline at end of file
... ...
1 1 import L from 'leaflet';
2   -import { createTooltip, safeExecute, parseFunction } from './maps-utils';
3 2 import { MarkerSettings } from './map-models';
4   -import { aspectCache } from '@app/core/utils';
  3 +import { aspectCache, safeExecute, parseFunction } from '@app/core/utils';
  4 +import { createTooltip } from './maps-utils';
5 5
6 6 export class Marker {
7 7
... ...
1 1 import L from 'leaflet';
2   -import { safeExecute } from './maps-utils';
  2 +import { safeExecute } from '@app/core/utils';
3 3
4 4 export class Polyline {
5 5
... ...
... ... @@ -14,7 +14,7 @@ export class GoogleMap extends LeafletMap {
14 14 this.loadGoogle(() => {
15 15 const map = L.map($container).setView(options?.defaultCenterPosition, options?.defaultZoomLevel);
16 16 var roads = (L.gridLayer as any).googleMutant({
17   - type: options?.gmDefaultMapType || 'roadmap' // valid values are 'roadmap', 'satellite', 'terrain' and 'hybrid'
  17 + type: options?.gmDefaultMapType || 'roadmap'
18 18 }).addTo(map);
19 19 super.setMap(map);
20 20 }, options.credentials.apiKey);
... ...
  1 +<div class="map" #map ></div>
  2 +<tb-history-selector [settings]="ctx.settings"></tb-history-selector>
\ No newline at end of file
... ...
  1 +
  2 +
  3 +.map{
  4 + width: 100%;
  5 + height: calc(100% - 100px);
  6 +}
\ No newline at end of file
... ...
  1 +import { Component, OnInit, Input, ViewChild, AfterViewInit } from '@angular/core';
  2 +import { MapWidgetController } from '../lib/maps/map-widget2';
  3 +import { MapProviders } from '../lib/maps/map-models';
  4 +import { parseArray } from '@app/core/utils';
  5 +
  6 +@Component({
  7 + selector: 'trip-animation',
  8 + templateUrl: './trip-animation.component.html',
  9 + styleUrls: ['./trip-animation.component.scss']
  10 +})
  11 +export class TripAnimationComponent implements OnInit, AfterViewInit {
  12 +
  13 + @Input() ctx;
  14 +
  15 + @ViewChild('map') mapContainer;
  16 +
  17 + mapWidget: MapWidgetController;
  18 + historicalData
  19 +
  20 + constructor() { }
  21 +
  22 + 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)
  26 + }
  27 +
  28 + ngAfterViewInit() {
  29 + this.mapWidget = new MapWidgetController(MapProviders.openstreet, false, this.ctx, this.mapContainer.nativeElement);
  30 + this.mapWidget.data
  31 + }
  32 +}
... ...
... ... @@ -31,6 +31,7 @@ import {
31 31 DateRangeNavigatorWidgetComponent
32 32 } from '@home/components/widget/lib/date-range-navigator/date-range-navigator.component';
33 33 import { MultipleInputWidgetComponent } from './lib/multiple-input-widget.component';
  34 +import { TripAnimationComponent } from './trip-animation/trip-animation.component';
34 35
35 36 @NgModule({
36 37 declarations:
... ... @@ -43,7 +44,8 @@ import { MultipleInputWidgetComponent } from './lib/multiple-input-widget.compon
43 44 EntitiesHierarchyWidgetComponent,
44 45 DateRangeNavigatorWidgetComponent,
45 46 DateRangeNavigatorPanelComponent,
46   - MultipleInputWidgetComponent
  47 + MultipleInputWidgetComponent,
  48 + TripAnimationComponent
47 49 ],
48 50 imports: [
49 51 CommonModule,
... ... @@ -58,7 +60,8 @@ import { MultipleInputWidgetComponent } from './lib/multiple-input-widget.compon
58 60 EntitiesHierarchyWidgetComponent,
59 61 RpcWidgetsModule,
60 62 DateRangeNavigatorWidgetComponent,
61   - MultipleInputWidgetComponent
  63 + MultipleInputWidgetComponent,
  64 + TripAnimationComponent
62 65 ],
63 66 providers: [
64 67 CustomDialogService
... ...
  1 +<div class="trip-animation-control-panel">
  2 + <div>
  3 + <button mat-icon-button class="mat-icon-button" aria-label="Start" ng-click="moveStart()">
  4 + <mat-icon class="material-icons" ng-style="{'color': staticSettings.buttonColor}">fast_rewind</mat-icon>
  5 + </button>
  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>
  8 + </button>
  9 + <!-- <mat-slider ng-model="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()">
  12 + <mat-icon class="material-icons" ng-style="{'color': staticSettings.buttonColor}">skip_next</mat-icon>
  13 + </button>
  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>
  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}">
  21 + play_circle_outline
  22 + </mat-icon>
  23 + </button>
  24 + <!-- <mat-select ng-model="speed" aria-label="Speed selector">
  25 + <mat-option ng-value="speed" flex="1" ng-repeat="speed in speeds track by $index">{{ speed}}
  26 + </mat-option>
  27 + </mat-select>-->
  28 + <button mat-icon-button class="mat-icon-button" aria-label="Stop playing" ng-click="stopPlay()"
  29 + ng-disabled="!isPlaying">
  30 + <mat-icon class="material-icons"
  31 + ng-style="{'color': isPlaying ? staticSettings.buttonColor : staticSettings.disabledButtonColor}">
  32 + pause_circle_outline
  33 + </mat-icon>
  34 + </button>
  35 + </div>
  36 + <div class="panel-timer">
  37 + <span *ngIf="animationTime">{{ animationTime | date:'medium'}}</span>
  38 + <span *ngIf="!animationTime">{{ "widget.no-data-found" | translate}}</span>
  39 + </div>
\ No newline at end of file
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 + .trip-animation-widget {
  17 +
  18 + position: relative;
  19 + 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;
  30 + z-index: 1;
  31 + flex: 1;
  32 + 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 + }
  42 + }
  43 +
  44 + .trip-animation-info-panel {
  45 + position: absolute;
  46 + 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 + }
  70 + }
  71 + }
  72 + }
  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 + }
  86 + }
  87 + }
  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 + }
  118 + }
  119 + }
  120 +
  121 + md-select {
  122 + margin: 0;
  123 + }
  124 + }
  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;
  134 + }
  135 + }
  136 + }
  137 +
\ No newline at end of file
... ...
  1 +import { Component, OnInit, Input } from '@angular/core';
  2 +
  3 +@Component({
  4 + selector: 'tb-history-selector',
  5 + templateUrl: './history-selector.component.html',
  6 + styleUrls: ['./history-selector.component.scss']
  7 +})
  8 +export class HistorySelectorComponent implements OnInit {
  9 +
  10 + @Input() settings
  11 +
  12 + animationTime
  13 +
  14 +
  15 + constructor() { }
  16 +
  17 + ngOnInit(): void {
  18 + console.log(this.settings);
  19 +
  20 + }
  21 +
  22 +}
... ...
... ... @@ -127,6 +127,7 @@ import { NavTreeComponent } from '@shared/components/nav-tree.component';
127 127 import { LedLightComponent } from '@shared/components/led-light.component';
128 128 import { TbJsonToStringDirective } from "@shared/components/directives/tb-json-to-string.directive";
129 129 import { JsonObjectEditDialogComponent } from "@shared/components/dialog/json-object-edit-dialog.component";
  130 +import { HistorySelectorComponent } from './components/time/history-selector/history-selector.component';
130 131
131 132 @NgModule({
132 133 providers: [
... ... @@ -209,7 +210,8 @@ import { JsonObjectEditDialogComponent } from "@shared/components/dialog/json-ob
209 210 TbJsonPipe,
210 211 KeyboardShortcutPipe,
211 212 TbJsonToStringDirective,
212   - JsonObjectEditDialogComponent
  213 + JsonObjectEditDialogComponent,
  214 + HistorySelectorComponent
213 215 ],
214 216 imports: [
215 217 CommonModule,
... ... @@ -367,7 +369,8 @@ import { JsonObjectEditDialogComponent } from "@shared/components/dialog/json-ob
367 369 TbJsonPipe,
368 370 KeyboardShortcutPipe,
369 371 TranslateModule,
370   - JsonObjectEditDialogComponent
  372 + JsonObjectEditDialogComponent,
  373 + HistorySelectorComponent
371 374 ]
372 375 })
373 376 export class SharedModule { }
... ...