Commit 416d0458933548db86aed4cb5dcd2efbea13d54f

Authored by ArtemHalushko
Committed by GitHub
1 parent 7a87e6c1

Map/3.0 (#2738)

* WIP on trip-animation settings

* trip-animation points & anchors

* fixes

Co-authored-by: Adsumus <artemtv42@gmail.com>
Co-authored-by: Igor Kulikov <ikulikov@thingsboard.io>
@@ -14,18 +14,18 @@ @@ -14,18 +14,18 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import L, { LatLngBounds, LatLngTuple, markerClusterGroup, MarkerClusterGroupOptions } from 'leaflet'; 17 +import L, { LatLngBounds, LatLngTuple, markerClusterGroup, MarkerClusterGroupOptions, FeatureGroup, LayerGroup } from 'leaflet';
18 18
19 import 'leaflet-providers'; 19 import 'leaflet-providers';
20 import 'leaflet.markercluster/dist/leaflet.markercluster'; 20 import 'leaflet.markercluster/dist/leaflet.markercluster';
21 21
22 import { 22 import {
23 - FormattedData,  
24 - MapSettings,  
25 - MarkerSettings,  
26 - PolygonSettings,  
27 - PolylineSettings,  
28 - UnitedMapSettings 23 + FormattedData,
  24 + MapSettings,
  25 + MarkerSettings,
  26 + PolygonSettings,
  27 + PolylineSettings,
  28 + UnitedMapSettings
29 } from './map-models'; 29 } from './map-models';
30 import { Marker } from './markers'; 30 import { Marker } from './markers';
31 import { BehaviorSubject, Observable } from 'rxjs'; 31 import { BehaviorSubject, Observable } from 'rxjs';
@@ -33,7 +33,7 @@ import { filter } from 'rxjs/operators'; @@ -33,7 +33,7 @@ import { filter } from 'rxjs/operators';
33 import { Polyline } from './polyline'; 33 import { Polyline } from './polyline';
34 import { Polygon } from './polygon'; 34 import { Polygon } from './polygon';
35 import { DatasourceData } from '@app/shared/models/widget.models'; 35 import { DatasourceData } from '@app/shared/models/widget.models';
36 -import { safeExecute } from '@home/components/widget/lib/maps/maps-utils'; 36 +import { safeExecute, createTooltip } from '@home/components/widget/lib/maps/maps-utils';
37 37
38 export default abstract class LeafletMap { 38 export default abstract class LeafletMap {
39 39
@@ -47,6 +47,8 @@ export default abstract class LeafletMap { @@ -47,6 +47,8 @@ export default abstract class LeafletMap {
47 bounds: L.LatLngBounds; 47 bounds: L.LatLngBounds;
48 datasources: FormattedData[]; 48 datasources: FormattedData[];
49 markersCluster; 49 markersCluster;
  50 + points: FeatureGroup;
  51 + markersData = [];
50 52
51 protected constructor(public $container: HTMLElement, options: UnitedMapSettings) { 53 protected constructor(public $container: HTMLElement, options: UnitedMapSettings) {
52 this.options = options; 54 this.options = options;
@@ -157,9 +159,9 @@ export default abstract class LeafletMap { @@ -157,9 +159,9 @@ export default abstract class LeafletMap {
157 this.map = map; 159 this.map = map;
158 if (this.options.useDefaultCenterPosition) { 160 if (this.options.useDefaultCenterPosition) {
159 this.map.panTo(this.options.defaultCenterPosition); 161 this.map.panTo(this.options.defaultCenterPosition);
160 - this.bounds = map.getBounds(); 162 + this.bounds = map.getBounds();
161 } 163 }
162 - else this.bounds = new L.LatLngBounds(null, null); 164 + else this.bounds = new L.LatLngBounds(null, null);
163 if (this.options.draggableMarker) { 165 if (this.options.draggableMarker) {
164 this.addMarkerControl(); 166 this.addMarkerControl();
165 } 167 }
@@ -200,9 +202,9 @@ export default abstract class LeafletMap { @@ -200,9 +202,9 @@ export default abstract class LeafletMap {
200 return this.map.getCenter(); 202 return this.map.getCenter();
201 } 203 }
202 204
203 - fitBounds(bounds: LatLngBounds, useDefaultZoom = false, padding?: LatLngTuple) { 205 + fitBounds(bounds: LatLngBounds, padding?: LatLngTuple) {
204 if (bounds.isValid()) { 206 if (bounds.isValid()) {
205 - this.bounds = this.bounds.extend(bounds); 207 + this.bounds = !!this.bounds ? this.bounds.extend(bounds) : bounds;
206 if (!this.options.fitMapBounds && this.options.defaultZoomLevel) { 208 if (!this.options.fitMapBounds && this.options.defaultZoomLevel) {
207 this.map.setZoom(this.options.defaultZoomLevel, { animate: false }); 209 this.map.setZoom(this.options.defaultZoomLevel, { animate: false });
208 if (this.options.useDefaultCenterPosition) { 210 if (this.options.useDefaultCenterPosition) {
@@ -218,9 +220,9 @@ export default abstract class LeafletMap { @@ -218,9 +220,9 @@ export default abstract class LeafletMap {
218 } 220 }
219 }); 221 });
220 if (this.options.useDefaultCenterPosition) { 222 if (this.options.useDefaultCenterPosition) {
221 - bounds = bounds.extend(this.options.defaultCenterPosition); 223 + this.bounds = this.bounds.extend(this.options.defaultCenterPosition);
222 } 224 }
223 - this.map.fitBounds(bounds, { padding: padding || [50, 50], animate: false }); 225 + this.map.fitBounds(this.bounds, { padding: padding || [50, 50], animate: false });
224 } 226 }
225 } 227 }
226 } 228 }
@@ -252,11 +254,10 @@ export default abstract class LeafletMap { @@ -252,11 +254,10 @@ export default abstract class LeafletMap {
252 const style = currentImage ? 'background-image: url(' + currentImage.url + ');' : ''; 254 const style = currentImage ? 'background-image: url(' + currentImage.url + ');' : '';
253 this.options.icon = L.divIcon({ 255 this.options.icon = L.divIcon({
254 html: `<div class="arrow" 256 html: `<div class="arrow"
255 - style="transform: translate(-10px, -10px);  
256 - ${style}  
257 - rotate(${data.rotationAngle}deg);  
258 - "><div>`  
259 - }) 257 + style="transform: translate(-10px, -10px)
  258 + rotate(${data.rotationAngle}deg);
  259 + ${style}"><div>`
  260 + });
260 } 261 }
261 else { 262 else {
262 this.options.icon = null; 263 this.options.icon = null;
@@ -268,6 +269,7 @@ export default abstract class LeafletMap { @@ -268,6 +269,7 @@ export default abstract class LeafletMap {
268 this.createMarker(data.entityName, data, markersData, this.options as MarkerSettings); 269 this.createMarker(data.entityName, data, markersData, this.options as MarkerSettings);
269 } 270 }
270 }); 271 });
  272 + this.markersData = markersData;
271 } 273 }
272 274
273 dragMarker = (e, data?) => { 275 dragMarker = (e, data?) => {
@@ -278,7 +280,8 @@ export default abstract class LeafletMap { @@ -278,7 +280,8 @@ export default abstract class LeafletMap {
278 private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings) { 280 private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings) {
279 this.ready$.subscribe(() => { 281 this.ready$.subscribe(() => {
280 const newMarker = new Marker(this.convertPosition(data), settings, data, dataSources, this.dragMarker); 282 const newMarker = new Marker(this.convertPosition(data), settings, data, dataSources, this.dragMarker);
281 - this.fitBounds(this.bounds.extend(newMarker.leafletMarker.getLatLng()), settings.draggableMarker && this.markers.size < 2); 283 + if (this.bounds)
  284 + this.fitBounds(this.bounds.extend(newMarker.leafletMarker.getLatLng()));
282 this.markers.set(key, newMarker); 285 this.markers.set(key, newMarker);
283 if (this.options.useClusterMarkers) { 286 if (this.options.useClusterMarkers) {
284 this.markersCluster.addLayer(newMarker.leafletMarker); 287 this.markersCluster.addLayer(newMarker.leafletMarker);
@@ -313,6 +316,29 @@ export default abstract class LeafletMap { @@ -313,6 +316,29 @@ export default abstract class LeafletMap {
313 } 316 }
314 } 317 }
315 318
  319 + updatePoints(pointsData: FormattedData[], getTooltip: (point: FormattedData, setTooltip?: boolean) => string) {
  320 + this.map$.subscribe(map => {
  321 + if (this.points) {
  322 + map.removeLayer(this.points);
  323 + }
  324 + this.points = new FeatureGroup();
  325 + pointsData.filter(pdata => !!this.convertPosition(pdata)).forEach(data => {
  326 + const point = L.circleMarker(this.convertPosition(data), {
  327 + color: this.options.pointColor,
  328 + radius: this.options.pointSize
  329 + });
  330 + if (!this.options.pointTooltipOnRightPanel) {
  331 + point.on('click', () => getTooltip(data));
  332 + }
  333 + else {
  334 + createTooltip(point, this.options, pointsData, getTooltip(data, false));
  335 + }
  336 + this.points.addLayer(point);
  337 + });
  338 + map.addLayer(this.points);
  339 + });
  340 + }
  341 +
316 setImageAlias(alias: Observable<any>) { 342 setImageAlias(alias: Observable<any>) {
317 } 343 }
318 344
@@ -337,15 +363,17 @@ export default abstract class LeafletMap { @@ -337,15 +363,17 @@ export default abstract class LeafletMap {
337 this.ready$.subscribe(() => { 363 this.ready$.subscribe(() => {
338 const poly = new Polyline(this.map, 364 const poly = new Polyline(this.map,
339 data.map(el => this.convertPosition(el)).filter(el => !!el), data, dataSources, settings); 365 data.map(el => this.convertPosition(el)).filter(el => !!el), data, dataSources, settings);
340 - const bounds = this.bounds.extend(poly.leafletPoly.getBounds());  
341 - this.fitBounds(bounds)  
342 - this.polylines.set(data[0].entityName, poly) 366 + const bounds = poly.leafletPoly.getBounds();
  367 + this.fitBounds(bounds);
  368 + this.polylines.set(data[0].entityName, poly);
343 }); 369 });
344 } 370 }
345 371
346 updatePolyline(key: string, data: FormattedData[], dataSources: FormattedData[], settings: PolylineSettings) { 372 updatePolyline(key: string, data: FormattedData[], dataSources: FormattedData[], settings: PolylineSettings) {
347 this.ready$.subscribe(() => { 373 this.ready$.subscribe(() => {
348 - this.polylines.get(key).updatePolyline(settings, data.map(el => this.convertPosition(el)), dataSources); 374 + const poly = this.polylines.get(key);
  375 + poly.updatePolyline(settings, data.map(el => this.convertPosition(el)), dataSources);
  376 + const bounds = poly.leafletPoly.getBounds();
349 }); 377 });
350 } 378 }
351 379
@@ -370,7 +398,7 @@ export default abstract class LeafletMap { @@ -370,7 +398,7 @@ export default abstract class LeafletMap {
370 createPolygon(polyData: DatasourceData, dataSources: DatasourceData[], settings: PolygonSettings) { 398 createPolygon(polyData: DatasourceData, dataSources: DatasourceData[], settings: PolygonSettings) {
371 this.ready$.subscribe(() => { 399 this.ready$.subscribe(() => {
372 const polygon = new Polygon(this.map, polyData, dataSources, settings); 400 const polygon = new Polygon(this.map, polyData, dataSources, settings);
373 - const bounds = this.bounds.extend(polygon.leafletPoly.getBounds()); 401 + const bounds = polygon.leafletPoly.getBounds();
374 this.fitBounds(bounds); 402 this.fitBounds(bounds);
375 this.polygons.set(polyData.datasource.entityName, polygon); 403 this.polygons.set(polyData.datasource.entityName, polygon);
376 }); 404 });
@@ -380,7 +408,6 @@ export default abstract class LeafletMap { @@ -380,7 +408,6 @@ export default abstract class LeafletMap {
380 this.ready$.subscribe(() => { 408 this.ready$.subscribe(() => {
381 const poly = this.polygons.get(polyData.datasource.entityName); 409 const poly = this.polygons.get(polyData.datasource.entityName);
382 poly.updatePolygon(polyData.data, dataSources, settings); 410 poly.updatePolygon(polyData.data, dataSources, settings);
383 - this.fitBounds(poly.leafletPoly.getBounds());  
384 }); 411 });
385 } 412 }
386 } 413 }
@@ -15,13 +15,19 @@ @@ -15,13 +15,19 @@
15 /// 15 ///
16 16
17 import { LatLngTuple } from 'leaflet'; 17 import { LatLngTuple } from 'leaflet';
18 -import { Datasource } from '@app/shared/models/widget.models'; 18 +import { Datasource, JsonSettingsSchema } from '@app/shared/models/widget.models';
  19 +import { Type } from '@angular/core';
  20 +import LeafletMap from './leaflet-map';
  21 +import { OpenStreetMap, TencentMap, GoogleMap, HEREMap, ImageMap } from './providers';
  22 +import {
  23 + openstreetMapSettingsSchema, tencentMapSettingsSchema,
  24 + googleMapSettingsSchema, hereMapSettingsSchema, imageMapSettingsSchema
  25 +} from './schemes';
19 26
20 export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; 27 export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string;
21 export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; 28 export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string;
22 29
23 export type MapSettings = { 30 export type MapSettings = {
24 - polygonKeyName: any;  
25 draggableMarker: boolean; 31 draggableMarker: boolean;
26 initCallback?: () => any; 32 initCallback?: () => any;
27 posFunction: (origXPos, origYPos) => { x, y }; 33 posFunction: (origXPos, origYPos) => { x, y };
@@ -108,7 +114,8 @@ export interface FormattedData { @@ -108,7 +114,8 @@ export interface FormattedData {
108 114
109 export type PolygonSettings = { 115 export type PolygonSettings = {
110 showPolygon: boolean; 116 showPolygon: boolean;
111 - showTooltip: any; 117 + polygonKeyName: string;
  118 + polKeyName: string;// deprecated
112 polygonStrokeOpacity: number; 119 polygonStrokeOpacity: number;
113 polygonOpacity: number; 120 polygonOpacity: number;
114 polygonStrokeWeight: number; 121 polygonStrokeWeight: number;
@@ -116,12 +123,13 @@ export type PolygonSettings = { @@ -116,12 +123,13 @@ export type PolygonSettings = {
116 polygonColor: string; 123 polygonColor: string;
117 showPolygonTooltip: boolean; 124 showPolygonTooltip: boolean;
118 autocloseTooltip: boolean; 125 autocloseTooltip: boolean;
119 - tooltipFunction: GenericFunction;  
120 showTooltipAction: string; 126 showTooltipAction: string;
121 tooltipAction: { [name: string]: actionsHandler }; 127 tooltipAction: { [name: string]: actionsHandler };
122 - tooltipPattern: string;  
123 - useTooltipFunction: boolean; 128 + polygonTooltipPattern: string;
  129 + usePolygonTooltipFunction: boolean;
124 polygonClick: { [name: string]: actionsHandler }; 130 polygonClick: { [name: string]: actionsHandler };
  131 + usePolygonColorFunction: boolean;
  132 + polygonTooltipFunction: GenericFunction;
125 polygonColorFunction?: GenericFunction; 133 polygonColorFunction?: GenericFunction;
126 } 134 }
127 135
@@ -154,6 +162,88 @@ export interface HistorySelectSettings { @@ -154,6 +162,88 @@ export interface HistorySelectSettings {
154 buttonColor: string; 162 buttonColor: string;
155 } 163 }
156 164
  165 +export type TripAnimationSttings = {
  166 + pointColor: string;
  167 + pointSize: number;
  168 + pointTooltipOnRightPanel: boolean;
  169 +}
  170 +
157 export type actionsHandler = ($event: Event, datasource: Datasource) => void; 171 export type actionsHandler = ($event: Event, datasource: Datasource) => void;
158 172
159 -export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolylineSettings; 173 +export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolylineSettings & TripAnimationSttings;
  174 +
  175 +interface IProvider {
  176 + MapClass: Type<LeafletMap>,
  177 + schema: JsonSettingsSchema,
  178 + name: string
  179 +}
  180 +
  181 +export const providerSets: { [key: string]: IProvider } = {
  182 + 'openstreet-map': {
  183 + MapClass: OpenStreetMap,
  184 + schema: openstreetMapSettingsSchema,
  185 + name: 'openstreet-map',
  186 + },
  187 + 'tencent-map': {
  188 + MapClass: TencentMap,
  189 + schema: tencentMapSettingsSchema,
  190 + name: 'tencent-map'
  191 + },
  192 + 'google-map': {
  193 + MapClass: GoogleMap,
  194 + schema: googleMapSettingsSchema,
  195 + name: 'google-map'
  196 + },
  197 + here: {
  198 + MapClass: HEREMap,
  199 + schema: hereMapSettingsSchema,
  200 + name: 'here'
  201 + },
  202 + 'image-map': {
  203 + MapClass: ImageMap,
  204 + schema: imageMapSettingsSchema,
  205 + name: 'image-map'
  206 + }
  207 +};
  208 +
  209 +export const defaultSettings: any = {
  210 + xPosKeyName: 'xPos',
  211 + yPosKeyName: 'yPos',
  212 + markerOffsetX: 0.5,
  213 + markerOffsetY: 1,
  214 + latKeyName: 'latitude',
  215 + lngKeyName: 'longitude',
  216 + polygonKeyName: 'coordinates',
  217 + showLabel: false,
  218 + label: '${entityName}',
  219 + showTooltip: false,
  220 + useDefaultCenterPosition: false,
  221 + showTooltipAction: 'click',
  222 + autocloseTooltip: false,
  223 + showPolygon: false,
  224 + labelColor: '#000000',
  225 + color: '#FE7569',
  226 + polygonColor: '#0000ff',
  227 + polygonStrokeColor: '#fe0001',
  228 + polygonOpacity: 0.5,
  229 + polygonStrokeOpacity: 1,
  230 + polygonStrokeWeight: 1,
  231 + useLabelFunction: false,
  232 + markerImages: [],
  233 + strokeWeight: 2,
  234 + strokeOpacity: 1.0,
  235 + initCallback: () => { },
  236 + defaultZoomLevel: 8,
  237 + disableScrollZooming: false,
  238 + minZoomLevel: 16,
  239 + credentials: '',
  240 + markerClusteringSetting: null,
  241 + draggableMarker: false,
  242 + fitMapBounds: true
  243 +};
  244 +
  245 +export const hereProviders = [
  246 + 'HERE.normalDay',
  247 + 'HERE.normalNight',
  248 + 'HERE.hybridDay',
  249 + 'HERE.terrainDay']
@@ -26,7 +26,7 @@ export interface MapWidgetInterface { @@ -26,7 +26,7 @@ export interface MapWidgetInterface {
26 26
27 export interface MapWidgetStaticInterface { 27 export interface MapWidgetStaticInterface {
28 settingsSchema(mapProvider?: MapProviders, drawRoutes?: boolean): JsonSettingsSchema; 28 settingsSchema(mapProvider?: MapProviders, drawRoutes?: boolean): JsonSettingsSchema;
29 - getProvidersSchema(mapProvider?: MapProviders): JsonSettingsSchema 29 + getProvidersSchema(mapProvider?: MapProviders, ignoreImageMap?: boolean): JsonSettingsSchema
30 dataKeySettingsSchema(): object; 30 dataKeySettingsSchema(): object;
31 actionSources(): object; 31 actionSources(): object;
32 } 32 }
@@ -14,23 +14,17 @@ @@ -14,23 +14,17 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { MapProviders, UnitedMapSettings } from './map-models'; 17 +import { MapProviders, UnitedMapSettings, providerSets, hereProviders, defaultSettings } from './map-models';
18 import LeafletMap from './leaflet-map'; 18 import LeafletMap from './leaflet-map';
19 import { 19 import {
20 - openstreetMapSettingsSchema,  
21 - googleMapSettingsSchema,  
22 - imageMapSettingsSchema,  
23 - tencentMapSettingsSchema,  
24 commonMapSettingsSchema, 20 commonMapSettingsSchema,
25 routeMapSettingsSchema, 21 routeMapSettingsSchema,
26 markerClusteringSettingsSchema, 22 markerClusteringSettingsSchema,
27 markerClusteringSettingsSchemaLeaflet, 23 markerClusteringSettingsSchemaLeaflet,
28 - hereMapSettingsSchema,  
29 mapProviderSchema, 24 mapProviderSchema,
30 mapPolygonSchema 25 mapPolygonSchema
31 } from './schemes'; 26 } from './schemes';
32 import { MapWidgetStaticInterface, MapWidgetInterface } from './map-widget.interface'; 27 import { MapWidgetStaticInterface, MapWidgetInterface } from './map-widget.interface';
33 -import { OpenStreetMap, TencentMap, GoogleMap, HEREMap, ImageMap } from './providers';  
34 import { initSchema, addToSchema, mergeSchemes, addCondition, addGroupInfo } from '@core/schema-utils'; 28 import { initSchema, addToSchema, mergeSchemes, addCondition, addGroupInfo } from '@core/schema-utils';
35 import { of, Subject } from 'rxjs'; 29 import { of, Subject } from 'rxjs';
36 import { WidgetContext } from '@app/modules/home/models/widget-component.models'; 30 import { WidgetContext } from '@app/modules/home/models/widget-component.models';
@@ -39,7 +33,6 @@ import { JsonSettingsSchema, WidgetActionDescriptor, DatasourceType, widgetType, @@ -39,7 +33,6 @@ import { JsonSettingsSchema, WidgetActionDescriptor, DatasourceType, widgetType,
39 import { EntityId } from '@shared/models/id/entity-id'; 33 import { EntityId } from '@shared/models/id/entity-id';
40 import { AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models'; 34 import { AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models';
41 import { AttributeService } from '@core/http/attribute.service'; 35 import { AttributeService } from '@core/http/attribute.service';
42 -import { Type } from '@angular/core';  
43 import { TranslateService } from '@ngx-translate/core'; 36 import { TranslateService } from '@ngx-translate/core';
44 import { UtilsService } from '@core/services/utils.service'; 37 import { UtilsService } from '@core/services/utils.service';
45 38
@@ -85,11 +78,19 @@ export class MapWidgetController implements MapWidgetInterface { @@ -85,11 +78,19 @@ export class MapWidgetController implements MapWidgetInterface {
85 return {}; 78 return {};
86 } 79 }
87 80
88 - public static getProvidersSchema(mapProvider: MapProviders) {  
89 - mapProviderSchema.schema.properties.provider.default = mapProvider;  
90 - return mergeSchemes([mapProviderSchema, 81 + public static getProvidersSchema(mapProvider: MapProviders, ignoreImageMap = false) {
  82 + if (mapProvider)
  83 + mapProviderSchema.schema.properties.provider.default = mapProvider;
  84 + const providerSchema = mapProviderSchema;
  85 + if (ignoreImageMap) {
  86 + providerSchema.form[0].items = providerSchema.form[0]?.items.filter(item => item.value !== 'image-map');
  87 + }
  88 + return mergeSchemes([providerSchema,
91 ...Object.keys(providerSets)?.map( 89 ...Object.keys(providerSets)?.map(
92 - (key: string) => { const setting = providerSets[key]; return addCondition(setting?.schema, `model.provider === '${setting.name}'`) })]); 90 + (key: string) => {
  91 + const setting = providerSets[key];
  92 + return addCondition(setting?.schema, `model.provider === '${setting.name}'`);
  93 + })]);
93 } 94 }
94 95
95 public static settingsSchema(mapProvider: MapProviders, drawRoutes: boolean): JsonSettingsSchema { 96 public static settingsSchema(mapProvider: MapProviders, drawRoutes: boolean): JsonSettingsSchema {
@@ -218,6 +219,7 @@ export class MapWidgetController implements MapWidgetInterface { @@ -218,6 +219,7 @@ export class MapWidgetController implements MapWidgetInterface {
218 polygonColorFunction: parseFunction(settings.polygonColorFunction, functionParams), 219 polygonColorFunction: parseFunction(settings.polygonColorFunction, functionParams),
219 markerImageFunction: parseFunction(settings.markerImageFunction, ['data', 'images', 'dsData', 'dsIndex']), 220 markerImageFunction: parseFunction(settings.markerImageFunction, ['data', 'images', 'dsData', 'dsIndex']),
220 labelColor: this.ctx.widgetConfig.color, 221 labelColor: this.ctx.widgetConfig.color,
  222 + polygonKeyName: settings.polKeyName ? settings.polKeyName : settings.polygonKeyName,
221 tooltipPattern: settings.tooltipPattern || 223 tooltipPattern: settings.tooltipPattern ||
222 '<b>${entityName}</b><br/><br/><b>Latitude:</b> ${' + 224 '<b>${entityName}</b><br/><br/><b>Latitude:</b> ${' +
223 settings.latKeyName + ':7}<br/><b>Longitude:</b> ${' + settings.lngKeyName + ':7}', 225 settings.latKeyName + ':7}<br/><b>Longitude:</b> ${' + settings.lngKeyName + ':7}',
@@ -295,78 +297,4 @@ export class MapWidgetController implements MapWidgetInterface { @@ -295,78 +297,4 @@ export class MapWidgetController implements MapWidgetInterface {
295 297
296 export let TbMapWidgetV2: MapWidgetStaticInterface = MapWidgetController; 298 export let TbMapWidgetV2: MapWidgetStaticInterface = MapWidgetController;
297 299
298 -interface IProvider {  
299 - MapClass: Type<LeafletMap>,  
300 - schema: JsonSettingsSchema,  
301 - name: string  
302 -}  
303 -  
304 -export const providerSets: { [key: string]: IProvider } = {  
305 - 'openstreet-map': {  
306 - MapClass: OpenStreetMap,  
307 - schema: openstreetMapSettingsSchema,  
308 - name: 'openstreet-map',  
309 - },  
310 - 'tencent-map': {  
311 - MapClass: TencentMap,  
312 - schema: tencentMapSettingsSchema,  
313 - name: 'tencent-map'  
314 - },  
315 - 'google-map': {  
316 - MapClass: GoogleMap,  
317 - schema: googleMapSettingsSchema,  
318 - name: 'google-map'  
319 - },  
320 - here: {  
321 - MapClass: HEREMap,  
322 - schema: hereMapSettingsSchema,  
323 - name: 'here'  
324 - },  
325 - 'image-map': {  
326 - MapClass: ImageMap,  
327 - schema: imageMapSettingsSchema,  
328 - name: 'image-map'  
329 - }  
330 -};  
331 -  
332 -export const defaultSettings: any = {  
333 - xPosKeyName: 'xPos',  
334 - yPosKeyName: 'yPos',  
335 - markerOffsetX: 0.5,  
336 - markerOffsetY: 1,  
337 - latKeyName: 'latitude',  
338 - lngKeyName: 'longitude',  
339 - polygonKeyName: 'coordinates',  
340 - showLabel: false,  
341 - label: '${entityName}',  
342 - showTooltip: false,  
343 - useDefaultCenterPosition: false,  
344 - showTooltipAction: 'click',  
345 - autocloseTooltip: false,  
346 - showPolygon: false,  
347 - labelColor: '#000000',  
348 - color: '#FE7569',  
349 - polygonColor: '#0000ff',  
350 - polygonStrokeColor: '#fe0001',  
351 - polygonOpacity: 0.5,  
352 - polygonStrokeOpacity: 1,  
353 - polygonStrokeWeight: 1,  
354 - useLabelFunction: false,  
355 - markerImages: [],  
356 - strokeWeight: 2,  
357 - strokeOpacity: 1.0,  
358 - initCallback: () => { },  
359 - defaultZoomLevel: 8,  
360 - disableScrollZooming: false,  
361 - minZoomLevel: 16,  
362 - credentials: '',  
363 - markerClusteringSetting: null,  
364 - draggableMarker: false,  
365 - fitMapBounds: true  
366 -};  
367 300
368 -export const hereProviders = [  
369 - 'HERE.normalDay',  
370 - 'HERE.normalNight',  
371 - 'HERE.hybridDay',  
372 - 'HERE.terrainDay']  
@@ -120,7 +120,6 @@ export class Marker { @@ -120,7 +120,6 @@ export class Marker {
120 [this.data, this.settings.markerImages, this.dataSources, this.data.dsIndex]) : this.settings.currentImage; 120 [this.data, this.settings.markerImages, this.dataSources, this.data.dsIndex]) : this.settings.currentImage;
121 const currentColor = tinycolor(this.settings.useColorFunction ? safeExecute(this.settings.colorFunction, 121 const currentColor = tinycolor(this.settings.useColorFunction ? safeExecute(this.settings.colorFunction,
122 [this.data, this.dataSources, this.data.dsIndex]) : this.settings.color).toHex(); 122 [this.data, this.dataSources, this.data.dsIndex]) : this.settings.color).toHex();
123 -  
124 if (currentImage && currentImage.url) { 123 if (currentImage && currentImage.url) {
125 aspectCache(currentImage.url).subscribe( 124 aspectCache(currentImage.url).subscribe(
126 (aspect) => { 125 (aspect) => {
@@ -53,8 +53,9 @@ export class Polygon { @@ -53,8 +53,9 @@ export class Polygon {
53 } 53 }
54 54
55 updateTooltip(data: DatasourceData) { 55 updateTooltip(data: DatasourceData) {
56 - const pattern = this.settings.useTooltipFunction ?  
57 - safeExecute(this.settings.tooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) : this.settings.tooltipPattern; 56 + const pattern = this.settings.usePolygonTooltipFunction ?
  57 + safeExecute(this.settings.polygonTooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) :
  58 + this.settings.polygonTooltipPattern;
58 this.tooltip.setContent(parseWithTranslation.parseTemplate(pattern, data, true)); 59 this.tooltip.setContent(parseWithTranslation.parseTemplate(pattern, data, true));
59 } 60 }
60 61
@@ -71,10 +72,12 @@ export class Polygon { @@ -71,10 +72,12 @@ export class Polygon {
71 this.map.removeLayer(this.leafletPoly); 72 this.map.removeLayer(this.leafletPoly);
72 } 73 }
73 74
74 - updatePolygonColor(settings) { 75 + updatePolygonColor(settings: PolygonSettings) {
  76 + const color = settings.usePolygonColorFunction ?
  77 + safeExecute(settings.polygonColorFunction, [this.data, this.dataSources, this.data.dsIndex]) : settings.polygonColor;
75 const style: L.PathOptions = { 78 const style: L.PathOptions = {
76 fill: true, 79 fill: true,
77 - fillColor: settings.polygonColor, 80 + fillColor: color,
78 color: settings.polygonStrokeColor, 81 color: settings.polygonStrokeColor,
79 weight: settings.polygonStrokeWeight, 82 weight: settings.polygonStrokeWeight,
80 fillOpacity: settings.polygonOpacity, 83 fillOpacity: settings.polygonOpacity,
@@ -109,13 +109,13 @@ export class ImageMap extends LeafletMap { @@ -109,13 +109,13 @@ export class ImageMap extends LeafletMap {
109 lastCenterPos.y /= prevHeight; 109 lastCenterPos.y /= prevHeight;
110 this.updateBounds(updateImage, lastCenterPos); 110 this.updateBounds(updateImage, lastCenterPos);
111 this.map.invalidateSize(true); 111 this.map.invalidateSize(true);
112 - // TODO: need add update marker position 112 + this.updateMarkers(this.markersData);
113 } 113 }
114 } 114 }
115 } 115 }
116 } 116 }
117 117
118 - fitBounds(bounds: LatLngBounds, useDefaultZoom = false, padding?: LatLngTuple) { } 118 + fitBounds(bounds: LatLngBounds, padding?: LatLngTuple) { }
119 119
120 initMap(updateImage?) { 120 initMap(updateImage?) {
121 if (!this.map && this.aspect > 0) { 121 if (!this.map && this.aspect > 0) {
@@ -546,6 +546,20 @@ export const mapPolygonSchema = @@ -546,6 +546,20 @@ export const mapPolygonSchema =
546 type: 'boolean', 546 type: 'boolean',
547 default: false 547 default: false
548 }, 548 },
  549 + polygonTooltipPattern: {
  550 + title: 'Tooltip (for ex. \'Text ${keyName} units.\' or <link-act name=\'my-action\'>Link text</link-act>\')',
  551 + type: 'string',
  552 + default: '<b>${entityName}</b><br/><br/><b>TimeStamp:</b> ${ts:7}'
  553 + },
  554 + usePolygonTooltipFunction: {
  555 + title: 'Use polygon tooltip function',
  556 + type: 'boolean',
  557 + default: false
  558 + },
  559 + polygonTooltipFunction: {
  560 + title: 'Polygon tooltip function: f(data, dsData, dsIndex)',
  561 + type: 'string'
  562 + },
549 usePolygonColorFunction: { 563 usePolygonColorFunction: {
550 title: 'Use polygon color function', 564 title: 'Use polygon color function',
551 type: 'boolean', 565 type: 'boolean',
@@ -570,7 +584,15 @@ export const mapPolygonSchema = @@ -570,7 +584,15 @@ export const mapPolygonSchema =
570 key: 'polygonStrokeColor', 584 key: 'polygonStrokeColor',
571 type: 'color' 585 type: 'color'
572 }, 586 },
573 - 'polygonStrokeOpacity', 'polygonStrokeWeight', 'usePolygonColorFunction', 'showPolygonTooltip', 587 + 'polygonStrokeOpacity', 'polygonStrokeWeight', 'showPolygonTooltip',
  588 + {
  589 + key: 'polygonTooltipPattern',
  590 + type: 'textarea'
  591 + }, 'usePolygonTooltipFunction', {
  592 + key: 'polygonTooltipFunction',
  593 + type: 'javascript'
  594 + },
  595 + 'usePolygonColorFunction',
574 { 596 {
575 key: 'polygonColorFunction', 597 key: 'polygonColorFunction',
576 type: 'javascript' 598 type: 'javascript'
@@ -710,6 +732,161 @@ export const imageMapSettingsSchema = @@ -710,6 +732,161 @@ export const imageMapSettingsSchema =
710 ] 732 ]
711 }; 733 };
712 734
  735 +export const pathSchema =
  736 +{
  737 + schema: {
  738 + title: 'Trip Animation Path Configuration',
  739 + type: 'object',
  740 + properties: {
  741 + color: {
  742 + title: 'Path color',
  743 + type: 'string'
  744 + },
  745 + strokeWeight: {
  746 + title: 'Stroke weight',
  747 + type: 'number',
  748 + default: 2
  749 + },
  750 + strokeOpacity: {
  751 + title: 'Stroke opacity',
  752 + type: 'number',
  753 + default: 1
  754 + },
  755 + useColorFunction: {
  756 + title: 'Use path color function',
  757 + type: 'boolean',
  758 + default: false
  759 + },
  760 + colorFunction: {
  761 + title: 'Path color function: f(data, dsData, dsIndex)',
  762 + type: 'string'
  763 + },
  764 + usePolylineDecorator: {
  765 + title: 'Use path decorator',
  766 + type: 'boolean',
  767 + default: false
  768 + },
  769 + decoratorSymbol: {
  770 + title: 'Decorator symbol',
  771 + type: 'string',
  772 + default: 'arrowHead'
  773 + },
  774 + decoratorSymbolSize: {
  775 + title: 'Decorator symbol size (px)',
  776 + type: 'number',
  777 + default: 10
  778 + },
  779 + useDecoratorCustomColor: {
  780 + title: 'Use path decorator custom color',
  781 + type: 'boolean',
  782 + default: false
  783 + },
  784 + decoratorCustomColor: {
  785 + title: 'Decorator custom color',
  786 + type: 'string',
  787 + default: '#000'
  788 + },
  789 + decoratorOffset: {
  790 + title: 'Decorator offset',
  791 + type: 'string',
  792 + default: '20px'
  793 + },
  794 + endDecoratorOffset: {
  795 + title: 'End decorator offset',
  796 + type: 'string',
  797 + default: '20px'
  798 + },
  799 + decoratorRepeat: {
  800 + title: 'Decorator repeat',
  801 + type: 'string',
  802 + default: '20px'
  803 + }
  804 + },
  805 + required: []
  806 + },
  807 + form: [
  808 + {
  809 + key: 'color',
  810 + type: 'color'
  811 + }, 'useColorFunction', {
  812 + key: 'colorFunction',
  813 + type: 'javascript'
  814 + }, 'strokeWeight', 'strokeOpacity',
  815 + 'usePolylineDecorator', {
  816 + key: 'decoratorSymbol',
  817 + type: 'rc-select',
  818 + multiple: false,
  819 + items: [{
  820 + value: 'arrowHead',
  821 + label: 'Arrow'
  822 + }, {
  823 + value: 'dash',
  824 + label: 'Dash'
  825 + }]
  826 + }, 'decoratorSymbolSize', 'useDecoratorCustomColor', {
  827 + key: 'decoratorCustomColor',
  828 + type: 'color'
  829 + }, {
  830 + key: 'decoratorOffset',
  831 + type: 'textarea'
  832 + }, {
  833 + key: 'endDecoratorOffset',
  834 + type: 'textarea'
  835 + }, {
  836 + key: 'decoratorRepeat',
  837 + type: 'textarea'
  838 + }
  839 + ]
  840 +};
  841 +
  842 +export const pointSchema =
  843 +{
  844 + schema: {
  845 + title: 'Trip Animation Path Configuration',
  846 + type: 'object',
  847 + properties: {
  848 + showPoints: {
  849 + title: 'Show points',
  850 + type: 'boolean',
  851 + default: false
  852 + },
  853 + pointColor: {
  854 + title: 'Point color',
  855 + type: 'string'
  856 + },
  857 + pointSize: {
  858 + title: 'Point size (px)',
  859 + type: 'number',
  860 + default: 10
  861 + },
  862 + usePointAsAnchor: {
  863 + title: 'Use point as anchor',
  864 + type: 'boolean',
  865 + default: false
  866 + },
  867 + pointAsAnchorFunction: {
  868 + title: 'Point as anchor function: f(data, dsData, dsIndex)',
  869 + type: 'string'
  870 + },
  871 + pointTooltipOnRightPanel: {
  872 + title: 'Independant point tooltip',
  873 + type: 'boolean',
  874 + default: true
  875 + },
  876 + },
  877 + required: []
  878 + },
  879 + form: [
  880 + 'showPoints', {
  881 + key: 'pointColor',
  882 + type: 'color'
  883 + }, 'pointSize', 'usePointAsAnchor', {
  884 + key: 'pointAsAnchorFunction',
  885 + type: 'javascript'
  886 + }, 'pointTooltipOnRightPanel',
  887 + ]
  888 +};
  889 +
713 export const mapProviderSchema = 890 export const mapProviderSchema =
714 { 891 {
715 schema: { 892 schema: {
@@ -755,7 +932,6 @@ export const mapProviderSchema = @@ -755,7 +932,6 @@ export const mapProviderSchema =
755 ] 932 ]
756 }; 933 };
757 934
758 -  
759 export const tripAnimationSchema = { 935 export const tripAnimationSchema = {
760 schema: { 936 schema: {
761 title: 'Openstreet Map Configuration', 937 title: 'Openstreet Map Configuration',
@@ -776,11 +952,6 @@ export const tripAnimationSchema = { @@ -776,11 +952,6 @@ export const tripAnimationSchema = {
776 type: 'string', 952 type: 'string',
777 default: 'longitude' 953 default: 'longitude'
778 }, 954 },
779 - polKeyName: {  
780 - title: 'Polygon key name',  
781 - type: 'string',  
782 - default: 'coordinates'  
783 - },  
784 showLabel: { 955 showLabel: {
785 title: 'Show label', 956 title: 'Show label',
786 type: 'boolean', 957 type: 'boolean',
@@ -834,148 +1005,6 @@ export const tripAnimationSchema = { @@ -834,148 +1005,6 @@ export const tripAnimationSchema = {
834 title: 'Tooltip function: f(data, dsData, dsIndex)', 1005 title: 'Tooltip function: f(data, dsData, dsIndex)',
835 type: 'string' 1006 type: 'string'
836 }, 1007 },
837 - color: {  
838 - title: 'Path color',  
839 - type: 'string'  
840 - },  
841 - strokeWeight: {  
842 - title: 'Stroke weight',  
843 - type: 'number',  
844 - default: 2  
845 - },  
846 - strokeOpacity: {  
847 - title: 'Stroke opacity',  
848 - type: 'number',  
849 - default: 1  
850 - },  
851 - useColorFunction: {  
852 - title: 'Use path color function',  
853 - type: 'boolean',  
854 - default: false  
855 - },  
856 - colorFunction: {  
857 - title: 'Path color function: f(data, dsData, dsIndex)',  
858 - type: 'string'  
859 - },  
860 - usePolylineDecorator: {  
861 - title: 'Use path decorator',  
862 - type: 'boolean',  
863 - default: false  
864 - },  
865 - decoratorSymbol: {  
866 - title: 'Decorator symbol',  
867 - type: 'string',  
868 - default: 'arrowHead'  
869 - },  
870 - decoratorSymbolSize: {  
871 - title: 'Decorator symbol size (px)',  
872 - type: 'number',  
873 - default: 10  
874 - },  
875 - useDecoratorCustomColor: {  
876 - title: 'Use path decorator custom color',  
877 - type: 'boolean',  
878 - default: false  
879 - },  
880 - decoratorCustomColor: {  
881 - title: 'Decorator custom color',  
882 - type: 'string',  
883 - default: '#000'  
884 - },  
885 - decoratorOffset: {  
886 - title: 'Decorator offset',  
887 - type: 'string',  
888 - default: '20px'  
889 - },  
890 - endDecoratorOffset: {  
891 - title: 'End decorator offset',  
892 - type: 'string',  
893 - default: '20px'  
894 - },  
895 - decoratorRepeat: {  
896 - title: 'Decorator repeat',  
897 - type: 'string',  
898 - default: '20px'  
899 - },  
900 - showPolygon: {  
901 - title: 'Show polygon',  
902 - type: 'boolean',  
903 - default: false  
904 - },  
905 - polygonTooltipPattern: {  
906 - title: 'Tooltip (for ex. \'Text ${keyName} units.\' or <link-act name=\'my-action\'>Link text</link-act>\')',  
907 - type: 'string',  
908 - default: '<b>${entityName}</b><br/><br/><b>TimeStamp:</b> ${ts:7}'  
909 - },  
910 - usePolygonTooltipFunction: {  
911 - title: 'Use polygon tooltip function',  
912 - type: 'boolean',  
913 - default: false  
914 - },  
915 - polygonTooltipFunction: {  
916 - title: 'Polygon tooltip function: f(data, dsData, dsIndex)',  
917 - type: 'string'  
918 - },  
919 - polygonColor: {  
920 - title: 'Polygon color',  
921 - type: 'string'  
922 - },  
923 - polygonOpacity: {  
924 - title: 'Polygon opacity',  
925 - type: 'number',  
926 - default: 0.5  
927 - },  
928 - polygonStrokeColor: {  
929 - title: 'Polygon border color',  
930 - type: 'string'  
931 - },  
932 - polygonStrokeOpacity: {  
933 - title: 'Polygon border opacity',  
934 - type: 'number',  
935 - default: 1  
936 - },  
937 - polygonStrokeWeight: {  
938 - title: 'Polygon border weight',  
939 - type: 'number',  
940 - default: 1  
941 - },  
942 - usePolygonColorFunction: {  
943 - title: 'Use polygon color function',  
944 - type: 'boolean',  
945 - default: false  
946 - },  
947 - polygonColorFunction: {  
948 - title: 'Polygon Color function: f(data, dsData, dsIndex)',  
949 - type: 'string'  
950 - },  
951 - showPoints: {  
952 - title: 'Show points',  
953 - type: 'boolean',  
954 - default: false  
955 - },  
956 - pointColor: {  
957 - title: 'Point color',  
958 - type: 'string'  
959 - },  
960 - pointSize: {  
961 - title: 'Point size (px)',  
962 - type: 'number',  
963 - default: 10  
964 - },  
965 - usePointAsAnchor: {  
966 - title: 'Use point as anchor',  
967 - type: 'boolean',  
968 - default: false  
969 - },  
970 - pointAsAnchorFunction: {  
971 - title: 'Point as anchor function: f(data, dsData, dsIndex)',  
972 - type: 'string'  
973 - },  
974 - pointTooltipOnRightPanel: {  
975 - title: 'Independant point tooltip',  
976 - type: 'boolean',  
977 - default: true  
978 - },  
979 autocloseTooltip: { 1008 autocloseTooltip: {
980 title: 'Auto-close point popup', 1009 title: 'Auto-close point popup',
981 type: 'boolean', 1010 type: 'boolean',
@@ -1015,111 +1044,35 @@ export const tripAnimationSchema = { @@ -1015,111 +1044,35 @@ export const tripAnimationSchema = {
1015 }, 1044 },
1016 required: [] 1045 required: []
1017 }, 1046 },
1018 - form: [{  
1019 - key: 'mapProvider',  
1020 - type: 'rc-select',  
1021 - multiple: false,  
1022 - items: [{  
1023 - value: 'OpenStreetMap.Mapnik',  
1024 - label: 'OpenStreetMap.Mapnik (Default)'  
1025 - }, {  
1026 - value: 'OpenStreetMap.BlackAndWhite',  
1027 - label: 'OpenStreetMap.BlackAndWhite'  
1028 - }, {  
1029 - value: 'OpenStreetMap.HOT',  
1030 - label: 'OpenStreetMap.HOT'  
1031 - }, {  
1032 - value: 'Esri.WorldStreetMap',  
1033 - label: 'Esri.WorldStreetMap'  
1034 - }, {  
1035 - value: 'Esri.WorldTopoMap',  
1036 - label: 'Esri.WorldTopoMap'  
1037 - }, {  
1038 - value: 'CartoDB.Positron',  
1039 - label: 'CartoDB.Positron'  
1040 - }, {  
1041 - value: 'CartoDB.DarkMatter',  
1042 - label: 'CartoDB.DarkMatter'  
1043 - }]  
1044 - }, 'normalizationStep', 'latKeyName', 'lngKeyName', 'polKeyName', 'showLabel', 'label', 'useLabelFunction', { 1047 + form: ['normalizationStep', 'latKeyName', 'lngKeyName', 'showLabel', 'label', 'useLabelFunction', {
1045 key: 'labelFunction', 1048 key: 'labelFunction',
1046 type: 'javascript' 1049 type: 'javascript'
1047 }, 'showTooltip', { 1050 }, 'showTooltip', {
1048 - key: 'tooltipColor',  
1049 - type: 'color'  
1050 - }, {  
1051 - key: 'tooltipFontColor',  
1052 - type: 'color'  
1053 - }, 'tooltipOpacity', {  
1054 - key: 'tooltipPattern',  
1055 - type: 'textarea'  
1056 - }, 'useTooltipFunction', {  
1057 - key: 'tooltipFunction',  
1058 - type: 'javascript'  
1059 - }, {  
1060 - key: 'color',  
1061 - type: 'color'  
1062 - }, 'useColorFunction', {  
1063 - key: 'colorFunction',  
1064 - type: 'javascript'  
1065 - }, 'usePolylineDecorator', {  
1066 - key: 'decoratorSymbol',  
1067 - type: 'rc-select',  
1068 - multiple: false,  
1069 - items: [{  
1070 - value: 'arrowHead',  
1071 - label: 'Arrow' 1051 + key: 'tooltipColor',
  1052 + type: 'color'
  1053 + }, {
  1054 + key: 'tooltipFontColor',
  1055 + type: 'color'
  1056 + }, 'tooltipOpacity', {
  1057 + key: 'tooltipPattern',
  1058 + type: 'textarea'
  1059 + }, 'useTooltipFunction', {
  1060 + key: 'tooltipFunction',
  1061 + type: 'javascript'
  1062 + }, 'autocloseTooltip', {
  1063 + key: 'markerImage',
  1064 + type: 'image'
  1065 + }, 'markerImageSize', 'rotationAngle', 'useMarkerImageFunction',
  1066 + {
  1067 + key: 'markerImageFunction',
  1068 + type: 'javascript'
1072 }, { 1069 }, {
1073 - value: 'dash',  
1074 - label: 'Dash' 1070 + key: 'markerImages',
  1071 + items: [
  1072 + {
  1073 + key: 'markerImages[]',
  1074 + type: 'image'
  1075 + }
  1076 + ]
1075 }] 1077 }]
1076 - }, 'decoratorSymbolSize', 'useDecoratorCustomColor', {  
1077 - key: 'decoratorCustomColor',  
1078 - type: 'color'  
1079 - }, {  
1080 - key: 'decoratorOffset',  
1081 - type: 'textarea'  
1082 - }, {  
1083 - key: 'endDecoratorOffset',  
1084 - type: 'textarea'  
1085 - }, {  
1086 - key: 'decoratorRepeat',  
1087 - type: 'textarea'  
1088 - }, 'strokeWeight', 'strokeOpacity', 'showPolygon', {  
1089 - key: 'polygonTooltipPattern',  
1090 - type: 'textarea'  
1091 - }, 'usePolygonTooltipFunction', {  
1092 - key: 'polygonTooltipFunction',  
1093 - type: 'javascript'  
1094 - }, {  
1095 - key: 'polygonColor',  
1096 - type: 'color'  
1097 - }, 'polygonOpacity', {  
1098 - key: 'polygonStrokeColor',  
1099 - type: 'color'  
1100 - }, 'polygonStrokeOpacity', 'polygonStrokeWeight', 'usePolygonColorFunction', {  
1101 - key: 'polygonColorFunction',  
1102 - type: 'javascript'  
1103 - }, 'showPoints', {  
1104 - key: 'pointColor',  
1105 - type: 'color'  
1106 - }, 'pointSize', 'usePointAsAnchor', {  
1107 - key: 'pointAsAnchorFunction',  
1108 - type: 'javascript'  
1109 - }, 'pointTooltipOnRightPanel', 'autocloseTooltip', {  
1110 - key: 'markerImage',  
1111 - type: 'image'  
1112 - }, 'markerImageSize', 'rotationAngle', 'useMarkerImageFunction',  
1113 - {  
1114 - key: 'markerImageFunction',  
1115 - type: 'javascript'  
1116 - }, {  
1117 - key: 'markerImages',  
1118 - items: [  
1119 - {  
1120 - key: 'markerImages[]',  
1121 - type: 'image'  
1122 - }  
1123 - ]  
1124 - }]  
1125 } 1078 }
@@ -32,6 +32,6 @@ @@ -32,6 +32,6 @@
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>
34 </div> 34 </div>
35 - <tb-history-selector *ngIf="historicalData" [settings]="settings" [intervals]="intervals" 35 + <tb-history-selector *ngIf="historicalData" [settings]="settings" [intervals]="intervals" [anchors]="anchors" [useAnchors]="useAnchors"
36 (timeUpdated)="timeUpdated($event)"></tb-history-selector> 36 (timeUpdated)="timeUpdated($event)"></tb-history-selector>
37 </div> 37 </div>
@@ -21,9 +21,9 @@ import { interpolateOnPointSegment } from 'leaflet-geometryutil'; @@ -21,9 +21,9 @@ import { interpolateOnPointSegment } from 'leaflet-geometryutil';
21 21
22 import { AfterViewInit, ChangeDetectorRef, Component, Input, OnInit, SecurityContext, ViewChild } from '@angular/core'; 22 import { AfterViewInit, ChangeDetectorRef, Component, Input, OnInit, SecurityContext, ViewChild } 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';  
25 -import { initSchema, addToSchema, addGroupInfo } from '@app/core/schema-utils';  
26 -import { tripAnimationSchema } from '../lib/maps/schemes'; 24 +import { MapProviders, FormattedData } from '../lib/maps/map-models';
  25 +import { initSchema, addToSchema, addGroupInfo, addCondition } from '@app/core/schema-utils';
  26 +import { tripAnimationSchema, mapPolygonSchema, pathSchema, pointSchema } from '../lib/maps/schemes';
27 import { DomSanitizer } from '@angular/platform-browser'; 27 import { DomSanitizer } from '@angular/platform-browser';
28 import { WidgetContext } from '@app/modules/home/models/widget-component.models'; 28 import { WidgetContext } from '@app/modules/home/models/widget-component.models';
29 import { findAngle, getRatio, parseArray, parseWithTranslation, safeExecute } from '../lib/maps/maps-utils'; 29 import { findAngle, getRatio, parseArray, parseWithTranslation, safeExecute } from '../lib/maps/maps-utils';
@@ -58,13 +58,21 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -58,13 +58,21 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
58 label; 58 label;
59 minTime; 59 minTime;
60 maxTime; 60 maxTime;
  61 + anchors = [];
  62 + useAnchors = false;
61 63
62 static getSettingsSchema(): JsonSettingsSchema { 64 static getSettingsSchema(): JsonSettingsSchema {
63 const schema = initSchema(); 65 const schema = initSchema();
64 - addToSchema(schema, TbMapWidgetV2.getProvidersSchema()); 66 + addToSchema(schema, TbMapWidgetV2.getProvidersSchema(null, true));
65 addGroupInfo(schema, 'Map Provider Settings'); 67 addGroupInfo(schema, 'Map Provider Settings');
66 addToSchema(schema, tripAnimationSchema); 68 addToSchema(schema, tripAnimationSchema);
67 addGroupInfo(schema, 'Trip Animation Settings'); 69 addGroupInfo(schema, 'Trip Animation Settings');
  70 + addToSchema(schema, pathSchema);
  71 + addGroupInfo(schema, 'Path Settings');
  72 + addToSchema(schema, addCondition(pointSchema, 'model.showPoints === true', ['showPoints']));
  73 + addGroupInfo(schema, 'Path Points Settings');
  74 + addToSchema(schema, addCondition(mapPolygonSchema, 'model.showPolygon === true', ['showPolygon']));
  75 + addGroupInfo(schema, 'Polygon Settings');
68 return schema; 76 return schema;
69 } 77 }
70 78
@@ -78,14 +86,15 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -78,14 +86,15 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
78 rotationAngle: 0 86 rotationAngle: 0
79 } 87 }
80 this.settings = { ...settings, ...this.ctx.settings }; 88 this.settings = { ...settings, ...this.ctx.settings };
  89 + this.useAnchors = this.settings.usePointAsAnchor && this.settings.showPoints;
  90 + this.settings.fitMapBounds = true;
  91 + this.normalizationStep = this.settings.normalizationStep;
81 const subscription = this.ctx.subscriptions[Object.keys(this.ctx.subscriptions)[0]]; 92 const subscription = this.ctx.subscriptions[Object.keys(this.ctx.subscriptions)[0]];
82 - if (subscription) subscription.callbacks.onDataUpdated = (updated) => { 93 + if (subscription) subscription.callbacks.onDataUpdated = () => {
83 this.historicalData = parseArray(this.ctx.data); 94 this.historicalData = parseArray(this.ctx.data);
84 this.activeTrip = this.historicalData[0][0]; 95 this.activeTrip = this.historicalData[0][0];
85 this.calculateIntervals(); 96 this.calculateIntervals();
86 this.timeUpdated(this.intervals[0]); 97 this.timeUpdated(this.intervals[0]);
87 - this.mapWidget.map.updatePolylines(this.interpolatedData.map(ds => _.values(ds)));  
88 -  
89 this.mapWidget.map.map?.invalidateSize(); 98 this.mapWidget.map.map?.invalidateSize();
90 this.cd.detectChanges(); 99 this.cd.detectChanges();
91 } 100 }
@@ -104,9 +113,17 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -104,9 +113,17 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
104 this.calcLabel(); 113 this.calcLabel();
105 this.calcTooltip(); 114 this.calcTooltip();
106 if (this.mapWidget) { 115 if (this.mapWidget) {
  116 + this.mapWidget.map.updatePolylines(this.interpolatedData.map(ds => _.values(ds)));
107 if (this.settings.showPolygon) { 117 if (this.settings.showPolygon) {
108 this.mapWidget.map.updatePolygons(this.interpolatedData); 118 this.mapWidget.map.updatePolygons(this.interpolatedData);
109 } 119 }
  120 + if (this.settings.showPoints) {
  121 + this.mapWidget.map.updatePoints(this.historicalData[0], this.calcTooltip);
  122 + this.anchors = this.historicalData[0]
  123 + .filter(data =>
  124 + this.settings.usePointAsAnchor ||
  125 + safeExecute(this.settings.pointAsAnchorFunction, [this.historicalData, data, data.dsIndex])).map(data => data.time);
  126 + }
110 this.mapWidget.map.updateMarkers(currentPosition); 127 this.mapWidget.map.updateMarkers(currentPosition);
111 } 128 }
112 } 129 }
@@ -117,23 +134,29 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -117,23 +134,29 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
117 calculateIntervals() { 134 calculateIntervals() {
118 this.historicalData.forEach((dataSource, index) => { 135 this.historicalData.forEach((dataSource, index) => {
119 this.intervals = []; 136 this.intervals = [];
120 -  
121 for (let time = dataSource[0]?.time; time < dataSource[dataSource.length - 1]?.time; time += this.normalizationStep) { 137 for (let time = dataSource[0]?.time; time < dataSource[dataSource.length - 1]?.time; time += this.normalizationStep) {
122 this.intervals.push(time); 138 this.intervals.push(time);
123 } 139 }
124 -  
125 this.intervals.push(dataSource[dataSource.length - 1]?.time); 140 this.intervals.push(dataSource[dataSource.length - 1]?.time);
126 this.interpolatedData[index] = this.interpolateArray(dataSource, this.intervals); 141 this.interpolatedData[index] = this.interpolateArray(dataSource, this.intervals);
127 }); 142 });
128 143
129 } 144 }
130 145
131 - calcTooltip() {  
132 - const data = { ...this.activeTrip, maxTime: this.maxTime, minTime: this.minTime }  
133 - const tooltipText: string = this.settings.useTooltipFunction ? 146 + calcTooltip = (point?: FormattedData, setTooltip = true) => {
  147 + if (!point) {
  148 + point = this.activeTrip;
  149 + }
  150 + const data = { ...point, maxTime: this.maxTime, minTime: this.minTime }
  151 + const tooltipPattern: string = this.settings.useTooltipFunction ?
134 safeExecute(this.settings.tooolTipFunction, [data, this.historicalData, 0]) : this.settings.tooltipPattern; 152 safeExecute(this.settings.tooolTipFunction, [data, this.historicalData, 0]) : this.settings.tooltipPattern;
135 - this.mainTooltip = this.sanitizer.sanitize(  
136 - SecurityContext.HTML, (parseWithTranslation.parseTemplate(tooltipText, data, true))); 153 + const tooltipText = parseWithTranslation.parseTemplate(tooltipPattern, data, true);
  154 + if (setTooltip) {
  155 + this.mainTooltip = this.sanitizer.sanitize(
  156 + SecurityContext.HTML, tooltipText);
  157 + this.cd.detectChanges();
  158 + }
  159 + return tooltipText;
137 } 160 }
138 161
139 calcLabel() { 162 calcLabel() {
@@ -28,6 +28,8 @@ export class HistorySelectorComponent implements OnInit, OnChanges { @@ -28,6 +28,8 @@ export class HistorySelectorComponent implements OnInit, OnChanges {
28 28
29 @Input() settings: HistorySelectSettings 29 @Input() settings: HistorySelectSettings
30 @Input() intervals = []; 30 @Input() intervals = [];
  31 + @Input() anchors = [];
  32 + @Input() useAnchors = false;
31 33
32 @Output() timeUpdated: EventEmitter<number> = new EventEmitter(); 34 @Output() timeUpdated: EventEmitter<number> = new EventEmitter();
33 35
@@ -56,7 +58,7 @@ export class HistorySelectorComponent implements OnInit, OnChanges { @@ -56,7 +58,7 @@ export class HistorySelectorComponent implements OnInit, OnChanges {
56 this.interval = interval(1000 / this.speed) 58 this.interval = interval(1000 / this.speed)
57 .pipe( 59 .pipe(
58 filter(() => this.playing)).subscribe(() => { 60 filter(() => this.playing)).subscribe(() => {
59 - this.index++; 61 + this.index++;
60 if (this.index < this.maxTimeIndex) { 62 if (this.index < this.maxTimeIndex) {
61 this.cd.detectChanges(); 63 this.cd.detectChanges();
62 this.timeUpdated.emit(this.intervals[this.index]); 64 this.timeUpdated.emit(this.intervals[this.index]);
@@ -91,14 +93,24 @@ export class HistorySelectorComponent implements OnInit, OnChanges { @@ -91,14 +93,24 @@ export class HistorySelectorComponent implements OnInit, OnChanges {
91 93
92 moveNext() { 94 moveNext() {
93 if (this.index < this.maxTimeIndex) { 95 if (this.index < this.maxTimeIndex) {
94 - this.index++; 96 + if (this.useAnchors) {
  97 + const anchorIndex = this.findIndex(this.intervals[this.index], this.anchors)+1;
  98 + this.index = this.findIndex(this.anchors[anchorIndex], this.intervals);
  99 + }
  100 + else
  101 + this.index++;
95 } 102 }
96 this.pause(); 103 this.pause();
97 } 104 }
98 105
99 movePrev() { 106 movePrev() {
100 if (this.index > this.minTimeIndex) { 107 if (this.index > this.minTimeIndex) {
101 - this.index++; 108 + if (this.useAnchors) {
  109 + const anchorIndex = this.findIndex(this.intervals[this.index], this.anchors) - 1;
  110 + this.index = this.findIndex(this.anchors[anchorIndex], this.intervals);
  111 + }
  112 + else
  113 + this.index--;
102 } 114 }
103 this.pause(); 115 this.pause();
104 } 116 }
@@ -113,6 +125,14 @@ export class HistorySelectorComponent implements OnInit, OnChanges { @@ -113,6 +125,14 @@ export class HistorySelectorComponent implements OnInit, OnChanges {
113 this.pause(); 125 this.pause();
114 } 126 }
115 127
  128 + findIndex(value, array: any[]) {
  129 + let i = 0;
  130 + while (array[i] < value) {
  131 + i++;
  132 + };
  133 + return i;
  134 + }
  135 +
116 changeIndex() { 136 changeIndex() {
117 this.timeUpdated.emit(this.intervals[this.index]); 137 this.timeUpdated.emit(this.intervals[this.index]);
118 } 138 }