Commit 8979a511a3c939cfaec51d7a7b7b8d9397770d50

Authored by Adsumus
1 parent 8460358f

map fixes

... ... @@ -39,7 +39,9 @@
39 39 "node_modules/rc-select/assets/index.less",
40 40 "node_modules/jstree-bootstrap-theme/dist/themes/proton/style.min.css",
41 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 46 "stylePreprocessorOptions": {
45 47 "includePaths": [
... ...
... ... @@ -1811,8 +1811,7 @@
1811 1811 "@types/geojson": {
1812 1812 "version": "7946.0.7",
1813 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 1816 "@types/glob": {
1818 1817 "version": "7.1.1",
... ... @@ -1876,7 +1875,6 @@
1876 1875 "version": "1.5.12",
1877 1876 "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.5.12.tgz",
1878 1877 "integrity": "sha512-61HRMIng+bWvnnAIqUWLBlrd/TQZc4gU+gN1JL4K47EDtwIrcMEhWgi7PdcpbG1YmpH4F0EfOimkvV82gJIl9w==",
1879   - "dev": true,
1880 1878 "requires": {
1881 1879 "@types/geojson": "*"
1882 1880 }
... ... @@ -1890,6 +1888,14 @@
1890 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 1899 "@types/lodash": {
1894 1900 "version": "4.14.150",
1895 1901 "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.150.tgz",
... ... @@ -8605,7 +8611,7 @@
8605 8611 "integrity": "sha512-4O3GWAYJaauMCILm07weko2rHA8a4kjn7+8Lg4s1d7SxwS/3IpkVD/GljbRrIJ1c1W/XGJ3GbuK7RyYZEJChhw=="
8606 8612 },
8607 8613 "ngx-flowchart": {
8608   - "version": "git://github.com/thingsboard/ngx-flowchart.git#a4157b0eef2eb3646ef920447c7b06b39d54f87f",
  8614 + "version": "git://github.com/thingsboard/ngx-flowchart.git#97a77477ca8579becf0e3a07866046b4536fe30a",
8609 8615 "from": "git://github.com/thingsboard/ngx-flowchart.git#master",
8610 8616 "requires": {
8611 8617 "tslib": "^1.10.0"
... ...
... ... @@ -108,6 +108,7 @@
108 108 "@types/jstree": "^3.3.39",
109 109 "@types/jszip": "^3.1.7",
110 110 "@types/leaflet": "^1.5.12",
  111 + "@types/leaflet.markercluster": "^1.4.2",
111 112 "@types/leaflet-polylinedecorator": "^1.6.0",
112 113 "@types/lodash": "^4.14.150",
113 114 "@types/raphael": "^2.3.0",
... ...
... ... @@ -15,7 +15,7 @@
15 15 ///
16 16
17 17 import _ from 'lodash';
18   -import { Observable, Subject, from, fromEvent, of } from 'rxjs';
  18 +import { Observable, Subject, fromEvent, of } from 'rxjs';
19 19 import { finalize, share, map } from 'rxjs/operators';
20 20 import base64js from 'base64-js';
21 21
... ... @@ -430,7 +430,7 @@ export function getDescendantProp(obj: any, path: string): any {
430 430
431 431 export function imageLoader(imageUrl: string): Observable<HTMLImageElement> {
432 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 434 image.src = imageUrl;
435 435 return imageLoad$;
436 436 }
... ... @@ -524,37 +524,49 @@ export function parseFunction(source: any, params: string[] = []): Function {
524 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 528 let res = '';
  529 + let variables = '';
529 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 552 catch (ex) {
  553 + console.log(ex, variables, template)
554 554 }
555 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 570 export function padValue(val: any, dec: number): string {
559 571 let strVal;
560 572 let n;
... ...
... ... @@ -14,12 +14,12 @@
14 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 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 24 import { MapSettings, MarkerSettings, FormattedData, UnitedMapSettings, PolygonSettings, PolylineSettings } from './map-models';
25 25 import { Marker } from './markers';
... ... @@ -34,7 +34,7 @@ export default abstract class LeafletMap {
34 34 markers: Map<string, Marker> = new Map();
35 35 polylines: Map<string, Polyline> = new Map();
36 36 polygons: Map<string, Polygon> = new Map();
37   - dragMode = true;
  37 + dragMode = false;
38 38 map: L.Map;
39 39 map$: BehaviorSubject<L.Map> = new BehaviorSubject(null);
40 40 ready$: Observable<L.Map> = this.map$.pipe(filter(map => !!map));
... ... @@ -43,6 +43,7 @@ export default abstract class LeafletMap {
43 43 bounds: L.LatLngBounds;
44 44 newMarker: L.Marker;
45 45 datasources: FormattedData[];
  46 + markersCluster: LM.markerClusterGroup;
46 47
47 48 constructor(public $container: HTMLElement, options: UnitedMapSettings) {
48 49 this.options = options;
... ... @@ -50,13 +51,38 @@ export default abstract class LeafletMap {
50 51
51 52 public initSettings(options: MapSettings) {
52 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 63 if (disableScrollZooming) {
55 64 this.map.scrollWheelZoom.disable();
56 65 }
57 66 if (initCallback) {
58 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 88 addMarkerControl() {
... ... @@ -80,7 +106,7 @@ export default abstract class LeafletMap {
80 106 this.saveMarkerLocation(updatedEnttity);
81 107 this.map.removeLayer(newMarker);
82 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 111 datasourcesList.append(dsItem);
86 112 });
... ... @@ -103,7 +129,7 @@ export default abstract class LeafletMap {
103 129 img.src = `assets/add_location.svg`;
104 130 img.style.width = '32px';
105 131 img.style.height = '32px';
106   - img.title = "Drag and drop to add marker";
  132 + img.title = 'Drag and drop to add marker';
107 133 img.onclick = this.dragMarker;
108 134 img.draggable = true;
109 135 const draggableImg = new L.Draggable(img);
... ... @@ -115,11 +141,9 @@ export default abstract class LeafletMap {
115 141 },
116 142 dragMarker: this.dragMarker
117 143 } as any);
118   -
119 144 L.control.addMarker = (opts) => {
120 145 return new L.Control.AddMarker(opts);
121 146 }
122   -
123 147 addMarker = L.control.addMarker({ position: 'topright' }).addTo(this.map);
124 148 }
125 149 }
... ... @@ -171,9 +195,9 @@ export default abstract class LeafletMap {
171 195 return this.map.getCenter();
172 196 }
173 197
174   - fitBounds(bounds, useDefaultZoom = false) {
  198 + fitBounds(bounds: LatLngBounds, useDefaultZoom = false, padding?: LatLngTuple) {
175 199 if (bounds.isValid()) {
176   - if ((this.options.dontFitMapBounds || useDefaultZoom) && this.options.defaultZoomLevel) {
  200 + if ((!this.options.fitMapBounds || useDefaultZoom) && this.options.defaultZoomLevel) {
177 201 this.map.setZoom(this.options.defaultZoomLevel, { animate: false });
178 202 this.map.panTo(bounds.getCenter(), { animate: false });
179 203 } else {
... ... @@ -182,7 +206,7 @@ export default abstract class LeafletMap {
182 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 211 this.bounds = this.bounds.extend(bounds);
188 212 }
... ... @@ -232,11 +256,17 @@ export default abstract class LeafletMap {
232 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 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 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 279 if (settings.showTooltip) {
250 280 marker.updateMarkerTooltip(data);
251 281 }
  282 + if (settings.useClusterMarkers)
  283 + this.markersCluster.refreshClusters()
252 284 marker.setDataSources(data, dataSources);
253 285 marker.updateMarkerIcon(settings);
254 286 }
... ... @@ -327,7 +359,9 @@ export default abstract class LeafletMap {
327 359
328 360 updatePolygon(key: string, data: LatLngTuple[], dataSources: DatasourceData[], settings: PolygonSettings) {
329 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 14 /// limitations under the License.
15 15 ///
16 16
17   -import { LatLngTuple } from 'leaflet';
  17 +import { LatLngTuple, LeafletMouseEvent } from 'leaflet';
18 18
19 19 export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string;
20 20 export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string;
... ... @@ -24,14 +24,15 @@ export type MapSettings = {
24 24 draggableMarker: boolean;
25 25 initCallback?: () => any;
26 26 defaultZoomLevel?: number;
27   - dontFitMapBounds?: boolean;
28 27 disableScrollZooming?: boolean;
29 28 minZoomLevel?: number;
  29 + useClusterMarkers: boolean;
30 30 latKeyName?: string;
31 31 lngKeyName?: string;
32 32 xPosKeyName?: string;
33 33 yPosKeyName?: string;
34 34 mapProvider: MapProviders;
  35 + mapProviderHere: string;
35 36 mapUrl?: string;
36 37 mapImageUrl?: string;
37 38 provider?: MapProviders;
... ... @@ -42,6 +43,13 @@ export type MapSettings = {
42 43 gmDefaultMapType?: string;
43 44 useLabelFunction: string;
44 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 55 export enum MapProviders {
... ... @@ -54,7 +62,7 @@ export enum MapProviders {
54 62
55 63 export type MarkerSettings = {
56 64 tooltipPattern?: any;
57   - tooltipActions: object;
  65 + tooltipAction: { [name: string]: actionsHandler };
58 66 icon?: any;
59 67 showLabel?: boolean;
60 68 label: string;
... ... @@ -63,19 +71,19 @@ export type MarkerSettings = {
63 71 useLabelFunction: boolean;
64 72 draggableMarker: boolean;
65 73 showTooltip?: boolean;
  74 + useTooltipFunction: boolean;
  75 + useColorFunction: boolean;
66 76 color?: string;
67 77 autocloseTooltip: boolean;
68   - displayTooltipAction: string;
  78 + showTooltipAction: string;
  79 + useClusterMarkers: boolean;
69 80 currentImage?: string;
70 81 useMarkerImageFunction?: boolean;
71 82 markerImages?: string[];
72   - useMarkerImage: boolean;
73 83 markerImageSize: number;
74 84 fitMapBounds: boolean;
75   - markerImage: {
76   - length: number
77   - }
78   -
  85 + markerImage: string;
  86 + markerClick: { [name: string]: actionsHandler };
79 87 colorFunction: GenericFunction;
80 88 tooltipFunction: GenericFunction;
81 89 labelFunction: GenericFunction;
... ... @@ -99,18 +107,18 @@ export type PolygonSettings = {
99 107 polygonStrokeColor: string;
100 108 polygonColor: string;
101 109 autocloseTooltip: boolean;
102   - displayTooltipAction: string;
103   - tooltipActions: object;
104   -
  110 + showTooltipAction: string;
  111 + tooltipAction: object;
  112 + polygonClick: { [name: string]: actionsHandler };
105 113 polygonColorFunction?: GenericFunction;
106 114 }
107 115
108 116 export type PolylineSettings = {
109 117 usePolylineDecorator: any;
110 118 autocloseTooltip: boolean;
111   - displayTooltipAction: string;
  119 + showTooltipAction: string;
112 120 useColorFunction: any;
113   - tooltipActions: object;
  121 + tooltipAction: { [name: string]: actionsHandler };
114 122 color: string;
115 123 useStrokeOpacityFunction: any;
116 124 strokeOpacity: number;
... ... @@ -134,4 +142,6 @@ export interface HistorySelectSettings {
134 142 buttonColor: string;
135 143 }
136 144
137   -export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolylineSettings;
\ No newline at end of file
  145 +export type actionsHandler = ($event: Event | LeafletMouseEvent) => void;
  146 +
  147 +export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolylineSettings;
... ...
... ... @@ -15,6 +15,7 @@
15 15 ///
16 16
17 17 import { JsonSettingsSchema } from '@shared/models/widget.models';
  18 +import { MapProviders } from './map-models';
18 19
19 20 export interface MapWidgetInterface {
20 21 resize(),
... ... @@ -24,8 +25,8 @@ export interface MapWidgetInterface {
24 25 }
25 26
26 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 30 dataKeySettingsSchema(): object;
30 31 actionSources(): object;
31 32 }
... ...
... ... @@ -26,11 +26,12 @@ import {
26 26 markerClusteringSettingsSchema,
27 27 markerClusteringSettingsSchemaLeaflet,
28 28 hereMapSettingsSchema,
29   - mapProviderSchema
  29 + mapProviderSchema,
  30 + mapPolygonSchema
30 31 } from './schemes';
31 32 import { MapWidgetStaticInterface, MapWidgetInterface } from './map-widget.interface';
32 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 35 import { initSchema, addToSchema, mergeSchemes, addCondition, addGroupInfo } from '@core/schema-utils';
35 36 import { forkJoin } from 'rxjs';
36 37 import { WidgetContext } from '@app/modules/home/models/widget-component.models';
... ... @@ -40,12 +41,13 @@ import { EntityId } from '@shared/models/id/entity-id';
40 41 import { AttributeScope } from '@shared/models/telemetry/telemetry.models';
41 42 import { AttributeService } from '@core/http/attribute.service';
42 43 import { Type } from '@angular/core';
  44 +import { TranslateService } from '@ngx-translate/core';
  45 +import { UtilsService } from '@app/core/public-api';
43 46
44 47 // @dynamic
45 48 export class MapWidgetController implements MapWidgetInterface {
46 49
47 50 constructor(public mapProvider: MapProviders, private drawRoutes: boolean, public ctx: WidgetContext, $element: HTMLElement) {
48   - console.log("MapWidgetController -> constructor -> ctx", ctx)
49 51 if (this.map) {
50 52 this.map.map.remove();
51 53 delete this.map;
... ... @@ -56,15 +58,16 @@ export class MapWidgetController implements MapWidgetInterface {
56 58 $element = ctx.$container[0];
57 59 }
58 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 66 const MapClass = providerSets[this.provider]?.MapClass;
65 67 if (!MapClass) {
66 68 return;
67 69 }
  70 + parseWithTranslation.setTranslate(this.translate);
68 71 this.map = new MapClass($element, this.settings);
69 72 this.map.saveMarkerLocation = this.setMarkerLocation;
70 73 }
... ... @@ -74,13 +77,13 @@ export class MapWidgetController implements MapWidgetInterface {
74 77 schema: JsonSettingsSchema;
75 78 data;
76 79 settings: UnitedMapSettings;
77   - actions: Map<string, Map<string, (widgetContext: WidgetContext) => void>>;
78 80
79 81 public static dataKeySettingsSchema(): object {
80 82 return {};
81 83 }
82 84
83   - public static getProvidersSchema() {
  85 + public static getProvidersSchema(mapProvider: MapProviders) {
  86 + mapProviderSchema.schema.properties.provider.default = mapProvider;
84 87 return mergeSchemes([mapProviderSchema,
85 88 ...Object.values(providerSets)?.map(
86 89 (setting: IProvider) => addCondition(setting?.schema, `model.provider === '${setting.name}'`))]);
... ... @@ -88,18 +91,20 @@ export class MapWidgetController implements MapWidgetInterface {
88 91
89 92 public static settingsSchema(mapProvider: MapProviders, drawRoutes: boolean): JsonSettingsSchema {
90 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 98 addGroupInfo(schema, 'Common Map Settings');
95 99 if (drawRoutes) {
96 100 addToSchema(schema, routeMapSettingsSchema);
97 101 addGroupInfo(schema, 'Route Map Settings');
98 102 } else if (mapProvider !== 'image-map') {
99   - const clusteringSchema = mergeSchemes([markerClusteringSettingsSchemaLeaflet, markerClusteringSettingsSchema])
  103 + const clusteringSchema = mergeSchemes([markerClusteringSettingsSchema,
  104 + addCondition(markerClusteringSettingsSchemaLeaflet, `model.useClusterMarkers === true`)])
100 105 addToSchema(schema, clusteringSchema);
101 106 addGroupInfo(schema, 'Markers Clustering Settings');
102   - }
  107 + }}
103 108 return schema;
104 109 }
105 110
... ... @@ -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 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 150 const entityInfo = this.ctx.actionsApi.getActiveEntityInfo();
132 151 const entityId = entityInfo ? entityInfo.entityId : null;
133 152 const entityName = entityInfo ? entityInfo.entityName : null;
... ... @@ -153,13 +172,17 @@ export class MapWidgetController implements MapWidgetInterface {
153 172 }]
154 173 );
155 174 })).subscribe(res => {
156   - console.log('MapWidgetController -> setMarkerLocation -> res', res)
157 175 });
158 176 }
159 177
160 178 initSettings(settings: UnitedMapSettings): UnitedMapSettings {
161 179 const functionParams = ['data', 'dsData', 'dsIndex'];
162 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 186 const customOptions = {
164 187 provider: this.provider,
165 188 mapUrl: settings?.mapImageUrl,
... ... @@ -173,7 +196,7 @@ export class MapWidgetController implements MapWidgetInterface {
173 196 '<b>${entityName}</b><br/><br/><b>Latitude:</b> ${' +
174 197 settings.latKeyName + ':7}<br/><b>Longitude:</b> ${' + settings.lngKeyName + ':7}',
175 198 defaultCenterPosition: getDefCenterPosition(settings?.defaultCenterPosition),
176   - currentImage: (settings.useMarkerImage && settings.markerImage?.length) ? {
  199 + currentImage: (settings.markerImage?.length) ? {
177 200 url: settings.markerImage,
178 201 size: settings.markerImageSize || 34
179 202 } : null
... ... @@ -253,7 +276,7 @@ export const defaultSettings: any = {
253 276 useDefaultCenterPosition: false,
254 277 showTooltipAction: 'click',
255 278 autocloseTooltip: false,
256   - showPolygon: true,
  279 + showPolygon: false,
257 280 labelColor: '#000000',
258 281 color: '#FE7569',
259 282 polygonColor: '#0000ff',
... ... @@ -267,10 +290,16 @@ export const defaultSettings: any = {
267 290 strokeOpacity: 1.0,
268 291 initCallback: () => { },
269 292 defaultZoomLevel: 8,
270   - dontFitMapBounds: false,
271 293 disableScrollZooming: false,
272 294 minZoomLevel: 16,
273 295 credentials: '',
274 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 17 import L from 'leaflet';
18 18 import _ from 'lodash';
19 19 import { MarkerSettings, PolylineSettings, PolygonSettings } from './map-models';
  20 +import { parseWithTranslation } from '@app/core/utils';
20 21
21 22 export function createTooltip(target: L.Layer,
22 23 settings: MarkerSettings | PolylineSettings | PolygonSettings,
23 24 content?: string | HTMLElement): L.Popup {
24 25 const popup = L.popup();
25 26 popup.setContent(content);
26   - console.log(settings);
27   -
28 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 29 target.off('click');
41 30 target.on('mouseover', function () {
42   - this.openPopup();
  31 + target.openPopup();
43 32 });
44 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 46 return popup;
49 47 }
50 48
... ...
... ... @@ -29,3 +29,7 @@
29 29 background: none;
30 30 box-shadow: none;
31 31 }
  32 +
  33 +.leaflet-container{
  34 + background-color: white;
  35 +}
... ...
... ... @@ -14,13 +14,13 @@
14 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 20 import { createTooltip } from './maps-utils';
  21 +import tinycolor from 'tinycolor2';
21 22
22 23 export class Marker {
23   -
24 24 leafletMarker: L.Marker;
25 25 tooltipOffset: [number, number];
26 26 tooltip: L.Popup;
... ... @@ -28,8 +28,8 @@ export class Marker {
28 28 data: FormattedData;
29 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 33 this.setDataSources(data, dataSources);
34 34 this.leafletMarker = L.marker(location, {
35 35 draggable: settings.draggableMarker
... ... @@ -39,15 +39,21 @@ export class Marker {
39 39 this.leafletMarker.setIcon(iconInfo.icon);
40 40 this.tooltipOffset = [0, -iconInfo.size[1] + 10];
41 41 this.updateMarkerLabel(settings);
42   - this.leafletMarker.addTo(map);
43 42 });
44 43
45 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 59 if (onDragendListener) {
... ... @@ -61,7 +67,9 @@ export class Marker {
61 67 }
62 68
63 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 75 updateMarkerPosition(position: L.LatLngExpression) {
... ... @@ -70,13 +78,10 @@ export class Marker {
70 78
71 79 updateMarkerLabel(settings: MarkerSettings) {
72 80 this.leafletMarker.unbindTooltip();
73   -
74 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 85 this.leafletMarker.bindTooltip(`<div style="color: ${settings.labelColor};"><b>${settings.labelText}</b></div>`,
81 86 { className: 'tb-marker-label', permanent: true, direction: 'top', offset: this.tooltipOffset });
82 87 }
... ... @@ -97,7 +102,6 @@ export class Marker {
97 102 }
98 103
99 104 createMarkerIcon(onMarkerIconReady) {
100   -
101 105 if (this.settings.icon) {
102 106 onMarkerIconReady({
103 107 size: [30, 30],
... ... @@ -105,11 +109,11 @@ export class Marker {
105 109 });
106 110 return;
107 111 }
108   -
109 112 const currentImage = this.settings.useMarkerImageFunction ?
110 113 safeExecute(this.settings.markerImageFunction,
111 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 118 if (currentImage && currentImage.url) {
115 119 aspectCache(currentImage.url).subscribe(
... ... @@ -136,19 +140,19 @@ export class Marker {
136 140 };
137 141 onMarkerIconReady(iconInfo);
138 142 } else {
139   - this.createDefaultMarkerIcon(this.settings.color, onMarkerIconReady);
  143 + this.createDefaultMarkerIcon(currentColor, onMarkerIconReady);
140 144 }
141 145 }
142 146 );
143 147 } else {
144   - this.createDefaultMarkerIcon(this.settings.color, onMarkerIconReady);
  148 + this.createDefaultMarkerIcon(currentColor, onMarkerIconReady);
145 149 }
146 150 }
147 151
148 152 createDefaultMarkerIcon(color, onMarkerIconReady) {
149 153 const pinColor = color.substr(1);
150 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 156 iconSize: [21, 34],
153 157 iconAnchor: [10, 34],
154 158 popupAnchor: [0, -34],
... ...
... ... @@ -20,14 +20,9 @@ import { MapSettings, UnitedMapSettings } from '../map-models';
20 20
21 21 export class HEREMap extends LeafletMap {
22 22 constructor($container, options: UnitedMapSettings) {
23   - const defaultCredentials =
24   - {
25   - app_id: 'AhM6TzD9ThyK78CT3ptx',
26   - app_code: 'p6NPiITB3Vv0GMUFnkLOOg'
27   - }
28 23 super($container, options);
29 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 26 tileLayer.addTo(map);
32 27 super.setMap(map);
33 28 super.initSettings(options);
... ...
... ... @@ -110,7 +110,7 @@ export const hereMapSettingsSchema =
110 110 title: 'HERE Map Configuration',
111 111 type: 'object',
112 112 properties: {
113   - mapProvider: {
  113 + mapProviderHere: {
114 114 title: 'Map layer',
115 115 type: 'string',
116 116 default: 'HERE.normalDay'
... ... @@ -120,11 +120,13 @@ export const hereMapSettingsSchema =
120 120 properties: {
121 121 app_id: {
122 122 title: 'HERE app id',
123   - type: 'string'
  123 + type: 'string',
  124 + default: 'AhM6TzD9ThyK78CT3ptx'
124 125 },
125 126 app_code: {
126 127 title: 'HERE app code',
127   - type: 'string'
  128 + type: 'string',
  129 + default: 'p6NPiITB3Vv0GMUFnkLOOg'
128 130 }
129 131 },
130 132 required: ['app_id', 'app_code']
... ... @@ -134,7 +136,7 @@ export const hereMapSettingsSchema =
134 136 },
135 137 form: [
136 138 {
137   - key: 'mapProvider',
  139 + key: 'mapProviderHere',
138 140 type: 'rc-select',
139 141 multiple: false,
140 142 items: [
... ... @@ -339,43 +341,6 @@ export const commonMapSettingsSchema =
339 341 type: 'boolean',
340 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 344 markerImage: {
380 345 title: 'Custom marker image',
381 346 type: 'string'
... ... @@ -455,20 +420,6 @@ export const commonMapSettingsSchema =
455 420 {
456 421 key: 'colorFunction',
457 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 425 key: 'markerImage',
... ... @@ -488,7 +439,73 @@ export const commonMapSettingsSchema =
488 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 544 title: 'Use map markers clustering',
528 545 type: 'boolean',
529 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 549 required: []
542 550 },
543 551 form: [
544 552 'useClusterMarkers',
545   - 'zoomOnClick',
546   - 'maxZoom'
547 553 ]
548 554 };
549 555
... ... @@ -571,12 +577,23 @@ export const markerClusteringSettingsSchemaGoogle =
571 577 ]
572 578 };
573 579
  580 +
  581 +
574 582 export const markerClusteringSettingsSchemaLeaflet =
575 583 {
576 584 schema: {
577 585 title: 'Markers Clustering Configuration Leaflet',
578 586 type: 'object',
579 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 597 showCoverageOnHover: {
581 598 title: 'Show the bounds of markers when mouse over a cluster',
582 599 type: 'boolean',
... ... @@ -606,6 +623,8 @@ export const markerClusteringSettingsSchemaLeaflet =
606 623 required: []
607 624 },
608 625 form: [
  626 + 'zoomOnClick',
  627 + 'maxZoom',
609 628 'showCoverageOnHover',
610 629 'animate',
611 630 'maxClusterRadius',
... ...
... ... @@ -17,17 +17,17 @@
17 17 -->
18 18 <div class="trip-animation-widget">
19 19 <div class="trip-animation-label-container" *ngIf="settings.showLabel">
20   - {{settings.label | tbParseTemplate: activeTrip}}
  20 + {{label }}
21 21 </div>
22   - <div class="trip-animation-container" layout="column">
  22 + <div class="trip-animation-container" fxLayout="column">
23 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 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 27 <mat-icon>info_outline</mat-icon>
28 28 </button>
29 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 31 [ngClass]="{'trip-animation-tooltip-hidden':!visibleTooltip}" [innerHTML]="mainTooltip"
32 32 [ngStyle]="{'background-color': settings.tooltipColor, 'opacity': settings.tooltipOpacity, 'color': settings.tooltipFontColor}">
33 33 </div>
... ...
... ... @@ -74,10 +74,10 @@
74 74
75 75 .trip-animation-tooltip {
76 76 position: absolute;
77   - top: 38px;
  77 + top: 30px;
78 78 right: 0;
79   - z-index: 400;
80   - padding: 10px;
  79 + z-index: 1000;
  80 + padding: 5px;
81 81 background-color: #fff;
82 82 transition: 0.3s ease-in-out;
83 83
... ... @@ -86,4 +86,4 @@
86 86 }
87 87 }
88 88 }
89   -}
  89 +}
\ No newline at end of file
... ...
... ... @@ -22,13 +22,14 @@ import { interpolateOnPointSegment } from 'leaflet-geometryutil';
22 22 import { Component, OnInit, Input, ViewChild, AfterViewInit, ChangeDetectorRef, SecurityContext } from '@angular/core';
23 23 import { MapWidgetController, TbMapWidgetV2 } from '../lib/maps/map-widget2';
24 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 26 import { initSchema, addToSchema, addGroupInfo } from '@app/core/schema-utils';
27 27 import { tripAnimationSchema } from '../lib/maps/schemes';
28 28 import { DomSanitizer } from '@angular/platform-browser';
29 29 import { WidgetContext } from '@app/modules/home/models/widget-component.models';
30 30 import { getRatio, findAngle } from '../lib/maps/maps-utils';
31 31 import { JsonSettingsSchema, WidgetConfig } from '@shared/models/widget.models';
  32 +import moment from 'moment';
32 33
33 34
34 35 @Component({
... ... @@ -55,6 +56,9 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
55 56 mainTooltip = '';
56 57 visibleTooltip = false;
57 58 activeTrip;
  59 + label;
  60 + minTime;
  61 + maxTime;
58 62
59 63 static getSettingsSchema(): JsonSettingsSchema {
60 64 const schema = initSchema();
... ... @@ -90,48 +94,58 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
90 94
91 95 ngAfterViewInit() {
92 96 const ctxCopy: WidgetContext = _.cloneDeep(this.ctx);
93   - ctxCopy.settings.showLabel = false;
94   - ctxCopy.settings.showTooltip = false;
95 97 this.mapWidget = new MapWidgetController(MapProviders.openstreet, false, ctxCopy, this.mapContainer.nativeElement);
96 98 }
97 99
98 100 timeUpdated(time: number) {
99 101 const currentPosition = this.interpolatedData.map(dataSource => dataSource[time]);
100 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 115 setActiveTrip() {
108   -
109 116 }
110 117
111 118 calculateIntervals() {
112 119 this.historicalData.forEach((dataSource, index) => {
113 120 this.intervals = [];
  121 +
114 122 for (let time = dataSource[0]?.time; time < dataSource[dataSource.length - 1]?.time; time += this.normalizationStep) {
115 123 this.intervals.push(time);
116 124 }
  125 +
117 126 this.intervals.push(dataSource[dataSource.length - 1]?.time);
118 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 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 147 interpolateArray(originData, interpolatedIntervals) {
132   -
133 148 const result = {};
134   -
135 149 for (let i = 1, j = 0; i < originData.length && j < interpolatedIntervals.length;) {
136 150 const currentTime = interpolatedIntervals[j];
137 151 while (originData[i].time < currentTime) i++;
... ...
... ... @@ -16,15 +16,21 @@
16 16
17 17 -->
18 18 <div class="trip-animation-control-panel">
19   - <div>
  19 + <div fxFlex fxLayout="row" fxFlexAlign="center">
20 20 <button mat-icon-button class="mat-icon-button" aria-label="Start" (click)="moveStart()">
21 21 <mat-icon class="material-icons" [ngStyle]="{'color': settings.buttonColor}">fast_rewind</mat-icon>
22 22 </button>
23 23 <button mat-icon-button class="mat-icon-button" aria-label="Previous" (click)="movePrev()">
24 24 <mat-icon class="material-icons" [ngStyle]="{'color': settings.buttonColor}">skip_previous</mat-icon>
25 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 34 <button mat-icon-button class="mat-icon-button" aria-label="Next" (click)="moveNext()">
29 35 <mat-icon class="material-icons" [ngStyle]="{'color': settings.buttonColor}">skip_next</mat-icon>
30 36 </button>
... ... @@ -41,11 +47,8 @@
41 47 pause_circle_outline
42 48 </mat-icon>
43 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 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>
\ No newline at end of file
  54 + </div>
\ No newline at end of file
... ...
... ... @@ -13,128 +13,120 @@
13 13 * See the License for the specific language governing permissions and
14 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 22 position: relative;
  23 + z-index: 1;
  24 + flex: 1;
18 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 28 z-index: 1;
30   - flex: 1;
31 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 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 129 width: 50px;
139   - margin-left: 20px;
  130 + margin-left: 10px;
  131 + margin-top: 10px;
140 132 }
... ...
... ... @@ -15,8 +15,8 @@
15 15 ///
16 16
17 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 20 import { HistorySelectSettings } from '@app/modules/home/components/widget/lib/maps/map-models';
21 21
22 22 @Component({
... ...
... ... @@ -15,11 +15,11 @@
15 15 ///
16 16
17 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 20 @Pipe({ name: 'tbParseTemplate' })
21 21 export class TbTemplatePipe implements PipeTransform {
22 22 transform(template, data): string {
23   - return parseTemplate(template, data);
  23 + return parseWithTranslation.parseTemplate(template, data);
24 24 }
25 25 }
... ...