Commit b05c94e9df3d4472cb02a5d4edc0c813020411aa

Authored by Artem Halushko
1 parent 60d8f79f

entity and map fixes

@@ -450,47 +450,46 @@ export function aspectCache(imageUrl: string): Observable<number> { @@ -450,47 +450,46 @@ export function aspectCache(imageUrl: string): Observable<number> {
450 450
451 451
452 export function parseArray(input: any[]): any[] { 452 export function parseArray(input: any[]): any[] {
453 - const alliases: any = _(input).groupBy(el => el?.datasource?.aliasName).values().value();  
454 - return alliases.map((alliasArray, dsIndex) =>  
455 - alliasArray[0].data.map((el, i) => { 453 + return _(input).groupBy(el => el?.datasource?.entityName)
  454 + .values().value().map((entityArray, dsIndex) =>
  455 + entityArray[0].data.map((el, i) => {
  456 + const obj = {
  457 + entityName: entityArray[0]?.datasource?.entityName,
  458 + $datasource: entityArray[0]?.datasource,
  459 + dsIndex,
  460 + time: el[0],
  461 + deviceType: null
  462 + };
  463 + entityArray.forEach(entity => {
  464 + obj[entity?.dataKey?.label] = entity?.data[i][1];
  465 + obj[entity?.dataKey?.label + '|ts'] = entity?.data[0][0];
  466 + if (entity?.dataKey?.label === 'type') {
  467 + obj.deviceType = entity?.data[0][1];
  468 + }
  469 + });
  470 + return obj;
  471 + })
  472 + );
  473 +}
  474 +
  475 +export function parseData(input: any[]): any[] {
  476 + return _(input).groupBy(el => el?.datasource?.entityName)
  477 + .values().value().map((entityArray, i) => {
456 const obj = { 478 const obj = {
457 - aliasName: alliasArray[0]?.datasource?.aliasName,  
458 - entityName: alliasArray[0]?.datasource?.entityName,  
459 - $datasource: alliasArray[0]?.datasource,  
460 - dsIndex,  
461 - time: el[0], 479 + entityName: entityArray[0]?.datasource?.entityName,
  480 + $datasource: entityArray[0]?.datasource,
  481 + dsIndex: i,
462 deviceType: null 482 deviceType: null
463 }; 483 };
464 - alliasArray.forEach(el => {  
465 - obj[el?.dataKey?.label] = el?.data[i][1]; 484 + entityArray.forEach(el => {
  485 + obj[el?.dataKey?.label] = el?.data[0][1];
466 obj[el?.dataKey?.label + '|ts'] = el?.data[0][0]; 486 obj[el?.dataKey?.label + '|ts'] = el?.data[0][0];
467 if (el?.dataKey?.label === 'type') { 487 if (el?.dataKey?.label === 'type') {
468 obj.deviceType = el?.data[0][1]; 488 obj.deviceType = el?.data[0][1];
469 } 489 }
470 }); 490 });
471 return obj; 491 return obj;
472 - })  
473 - );  
474 -}  
475 -  
476 -export function parseData(input: any[]): any[] {  
477 - return _(input).groupBy(el => el?.datasource?.aliasName).values().value().map((alliasArray, i) => {  
478 - const obj = {  
479 - aliasName: alliasArray[0]?.datasource?.aliasName,  
480 - entityName: alliasArray[0]?.datasource?.entityName,  
481 - $datasource: alliasArray[0]?.datasource,  
482 - dsIndex: i,  
483 - deviceType: null  
484 - };  
485 - alliasArray.forEach(el => {  
486 - obj[el?.dataKey?.label] = el?.data[0][1];  
487 - obj[el?.dataKey?.label + '|ts'] = el?.data[0][0];  
488 - if (el?.dataKey?.label === 'type') {  
489 - obj.deviceType = el?.data[0][1];  
490 - }  
491 }); 492 });
492 - return obj;  
493 - });  
494 } 493 }
495 494
496 export function safeExecute(func: Function, params = []) { 495 export function safeExecute(func: Function, params = []) {
@@ -14,26 +14,27 @@ @@ -14,26 +14,27 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import L from 'leaflet'; 17 +import L, { LatLngTuple } from 'leaflet';
18 18
19 import 'leaflet-providers'; 19 import 'leaflet-providers';
20 import 'leaflet.markercluster/dist/MarkerCluster.css' 20 import 'leaflet.markercluster/dist/MarkerCluster.css'
21 import 'leaflet.markercluster/dist/MarkerCluster.Default.css' 21 import 'leaflet.markercluster/dist/MarkerCluster.Default.css'
22 import 'leaflet.markercluster/dist/leaflet.markercluster' 22 import 'leaflet.markercluster/dist/leaflet.markercluster'
23 23
24 -import { MapSettings, MarkerSettings, FormattedData, UnitedMapSettings, PolygonSettings } from './map-models'; 24 +import { MapSettings, MarkerSettings, FormattedData, UnitedMapSettings, PolygonSettings, PolylineSettings } from './map-models';
25 import { Marker } from './markers'; 25 import { Marker } from './markers';
26 import { Observable, of, BehaviorSubject, Subject } from 'rxjs'; 26 import { Observable, of, BehaviorSubject, Subject } from 'rxjs';
27 import { filter } from 'rxjs/operators'; 27 import { filter } from 'rxjs/operators';
28 import { Polyline } from './polyline'; 28 import { Polyline } from './polyline';
29 import { Polygon } from './polygon'; 29 import { Polygon } from './polygon';
  30 +import { DatasourceData } from '@app/shared/models/widget.models';
30 31
31 export default abstract class LeafletMap { 32 export default abstract class LeafletMap {
32 33
33 markers: Map<string, Marker> = new Map(); 34 markers: Map<string, Marker> = new Map();
  35 + polylines: Map<string, Polyline> = new Map();
  36 + polygons: Map<string, Polygon> = new Map();
34 dragMode = true; 37 dragMode = true;
35 - poly: Polyline;  
36 - polygon: Polygon;  
37 map: L.Map; 38 map: L.Map;
38 map$: BehaviorSubject<L.Map> = new BehaviorSubject(null); 39 map$: BehaviorSubject<L.Map> = new BehaviorSubject(null);
39 ready$: Observable<L.Map> = this.map$.pipe(filter(map => !!map)); 40 ready$: Observable<L.Map> = this.map$.pipe(filter(map => !!map));
@@ -78,15 +79,14 @@ export default abstract class LeafletMap { @@ -78,15 +79,14 @@ export default abstract class LeafletMap {
78 const updatedEnttity = { ...ds, ...customLatLng }; 79 const updatedEnttity = { ...ds, ...customLatLng };
79 this.saveMarkerLocation(updatedEnttity); 80 this.saveMarkerLocation(updatedEnttity);
80 this.map.removeLayer(newMarker); 81 this.map.removeLayer(newMarker);
81 - this.deleteMarker(ds.aliasName);  
82 - this.createMarker(ds.aliasName, updatedEnttity, this.datasources, this.options, false); 82 + this.deleteMarker(ds.entityName);
  83 + this.createMarker(ds.entityName, updatedEnttity, this.datasources, this.options, false);
83 } 84 }
84 datasourcesList.append(dsItem); 85 datasourcesList.append(dsItem);
85 }) 86 })
86 const popup = L.popup(); 87 const popup = L.popup();
87 popup.setContent(datasourcesList); 88 popup.setContent(datasourcesList);
88 newMarker.bindPopup(popup).openPopup(); 89 newMarker.bindPopup(popup).openPopup();
89 -  
90 } 90 }
91 addMarker.setPosition('topright') 91 addMarker.setPosition('topright')
92 } 92 }
@@ -165,6 +165,7 @@ export default abstract class LeafletMap { @@ -165,6 +165,7 @@ export default abstract class LeafletMap {
165 } 165 }
166 166
167 convertPosition(expression: object): L.LatLng { 167 convertPosition(expression: object): L.LatLng {
  168 + if (!expression) return null;
168 const lat = expression[this.options.latKeyName]; 169 const lat = expression[this.options.latKeyName];
169 const lng = expression[this.options.lngKeyName]; 170 const lng = expression[this.options.lngKeyName];
170 if (isNaN(lat) || isNaN(lng)) 171 if (isNaN(lat) || isNaN(lng))
@@ -192,11 +193,11 @@ export default abstract class LeafletMap { @@ -192,11 +193,11 @@ export default abstract class LeafletMap {
192 else { 193 else {
193 this.options.icon = null; 194 this.options.icon = null;
194 } 195 }
195 - if (this.markers.get(data.aliasName)) {  
196 - this.updateMarker(data.aliasName, data, markersData, this.options) 196 + if (this.markers.get(data.entityName)) {
  197 + this.updateMarker(data.entityName, data, markersData, this.options)
197 } 198 }
198 else { 199 else {
199 - this.createMarker(data.aliasName, data, markersData, this.options as MarkerSettings); 200 + this.createMarker(data.entityName, data, markersData, this.options as MarkerSettings);
200 } 201 }
201 } 202 }
202 }); 203 });
@@ -207,16 +208,16 @@ export default abstract class LeafletMap { @@ -207,16 +208,16 @@ export default abstract class LeafletMap {
207 this.saveMarkerLocation({ ...data, ...this.convertToCustomFormat(e.target._latlng) }); 208 this.saveMarkerLocation({ ...data, ...this.convertToCustomFormat(e.target._latlng) });
208 } 209 }
209 210
210 - private createMarker(key, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings, setFocus = true) { 211 + private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings, setFocus = true) {
211 this.ready$.subscribe(() => { 212 this.ready$.subscribe(() => {
212 const newMarker = new Marker(this.map, this.convertPosition(data), settings, data, dataSources, () => { }, this.dragMarker); 213 const newMarker = new Marker(this.map, this.convertPosition(data), settings, data, dataSources, () => { }, this.dragMarker);
213 - if (setFocus && settings.fitMapBounds) 214 + if (setFocus /*&& settings.fitMapBounds*/)
214 this.map.fitBounds(this.bounds.extend(newMarker.leafletMarker.getLatLng()).pad(0.2)); 215 this.map.fitBounds(this.bounds.extend(newMarker.leafletMarker.getLatLng()).pad(0.2));
215 this.markers.set(key, newMarker); 216 this.markers.set(key, newMarker);
216 }); 217 });
217 } 218 }
218 219
219 - private updateMarker(key, data, dataSources, settings: MarkerSettings) { 220 + private updateMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings) {
220 const marker: Marker = this.markers.get(key); 221 const marker: Marker = this.markers.get(key);
221 const location = this.convertPosition(data) 222 const location = this.convertPosition(data)
222 if (!location.equals(marker.location)) { 223 if (!location.equals(marker.location)) {
@@ -229,7 +230,7 @@ export default abstract class LeafletMap { @@ -229,7 +230,7 @@ export default abstract class LeafletMap {
229 marker.updateMarkerIcon(settings); 230 marker.updateMarkerIcon(settings);
230 } 231 }
231 232
232 - deleteMarker(key) { 233 + deleteMarker(key: string) {
233 let marker = this.markers.get(key)?.leafletMarker; 234 let marker = this.markers.get(key)?.leafletMarker;
234 if (marker) { 235 if (marker) {
235 this.map.removeLayer(marker); 236 this.map.removeLayer(marker);
@@ -240,12 +241,12 @@ export default abstract class LeafletMap { @@ -240,12 +241,12 @@ export default abstract class LeafletMap {
240 241
241 // Polyline 242 // Polyline
242 243
243 - updatePolylines(polyData: Array<Array<any>>) {  
244 - polyData.forEach(data => { 244 + updatePolylines(polyData: FormattedData[][]) {
  245 + polyData.forEach((data: FormattedData[]) => {
245 if (data.length) { 246 if (data.length) {
246 const dataSource = polyData.map(arr => arr[0]); 247 const dataSource = polyData.map(arr => arr[0]);
247 - if (this.poly) {  
248 - this.updatePolyline(data, dataSource, this.options); 248 + if (this.polylines.get(data[0].entityName)) {
  249 + this.updatePolyline(data[0].entityName, data, dataSource, this.options);
249 } 250 }
250 else { 251 else {
251 this.createPolyline(data, dataSource, this.options); 252 this.createPolyline(data, dataSource, this.options);
@@ -254,67 +255,59 @@ export default abstract class LeafletMap { @@ -254,67 +255,59 @@ export default abstract class LeafletMap {
254 }) 255 })
255 } 256 }
256 257
257 - createPolyline(data: any[], dataSources, settings) { 258 + createPolyline(data: FormattedData[], dataSources: FormattedData[], settings: PolylineSettings) {
258 if (data.length) 259 if (data.length)
259 this.ready$.subscribe(() => { 260 this.ready$.subscribe(() => {
260 - this.poly = new Polyline(this.map, 261 + const poly = new Polyline(this.map,
261 data.map(el => this.convertPosition(el)).filter(el => !!el), data, dataSources, settings); 262 data.map(el => this.convertPosition(el)).filter(el => !!el), data, dataSources, settings);
262 - const bounds = this.bounds.extend(this.poly.leafletPoly.getBounds().pad(0.2)); 263 + const bounds = this.bounds.extend(poly.leafletPoly.getBounds().pad(0.2));
263 if (bounds.isValid()) { 264 if (bounds.isValid()) {
264 this.map.fitBounds(bounds); 265 this.map.fitBounds(bounds);
265 this.bounds = bounds; 266 this.bounds = bounds;
266 } 267 }
  268 + this.polylines.set(data[0].entityName, poly)
267 }); 269 });
268 } 270 }
269 271
270 - updatePolyline(data, dataSources, settings) { 272 + updatePolyline(key: string, data: FormattedData[], dataSources: FormattedData[], settings: PolylineSettings) {
271 this.ready$.subscribe(() => { 273 this.ready$.subscribe(() => {
272 - this.poly.updatePolyline(settings, data, dataSources);  
273 - const bounds = this.bounds.extend(this.poly.leafletPoly.getBounds().pad(0.2));  
274 - if (bounds.isValid()) {  
275 - this.map.fitBounds(bounds);  
276 - this.bounds = bounds;  
277 - } 274 + this.polylines.get(key).updatePolyline(settings, data, dataSources);
278 }); 275 });
279 } 276 }
280 277
281 // Polygon 278 // Polygon
282 279
283 - updatePolygons(polyData: any[]) {  
284 - polyData.forEach((data: any) => { 280 + updatePolygons(polyData: DatasourceData[]) {
  281 + polyData.forEach((data: DatasourceData) => {
285 if (data.data.length && data.dataKey.name === this.options.polygonKeyName) { 282 if (data.data.length && data.dataKey.name === this.options.polygonKeyName) {
286 if (typeof (data?.data[0][1]) === 'string') { 283 if (typeof (data?.data[0][1]) === 'string') {
287 - data.data = JSON.parse(data.data[0][1]); 284 + data.data = JSON.parse(data.data[0][1]) as LatLngTuple[];
288 } 285 }
289 - if (this.polygon) {  
290 - this.updatePolygon(data.data, polyData, this.options); 286 + if (this.polygons.get(data.datasource.entityName)) {
  287 + this.updatePolygon(data.datasource.entityName, data.data, polyData, this.options);
291 } 288 }
292 else { 289 else {
293 - this.createPolygon(data.data, polyData, this.options); 290 + this.createPolygon(data.datasource.entityName, data.data, polyData, this.options);
294 } 291 }
295 } 292 }
296 }); 293 });
297 } 294 }
298 295
299 - createPolygon(data: FormattedData, dataSources: FormattedData[], settings: PolygonSettings) { 296 + createPolygon(key: string, data: LatLngTuple[], dataSources: DatasourceData[], settings: PolygonSettings) {
300 this.ready$.subscribe(() => { 297 this.ready$.subscribe(() => {
301 - this.polygon = new Polygon(this.map, data, dataSources, settings);  
302 - const bounds = this.bounds.extend(this.polygon.leafletPoly.getBounds().pad(0.2)); 298 + const polygon = new Polygon(this.map, data, dataSources, settings);
  299 + const bounds = this.bounds.extend(polygon.leafletPoly.getBounds().pad(0.2));
303 if (bounds.isValid()) { 300 if (bounds.isValid()) {
304 this.map.fitBounds(bounds); 301 this.map.fitBounds(bounds);
305 this.bounds = bounds; 302 this.bounds = bounds;
306 } 303 }
  304 + this.polygons.set(key, polygon);
307 }); 305 });
308 } 306 }
309 307
310 - updatePolygon(data, dataSources, settings) { 308 + updatePolygon(key: string, data: LatLngTuple[], dataSources: DatasourceData[], settings: PolygonSettings) {
311 this.ready$.subscribe(() => { 309 this.ready$.subscribe(() => {
312 - // this.polygon.updatePolygon(settings, data, dataSources);  
313 - const bounds = this.bounds.extend(this.polygon.leafletPoly.getBounds().pad(0.2));  
314 - if (bounds.isValid()) {  
315 - this.map.fitBounds(bounds);  
316 - this.bounds = bounds;  
317 - } 310 + this.polygons.get(key).updatePolygon(data, dataSources, settings);
318 }); 311 });
319 } 312 }
320 } 313 }
@@ -131,4 +131,4 @@ export interface HistorySelectSettings { @@ -131,4 +131,4 @@ export interface HistorySelectSettings {
131 buttonColor: string; 131 buttonColor: string;
132 } 132 }
133 133
134 -export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolygonSettings;  
  134 +export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolylineSettings;
@@ -74,7 +74,8 @@ export class Marker { @@ -74,7 +74,8 @@ export class Marker {
74 74
75 if (settings.showLabel) { 75 if (settings.showLabel) {
76 if (settings.useLabelFunction) { 76 if (settings.useLabelFunction) {
77 - settings.labelText = safeExecute(settings.labelFunction, [this.data, this.dataSources, this.data.dsIndex]) 77 + settings.labelText = parseTemplate(
  78 + safeExecute(settings.labelFunction, [this.data, this.dataSources, this.data.dsIndex]), this.data)
78 } 79 }
79 else settings.labelText = parseTemplate(settings.label, this.data); 80 else settings.labelText = parseTemplate(settings.label, this.data);
80 this.leafletMarker.bindTooltip(`<div style="color: ${settings.labelColor};"><b>${settings.labelText}</b></div>`, 81 this.leafletMarker.bindTooltip(`<div style="color: ${settings.labelColor};"><b>${settings.labelText}</b></div>`,
@@ -14,14 +14,17 @@ @@ -14,14 +14,17 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import L, { LatLngExpression } from 'leaflet'; 17 +import L, { LatLngExpression, LatLngTuple } from 'leaflet';
18 import { createTooltip } from './maps-utils'; 18 import { createTooltip } from './maps-utils';
19 import { PolygonSettings } from './map-models'; 19 import { PolygonSettings } from './map-models';
  20 +import { DatasourceData } from '@app/shared/models/widget.models';
20 21
21 export class Polygon { 22 export class Polygon {
22 23
23 leafletPoly: L.Polygon; 24 leafletPoly: L.Polygon;
24 tooltip; 25 tooltip;
  26 + data;
  27 + dataSources;
25 28
26 constructor(public map, coordinates, dataSources, settings: PolygonSettings, onClickListener?) { 29 constructor(public map, coordinates, dataSources, settings: PolygonSettings, onClickListener?) {
27 this.leafletPoly = L.polygon(coordinates, { 30 this.leafletPoly = L.polygon(coordinates, {
@@ -41,6 +44,13 @@ export class Polygon { @@ -41,6 +44,13 @@ export class Polygon {
41 } 44 }
42 } 45 }
43 46
  47 + updatePolygon(data: LatLngTuple[], dataSources: DatasourceData[], settings: PolygonSettings) {
  48 + this.data = data;
  49 + this.dataSources = dataSources;
  50 + this.leafletPoly.setLatLngs(data);
  51 + this.updatePolygonColor(settings);
  52 + }
  53 +
44 removePolygon() { 54 removePolygon() {
45 this.map.removeLayer(this.leafletPoly); 55 this.map.removeLayer(this.leafletPoly);
46 } 56 }
@@ -253,6 +253,11 @@ export const commonMapSettingsSchema = @@ -253,6 +253,11 @@ export const commonMapSettingsSchema =
253 type: 'boolean', 253 type: 'boolean',
254 default: true 254 default: true
255 }, 255 },
  256 + draggableMarker: {
  257 + title: 'Draggable Marker',
  258 + type: 'boolean',
  259 + default: false
  260 + },
256 disableScrollZooming: { 261 disableScrollZooming: {
257 title: 'Disable scroll zooming', 262 title: 'Disable scroll zooming',
258 type: 'boolean', 263 type: 'boolean',
@@ -371,11 +376,6 @@ export const commonMapSettingsSchema = @@ -371,11 +376,6 @@ export const commonMapSettingsSchema =
371 title: 'Polygon Color function: f(data, dsData, dsIndex)', 376 title: 'Polygon Color function: f(data, dsData, dsIndex)',
372 type: 'string' 377 type: 'string'
373 }, 378 },
374 - draggableMarker: {  
375 - title: 'Draggable Marker',  
376 - type: 'boolean',  
377 - default: false  
378 - },  
379 markerImage: { 379 markerImage: {
380 title: 'Custom marker image', 380 title: 'Custom marker image',
381 type: 'string' 381 type: 'string'
@@ -410,13 +410,13 @@ export const commonMapSettingsSchema = @@ -410,13 +410,13 @@ export const commonMapSettingsSchema =
410 'useDefaultCenterPosition', 410 'useDefaultCenterPosition',
411 'defaultCenterPosition', 411 'defaultCenterPosition',
412 'fitMapBounds', 412 'fitMapBounds',
  413 + 'draggableMarker',
413 'disableScrollZooming', 414 'disableScrollZooming',
414 'latKeyName', 415 'latKeyName',
415 'lngKeyName', 416 'lngKeyName',
416 'showLabel', 417 'showLabel',
417 'label', 418 'label',
418 'useLabelFunction', 419 'useLabelFunction',
419 - 'draggableMarker',  
420 { 420 {
421 key: 'labelFunction', 421 key: 'labelFunction',
422 type: 'javascript' 422 type: 'javascript'