Commit c04a86e914541c8021a8bccbfd3c32176b9eeff3

Authored by Artem Halushko
1 parent 78c0e519

add label to trip animation

... ... @@ -498,7 +498,7 @@ export function safeExecute(func: Function, params = []) {
498 498 res = func(...params);
499 499 }
500 500 catch (err) {
501   - console.log(err);
  501 + console.log('error in external function:', err);
502 502 res = null;
503 503 }
504 504 }
... ... @@ -519,7 +519,7 @@ export function parseFunction(source: string, params: string[] = []): Function {
519 519 return res;
520 520 }
521 521
522   -export function parseTemplate(template, data) {
  522 +export function parseTemplate(template: string, data: object) {
523 523 let res = '';
524 524 try {
525 525 let variables = '';
... ...
... ... @@ -27,7 +27,6 @@ import { Observable, of, BehaviorSubject, Subject } from 'rxjs';
27 27 import { filter } from 'rxjs/operators';
28 28 import { Polyline } from './polyline';
29 29 import { Polygon } from './polygon';
30   -import { string } from 'prop-types';
31 30
32 31 export default abstract class LeafletMap {
33 32
... ... @@ -43,7 +42,6 @@ export default abstract class LeafletMap {
43 42 isMarketCluster;
44 43 bounds: L.LatLngBounds;
45 44
46   -
47 45 constructor($container: HTMLElement, options: MapOptions) {
48 46 this.options = options;
49 47 }
... ... @@ -138,7 +136,7 @@ export default abstract class LeafletMap {
138 136 }
139 137
140 138 invalidateSize() {
141   - this.map.invalidateSize(true);
  139 + this.map?.invalidateSize(true);
142 140 }
143 141
144 142 onResize() {
... ... @@ -160,7 +158,7 @@ export default abstract class LeafletMap {
160 158 }
161 159 }
162 160
163   - ////Markers
  161 + //Markers
164 162 updateMarkers(markersData) {
165 163 markersData.forEach(data => {
166 164 if (data.rotationAngle) {
... ... @@ -259,7 +257,6 @@ export default abstract class LeafletMap {
259 257
260 258 createPolygon(data, dataSources, settings) {
261 259 this.ready$.subscribe(() => {
262   - //public map, coordinates, dataSources, settings, onClickListener?
263 260 this.polygon = new Polygon(this.map, data, dataSources, settings);
264 261 const bounds = this.bounds.extend(this.polygon.leafletPoly.getBounds());
265 262 if (bounds.isValid()) {
... ...
... ... @@ -91,6 +91,15 @@ export class MapWidgetController implements MapWidgetInterface {
91 91 initSettings(settings: any) {
92 92 const functionParams = ['data', 'dsData', 'dsIndex'];
93 93 this.provider = settings.provider ? settings.provider : this.mapProvider;
  94 +
  95 + function getDefCenterPosition(position) {
  96 + if (typeof (position) === 'string')
  97 + return position.split(',');
  98 + if (typeof (position) === 'object')
  99 + return position;
  100 + return [0, 0];
  101 + }
  102 +
94 103 const customOptions = {
95 104 provider: this.provider,
96 105 mapUrl: settings?.mapImageUrl,
... ... @@ -102,7 +111,7 @@ export class MapWidgetController implements MapWidgetInterface {
102 111 labelColor: this.ctx.widgetConfig.color,
103 112 tooltipPattern: settings.tooltipPattern ||
104 113 "<b>${entityName}</b><br/><br/><b>Latitude:</b> ${" + settings.latKeyName + ":7}<br/><b>Longitude:</b> ${" + settings.lngKeyName + ":7}",
105   - defaultCenterPosition: settings?.defaultCenterPosition?.split(',') || [0, 0],
  114 + defaultCenterPosition: getDefCenterPosition(settings?.defaultCenterPosition),
106 115 useDraggableMarker: true,
107 116 currentImage: (settings.useMarkerImage && settings.markerImage?.length) ? {
108 117 url: settings.markerImage,
... ... @@ -137,7 +146,7 @@ export class MapWidgetController implements MapWidgetInterface {
137 146 return {};
138 147 }
139 148
140   - public static getProvidersSchema() {
  149 + public static getProvidersSchema() {
141 150 return mergeSchemes([mapProviderSchema,
142 151 ...Object.values(providerSets)?.map(
143 152 setting => addCondition(setting?.schema, `model.provider === '${setting.name}'`))]);
... ... @@ -246,5 +255,5 @@ const defaultSettings = {
246 255 minZoomLevel: 16,
247 256 credentials: '',
248 257 markerClusteringSetting: null,
249   - draggebleMarker: true
  258 + draggebleMarker: false
250 259 }
\ No newline at end of file
... ...
... ... @@ -15,7 +15,6 @@
15 15 ///
16 16
17 17 import L from 'leaflet';
18   -import { interpolateOnPointSegment } from 'leaflet-geometryutil';
19 18 import _ from 'lodash';
20 19
21 20 export function createTooltip(target, settings) {
... ... @@ -34,38 +33,3 @@ export function createTooltip(target, settings) {
34 33 return popup;
35 34 }
36 35
37   -
38   -export function interpolateArray(originData, interpolatedIntervals) {
39   -
40   - const getRatio = (firsMoment, secondMoment, intermediateMoment) => {
41   - return (intermediateMoment - firsMoment) / (secondMoment - firsMoment);
42   - };
43   -
44   - function findAngle(startPoint, endPoint) {
45   - let angle = -Math.atan2(endPoint.latitude - startPoint.latitude, endPoint.longitude - startPoint.longitude);
46   - angle = angle * 180 / Math.PI;
47   - return parseInt(angle.toFixed(2));
48   - }
49   -
50   - const result = {};
51   -
52   - for (let i = 1, j = 0; i < originData.length, j < interpolatedIntervals.length;) {
53   - const currentTime = interpolatedIntervals[j];
54   - while (originData[i].time < currentTime) i++;
55   - const before = originData[i - 1];
56   - const after = originData[i];
57   - const interpolation = interpolateOnPointSegment(
58   - new L.Point(before.latitude, before.longitude),
59   - new L.Point(after.latitude, after.longitude),
60   - getRatio(before.time, after.time, currentTime));
61   - result[currentTime] = ({
62   - ...originData[i],
63   - rotationAngle: findAngle(before, after),
64   - latitude: interpolation.x,
65   - longitude: interpolation.y
66   - });
67   - j++;
68   - }
69   -
70   - return result;
71   -};
... ...
... ... @@ -56,7 +56,6 @@ export class Marker {
56 56 if (onDragendListener) {
57 57 this.leafletMarker.on('dragend', onDragendListener);
58 58 }
59   -
60 59 }
61 60
62 61 setDataSources(data, dataSources) {
... ...
... ... @@ -24,7 +24,6 @@ export class Polyline {
24 24 data;
25 25
26 26 constructor(private map: L.Map, locations, data, dataSources, settings) {
27   - console.log("Polyline -> constructor -> data", data)
28 27 this.dataSources = dataSources;
29 28 this.data = data;
30 29 this.leafletPoly = L.polyline(locations,
... ... @@ -34,7 +33,6 @@ export class Polyline {
34 33
35 34 updatePolyline(settings, data, dataSources) {
36 35 this.leafletPoly.setStyle(this.getPolyStyle(settings, data, dataSources));
37   -
38 36 }
39 37
40 38 getPolyStyle(settings, data, dataSources): L.PolylineOptions {
... ...
... ... @@ -15,6 +15,23 @@
15 15 limitations under the License.
16 16
17 17 -->
18   -<div class="map" #map ></div>
19   -<tb-history-selector *ngIf="historicalData" [settings]="settings" [intervals]="intervals"
20   - (onTimeUpdated)="timeUpdated($event)"></tb-history-selector>
  18 +<div class="trip-animation-widget">
  19 + <div class="trip-animation-label-container" *ngIf="settings.showLabel">
  20 + {{settings.label | tbParseTemplate: activeTrip}}
  21 + </div>
  22 + <div class="trip-animation-container" layout="column">
  23 + <div class="map" #map></div>
  24 + <div class="trip-animation-info-panel" layout="row">
  25 + <button class="tooltip-button" mat-mini-fab color="primary" aria-label="tooltip"
  26 + *ngIf="settings.showTooltip" (click)="showHideTooltip()">
  27 + <mat-icon>info_outline</mat-icon>
  28 + </button>
  29 + </div>
  30 + <div class="trip-animation-tooltip md-whiteframe-z4" layout="column"
  31 + [ngClass]="{ 'trip-animation-tooltip-hidden':!settings.showTooltip}" [innerHTML]="mainTooltip"
  32 + [ngStyle]="{'background-color': settings.tooltipColor, 'opacity': settings.tooltipOpacity, 'color': settings.tooltipFontColor}">
  33 + </div>
  34 + </div>
  35 + <tb-history-selector *ngIf="historicalData" [settings]="settings" [intervals]="intervals"
  36 + (onTimeUpdated)="timeUpdated($event)"></tb-history-selector>
  37 +</div>
\ No newline at end of file
... ...
... ... @@ -14,8 +14,76 @@
14 14 * limitations under the License.
15 15 */
16 16
  17 +.trip-animation-widget {
  18 + position: relative;
  19 + width: 100%;
  20 + height: 100%;
  21 + font-size: 16px;
  22 + line-height: 24px;
  23 + display: flex;
  24 + flex-direction: column;
17 25
18   -.map{
  26 + .trip-animation-label-container {
  27 + height: 24px;
  28 + }
  29 +
  30 + .trip-animation-container {
  31 + position: relative;
  32 + z-index: 1;
  33 + flex: 1;
19 34 width: 100%;
20   - height: calc(100% - 100px);
21   -}
\ No newline at end of file
  35 +
  36 + .map {
  37 + width: 100%;
  38 + height: 100%;
  39 + }
  40 +
  41 + .trip-animation-info-panel {
  42 + position: absolute;
  43 + top: 0;
  44 + right: 0;
  45 + pointer-events: none;
  46 +
  47 + .tooltip-button {
  48 + top: 0;
  49 + left: 0;
  50 + width: 32px;
  51 + min-width: 32px;
  52 + height: 32px;
  53 + min-height: 32px;
  54 + padding: 0 0 2px;
  55 + margin: 2px;
  56 + line-height: 24px;
  57 + z-index: 999;
  58 +
  59 + &::ng-deep .mat-button-wrapper {
  60 + padding: 0;
  61 + }
  62 +
  63 + mat-icon {
  64 + width: 24px;
  65 + height: 24px;
  66 +
  67 + svg {
  68 + width: inherit;
  69 + height: inherit;
  70 + }
  71 + }
  72 + }
  73 + }
  74 +
  75 + .trip-animation-tooltip {
  76 + position: absolute;
  77 + top: 38px;
  78 + right: 0;
  79 + z-index: 2;
  80 + padding: 10px;
  81 + background-color: #fff;
  82 + transition: 0.3s ease-in-out;
  83 +
  84 + &-hidden {
  85 + transform: translateX(110%);
  86 + }
  87 + }
  88 + }
  89 +}
... ...
... ... @@ -14,15 +14,19 @@
14 14 /// limitations under the License.
15 15 ///
16 16
  17 +import L from 'leaflet';
  18 +import _ from 'lodash';
  19 +import tinycolor from "tinycolor2";
  20 +import { interpolateOnPointSegment } from 'leaflet-geometryutil';
  21 +
17 22 import { Component, OnInit, Input, ViewChild, AfterViewInit, ChangeDetectorRef } from '@angular/core';
18 23 import { MapWidgetController, TbMapWidgetV2 } from '../lib/maps/map-widget2';
19 24 import { MapProviders } from '../lib/maps/map-models';
20 25 import { parseArray } from '@app/core/utils';
21   -import { interpolateArray } from '../lib/maps/maps-utils';
22   -import tinycolor from "tinycolor2";
23 26 import { initSchema, addToSchema, addGroupInfo } from '@app/core/schema-utils';
24 27 import { tripAnimationSchema } from '../lib/maps/schemes';
25 28
  29 +
26 30 @Component({
27 31 selector: 'trip-animation',
28 32 templateUrl: './trip-animation.component.html',
... ... @@ -41,6 +45,8 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
41 45 interpolatedData = [];
42 46 widgetConfig;
43 47 settings;
  48 + mainTooltip;
  49 + activeTrip;
44 50
45 51 constructor(private cd: ChangeDetectorRef) { }
46 52
... ... @@ -57,19 +63,27 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
57 63 let subscription = this.ctx.subscriptions[Object.keys(this.ctx.subscriptions)[0]];
58 64 if (subscription) subscription.callbacks.onDataUpdated = (updated) => {
59 65 this.historicalData = parseArray(this.ctx.data);
  66 + this.activeTrip = this.historicalData[0][0];
60 67 this.calculateIntervals();
61 68 this.timeUpdated(this.intervals[0]);
62   - this.mapWidget.map.map.invalidateSize();
  69 + this.mapWidget.map.map?.invalidateSize();
63 70 this.cd.detectChanges();
64 71 }
65 72 }
66 73
67 74 ngAfterViewInit() {
68   - this.mapWidget = new MapWidgetController(MapProviders.openstreet, false, this.ctx, this.mapContainer.nativeElement);
  75 + let ctxCopy = _.cloneDeep(this.ctx);
  76 + ctxCopy.settings.showLabel = false;
  77 + this.mapWidget = new MapWidgetController(MapProviders.openstreet, false, ctxCopy, this.mapContainer.nativeElement);
69 78 }
70 79
71 80 timeUpdated(time) {
72 81 const currentPosition = this.interpolatedData.map(dataSource => dataSource[time]);
  82 + this.activeTrip = currentPosition[0];
  83 + this.mapWidget.map.updatePolylines(this.interpolatedData);
  84 + if (this.settings.showPolygon) {
  85 + this.mapWidget.map.updatePolygons(this.interpolatedData);
  86 + }
73 87 this.mapWidget.map.updateMarkers(currentPosition);
74 88 }
75 89
... ... @@ -80,10 +94,47 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
80 94 this.intervals.push(time);
81 95 }
82 96 this.intervals.push(dataSource[dataSource.length - 1]?.time);
83   - this.interpolatedData[index] = interpolateArray(dataSource, this.intervals);
  97 + this.interpolatedData[index] = this.interpolateArray(dataSource, this.intervals);
84 98 });
85 99 }
86 100
  101 + showHideTooltip() {
  102 + }
  103 +
  104 + interpolateArray(originData, interpolatedIntervals) {
  105 +
  106 + const getRatio = (firsMoment, secondMoment, intermediateMoment) => {
  107 + return (intermediateMoment - firsMoment) / (secondMoment - firsMoment);
  108 + };
  109 +
  110 + function findAngle(startPoint, endPoint) {
  111 + let angle = -Math.atan2(endPoint.latitude - startPoint.latitude, endPoint.longitude - startPoint.longitude);
  112 + angle = angle * 180 / Math.PI;
  113 + return parseInt(angle.toFixed(2));
  114 + }
  115 +
  116 + const result = {};
  117 +
  118 + for (let i = 1, j = 0; i < originData.length, j < interpolatedIntervals.length;) {
  119 + const currentTime = interpolatedIntervals[j];
  120 + while (originData[i].time < currentTime) i++;
  121 + const before = originData[i - 1];
  122 + const after = originData[i];
  123 + const interpolation = interpolateOnPointSegment(
  124 + new L.Point(before.latitude, before.longitude),
  125 + new L.Point(after.latitude, after.longitude),
  126 + getRatio(before.time, after.time, currentTime));
  127 + result[currentTime] = ({
  128 + ...originData[i],
  129 + rotationAngle: findAngle(before, after) + this.settings.rotationAngle,
  130 + latitude: interpolation.x,
  131 + longitude: interpolation.y
  132 + });
  133 + j++;
  134 + }
  135 + return result;
  136 + };
  137 +
87 138 static getSettingsSchema() {
88 139 let schema = initSchema();
89 140 addToSchema(schema, TbMapWidgetV2.getProvidersSchema());
... ...
... ... @@ -20,3 +20,4 @@ export * from './keyboard-shortcut.pipe';
20 20 export * from './milliseconds-to-time-string.pipe';
21 21 export * from './nospace.pipe';
22 22 export * from './truncate.pipe';
  23 +export * from './template.pipe';
... ...
  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 +
  17 +import { Pipe, PipeTransform } from '@angular/core';
  18 +import { parseTemplate } from '@app/core/utils';
  19 +
  20 +@Pipe({ name: 'tbParseTemplate' })
  21 +export class TbTemplatePipe implements PipeTransform {
  22 + transform(template, data): string {
  23 + console.log("TbTemplatePipe -> transform -> template, data", template, data)
  24 + return parseTemplate(template, data);
  25 + }
  26 +}
... ...
... ... @@ -128,6 +128,7 @@ 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 130 import { HistorySelectorComponent } from './components/time/history-selector/history-selector.component';
  131 +import { TbTemplatePipe } from './pipe/public-api';
131 132
132 133 @NgModule({
133 134 providers: [
... ... @@ -208,6 +209,7 @@ import { HistorySelectorComponent } from './components/time/history-selector/his
208 209 HighlightPipe,
209 210 TruncatePipe,
210 211 TbJsonPipe,
  212 + TbTemplatePipe,
211 213 KeyboardShortcutPipe,
212 214 TbJsonToStringDirective,
213 215 JsonObjectEditDialogComponent,
... ... @@ -367,6 +369,7 @@ import { HistorySelectorComponent } from './components/time/history-selector/his
367 369 HighlightPipe,
368 370 TruncatePipe,
369 371 TbJsonPipe,
  372 + TbTemplatePipe,
370 373 KeyboardShortcutPipe,
371 374 TranslateModule,
372 375 JsonObjectEditDialogComponent,
... ...