Browse Source

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>
pull/2747/head
ArtemHalushko 6 years ago
committed by GitHub
parent
commit
416d045893
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 79
      ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts
  2. 104
      ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts
  3. 2
      ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget.interface.ts
  4. 100
      ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts
  5. 1
      ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts
  6. 11
      ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts
  7. 4
      ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts
  8. 455
      ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts
  9. 2
      ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.html
  10. 51
      ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts
  11. 26
      ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts

79
ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts

@ -14,18 +14,18 @@
/// limitations under the License. /// 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-providers';
import 'leaflet.markercluster/dist/leaflet.markercluster'; import 'leaflet.markercluster/dist/leaflet.markercluster';
import { import {
FormattedData, FormattedData,
MapSettings, MapSettings,
MarkerSettings, MarkerSettings,
PolygonSettings, PolygonSettings,
PolylineSettings, PolylineSettings,
UnitedMapSettings UnitedMapSettings
} from './map-models'; } from './map-models';
import { Marker } from './markers'; import { Marker } from './markers';
import { BehaviorSubject, Observable } from 'rxjs'; import { BehaviorSubject, Observable } from 'rxjs';
@ -33,7 +33,7 @@ import { filter } from 'rxjs/operators';
import { Polyline } from './polyline'; import { Polyline } from './polyline';
import { Polygon } from './polygon'; import { Polygon } from './polygon';
import { DatasourceData } from '@app/shared/models/widget.models'; 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 { export default abstract class LeafletMap {
@ -47,6 +47,8 @@ export default abstract class LeafletMap {
bounds: L.LatLngBounds; bounds: L.LatLngBounds;
datasources: FormattedData[]; datasources: FormattedData[];
markersCluster; markersCluster;
points: FeatureGroup;
markersData = [];
protected constructor(public $container: HTMLElement, options: UnitedMapSettings) { protected constructor(public $container: HTMLElement, options: UnitedMapSettings) {
this.options = options; this.options = options;
@ -157,9 +159,9 @@ export default abstract class LeafletMap {
this.map = map; this.map = map;
if (this.options.useDefaultCenterPosition) { if (this.options.useDefaultCenterPosition) {
this.map.panTo(this.options.defaultCenterPosition); 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) { if (this.options.draggableMarker) {
this.addMarkerControl(); this.addMarkerControl();
} }
@ -200,9 +202,9 @@ export default abstract class LeafletMap {
return this.map.getCenter(); return this.map.getCenter();
} }
fitBounds(bounds: LatLngBounds, useDefaultZoom = false, padding?: LatLngTuple) { fitBounds(bounds: LatLngBounds, padding?: LatLngTuple) {
if (bounds.isValid()) { 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) { if (!this.options.fitMapBounds && this.options.defaultZoomLevel) {
this.map.setZoom(this.options.defaultZoomLevel, { animate: false }); this.map.setZoom(this.options.defaultZoomLevel, { animate: false });
if (this.options.useDefaultCenterPosition) { if (this.options.useDefaultCenterPosition) {
@ -218,9 +220,9 @@ export default abstract class LeafletMap {
} }
}); });
if (this.options.useDefaultCenterPosition) { 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 });
} }
} }
} }
@ -252,11 +254,10 @@ export default abstract class LeafletMap {
const style = currentImage ? 'background-image: url(' + currentImage.url + ');' : ''; const style = currentImage ? 'background-image: url(' + currentImage.url + ');' : '';
this.options.icon = L.divIcon({ this.options.icon = L.divIcon({
html: `<div class="arrow" html: `<div class="arrow"
style="transform: translate(-10px, -10px); style="transform: translate(-10px, -10px)
${style} rotate(${data.rotationAngle}deg);
rotate(${data.rotationAngle}deg); ${style}"><div>`
"><div>` });
})
} }
else { else {
this.options.icon = null; this.options.icon = null;
@ -268,6 +269,7 @@ export default abstract class LeafletMap {
this.createMarker(data.entityName, data, markersData, this.options as MarkerSettings); this.createMarker(data.entityName, data, markersData, this.options as MarkerSettings);
} }
}); });
this.markersData = markersData;
} }
dragMarker = (e, data?) => { dragMarker = (e, data?) => {
@ -278,7 +280,8 @@ export default abstract class LeafletMap {
private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings) { private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings) {
this.ready$.subscribe(() => { this.ready$.subscribe(() => {
const newMarker = new Marker(this.convertPosition(data), settings, data, dataSources, this.dragMarker); 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); this.markers.set(key, newMarker);
if (this.options.useClusterMarkers) { if (this.options.useClusterMarkers) {
this.markersCluster.addLayer(newMarker.leafletMarker); this.markersCluster.addLayer(newMarker.leafletMarker);
@ -313,6 +316,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<any>) { setImageAlias(alias: Observable<any>) {
} }
@ -337,15 +363,17 @@ export default abstract class LeafletMap {
this.ready$.subscribe(() => { this.ready$.subscribe(() => {
const poly = new Polyline(this.map, const poly = new Polyline(this.map,
data.map(el => this.convertPosition(el)).filter(el => !!el), data, dataSources, settings); data.map(el => this.convertPosition(el)).filter(el => !!el), data, dataSources, settings);
const bounds = this.bounds.extend(poly.leafletPoly.getBounds()); const bounds = poly.leafletPoly.getBounds();
this.fitBounds(bounds) this.fitBounds(bounds);
this.polylines.set(data[0].entityName, poly) this.polylines.set(data[0].entityName, poly);
}); });
} }
updatePolyline(key: string, data: FormattedData[], dataSources: FormattedData[], settings: PolylineSettings) { updatePolyline(key: string, data: FormattedData[], dataSources: FormattedData[], settings: PolylineSettings) {
this.ready$.subscribe(() => { 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();
}); });
} }
@ -370,7 +398,7 @@ export default abstract class LeafletMap {
createPolygon(polyData: DatasourceData, dataSources: DatasourceData[], settings: PolygonSettings) { createPolygon(polyData: DatasourceData, dataSources: DatasourceData[], settings: PolygonSettings) {
this.ready$.subscribe(() => { this.ready$.subscribe(() => {
const polygon = new Polygon(this.map, polyData, dataSources, settings); 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.fitBounds(bounds);
this.polygons.set(polyData.datasource.entityName, polygon); this.polygons.set(polyData.datasource.entityName, polygon);
}); });
@ -380,7 +408,6 @@ export default abstract class LeafletMap {
this.ready$.subscribe(() => { this.ready$.subscribe(() => {
const poly = this.polygons.get(polyData.datasource.entityName); const poly = this.polygons.get(polyData.datasource.entityName);
poly.updatePolygon(polyData.data, dataSources, settings); poly.updatePolygon(polyData.data, dataSources, settings);
this.fitBounds(poly.leafletPoly.getBounds());
}); });
} }
} }

104
ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts

@ -15,13 +15,19 @@
/// ///
import { LatLngTuple } from 'leaflet'; import { LatLngTuple } from 'leaflet';
import { Datasource } from '@app/shared/models/widget.models'; import { Datasource, JsonSettingsSchema } from '@app/shared/models/widget.models';
import { Type } from '@angular/core';
import LeafletMap from './leaflet-map';
import { OpenStreetMap, TencentMap, GoogleMap, HEREMap, ImageMap } from './providers';
import {
openstreetMapSettingsSchema, tencentMapSettingsSchema,
googleMapSettingsSchema, hereMapSettingsSchema, imageMapSettingsSchema
} from './schemes';
export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; export type GenericFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string;
export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string; export type MarkerImageFunction = (data: FormattedData, dsData: FormattedData[], dsIndex: number) => string;
export type MapSettings = { export type MapSettings = {
polygonKeyName: any;
draggableMarker: boolean; draggableMarker: boolean;
initCallback?: () => any; initCallback?: () => any;
posFunction: (origXPos, origYPos) => { x, y }; posFunction: (origXPos, origYPos) => { x, y };
@ -108,7 +114,8 @@ export interface FormattedData {
export type PolygonSettings = { export type PolygonSettings = {
showPolygon: boolean; showPolygon: boolean;
showTooltip: any; polygonKeyName: string;
polKeyName: string;// deprecated
polygonStrokeOpacity: number; polygonStrokeOpacity: number;
polygonOpacity: number; polygonOpacity: number;
polygonStrokeWeight: number; polygonStrokeWeight: number;
@ -116,12 +123,13 @@ export type PolygonSettings = {
polygonColor: string; polygonColor: string;
showPolygonTooltip: boolean; showPolygonTooltip: boolean;
autocloseTooltip: boolean; autocloseTooltip: boolean;
tooltipFunction: GenericFunction;
showTooltipAction: string; showTooltipAction: string;
tooltipAction: { [name: string]: actionsHandler }; tooltipAction: { [name: string]: actionsHandler };
tooltipPattern: string; polygonTooltipPattern: string;
useTooltipFunction: boolean; usePolygonTooltipFunction: boolean;
polygonClick: { [name: string]: actionsHandler }; polygonClick: { [name: string]: actionsHandler };
usePolygonColorFunction: boolean;
polygonTooltipFunction: GenericFunction;
polygonColorFunction?: GenericFunction; polygonColorFunction?: GenericFunction;
} }
@ -154,6 +162,88 @@ export interface HistorySelectSettings {
buttonColor: string; buttonColor: string;
} }
export type TripAnimationSttings = {
pointColor: string;
pointSize: number;
pointTooltipOnRightPanel: boolean;
}
export type actionsHandler = ($event: Event, datasource: Datasource) => void; 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<LeafletMap>,
schema: JsonSettingsSchema,
name: string
}
export const providerSets: { [key: string]: IProvider } = {
'openstreet-map': {
MapClass: OpenStreetMap,
schema: openstreetMapSettingsSchema,
name: 'openstreet-map',
},
'tencent-map': {
MapClass: TencentMap,
schema: tencentMapSettingsSchema,
name: 'tencent-map'
},
'google-map': {
MapClass: GoogleMap,
schema: googleMapSettingsSchema,
name: 'google-map'
},
here: {
MapClass: HEREMap,
schema: hereMapSettingsSchema,
name: 'here'
},
'image-map': {
MapClass: ImageMap,
schema: imageMapSettingsSchema,
name: 'image-map'
}
};
export const defaultSettings: any = {
xPosKeyName: 'xPos',
yPosKeyName: 'yPos',
markerOffsetX: 0.5,
markerOffsetY: 1,
latKeyName: 'latitude',
lngKeyName: 'longitude',
polygonKeyName: 'coordinates',
showLabel: false,
label: '${entityName}',
showTooltip: false,
useDefaultCenterPosition: false,
showTooltipAction: 'click',
autocloseTooltip: false,
showPolygon: false,
labelColor: '#000000',
color: '#FE7569',
polygonColor: '#0000ff',
polygonStrokeColor: '#fe0001',
polygonOpacity: 0.5,
polygonStrokeOpacity: 1,
polygonStrokeWeight: 1,
useLabelFunction: false,
markerImages: [],
strokeWeight: 2,
strokeOpacity: 1.0,
initCallback: () => { },
defaultZoomLevel: 8,
disableScrollZooming: false,
minZoomLevel: 16,
credentials: '',
markerClusteringSetting: null,
draggableMarker: false,
fitMapBounds: true
};
export const hereProviders = [
'HERE.normalDay',
'HERE.normalNight',
'HERE.hybridDay',
'HERE.terrainDay']

2
ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget.interface.ts

@ -26,7 +26,7 @@ export interface MapWidgetInterface {
export interface MapWidgetStaticInterface { export interface MapWidgetStaticInterface {
settingsSchema(mapProvider?: MapProviders, drawRoutes?: boolean): JsonSettingsSchema; settingsSchema(mapProvider?: MapProviders, drawRoutes?: boolean): JsonSettingsSchema;
getProvidersSchema(mapProvider?: MapProviders): JsonSettingsSchema getProvidersSchema(mapProvider?: MapProviders, ignoreImageMap?: boolean): JsonSettingsSchema
dataKeySettingsSchema(): object; dataKeySettingsSchema(): object;
actionSources(): object; actionSources(): object;
} }

100
ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts

@ -14,23 +14,17 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { MapProviders, UnitedMapSettings } from './map-models'; import { MapProviders, UnitedMapSettings, providerSets, hereProviders, defaultSettings } from './map-models';
import LeafletMap from './leaflet-map'; import LeafletMap from './leaflet-map';
import { import {
openstreetMapSettingsSchema,
googleMapSettingsSchema,
imageMapSettingsSchema,
tencentMapSettingsSchema,
commonMapSettingsSchema, commonMapSettingsSchema,
routeMapSettingsSchema, routeMapSettingsSchema,
markerClusteringSettingsSchema, markerClusteringSettingsSchema,
markerClusteringSettingsSchemaLeaflet, markerClusteringSettingsSchemaLeaflet,
hereMapSettingsSchema,
mapProviderSchema, mapProviderSchema,
mapPolygonSchema mapPolygonSchema
} from './schemes'; } from './schemes';
import { MapWidgetStaticInterface, MapWidgetInterface } from './map-widget.interface'; import { MapWidgetStaticInterface, MapWidgetInterface } from './map-widget.interface';
import { OpenStreetMap, TencentMap, GoogleMap, HEREMap, ImageMap } from './providers';
import { initSchema, addToSchema, mergeSchemes, addCondition, addGroupInfo } from '@core/schema-utils'; import { initSchema, addToSchema, mergeSchemes, addCondition, addGroupInfo } from '@core/schema-utils';
import { of, Subject } from 'rxjs'; import { of, Subject } from 'rxjs';
import { WidgetContext } from '@app/modules/home/models/widget-component.models'; import { WidgetContext } from '@app/modules/home/models/widget-component.models';
@ -39,7 +33,6 @@ import { JsonSettingsSchema, WidgetActionDescriptor, DatasourceType, widgetType,
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models'; import { AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models';
import { AttributeService } from '@core/http/attribute.service'; import { AttributeService } from '@core/http/attribute.service';
import { Type } from '@angular/core';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
@ -85,11 +78,19 @@ export class MapWidgetController implements MapWidgetInterface {
return {}; return {};
} }
public static getProvidersSchema(mapProvider: MapProviders) { public static getProvidersSchema(mapProvider: MapProviders, ignoreImageMap = false) {
mapProviderSchema.schema.properties.provider.default = mapProvider; if (mapProvider)
return mergeSchemes([mapProviderSchema, mapProviderSchema.schema.properties.provider.default = mapProvider;
const providerSchema = mapProviderSchema;
if (ignoreImageMap) {
providerSchema.form[0].items = providerSchema.form[0]?.items.filter(item => item.value !== 'image-map');
}
return mergeSchemes([providerSchema,
...Object.keys(providerSets)?.map( ...Object.keys(providerSets)?.map(
(key: string) => { const setting = providerSets[key]; return addCondition(setting?.schema, `model.provider === '${setting.name}'`) })]); (key: string) => {
const setting = providerSets[key];
return addCondition(setting?.schema, `model.provider === '${setting.name}'`);
})]);
} }
public static settingsSchema(mapProvider: MapProviders, drawRoutes: boolean): JsonSettingsSchema { public static settingsSchema(mapProvider: MapProviders, drawRoutes: boolean): JsonSettingsSchema {
@ -218,6 +219,7 @@ export class MapWidgetController implements MapWidgetInterface {
polygonColorFunction: parseFunction(settings.polygonColorFunction, functionParams), polygonColorFunction: parseFunction(settings.polygonColorFunction, functionParams),
markerImageFunction: parseFunction(settings.markerImageFunction, ['data', 'images', 'dsData', 'dsIndex']), markerImageFunction: parseFunction(settings.markerImageFunction, ['data', 'images', 'dsData', 'dsIndex']),
labelColor: this.ctx.widgetConfig.color, labelColor: this.ctx.widgetConfig.color,
polygonKeyName: settings.polKeyName ? settings.polKeyName : settings.polygonKeyName,
tooltipPattern: settings.tooltipPattern || tooltipPattern: settings.tooltipPattern ||
'<b>${entityName}</b><br/><br/><b>Latitude:</b> ${' + '<b>${entityName}</b><br/><br/><b>Latitude:</b> ${' +
settings.latKeyName + ':7}<br/><b>Longitude:</b> ${' + settings.lngKeyName + ':7}', settings.latKeyName + ':7}<br/><b>Longitude:</b> ${' + settings.lngKeyName + ':7}',
@ -295,78 +297,4 @@ export class MapWidgetController implements MapWidgetInterface {
export let TbMapWidgetV2: MapWidgetStaticInterface = MapWidgetController; export let TbMapWidgetV2: MapWidgetStaticInterface = MapWidgetController;
interface IProvider {
MapClass: Type<LeafletMap>,
schema: JsonSettingsSchema,
name: string
}
export const providerSets: { [key: string]: IProvider } = {
'openstreet-map': {
MapClass: OpenStreetMap,
schema: openstreetMapSettingsSchema,
name: 'openstreet-map',
},
'tencent-map': {
MapClass: TencentMap,
schema: tencentMapSettingsSchema,
name: 'tencent-map'
},
'google-map': {
MapClass: GoogleMap,
schema: googleMapSettingsSchema,
name: 'google-map'
},
here: {
MapClass: HEREMap,
schema: hereMapSettingsSchema,
name: 'here'
},
'image-map': {
MapClass: ImageMap,
schema: imageMapSettingsSchema,
name: 'image-map'
}
};
export const defaultSettings: any = {
xPosKeyName: 'xPos',
yPosKeyName: 'yPos',
markerOffsetX: 0.5,
markerOffsetY: 1,
latKeyName: 'latitude',
lngKeyName: 'longitude',
polygonKeyName: 'coordinates',
showLabel: false,
label: '${entityName}',
showTooltip: false,
useDefaultCenterPosition: false,
showTooltipAction: 'click',
autocloseTooltip: false,
showPolygon: false,
labelColor: '#000000',
color: '#FE7569',
polygonColor: '#0000ff',
polygonStrokeColor: '#fe0001',
polygonOpacity: 0.5,
polygonStrokeOpacity: 1,
polygonStrokeWeight: 1,
useLabelFunction: false,
markerImages: [],
strokeWeight: 2,
strokeOpacity: 1.0,
initCallback: () => { },
defaultZoomLevel: 8,
disableScrollZooming: false,
minZoomLevel: 16,
credentials: '',
markerClusteringSetting: null,
draggableMarker: false,
fitMapBounds: true
};
export const hereProviders = [
'HERE.normalDay',
'HERE.normalNight',
'HERE.hybridDay',
'HERE.terrainDay']

1
ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts

@ -120,7 +120,6 @@ export class Marker {
[this.data, this.settings.markerImages, this.dataSources, this.data.dsIndex]) : this.settings.currentImage; [this.data, this.settings.markerImages, this.dataSources, this.data.dsIndex]) : this.settings.currentImage;
const currentColor = tinycolor(this.settings.useColorFunction ? safeExecute(this.settings.colorFunction, const currentColor = tinycolor(this.settings.useColorFunction ? safeExecute(this.settings.colorFunction,
[this.data, this.dataSources, this.data.dsIndex]) : this.settings.color).toHex(); [this.data, this.dataSources, this.data.dsIndex]) : this.settings.color).toHex();
if (currentImage && currentImage.url) { if (currentImage && currentImage.url) {
aspectCache(currentImage.url).subscribe( aspectCache(currentImage.url).subscribe(
(aspect) => { (aspect) => {

11
ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts

@ -53,8 +53,9 @@ export class Polygon {
} }
updateTooltip(data: DatasourceData) { updateTooltip(data: DatasourceData) {
const pattern = this.settings.useTooltipFunction ? const pattern = this.settings.usePolygonTooltipFunction ?
safeExecute(this.settings.tooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) : this.settings.tooltipPattern; safeExecute(this.settings.polygonTooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) :
this.settings.polygonTooltipPattern;
this.tooltip.setContent(parseWithTranslation.parseTemplate(pattern, data, true)); this.tooltip.setContent(parseWithTranslation.parseTemplate(pattern, data, true));
} }
@ -71,10 +72,12 @@ export class Polygon {
this.map.removeLayer(this.leafletPoly); this.map.removeLayer(this.leafletPoly);
} }
updatePolygonColor(settings) { updatePolygonColor(settings: PolygonSettings) {
const color = settings.usePolygonColorFunction ?
safeExecute(settings.polygonColorFunction, [this.data, this.dataSources, this.data.dsIndex]) : settings.polygonColor;
const style: L.PathOptions = { const style: L.PathOptions = {
fill: true, fill: true,
fillColor: settings.polygonColor, fillColor: color,
color: settings.polygonStrokeColor, color: settings.polygonStrokeColor,
weight: settings.polygonStrokeWeight, weight: settings.polygonStrokeWeight,
fillOpacity: settings.polygonOpacity, fillOpacity: settings.polygonOpacity,

4
ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts

@ -109,13 +109,13 @@ export class ImageMap extends LeafletMap {
lastCenterPos.y /= prevHeight; lastCenterPos.y /= prevHeight;
this.updateBounds(updateImage, lastCenterPos); this.updateBounds(updateImage, lastCenterPos);
this.map.invalidateSize(true); this.map.invalidateSize(true);
// TODO: need add update marker position this.updateMarkers(this.markersData);
} }
} }
} }
} }
fitBounds(bounds: LatLngBounds, useDefaultZoom = false, padding?: LatLngTuple) { } fitBounds(bounds: LatLngBounds, padding?: LatLngTuple) { }
initMap(updateImage?) { initMap(updateImage?) {
if (!this.map && this.aspect > 0) { if (!this.map && this.aspect > 0) {

455
ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts

@ -546,6 +546,20 @@ export const mapPolygonSchema =
type: 'boolean', type: 'boolean',
default: false default: false
}, },
polygonTooltipPattern: {
title: 'Tooltip (for ex. \'Text ${keyName} units.\' or <link-act name=\'my-action\'>Link text</link-act>\')',
type: 'string',
default: '<b>${entityName}</b><br/><br/><b>TimeStamp:</b> ${ts:7}'
},
usePolygonTooltipFunction: {
title: 'Use polygon tooltip function',
type: 'boolean',
default: false
},
polygonTooltipFunction: {
title: 'Polygon tooltip function: f(data, dsData, dsIndex)',
type: 'string'
},
usePolygonColorFunction: { usePolygonColorFunction: {
title: 'Use polygon color function', title: 'Use polygon color function',
type: 'boolean', type: 'boolean',
@ -570,7 +584,15 @@ export const mapPolygonSchema =
key: 'polygonStrokeColor', key: 'polygonStrokeColor',
type: 'color' type: 'color'
}, },
'polygonStrokeOpacity', 'polygonStrokeWeight', 'usePolygonColorFunction', 'showPolygonTooltip', 'polygonStrokeOpacity', 'polygonStrokeWeight', 'showPolygonTooltip',
{
key: 'polygonTooltipPattern',
type: 'textarea'
}, 'usePolygonTooltipFunction', {
key: 'polygonTooltipFunction',
type: 'javascript'
},
'usePolygonColorFunction',
{ {
key: 'polygonColorFunction', key: 'polygonColorFunction',
type: 'javascript' type: 'javascript'
@ -710,6 +732,161 @@ export const imageMapSettingsSchema =
] ]
}; };
export const pathSchema =
{
schema: {
title: 'Trip Animation Path Configuration',
type: 'object',
properties: {
color: {
title: 'Path color',
type: 'string'
},
strokeWeight: {
title: 'Stroke weight',
type: 'number',
default: 2
},
strokeOpacity: {
title: 'Stroke opacity',
type: 'number',
default: 1
},
useColorFunction: {
title: 'Use path color function',
type: 'boolean',
default: false
},
colorFunction: {
title: 'Path color function: f(data, dsData, dsIndex)',
type: 'string'
},
usePolylineDecorator: {
title: 'Use path decorator',
type: 'boolean',
default: false
},
decoratorSymbol: {
title: 'Decorator symbol',
type: 'string',
default: 'arrowHead'
},
decoratorSymbolSize: {
title: 'Decorator symbol size (px)',
type: 'number',
default: 10
},
useDecoratorCustomColor: {
title: 'Use path decorator custom color',
type: 'boolean',
default: false
},
decoratorCustomColor: {
title: 'Decorator custom color',
type: 'string',
default: '#000'
},
decoratorOffset: {
title: 'Decorator offset',
type: 'string',
default: '20px'
},
endDecoratorOffset: {
title: 'End decorator offset',
type: 'string',
default: '20px'
},
decoratorRepeat: {
title: 'Decorator repeat',
type: 'string',
default: '20px'
}
},
required: []
},
form: [
{
key: 'color',
type: 'color'
}, 'useColorFunction', {
key: 'colorFunction',
type: 'javascript'
}, 'strokeWeight', 'strokeOpacity',
'usePolylineDecorator', {
key: 'decoratorSymbol',
type: 'rc-select',
multiple: false,
items: [{
value: 'arrowHead',
label: 'Arrow'
}, {
value: 'dash',
label: 'Dash'
}]
}, 'decoratorSymbolSize', 'useDecoratorCustomColor', {
key: 'decoratorCustomColor',
type: 'color'
}, {
key: 'decoratorOffset',
type: 'textarea'
}, {
key: 'endDecoratorOffset',
type: 'textarea'
}, {
key: 'decoratorRepeat',
type: 'textarea'
}
]
};
export const pointSchema =
{
schema: {
title: 'Trip Animation Path Configuration',
type: 'object',
properties: {
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: []
},
form: [
'showPoints', {
key: 'pointColor',
type: 'color'
}, 'pointSize', 'usePointAsAnchor', {
key: 'pointAsAnchorFunction',
type: 'javascript'
}, 'pointTooltipOnRightPanel',
]
};
export const mapProviderSchema = export const mapProviderSchema =
{ {
schema: { schema: {
@ -755,7 +932,6 @@ export const mapProviderSchema =
] ]
}; };
export const tripAnimationSchema = { export const tripAnimationSchema = {
schema: { schema: {
title: 'Openstreet Map Configuration', title: 'Openstreet Map Configuration',
@ -776,11 +952,6 @@ export const tripAnimationSchema = {
type: 'string', type: 'string',
default: 'longitude' default: 'longitude'
}, },
polKeyName: {
title: 'Polygon key name',
type: 'string',
default: 'coordinates'
},
showLabel: { showLabel: {
title: 'Show label', title: 'Show label',
type: 'boolean', type: 'boolean',
@ -834,148 +1005,6 @@ export const tripAnimationSchema = {
title: 'Tooltip function: f(data, dsData, dsIndex)', title: 'Tooltip function: f(data, dsData, dsIndex)',
type: 'string' type: 'string'
}, },
color: {
title: 'Path color',
type: 'string'
},
strokeWeight: {
title: 'Stroke weight',
type: 'number',
default: 2
},
strokeOpacity: {
title: 'Stroke opacity',
type: 'number',
default: 1
},
useColorFunction: {
title: 'Use path color function',
type: 'boolean',
default: false
},
colorFunction: {
title: 'Path color function: f(data, dsData, dsIndex)',
type: 'string'
},
usePolylineDecorator: {
title: 'Use path decorator',
type: 'boolean',
default: false
},
decoratorSymbol: {
title: 'Decorator symbol',
type: 'string',
default: 'arrowHead'
},
decoratorSymbolSize: {
title: 'Decorator symbol size (px)',
type: 'number',
default: 10
},
useDecoratorCustomColor: {
title: 'Use path decorator custom color',
type: 'boolean',
default: false
},
decoratorCustomColor: {
title: 'Decorator custom color',
type: 'string',
default: '#000'
},
decoratorOffset: {
title: 'Decorator offset',
type: 'string',
default: '20px'
},
endDecoratorOffset: {
title: 'End decorator offset',
type: 'string',
default: '20px'
},
decoratorRepeat: {
title: 'Decorator repeat',
type: 'string',
default: '20px'
},
showPolygon: {
title: 'Show polygon',
type: 'boolean',
default: false
},
polygonTooltipPattern: {
title: 'Tooltip (for ex. \'Text ${keyName} units.\' or <link-act name=\'my-action\'>Link text</link-act>\')',
type: 'string',
default: '<b>${entityName}</b><br/><br/><b>TimeStamp:</b> ${ts:7}'
},
usePolygonTooltipFunction: {
title: 'Use polygon tooltip function',
type: 'boolean',
default: false
},
polygonTooltipFunction: {
title: 'Polygon tooltip function: f(data, dsData, dsIndex)',
type: 'string'
},
polygonColor: {
title: 'Polygon color',
type: 'string'
},
polygonOpacity: {
title: 'Polygon opacity',
type: 'number',
default: 0.5
},
polygonStrokeColor: {
title: 'Polygon border color',
type: 'string'
},
polygonStrokeOpacity: {
title: 'Polygon border opacity',
type: 'number',
default: 1
},
polygonStrokeWeight: {
title: 'Polygon border weight',
type: 'number',
default: 1
},
usePolygonColorFunction: {
title: 'Use polygon color function',
type: 'boolean',
default: false
},
polygonColorFunction: {
title: 'Polygon Color function: f(data, dsData, dsIndex)',
type: 'string'
},
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
},
autocloseTooltip: { autocloseTooltip: {
title: 'Auto-close point popup', title: 'Auto-close point popup',
type: 'boolean', type: 'boolean',
@ -1015,111 +1044,35 @@ export const tripAnimationSchema = {
}, },
required: [] required: []
}, },
form: [{ form: ['normalizationStep', 'latKeyName', 'lngKeyName', 'showLabel', 'label', 'useLabelFunction', {
key: 'mapProvider',
type: 'rc-select',
multiple: false,
items: [{
value: 'OpenStreetMap.Mapnik',
label: 'OpenStreetMap.Mapnik (Default)'
}, {
value: 'OpenStreetMap.BlackAndWhite',
label: 'OpenStreetMap.BlackAndWhite'
}, {
value: 'OpenStreetMap.HOT',
label: 'OpenStreetMap.HOT'
}, {
value: 'Esri.WorldStreetMap',
label: 'Esri.WorldStreetMap'
}, {
value: 'Esri.WorldTopoMap',
label: 'Esri.WorldTopoMap'
}, {
value: 'CartoDB.Positron',
label: 'CartoDB.Positron'
}, {
value: 'CartoDB.DarkMatter',
label: 'CartoDB.DarkMatter'
}]
}, 'normalizationStep', 'latKeyName', 'lngKeyName', 'polKeyName', 'showLabel', 'label', 'useLabelFunction', {
key: 'labelFunction', key: 'labelFunction',
type: 'javascript' type: 'javascript'
}, 'showTooltip', { }, 'showTooltip', {
key: 'tooltipColor', key: 'tooltipColor',
type: 'color' type: 'color'
}, { }, {
key: 'tooltipFontColor', key: 'tooltipFontColor',
type: 'color' type: 'color'
}, 'tooltipOpacity', { }, 'tooltipOpacity', {
key: 'tooltipPattern', key: 'tooltipPattern',
type: 'textarea' type: 'textarea'
}, 'useTooltipFunction', { }, 'useTooltipFunction', {
key: 'tooltipFunction', key: 'tooltipFunction',
type: 'javascript' type: 'javascript'
}, { }, 'autocloseTooltip', {
key: 'color', key: 'markerImage',
type: 'color' type: 'image'
}, 'useColorFunction', { }, 'markerImageSize', 'rotationAngle', 'useMarkerImageFunction',
key: 'colorFunction', {
type: 'javascript' key: 'markerImageFunction',
}, 'usePolylineDecorator', { type: 'javascript'
key: 'decoratorSymbol',
type: 'rc-select',
multiple: false,
items: [{
value: 'arrowHead',
label: 'Arrow'
}, { }, {
value: 'dash', key: 'markerImages',
label: 'Dash' items: [
{
key: 'markerImages[]',
type: 'image'
}
]
}] }]
}, 'decoratorSymbolSize', 'useDecoratorCustomColor', {
key: 'decoratorCustomColor',
type: 'color'
}, {
key: 'decoratorOffset',
type: 'textarea'
}, {
key: 'endDecoratorOffset',
type: 'textarea'
}, {
key: 'decoratorRepeat',
type: 'textarea'
}, 'strokeWeight', 'strokeOpacity', 'showPolygon', {
key: 'polygonTooltipPattern',
type: 'textarea'
}, 'usePolygonTooltipFunction', {
key: 'polygonTooltipFunction',
type: 'javascript'
}, {
key: 'polygonColor',
type: 'color'
}, 'polygonOpacity', {
key: 'polygonStrokeColor',
type: 'color'
}, 'polygonStrokeOpacity', 'polygonStrokeWeight', 'usePolygonColorFunction', {
key: 'polygonColorFunction',
type: 'javascript'
}, 'showPoints', {
key: 'pointColor',
type: 'color'
}, 'pointSize', 'usePointAsAnchor', {
key: 'pointAsAnchorFunction',
type: 'javascript'
}, 'pointTooltipOnRightPanel', 'autocloseTooltip', {
key: 'markerImage',
type: 'image'
}, 'markerImageSize', 'rotationAngle', 'useMarkerImageFunction',
{
key: 'markerImageFunction',
type: 'javascript'
}, {
key: 'markerImages',
items: [
{
key: 'markerImages[]',
type: 'image'
}
]
}]
} }

2
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}"> [ngStyle]="{'background-color': settings.tooltipColor, 'opacity': settings.tooltipOpacity, 'color': settings.tooltipFontColor}">
</div> </div>
</div> </div>
<tb-history-selector *ngIf="historicalData" [settings]="settings" [intervals]="intervals" <tb-history-selector *ngIf="historicalData" [settings]="settings" [intervals]="intervals" [anchors]="anchors" [useAnchors]="useAnchors"
(timeUpdated)="timeUpdated($event)"></tb-history-selector> (timeUpdated)="timeUpdated($event)"></tb-history-selector>
</div> </div>

51
ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts

@ -21,9 +21,9 @@ import { interpolateOnPointSegment } from 'leaflet-geometryutil';
import { AfterViewInit, ChangeDetectorRef, Component, Input, OnInit, SecurityContext, ViewChild } from '@angular/core'; import { AfterViewInit, ChangeDetectorRef, Component, Input, OnInit, SecurityContext, ViewChild } from '@angular/core';
import { MapWidgetController, TbMapWidgetV2 } from '../lib/maps/map-widget2'; 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 } from '@app/core/schema-utils'; import { initSchema, addToSchema, addGroupInfo, addCondition } from '@app/core/schema-utils';
import { tripAnimationSchema } from '../lib/maps/schemes'; import { tripAnimationSchema, mapPolygonSchema, pathSchema, pointSchema } from '../lib/maps/schemes';
import { DomSanitizer } from '@angular/platform-browser'; import { DomSanitizer } from '@angular/platform-browser';
import { WidgetContext } from '@app/modules/home/models/widget-component.models'; import { WidgetContext } from '@app/modules/home/models/widget-component.models';
import { findAngle, getRatio, parseArray, parseWithTranslation, safeExecute } from '../lib/maps/maps-utils'; import { findAngle, getRatio, parseArray, parseWithTranslation, safeExecute } from '../lib/maps/maps-utils';
@ -58,13 +58,21 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
label; label;
minTime; minTime;
maxTime; maxTime;
anchors = [];
useAnchors = false;
static getSettingsSchema(): JsonSettingsSchema { static getSettingsSchema(): JsonSettingsSchema {
const schema = initSchema(); const schema = initSchema();
addToSchema(schema, TbMapWidgetV2.getProvidersSchema()); addToSchema(schema, TbMapWidgetV2.getProvidersSchema(null, true));
addGroupInfo(schema, 'Map Provider Settings'); addGroupInfo(schema, 'Map Provider Settings');
addToSchema(schema, tripAnimationSchema); addToSchema(schema, tripAnimationSchema);
addGroupInfo(schema, 'Trip Animation Settings'); addGroupInfo(schema, 'Trip Animation Settings');
addToSchema(schema, pathSchema);
addGroupInfo(schema, 'Path 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; return schema;
} }
@ -78,14 +86,15 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
rotationAngle: 0 rotationAngle: 0
} }
this.settings = { ...settings, ...this.ctx.settings }; 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]]; const subscription = this.ctx.subscriptions[Object.keys(this.ctx.subscriptions)[0]];
if (subscription) subscription.callbacks.onDataUpdated = (updated) => { if (subscription) subscription.callbacks.onDataUpdated = () => {
this.historicalData = parseArray(this.ctx.data); this.historicalData = parseArray(this.ctx.data);
this.activeTrip = this.historicalData[0][0]; this.activeTrip = this.historicalData[0][0];
this.calculateIntervals(); this.calculateIntervals();
this.timeUpdated(this.intervals[0]); this.timeUpdated(this.intervals[0]);
this.mapWidget.map.updatePolylines(this.interpolatedData.map(ds => _.values(ds)));
this.mapWidget.map.map?.invalidateSize(); this.mapWidget.map.map?.invalidateSize();
this.cd.detectChanges(); this.cd.detectChanges();
} }
@ -104,9 +113,17 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
this.calcLabel(); this.calcLabel();
this.calcTooltip(); this.calcTooltip();
if (this.mapWidget) { if (this.mapWidget) {
this.mapWidget.map.updatePolylines(this.interpolatedData.map(ds => _.values(ds)));
if (this.settings.showPolygon) { if (this.settings.showPolygon) {
this.mapWidget.map.updatePolygons(this.interpolatedData); this.mapWidget.map.updatePolygons(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); this.mapWidget.map.updateMarkers(currentPosition);
} }
} }
@ -117,23 +134,29 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
calculateIntervals() { calculateIntervals() {
this.historicalData.forEach((dataSource, index) => { this.historicalData.forEach((dataSource, index) => {
this.intervals = []; this.intervals = [];
for (let time = dataSource[0]?.time; time < dataSource[dataSource.length - 1]?.time; time += this.normalizationStep) { for (let time = dataSource[0]?.time; time < dataSource[dataSource.length - 1]?.time; time += this.normalizationStep) {
this.intervals.push(time); this.intervals.push(time);
} }
this.intervals.push(dataSource[dataSource.length - 1]?.time); this.intervals.push(dataSource[dataSource.length - 1]?.time);
this.interpolatedData[index] = this.interpolateArray(dataSource, this.intervals); this.interpolatedData[index] = this.interpolateArray(dataSource, this.intervals);
}); });
} }
calcTooltip() { calcTooltip = (point?: FormattedData, setTooltip = true) => {
const data = { ...this.activeTrip, maxTime: this.maxTime, minTime: this.minTime } if (!point) {
const tooltipText: string = this.settings.useTooltipFunction ? 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; safeExecute(this.settings.tooolTipFunction, [data, this.historicalData, 0]) : this.settings.tooltipPattern;
this.mainTooltip = this.sanitizer.sanitize( const tooltipText = parseWithTranslation.parseTemplate(tooltipPattern, data, true);
SecurityContext.HTML, (parseWithTranslation.parseTemplate(tooltipText, data, true))); if (setTooltip) {
this.mainTooltip = this.sanitizer.sanitize(
SecurityContext.HTML, tooltipText);
this.cd.detectChanges();
}
return tooltipText;
} }
calcLabel() { calcLabel() {

26
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() settings: HistorySelectSettings
@Input() intervals = []; @Input() intervals = [];
@Input() anchors = [];
@Input() useAnchors = false;
@Output() timeUpdated: EventEmitter<number> = new EventEmitter(); @Output() timeUpdated: EventEmitter<number> = new EventEmitter();
@ -56,7 +58,7 @@ export class HistorySelectorComponent implements OnInit, OnChanges {
this.interval = interval(1000 / this.speed) this.interval = interval(1000 / this.speed)
.pipe( .pipe(
filter(() => this.playing)).subscribe(() => { filter(() => this.playing)).subscribe(() => {
this.index++; this.index++;
if (this.index < this.maxTimeIndex) { if (this.index < this.maxTimeIndex) {
this.cd.detectChanges(); this.cd.detectChanges();
this.timeUpdated.emit(this.intervals[this.index]); this.timeUpdated.emit(this.intervals[this.index]);
@ -91,14 +93,24 @@ export class HistorySelectorComponent implements OnInit, OnChanges {
moveNext() { moveNext() {
if (this.index < this.maxTimeIndex) { 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(); this.pause();
} }
movePrev() { movePrev() {
if (this.index > this.minTimeIndex) { 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(); this.pause();
} }
@ -113,6 +125,14 @@ export class HistorySelectorComponent implements OnInit, OnChanges {
this.pause(); this.pause();
} }
findIndex(value, array: any[]) {
let i = 0;
while (array[i] < value) {
i++;
};
return i;
}
changeIndex() { changeIndex() {
this.timeUpdated.emit(this.intervals[this.index]); this.timeUpdated.emit(this.intervals[this.index]);
} }

Loading…
Cancel
Save