Showing
8 changed files
with
113 additions
and
83 deletions
@@ -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 | ||
@@ -48,6 +48,7 @@ export default abstract class LeafletMap { | @@ -48,6 +48,7 @@ export default abstract class LeafletMap { | ||
48 | bounds: L.LatLngBounds; | 48 | bounds: L.LatLngBounds; |
49 | datasources: FormattedData[]; | 49 | datasources: FormattedData[]; |
50 | markersCluster; | 50 | markersCluster; |
51 | + points: FeatureGroup; | ||
51 | 52 | ||
52 | protected constructor(public $container: HTMLElement, options: UnitedMapSettings) { | 53 | protected constructor(public $container: HTMLElement, options: UnitedMapSettings) { |
53 | this.options = options; | 54 | this.options = options; |
@@ -158,9 +159,9 @@ export default abstract class LeafletMap { | @@ -158,9 +159,9 @@ export default abstract class LeafletMap { | ||
158 | this.map = map; | 159 | this.map = map; |
159 | if (this.options.useDefaultCenterPosition) { | 160 | if (this.options.useDefaultCenterPosition) { |
160 | this.map.panTo(this.options.defaultCenterPosition); | 161 | this.map.panTo(this.options.defaultCenterPosition); |
161 | - this.bounds = map.getBounds(); | 162 | + // this.bounds = map.getBounds(); |
162 | } | 163 | } |
163 | - else this.bounds = new L.LatLngBounds(null, null); | 164 | + // else this.bounds = new L.LatLngBounds(null, null); |
164 | if (this.options.draggableMarker) { | 165 | if (this.options.draggableMarker) { |
165 | this.addMarkerControl(); | 166 | this.addMarkerControl(); |
166 | } | 167 | } |
@@ -201,9 +202,9 @@ export default abstract class LeafletMap { | @@ -201,9 +202,9 @@ export default abstract class LeafletMap { | ||
201 | return this.map.getCenter(); | 202 | return this.map.getCenter(); |
202 | } | 203 | } |
203 | 204 | ||
204 | - fitBounds(bounds: LatLngBounds, useDefaultZoom = false, padding?: LatLngTuple) { | 205 | + fitBounds(bounds: LatLngBounds, padding?: LatLngTuple) { |
205 | if (bounds.isValid()) { | 206 | if (bounds.isValid()) { |
206 | - this.bounds = this.bounds.extend(bounds); | 207 | + this.bounds = !!this.bounds ? this.bounds.extend(bounds) : bounds; |
207 | if (!this.options.fitMapBounds && this.options.defaultZoomLevel) { | 208 | if (!this.options.fitMapBounds && this.options.defaultZoomLevel) { |
208 | this.map.setZoom(this.options.defaultZoomLevel, { animate: false }); | 209 | this.map.setZoom(this.options.defaultZoomLevel, { animate: false }); |
209 | if (this.options.useDefaultCenterPosition) { | 210 | if (this.options.useDefaultCenterPosition) { |
@@ -219,9 +220,9 @@ export default abstract class LeafletMap { | @@ -219,9 +220,9 @@ export default abstract class LeafletMap { | ||
219 | } | 220 | } |
220 | }); | 221 | }); |
221 | if (this.options.useDefaultCenterPosition) { | 222 | if (this.options.useDefaultCenterPosition) { |
222 | - bounds = bounds.extend(this.options.defaultCenterPosition); | 223 | + this.bounds = this.bounds.extend(this.options.defaultCenterPosition); |
223 | } | 224 | } |
224 | - this.map.fitBounds(bounds, { padding: padding || [50, 50], animate: false }); | 225 | + this.map.fitBounds(this.bounds, { padding: padding || [50, 50], animate: false }); |
225 | } | 226 | } |
226 | } | 227 | } |
227 | } | 228 | } |
@@ -253,11 +254,10 @@ export default abstract class LeafletMap { | @@ -253,11 +254,10 @@ export default abstract class LeafletMap { | ||
253 | const style = currentImage ? 'background-image: url(' + currentImage.url + ');' : ''; | 254 | const style = currentImage ? 'background-image: url(' + currentImage.url + ');' : ''; |
254 | this.options.icon = L.divIcon({ | 255 | this.options.icon = L.divIcon({ |
255 | html: `<div class="arrow" | 256 | html: `<div class="arrow" |
256 | - style="transform: translate(-10px, -10px); | ||
257 | - ${style} | ||
258 | - rotate(${data.rotationAngle}deg); | ||
259 | - "><div>` | ||
260 | - }) | 257 | + style="transform: translate(-10px, -10px) |
258 | + rotate(${data.rotationAngle}deg); | ||
259 | + ${style}"><div>` | ||
260 | + }); | ||
261 | } | 261 | } |
262 | else { | 262 | else { |
263 | this.options.icon = null; | 263 | this.options.icon = null; |
@@ -279,7 +279,8 @@ export default abstract class LeafletMap { | @@ -279,7 +279,8 @@ export default abstract class LeafletMap { | ||
279 | private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings) { | 279 | private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings) { |
280 | this.ready$.subscribe(() => { | 280 | this.ready$.subscribe(() => { |
281 | const newMarker = new Marker(this.convertPosition(data), settings, data, dataSources, this.dragMarker); | 281 | const newMarker = new Marker(this.convertPosition(data), settings, data, dataSources, this.dragMarker); |
282 | - this.fitBounds(this.bounds.extend(newMarker.leafletMarker.getLatLng()), settings.draggableMarker && this.markers.size < 2); | 282 | + if (this.bounds) |
283 | + this.fitBounds(this.bounds.extend(newMarker.leafletMarker.getLatLng())); | ||
283 | this.markers.set(key, newMarker); | 284 | this.markers.set(key, newMarker); |
284 | if (this.options.useClusterMarkers) { | 285 | if (this.options.useClusterMarkers) { |
285 | this.markersCluster.addLayer(newMarker.leafletMarker); | 286 | this.markersCluster.addLayer(newMarker.leafletMarker); |
@@ -314,6 +315,29 @@ export default abstract class LeafletMap { | @@ -314,6 +315,29 @@ export default abstract class LeafletMap { | ||
314 | } | 315 | } |
315 | } | 316 | } |
316 | 317 | ||
318 | + updatePoints(pointsData: FormattedData[], getTooltip: (point: FormattedData, setTooltip?: boolean) => string) { | ||
319 | + this.map$.subscribe(map => { | ||
320 | + if (this.points) { | ||
321 | + map.removeLayer(this.points); | ||
322 | + } | ||
323 | + this.points = new FeatureGroup(); | ||
324 | + pointsData.filter(pdata => !!this.convertPosition(pdata)).forEach(data => { | ||
325 | + const point = L.circleMarker(this.convertPosition(data), { | ||
326 | + color: this.options.pointColor, | ||
327 | + radius: this.options.pointSize | ||
328 | + }); | ||
329 | + if (!this.options.pointTooltipOnRightPanel) { | ||
330 | + point.on('click', () => getTooltip(data)); | ||
331 | + } | ||
332 | + else { | ||
333 | + createTooltip(point, this.options, pointsData, getTooltip(data, false)); | ||
334 | + } | ||
335 | + this.points.addLayer(point); | ||
336 | + }); | ||
337 | + map.addLayer(this.points); | ||
338 | + }); | ||
339 | + } | ||
340 | + | ||
317 | setImageAlias(alias: Observable<any>) { | 341 | setImageAlias(alias: Observable<any>) { |
318 | } | 342 | } |
319 | 343 | ||
@@ -338,15 +362,17 @@ export default abstract class LeafletMap { | @@ -338,15 +362,17 @@ export default abstract class LeafletMap { | ||
338 | this.ready$.subscribe(() => { | 362 | this.ready$.subscribe(() => { |
339 | const poly = new Polyline(this.map, | 363 | const poly = new Polyline(this.map, |
340 | data.map(el => this.convertPosition(el)).filter(el => !!el), data, dataSources, settings); | 364 | data.map(el => this.convertPosition(el)).filter(el => !!el), data, dataSources, settings); |
341 | - const bounds = this.bounds.extend(poly.leafletPoly.getBounds()); | ||
342 | - this.fitBounds(bounds) | ||
343 | - this.polylines.set(data[0].entityName, poly) | 365 | + const bounds = poly.leafletPoly.getBounds(); |
366 | + this.fitBounds(bounds); | ||
367 | + this.polylines.set(data[0].entityName, poly); | ||
344 | }); | 368 | }); |
345 | } | 369 | } |
346 | 370 | ||
347 | updatePolyline(key: string, data: FormattedData[], dataSources: FormattedData[], settings: PolylineSettings) { | 371 | updatePolyline(key: string, data: FormattedData[], dataSources: FormattedData[], settings: PolylineSettings) { |
348 | this.ready$.subscribe(() => { | 372 | this.ready$.subscribe(() => { |
349 | - this.polylines.get(key).updatePolyline(settings, data.map(el => this.convertPosition(el)), dataSources); | 373 | + const poly = this.polylines.get(key); |
374 | + poly.updatePolyline(settings, data.map(el => this.convertPosition(el)), dataSources); | ||
375 | + const bounds = poly.leafletPoly.getBounds(); | ||
350 | }); | 376 | }); |
351 | } | 377 | } |
352 | 378 | ||
@@ -371,7 +397,7 @@ export default abstract class LeafletMap { | @@ -371,7 +397,7 @@ export default abstract class LeafletMap { | ||
371 | createPolygon(polyData: DatasourceData, dataSources: DatasourceData[], settings: PolygonSettings) { | 397 | createPolygon(polyData: DatasourceData, dataSources: DatasourceData[], settings: PolygonSettings) { |
372 | this.ready$.subscribe(() => { | 398 | this.ready$.subscribe(() => { |
373 | const polygon = new Polygon(this.map, polyData, dataSources, settings); | 399 | const polygon = new Polygon(this.map, polyData, dataSources, settings); |
374 | - const bounds = this.bounds.extend(polygon.leafletPoly.getBounds()); | 400 | + const bounds = polygon.leafletPoly.getBounds(); |
375 | this.fitBounds(bounds); | 401 | this.fitBounds(bounds); |
376 | this.polygons.set(polyData.datasource.entityName, polygon); | 402 | this.polygons.set(polyData.datasource.entityName, polygon); |
377 | }); | 403 | }); |
@@ -381,7 +407,6 @@ export default abstract class LeafletMap { | @@ -381,7 +407,6 @@ export default abstract class LeafletMap { | ||
381 | this.ready$.subscribe(() => { | 407 | this.ready$.subscribe(() => { |
382 | const poly = this.polygons.get(polyData.datasource.entityName); | 408 | const poly = this.polygons.get(polyData.datasource.entityName); |
383 | poly.updatePolygon(polyData.data, dataSources, settings); | 409 | poly.updatePolygon(polyData.data, dataSources, settings); |
384 | - this.fitBounds(poly.leafletPoly.getBounds()); | ||
385 | }); | 410 | }); |
386 | } | 411 | } |
387 | } | 412 | } |
@@ -159,9 +159,15 @@ export interface HistorySelectSettings { | @@ -159,9 +159,15 @@ export interface HistorySelectSettings { | ||
159 | buttonColor: string; | 159 | buttonColor: string; |
160 | } | 160 | } |
161 | 161 | ||
162 | +export type TripAnimationSttings = { | ||
163 | + pointColor: string; | ||
164 | + pointSize: number; | ||
165 | + pointTooltipOnRightPanel: boolean; | ||
166 | +} | ||
167 | + | ||
162 | export type actionsHandler = ($event: Event, datasource: Datasource) => void; | 168 | export type actionsHandler = ($event: Event, datasource: Datasource) => void; |
163 | 169 | ||
164 | -export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolylineSettings; | 170 | +export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolylineSettings & TripAnimationSttings; |
165 | 171 | ||
166 | interface IProvider { | 172 | interface IProvider { |
167 | MapClass: Type<LeafletMap>, | 173 | MapClass: Type<LeafletMap>, |
@@ -113,7 +113,6 @@ export class Marker { | @@ -113,7 +113,6 @@ export class Marker { | ||
113 | [this.data, this.settings.markerImages, this.dataSources, this.data.dsIndex]) : this.settings.currentImage; | 113 | [this.data, this.settings.markerImages, this.dataSources, this.data.dsIndex]) : this.settings.currentImage; |
114 | const currentColor = tinycolor(this.settings.useColorFunction ? safeExecute(this.settings.colorFunction, | 114 | const currentColor = tinycolor(this.settings.useColorFunction ? safeExecute(this.settings.colorFunction, |
115 | [this.data, this.dataSources, this.data.dsIndex]) : this.settings.color).toHex(); | 115 | [this.data, this.dataSources, this.data.dsIndex]) : this.settings.color).toHex(); |
116 | - | ||
117 | if (currentImage && currentImage.url) { | 116 | if (currentImage && currentImage.url) { |
118 | aspectCache(currentImage.url).subscribe( | 117 | aspectCache(currentImage.url).subscribe( |
119 | (aspect) => { | 118 | (aspect) => { |
@@ -112,7 +112,7 @@ export class ImageMap extends LeafletMap { | @@ -112,7 +112,7 @@ export class ImageMap extends LeafletMap { | ||
112 | } | 112 | } |
113 | } | 113 | } |
114 | 114 | ||
115 | - fitBounds(bounds: LatLngBounds, useDefaultZoom = false, padding?: LatLngTuple) { } | 115 | + fitBounds(bounds: LatLngBounds, padding?: LatLngTuple) { } |
116 | 116 | ||
117 | initMap(updateImage?) { | 117 | initMap(updateImage?) { |
118 | if (!this.map && this.aspect > 0) { | 118 | if (!this.map && this.aspect > 0) { |
@@ -923,35 +923,7 @@ export const pathSchema = | @@ -923,35 +923,7 @@ export const pathSchema = | ||
923 | title: 'Decorator repeat', | 923 | title: 'Decorator repeat', |
924 | type: 'string', | 924 | type: 'string', |
925 | default: '20px' | 925 | default: '20px' |
926 | - }, | ||
927 | - showPoints: { | ||
928 | - title: 'Show points', | ||
929 | - type: 'boolean', | ||
930 | - default: false | ||
931 | - }, | ||
932 | - pointColor: { | ||
933 | - title: 'Point color', | ||
934 | - type: 'string' | ||
935 | - }, | ||
936 | - pointSize: { | ||
937 | - title: 'Point size (px)', | ||
938 | - type: 'number', | ||
939 | - default: 10 | ||
940 | - }, | ||
941 | - usePointAsAnchor: { | ||
942 | - title: 'Use point as anchor', | ||
943 | - type: 'boolean', | ||
944 | - default: false | ||
945 | - }, | ||
946 | - pointAsAnchorFunction: { | ||
947 | - title: 'Point as anchor function: f(data, dsData, dsIndex)', | ||
948 | - type: 'string' | ||
949 | - }, | ||
950 | - pointTooltipOnRightPanel: { | ||
951 | - title: 'Independant point tooltip', | ||
952 | - type: 'boolean', | ||
953 | - default: true | ||
954 | - }, | 926 | + } |
955 | }, | 927 | }, |
956 | required: [] | 928 | required: [] |
957 | }, | 929 | }, |
@@ -986,13 +958,7 @@ export const pathSchema = | @@ -986,13 +958,7 @@ export const pathSchema = | ||
986 | }, { | 958 | }, { |
987 | key: 'decoratorRepeat', | 959 | key: 'decoratorRepeat', |
988 | type: 'textarea' | 960 | type: 'textarea' |
989 | - }, 'showPoints', { | ||
990 | - key: 'pointColor', | ||
991 | - type: 'color' | ||
992 | - }, 'pointSize', 'usePointAsAnchor', { | ||
993 | - key: 'pointAsAnchorFunction', | ||
994 | - type: 'javascript' | ||
995 | - }, 'pointTooltipOnRightPanel', | 961 | + } |
996 | ] | 962 | ] |
997 | }; | 963 | }; |
998 | 964 |
@@ -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,7 +21,7 @@ import { interpolateOnPointSegment } from 'leaflet-geometryutil'; | @@ -21,7 +21,7 @@ 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'; | 24 | +import { MapProviders, FormattedData } from '../lib/maps/map-models'; |
25 | import { initSchema, addToSchema, addGroupInfo, addCondition } from '@app/core/schema-utils'; | 25 | import { initSchema, addToSchema, addGroupInfo, addCondition } from '@app/core/schema-utils'; |
26 | import { tripAnimationSchema, mapPolygonSchema, pathSchema, pointSchema } from '../lib/maps/schemes'; | 26 | import { tripAnimationSchema, mapPolygonSchema, pathSchema, pointSchema } from '../lib/maps/schemes'; |
27 | import { DomSanitizer } from '@angular/platform-browser'; | 27 | import { DomSanitizer } from '@angular/platform-browser'; |
@@ -58,6 +58,8 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { | @@ -58,6 +58,8 @@ 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(); |
@@ -67,8 +69,8 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { | @@ -67,8 +69,8 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { | ||
67 | addGroupInfo(schema, 'Trip Animation Settings'); | 69 | addGroupInfo(schema, 'Trip Animation Settings'); |
68 | addToSchema(schema, pathSchema); | 70 | addToSchema(schema, pathSchema); |
69 | addGroupInfo(schema, 'Path Settings'); | 71 | addGroupInfo(schema, 'Path Settings'); |
70 | - addToSchema(schema, addCondition(pointSchema, 'model.showPoint === true', ['showPoint'])); | ||
71 | - addGroupInfo(schema, 'Polygon Settings'); | 72 | + addToSchema(schema, addCondition(pointSchema, 'model.showPoints === true', ['showPoints'])); |
73 | + addGroupInfo(schema, 'Path Points Settings'); | ||
72 | addToSchema(schema, addCondition(mapPolygonSchema, 'model.showPolygon === true', ['showPolygon'])); | 74 | addToSchema(schema, addCondition(mapPolygonSchema, 'model.showPolygon === true', ['showPolygon'])); |
73 | addGroupInfo(schema, 'Polygon Settings'); | 75 | addGroupInfo(schema, 'Polygon Settings'); |
74 | return schema; | 76 | return schema; |
@@ -84,14 +86,15 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { | @@ -84,14 +86,15 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { | ||
84 | rotationAngle: 0 | 86 | rotationAngle: 0 |
85 | } | 87 | } |
86 | 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; | ||
87 | const subscription = this.ctx.subscriptions[Object.keys(this.ctx.subscriptions)[0]]; | 92 | const subscription = this.ctx.subscriptions[Object.keys(this.ctx.subscriptions)[0]]; |
88 | if (subscription) subscription.callbacks.onDataUpdated = () => { | 93 | if (subscription) subscription.callbacks.onDataUpdated = () => { |
89 | this.historicalData = parseArray(this.ctx.data); | 94 | this.historicalData = parseArray(this.ctx.data); |
90 | this.activeTrip = this.historicalData[0][0]; | 95 | this.activeTrip = this.historicalData[0][0]; |
91 | this.calculateIntervals(); | 96 | this.calculateIntervals(); |
92 | this.timeUpdated(this.intervals[0]); | 97 | this.timeUpdated(this.intervals[0]); |
93 | - this.mapWidget.map.updatePolylines(this.interpolatedData.map(ds => _.values(ds))); | ||
94 | - | ||
95 | this.mapWidget.map.map?.invalidateSize(); | 98 | this.mapWidget.map.map?.invalidateSize(); |
96 | this.cd.detectChanges(); | 99 | this.cd.detectChanges(); |
97 | } | 100 | } |
@@ -110,11 +113,16 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { | @@ -110,11 +113,16 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { | ||
110 | this.calcLabel(); | 113 | this.calcLabel(); |
111 | this.calcTooltip(); | 114 | this.calcTooltip(); |
112 | if (this.mapWidget) { | 115 | if (this.mapWidget) { |
116 | + this.mapWidget.map.updatePolylines(this.interpolatedData.map(ds => _.values(ds))); | ||
113 | if (this.settings.showPolygon) { | 117 | if (this.settings.showPolygon) { |
114 | this.mapWidget.map.updatePolygons(this.interpolatedData); | 118 | this.mapWidget.map.updatePolygons(this.interpolatedData); |
115 | } | 119 | } |
116 | - if(this.settings.showPoint){ | ||
117 | - this.mapWidget.map.updateMarkers(this.interpolatedData) | 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); | ||
118 | } | 126 | } |
119 | this.mapWidget.map.updateMarkers(currentPosition); | 127 | this.mapWidget.map.updateMarkers(currentPosition); |
120 | } | 128 | } |
@@ -126,23 +134,29 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { | @@ -126,23 +134,29 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { | ||
126 | calculateIntervals() { | 134 | calculateIntervals() { |
127 | this.historicalData.forEach((dataSource, index) => { | 135 | this.historicalData.forEach((dataSource, index) => { |
128 | this.intervals = []; | 136 | this.intervals = []; |
129 | - | ||
130 | 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) { |
131 | this.intervals.push(time); | 138 | this.intervals.push(time); |
132 | } | 139 | } |
133 | - | ||
134 | this.intervals.push(dataSource[dataSource.length - 1]?.time); | 140 | this.intervals.push(dataSource[dataSource.length - 1]?.time); |
135 | this.interpolatedData[index] = this.interpolateArray(dataSource, this.intervals); | 141 | this.interpolatedData[index] = this.interpolateArray(dataSource, this.intervals); |
136 | }); | 142 | }); |
137 | 143 | ||
138 | } | 144 | } |
139 | 145 | ||
140 | - calcTooltip() { | ||
141 | - const data = { ...this.activeTrip, maxTime: this.maxTime, minTime: this.minTime } | ||
142 | - 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 ? | ||
143 | safeExecute(this.settings.tooolTipFunction, [data, this.historicalData, 0]) : this.settings.tooltipPattern; | 152 | safeExecute(this.settings.tooolTipFunction, [data, this.historicalData, 0]) : this.settings.tooltipPattern; |
144 | - this.mainTooltip = this.sanitizer.sanitize( | ||
145 | - 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; | ||
146 | } | 160 | } |
147 | 161 | ||
148 | 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 | } |