mobility-toolbox-js 2.0.0-beta.53 → 2.0.0-beta.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api/RealtimeAPI.d.ts +71 -81
- package/api/RealtimeAPI.d.ts.map +1 -1
- package/api/RealtimeAPI.js +98 -265
- package/common/api/WebSocketAPI.d.ts +13 -15
- package/common/api/WebSocketAPI.d.ts.map +1 -1
- package/common/mixins/RealtimeLayerMixin.d.ts +11 -8
- package/common/mixins/RealtimeLayerMixin.d.ts.map +1 -1
- package/common/mixins/RealtimeLayerMixin.js +36 -7
- package/common/utils/debounceDeparturesMessages.d.ts +12 -0
- package/common/utils/debounceDeparturesMessages.d.ts.map +1 -0
- package/common/utils/debounceDeparturesMessages.js +24 -0
- package/common/utils/debounceWebsocketMessages.d.ts +11 -0
- package/common/utils/debounceWebsocketMessages.d.ts.map +1 -0
- package/common/utils/debounceWebsocketMessages.js +29 -0
- package/common/utils/index.d.ts +3 -0
- package/common/utils/index.js +3 -0
- package/common/utils/sortAndFilterDepartures.d.ts +16 -0
- package/common/utils/sortAndFilterDepartures.d.ts.map +1 -0
- package/common/utils/sortAndFilterDepartures.js +58 -0
- package/mapbox/layers/RealtimeLayer.d.ts +7 -5
- package/mapbox/layers/RealtimeLayer.d.ts.map +1 -1
- package/mbt.js +203 -246
- package/mbt.js.map +3 -3
- package/mbt.min.js +11 -11
- package/mbt.min.js.map +3 -3
- package/ol/layers/RealtimeLayer.d.ts +9 -6
- package/ol/layers/RealtimeLayer.d.ts.map +1 -1
- package/ol/layers/RealtimeLayer.js +3 -3
- package/package.json +1 -1
- package/types/common.d.ts +1 -1
- package/types/realtime.d.ts +0 -2
- package/common/utils/cleanStopTime.d.ts +0 -8
- package/common/utils/cleanStopTime.d.ts.map +0 -1
- package/common/utils/cleanStopTime.js +0 -25
|
@@ -3,7 +3,7 @@ import { EventsKey } from 'ol/events';
|
|
|
3
3
|
import { Coordinate } from 'ol/coordinate';
|
|
4
4
|
import { Feature } from 'ol';
|
|
5
5
|
import { RealtimeAPI } from '../../api';
|
|
6
|
-
import { AnyCanvas, AnyLayerClass, AnyMap, AnyRealtimeLayer, LayerGetFeatureInfoOptions, RealtimeGeneralizationLevel, RealtimeMode, RealtimeRenderState, RealtimeStyleFunction, RealtimeStyleOptions, RealtimeTenant, RealtimeTrainId, ViewState } from '../../types';
|
|
6
|
+
import { AnyCanvas, AnyLayerClass, AnyMap, AnyRealtimeLayer, LayerGetFeatureInfoOptions, RealtimeGeneralizationLevel, RealtimeMode, RealtimeMot, RealtimeRenderState, RealtimeStyleFunction, RealtimeStyleOptions, RealtimeTenant, RealtimeTrainId, ViewState } from '../../types';
|
|
7
7
|
import { RealtimeTrajectory } from '../../api/typedefs';
|
|
8
8
|
import { WebSocketAPIMessageEventData } from '../api/WebSocketAPI';
|
|
9
9
|
import type { OlLayerOptions } from '../../ol/layers/Layer';
|
|
@@ -12,9 +12,9 @@ export declare type RealtimeLayerMixinOptions = OlLayerOptions & {
|
|
|
12
12
|
mode?: RealtimeMode;
|
|
13
13
|
api?: RealtimeAPI;
|
|
14
14
|
tenant?: RealtimeTenant;
|
|
15
|
-
minZoomNonTrain?: number;
|
|
16
15
|
minZoomInterpolation?: number;
|
|
17
16
|
isUpdateBboxOnMoveEnd?: boolean;
|
|
17
|
+
motsByZoom?: RealtimeMot[][];
|
|
18
18
|
generalizationLevelByZoom?: RealtimeGeneralizationLevel[];
|
|
19
19
|
renderTimeIntervalByZoom?: number[];
|
|
20
20
|
style?: RealtimeStyleFunction;
|
|
@@ -31,6 +31,7 @@ export declare type RealtimeLayerMixinOptions = OlLayerOptions & {
|
|
|
31
31
|
useRequestAnimationFrame?: boolean;
|
|
32
32
|
useDebounce?: boolean;
|
|
33
33
|
useThrottle?: boolean;
|
|
34
|
+
getMotsByZoom: (zoom: number, motsByZoom: RealtimeMot[][]) => RealtimeMot[];
|
|
34
35
|
getGeneralizationLevelByZoom?: (zoom: number, generalizationLevelByZoom: RealtimeGeneralizationLevel[]) => RealtimeGeneralizationLevel;
|
|
35
36
|
getRenderTimeIntervalByZoom?: (zoom: number, renderTimeIntervalByZoom: number[]) => number;
|
|
36
37
|
url?: string;
|
|
@@ -105,7 +106,6 @@ declare function RealtimeLayerMixin<T extends AnyLayerClass>(Base: T): {
|
|
|
105
106
|
style?: RealtimeStyleFunction | undefined;
|
|
106
107
|
styleOptions?: RealtimeStyleOptions | undefined;
|
|
107
108
|
pixelRatio?: number | undefined;
|
|
108
|
-
minZoomNonTrain: number;
|
|
109
109
|
minZoomInterpolation: number;
|
|
110
110
|
isUpdateBboxOnMoveEnd: boolean;
|
|
111
111
|
hoverVehicleId?: string | undefined;
|
|
@@ -114,6 +114,8 @@ declare function RealtimeLayerMixin<T extends AnyLayerClass>(Base: T): {
|
|
|
114
114
|
useRequestAnimationFrame?: boolean | undefined;
|
|
115
115
|
useDebounce?: boolean | undefined;
|
|
116
116
|
useThrottle?: boolean | undefined;
|
|
117
|
+
mots?: RealtimeMot[] | undefined;
|
|
118
|
+
motsByZoom: RealtimeMot[][];
|
|
117
119
|
generalizationLevel?: RealtimeGeneralizationLevel | undefined;
|
|
118
120
|
generalizationLevelByZoom: RealtimeGeneralizationLevel[];
|
|
119
121
|
renderTimeIntervalByZoom: number[];
|
|
@@ -123,6 +125,7 @@ declare function RealtimeLayerMixin<T extends AnyLayerClass>(Base: T): {
|
|
|
123
125
|
updateTimeDelay?: number | undefined;
|
|
124
126
|
visibilityRef: EventsKey;
|
|
125
127
|
selectedVehicle: GeoJSONFeature;
|
|
128
|
+
getMotsByZoom: (zoom: number) => RealtimeMot[];
|
|
126
129
|
getGeneralizationLevelByZoom: (zoom: number) => RealtimeGeneralizationLevel;
|
|
127
130
|
getRenderTimeIntervalByZoom: (zoom: number) => number;
|
|
128
131
|
throttleRenderTrajectories: (viewState: ViewState, noInterpolate?: boolean) => void;
|
|
@@ -214,14 +217,14 @@ declare function RealtimeLayerMixin<T extends AnyLayerClass>(Base: T): {
|
|
|
214
217
|
* @return {Promise<{stopSequence: StopSequence, fullTrajectory: FullTrajectory>} A promise that will be resolved with the trajectory informations.
|
|
215
218
|
*/
|
|
216
219
|
getTrajectoryInfos(id: RealtimeTrainId): Promise<{
|
|
217
|
-
stopSequence:
|
|
218
|
-
fullTrajectory:
|
|
220
|
+
stopSequence: WebSocketAPIMessageEventData<import("../../types").RealtimeFullTrajectory> | WebSocketAPIMessageEventData<GeoJSONFeature[]>;
|
|
221
|
+
fullTrajectory: WebSocketAPIMessageEventData<import("../../types").RealtimeFullTrajectory> | WebSocketAPIMessageEventData<GeoJSONFeature[]>;
|
|
219
222
|
}>;
|
|
220
223
|
/**
|
|
221
224
|
* Determine if the trajectory is useless and should be removed from the list or not.
|
|
222
225
|
* By default, this function exclude vehicles:
|
|
223
226
|
* - that have their trajectory outside the current extent and
|
|
224
|
-
* - that
|
|
227
|
+
* - that aren't in the MOT list.
|
|
225
228
|
*
|
|
226
229
|
* @param {RealtimeTrajectory} trajectory
|
|
227
230
|
* @param {Array<number>} extent
|
|
@@ -251,7 +254,7 @@ declare function RealtimeLayerMixin<T extends AnyLayerClass>(Base: T): {
|
|
|
251
254
|
*
|
|
252
255
|
* @private
|
|
253
256
|
*/
|
|
254
|
-
onTrajectoryMessage(data: WebSocketAPIMessageEventData): void;
|
|
257
|
+
onTrajectoryMessage(data: WebSocketAPIMessageEventData<RealtimeTrajectory>): void;
|
|
255
258
|
/**
|
|
256
259
|
* Callback on websocket's deleted_vehicles channel events.
|
|
257
260
|
* It removes the trajectory from the list.
|
|
@@ -259,7 +262,7 @@ declare function RealtimeLayerMixin<T extends AnyLayerClass>(Base: T): {
|
|
|
259
262
|
* @private
|
|
260
263
|
* @override
|
|
261
264
|
*/
|
|
262
|
-
onDeleteTrajectoryMessage(data: WebSocketAPIMessageEventData): void;
|
|
265
|
+
onDeleteTrajectoryMessage(data: WebSocketAPIMessageEventData<RealtimeTrainId>): void;
|
|
263
266
|
/**
|
|
264
267
|
* Callback when user moves the mouse/pointer over the map.
|
|
265
268
|
* It sets the layer's hoverVehicleId property with the current hovered vehicle's id.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RealtimeLayerMixin.d.ts","sourceRoot":"","sources":["../../../src/common/mixins/RealtimeLayerMixin.ts"],"names":[],"mappings":"AAOA,OAAO,OAAO,MAAM,mBAAmB,CAAC;AAIxC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAE7B,OAAO,EAAE,WAAW,EAAiB,MAAM,WAAW,CAAC;AAGvD,OAAO,EACL,SAAS,EACT,aAAa,EACb,MAAM,EACN,gBAAgB,EAChB,0BAA0B,EAC1B,2BAA2B,EAC3B,YAAY,EACZ,mBAAmB,EACnB,qBAAqB,EACrB,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,SAAS,EACV,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AAEnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,oBAAY,yBAAyB,GAAG,cAAc,GAAG;IACvD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,GAAG,CAAC,EAAE,WAAW,CAAC;IAClB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,
|
|
1
|
+
{"version":3,"file":"RealtimeLayerMixin.d.ts","sourceRoot":"","sources":["../../../src/common/mixins/RealtimeLayerMixin.ts"],"names":[],"mappings":"AAOA,OAAO,OAAO,MAAM,mBAAmB,CAAC;AAIxC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAE7B,OAAO,EAAE,WAAW,EAAiB,MAAM,WAAW,CAAC;AAGvD,OAAO,EACL,SAAS,EACT,aAAa,EACb,MAAM,EACN,gBAAgB,EAChB,0BAA0B,EAC1B,2BAA2B,EAC3B,YAAY,EACZ,WAAW,EACX,mBAAmB,EACnB,qBAAqB,EACrB,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,SAAS,EACV,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AAEnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,oBAAY,yBAAyB,GAAG,cAAc,GAAG;IACvD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,GAAG,CAAC,EAAE,WAAW,CAAC;IAClB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,UAAU,CAAC,EAAE,WAAW,EAAE,EAAE,CAAC;IAC7B,yBAAyB,CAAC,EAAE,2BAA2B,EAAE,CAAC;IAC1D,wBAAwB,CAAC,EAAE,MAAM,EAAE,CAAC;IACpC,KAAK,CAAC,EAAE,qBAAqB,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,eAAe,CAAC;IACjC,iBAAiB,CAAC,EAAE,eAAe,CAAC;IACpC,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,YAAY,CAAC,EAAE,oBAAoB,CAAC;IACpC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,EAAE,KAAK,WAAW,EAAE,CAAC;IAC5E,4BAA4B,CAAC,EAAE,CAC7B,IAAI,EAAE,MAAM,EACZ,yBAAyB,EAAE,2BAA2B,EAAE,KACrD,2BAA2B,CAAC;IACjC,2BAA2B,CAAC,EAAE,CAC5B,IAAI,EAAE,MAAM,EACZ,wBAAwB,EAAE,MAAM,EAAE,KAC/B,MAAM,CAAC;IAGZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;GAEG;AACH,qBAAa,sBAAsB;IACjC;;OAEG;IACH,KAAK;IAEL;;OAEG;IACH,IAAI;IAEJ;;;;;OAKG;IACH,OAAO,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM;IAE9D;;;;OAIG;IACH,OAAO,CAAC,IAAI,EAAE,YAAY;IAE1B;;OAEG;IACH,kBAAkB;IAElB;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY;CAClD;AAED;;;;;;GAMG;AACH,iBAAS,kBAAkB,CAAC,CAAC,SAAS,aAAa,EAAE,IAAI,EAAE,CAAC;kBAqFnC,yBAAyB;;eAlFvC,OAAO;;;;;cAMR,YAAY;aAEb,WAAW;gBAER,cAAc;;;;;;;;;8BAkBA,MAAM;+BAEL,OAAO;;;;;;;;oBAgBlB,WAAW,EAAE,EAAE;;mCAIA,2BAA2B,EAAE;kCAE9B,MAAM,EAAE;gBAE1B,OAAO;;;;uBAQC,SAAS;;8BAIH,MAAM,KAAK,WAAW,EAAE;6CAET,MAAM,KAAK,2BAA2B;4CAEvC,MAAM,KAAK,MAAM;gDAGxC,SAAS,kBACJ,OAAO,KACpB,IAAI;gDAGI,SAAS,kBACJ,OAAO,KACpB,IAAI;QAmGT;;;;WAIG;kCACuB,yBAAyB;yBAmKlC,MAAM;;;QAmEvB;;;WAGG;;;QAsBH;;;WAGG;;QAQH;;;;;;;;;;;;;WAaG;8CAEU,SAAS,kBACL,OAAO;QA8CxB;;;;;;;;;;;;;;WAcG;sCAEU,SAAS,GAAG,SAAS,iBACjB,OAAO,GAAG,SAAS;yBAwBnB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,SAAS,MAAM;sBAyClD,YAAY;QAmB1B;;;;;WAKG;kCACsB,MAAM,GAAG,SAAS,GAAO,MAAM;QA0BxD;;;;WAIG;6BACkB,cAAc;QASnC;;;;;;;;WAQG;oEAGQ,0BAA0B;;;;;QAkCrC;;;;;WAKG;+BACoB,eAAe;;;;QAiBtC;;;;;;;;;;;WAWG;4DAGO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,QAClC,MAAM;QAcd;;;;WAIG;;yCAa8B,kBAAkB,GAAG,eAAe;QAYrE;;;;;WAKG;;;QAgBH;;;;;WAKG;kCAEK,6BAA6B,kBAAkB,CAAC;QAgDxD;;;;;;WAMG;wCAEK,6BAA6B,eAAe,CAAC;QAQrD;;;;;;WAMG;iCAES,OAAO,EAAE,SACZ,gBAAgB;QAgBzB;;;;;;WAMG;iCAES,OAAO,EAAE,SACZ,gBAAgB;;MAkB5B;AAED,eAAe,kBAAkB,CAAC"}
|
|
@@ -67,9 +67,29 @@ function RealtimeLayerMixin(Base) {
|
|
|
67
67
|
this.mode = options.mode || RealtimeModes.TOPOGRAPHIC;
|
|
68
68
|
this.api = options.api || new RealtimeAPI(options);
|
|
69
69
|
this.tenant = options.tenant || ''; // sbb,sbh or sbm
|
|
70
|
-
this.minZoomNonTrain = options.minZoomNonTrain || 9; // Min zoom level from which non trains are allowed to be displayed. Min value is 9 (as configured by the server
|
|
71
70
|
this.minZoomInterpolation = options.minZoomInterpolation || 8; // Min zoom level from which trains positions are not interpolated.
|
|
72
71
|
this.format = new GeoJSON();
|
|
72
|
+
// MOTs by zoom
|
|
73
|
+
const allMots = [
|
|
74
|
+
'tram',
|
|
75
|
+
'subway',
|
|
76
|
+
'rail',
|
|
77
|
+
'bus',
|
|
78
|
+
'ferry',
|
|
79
|
+
'cablecar',
|
|
80
|
+
'gondola',
|
|
81
|
+
'funicular',
|
|
82
|
+
'coach',
|
|
83
|
+
];
|
|
84
|
+
// Server will block non train before zoom 9
|
|
85
|
+
this.motsByZoom = options.motsByZoom || [allMots];
|
|
86
|
+
this.getMotsByZoom = (zoom) => {
|
|
87
|
+
return ((options.getMotsByZoom &&
|
|
88
|
+
options.getMotsByZoom(zoom, this.motsByZoom)) ||
|
|
89
|
+
this.motsByZoom[zoom] ||
|
|
90
|
+
this.motsByZoom[this.motsByZoom.length - 1]);
|
|
91
|
+
};
|
|
92
|
+
// Generalization levels by zoom
|
|
73
93
|
this.generalizationLevelByZoom = options.generalizationLevelByZoom || [
|
|
74
94
|
5, 5, 5, 5, 5, 5, 5, 5, 10, 30, 30, 100, 100, 100,
|
|
75
95
|
];
|
|
@@ -78,6 +98,7 @@ function RealtimeLayerMixin(Base) {
|
|
|
78
98
|
options.getGeneralizationLevelByZoom(zoom, this.generalizationLevelByZoom)) ||
|
|
79
99
|
this.generalizationLevelByZoom[zoom]);
|
|
80
100
|
};
|
|
101
|
+
// Render time interval by zoom
|
|
81
102
|
this.renderTimeIntervalByZoom = options.renderTimeIntervalByZoom || [
|
|
82
103
|
100000, 50000, 40000, 30000, 20000, 15000, 10000, 5000, 2000, 1000, 400,
|
|
83
104
|
300, 250, 180, 90, 60, 50, 50, 50, 50, 50,
|
|
@@ -273,8 +294,8 @@ function RealtimeLayerMixin(Base) {
|
|
|
273
294
|
this.renderTrajectories();
|
|
274
295
|
this.startUpdateTime();
|
|
275
296
|
this.api.open();
|
|
276
|
-
this.api.subscribeTrajectory(this.mode, this.onTrajectoryMessage, this.isUpdateBboxOnMoveEnd);
|
|
277
|
-
this.api.subscribeDeletedVehicles(this.mode, this.onDeleteTrajectoryMessage, this.isUpdateBboxOnMoveEnd);
|
|
297
|
+
this.api.subscribeTrajectory(this.mode, this.onTrajectoryMessage, undefined, this.isUpdateBboxOnMoveEnd);
|
|
298
|
+
this.api.subscribeDeletedVehicles(this.mode, this.onDeleteTrajectoryMessage, undefined, this.isUpdateBboxOnMoveEnd);
|
|
278
299
|
if (this.isUpdateBboxOnMoveEnd) {
|
|
279
300
|
// Update the bbox on each move end
|
|
280
301
|
// @ts-ignore function without parameters defined by subclasses
|
|
@@ -413,6 +434,11 @@ function RealtimeLayerMixin(Base) {
|
|
|
413
434
|
if (this.generalizationLevel) {
|
|
414
435
|
bbox.push(`gen=${this.generalizationLevel}`);
|
|
415
436
|
}
|
|
437
|
+
/* @ignore */
|
|
438
|
+
this.mots = this.getMotsByZoom(zoom);
|
|
439
|
+
if (this.mots) {
|
|
440
|
+
bbox.push(`mots=${this.mots}`);
|
|
441
|
+
}
|
|
416
442
|
}
|
|
417
443
|
this.api.bbox = bbox;
|
|
418
444
|
}
|
|
@@ -421,8 +447,8 @@ function RealtimeLayerMixin(Base) {
|
|
|
421
447
|
return;
|
|
422
448
|
}
|
|
423
449
|
this.mode = mode;
|
|
424
|
-
this.api.subscribeTrajectory(this.mode, this.onTrajectoryMessage, this.isUpdateBboxOnMoveEnd);
|
|
425
|
-
this.api.subscribeDeletedVehicles(this.mode, this.onDeleteTrajectoryMessage, this.isUpdateBboxOnMoveEnd);
|
|
450
|
+
this.api.subscribeTrajectory(this.mode, this.onTrajectoryMessage, undefined, this.isUpdateBboxOnMoveEnd);
|
|
451
|
+
this.api.subscribeDeletedVehicles(this.mode, this.onDeleteTrajectoryMessage, undefined, this.isUpdateBboxOnMoveEnd);
|
|
426
452
|
}
|
|
427
453
|
/**
|
|
428
454
|
* Get the duration before the next update depending on zoom level.
|
|
@@ -518,7 +544,7 @@ function RealtimeLayerMixin(Base) {
|
|
|
518
544
|
* Determine if the trajectory is useless and should be removed from the list or not.
|
|
519
545
|
* By default, this function exclude vehicles:
|
|
520
546
|
* - that have their trajectory outside the current extent and
|
|
521
|
-
* - that
|
|
547
|
+
* - that aren't in the MOT list.
|
|
522
548
|
*
|
|
523
549
|
* @param {RealtimeTrajectory} trajectory
|
|
524
550
|
* @param {Array<number>} extent
|
|
@@ -527,9 +553,12 @@ function RealtimeLayerMixin(Base) {
|
|
|
527
553
|
* @ignore
|
|
528
554
|
*/
|
|
529
555
|
purgeTrajectory(trajectory, extent, zoom) {
|
|
556
|
+
var _a;
|
|
530
557
|
const { type, bounds } = trajectory.properties;
|
|
531
558
|
if (!intersects(extent, bounds) ||
|
|
532
|
-
(
|
|
559
|
+
!((_a = this.mots) === null || _a === void 0 ? void 0 : _a.includes(type)) ||
|
|
560
|
+
(type !== 'rail' && zoom < 9) // zoom 9 is defined by the backend
|
|
561
|
+
) {
|
|
533
562
|
this.removeTrajectory(trajectory);
|
|
534
563
|
return true;
|
|
535
564
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RealtimeDeparture, RealtimeDepartureExtended } from '../../types';
|
|
2
|
+
import type { WebSocketAPIMessageCallback } from '../api/WebSocketAPI';
|
|
3
|
+
/**
|
|
4
|
+
* This function returns a WebSocket api callback, and call the onDeparturesUpdate function with the list of current departures to display.
|
|
5
|
+
* @param {function(departures: RealtimeDeparture[])} onDeparturesUpdate callback when list of departures changes, called after 100 ms
|
|
6
|
+
* @param {boolean} [sortByMinArrivalTime = true] Sort departures by arrival time
|
|
7
|
+
* @param {number} [maxDepartureAge = 30] max departure age of departures in minutes
|
|
8
|
+
* @param {number} [timeout = 100] debounce timeout in ms
|
|
9
|
+
*/
|
|
10
|
+
declare const debounceDeparturesMessages: (onDeparturesUpdate: (departures: RealtimeDepartureExtended[]) => {}, sortByMinArrivalTime?: boolean, maxDepartureAge?: number, timeout?: number) => WebSocketAPIMessageCallback<RealtimeDeparture>;
|
|
11
|
+
export default debounceDeparturesMessages;
|
|
12
|
+
//# sourceMappingURL=debounceDeparturesMessages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debounceDeparturesMessages.d.ts","sourceRoot":"","sources":["../../../src/common/utils/debounceDeparturesMessages.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,KAAK,EACV,2BAA2B,EAE5B,MAAM,qBAAqB,CAAC;AAG7B;;;;;;GAMG;AACH,QAAA,MAAM,0BAA0B,oCACG,yBAAyB,EAAE,KAAK,EAAE,iFAIlE,4BAA4B,iBAAiB,CAwB/C,CAAC;AAEF,eAAe,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import sortAndfilterDepartures from './sortAndFilterDepartures';
|
|
2
|
+
/**
|
|
3
|
+
* This function returns a WebSocket api callback, and call the onDeparturesUpdate function with the list of current departures to display.
|
|
4
|
+
* @param {function(departures: RealtimeDeparture[])} onDeparturesUpdate callback when list of departures changes, called after 100 ms
|
|
5
|
+
* @param {boolean} [sortByMinArrivalTime = true] Sort departures by arrival time
|
|
6
|
+
* @param {number} [maxDepartureAge = 30] max departure age of departures in minutes
|
|
7
|
+
* @param {number} [timeout = 100] debounce timeout in ms
|
|
8
|
+
*/
|
|
9
|
+
const debounceDeparturesMessages = (onDeparturesUpdate, sortByMinArrivalTime = true, maxDepartureAge = 30, timeout = 100) => {
|
|
10
|
+
const departureUpdateTimeout = {};
|
|
11
|
+
const departureObject = {};
|
|
12
|
+
return (data) => {
|
|
13
|
+
const { source, content: departure } = data;
|
|
14
|
+
if (departureUpdateTimeout[source]) {
|
|
15
|
+
window.clearTimeout(departureUpdateTimeout[source]);
|
|
16
|
+
}
|
|
17
|
+
departureObject[departure.call_id] = departure;
|
|
18
|
+
departureUpdateTimeout[source] = window.setTimeout(() => {
|
|
19
|
+
const departures = sortAndfilterDepartures(departureObject, sortByMinArrivalTime || false, maxDepartureAge);
|
|
20
|
+
onDeparturesUpdate(departures);
|
|
21
|
+
}, timeout);
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
export default debounceDeparturesMessages;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { WebSocketAPIMessageCallback } from '../api/WebSocketAPI';
|
|
2
|
+
/**
|
|
3
|
+
* This function returns a WebSocket api callback, and call the onUpdate function with the list of of subscribed objects changes.
|
|
4
|
+
*
|
|
5
|
+
* @param {function(objects: any[])} onUpdate callback when list of subscribed objects changes, called after 100 ms
|
|
6
|
+
* @param {function(object: any)} [getObjectId = true] function returning the id of an object
|
|
7
|
+
* @param {number} [timeout = 100] debounce timeout in ms
|
|
8
|
+
*/
|
|
9
|
+
declare const debounceWebsocketMessages: (onUpdate: (objects: any[]) => void, getObjectId?: ((object: any) => string) | undefined, timeout?: number) => WebSocketAPIMessageCallback<any>;
|
|
10
|
+
export default debounceWebsocketMessages;
|
|
11
|
+
//# sourceMappingURL=debounceWebsocketMessages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debounceWebsocketMessages.d.ts","sourceRoot":"","sources":["../../../src/common/utils/debounceWebsocketMessages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,2BAA2B,EAE5B,MAAM,qBAAqB,CAAC;AAE7B;;;;;;GAMG;AACH,QAAA,MAAM,yBAAyB,uBACT,GAAG,EAAE,KAAK,IAAI,0BACX,GAAG,KAAK,MAAM,oCAEpC,4BAA4B,GAAG,CA2BjC,CAAC;AAEF,eAAe,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This function returns a WebSocket api callback, and call the onUpdate function with the list of of subscribed objects changes.
|
|
3
|
+
*
|
|
4
|
+
* @param {function(objects: any[])} onUpdate callback when list of subscribed objects changes, called after 100 ms
|
|
5
|
+
* @param {function(object: any)} [getObjectId = true] function returning the id of an object
|
|
6
|
+
* @param {number} [timeout = 100] debounce timeout in ms
|
|
7
|
+
*/
|
|
8
|
+
const debounceWebsocketMessages = (onUpdate, getObjectId, timeout = 100) => {
|
|
9
|
+
const updateTimeout = {};
|
|
10
|
+
const objectsById = {};
|
|
11
|
+
const objects = [];
|
|
12
|
+
return (data) => {
|
|
13
|
+
const { source, content } = data;
|
|
14
|
+
if (updateTimeout[source]) {
|
|
15
|
+
window.clearTimeout(updateTimeout[source]);
|
|
16
|
+
}
|
|
17
|
+
if (getObjectId) {
|
|
18
|
+
objectsById[getObjectId(content)] = content;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
objects.push(content);
|
|
22
|
+
}
|
|
23
|
+
updateTimeout[source] = window.setTimeout(() => {
|
|
24
|
+
const objectToReturn = getObjectId ? Object.values(objectsById) : objects;
|
|
25
|
+
onUpdate(objectToReturn);
|
|
26
|
+
}, timeout);
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
export default debounceWebsocketMessages;
|
package/common/utils/index.d.ts
CHANGED
|
@@ -8,5 +8,8 @@ export { default as sortByDelay } from "./sortByDelay";
|
|
|
8
8
|
export { default as renderTrajectories } from "./renderTrajectories";
|
|
9
9
|
export { default as getMaplibreRender } from "./getMaplibreRender";
|
|
10
10
|
export { default as getMapboxRender } from "./getMapboxRender";
|
|
11
|
+
export { default as debounceDeparturesMessages } from "./debounceDeparturesMessages";
|
|
12
|
+
export { default as debounceWebsocketMessages } from "./debounceWebsocketMessages";
|
|
13
|
+
export { default as sortAndFilterDepartures } from "./sortAndFilterDepartures";
|
|
11
14
|
export * as realtimeConfig from "./realtimeConfig";
|
|
12
15
|
//# sourceMappingURL=index.d.ts.map
|
package/common/utils/index.js
CHANGED
|
@@ -8,5 +8,8 @@ export { default as sortByDelay } from './sortByDelay';
|
|
|
8
8
|
export { default as renderTrajectories } from './renderTrajectories';
|
|
9
9
|
export { default as getMaplibreRender } from './getMaplibreRender';
|
|
10
10
|
export { default as getMapboxRender } from './getMapboxRender';
|
|
11
|
+
export { default as debounceDeparturesMessages } from './debounceDeparturesMessages';
|
|
12
|
+
export { default as debounceWebsocketMessages } from './debounceWebsocketMessages';
|
|
13
|
+
export { default as sortAndFilterDepartures } from './sortAndFilterDepartures';
|
|
11
14
|
import * as realtimeConfig_1 from './realtimeConfig';
|
|
12
15
|
export { realtimeConfig_1 as realtimeConfig };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { RealtimeAPIDeparturesById } from '../../api/RealtimeAPI';
|
|
2
|
+
import type { RealtimeDepartureExtended } from '../../types';
|
|
3
|
+
/**
|
|
4
|
+
* This function sort Departures by arrival time and filter out unwanted departures:
|
|
5
|
+
* - when dparture time is in the past
|
|
6
|
+
* - when departure are duplicated
|
|
7
|
+
* - when departure is not in the next 30 min
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} depObject The object containing departures by id.
|
|
10
|
+
* @param {boolean} [sortByMinArrivalTime=false] If true sort departures by arrival time.
|
|
11
|
+
* @return {Array<Departure>} Return departures array.
|
|
12
|
+
* @private
|
|
13
|
+
*/
|
|
14
|
+
declare const sortAndfilterDepartures: (depObject: RealtimeAPIDeparturesById, sortByMinArrivalTime?: boolean, maxDepartureAge?: number) => RealtimeDepartureExtended[];
|
|
15
|
+
export default sortAndfilterDepartures;
|
|
16
|
+
//# sourceMappingURL=sortAndFilterDepartures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sortAndFilterDepartures.d.ts","sourceRoot":"","sources":["../../../src/common/utils/sortAndFilterDepartures.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAG7D;;;;;;;;;;GAUG;AACH,QAAA,MAAM,uBAAuB,cAChB,yBAAyB,yBACd,OAAO,oBACZ,MAAM,KACtB,yBAAyB,EAwD3B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import compareDepartures from './compareDepartures';
|
|
2
|
+
/**
|
|
3
|
+
* This function sort Departures by arrival time and filter out unwanted departures:
|
|
4
|
+
* - when dparture time is in the past
|
|
5
|
+
* - when departure are duplicated
|
|
6
|
+
* - when departure is not in the next 30 min
|
|
7
|
+
*
|
|
8
|
+
* @param {Object} depObject The object containing departures by id.
|
|
9
|
+
* @param {boolean} [sortByMinArrivalTime=false] If true sort departures by arrival time.
|
|
10
|
+
* @return {Array<Departure>} Return departures array.
|
|
11
|
+
* @private
|
|
12
|
+
*/
|
|
13
|
+
const sortAndfilterDepartures = (depObject, sortByMinArrivalTime = false, maxDepartureAge = 30) => {
|
|
14
|
+
const departures = Object.keys(depObject).map((k) => depObject[k]);
|
|
15
|
+
departures.sort((a, b) => compareDepartures(a, b, sortByMinArrivalTime));
|
|
16
|
+
const futureDate = new Date();
|
|
17
|
+
futureDate.setMinutes(futureDate.getMinutes() + maxDepartureAge);
|
|
18
|
+
const future = futureDate.getTime();
|
|
19
|
+
const pastDate = new Date();
|
|
20
|
+
pastDate.setMinutes(pastDate.getMinutes() - maxDepartureAge);
|
|
21
|
+
const past = pastDate.getTime();
|
|
22
|
+
const departureArray = [];
|
|
23
|
+
const platformsBoarding = [];
|
|
24
|
+
let previousDeparture = null;
|
|
25
|
+
for (let i = departures.length - 1; i >= 0; i -= 1) {
|
|
26
|
+
const departure = Object.assign({}, departures[i]);
|
|
27
|
+
const time = new Date(departure.time).getTime();
|
|
28
|
+
// Only show departures within the next 30 minutes
|
|
29
|
+
if (time > past && time < future) {
|
|
30
|
+
// If 2 trains are boarding at the same platform,
|
|
31
|
+
// remove the older one.
|
|
32
|
+
if (departure.state === 'BOARDING') {
|
|
33
|
+
if (!platformsBoarding.includes(departure.platform)) {
|
|
34
|
+
platformsBoarding.push(departure.platform);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
departure.state = 'HIDDEN';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// If two trains with the same line number and destinatin
|
|
41
|
+
// and a departure difference < 1 minute, hide the second one.
|
|
42
|
+
if (previousDeparture &&
|
|
43
|
+
departure.to[0] === previousDeparture.to[0] &&
|
|
44
|
+
Math.abs(time - previousDeparture.time) < 1000 &&
|
|
45
|
+
departure.line.name === previousDeparture.line.name) {
|
|
46
|
+
departure.state = 'HIDDEN';
|
|
47
|
+
}
|
|
48
|
+
if (/(STOP_CANCELLED|JOURNEY_CANCELLED)/.test(departure.state)) {
|
|
49
|
+
departure.cancelled = true;
|
|
50
|
+
}
|
|
51
|
+
previousDeparture = departure;
|
|
52
|
+
previousDeparture.time = time;
|
|
53
|
+
departureArray.unshift(departure);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return departureArray;
|
|
57
|
+
};
|
|
58
|
+
export default sortAndfilterDepartures;
|
|
@@ -23,7 +23,6 @@ declare const RealtimeLayer_base: {
|
|
|
23
23
|
style?: import("../../types").RealtimeStyleFunction | undefined;
|
|
24
24
|
styleOptions?: import("../../types").RealtimeStyleOptions | undefined;
|
|
25
25
|
pixelRatio?: number | undefined;
|
|
26
|
-
minZoomNonTrain: number;
|
|
27
26
|
minZoomInterpolation: number;
|
|
28
27
|
isUpdateBboxOnMoveEnd: boolean;
|
|
29
28
|
hoverVehicleId?: string | undefined;
|
|
@@ -32,6 +31,8 @@ declare const RealtimeLayer_base: {
|
|
|
32
31
|
useRequestAnimationFrame?: boolean | undefined;
|
|
33
32
|
useDebounce?: boolean | undefined;
|
|
34
33
|
useThrottle?: boolean | undefined;
|
|
34
|
+
mots?: import("../../types").RealtimeMot[] | undefined;
|
|
35
|
+
motsByZoom: import("../../types").RealtimeMot[][];
|
|
35
36
|
generalizationLevel?: import("../../types").RealtimeGeneralizationLevel | undefined;
|
|
36
37
|
generalizationLevelByZoom: import("../../types").RealtimeGeneralizationLevel[];
|
|
37
38
|
renderTimeIntervalByZoom: number[];
|
|
@@ -41,6 +42,7 @@ declare const RealtimeLayer_base: {
|
|
|
41
42
|
updateTimeDelay?: number | undefined;
|
|
42
43
|
visibilityRef: import("ol/events").EventsKey;
|
|
43
44
|
selectedVehicle: GeoJSONFeature;
|
|
45
|
+
getMotsByZoom: (zoom: number) => import("../../types").RealtimeMot[];
|
|
44
46
|
getGeneralizationLevelByZoom: (zoom: number) => import("../../types").RealtimeGeneralizationLevel;
|
|
45
47
|
getRenderTimeIntervalByZoom: (zoom: number) => number;
|
|
46
48
|
throttleRenderTrajectories: (viewState: import("../../types").ViewState, noInterpolate?: boolean | undefined) => void;
|
|
@@ -64,16 +66,16 @@ declare const RealtimeLayer_base: {
|
|
|
64
66
|
coordinate: Coordinate;
|
|
65
67
|
}>;
|
|
66
68
|
getTrajectoryInfos(id: string): Promise<{
|
|
67
|
-
stopSequence:
|
|
68
|
-
fullTrajectory:
|
|
69
|
+
stopSequence: import("../../common/api/WebSocketAPI").WebSocketAPIMessageEventData<import("../../types").RealtimeFullTrajectory> | import("../../common/api/WebSocketAPI").WebSocketAPIMessageEventData<GeoJSONFeature[]>;
|
|
70
|
+
fullTrajectory: import("../../common/api/WebSocketAPI").WebSocketAPIMessageEventData<import("../../types").RealtimeFullTrajectory> | import("../../common/api/WebSocketAPI").WebSocketAPIMessageEventData<GeoJSONFeature[]>;
|
|
69
71
|
}>;
|
|
70
72
|
purgeTrajectory(trajectory: GeoJSONFeature, extent: [number, number, number, number], zoom: number): boolean;
|
|
71
73
|
addTrajectory(trajectory: GeoJSONFeature): void;
|
|
72
74
|
removeTrajectory(trajectoryOrId: any): void;
|
|
73
75
|
onZoomEnd(): void;
|
|
74
76
|
onDocumentVisibilityChange(): void;
|
|
75
|
-
onTrajectoryMessage(data: import("../../common/api/WebSocketAPI").WebSocketAPIMessageEventData): void;
|
|
76
|
-
onDeleteTrajectoryMessage(data: import("../../common/api/WebSocketAPI").WebSocketAPIMessageEventData): void;
|
|
77
|
+
onTrajectoryMessage(data: import("../../common/api/WebSocketAPI").WebSocketAPIMessageEventData<GeoJSONFeature>): void;
|
|
78
|
+
onDeleteTrajectoryMessage(data: import("../../common/api/WebSocketAPI").WebSocketAPIMessageEventData<string>): void;
|
|
77
79
|
onFeatureHover(features: Feature<import("ol/geom/Geometry").default>[], layer: import("../../types").AnyRealtimeLayer, coordinate: Coordinate): void;
|
|
78
80
|
onFeatureClick(features: Feature<import("ol/geom/Geometry").default>[], layer: import("../../types").AnyRealtimeLayer, coordinate: Coordinate): void;
|
|
79
81
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RealtimeLayer.d.ts","sourceRoot":"","sources":["../../../src/mapbox/layers/RealtimeLayer.ts"],"names":[],"mappings":";AAKA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAE7B,OAAO,KAAK,MAAM,SAAS,CAAC;AAE5B,OAAO,EACL,YAAY,EACZ,0BAA0B,EAC1B,2BAA2B,EAC5B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC
|
|
1
|
+
{"version":3,"file":"RealtimeLayer.d.ts","sourceRoot":"","sources":["../../../src/mapbox/layers/RealtimeLayer.ts"],"names":[],"mappings":";AAKA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAE7B,OAAO,KAAK,MAAM,SAAS,CAAC;AAE5B,OAAO,EACL,YAAY,EACZ,0BAA0B,EAC1B,2BAA2B,EAC5B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAExD;;;;;;;;;;;;;;;;GAgBG;AAEH,cAAM,aAAc,SAAQ,kBAAY;gBAC1B,OAAO,KAAK;IAqBxB;;;;;;OAMG;IAEH,WAAW,CAAC,GAAG,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM;IAuC/C;;OAEG;IACH,aAAa;IAkBb;;;;;;OAMG;IACH,KAAK;IAQL;;;;OAIG;IACH,IAAI;IASJ,MAAM;IASN;;;OAGG;IACH,kBAAkB,CAChB,GAAG,EAAE,QAAQ,CAAC,kBAAkB,GAAG,UAAU,CAAC,aAAa;IAQ7D;;;;OAIG;IAEH,kBAAkB,CAAC,aAAa,CAAC,EAAE,OAAe;IAuDlD;;OAEG;IACH,kBAAkB;IAIlB,0BAA0B,CACxB,UAAU,EAAE,UAAU,EACtB,OAAO,KAAK,GACX,OAAO,CAAC,2BAA2B,CAAC;IAQvC,kBAAkB;IAclB;;;;OAIG;IACH,eAAe,CACb,UAAU,EAAE,kBAAkB,EAC9B,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EACxC,IAAI,EAAE,MAAM;IASd;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM;IAUhE;;;;OAIG;IACH,MAAM;IAIN,0BAA0B,CACxB,SAAS,EAAE,SAAS,EACpB,aAAa,GAAE,OAAe;IAahC;;;;;OAKG;IACH,SAAS;IAQT;;;;;OAKG;IACH,cAAc,CACZ,QAAQ,EAAE,OAAO,EAAE,EACnB,KAAK,EAAE,aAAa,EACpB,UAAU,EAAE,UAAU;CAOzB;AAED,eAAe,aAAa,CAAC"}
|