diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts
index 93477c1782..31db505122 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts
@@ -14,18 +14,18 @@
/// limitations under the License.
///
-import L, { LatLngBounds, LatLngTuple, markerClusterGroup, MarkerClusterGroupOptions } from 'leaflet';
+import L, { LatLngBounds, LatLngTuple, markerClusterGroup, MarkerClusterGroupOptions, FeatureGroup, LayerGroup } from 'leaflet';
import 'leaflet-providers';
import 'leaflet.markercluster/dist/leaflet.markercluster';
import {
- FormattedData,
- MapSettings,
- MarkerSettings,
- PolygonSettings,
- PolylineSettings,
- UnitedMapSettings
+ FormattedData,
+ MapSettings,
+ MarkerSettings,
+ PolygonSettings,
+ PolylineSettings,
+ UnitedMapSettings
} from './map-models';
import { Marker } from './markers';
import { BehaviorSubject, Observable } from 'rxjs';
@@ -33,7 +33,7 @@ import { filter } from 'rxjs/operators';
import { Polyline } from './polyline';
import { Polygon } from './polygon';
import { DatasourceData } from '@app/shared/models/widget.models';
-import { safeExecute } from '@home/components/widget/lib/maps/maps-utils';
+import { safeExecute, createTooltip } from '@home/components/widget/lib/maps/maps-utils';
export default abstract class LeafletMap {
@@ -48,6 +48,7 @@ export default abstract class LeafletMap {
bounds: L.LatLngBounds;
datasources: FormattedData[];
markersCluster;
+ points: FeatureGroup;
protected constructor(public $container: HTMLElement, options: UnitedMapSettings) {
this.options = options;
@@ -158,9 +159,9 @@ export default abstract class LeafletMap {
this.map = map;
if (this.options.useDefaultCenterPosition) {
this.map.panTo(this.options.defaultCenterPosition);
- this.bounds = map.getBounds();
+ // this.bounds = map.getBounds();
}
- else this.bounds = new L.LatLngBounds(null, null);
+ // else this.bounds = new L.LatLngBounds(null, null);
if (this.options.draggableMarker) {
this.addMarkerControl();
}
@@ -201,9 +202,9 @@ export default abstract class LeafletMap {
return this.map.getCenter();
}
- fitBounds(bounds: LatLngBounds, useDefaultZoom = false, padding?: LatLngTuple) {
+ fitBounds(bounds: LatLngBounds, padding?: LatLngTuple) {
if (bounds.isValid()) {
- this.bounds = this.bounds.extend(bounds);
+ this.bounds = !!this.bounds ? this.bounds.extend(bounds) : bounds;
if (!this.options.fitMapBounds && this.options.defaultZoomLevel) {
this.map.setZoom(this.options.defaultZoomLevel, { animate: false });
if (this.options.useDefaultCenterPosition) {
@@ -219,9 +220,9 @@ export default abstract class LeafletMap {
}
});
if (this.options.useDefaultCenterPosition) {
- bounds = bounds.extend(this.options.defaultCenterPosition);
+ this.bounds = this.bounds.extend(this.options.defaultCenterPosition);
}
- this.map.fitBounds(bounds, { padding: padding || [50, 50], animate: false });
+ this.map.fitBounds(this.bounds, { padding: padding || [50, 50], animate: false });
}
}
}
@@ -253,11 +254,10 @@ export default abstract class LeafletMap {
const style = currentImage ? 'background-image: url(' + currentImage.url + ');' : '';
this.options.icon = L.divIcon({
html: `
`
- })
+ style="transform: translate(-10px, -10px)
+ rotate(${data.rotationAngle}deg);
+ ${style}">
`
+ });
}
else {
this.options.icon = null;
@@ -279,7 +279,8 @@ export default abstract class LeafletMap {
private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings) {
this.ready$.subscribe(() => {
const newMarker = new Marker(this.convertPosition(data), settings, data, dataSources, this.dragMarker);
- this.fitBounds(this.bounds.extend(newMarker.leafletMarker.getLatLng()), settings.draggableMarker && this.markers.size < 2);
+ if (this.bounds)
+ this.fitBounds(this.bounds.extend(newMarker.leafletMarker.getLatLng()));
this.markers.set(key, newMarker);
if (this.options.useClusterMarkers) {
this.markersCluster.addLayer(newMarker.leafletMarker);
@@ -314,6 +315,29 @@ export default abstract class LeafletMap {
}
}
+ updatePoints(pointsData: FormattedData[], getTooltip: (point: FormattedData, setTooltip?: boolean) => string) {
+ this.map$.subscribe(map => {
+ if (this.points) {
+ map.removeLayer(this.points);
+ }
+ this.points = new FeatureGroup();
+ pointsData.filter(pdata => !!this.convertPosition(pdata)).forEach(data => {
+ const point = L.circleMarker(this.convertPosition(data), {
+ color: this.options.pointColor,
+ radius: this.options.pointSize
+ });
+ if (!this.options.pointTooltipOnRightPanel) {
+ point.on('click', () => getTooltip(data));
+ }
+ else {
+ createTooltip(point, this.options, pointsData, getTooltip(data, false));
+ }
+ this.points.addLayer(point);
+ });
+ map.addLayer(this.points);
+ });
+ }
+
setImageAlias(alias: Observable
) {
}
@@ -338,15 +362,17 @@ export default abstract class LeafletMap {
this.ready$.subscribe(() => {
const poly = new Polyline(this.map,
data.map(el => this.convertPosition(el)).filter(el => !!el), data, dataSources, settings);
- const bounds = this.bounds.extend(poly.leafletPoly.getBounds());
- this.fitBounds(bounds)
- this.polylines.set(data[0].entityName, poly)
+ const bounds = poly.leafletPoly.getBounds();
+ this.fitBounds(bounds);
+ this.polylines.set(data[0].entityName, poly);
});
}
updatePolyline(key: string, data: FormattedData[], dataSources: FormattedData[], settings: PolylineSettings) {
this.ready$.subscribe(() => {
- this.polylines.get(key).updatePolyline(settings, data.map(el => this.convertPosition(el)), dataSources);
+ const poly = this.polylines.get(key);
+ poly.updatePolyline(settings, data.map(el => this.convertPosition(el)), dataSources);
+ const bounds = poly.leafletPoly.getBounds();
});
}
@@ -371,7 +397,7 @@ export default abstract class LeafletMap {
createPolygon(polyData: DatasourceData, dataSources: DatasourceData[], settings: PolygonSettings) {
this.ready$.subscribe(() => {
const polygon = new Polygon(this.map, polyData, dataSources, settings);
- const bounds = this.bounds.extend(polygon.leafletPoly.getBounds());
+ const bounds = polygon.leafletPoly.getBounds();
this.fitBounds(bounds);
this.polygons.set(polyData.datasource.entityName, polygon);
});
@@ -381,7 +407,6 @@ export default abstract class LeafletMap {
this.ready$.subscribe(() => {
const poly = this.polygons.get(polyData.datasource.entityName);
poly.updatePolygon(polyData.data, dataSources, settings);
- this.fitBounds(poly.leafletPoly.getBounds());
});
}
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts
index 3532514a56..d05a2b4e87 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts
@@ -159,9 +159,15 @@ export interface HistorySelectSettings {
buttonColor: string;
}
+export type TripAnimationSttings = {
+ pointColor: string;
+ pointSize: number;
+ pointTooltipOnRightPanel: boolean;
+}
+
export type actionsHandler = ($event: Event, datasource: Datasource) => void;
-export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolylineSettings;
+export type UnitedMapSettings = MapSettings & PolygonSettings & MarkerSettings & PolylineSettings & TripAnimationSttings;
interface IProvider {
MapClass: Type,
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts
index 388f08eda5..eb14f99570 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts
@@ -113,7 +113,6 @@ export class Marker {
[this.data, this.settings.markerImages, this.dataSources, this.data.dsIndex]) : this.settings.currentImage;
const currentColor = tinycolor(this.settings.useColorFunction ? safeExecute(this.settings.colorFunction,
[this.data, this.dataSources, this.data.dsIndex]) : this.settings.color).toHex();
-
if (currentImage && currentImage.url) {
aspectCache(currentImage.url).subscribe(
(aspect) => {
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts
index bd12185ad6..bd6d3fe89f 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts
@@ -112,7 +112,7 @@ export class ImageMap extends LeafletMap {
}
}
- fitBounds(bounds: LatLngBounds, useDefaultZoom = false, padding?: LatLngTuple) { }
+ fitBounds(bounds: LatLngBounds, padding?: LatLngTuple) { }
initMap(updateImage?) {
if (!this.map && this.aspect > 0) {
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts
index 2b6546f26b..1a293cc496 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts
@@ -923,35 +923,7 @@ export const pathSchema =
title: 'Decorator repeat',
type: 'string',
default: '20px'
- },
- showPoints: {
- title: 'Show points',
- type: 'boolean',
- default: false
- },
- pointColor: {
- title: 'Point color',
- type: 'string'
- },
- pointSize: {
- title: 'Point size (px)',
- type: 'number',
- default: 10
- },
- usePointAsAnchor: {
- title: 'Use point as anchor',
- type: 'boolean',
- default: false
- },
- pointAsAnchorFunction: {
- title: 'Point as anchor function: f(data, dsData, dsIndex)',
- type: 'string'
- },
- pointTooltipOnRightPanel: {
- title: 'Independant point tooltip',
- type: 'boolean',
- default: true
- },
+ }
},
required: []
},
@@ -986,13 +958,7 @@ export const pathSchema =
}, {
key: 'decoratorRepeat',
type: 'textarea'
- }, 'showPoints', {
- key: 'pointColor',
- type: 'color'
- }, 'pointSize', 'usePointAsAnchor', {
- key: 'pointAsAnchorFunction',
- type: 'javascript'
- }, 'pointTooltipOnRightPanel',
+ }
]
};
diff --git a/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.html b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.html
index b973284b53..9449e3aba9 100644
--- a/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.html
+++ b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.html
@@ -32,6 +32,6 @@
[ngStyle]="{'background-color': settings.tooltipColor, 'opacity': settings.tooltipOpacity, 'color': settings.tooltipFontColor}">
-
\ No newline at end of file
diff --git a/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts
index b80fa1bada..7dfec08c9f 100644
--- a/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts
@@ -21,7 +21,7 @@ import { interpolateOnPointSegment } from 'leaflet-geometryutil';
import { AfterViewInit, ChangeDetectorRef, Component, Input, OnInit, SecurityContext, ViewChild } from '@angular/core';
import { MapWidgetController, TbMapWidgetV2 } from '../lib/maps/map-widget2';
-import { MapProviders } from '../lib/maps/map-models';
+import { MapProviders, FormattedData } from '../lib/maps/map-models';
import { initSchema, addToSchema, addGroupInfo, addCondition } from '@app/core/schema-utils';
import { tripAnimationSchema, mapPolygonSchema, pathSchema, pointSchema } from '../lib/maps/schemes';
import { DomSanitizer } from '@angular/platform-browser';
@@ -58,6 +58,8 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
label;
minTime;
maxTime;
+ anchors = [];
+ useAnchors = false;
static getSettingsSchema(): JsonSettingsSchema {
const schema = initSchema();
@@ -67,8 +69,8 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
addGroupInfo(schema, 'Trip Animation Settings');
addToSchema(schema, pathSchema);
addGroupInfo(schema, 'Path Settings');
- addToSchema(schema, addCondition(pointSchema, 'model.showPoint === true', ['showPoint']));
- addGroupInfo(schema, 'Polygon Settings');
+ addToSchema(schema, addCondition(pointSchema, 'model.showPoints === true', ['showPoints']));
+ addGroupInfo(schema, 'Path Points Settings');
addToSchema(schema, addCondition(mapPolygonSchema, 'model.showPolygon === true', ['showPolygon']));
addGroupInfo(schema, 'Polygon Settings');
return schema;
@@ -84,14 +86,15 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
rotationAngle: 0
}
this.settings = { ...settings, ...this.ctx.settings };
+ this.useAnchors = this.settings.usePointAsAnchor && this.settings.showPoints;
+ this.settings.fitMapBounds = true;
+ this.normalizationStep = this.settings.normalizationStep;
const subscription = this.ctx.subscriptions[Object.keys(this.ctx.subscriptions)[0]];
if (subscription) subscription.callbacks.onDataUpdated = () => {
this.historicalData = parseArray(this.ctx.data);
this.activeTrip = this.historicalData[0][0];
this.calculateIntervals();
this.timeUpdated(this.intervals[0]);
- this.mapWidget.map.updatePolylines(this.interpolatedData.map(ds => _.values(ds)));
-
this.mapWidget.map.map?.invalidateSize();
this.cd.detectChanges();
}
@@ -110,11 +113,16 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
this.calcLabel();
this.calcTooltip();
if (this.mapWidget) {
+ this.mapWidget.map.updatePolylines(this.interpolatedData.map(ds => _.values(ds)));
if (this.settings.showPolygon) {
this.mapWidget.map.updatePolygons(this.interpolatedData);
}
- if(this.settings.showPoint){
- this.mapWidget.map.updateMarkers(this.interpolatedData)
+ if (this.settings.showPoints) {
+ this.mapWidget.map.updatePoints(this.historicalData[0], this.calcTooltip);
+ this.anchors = this.historicalData[0]
+ .filter(data =>
+ this.settings.usePointAsAnchor ||
+ safeExecute(this.settings.pointAsAnchorFunction, [this.historicalData, data, data.dsIndex])).map(data => data.time);
}
this.mapWidget.map.updateMarkers(currentPosition);
}
@@ -126,23 +134,29 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
calculateIntervals() {
this.historicalData.forEach((dataSource, index) => {
this.intervals = [];
-
for (let time = dataSource[0]?.time; time < dataSource[dataSource.length - 1]?.time; time += this.normalizationStep) {
this.intervals.push(time);
}
-
this.intervals.push(dataSource[dataSource.length - 1]?.time);
this.interpolatedData[index] = this.interpolateArray(dataSource, this.intervals);
});
}
- calcTooltip() {
- const data = { ...this.activeTrip, maxTime: this.maxTime, minTime: this.minTime }
- const tooltipText: string = this.settings.useTooltipFunction ?
+ calcTooltip = (point?: FormattedData, setTooltip = true) => {
+ if (!point) {
+ point = this.activeTrip;
+ }
+ const data = { ...point, maxTime: this.maxTime, minTime: this.minTime }
+ const tooltipPattern: string = this.settings.useTooltipFunction ?
safeExecute(this.settings.tooolTipFunction, [data, this.historicalData, 0]) : this.settings.tooltipPattern;
- this.mainTooltip = this.sanitizer.sanitize(
- SecurityContext.HTML, (parseWithTranslation.parseTemplate(tooltipText, data, true)));
+ const tooltipText = parseWithTranslation.parseTemplate(tooltipPattern, data, true);
+ if (setTooltip) {
+ this.mainTooltip = this.sanitizer.sanitize(
+ SecurityContext.HTML, tooltipText);
+ this.cd.detectChanges();
+ }
+ return tooltipText;
}
calcLabel() {
diff --git a/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts
index 252720033c..a15b892349 100644
--- a/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts
+++ b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts
@@ -28,6 +28,8 @@ export class HistorySelectorComponent implements OnInit, OnChanges {
@Input() settings: HistorySelectSettings
@Input() intervals = [];
+ @Input() anchors = [];
+ @Input() useAnchors = false;
@Output() timeUpdated: EventEmitter = new EventEmitter();
@@ -56,7 +58,7 @@ export class HistorySelectorComponent implements OnInit, OnChanges {
this.interval = interval(1000 / this.speed)
.pipe(
filter(() => this.playing)).subscribe(() => {
- this.index++;
+ this.index++;
if (this.index < this.maxTimeIndex) {
this.cd.detectChanges();
this.timeUpdated.emit(this.intervals[this.index]);
@@ -91,14 +93,24 @@ export class HistorySelectorComponent implements OnInit, OnChanges {
moveNext() {
if (this.index < this.maxTimeIndex) {
- this.index++;
+ if (this.useAnchors) {
+ const anchorIndex = this.findIndex(this.intervals[this.index], this.anchors)+1;
+ this.index = this.findIndex(this.anchors[anchorIndex], this.intervals);
+ }
+ else
+ this.index++;
}
this.pause();
}
movePrev() {
if (this.index > this.minTimeIndex) {
- this.index++;
+ if (this.useAnchors) {
+ const anchorIndex = this.findIndex(this.intervals[this.index], this.anchors) - 1;
+ this.index = this.findIndex(this.anchors[anchorIndex], this.intervals);
+ }
+ else
+ this.index--;
}
this.pause();
}
@@ -113,6 +125,14 @@ export class HistorySelectorComponent implements OnInit, OnChanges {
this.pause();
}
+ findIndex(value, array: any[]) {
+ let i = 0;
+ while (array[i] < value) {
+ i++;
+ };
+ return i;
+ }
+
changeIndex() {
this.timeUpdated.emit(this.intervals[this.index]);
}