Add support of latest values for timeseries map widgets

This commit is contained in:
Igor Kulikov 2022-03-28 16:25:07 +03:00
parent 1b95f32b19
commit c35d60abd9
6 changed files with 73 additions and 31 deletions

File diff suppressed because one or more lines are too long

View File

@ -471,6 +471,19 @@ export function flatFormattedData(input: FormattedData[]): FormattedData {
return result; return result;
} }
export function mergeFormattedData(first: FormattedData[], second: FormattedData[]): FormattedData[] {
const merged = first.concat(second);
return _(merged).groupBy(el => el.$datasource)
.values().value().map((formattedDataArray, i) => {
let res = formattedDataArray[0];
if (formattedDataArray.length > 1) {
const toMerge = formattedDataArray[1];
res = {...res, ...toMerge};
}
return res;
});
}
export function processDataPattern(pattern: string, data: FormattedData): Array<ReplaceInfo> { export function processDataPattern(pattern: string, data: FormattedData): Array<ReplaceInfo> {
const replaceInfo: Array<ReplaceInfo> = []; const replaceInfo: Array<ReplaceInfo> = [];
try { try {

View File

@ -48,7 +48,7 @@ import {
formattedDataFormDatasourceData, formattedDataFormDatasourceData,
isDefinedAndNotNull, isDefinedAndNotNull,
isNotEmptyStr, isNotEmptyStr,
isString, safeExecute isString, mergeFormattedData, safeExecute
} from '@core/utils'; } from '@core/utils';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { import {
@ -644,13 +644,25 @@ export default abstract class LeafletMap {
} }
updateData(drawRoutes: boolean, showPolygon: boolean) { updateData(drawRoutes: boolean, showPolygon: boolean) {
const data = this.ctx.data;
let formattedData = formattedDataFormDatasourceData(data);
if (this.ctx.latestData && this.ctx.latestData.length) {
const formattedLatestData = formattedDataFormDatasourceData(this.ctx.latestData);
formattedData = mergeFormattedData(formattedData, formattedLatestData);
}
let polyData: FormattedData[][] = null;
if (drawRoutes) {
polyData = formattedDataArrayFromDatasourceData(data);
}
this.updateFromData(drawRoutes, showPolygon, formattedData, polyData);
}
updateFromData(drawRoutes: boolean, showPolygon: boolean, formattedData: FormattedData[],
polyData: FormattedData[][], markerClickCallback?: any) {
this.drawRoutes = drawRoutes; this.drawRoutes = drawRoutes;
this.showPolygon = showPolygon; this.showPolygon = showPolygon;
if (this.map) { if (this.map) {
const data = this.ctx.data;
const formattedData = formattedDataFormDatasourceData(data);
if (drawRoutes) { if (drawRoutes) {
const polyData = formattedDataArrayFromDatasourceData(data);
this.updatePolylines(polyData, formattedData, false); this.updatePolylines(polyData, formattedData, false);
} }
if (showPolygon) { if (showPolygon) {
@ -659,7 +671,7 @@ export default abstract class LeafletMap {
if (this.options.showCircle) { if (this.options.showCircle) {
this.updateCircle(formattedData, false); this.updateCircle(formattedData, false);
} }
this.updateMarkers(formattedData, false); this.updateMarkers(formattedData, false, markerClickCallback);
this.updateBoundsInternal(); this.updateBoundsInternal();
if (this.options.draggableMarker || this.editPolygons || this.editCircle) { if (this.options.draggableMarker || this.editPolygons || this.editCircle) {
let foundEntityWithLocation = false; let foundEntityWithLocation = false;

View File

@ -221,7 +221,7 @@ export interface MapImage {
update?: boolean; update?: boolean;
} }
export interface TripAnimationSettings extends PolygonSettings { export interface TripAnimationSettings extends PolygonSettings, CircleSettings {
showPoints: boolean; showPoints: boolean;
pointColor: string; pointColor: string;
pointSize: number; pointSize: number;

View File

@ -226,8 +226,12 @@ export class MapWidgetController implements MapWidgetInterface {
id: e.$datasource.entityId id: e.$datasource.entityId
}; };
let dataKeys = e.$datasource.dataKeys;
if (e.$datasource.latestDataKeys) {
dataKeys = dataKeys.concat(e.$datasource.latestDataKeys);
}
for (const dataKeyName of Object.keys(values)) { for (const dataKeyName of Object.keys(values)) {
for (const key of e.$datasource.dataKeys) { for (const key of dataKeys) {
if (dataKeyName === key.name) { if (dataKeyName === key.name) {
const value = { const value = {
key: key.name, key: key.name,
@ -317,6 +321,10 @@ export class MapWidgetController implements MapWidgetInterface {
this.map.setLoading(false); this.map.setLoading(false);
} }
latestDataUpdate() {
this.map.updateData(this.drawRoutes, this.settings.showPolygon);
}
resize() { resize() {
this.map.onResize(); this.map.onResize();
this.map?.invalidateSize(); this.map?.invalidateSize();

View File

@ -30,6 +30,7 @@ import {
import { MapProviders, TripAnimationSettings } from '@home/components/widget/lib/maps/map-models'; import { MapProviders, TripAnimationSettings } from '@home/components/widget/lib/maps/map-models';
import { addCondition, addGroupInfo, addToSchema, initSchema } from '@app/core/schema-utils'; import { addCondition, addGroupInfo, addToSchema, initSchema } from '@app/core/schema-utils';
import { import {
mapCircleSchema,
mapPolygonSchema, mapPolygonSchema,
pathSchema, pathSchema,
pointSchema, pointSchema,
@ -48,9 +49,9 @@ import { FormattedData, JsonSettingsSchema, WidgetConfig } from '@shared/models/
import moment from 'moment'; import moment from 'moment';
import { import {
deepClone, deepClone,
formattedDataArrayFromDatasourceData, formattedDataArrayFromDatasourceData, formattedDataFormDatasourceData,
isDefined, isDefined,
isUndefined, isUndefined, mergeFormattedData,
parseFunction, parseFunction,
safeExecute safeExecute
} from '@core/utils'; } from '@core/utils';
@ -82,6 +83,8 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
normalizationStep: number; normalizationStep: number;
interpolatedTimeData: {[time: number]: FormattedData}[] = []; interpolatedTimeData: {[time: number]: FormattedData}[] = [];
formattedInterpolatedTimeData: FormattedData[][] = []; formattedInterpolatedTimeData: FormattedData[][] = [];
formattedCurrentPosition: FormattedData[] = [];
formattedLatestData: FormattedData[] = [];
widgetConfig: WidgetConfig; widgetConfig: WidgetConfig;
settings: TripAnimationSettings; settings: TripAnimationSettings;
mainTooltips = []; mainTooltips = [];
@ -104,11 +107,10 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
addGroupInfo(schema, 'Path Settings'); addGroupInfo(schema, 'Path Settings');
addToSchema(schema, addCondition(pointSchema, 'model.showPoints === true', ['showPoints'])); addToSchema(schema, addCondition(pointSchema, 'model.showPoints === true', ['showPoints']));
addGroupInfo(schema, 'Path Points Settings'); addGroupInfo(schema, 'Path Points Settings');
const mapPolygonSchemaWithoutEdit = deepClone(mapPolygonSchema); addToSchema(schema, addCondition(mapPolygonSchema, 'model.showPolygon === true', ['showPolygon']));
delete mapPolygonSchemaWithoutEdit.schema.properties.editablePolygon;
mapPolygonSchemaWithoutEdit.form.splice(mapPolygonSchemaWithoutEdit.form.indexOf('editablePolygon'), 1);
addToSchema(schema, addCondition(mapPolygonSchemaWithoutEdit, 'model.showPolygon === true', ['showPolygon']));
addGroupInfo(schema, 'Polygon Settings'); addGroupInfo(schema, 'Polygon Settings');
addToSchema(schema, addCondition(mapCircleSchema, 'model.showCircle === true', ['showCircle']));
addGroupInfo(schema, 'Circle Settings');
return schema; return schema;
} }
@ -122,7 +124,6 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
rotationAngle: 0 rotationAngle: 0
}; };
this.settings = { ...settings, ...this.ctx.settings }; this.settings = { ...settings, ...this.ctx.settings };
this.settings.editablePolygon = false;
this.useAnchors = this.settings.showPoints && this.settings.usePointAsAnchor; this.useAnchors = this.settings.showPoints && this.settings.usePointAsAnchor;
this.settings.pointAsAnchorFunction = parseFunction(this.settings.pointAsAnchorFunction, ['data', 'dsData', 'dsIndex']); this.settings.pointAsAnchorFunction = parseFunction(this.settings.pointAsAnchorFunction, ['data', 'dsData', 'dsIndex']);
this.settings.tooltipFunction = parseFunction(this.settings.tooltipFunction, ['data', 'dsData', 'dsIndex']); this.settings.tooltipFunction = parseFunction(this.settings.tooltipFunction, ['data', 'dsData', 'dsIndex']);
@ -148,6 +149,10 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
this.mapWidget.map.setLoading(false); this.mapWidget.map.setLoading(false);
this.cd.detectChanges(); this.cd.detectChanges();
}; };
subscription.callbacks.onLatestDataUpdated = () => {
this.formattedLatestData = formattedDataFormDatasourceData(this.ctx.latestData);
this.updateCurrentData();
};
} }
ngAfterViewInit() { ngAfterViewInit() {
@ -171,17 +176,17 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
timeUpdated(time: number) { timeUpdated(time: number) {
this.currentTime = time; this.currentTime = time;
// get point for each datasource associated with time // get point for each datasource associated with time
const currentPosition = this.interpolatedTimeData this.formattedCurrentPosition = this.interpolatedTimeData
.map(dataSource => dataSource[time]); .map(dataSource => dataSource[time]);
for (let j = 0; j < this.interpolatedTimeData.length; j++) { for (let j = 0; j < this.interpolatedTimeData.length; j++) {
if (isUndefined(currentPosition[j])) { if (isUndefined(this.formattedCurrentPosition[j])) {
const timePoints = Object.keys(this.interpolatedTimeData[j]).map(item => parseInt(item, 10)); const timePoints = Object.keys(this.interpolatedTimeData[j]).map(item => parseInt(item, 10));
for (let i = 1; i < timePoints.length; i++) { for (let i = 1; i < timePoints.length; i++) {
if (timePoints[i - 1] < time && timePoints[i] > time) { if (timePoints[i - 1] < time && timePoints[i] > time) {
const beforePosition = this.interpolatedTimeData[j][timePoints[i - 1]]; const beforePosition = this.interpolatedTimeData[j][timePoints[i - 1]];
const afterPosition = this.interpolatedTimeData[j][timePoints[i]]; const afterPosition = this.interpolatedTimeData[j][timePoints[i]];
const ratio = getRatio(timePoints[i - 1], timePoints[i], time); const ratio = getRatio(timePoints[i - 1], timePoints[i], time);
currentPosition[j] = { this.formattedCurrentPosition[j] = {
...beforePosition, ...beforePosition,
time, time,
...interpolateOnLineSegment(beforePosition, afterPosition, this.settings.latKeyName, this.settings.lngKeyName, ratio) ...interpolateOnLineSegment(beforePosition, afterPosition, this.settings.latKeyName, this.settings.lngKeyName, ratio)
@ -192,25 +197,29 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
} }
} }
for (let j = 0; j < this.interpolatedTimeData.length; j++) { for (let j = 0; j < this.interpolatedTimeData.length; j++) {
if (isUndefined(currentPosition[j])) { if (isUndefined(this.formattedCurrentPosition[j])) {
currentPosition[j] = this.calculateLastPoints(this.interpolatedTimeData[j], time); this.formattedCurrentPosition[j] = this.calculateLastPoints(this.interpolatedTimeData[j], time);
} }
} }
this.updateCurrentData();
}
private updateCurrentData() {
let currentPosition = this.formattedCurrentPosition;
if (this.formattedLatestData.length) {
currentPosition = mergeFormattedData(this.formattedCurrentPosition, this.formattedLatestData);
}
this.calcLabel(currentPosition); this.calcLabel(currentPosition);
this.calcMainTooltip(currentPosition); this.calcMainTooltip(currentPosition);
if (this.mapWidget && this.mapWidget.map && this.mapWidget.map.map) { if (this.mapWidget && this.mapWidget.map && this.mapWidget.map.map) {
this.mapWidget.map.updatePolylines(this.formattedInterpolatedTimeData, currentPosition, true); this.mapWidget.map.updateFromData(true, this.settings.showPolygon, currentPosition, this.formattedInterpolatedTimeData, (trip) => {
if (this.settings.showPolygon) {
this.mapWidget.map.updatePolygons(currentPosition);
}
if (this.settings.showPoints) {
this.mapWidget.map.updatePoints(this.formattedInterpolatedTimeData, this.calcTooltip);
}
this.mapWidget.map.updateMarkers(currentPosition, true, (trip) => {
this.activeTrip = trip; this.activeTrip = trip;
this.timeUpdated(this.currentTime); this.timeUpdated(this.currentTime);
this.cd.markForCheck(); this.cd.markForCheck();
}); });
if (this.settings.showPoints) {
this.mapWidget.map.updatePoints(this.formattedInterpolatedTimeData, this.calcTooltip);
}
} }
} }