Commit 8979a511a3c939cfaec51d7a7b7b8d9397770d50

Authored by Adsumus
1 parent 8460358f

map fixes

@@ -39,7 +39,9 @@ @@ -39,7 +39,9 @@
39 "node_modules/rc-select/assets/index.less", 39 "node_modules/rc-select/assets/index.less",
40 "node_modules/jstree-bootstrap-theme/dist/themes/proton/style.min.css", 40 "node_modules/jstree-bootstrap-theme/dist/themes/proton/style.min.css",
41 "node_modules/leaflet/dist/leaflet.css", 41 "node_modules/leaflet/dist/leaflet.css",
42 - "src/app/modules/home/components/widget/lib/maps/markers.scss" 42 + "src/app/modules/home/components/widget/lib/maps/markers.scss",
  43 + "node_modules/leaflet.markercluster/dist/MarkerCluster.css",
  44 + "node_modules/leaflet.markercluster/dist/MarkerCluster.Default.css"
43 ], 45 ],
44 "stylePreprocessorOptions": { 46 "stylePreprocessorOptions": {
45 "includePaths": [ 47 "includePaths": [
@@ -1811,8 +1811,7 @@ @@ -1811,8 +1811,7 @@
1811 "@types/geojson": { 1811 "@types/geojson": {
1812 "version": "7946.0.7", 1812 "version": "7946.0.7",
1813 "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.7.tgz", 1813 "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.7.tgz",
1814 - "integrity": "sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ==",  
1815 - "dev": true 1814 + "integrity": "sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ=="
1816 }, 1815 },
1817 "@types/glob": { 1816 "@types/glob": {
1818 "version": "7.1.1", 1817 "version": "7.1.1",
@@ -1876,7 +1875,6 @@ @@ -1876,7 +1875,6 @@
1876 "version": "1.5.12", 1875 "version": "1.5.12",
1877 "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.5.12.tgz", 1876 "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.5.12.tgz",
1878 "integrity": "sha512-61HRMIng+bWvnnAIqUWLBlrd/TQZc4gU+gN1JL4K47EDtwIrcMEhWgi7PdcpbG1YmpH4F0EfOimkvV82gJIl9w==", 1877 "integrity": "sha512-61HRMIng+bWvnnAIqUWLBlrd/TQZc4gU+gN1JL4K47EDtwIrcMEhWgi7PdcpbG1YmpH4F0EfOimkvV82gJIl9w==",
1879 - "dev": true,  
1880 "requires": { 1878 "requires": {
1881 "@types/geojson": "*" 1879 "@types/geojson": "*"
1882 } 1880 }
@@ -1890,6 +1888,14 @@ @@ -1890,6 +1888,14 @@
1890 "@types/leaflet": "*" 1888 "@types/leaflet": "*"
1891 } 1889 }
1892 }, 1890 },
  1891 + "@types/leaflet.markercluster": {
  1892 + "version": "1.4.2",
  1893 + "resolved": "https://registry.npmjs.org/@types/leaflet.markercluster/-/leaflet.markercluster-1.4.2.tgz",
  1894 + "integrity": "sha512-QQ//hevAxMH2dlRQdRre7V/1G+TbtuDtZnZF/75TNwVIgklrsQVCIcS/cvLsl7UUryfPJ6xmoYHfFzK5iGVgpg==",
  1895 + "requires": {
  1896 + "@types/leaflet": "*"
  1897 + }
  1898 + },
1893 "@types/lodash": { 1899 "@types/lodash": {
1894 "version": "4.14.150", 1900 "version": "4.14.150",
1895 "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.150.tgz", 1901 "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.150.tgz",
@@ -8605,7 +8611,7 @@ @@ -8605,7 +8611,7 @@
8605 "integrity": "sha512-4O3GWAYJaauMCILm07weko2rHA8a4kjn7+8Lg4s1d7SxwS/3IpkVD/GljbRrIJ1c1W/XGJ3GbuK7RyYZEJChhw==" 8611 "integrity": "sha512-4O3GWAYJaauMCILm07weko2rHA8a4kjn7+8Lg4s1d7SxwS/3IpkVD/GljbRrIJ1c1W/XGJ3GbuK7RyYZEJChhw=="
8606 }, 8612 },
8607 "ngx-flowchart": { 8613 "ngx-flowchart": {
8608 - "version": "git://github.com/thingsboard/ngx-flowchart.git#a4157b0eef2eb3646ef920447c7b06b39d54f87f", 8614 + "version": "git://github.com/thingsboard/ngx-flowchart.git#97a77477ca8579becf0e3a07866046b4536fe30a",
8609 "from": "git://github.com/thingsboard/ngx-flowchart.git#master", 8615 "from": "git://github.com/thingsboard/ngx-flowchart.git#master",
8610 "requires": { 8616 "requires": {
8611 "tslib": "^1.10.0" 8617 "tslib": "^1.10.0"
@@ -108,6 +108,7 @@ @@ -108,6 +108,7 @@
108 "@types/jstree": "^3.3.39", 108 "@types/jstree": "^3.3.39",
109 "@types/jszip": "^3.1.7", 109 "@types/jszip": "^3.1.7",
110 "@types/leaflet": "^1.5.12", 110 "@types/leaflet": "^1.5.12",
  111 + "@types/leaflet.markercluster": "^1.4.2",
111 "@types/leaflet-polylinedecorator": "^1.6.0", 112 "@types/leaflet-polylinedecorator": "^1.6.0",
112 "@types/lodash": "^4.14.150", 113 "@types/lodash": "^4.14.150",
113 "@types/raphael": "^2.3.0", 114 "@types/raphael": "^2.3.0",
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 /// 15 ///
16 16
17 import _ from 'lodash'; 17 import _ from 'lodash';
18 -import { Observable, Subject, from, fromEvent, of } from 'rxjs'; 18 +import { Observable, Subject, fromEvent, of } from 'rxjs';
19 import { finalize, share, map } from 'rxjs/operators'; 19 import { finalize, share, map } from 'rxjs/operators';
20 import base64js from 'base64-js'; 20 import base64js from 'base64-js';
21 21
@@ -430,7 +430,7 @@ export function getDescendantProp(obj: any, path: string): any { @@ -430,7 +430,7 @@ export function getDescendantProp(obj: any, path: string): any {
430 430
431 export function imageLoader(imageUrl: string): Observable<HTMLImageElement> { 431 export function imageLoader(imageUrl: string): Observable<HTMLImageElement> {
432 const image = new Image(); 432 const image = new Image();
433 - const imageLoad$ = fromEvent(image, 'load').pipe(map(event => image)); 433 + const imageLoad$ = fromEvent(image, 'load').pipe(map(() => image));
434 image.src = imageUrl; 434 image.src = imageUrl;
435 return imageLoad$; 435 return imageLoad$;
436 } 436 }
@@ -524,37 +524,49 @@ export function parseFunction(source: any, params: string[] = []): Function { @@ -524,37 +524,49 @@ export function parseFunction(source: any, params: string[] = []): Function {
524 return res; 524 return res;
525 } 525 }
526 526
527 -export function parseTemplate(template: string, data: object) { 527 +export function parseTemplate(template: string, data: object, translateFn?: (key: string) => string) {
528 let res = ''; 528 let res = '';
  529 + let variables = '';
529 try { 530 try {
530 - template = template.replace(/<link-act/g, '<a').replace(/link-act>/g, 'a>').replace(/name=(\'|")(.*?)(\'|")/g, `class='tb-custom-action' id='$2'`);  
531 - let variables = '';  
532 - const expressions = template  
533 - .match(/\{(.*?)\}/g) // find expressions  
534 - .map(exp => exp.replace(/{|}/g, '')) // remove brackets  
535 - .map(exp => exp.split(':'))  
536 - .map(arr => {  
537 - variables += `let ${arr[0]} = ''; `;  
538 - return arr;  
539 - })  
540 - .filter(arr => !!arr[1]) // filter expressions without format  
541 - .reduce((res, current) => {  
542 - res[current[0]] = current[1];  
543 - return res;  
544 - }, {});  
545 -  
546 - for (const key in data) {  
547 - if (!key.includes('|'))  
548 - variables += `${key} = '${expressions[key] ? padValue(data[key], +expressions[key]) : data[key]}';`; 531 + if (template.match(/<link-act/g)) {
  532 + template = template.replace(/<link-act/g, '<a').replace(/link-act>/g, 'a>').replace(/name=(\'|")(.*?)(\'|")/g, `class='tb-custom-action' id='$2'`);
  533 + }
  534 + if (template.includes('i18n')) {
  535 + const translateRegexp = /\{i18n:(.*?)\}/;
  536 + template.match(new RegExp(translateRegexp.source, translateRegexp.flags + 'g')).forEach(match => {
  537 + template = template.replace(match, translateFn(match.match(translateRegexp)[1]));
  538 + });
  539 + }
  540 + const expressions = template.match(/\{(.*?)\}/g);
  541 + if (expressions) {
  542 + const clearMatches = template.match(/(?<=\{)(.+?)(?=(\}|\:))/g);
  543 + for (const key in data) {
  544 + if (!key.includes('|'))
  545 + variables += `let ${key} = '${clearMatches[key] ? padValue(data[key], +clearMatches[key]) : data[key]}';`;
  546 + }
  547 + template = template.replace(/\:\d+\}/g, '}');
  548 + res = safeExecute(parseFunction(variables + ' return' + '`' + template + '`'));
549 } 549 }
550 - template = template.replace(/:\d+}/g, '}');  
551 - res = safeExecute(parseFunction(variables + 'return' + '`' + template + '`')); 550 + else res = template;
552 } 551 }
553 catch (ex) { 552 catch (ex) {
  553 + console.log(ex, variables, template)
554 } 554 }
555 return res; 555 return res;
556 } 556 }
557 557
  558 +export let parseWithTranslation = {
  559 + translate(): string {
  560 + throw console.error('Translate not assigned');
  561 + },
  562 + parseTemplate(template: string, data: object, forceTranslate = false): string {
  563 + return parseTemplate(forceTranslate ? this.translate(template) : template, data, this?.translate);
  564 + },
  565 + setTranslate(translateFn: (key: string, defaultTranslation?: string) => string) {
  566 + this.translate = translateFn;
  567 + }
  568 +}
  569 +
558 export function padValue(val: any, dec: number): string { 570 export function padValue(val: any, dec: number): string {
559 let strVal; 571 let strVal;
560 let n; 572 let n;
@@ -14,12 +14,12 @@ @@ -14,12 +14,12 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import L, { LatLngTuple } from 'leaflet'; 17 +import L, { LatLngTuple, LatLngBounds, Point } from 'leaflet';
18 18
19 import 'leaflet-providers'; 19 import 'leaflet-providers';
20 -import 'leaflet.markercluster/dist/MarkerCluster.css'  
21 -import 'leaflet.markercluster/dist/MarkerCluster.Default.css'  
22 -import 'leaflet.markercluster/dist/leaflet.markercluster' 20 +import 'leaflet.markercluster/dist/MarkerCluster.css';
  21 +import 'leaflet.markercluster/dist/MarkerCluster.Default.css';
  22 +import LM from 'leaflet.markercluster/dist/leaflet.markercluster';
23 23
24 import { MapSettings, MarkerSettings, FormattedData, UnitedMapSettings, PolygonSettings, PolylineSettings } from './map-models'; 24 import { MapSettings, MarkerSettings, FormattedData, UnitedMapSettings, PolygonSettings, PolylineSettings } from './map-models';
25 import { Marker } from './markers'; 25 import { Marker } from './markers';
@@ -34,7 +34,7 @@ export default abstract class LeafletMap { @@ -34,7 +34,7 @@ export default abstract class LeafletMap {
34 markers: Map<string, Marker> = new Map(); 34 markers: Map<string, Marker> = new Map();
35 polylines: Map<string, Polyline> = new Map(); 35 polylines: Map<string, Polyline> = new Map();
36 polygons: Map<string, Polygon> = new Map(); 36 polygons: Map<string, Polygon> = new Map();
37 - dragMode = true; 37 + dragMode = false;
38 map: L.Map; 38 map: L.Map;
39 map$: BehaviorSubject<L.Map> = new BehaviorSubject(null); 39 map$: BehaviorSubject<L.Map> = new BehaviorSubject(null);
40 ready$: Observable<L.Map> = this.map$.pipe(filter(map => !!map)); 40 ready$: Observable<L.Map> = this.map$.pipe(filter(map => !!map));
@@ -43,6 +43,7 @@ export default abstract class LeafletMap { @@ -43,6 +43,7 @@ export default abstract class LeafletMap {
43 bounds: L.LatLngBounds; 43 bounds: L.LatLngBounds;
44 newMarker: L.Marker; 44 newMarker: L.Marker;
45 datasources: FormattedData[]; 45 datasources: FormattedData[];
  46 + markersCluster: LM.markerClusterGroup;
46 47
47 constructor(public $container: HTMLElement, options: UnitedMapSettings) { 48 constructor(public $container: HTMLElement, options: UnitedMapSettings) {
48 this.options = options; 49 this.options = options;
@@ -50,13 +51,38 @@ export default abstract class LeafletMap { @@ -50,13 +51,38 @@ export default abstract class LeafletMap {
50 51
51 public initSettings(options: MapSettings) { 52 public initSettings(options: MapSettings) {
52 const { initCallback, 53 const { initCallback,
53 - disableScrollZooming, }: MapSettings = options; 54 + disableScrollZooming,
  55 + useClusterMarkers,
  56 + zoomOnClick,
  57 + showCoverageOnHover,
  58 + removeOutsideVisibleBounds,
  59 + animate,
  60 + chunkedLoading,
  61 + maxClusterRadius,
  62 + maxZoom }: MapSettings = options;
54 if (disableScrollZooming) { 63 if (disableScrollZooming) {
55 this.map.scrollWheelZoom.disable(); 64 this.map.scrollWheelZoom.disable();
56 } 65 }
57 if (initCallback) { 66 if (initCallback) {
58 setTimeout(options.initCallback, 0); 67 setTimeout(options.initCallback, 0);
59 } 68 }
  69 + if (useClusterMarkers) {
  70 + const clusteringSettings: LM.MarkerClusterGroupOptions = {
  71 + zoomToBoundsOnClick: zoomOnClick,
  72 + showCoverageOnHover,
  73 + removeOutsideVisibleBounds,
  74 + animate,
  75 + chunkedLoading
  76 + };
  77 + if (maxClusterRadius && maxClusterRadius > 0) {
  78 + clusteringSettings.maxClusterRadius = Math.floor(maxClusterRadius);
  79 + }
  80 + if (maxZoom && maxZoom >= 0 && maxZoom < 19) {
  81 + clusteringSettings.disableClusteringAtZoom = Math.floor(maxZoom);
  82 + }
  83 + this.markersCluster = LM.markerClusterGroup(clusteringSettings);
  84 + this.ready$.subscribe(map => map.addLayer(this.markersCluster));
  85 + }
60 } 86 }
61 87
62 addMarkerControl() { 88 addMarkerControl() {
@@ -80,7 +106,7 @@ export default abstract class LeafletMap { @@ -80,7 +106,7 @@ export default abstract class LeafletMap {
80 this.saveMarkerLocation(updatedEnttity); 106 this.saveMarkerLocation(updatedEnttity);
81 this.map.removeLayer(newMarker); 107 this.map.removeLayer(newMarker);
82 this.deleteMarker(ds.entityName); 108 this.deleteMarker(ds.entityName);
83 - this.createMarker(ds.entityName, updatedEnttity, this.datasources, this.options, false); 109 + this.createMarker(ds.entityName, updatedEnttity, this.datasources, this.options);
84 } 110 }
85 datasourcesList.append(dsItem); 111 datasourcesList.append(dsItem);
86 }); 112 });
@@ -103,7 +129,7 @@ export default abstract class LeafletMap { @@ -103,7 +129,7 @@ export default abstract class LeafletMap {
103 img.src = `assets/add_location.svg`; 129 img.src = `assets/add_location.svg`;
104 img.style.width = '32px'; 130 img.style.width = '32px';
105 img.style.height = '32px'; 131 img.style.height = '32px';
106 - img.title = "Drag and drop to add marker"; 132 + img.title = 'Drag and drop to add marker';
107 img.onclick = this.dragMarker; 133 img.onclick = this.dragMarker;
108 img.draggable = true; 134 img.draggable = true;
109 const draggableImg = new L.Draggable(img); 135 const draggableImg = new L.Draggable(img);
@@ -115,11 +141,9 @@ export default abstract class LeafletMap { @@ -115,11 +141,9 @@ export default abstract class LeafletMap {
115 }, 141 },
116 dragMarker: this.dragMarker 142 dragMarker: this.dragMarker
117 } as any); 143 } as any);
118 -  
119 L.control.addMarker = (opts) => { 144 L.control.addMarker = (opts) => {
120 return new L.Control.AddMarker(opts); 145 return new L.Control.AddMarker(opts);
121 } 146 }
122 -  
123 addMarker = L.control.addMarker({ position: 'topright' }).addTo(this.map); 147 addMarker = L.control.addMarker({ position: 'topright' }).addTo(this.map);
124 } 148 }
125 } 149 }
@@ -171,9 +195,9 @@ export default abstract class LeafletMap { @@ -171,9 +195,9 @@ export default abstract class LeafletMap {
171 return this.map.getCenter(); 195 return this.map.getCenter();
172 } 196 }
173 197
174 - fitBounds(bounds, useDefaultZoom = false) { 198 + fitBounds(bounds: LatLngBounds, useDefaultZoom = false, padding?: LatLngTuple) {
175 if (bounds.isValid()) { 199 if (bounds.isValid()) {
176 - if ((this.options.dontFitMapBounds || useDefaultZoom) && this.options.defaultZoomLevel) { 200 + if ((!this.options.fitMapBounds || useDefaultZoom) && this.options.defaultZoomLevel) {
177 this.map.setZoom(this.options.defaultZoomLevel, { animate: false }); 201 this.map.setZoom(this.options.defaultZoomLevel, { animate: false });
178 this.map.panTo(bounds.getCenter(), { animate: false }); 202 this.map.panTo(bounds.getCenter(), { animate: false });
179 } else { 203 } else {
@@ -182,7 +206,7 @@ export default abstract class LeafletMap { @@ -182,7 +206,7 @@ export default abstract class LeafletMap {
182 this.map.setZoom(this.options.minZoomLevel, { animate: false }); 206 this.map.setZoom(this.options.minZoomLevel, { animate: false });
183 } 207 }
184 }); 208 });
185 - this.map.fitBounds(bounds, { padding: [50, 50], animate: false }); 209 + this.map.fitBounds(bounds, { padding: padding || [50, 50], animate: false });
186 } 210 }
187 this.bounds = this.bounds.extend(bounds); 211 this.bounds = this.bounds.extend(bounds);
188 } 212 }
@@ -232,11 +256,17 @@ export default abstract class LeafletMap { @@ -232,11 +256,17 @@ export default abstract class LeafletMap {
232 this.saveMarkerLocation({ ...data, ...this.convertToCustomFormat(e.target._latlng) }); 256 this.saveMarkerLocation({ ...data, ...this.convertToCustomFormat(e.target._latlng) });
233 } 257 }
234 258
235 - private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings, setFocus = true) { 259 + private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings) {
236 this.ready$.subscribe(() => { 260 this.ready$.subscribe(() => {
237 - const newMarker = new Marker(this.map, this.convertPosition(data), settings, data, dataSources, () => { }, this.dragMarker);  
238 - this.fitBounds(this.bounds.extend(newMarker.leafletMarker.getLatLng()), setFocus); 261 + const newMarker = new Marker(this.convertPosition(data), settings, data, dataSources, this.dragMarker);
  262 + this.fitBounds(this.bounds.extend(newMarker.leafletMarker.getLatLng()), settings.draggableMarker && this.markers.size > 2);
239 this.markers.set(key, newMarker); 263 this.markers.set(key, newMarker);
  264 + if (this.options.useClusterMarkers) {
  265 + this.markersCluster.addLayer(newMarker.leafletMarker);
  266 + }
  267 + else {
  268 + this.map.addLayer(newMarker.leafletMarker);
  269 + }
240 }); 270 });
241 } 271 }
242 272
@@ -249,6 +279,8 @@ export default abstract class LeafletMap { @@ -249,6 +279,8 @@ export default abstract class LeafletMap {
249 if (settings.showTooltip) { 279 if (settings.showTooltip) {
250 marker.updateMarkerTooltip(data); 280 marker.updateMarkerTooltip(data);
251 } 281 }
  282 + if (settings.useClusterMarkers)
  283 + this.markersCluster.refreshClusters()
252 marker.setDataSources(data, dataSources); 284 marker.setDataSources(data, dataSources);
253 marker.updateMarkerIcon(settings); 285 marker.updateMarkerIcon(settings);
254 } 286 }
@@ -327,7 +359,9 @@ export default abstract class LeafletMap { @@ -327,7 +359,9 @@ export default abstract class LeafletMap {
327 359
328 updatePolygon(key: string, data: LatLngTuple[], dataSources: DatasourceData[], settings: PolygonSettings) { 360 updatePolygon(key: string, data: LatLngTuple[], dataSources: DatasourceData[], settings: PolygonSettings) {
329 this.ready$.subscribe(() => { 361 this.ready$.subscribe(() => {
330 - this.polygons.get(key).updatePolygon(data, dataSources, settings); 362 + const poly = this.polygons.get(key);
  363 + poly.updatePolygon(data, dataSources, settings);
  364 + this.fitBounds(poly.leafletPoly.getBounds());
331 }); 365 });
332 } 366 }
333 } 367 }
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { LatLngTuple } from 'leaflet'; 17 +import { LatLngTuple, LeafletMouseEvent } from 'leaflet';
18 18
19 export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; 19 export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string;
20 export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; 20 export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string;
@@ -24,14 +24,15 @@ export type MapSettings = { @@ -24,14 +24,15 @@ export type MapSettings = {
24 draggableMarker: boolean; 24 draggableMarker: boolean;
25 initCallback?: () => any; 25 initCallback?: () => any;
26 defaultZoomLevel?: number; 26 defaultZoomLevel?: number;
27 - dontFitMapBounds?: boolean;  
28 disableScrollZooming?: boolean; 27 disableScrollZooming?: boolean;
29 minZoomLevel?: number; 28 minZoomLevel?: number;
  29 + useClusterMarkers: boolean;
30 latKeyName?: string; 30 latKeyName?: string;
31 lngKeyName?: string; 31 lngKeyName?: string;
32 xPosKeyName?: string; 32 xPosKeyName?: string;
33 yPosKeyName?: string; 33 yPosKeyName?: string;
34 mapProvider: MapProviders; 34 mapProvider: MapProviders;
  35 + mapProviderHere: string;
35 mapUrl?: string; 36 mapUrl?: string;
36 mapImageUrl?: string; 37 mapImageUrl?: string;
37 provider?: MapProviders; 38 provider?: MapProviders;
@@ -42,6 +43,13 @@ export type MapSettings = { @@ -42,6 +43,13 @@ export type MapSettings = {
42 gmDefaultMapType?: string; 43 gmDefaultMapType?: string;
43 useLabelFunction: string; 44 useLabelFunction: string;
44 icon?: any; 45 icon?: any;
  46 + zoomOnClick: boolean,
  47 + maxZoom: number,
  48 + showCoverageOnHover: boolean,
  49 + animate: boolean,
  50 + maxClusterRadius: number,
  51 + chunkedLoading: boolean,
  52 + removeOutsideVisibleBounds: boolean
45 } 53 }
46 54
47 export enum MapProviders { 55 export enum MapProviders {
@@ -54,7 +62,7 @@ export enum MapProviders { @@ -54,7 +62,7 @@ export enum MapProviders {
54 62
55 export type MarkerSettings = { 63 export type MarkerSettings = {
56 tooltipPattern?: any; 64 tooltipPattern?: any;
57 - tooltipActions: object; 65 + tooltipAction: { [name: string]: actionsHandler };
58 icon?: any; 66 icon?: any;
59 showLabel?: boolean; 67 showLabel?: boolean;
60 label: string; 68 label: string;
@@ -63,19 +71,19 @@ export type MarkerSettings = { @@ -63,19 +71,19 @@ export type MarkerSettings = {
63 useLabelFunction: boolean; 71 useLabelFunction: boolean;
64 draggableMarker: boolean; 72 draggableMarker: boolean;
65 showTooltip?: boolean; 73 showTooltip?: boolean;
  74 + useTooltipFunction: boolean;
  75 + useColorFunction: boolean;
66 color?: string; 76 color?: string;
67 autocloseTooltip: boolean; 77 autocloseTooltip: boolean;
68 - displayTooltipAction: string; 78 + showTooltipAction: string;
  79 + useClusterMarkers: boolean;
69 currentImage?: string; 80 currentImage?: string;
70 useMarkerImageFunction?: boolean; 81 useMarkerImageFunction?: boolean;
71 markerImages?: string[]; 82 markerImages?: string[];
72 - useMarkerImage: boolean;  
73 markerImageSize: number; 83 markerImageSize: number;
74 fitMapBounds: boolean; 84 fitMapBounds: boolean;
75 - markerImage: {  
76 - length: number  
77 - }  
78 - 85 + markerImage: string;
  86 + markerClick: { [name: string]: actionsHandler };
79 colorFunction: GenericFunction; 87 colorFunction: GenericFunction;
80 tooltipFunction: GenericFunction; 88 tooltipFunction: GenericFunction;
81 labelFunction: GenericFunction; 89 labelFunction: GenericFunction;
@@ -99,18 +107,18 @@ export type PolygonSettings = { @@ -99,18 +107,18 @@ export type PolygonSettings = {
99 polygonStrokeColor: string; 107 polygonStrokeColor: string;
100 polygonColor: string; 108 polygonColor: string;
101 autocloseTooltip: boolean; 109 autocloseTooltip: boolean;
102 - displayTooltipAction: string;  
103 - tooltipActions: object;  
104 - 110 + showTooltipAction: string;
  111 + tooltipAction: object;
  112 + polygonClick: { [name: string]: actionsHandler };
105 polygonColorFunction?: GenericFunction; 113 polygonColorFunction?: GenericFunction;
106 } 114 }
107 115
108 export type PolylineSettings = { 116 export type PolylineSettings = {
109 usePolylineDecorator: any; 117 usePolylineDecorator: any;
110 autocloseTooltip: boolean; 118 autocloseTooltip: boolean;
111 - displayTooltipAction: string; 119 + showTooltipAction: string;
112 useColorFunction: any; 120 useColorFunction: any;
113 - tooltipActions: object; 121 + tooltipAction: { [name: string]: actionsHandler };
114 color: string; 122 color: string;
115 useStrokeOpacityFunction: any; 123 useStrokeOpacityFunction: any;
116 strokeOpacity: number; 124 strokeOpacity: number;
@@ -134,4 +142,6 @@ export interface HistorySelectSettings { @@ -134,4 +142,6 @@ export interface HistorySelectSettings {
134 buttonColor: string; 142 buttonColor: string;
135 } 143 }
136 144
137 -export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolylineSettings;  
  145 +export type actionsHandler = ($event: Event | LeafletMouseEvent) => void;
  146 +
  147 +export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolylineSettings;
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 /// 15 ///
16 16
17 import { JsonSettingsSchema } from '@shared/models/widget.models'; 17 import { JsonSettingsSchema } from '@shared/models/widget.models';
  18 +import { MapProviders } from './map-models';
18 19
19 export interface MapWidgetInterface { 20 export interface MapWidgetInterface {
20 resize(), 21 resize(),
@@ -24,8 +25,8 @@ export interface MapWidgetInterface { @@ -24,8 +25,8 @@ export interface MapWidgetInterface {
24 } 25 }
25 26
26 export interface MapWidgetStaticInterface { 27 export interface MapWidgetStaticInterface {
27 - settingsSchema(mapProvider?, drawRoutes?): JsonSettingsSchema;  
28 - getProvidersSchema():JsonSettingsSchema 28 + settingsSchema(mapProvider?: MapProviders, drawRoutes?: boolean): JsonSettingsSchema;
  29 + getProvidersSchema(mapProvider?: MapProviders): JsonSettingsSchema
29 dataKeySettingsSchema(): object; 30 dataKeySettingsSchema(): object;
30 actionSources(): object; 31 actionSources(): object;
31 } 32 }
@@ -26,11 +26,12 @@ import { @@ -26,11 +26,12 @@ import {
26 markerClusteringSettingsSchema, 26 markerClusteringSettingsSchema,
27 markerClusteringSettingsSchemaLeaflet, 27 markerClusteringSettingsSchemaLeaflet,
28 hereMapSettingsSchema, 28 hereMapSettingsSchema,
29 - mapProviderSchema 29 + mapProviderSchema,
  30 + mapPolygonSchema
30 } from './schemes'; 31 } from './schemes';
31 import { MapWidgetStaticInterface, MapWidgetInterface } from './map-widget.interface'; 32 import { MapWidgetStaticInterface, MapWidgetInterface } from './map-widget.interface';
32 import { OpenStreetMap, TencentMap, GoogleMap, HEREMap, ImageMap } from './providers'; 33 import { OpenStreetMap, TencentMap, GoogleMap, HEREMap, ImageMap } from './providers';
33 -import { parseFunction, parseArray, parseData, safeExecute } from '@core/utils'; 34 +import { parseFunction, parseArray, parseData, safeExecute, parseWithTranslation } from '@core/utils';
34 import { initSchema, addToSchema, mergeSchemes, addCondition, addGroupInfo } from '@core/schema-utils'; 35 import { initSchema, addToSchema, mergeSchemes, addCondition, addGroupInfo } from '@core/schema-utils';
35 import { forkJoin } from 'rxjs'; 36 import { forkJoin } from 'rxjs';
36 import { WidgetContext } from '@app/modules/home/models/widget-component.models'; 37 import { WidgetContext } from '@app/modules/home/models/widget-component.models';
@@ -40,12 +41,13 @@ import { EntityId } from '@shared/models/id/entity-id'; @@ -40,12 +41,13 @@ import { EntityId } from '@shared/models/id/entity-id';
40 import { AttributeScope } from '@shared/models/telemetry/telemetry.models'; 41 import { AttributeScope } from '@shared/models/telemetry/telemetry.models';
41 import { AttributeService } from '@core/http/attribute.service'; 42 import { AttributeService } from '@core/http/attribute.service';
42 import { Type } from '@angular/core'; 43 import { Type } from '@angular/core';
  44 +import { TranslateService } from '@ngx-translate/core';
  45 +import { UtilsService } from '@app/core/public-api';
43 46
44 // @dynamic 47 // @dynamic
45 export class MapWidgetController implements MapWidgetInterface { 48 export class MapWidgetController implements MapWidgetInterface {
46 49
47 constructor(public mapProvider: MapProviders, private drawRoutes: boolean, public ctx: WidgetContext, $element: HTMLElement) { 50 constructor(public mapProvider: MapProviders, private drawRoutes: boolean, public ctx: WidgetContext, $element: HTMLElement) {
48 - console.log("MapWidgetController -> constructor -> ctx", ctx)  
49 if (this.map) { 51 if (this.map) {
50 this.map.map.remove(); 52 this.map.map.remove();
51 delete this.map; 53 delete this.map;
@@ -56,15 +58,16 @@ export class MapWidgetController implements MapWidgetInterface { @@ -56,15 +58,16 @@ export class MapWidgetController implements MapWidgetInterface {
56 $element = ctx.$container[0]; 58 $element = ctx.$container[0];
57 } 59 }
58 this.settings = this.initSettings(ctx.settings); 60 this.settings = this.initSettings(ctx.settings);
59 - const descriptors = this.ctx.actionsApi.getActionDescriptors('tooltipAction');  
60 - this.settings.tooltipActions = {};  
61 - descriptors.forEach(descriptor => {  
62 - this.settings.tooltipActions[descriptor.name] = ($event) => this.onTooltipClick(descriptor, $event);  
63 - }, this.settings.tooltipActions); 61 + this.settings.tooltipAction = this.getDescriptors('tooltipAction');
  62 + this.settings.markerClick = this.getDescriptors('markerClick');
  63 + this.settings.polygonClick = this.getDescriptors('polygonClick');
  64 +
  65 + // this.settings.
64 const MapClass = providerSets[this.provider]?.MapClass; 66 const MapClass = providerSets[this.provider]?.MapClass;
65 if (!MapClass) { 67 if (!MapClass) {
66 return; 68 return;
67 } 69 }
  70 + parseWithTranslation.setTranslate(this.translate);
68 this.map = new MapClass($element, this.settings); 71 this.map = new MapClass($element, this.settings);
69 this.map.saveMarkerLocation = this.setMarkerLocation; 72 this.map.saveMarkerLocation = this.setMarkerLocation;
70 } 73 }
@@ -74,13 +77,13 @@ export class MapWidgetController implements MapWidgetInterface { @@ -74,13 +77,13 @@ export class MapWidgetController implements MapWidgetInterface {
74 schema: JsonSettingsSchema; 77 schema: JsonSettingsSchema;
75 data; 78 data;
76 settings: UnitedMapSettings; 79 settings: UnitedMapSettings;
77 - actions: Map<string, Map<string, (widgetContext: WidgetContext) => void>>;  
78 80
79 public static dataKeySettingsSchema(): object { 81 public static dataKeySettingsSchema(): object {
80 return {}; 82 return {};
81 } 83 }
82 84
83 - public static getProvidersSchema() { 85 + public static getProvidersSchema(mapProvider: MapProviders) {
  86 + mapProviderSchema.schema.properties.provider.default = mapProvider;
84 return mergeSchemes([mapProviderSchema, 87 return mergeSchemes([mapProviderSchema,
85 ...Object.values(providerSets)?.map( 88 ...Object.values(providerSets)?.map(
86 (setting: IProvider) => addCondition(setting?.schema, `model.provider === '${setting.name}'`))]); 89 (setting: IProvider) => addCondition(setting?.schema, `model.provider === '${setting.name}'`))]);
@@ -88,18 +91,20 @@ export class MapWidgetController implements MapWidgetInterface { @@ -88,18 +91,20 @@ export class MapWidgetController implements MapWidgetInterface {
88 91
89 public static settingsSchema(mapProvider: MapProviders, drawRoutes: boolean): JsonSettingsSchema { 92 public static settingsSchema(mapProvider: MapProviders, drawRoutes: boolean): JsonSettingsSchema {
90 const schema = initSchema(); 93 const schema = initSchema();
91 - addToSchema(schema, this.getProvidersSchema());  
92 - addGroupInfo(schema, 'Map Provider Settings');  
93 - addToSchema(schema, commonMapSettingsSchema); 94 + addToSchema(schema, this.getProvidersSchema(mapProvider));
  95 + if(mapProvider!=='image-map'){
  96 + addGroupInfo(schema, 'Map Provider Settings');
  97 + addToSchema(schema, mergeSchemes([commonMapSettingsSchema, addCondition(mapPolygonSchema, 'model.showPolygon === true')]));
94 addGroupInfo(schema, 'Common Map Settings'); 98 addGroupInfo(schema, 'Common Map Settings');
95 if (drawRoutes) { 99 if (drawRoutes) {
96 addToSchema(schema, routeMapSettingsSchema); 100 addToSchema(schema, routeMapSettingsSchema);
97 addGroupInfo(schema, 'Route Map Settings'); 101 addGroupInfo(schema, 'Route Map Settings');
98 } else if (mapProvider !== 'image-map') { 102 } else if (mapProvider !== 'image-map') {
99 - const clusteringSchema = mergeSchemes([markerClusteringSettingsSchemaLeaflet, markerClusteringSettingsSchema]) 103 + const clusteringSchema = mergeSchemes([markerClusteringSettingsSchema,
  104 + addCondition(markerClusteringSettingsSchemaLeaflet, `model.useClusterMarkers === true`)])
100 addToSchema(schema, clusteringSchema); 105 addToSchema(schema, clusteringSchema);
101 addGroupInfo(schema, 'Markers Clustering Settings'); 106 addGroupInfo(schema, 'Markers Clustering Settings');
102 - } 107 + }}
103 return schema; 108 return schema;
104 } 109 }
105 110
@@ -120,14 +125,28 @@ export class MapWidgetController implements MapWidgetInterface { @@ -120,14 +125,28 @@ export class MapWidgetController implements MapWidgetInterface {
120 }; 125 };
121 } 126 }
122 127
  128 + translate = (key: string, defaultTranslation?: string):string => {
  129 + return (this.ctx.$injector.get(UtilsService).customTranslation(key, defaultTranslation || key)
  130 + || this.ctx.$injector.get(TranslateService).instant(key));
  131 + }
  132 +
  133 + getDescriptors(name: string): { [name: string]: ($event: Event) => void } {
  134 + const descriptors = this.ctx.actionsApi.getActionDescriptors(name);
  135 + const actions = {};
  136 + descriptors.forEach(descriptor => {
  137 + actions[descriptor.name] = ($event: Event) => this.onCustomAction(descriptor, $event);
  138 + }, actions);
  139 + return actions;
  140 + }
  141 +
123 onInit() { 142 onInit() {
124 } 143 }
125 144
126 - private onTooltipClick(descriptor: WidgetActionDescriptor, $event: any) {  
127 - if ($event) {  
128 - $event.stopPropagation(); 145 + private onCustomAction(descriptor: WidgetActionDescriptor, $event: any) {
  146 + if ($event & $event.stopPropagation) {
  147 + $event?.stopPropagation();
129 } 148 }
130 - // safeExecute(parseFunction(descriptor.customFunction, ['$event', 'widgetContext']), [$event, this.ctx]) 149 + // safeExecute(parseFunction(descriptor.customFunction, ['$event', 'widgetContext']), [$event, this.ctx])
131 const entityInfo = this.ctx.actionsApi.getActiveEntityInfo(); 150 const entityInfo = this.ctx.actionsApi.getActiveEntityInfo();
132 const entityId = entityInfo ? entityInfo.entityId : null; 151 const entityId = entityInfo ? entityInfo.entityId : null;
133 const entityName = entityInfo ? entityInfo.entityName : null; 152 const entityName = entityInfo ? entityInfo.entityName : null;
@@ -153,13 +172,17 @@ export class MapWidgetController implements MapWidgetInterface { @@ -153,13 +172,17 @@ export class MapWidgetController implements MapWidgetInterface {
153 }] 172 }]
154 ); 173 );
155 })).subscribe(res => { 174 })).subscribe(res => {
156 - console.log('MapWidgetController -> setMarkerLocation -> res', res)  
157 }); 175 });
158 } 176 }
159 177
160 initSettings(settings: UnitedMapSettings): UnitedMapSettings { 178 initSettings(settings: UnitedMapSettings): UnitedMapSettings {
161 const functionParams = ['data', 'dsData', 'dsIndex']; 179 const functionParams = ['data', 'dsData', 'dsIndex'];
162 this.provider = settings.provider || this.mapProvider; 180 this.provider = settings.provider || this.mapProvider;
  181 + if (!settings.mapProviderHere) {
  182 + if (settings.mapProvider && hereProviders.includes(settings.mapProvider))
  183 + settings.mapProviderHere = settings.mapProvider
  184 + else settings.mapProviderHere = hereProviders[0];
  185 + }
163 const customOptions = { 186 const customOptions = {
164 provider: this.provider, 187 provider: this.provider,
165 mapUrl: settings?.mapImageUrl, 188 mapUrl: settings?.mapImageUrl,
@@ -173,7 +196,7 @@ export class MapWidgetController implements MapWidgetInterface { @@ -173,7 +196,7 @@ export class MapWidgetController implements MapWidgetInterface {
173 '<b>${entityName}</b><br/><br/><b>Latitude:</b> ${' + 196 '<b>${entityName}</b><br/><br/><b>Latitude:</b> ${' +
174 settings.latKeyName + ':7}<br/><b>Longitude:</b> ${' + settings.lngKeyName + ':7}', 197 settings.latKeyName + ':7}<br/><b>Longitude:</b> ${' + settings.lngKeyName + ':7}',
175 defaultCenterPosition: getDefCenterPosition(settings?.defaultCenterPosition), 198 defaultCenterPosition: getDefCenterPosition(settings?.defaultCenterPosition),
176 - currentImage: (settings.useMarkerImage && settings.markerImage?.length) ? { 199 + currentImage: (settings.markerImage?.length) ? {
177 url: settings.markerImage, 200 url: settings.markerImage,
178 size: settings.markerImageSize || 34 201 size: settings.markerImageSize || 34
179 } : null 202 } : null
@@ -253,7 +276,7 @@ export const defaultSettings: any = { @@ -253,7 +276,7 @@ export const defaultSettings: any = {
253 useDefaultCenterPosition: false, 276 useDefaultCenterPosition: false,
254 showTooltipAction: 'click', 277 showTooltipAction: 'click',
255 autocloseTooltip: false, 278 autocloseTooltip: false,
256 - showPolygon: true, 279 + showPolygon: false,
257 labelColor: '#000000', 280 labelColor: '#000000',
258 color: '#FE7569', 281 color: '#FE7569',
259 polygonColor: '#0000ff', 282 polygonColor: '#0000ff',
@@ -267,10 +290,16 @@ export const defaultSettings: any = { @@ -267,10 +290,16 @@ export const defaultSettings: any = {
267 strokeOpacity: 1.0, 290 strokeOpacity: 1.0,
268 initCallback: () => { }, 291 initCallback: () => { },
269 defaultZoomLevel: 8, 292 defaultZoomLevel: 8,
270 - dontFitMapBounds: false,  
271 disableScrollZooming: false, 293 disableScrollZooming: false,
272 minZoomLevel: 16, 294 minZoomLevel: 16,
273 credentials: '', 295 credentials: '',
274 markerClusteringSetting: null, 296 markerClusteringSetting: null,
275 - draggableMarker: false 297 + draggableMarker: false,
  298 + fitMapBounds: true
276 }; 299 };
  300 +
  301 +export const hereProviders = [
  302 + 'HERE.normalDay',
  303 + 'HERE.normalNight',
  304 + 'HERE.hybridDay',
  305 + 'HERE.terrainDay']
@@ -17,34 +17,32 @@ @@ -17,34 +17,32 @@
17 import L from 'leaflet'; 17 import L from 'leaflet';
18 import _ from 'lodash'; 18 import _ from 'lodash';
19 import { MarkerSettings, PolylineSettings, PolygonSettings } from './map-models'; 19 import { MarkerSettings, PolylineSettings, PolygonSettings } from './map-models';
  20 +import { parseWithTranslation } from '@app/core/utils';
20 21
21 export function createTooltip(target: L.Layer, 22 export function createTooltip(target: L.Layer,
22 settings: MarkerSettings | PolylineSettings | PolygonSettings, 23 settings: MarkerSettings | PolylineSettings | PolygonSettings,
23 content?: string | HTMLElement): L.Popup { 24 content?: string | HTMLElement): L.Popup {
24 const popup = L.popup(); 25 const popup = L.popup();
25 popup.setContent(content); 26 popup.setContent(content);
26 - console.log(settings);  
27 -  
28 target.bindPopup(popup, { autoClose: settings.autocloseTooltip, closeOnClick: false }); 27 target.bindPopup(popup, { autoClose: settings.autocloseTooltip, closeOnClick: false });
29 - target.on('popupopen', () => {  
30 - let actions = document.getElementsByClassName('tb-custom-action');  
31 - Array.from(actions).forEach(  
32 - (element: HTMLElement) => {  
33 - if (element && settings.tooltipActions[element.id]) {  
34 - console.log(settings.tooltipActions[element.id]);  
35 - element.addEventListener('click', settings.tooltipActions[element.id])  
36 - }  
37 - })  
38 - })  
39 - if (settings.displayTooltipAction === 'hover') { 28 + if (settings.showTooltipAction === 'hover') {
40 target.off('click'); 29 target.off('click');
41 target.on('mouseover', function () { 30 target.on('mouseover', function () {
42 - this.openPopup(); 31 + target.openPopup();
43 }); 32 });
44 target.on('mouseout', function () { 33 target.on('mouseout', function () {
45 - this.closePopup(); 34 + target.closePopup();
46 }); 35 });
47 } 36 }
  37 + target.on('popupopen', () => {
  38 + const actions = document.getElementsByClassName('tb-custom-action');
  39 + Array.from(actions).forEach(
  40 + (element: HTMLElement) => {
  41 + if (element && settings.tooltipAction[element.id]) {
  42 + element.addEventListener('click', settings.tooltipAction[element.id])
  43 + }
  44 + });
  45 + });
48 return popup; 46 return popup;
49 } 47 }
50 48
@@ -29,3 +29,7 @@ @@ -29,3 +29,7 @@
29 background: none; 29 background: none;
30 box-shadow: none; 30 box-shadow: none;
31 } 31 }
  32 +
  33 +.leaflet-container{
  34 + background-color: white;
  35 +}
@@ -14,13 +14,13 @@ @@ -14,13 +14,13 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import L from 'leaflet';  
18 -import { MarkerSettings, FormattedData } from './map-models';  
19 -import { aspectCache, safeExecute, parseTemplate } from '@app/core/utils'; 17 +import { aspectCache, parseWithTranslation, safeExecute } from '@app/core/utils';
  18 +import L, { LeafletMouseEvent } from 'leaflet';
  19 +import { FormattedData, MarkerSettings } from './map-models';
20 import { createTooltip } from './maps-utils'; 20 import { createTooltip } from './maps-utils';
  21 +import tinycolor from 'tinycolor2';
21 22
22 export class Marker { 23 export class Marker {
23 -  
24 leafletMarker: L.Marker; 24 leafletMarker: L.Marker;
25 tooltipOffset: [number, number]; 25 tooltipOffset: [number, number];
26 tooltip: L.Popup; 26 tooltip: L.Popup;
@@ -28,8 +28,8 @@ export class Marker { @@ -28,8 +28,8 @@ export class Marker {
28 data: FormattedData; 28 data: FormattedData;
29 dataSources: FormattedData[]; 29 dataSources: FormattedData[];
30 30
31 - constructor(private map: L.Map, location: L.LatLngExpression, public settings: MarkerSettings,  
32 - data?, dataSources?, onClickListener?, onDragendListener?) { 31 + constructor(location: L.LatLngExpression, public settings: MarkerSettings,
  32 + data?: FormattedData, dataSources?, onDragendListener?) {
33 this.setDataSources(data, dataSources); 33 this.setDataSources(data, dataSources);
34 this.leafletMarker = L.marker(location, { 34 this.leafletMarker = L.marker(location, {
35 draggable: settings.draggableMarker 35 draggable: settings.draggableMarker
@@ -39,15 +39,21 @@ export class Marker { @@ -39,15 +39,21 @@ export class Marker {
39 this.leafletMarker.setIcon(iconInfo.icon); 39 this.leafletMarker.setIcon(iconInfo.icon);
40 this.tooltipOffset = [0, -iconInfo.size[1] + 10]; 40 this.tooltipOffset = [0, -iconInfo.size[1] + 10];
41 this.updateMarkerLabel(settings); 41 this.updateMarkerLabel(settings);
42 - this.leafletMarker.addTo(map);  
43 }); 42 });
44 43
45 if (settings.showTooltip) { 44 if (settings.showTooltip) {
46 - this.tooltip = createTooltip(this.leafletMarker, settings, parseTemplate(this.settings.tooltipPattern, data)); 45 + this.tooltip = createTooltip(this.leafletMarker, settings);
  46 + this.updateMarkerTooltip(data);
47 } 47 }
48 48
49 - if (onClickListener) {  
50 - this.leafletMarker.on('click', onClickListener); 49 + if (this.settings.markerClick) {
  50 + this.leafletMarker.on('click', (event: LeafletMouseEvent) => {
  51 + for (const action in this.settings.markerClick) {
  52 + if (typeof (this.settings.markerClick[action]) === 'function') {
  53 + this.settings.markerClick[action](event);
  54 + }
  55 + }
  56 + });
51 } 57 }
52 58
53 if (onDragendListener) { 59 if (onDragendListener) {
@@ -61,7 +67,9 @@ export class Marker { @@ -61,7 +67,9 @@ export class Marker {
61 } 67 }
62 68
63 updateMarkerTooltip(data: FormattedData) { 69 updateMarkerTooltip(data: FormattedData) {
64 - this.tooltip.setContent(parseTemplate(this.settings.tooltipPattern, data)); 70 + const pattern = this.settings.useTooltipFunction ?
  71 + safeExecute(this.settings.tooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) : this.settings.tooltipPattern;
  72 + this.tooltip.setContent(parseWithTranslation.parseTemplate(pattern, data, true));
65 } 73 }
66 74
67 updateMarkerPosition(position: L.LatLngExpression) { 75 updateMarkerPosition(position: L.LatLngExpression) {
@@ -70,13 +78,10 @@ export class Marker { @@ -70,13 +78,10 @@ export class Marker {
70 78
71 updateMarkerLabel(settings: MarkerSettings) { 79 updateMarkerLabel(settings: MarkerSettings) {
72 this.leafletMarker.unbindTooltip(); 80 this.leafletMarker.unbindTooltip();
73 -  
74 if (settings.showLabel) { 81 if (settings.showLabel) {
75 - if (settings.useLabelFunction) {  
76 - settings.labelText = parseTemplate(  
77 - safeExecute(settings.labelFunction, [this.data, this.dataSources, this.data.dsIndex]), this.data)  
78 - }  
79 - else settings.labelText = parseTemplate(settings.label, this.data); 82 + const pattern = settings.useLabelFunction ?
  83 + safeExecute(settings.labelFunction, [this.data, this.dataSources, this.data.dsIndex]) : settings.label;
  84 + settings.labelText = parseWithTranslation.parseTemplate(pattern, this.data, true);
80 this.leafletMarker.bindTooltip(`<div style="color: ${settings.labelColor};"><b>${settings.labelText}</b></div>`, 85 this.leafletMarker.bindTooltip(`<div style="color: ${settings.labelColor};"><b>${settings.labelText}</b></div>`,
81 { className: 'tb-marker-label', permanent: true, direction: 'top', offset: this.tooltipOffset }); 86 { className: 'tb-marker-label', permanent: true, direction: 'top', offset: this.tooltipOffset });
82 } 87 }
@@ -97,7 +102,6 @@ export class Marker { @@ -97,7 +102,6 @@ export class Marker {
97 } 102 }
98 103
99 createMarkerIcon(onMarkerIconReady) { 104 createMarkerIcon(onMarkerIconReady) {
100 -  
101 if (this.settings.icon) { 105 if (this.settings.icon) {
102 onMarkerIconReady({ 106 onMarkerIconReady({
103 size: [30, 30], 107 size: [30, 30],
@@ -105,11 +109,11 @@ export class Marker { @@ -105,11 +109,11 @@ export class Marker {
105 }); 109 });
106 return; 110 return;
107 } 111 }
108 -  
109 const currentImage = this.settings.useMarkerImageFunction ? 112 const currentImage = this.settings.useMarkerImageFunction ?
110 safeExecute(this.settings.markerImageFunction, 113 safeExecute(this.settings.markerImageFunction,
111 [this.data, this.settings.markerImages, this.dataSources, this.data.dsIndex]) : this.settings.currentImage; 114 [this.data, this.settings.markerImages, this.dataSources, this.data.dsIndex]) : this.settings.currentImage;
112 - 115 + const currentColor = tinycolor(this.settings.useColorFunction ? safeExecute(this.settings.colorFunction,
  116 + [this.data, this.dataSources, this.data.dsIndex]) : this.settings.color).toHex();
113 117
114 if (currentImage && currentImage.url) { 118 if (currentImage && currentImage.url) {
115 aspectCache(currentImage.url).subscribe( 119 aspectCache(currentImage.url).subscribe(
@@ -136,19 +140,19 @@ export class Marker { @@ -136,19 +140,19 @@ export class Marker {
136 }; 140 };
137 onMarkerIconReady(iconInfo); 141 onMarkerIconReady(iconInfo);
138 } else { 142 } else {
139 - this.createDefaultMarkerIcon(this.settings.color, onMarkerIconReady); 143 + this.createDefaultMarkerIcon(currentColor, onMarkerIconReady);
140 } 144 }
141 } 145 }
142 ); 146 );
143 } else { 147 } else {
144 - this.createDefaultMarkerIcon(this.settings.color, onMarkerIconReady); 148 + this.createDefaultMarkerIcon(currentColor, onMarkerIconReady);
145 } 149 }
146 } 150 }
147 151
148 createDefaultMarkerIcon(color, onMarkerIconReady) { 152 createDefaultMarkerIcon(color, onMarkerIconReady) {
149 const pinColor = color.substr(1); 153 const pinColor = color.substr(1);
150 const icon = L.icon({ 154 const icon = L.icon({
151 - iconUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + pinColor, 155 + iconUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + color,
152 iconSize: [21, 34], 156 iconSize: [21, 34],
153 iconAnchor: [10, 34], 157 iconAnchor: [10, 34],
154 popupAnchor: [0, -34], 158 popupAnchor: [0, -34],
@@ -20,14 +20,9 @@ import { MapSettings, UnitedMapSettings } from '../map-models'; @@ -20,14 +20,9 @@ import { MapSettings, UnitedMapSettings } from '../map-models';
20 20
21 export class HEREMap extends LeafletMap { 21 export class HEREMap extends LeafletMap {
22 constructor($container, options: UnitedMapSettings) { 22 constructor($container, options: UnitedMapSettings) {
23 - const defaultCredentials =  
24 - {  
25 - app_id: 'AhM6TzD9ThyK78CT3ptx',  
26 - app_code: 'p6NPiITB3Vv0GMUFnkLOOg'  
27 - }  
28 super($container, options); 23 super($container, options);
29 const map = L.map($container).setView(options?.defaultCenterPosition, options?.defaultZoomLevel); 24 const map = L.map($container).setView(options?.defaultCenterPosition, options?.defaultZoomLevel);
30 - const tileLayer = (L.tileLayer as any).provider(options.mapProvider || 'HERE.normalDay', options.credentials || defaultCredentials); 25 + const tileLayer = (L.tileLayer as any).provider(options.mapProviderHere || 'HERE.normalDay', options.credentials);
31 tileLayer.addTo(map); 26 tileLayer.addTo(map);
32 super.setMap(map); 27 super.setMap(map);
33 super.initSettings(options); 28 super.initSettings(options);
@@ -110,7 +110,7 @@ export const hereMapSettingsSchema = @@ -110,7 +110,7 @@ export const hereMapSettingsSchema =
110 title: 'HERE Map Configuration', 110 title: 'HERE Map Configuration',
111 type: 'object', 111 type: 'object',
112 properties: { 112 properties: {
113 - mapProvider: { 113 + mapProviderHere: {
114 title: 'Map layer', 114 title: 'Map layer',
115 type: 'string', 115 type: 'string',
116 default: 'HERE.normalDay' 116 default: 'HERE.normalDay'
@@ -120,11 +120,13 @@ export const hereMapSettingsSchema = @@ -120,11 +120,13 @@ export const hereMapSettingsSchema =
120 properties: { 120 properties: {
121 app_id: { 121 app_id: {
122 title: 'HERE app id', 122 title: 'HERE app id',
123 - type: 'string' 123 + type: 'string',
  124 + default: 'AhM6TzD9ThyK78CT3ptx'
124 }, 125 },
125 app_code: { 126 app_code: {
126 title: 'HERE app code', 127 title: 'HERE app code',
127 - type: 'string' 128 + type: 'string',
  129 + default: 'p6NPiITB3Vv0GMUFnkLOOg'
128 } 130 }
129 }, 131 },
130 required: ['app_id', 'app_code'] 132 required: ['app_id', 'app_code']
@@ -134,7 +136,7 @@ export const hereMapSettingsSchema = @@ -134,7 +136,7 @@ export const hereMapSettingsSchema =
134 }, 136 },
135 form: [ 137 form: [
136 { 138 {
137 - key: 'mapProvider', 139 + key: 'mapProviderHere',
138 type: 'rc-select', 140 type: 'rc-select',
139 multiple: false, 141 multiple: false,
140 items: [ 142 items: [
@@ -339,43 +341,6 @@ export const commonMapSettingsSchema = @@ -339,43 +341,6 @@ export const commonMapSettingsSchema =
339 type: 'boolean', 341 type: 'boolean',
340 default: false 342 default: false
341 }, 343 },
342 - polygonKeyName: {  
343 - title: 'Polygon key name',  
344 - type: 'string',  
345 - default: 'coordinates'  
346 - },  
347 - polygonColor: {  
348 - title: 'Polygon color',  
349 - type: 'string'  
350 - },  
351 - polygonOpacity: {  
352 - title: 'Polygon opacity',  
353 - type: 'number',  
354 - default: 0.5  
355 - },  
356 - polygonStrokeColor: {  
357 - title: 'Stroke color',  
358 - type: 'string'  
359 - },  
360 - polygonStrokeOpacity: {  
361 - title: 'Stroke opacity',  
362 - type: 'number',  
363 - default: 1  
364 - },  
365 - polygonStrokeWeight: {  
366 - title: 'Stroke weight',  
367 - type: 'number',  
368 - default: 1  
369 - },  
370 - usePolygonColorFunction: {  
371 - title: 'Use polygon color function',  
372 - type: 'boolean',  
373 - default: false  
374 - },  
375 - polygonColorFunction: {  
376 - title: 'Polygon Color function: f(data, dsData, dsIndex)',  
377 - type: 'string'  
378 - },  
379 markerImage: { 344 markerImage: {
380 title: 'Custom marker image', 345 title: 'Custom marker image',
381 type: 'string' 346 type: 'string'
@@ -455,20 +420,6 @@ export const commonMapSettingsSchema = @@ -455,20 +420,6 @@ export const commonMapSettingsSchema =
455 { 420 {
456 key: 'colorFunction', 421 key: 'colorFunction',
457 type: 'javascript' 422 type: 'javascript'
458 - }, 'showPolygon', 'polygonKeyName',  
459 - {  
460 - key: 'polygonColor',  
461 - type: 'color'  
462 - },  
463 - 'polygonOpacity',  
464 - {  
465 - key: 'polygonStrokeColor',  
466 - type: 'color'  
467 - },  
468 - 'polygonStrokeOpacity', 'polygonStrokeWeight', 'usePolygonColorFunction',  
469 - {  
470 - key: 'polygonColorFunction',  
471 - type: 'javascript'  
472 }, 423 },
473 { 424 {
474 key: 'markerImage', 425 key: 'markerImage',
@@ -488,7 +439,73 @@ export const commonMapSettingsSchema = @@ -488,7 +439,73 @@ export const commonMapSettingsSchema =
488 type: 'image' 439 type: 'image'
489 } 440 }
490 ] 441 ]
491 - } 442 + },
  443 + 'showPolygon',
  444 + ]
  445 +};
  446 +
  447 +export const mapPolygonSchema =
  448 +{
  449 + schema: {
  450 + title: 'Map Polygon Configuration',
  451 + type: 'object',
  452 + properties: {
  453 + polygonKeyName: {
  454 + title: 'Polygon key name',
  455 + type: 'string',
  456 + default: 'coordinates'
  457 + },
  458 + polygonColor: {
  459 + title: 'Polygon color',
  460 + type: 'string'
  461 + },
  462 + polygonOpacity: {
  463 + title: 'Polygon opacity',
  464 + type: 'number',
  465 + default: 0.5
  466 + },
  467 + polygonStrokeColor: {
  468 + title: 'Stroke color',
  469 + type: 'string'
  470 + },
  471 + polygonStrokeOpacity: {
  472 + title: 'Stroke opacity',
  473 + type: 'number',
  474 + default: 1
  475 + },
  476 + polygonStrokeWeight: {
  477 + title: 'Stroke weight',
  478 + type: 'number',
  479 + default: 1
  480 + },
  481 + usePolygonColorFunction: {
  482 + title: 'Use polygon color function',
  483 + type: 'boolean',
  484 + default: false
  485 + },
  486 + polygonColorFunction: {
  487 + title: 'Polygon Color function: f(data, dsData, dsIndex)',
  488 + type: 'string'
  489 + },
  490 + },
  491 + required: []
  492 + },
  493 + form: [
  494 + 'polygonKeyName',
  495 + {
  496 + key: 'polygonColor',
  497 + type: 'color'
  498 + },
  499 + 'polygonOpacity',
  500 + {
  501 + key: 'polygonStrokeColor',
  502 + type: 'color'
  503 + },
  504 + 'polygonStrokeOpacity', 'polygonStrokeWeight', 'usePolygonColorFunction',
  505 + {
  506 + key: 'polygonColorFunction',
  507 + type: 'javascript'
  508 + },
492 ] 509 ]
493 }; 510 };
494 511
@@ -527,23 +544,12 @@ export const markerClusteringSettingsSchema = @@ -527,23 +544,12 @@ export const markerClusteringSettingsSchema =
527 title: 'Use map markers clustering', 544 title: 'Use map markers clustering',
528 type: 'boolean', 545 type: 'boolean',
529 default: false 546 default: false
530 - },  
531 - zoomOnClick: {  
532 - title: 'Zoom when clicking on a cluster',  
533 - type: 'boolean',  
534 - default: true  
535 - },  
536 - maxZoom: {  
537 - title: 'The maximum zoom level when a marker can be part of a cluster (0 - 18)',  
538 - type: 'number'  
539 } 547 }
540 }, 548 },
541 required: [] 549 required: []
542 }, 550 },
543 form: [ 551 form: [
544 'useClusterMarkers', 552 'useClusterMarkers',
545 - 'zoomOnClick',  
546 - 'maxZoom'  
547 ] 553 ]
548 }; 554 };
549 555
@@ -571,12 +577,23 @@ export const markerClusteringSettingsSchemaGoogle = @@ -571,12 +577,23 @@ export const markerClusteringSettingsSchemaGoogle =
571 ] 577 ]
572 }; 578 };
573 579
  580 +
  581 +
574 export const markerClusteringSettingsSchemaLeaflet = 582 export const markerClusteringSettingsSchemaLeaflet =
575 { 583 {
576 schema: { 584 schema: {
577 title: 'Markers Clustering Configuration Leaflet', 585 title: 'Markers Clustering Configuration Leaflet',
578 type: 'object', 586 type: 'object',
579 properties: { 587 properties: {
  588 + zoomOnClick: {
  589 + title: 'Zoom when clicking on a cluster',
  590 + type: 'boolean',
  591 + default: true
  592 + },
  593 + maxZoom: {
  594 + title: 'The maximum zoom level when a marker can be part of a cluster (0 - 18)',
  595 + type: 'number'
  596 + },
580 showCoverageOnHover: { 597 showCoverageOnHover: {
581 title: 'Show the bounds of markers when mouse over a cluster', 598 title: 'Show the bounds of markers when mouse over a cluster',
582 type: 'boolean', 599 type: 'boolean',
@@ -606,6 +623,8 @@ export const markerClusteringSettingsSchemaLeaflet = @@ -606,6 +623,8 @@ export const markerClusteringSettingsSchemaLeaflet =
606 required: [] 623 required: []
607 }, 624 },
608 form: [ 625 form: [
  626 + 'zoomOnClick',
  627 + 'maxZoom',
609 'showCoverageOnHover', 628 'showCoverageOnHover',
610 'animate', 629 'animate',
611 'maxClusterRadius', 630 'maxClusterRadius',
@@ -17,17 +17,17 @@ @@ -17,17 +17,17 @@
17 --> 17 -->
18 <div class="trip-animation-widget"> 18 <div class="trip-animation-widget">
19 <div class="trip-animation-label-container" *ngIf="settings.showLabel"> 19 <div class="trip-animation-label-container" *ngIf="settings.showLabel">
20 - {{settings.label | tbParseTemplate: activeTrip}} 20 + {{label }}
21 </div> 21 </div>
22 - <div class="trip-animation-container" layout="column"> 22 + <div class="trip-animation-container" fxLayout="column">
23 <div class="map" #map></div> 23 <div class="map" #map></div>
24 - <div class="trip-animation-info-panel" layout="row"> 24 + <div class="trip-animation-info-panel" fxLayout="row">
25 <button class="tooltip-button" mat-mini-fab color="primary" aria-label="tooltip" 25 <button class="tooltip-button" mat-mini-fab color="primary" aria-label="tooltip"
26 - *ngIf="settings.showTooltip" (click)="showHideTooltip()"> 26 + *ngIf="settings.showTooltip" (click)="visibleTooltip = !visibleTooltip">
27 <mat-icon>info_outline</mat-icon> 27 <mat-icon>info_outline</mat-icon>
28 </button> 28 </button>
29 </div> 29 </div>
30 - <div class="trip-animation-tooltip md-whiteframe-z4" layout="column" 30 + <div class="trip-animation-tooltip md-whiteframe-z4" fxLayout="column"
31 [ngClass]="{'trip-animation-tooltip-hidden':!visibleTooltip}" [innerHTML]="mainTooltip" 31 [ngClass]="{'trip-animation-tooltip-hidden':!visibleTooltip}" [innerHTML]="mainTooltip"
32 [ngStyle]="{'background-color': settings.tooltipColor, 'opacity': settings.tooltipOpacity, 'color': settings.tooltipFontColor}"> 32 [ngStyle]="{'background-color': settings.tooltipColor, 'opacity': settings.tooltipOpacity, 'color': settings.tooltipFontColor}">
33 </div> 33 </div>
@@ -74,10 +74,10 @@ @@ -74,10 +74,10 @@
74 74
75 .trip-animation-tooltip { 75 .trip-animation-tooltip {
76 position: absolute; 76 position: absolute;
77 - top: 38px; 77 + top: 30px;
78 right: 0; 78 right: 0;
79 - z-index: 400;  
80 - padding: 10px; 79 + z-index: 1000;
  80 + padding: 5px;
81 background-color: #fff; 81 background-color: #fff;
82 transition: 0.3s ease-in-out; 82 transition: 0.3s ease-in-out;
83 83
@@ -86,4 +86,4 @@ @@ -86,4 +86,4 @@
86 } 86 }
87 } 87 }
88 } 88 }
89 -} 89 +}
@@ -22,13 +22,14 @@ import { interpolateOnPointSegment } from 'leaflet-geometryutil'; @@ -22,13 +22,14 @@ import { interpolateOnPointSegment } from 'leaflet-geometryutil';
22 import { Component, OnInit, Input, ViewChild, AfterViewInit, ChangeDetectorRef, SecurityContext } from '@angular/core'; 22 import { Component, OnInit, Input, ViewChild, AfterViewInit, ChangeDetectorRef, SecurityContext } from '@angular/core';
23 import { MapWidgetController, TbMapWidgetV2 } from '../lib/maps/map-widget2'; 23 import { MapWidgetController, TbMapWidgetV2 } from '../lib/maps/map-widget2';
24 import { MapProviders } from '../lib/maps/map-models'; 24 import { MapProviders } from '../lib/maps/map-models';
25 -import { parseArray, parseTemplate, safeExecute } from '@app/core/utils'; 25 +import { parseArray, parseWithTranslation, safeExecute, parseTemplate } from '@app/core/utils';
26 import { initSchema, addToSchema, addGroupInfo } from '@app/core/schema-utils'; 26 import { initSchema, addToSchema, addGroupInfo } from '@app/core/schema-utils';
27 import { tripAnimationSchema } from '../lib/maps/schemes'; 27 import { tripAnimationSchema } from '../lib/maps/schemes';
28 import { DomSanitizer } from '@angular/platform-browser'; 28 import { DomSanitizer } from '@angular/platform-browser';
29 import { WidgetContext } from '@app/modules/home/models/widget-component.models'; 29 import { WidgetContext } from '@app/modules/home/models/widget-component.models';
30 import { getRatio, findAngle } from '../lib/maps/maps-utils'; 30 import { getRatio, findAngle } from '../lib/maps/maps-utils';
31 import { JsonSettingsSchema, WidgetConfig } from '@shared/models/widget.models'; 31 import { JsonSettingsSchema, WidgetConfig } from '@shared/models/widget.models';
  32 +import moment from 'moment';
32 33
33 34
34 @Component({ 35 @Component({
@@ -55,6 +56,9 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -55,6 +56,9 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
55 mainTooltip = ''; 56 mainTooltip = '';
56 visibleTooltip = false; 57 visibleTooltip = false;
57 activeTrip; 58 activeTrip;
  59 + label;
  60 + minTime;
  61 + maxTime;
58 62
59 static getSettingsSchema(): JsonSettingsSchema { 63 static getSettingsSchema(): JsonSettingsSchema {
60 const schema = initSchema(); 64 const schema = initSchema();
@@ -90,48 +94,58 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -90,48 +94,58 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
90 94
91 ngAfterViewInit() { 95 ngAfterViewInit() {
92 const ctxCopy: WidgetContext = _.cloneDeep(this.ctx); 96 const ctxCopy: WidgetContext = _.cloneDeep(this.ctx);
93 - ctxCopy.settings.showLabel = false;  
94 - ctxCopy.settings.showTooltip = false;  
95 this.mapWidget = new MapWidgetController(MapProviders.openstreet, false, ctxCopy, this.mapContainer.nativeElement); 97 this.mapWidget = new MapWidgetController(MapProviders.openstreet, false, ctxCopy, this.mapContainer.nativeElement);
96 } 98 }
97 99
98 timeUpdated(time: number) { 100 timeUpdated(time: number) {
99 const currentPosition = this.interpolatedData.map(dataSource => dataSource[time]); 101 const currentPosition = this.interpolatedData.map(dataSource => dataSource[time]);
100 this.activeTrip = currentPosition[0]; 102 this.activeTrip = currentPosition[0];
101 - if (this.settings.showPolygon) {  
102 - this.mapWidget.map.updatePolygons(this.interpolatedData); 103 + this.minTime = moment(this.historicalData[0][this.historicalData.length - 1]?.time).format('YYYY-MM-DD HH:mm:ss')
  104 + this.maxTime = moment(this.historicalData[0][0]?.time).format('YYYY-MM-DD HH:mm:ss')
  105 + this.calcLabel();
  106 + this.calcTooltip();
  107 + if (this.mapWidget) {
  108 + if (this.settings.showPolygon) {
  109 + this.mapWidget.map.updatePolygons(this.interpolatedData);
  110 + }
  111 + this.mapWidget.map.updateMarkers(currentPosition);
103 } 112 }
104 - this.mapWidget.map.updateMarkers(currentPosition);  
105 } 113 }
106 114
107 setActiveTrip() { 115 setActiveTrip() {
108 -  
109 } 116 }
110 117
111 calculateIntervals() { 118 calculateIntervals() {
112 this.historicalData.forEach((dataSource, index) => { 119 this.historicalData.forEach((dataSource, index) => {
113 this.intervals = []; 120 this.intervals = [];
  121 +
114 for (let time = dataSource[0]?.time; time < dataSource[dataSource.length - 1]?.time; time += this.normalizationStep) { 122 for (let time = dataSource[0]?.time; time < dataSource[dataSource.length - 1]?.time; time += this.normalizationStep) {
115 this.intervals.push(time); 123 this.intervals.push(time);
116 } 124 }
  125 +
117 this.intervals.push(dataSource[dataSource.length - 1]?.time); 126 this.intervals.push(dataSource[dataSource.length - 1]?.time);
118 this.interpolatedData[index] = this.interpolateArray(dataSource, this.intervals); 127 this.interpolatedData[index] = this.interpolateArray(dataSource, this.intervals);
119 }); 128 });
  129 +
120 } 130 }
121 131
122 - showHideTooltip() { 132 + calcTooltip() {
  133 + const data = { ...this.activeTrip, maxTime: this.maxTime, minTime: this.minTime }
123 const tooltipText: string = this.settings.useTooltipFunction ? 134 const tooltipText: string = this.settings.useTooltipFunction ?
124 - safeExecute(this.settings.tooolTipFunction, [this.activeTrip, this.historicalData, 0])  
125 - : this.settings.tooltipPattern; 135 + safeExecute(this.settings.tooolTipFunction, [data, this.historicalData, 0]) : this.settings.tooltipPattern;
  136 + this.mainTooltip = this.sanitizer.sanitize(
  137 + SecurityContext.HTML, (parseWithTranslation.parseTemplate(tooltipText, data, true)));
  138 + }
126 139
127 - this.mainTooltip = this.sanitizer.sanitize(SecurityContext.HTML, parseTemplate(tooltipText, this.activeTrip))  
128 - this.visibleTooltip = !this.visibleTooltip; 140 + calcLabel() {
  141 + const data = { ...this.activeTrip, maxTime: this.maxTime, minTime: this.minTime }
  142 + const labelText: string = this.settings.useLabelFunction ?
  143 + safeExecute(this.settings.labelFunction, [data, this.historicalData, 0]) : this.settings.label;
  144 + this.label = (parseWithTranslation.parseTemplate(labelText, data, true));
129 } 145 }
130 146
131 interpolateArray(originData, interpolatedIntervals) { 147 interpolateArray(originData, interpolatedIntervals) {
132 -  
133 const result = {}; 148 const result = {};
134 -  
135 for (let i = 1, j = 0; i < originData.length && j < interpolatedIntervals.length;) { 149 for (let i = 1, j = 0; i < originData.length && j < interpolatedIntervals.length;) {
136 const currentTime = interpolatedIntervals[j]; 150 const currentTime = interpolatedIntervals[j];
137 while (originData[i].time < currentTime) i++; 151 while (originData[i].time < currentTime) i++;
@@ -16,15 +16,21 @@ @@ -16,15 +16,21 @@
16 16
17 --> 17 -->
18 <div class="trip-animation-control-panel"> 18 <div class="trip-animation-control-panel">
19 - <div> 19 + <div fxFlex fxLayout="row" fxFlexAlign="center">
20 <button mat-icon-button class="mat-icon-button" aria-label="Start" (click)="moveStart()"> 20 <button mat-icon-button class="mat-icon-button" aria-label="Start" (click)="moveStart()">
21 <mat-icon class="material-icons" [ngStyle]="{'color': settings.buttonColor}">fast_rewind</mat-icon> 21 <mat-icon class="material-icons" [ngStyle]="{'color': settings.buttonColor}">fast_rewind</mat-icon>
22 </button> 22 </button>
23 <button mat-icon-button class="mat-icon-button" aria-label="Previous" (click)="movePrev()"> 23 <button mat-icon-button class="mat-icon-button" aria-label="Previous" (click)="movePrev()">
24 <mat-icon class="material-icons" [ngStyle]="{'color': settings.buttonColor}">skip_previous</mat-icon> 24 <mat-icon class="material-icons" [ngStyle]="{'color': settings.buttonColor}">skip_previous</mat-icon>
25 </button> 25 </button>
26 - <mat-slider [(ngModel)]="index" [min]="minTimeIndex" [max]="maxTimeIndex" (change)="changeIndex()">  
27 - </mat-slider> 26 + <div fxLayout="column" fxFlex="100">
  27 + <mat-slider [(ngModel)]="index" [min]="minTimeIndex" [max]="maxTimeIndex" (change)="changeIndex()">
  28 + </mat-slider>
  29 + <div class="panel-timer">
  30 + <span *ngIf="this.intervals[this.index]">{{ this.intervals[this.index] | date:'medium'}}</span>
  31 + <span *ngIf="!this.intervals[this.index]">{{ "widget.no-data-found" | translate}}</span>
  32 + </div>
  33 + </div>
28 <button mat-icon-button class="mat-icon-button" aria-label="Next" (click)="moveNext()"> 34 <button mat-icon-button class="mat-icon-button" aria-label="Next" (click)="moveNext()">
29 <mat-icon class="material-icons" [ngStyle]="{'color': settings.buttonColor}">skip_next</mat-icon> 35 <mat-icon class="material-icons" [ngStyle]="{'color': settings.buttonColor}">skip_next</mat-icon>
30 </button> 36 </button>
@@ -41,11 +47,8 @@ @@ -41,11 +47,8 @@
41 pause_circle_outline 47 pause_circle_outline
42 </mat-icon> 48 </mat-icon>
43 </button> 49 </button>
44 - <mat-select matInput [(ngModel)]="speed" (selectionChange)="reeneble()" class="speed-select" aria-label="Speed selector">  
45 - <mat-option [value]="speedValue" flex="1" *ngFor="let speedValue of speeds">{{speedValue}} </mat-option> 50 + <mat-select matInput [(ngModel)]="speed" (selectionChange)="reeneble()" class="speed-select"
  51 + aria-label="Speed selector">
  52 + <mat-option [value]="speedValue" *ngFor="let speedValue of speeds">{{speedValue}} </mat-option>
46 </mat-select> 53 </mat-select>
47 - </div>  
48 - <div class="panel-timer">  
49 - <span *ngIf="this.intervals[this.index]">{{ this.intervals[this.index] | date:'medium'}}</span>  
50 - <span *ngIf="!this.intervals[this.index]">{{ "widget.no-data-found" | translate}}</span>  
51 - </div>  
  54 + </div>
@@ -13,128 +13,120 @@ @@ -13,128 +13,120 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -.trip-animation-widget { 16 +
  17 +.trip-animation-label-container {
  18 + height: 24px;
  19 +}
  20 +
  21 +.trip-animation-container {
17 position: relative; 22 position: relative;
  23 + z-index: 1;
  24 + flex: 1;
18 width: 100%; 25 width: 100%;
19 - height: 100%;  
20 - font-size: 16px;  
21 - line-height: 24px;  
22 26
23 - .trip-animation-label-container {  
24 - height: 24px;  
25 - }  
26 -  
27 - .trip-animation-container {  
28 - position: relative; 27 + #trip-animation-map {
29 z-index: 1; 28 z-index: 1;
30 - flex: 1;  
31 width: 100%; 29 width: 100%;
  30 + height: 100%;
32 31
33 - #trip-animation-map {  
34 - z-index: 1;  
35 - width: 100%;  
36 - height: 100%;  
37 -  
38 - .pointsLayerMarkerIcon {  
39 - border-radius: 50%;  
40 - } 32 + .pointsLayerMarkerIcon {
  33 + border-radius: 50%;
41 } 34 }
  35 + }
  36 +
  37 + .trip-animation-info-panel {
  38 + position: absolute;
  39 + top: 0;
  40 + right: 0;
  41 + z-index: 2;
  42 + pointer-events: none;
42 43
43 - .trip-animation-info-panel {  
44 - position: absolute; 44 + .mat-button {
45 top: 0; 45 top: 0;
46 - right: 0;  
47 - z-index: 2;  
48 - pointer-events: none;  
49 -  
50 - .md-button {  
51 - top: 0;  
52 - left: 0;  
53 - width: 32px;  
54 - min-width: 32px;  
55 - height: 32px;  
56 - min-height: 32px;  
57 - padding: 0 0 2px;  
58 - margin: 2px;  
59 - line-height: 24px;  
60 -  
61 - ng-md-icon {  
62 - width: 24px;  
63 - height: 24px;  
64 -  
65 - svg {  
66 - width: inherit;  
67 - height: inherit;  
68 - } 46 + left: 0;
  47 + width: 32px;
  48 + min-width: 32px;
  49 + height: 32px;
  50 + min-height: 32px;
  51 + padding: 0 0 2px;
  52 + margin: 2px;
  53 + line-height: 24px;
  54 +
  55 + ng-mat-icon {
  56 + width: 24px;
  57 + height: 24px;
  58 +
  59 + svg {
  60 + width: inherit;
  61 + height: inherit;
69 } 62 }
70 } 63 }
71 } 64 }
  65 + }
72 66
73 - .trip-animation-tooltip {  
74 - position: absolute;  
75 - top: 38px;  
76 - right: 0;  
77 - z-index: 2;  
78 - padding: 10px;  
79 - background-color: #fff;  
80 - transition: 0.3s ease-in-out;  
81 -  
82 - &-hidden {  
83 - transform: translateX(110%);  
84 - } 67 + .trip-animation-tooltip {
  68 + position: absolute;
  69 + top: 38px;
  70 + right: 0;
  71 + z-index: 2;
  72 + padding: 10px;
  73 + background-color: #fff;
  74 + transition: 0.3s ease-in-out;
  75 +
  76 + &-hidden {
  77 + transform: translateX(110%);
85 } 78 }
86 } 79 }
  80 +}
87 81
88 - .trip-animation-control-panel {  
89 - position: relative;  
90 - box-sizing: border-box;  
91 - width: 100%;  
92 - padding-bottom: 16px;  
93 - padding-left: 10px; 82 +.trip-animation-control-panel {
  83 + position: relative;
  84 + box-sizing: border-box;
  85 + width: 100%;
  86 + padding-bottom: 16px;
  87 + padding-left: 10px;
94 88
95 - md-slider-container {  
96 - md-slider {  
97 - min-width: 80px;  
98 - } 89 + mat-slider-container {
  90 + mat-slider {
  91 + min-width: 80px;
  92 + }
99 93
100 - button.md-button.md-icon-button {  
101 - width: 44px;  
102 - min-width: 44px;  
103 - height: 44px;  
104 - min-height: 44px;  
105 - margin: 0;  
106 - line-height: 28px;  
107 -  
108 - md-icon {  
109 - width: 28px;  
110 - height: 28px;  
111 - font-size: 28px;  
112 -  
113 - svg {  
114 - width: inherit;  
115 - height: inherit;  
116 - } 94 + button.mat-button.mat-icon-button {
  95 + width: 44px;
  96 + min-width: 44px;
  97 + height: 44px;
  98 + min-height: 44px;
  99 + margin: 0;
  100 + line-height: 28px;
  101 +
  102 + mat-icon {
  103 + width: 28px;
  104 + height: 28px;
  105 + font-size: 28px;
  106 +
  107 + svg {
  108 + width: inherit;
  109 + height: inherit;
117 } 110 }
118 } 111 }
119 -  
120 - md-select {  
121 - margin: 0;  
122 - }  
123 } 112 }
124 113
125 - .panel-timer {  
126 - max-width: none;  
127 - padding-right: 250px;  
128 - padding-left: 90px;  
129 - margin-top: -20px;  
130 - font-size: 12px;  
131 - font-weight: 500;  
132 - text-align: center; 114 + mat-select {
  115 + margin: 0;
133 } 116 }
134 } 117 }
  118 +
  119 + .panel-timer {
  120 + max-width: none;
  121 + margin-top: -20px;
  122 + font-size: 12px;
  123 + font-weight: 500;
  124 + text-align: center;
  125 + }
135 } 126 }
136 127
137 -.speed-select{ 128 +.speed-select {
138 width: 50px; 129 width: 50px;
139 - margin-left: 20px; 130 + margin-left: 10px;
  131 + margin-top: 10px;
140 } 132 }
@@ -15,8 +15,8 @@ @@ -15,8 +15,8 @@
15 /// 15 ///
16 16
17 import { Component, OnInit, OnChanges, Input, Output, EventEmitter, ChangeDetectorRef } from '@angular/core'; 17 import { Component, OnInit, OnChanges, Input, Output, EventEmitter, ChangeDetectorRef } from '@angular/core';
18 -import { interval, Subscription, Subscriber, SubscriptionLike, Observer } from 'rxjs';  
19 -import { filter, tap } from 'rxjs/operators'; 18 +import { interval } from 'rxjs';
  19 +import { filter } from 'rxjs/operators';
20 import { HistorySelectSettings } from '@app/modules/home/components/widget/lib/maps/map-models'; 20 import { HistorySelectSettings } from '@app/modules/home/components/widget/lib/maps/map-models';
21 21
22 @Component({ 22 @Component({
@@ -15,11 +15,11 @@ @@ -15,11 +15,11 @@
15 /// 15 ///
16 16
17 import { Pipe, PipeTransform } from '@angular/core'; 17 import { Pipe, PipeTransform } from '@angular/core';
18 -import { parseTemplate } from '@app/core/utils'; 18 +import { parseTemplate, parseWithTranslation } from '@app/core/utils';
19 19
20 @Pipe({ name: 'tbParseTemplate' }) 20 @Pipe({ name: 'tbParseTemplate' })
21 export class TbTemplatePipe implements PipeTransform { 21 export class TbTemplatePipe implements PipeTransform {
22 transform(template, data): string { 22 transform(template, data): string {
23 - return parseTemplate(template, data); 23 + return parseWithTranslation.parseTemplate(template, data);
24 } 24 }
25 } 25 }