Commit c7ec88cc131de38b7b83d3076f4fb53a6693ab64

Authored by Igor Kulikov
Committed by GitHub
2 parents a70a4b72 c9c0f465

Merge pull request #2808 from ArtemHalushko/map/3.0

Map/3.0
@@ -389,6 +389,9 @@ export default abstract class LeafletMap { @@ -389,6 +389,9 @@ export default abstract class LeafletMap {
389 const poly = this.polylines.get(name); 389 const poly = this.polylines.get(name);
390 if (poly) { 390 if (poly) {
391 this.map.removeLayer(poly.leafletPoly); 391 this.map.removeLayer(poly.leafletPoly);
  392 + if (poly.polylineDecorator) {
  393 + this.map.removeLayer(poly.polylineDecorator);
  394 + }
392 this.polylines.delete(name); 395 this.polylines.delete(name);
393 } 396 }
394 } 397 }
@@ -26,7 +26,7 @@ import { @@ -26,7 +26,7 @@ import {
26 26
27 export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; 27 export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string;
28 export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; 28 export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string;
29 -export type GetTooltip= (point: FormattedData, setTooltip?: boolean) => string; 29 +export type GetTooltip = (point: FormattedData, setTooltip?: boolean) => string;
30 30
31 export type MapSettings = { 31 export type MapSettings = {
32 draggableMarker: boolean; 32 draggableMarker: boolean;
@@ -164,9 +164,23 @@ export interface HistorySelectSettings { @@ -164,9 +164,23 @@ export interface HistorySelectSettings {
164 } 164 }
165 165
166 export type TripAnimationSettings = { 166 export type TripAnimationSettings = {
  167 + showPoints: boolean;
167 pointColor: string; 168 pointColor: string;
168 pointSize: number; 169 pointSize: number;
169 pointTooltipOnRightPanel: boolean; 170 pointTooltipOnRightPanel: boolean;
  171 + usePointAsAnchor: boolean;
  172 + normalizationStep: number;
  173 + showPolygon: boolean;
  174 + latKeyName: string;
  175 + lngKeyName: string;
  176 + rotationAngle: number;
  177 + label: string;
  178 + tooltipPattern: string;
  179 + useTooltipFunction :boolean;
  180 + useLabelFunction:boolean;
  181 + pointAsAnchorFunction: GenericFunction;
  182 + tooltipFunction: GenericFunction;
  183 + labelFunction: GenericFunction;
170 } 184 }
171 185
172 export type actionsHandler = ($event: Event, datasource: Datasource) => void; 186 export type actionsHandler = ($event: Event, datasource: Datasource) => void;
@@ -17,12 +17,12 @@ @@ -17,12 +17,12 @@
17 import { defaultSettings, hereProviders, MapProviders, providerSets, UnitedMapSettings } from './map-models'; 17 import { defaultSettings, hereProviders, MapProviders, providerSets, UnitedMapSettings } from './map-models';
18 import LeafletMap from './leaflet-map'; 18 import LeafletMap from './leaflet-map';
19 import { 19 import {
20 - commonMapSettingsSchema,  
21 - mapPolygonSchema,  
22 - mapProviderSchema,  
23 - markerClusteringSettingsSchema,  
24 - markerClusteringSettingsSchemaLeaflet,  
25 - routeMapSettingsSchema 20 + commonMapSettingsSchema,
  21 + mapPolygonSchema,
  22 + mapProviderSchema,
  23 + markerClusteringSettingsSchema,
  24 + markerClusteringSettingsSchemaLeaflet,
  25 + routeMapSettingsSchema
26 } from './schemes'; 26 } from './schemes';
27 import { MapWidgetInterface, MapWidgetStaticInterface } from './map-widget.interface'; 27 import { MapWidgetInterface, MapWidgetStaticInterface } from './map-widget.interface';
28 import { addCondition, addGroupInfo, addToSchema, initSchema, mergeSchemes } from '@core/schema-utils'; 28 import { addCondition, addGroupInfo, addToSchema, initSchema, mergeSchemes } from '@core/schema-utils';
@@ -30,17 +30,18 @@ import { of, Subject } from 'rxjs'; @@ -30,17 +30,18 @@ import { of, Subject } from 'rxjs';
30 import { WidgetContext } from '@app/modules/home/models/widget-component.models'; 30 import { WidgetContext } from '@app/modules/home/models/widget-component.models';
31 import { getDefCenterPosition, parseArray, parseData, parseFunction, parseWithTranslation } from './maps-utils'; 31 import { getDefCenterPosition, parseArray, parseData, parseFunction, parseWithTranslation } from './maps-utils';
32 import { 32 import {
33 - Datasource,  
34 - DatasourceType,  
35 - JsonSettingsSchema,  
36 - WidgetActionDescriptor,  
37 - widgetType 33 + Datasource,
  34 + DatasourceType,
  35 + JsonSettingsSchema,
  36 + WidgetActionDescriptor,
  37 + widgetType
38 } from '@shared/models/widget.models'; 38 } from '@shared/models/widget.models';
39 import { EntityId } from '@shared/models/id/entity-id'; 39 import { EntityId } from '@shared/models/id/entity-id';
40 import { AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models'; 40 import { AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models';
41 import { AttributeService } from '@core/http/attribute.service'; 41 import { AttributeService } from '@core/http/attribute.service';
42 import { TranslateService } from '@ngx-translate/core'; 42 import { TranslateService } from '@ngx-translate/core';
43 import { UtilsService } from '@core/services/utils.service'; 43 import { UtilsService } from '@core/services/utils.service';
  44 +import _ from 'lodash';
44 45
45 // @dynamic 46 // @dynamic
46 export class MapWidgetController implements MapWidgetInterface { 47 export class MapWidgetController implements MapWidgetInterface {
@@ -90,9 +91,9 @@ export class MapWidgetController implements MapWidgetInterface { @@ -90,9 +91,9 @@ export class MapWidgetController implements MapWidgetInterface {
90 } 91 }
91 92
92 public static getProvidersSchema(mapProvider: MapProviders, ignoreImageMap = false) { 93 public static getProvidersSchema(mapProvider: MapProviders, ignoreImageMap = false) {
  94 + const providerSchema = _.cloneDeep(mapProviderSchema);
93 if (mapProvider) 95 if (mapProvider)
94 - mapProviderSchema.schema.properties.provider.default = mapProvider;  
95 - const providerSchema = mapProviderSchema; 96 + providerSchema.schema.properties.provider.default = mapProvider;
96 if (ignoreImageMap) { 97 if (ignoreImageMap) {
97 providerSchema.form[0].items = providerSchema.form[0]?.items.filter(item => item.value !== 'image-map'); 98 providerSchema.form[0].items = providerSchema.form[0]?.items.filter(item => item.value !== 'image-map');
98 } 99 }
@@ -19,7 +19,7 @@ import tinycolor from 'tinycolor2'; @@ -19,7 +19,7 @@ import tinycolor from 'tinycolor2';
19 19
20 import { AfterViewInit, ChangeDetectorRef, Component, Input, OnInit, SecurityContext, ViewChild } from '@angular/core'; 20 import { AfterViewInit, ChangeDetectorRef, Component, Input, OnInit, SecurityContext, ViewChild } from '@angular/core';
21 import { MapWidgetController, TbMapWidgetV2 } from '../lib/maps/map-widget2'; 21 import { MapWidgetController, TbMapWidgetV2 } from '../lib/maps/map-widget2';
22 -import { FormattedData, MapProviders } from '../lib/maps/map-models'; 22 +import { FormattedData, MapProviders, TripAnimationSettings } from '../lib/maps/map-models';
23 import { addCondition, addGroupInfo, addToSchema, initSchema } from '@app/core/schema-utils'; 23 import { addCondition, addGroupInfo, addToSchema, initSchema } from '@app/core/schema-utils';
24 import { mapPolygonSchema, pathSchema, pointSchema, tripAnimationSchema } from '../lib/maps/schemes'; 24 import { mapPolygonSchema, pathSchema, pointSchema, tripAnimationSchema } from '../lib/maps/schemes';
25 import { DomSanitizer } from '@angular/platform-browser'; 25 import { DomSanitizer } from '@angular/platform-browser';
@@ -56,17 +56,14 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -56,17 +56,14 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
56 historicalData: FormattedData[][]; 56 historicalData: FormattedData[][];
57 normalizationStep: number; 57 normalizationStep: number;
58 interpolatedTimeData = []; 58 interpolatedTimeData = [];
59 - intervals = [];  
60 widgetConfig: WidgetConfig; 59 widgetConfig: WidgetConfig;
61 - settings; 60 + settings: TripAnimationSettings;
62 mainTooltip = ''; 61 mainTooltip = '';
63 visibleTooltip = false; 62 visibleTooltip = false;
64 activeTrip: FormattedData; 63 activeTrip: FormattedData;
65 label; 64 label;
66 minTime: number; 65 minTime: number;
67 - minTimeFormat: string;  
68 maxTime: number; 66 maxTime: number;
69 - maxTimeFormat: string;  
70 anchors: number[] = []; 67 anchors: number[] = [];
71 useAnchors: boolean; 68 useAnchors: boolean;
72 currentTime: number; 69 currentTime: number;
@@ -98,15 +95,15 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -98,15 +95,15 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
98 this.settings = { ...settings, ...this.ctx.settings }; 95 this.settings = { ...settings, ...this.ctx.settings };
99 this.useAnchors = this.settings.showPoints && this.settings.usePointAsAnchor; 96 this.useAnchors = this.settings.showPoints && this.settings.usePointAsAnchor;
100 this.settings.pointAsAnchorFunction = parseFunction(this.settings.pointAsAnchorFunction, ['data', 'dsData', 'dsIndex']); 97 this.settings.pointAsAnchorFunction = parseFunction(this.settings.pointAsAnchorFunction, ['data', 'dsData', 'dsIndex']);
101 - this.settings.fitMapBounds = true; 98 + this.settings.tooltipFunction = parseFunction(this.settings.tooltipFunction, ['data', 'dsData', 'dsIndex']);
  99 + this.settings.labelFunction = parseFunction(this.settings.labelFunction, ['data', 'dsData', 'dsIndex']);
102 this.normalizationStep = this.settings.normalizationStep; 100 this.normalizationStep = this.settings.normalizationStep;
103 const subscription = this.ctx.subscriptions[Object.keys(this.ctx.subscriptions)[0]]; 101 const subscription = this.ctx.subscriptions[Object.keys(this.ctx.subscriptions)[0]];
104 if (subscription) subscription.callbacks.onDataUpdated = () => { 102 if (subscription) subscription.callbacks.onDataUpdated = () => {
105 this.historicalData = parseArray(this.ctx.data).filter(arr => arr.length); 103 this.historicalData = parseArray(this.ctx.data).filter(arr => arr.length);
106 if (this.historicalData.length) { 104 if (this.historicalData.length) {
107 - this.activeTrip = this.historicalData[0][0];  
108 this.calculateIntervals(); 105 this.calculateIntervals();
109 - this.timeUpdated(this.minTime); 106 + this.timeUpdated(this.currentTime && this.currentTime > this.minTime ? this.currentTime : this.minTime);
110 } 107 }
111 this.mapWidget.map.map?.invalidateSize(); 108 this.mapWidget.map.map?.invalidateSize();
112 this.cd.detectChanges(); 109 this.cd.detectChanges();
@@ -122,12 +119,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -122,12 +119,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
122 this.currentTime = time; 119 this.currentTime = time;
123 const currentPosition = this.interpolatedTimeData 120 const currentPosition = this.interpolatedTimeData
124 .map(dataSource => dataSource[time]) 121 .map(dataSource => dataSource[time])
125 - .filter(ds => ds)  
126 - .map(ds => {  
127 - ds.minTime = this.minTimeFormat;  
128 - ds.maxTime = this.maxTimeFormat;  
129 - return ds;  
130 - }); 122 + .filter(ds => ds);
131 if (isUndefined(currentPosition[0])) { 123 if (isUndefined(currentPosition[0])) {
132 const timePoints = Object.keys(this.interpolatedTimeData[0]).map(item => parseInt(item, 10)); 124 const timePoints = Object.keys(this.interpolatedTimeData[0]).map(item => parseInt(item, 10));
133 for (let i = 1; i < timePoints.length; i++) { 125 for (let i = 1; i < timePoints.length; i++) {
@@ -145,7 +137,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -145,7 +137,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
145 } 137 }
146 } 138 }
147 this.calcLabel(); 139 this.calcLabel();
148 - this.calcTooltip(); 140 + this.calcTooltip(currentPosition.find(position => position.entityName === this.activeTrip.entityName));
149 if (this.mapWidget) { 141 if (this.mapWidget) {
150 this.mapWidget.map.updatePolylines(this.interpolatedTimeData.map(ds => _.values(ds)), this.activeTrip); 142 this.mapWidget.map.updatePolylines(this.interpolatedTimeData.map(ds => _.values(ds)), this.activeTrip);
151 if (this.settings.showPolygon) { 143 if (this.settings.showPolygon) {
@@ -167,11 +159,14 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -167,11 +159,14 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
167 calculateIntervals() { 159 calculateIntervals() {
168 this.historicalData.forEach((dataSource, index) => { 160 this.historicalData.forEach((dataSource, index) => {
169 this.minTime = dataSource[0]?.time || Infinity; 161 this.minTime = dataSource[0]?.time || Infinity;
170 - this.minTimeFormat = this.minTime !== Infinity ? moment(this.minTime).format('YYYY-MM-DD HH:mm:ss') : ''; 162 + const minTimeFormat = this.minTime !== Infinity ? moment(this.minTime).format('YYYY-MM-DD HH:mm:ss') : '';
171 this.maxTime = dataSource[dataSource.length - 1]?.time || -Infinity; 163 this.maxTime = dataSource[dataSource.length - 1]?.time || -Infinity;
172 - this.maxTimeFormat = this.maxTime !== -Infinity ? moment(this.maxTime).format('YYYY-MM-DD HH:mm:ss') : '';  
173 - this.interpolatedTimeData[index] = this.interpolateArray(dataSource); 164 + const maxTimeFormat = this.maxTime !== -Infinity ? moment(this.maxTime).format('YYYY-MM-DD HH:mm:ss') : '';
  165 + this.interpolatedTimeData[index] = this.interpolateArray(dataSource, minTimeFormat, maxTimeFormat);
174 }); 166 });
  167 + if(!this.activeTrip){
  168 + this.activeTrip = this.interpolatedTimeData.map(dataSource => dataSource[this.minTime]).filter(ds => ds)[0];
  169 + }
175 if (this.useAnchors) { 170 if (this.useAnchors) {
176 const anchorDate = Object.entries(_.union(this.interpolatedTimeData)[0]); 171 const anchorDate = Object.entries(_.union(this.interpolatedTimeData)[0]);
177 this.anchors = anchorDate 172 this.anchors = anchorDate
@@ -180,39 +175,26 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -180,39 +175,26 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
180 } 175 }
181 } 176 }
182 177
183 - calcTooltip = (point?: FormattedData, setTooltip = true) => {  
184 - if (!point) {  
185 - point = this.activeTrip;  
186 - }  
187 - const data = {  
188 - ...this.activeTrip,  
189 - maxTime: this.maxTimeFormat,  
190 - minTime: this.minTimeFormat  
191 - } 178 + calcTooltip = (point?: FormattedData) => {
  179 + const data = point ? point : this.activeTrip;
192 const tooltipPattern: string = this.settings.useTooltipFunction ? 180 const tooltipPattern: string = this.settings.useTooltipFunction ?
193 - safeExecute(this.settings.tooolTipFunction, [data, this.historicalData, point.dsIndex]) : this.settings.tooltipPattern; 181 + safeExecute(this.settings.tooltipFunction, [data, this.historicalData, point.dsIndex]) : this.settings.tooltipPattern;
194 const tooltipText = parseWithTranslation.parseTemplate(tooltipPattern, data, true); 182 const tooltipText = parseWithTranslation.parseTemplate(tooltipPattern, data, true);
195 - if (setTooltip) {  
196 - this.mainTooltip = this.sanitizer.sanitize(  
197 - SecurityContext.HTML, tooltipText);  
198 - this.cd.detectChanges();  
199 - } 183 + this.mainTooltip = this.sanitizer.sanitize(
  184 + SecurityContext.HTML, tooltipText);
  185 + this.cd.detectChanges();
200 this.activeTrip = point; 186 this.activeTrip = point;
201 return tooltipText; 187 return tooltipText;
202 } 188 }
203 189
204 calcLabel() { 190 calcLabel() {
205 - const data = {  
206 - ...this.activeTrip,  
207 - maxTime: this.maxTimeFormat,  
208 - minTime: this.minTimeFormat  
209 - } 191 + const data = this.activeTrip;
210 const labelText: string = this.settings.useLabelFunction ? 192 const labelText: string = this.settings.useLabelFunction ?
211 safeExecute(this.settings.labelFunction, [data, this.historicalData, data.dsIndex]) : this.settings.label; 193 safeExecute(this.settings.labelFunction, [data, this.historicalData, data.dsIndex]) : this.settings.label;
212 this.label = (parseWithTranslation.parseTemplate(labelText, data, true)); 194 this.label = (parseWithTranslation.parseTemplate(labelText, data, true));
213 } 195 }
214 196
215 - interpolateArray(originData: FormattedData[]) { 197 + interpolateArray(originData: FormattedData[], minTimeFormat?: string, maxTimeFormat?: string) {
216 const result = {}; 198 const result = {};
217 const latKeyName = this.settings.latKeyName; 199 const latKeyName = this.settings.latKeyName;
218 const lngKeyName = this.settings.lngKeyName; 200 const lngKeyName = this.settings.lngKeyName;
@@ -221,6 +203,8 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -221,6 +203,8 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
221 const normalizeTime = this.minTime + Math.ceil((currentTime - this.minTime) / this.normalizationStep) * this.normalizationStep; 203 const normalizeTime = this.minTime + Math.ceil((currentTime - this.minTime) / this.normalizationStep) * this.normalizationStep;
222 result[normalizeTime] = { 204 result[normalizeTime] = {
223 ...data, 205 ...data,
  206 + minTime: minTimeFormat,
  207 + maxTime: maxTimeFormat,
224 rotationAngle: this.settings.rotationAngle 208 rotationAngle: this.settings.rotationAngle
225 }; 209 };
226 } 210 }