mobility-toolbox-js 3.0.0-beta.2 → 3.0.0-beta.21

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.
Files changed (125) hide show
  1. package/api/HttpAPI.d.ts +20 -0
  2. package/api/HttpAPI.js +0 -11
  3. package/api/RealtimeAPI.d.ts +404 -0
  4. package/api/RealtimeAPI.js +342 -276
  5. package/api/RoutingAPI.d.ts +47 -0
  6. package/api/RoutingAPI.js +17 -7
  7. package/api/StopsAPI.d.ts +44 -0
  8. package/api/StopsAPI.js +16 -10
  9. package/api/WebSocketAPI.d.ts +147 -0
  10. package/api/WebSocketAPI.js +164 -164
  11. package/api/index.d.ts +3 -0
  12. package/api/index.js +1 -1
  13. package/api/typedefs.d.ts +76 -0
  14. package/api/typedefs.js +27 -42
  15. package/common/controls/StopFinderControlCommon.d.ts +53 -0
  16. package/common/controls/StopFinderControlCommon.js +31 -31
  17. package/common/index.d.ts +2 -0
  18. package/common/index.js +1 -1
  19. package/common/mixins/RealtimeLayerMixin.d.ts +267 -0
  20. package/common/mixins/RealtimeLayerMixin.js +401 -393
  21. package/common/styles/index.d.ts +4 -0
  22. package/common/styles/realtimeDefaultStyle.d.ts +36 -0
  23. package/common/styles/realtimeDefaultStyle.js +6 -6
  24. package/common/styles/realtimeDelayStyle.d.ts +12 -0
  25. package/common/styles/realtimeHeadingStyle.d.ts +12 -0
  26. package/common/styles/realtimeHeadingStyle.js +5 -5
  27. package/common/styles/realtimeSimpleStyle.d.ts +4 -0
  28. package/common/typedefs.d.ts +219 -0
  29. package/common/typedefs.js +7 -1
  30. package/common/utils/compareDepartures.d.ts +10 -0
  31. package/common/utils/compareDepartures.js +2 -2
  32. package/common/utils/constants.d.ts +5 -0
  33. package/common/utils/createCanvas.d.ts +10 -0
  34. package/common/utils/createDefaultCopyrightElt.d.ts +5 -0
  35. package/common/utils/createDefaultStopFinderElt.d.ts +5 -0
  36. package/common/utils/createRealtimeFilters.d.ts +12 -0
  37. package/common/utils/debounceDeparturesMessages.d.ts +12 -0
  38. package/common/utils/debounceWebsocketMessages.d.ts +11 -0
  39. package/common/utils/getLayersAsFlatArray.d.ts +3 -0
  40. package/common/utils/getLayersAsFlatArray.js +5 -1
  41. package/common/utils/getMapGlCopyrights.d.ts +17 -0
  42. package/common/utils/getMapGlCopyrights.js +3 -3
  43. package/common/utils/getRealtimeModeSuffix.d.ts +10 -0
  44. package/common/utils/getRealtimeModeSuffix.js +1 -0
  45. package/common/utils/getUrlWithParams.d.ts +8 -0
  46. package/common/utils/getVehiclePosition.d.ts +17 -0
  47. package/common/utils/getVehiclePosition.js +9 -3
  48. package/common/utils/index.d.ts +16 -0
  49. package/common/utils/realtimeConfig.d.ts +64 -0
  50. package/common/utils/removeDuplicate.d.ts +9 -0
  51. package/common/utils/renderTrajectories.d.ts +16 -0
  52. package/common/utils/renderTrajectories.js +6 -6
  53. package/common/utils/sortAndFilterDepartures.d.ts +15 -0
  54. package/common/utils/sortAndFilterDepartures.js +1 -1
  55. package/common/utils/sortByDelay.d.ts +3 -0
  56. package/common/utils/sortByDelay.js +5 -1
  57. package/common/utils/timeUtils.d.ts +23 -0
  58. package/common/utils/toMercatorExtent.d.ts +5 -0
  59. package/iife.d.ts +2 -0
  60. package/index.d.ts +9 -0
  61. package/maplibre/controls/CopyrightControl.d.ts +35 -0
  62. package/maplibre/controls/index.d.ts +1 -0
  63. package/maplibre/index.d.ts +5 -0
  64. package/maplibre/layers/Layer.d.ts +28 -0
  65. package/maplibre/layers/Layer.js +1 -1
  66. package/maplibre/layers/RealtimeLayer.d.ts +181 -0
  67. package/maplibre/layers/RealtimeLayer.js +29 -5
  68. package/maplibre/layers/index.d.ts +2 -0
  69. package/maplibre/utils/getMercatorResolution.d.ts +7 -0
  70. package/maplibre/utils/getSourceCoordinates.d.ts +7 -0
  71. package/maplibre/utils/getSourceCoordinates.js +5 -5
  72. package/maplibre/utils/index.d.ts +2 -0
  73. package/mbt.js +22103 -14430
  74. package/mbt.js.map +4 -4
  75. package/mbt.min.js +61 -58
  76. package/mbt.min.js.map +4 -4
  77. package/ol/controls/CopyrightControl.d.ts +31 -0
  78. package/ol/controls/CopyrightControl.js +18 -8
  79. package/ol/controls/RoutingControl.d.ts +202 -0
  80. package/ol/controls/RoutingControl.js +220 -219
  81. package/ol/controls/StopFinderControl.d.ts +37 -0
  82. package/ol/controls/StopFinderControl.js +4 -1
  83. package/ol/controls/index.d.ts +3 -0
  84. package/ol/index.d.ts +7 -0
  85. package/ol/index.js +1 -0
  86. package/ol/layers/Layer.d.ts +101 -0
  87. package/ol/layers/Layer.js +25 -0
  88. package/ol/layers/MaplibreLayer.d.ts +160 -0
  89. package/ol/layers/MaplibreLayer.js +97 -27
  90. package/ol/layers/MaplibreStyleLayer.d.ts +237 -0
  91. package/ol/layers/MaplibreStyleLayer.js +291 -267
  92. package/ol/layers/RealtimeLayer.d.ts +283 -0
  93. package/ol/layers/RealtimeLayer.js +143 -128
  94. package/ol/layers/VectorLayer.d.ts +18 -0
  95. package/ol/layers/VectorLayer.js +31 -0
  96. package/ol/layers/index.d.ts +5 -0
  97. package/ol/layers/index.js +3 -0
  98. package/ol/mixins/MobilityLayerMixin.d.ts +96 -0
  99. package/ol/mixins/MobilityLayerMixin.js +1 -4
  100. package/ol/mixins/PropertiesLayerMixin.d.ts +135 -0
  101. package/ol/mixins/PropertiesLayerMixin.js +112 -140
  102. package/ol/mixins/index.d.ts +1 -0
  103. package/ol/mixins/index.js +2 -0
  104. package/ol/renderers/MaplibreLayerRenderer.d.ts +0 -0
  105. package/ol/renderers/MaplibreLayerRenderer.js +142 -114
  106. package/ol/renderers/MaplibreStyleLayerRenderer.d.ts +20 -0
  107. package/ol/renderers/MaplibreStyleLayerRenderer.js +20 -23
  108. package/ol/renderers/RealtimeLayerRenderer.d.ts +22 -0
  109. package/ol/renderers/RealtimeLayerRenderer.js +58 -53
  110. package/ol/styles/fullTrajectoryDelayStyle.d.ts +6 -0
  111. package/ol/styles/fullTrajectoryStyle.d.ts +5 -0
  112. package/ol/styles/index.d.ts +3 -0
  113. package/ol/styles/routingStyle.d.ts +4 -0
  114. package/ol/utils/getFeatureInfoAtCoordinate.d.ts +8 -0
  115. package/ol/utils/getFeatureInfoAtCoordinate.js +12 -18
  116. package/ol/utils/index.d.ts +1 -0
  117. package/package.json +31 -31
  118. package/setupTests.d.ts +1 -0
  119. package/setupTests.js +3 -4
  120. package/types/common.d.ts +55 -48
  121. package/types/index.d.ts +1 -1
  122. package/types/realtime.d.ts +91 -93
  123. package/types/routing.d.ts +60 -60
  124. package/types/stops.d.ts +62 -62
  125. package/ol/layers/MapGlLayer.js +0 -142
@@ -2,30 +2,34 @@
2
2
  /* eslint-disable no-useless-constructor,@typescript-eslint/no-useless-constructor */
3
3
  /* eslint-disable no-unused-vars,@typescript-eslint/no-unused-vars */
4
4
  /* eslint-disable class-methods-use-this */
5
+ import debounce from 'lodash.debounce';
6
+ import throttle from 'lodash.throttle';
5
7
  /* eslint-disable max-classes-per-file */
6
8
  import { buffer, containsCoordinate, intersects } from 'ol/extent';
7
- import { unByKey } from 'ol/Observable';
8
9
  import GeoJSON from 'ol/format/GeoJSON';
9
- import debounce from 'lodash.debounce';
10
- import throttle from 'lodash.throttle';
10
+ import { unByKey } from 'ol/Observable';
11
11
  import { fromLonLat } from 'ol/proj';
12
- import realtimeDefaultStyle from '../styles/realtimeDefaultStyle';
13
12
  import { RealtimeAPI, RealtimeModes } from '../../api';
14
- import renderTrajectories from '../utils/renderTrajectories';
13
+ import realtimeDefaultStyle from '../styles/realtimeDefaultStyle';
15
14
  import * as realtimeConfig from '../utils/realtimeConfig';
15
+ import renderTrajectories from '../utils/renderTrajectories';
16
16
  /**
17
17
  * RealtimeLayerInterface.
18
18
  * @private
19
19
  */
20
20
  export class RealtimeLayerInterface {
21
21
  /**
22
- * Start the clock.
22
+ * Request the stopSequence and the fullTrajectory informations for a vehicle.
23
+ *
24
+ * @param {string} id The vehicle identifier (the train_id property).
25
+ * @param {RealtimeMode} mode The mode to request. If not defined, the layer´s mode propetrty will be used.
26
+ * @return {Promise<{stopSequence: RealtimeStopSequence, fullTrajectory: RealtimeFullTrajectory>} A promise that will be resolved with the trajectory informations.
23
27
  */
24
- start() { }
28
+ getTrajectoryInfos(id, mode) { }
25
29
  /**
26
- * Stop the clock.
30
+ * Render the trajectories
27
31
  */
28
- stop() { }
32
+ renderTrajectories() { }
29
33
  /**
30
34
  * Set the Realtime api's bbox.
31
35
  *
@@ -34,17 +38,13 @@ export class RealtimeLayerInterface {
34
38
  */
35
39
  setBbox(extent, zoom) { }
36
40
  /**
37
- * Render the trajectories
41
+ * Start the clock.
38
42
  */
39
- renderTrajectories() { }
43
+ start() { }
40
44
  /**
41
- * Request the stopSequence and the fullTrajectory informations for a vehicle.
42
- *
43
- * @param {string} id The vehicle identifier (the train_id property).
44
- * @param {RealtimeMode} mode The mode to request. If not defined, the layer´s mode propetrty will be used.
45
- * @return {Promise<{stopSequence: StopSequence, fullTrajectory: FullTrajectory>} A promise that will be resolved with the trajectory informations.
45
+ * Stop the clock.
46
46
  */
47
- getTrajectoryInfos(id, mode) { }
47
+ stop() { }
48
48
  }
49
49
  /**
50
50
  * Mixin for RealtimeLayerInterface.
@@ -54,7 +54,7 @@ export class RealtimeLayerInterface {
54
54
  * @private
55
55
  */
56
56
  function RealtimeLayerMixin(Base) {
57
- // @ts-ignore
57
+ // @ts-expect-error
58
58
  return class Mixin extends Base {
59
59
  constructor(options) {
60
60
  super(Object.assign({ hitTolerance: 10 }, options));
@@ -110,7 +110,7 @@ function RealtimeLayerMixin(Base) {
110
110
  this.isUpdateBboxOnMoveEnd = options.isUpdateBboxOnMoveEnd !== false;
111
111
  // Define throttling and debounce render function
112
112
  this.throttleRenderTrajectories = throttle(this.renderTrajectoriesInternal, 50, { leading: false, trailing: true });
113
- this.debounceRenderTrajectories = debounce(this.renderTrajectoriesInternal, 50, { leading: true, trailing: true, maxWait: 5000 });
113
+ this.debounceRenderTrajectories = debounce(this.renderTrajectoriesInternal, 50, { leading: true, maxWait: 5000, trailing: true });
114
114
  // Bind callbacks
115
115
  // this.onFeatureHover = this.onFeatureHover.bind(this);
116
116
  // this.onFeatureClick = this.onFeatureClick.bind(this);
@@ -122,6 +122,28 @@ function RealtimeLayerMixin(Base) {
122
122
  this.onDocumentVisibilityChange =
123
123
  this.onDocumentVisibilityChange.bind(this);
124
124
  }
125
+ /**
126
+ * Add a trajectory.
127
+ * @param {RealtimeTrajectory} trajectory The trajectory to add.
128
+ * @private
129
+ */
130
+ addTrajectory(trajectory) {
131
+ if (!this.trajectories) {
132
+ this.trajectories = {};
133
+ }
134
+ const id = trajectory.properties.train_id;
135
+ if (id !== undefined) {
136
+ this.trajectories[id] = trajectory;
137
+ }
138
+ // @ts-expect-error the parameter are set by subclasses
139
+ this.renderTrajectories();
140
+ }
141
+ attachToMap(map) {
142
+ super.attachToMap(map);
143
+ // To avoid browser hanging when the tab is not visible for a certain amount of time,
144
+ // We stop the rendering and the websocket when hide and start again when show.
145
+ document.addEventListener('visibilitychange', this.onDocumentVisibilityChange);
146
+ }
125
147
  /**
126
148
  * Define layer's properties.
127
149
  *
@@ -129,14 +151,20 @@ function RealtimeLayerMixin(Base) {
129
151
  */
130
152
  defineProperties(options) {
131
153
  (super.defineProperties || (() => { }))(options);
132
- const { style, speed, pixelRatio, hoverVehicleId, selectedVehicleId, filter, sort, time, live, canvas, styleOptions, mode, bboxParameters, } = options;
154
+ const { bboxParameters, canvas, filter, hoverVehicleId, live, mode, pixelRatio, selectedVehicleId, sort, speed, style, styleOptions, time, } = options;
133
155
  let currCanvas = canvas;
134
156
  let currSpeed = speed || 1;
135
157
  let currTime = time || new Date();
136
158
  let currMode = mode || RealtimeModes.TOPOGRAPHIC;
137
159
  let currStyle = style || realtimeDefaultStyle;
138
160
  Object.defineProperties(this, {
139
- isTrackerLayer: { value: true },
161
+ /**
162
+ * Custom parameters to send on each BBOX request.
163
+ */
164
+ bboxParameters: {
165
+ value: bboxParameters,
166
+ writable: true,
167
+ },
140
168
  canvas: {
141
169
  get: () => {
142
170
  if (!currCanvas) {
@@ -148,6 +176,29 @@ function RealtimeLayerMixin(Base) {
148
176
  currCanvas = cnvas;
149
177
  },
150
178
  },
179
+ /**
180
+ * Function to filter which vehicles to display.
181
+ */
182
+ filter: {
183
+ value: filter,
184
+ writable: true,
185
+ },
186
+ /**
187
+ * Id of the hovered vehicle.
188
+ */
189
+ hoverVehicleId: {
190
+ value: hoverVehicleId,
191
+ writable: true,
192
+ },
193
+ isTrackerLayer: { value: true },
194
+ /**
195
+ * If true. The layer will always use Date.now() on the next tick to render the trajectories.
196
+ * When true, setting the time property has no effect.
197
+ */
198
+ live: {
199
+ value: live === false ? live : true,
200
+ writable: true,
201
+ },
151
202
  /**
152
203
  * Style function used to render a vehicle.
153
204
  */
@@ -166,21 +217,26 @@ function RealtimeLayerMixin(Base) {
166
217
  },
167
218
  },
168
219
  /**
169
- * Style function used to render a vehicle.
220
+ * Id of the selected vehicle.
170
221
  */
171
- style: {
172
- get: () => currStyle,
173
- set: (newStyle) => {
174
- currStyle = newStyle;
175
- // @ts-ignore function without parameters is defined in subclasses
176
- this.renderTrajectories();
177
- },
222
+ pixelRatio: {
223
+ value: pixelRatio ||
224
+ (typeof window !== 'undefined' ? window.devicePixelRatio : 1),
225
+ writable: true,
178
226
  },
179
227
  /**
180
- * Custom options to pass as last parameter of the style function.
228
+ * Id of the selected vehicle.
181
229
  */
182
- styleOptions: {
183
- value: Object.assign(Object.assign({}, realtimeConfig), (styleOptions || {})),
230
+ selectedVehicleId: {
231
+ value: selectedVehicleId,
232
+ writable: true,
233
+ },
234
+ /**
235
+ * Function to sort the vehicles to display.
236
+ */
237
+ sort: {
238
+ value: sort,
239
+ writable: true,
184
240
  },
185
241
  /**
186
242
  * Speed of the wheel of time.
@@ -194,33 +250,21 @@ function RealtimeLayerMixin(Base) {
194
250
  },
195
251
  },
196
252
  /**
197
- * Custom parameters to send on each BBOX request.
198
- */
199
- bboxParameters: {
200
- value: bboxParameters,
201
- writable: true,
202
- },
203
- /**
204
- * Function to filter which vehicles to display.
205
- */
206
- filter: {
207
- value: filter,
208
- writable: true,
209
- },
210
- /**
211
- * Function to sort the vehicles to display.
253
+ * Style function used to render a vehicle.
212
254
  */
213
- sort: {
214
- value: sort,
215
- writable: true,
255
+ style: {
256
+ get: () => currStyle,
257
+ set: (newStyle) => {
258
+ currStyle = newStyle;
259
+ // @ts-expect-error function without parameters is defined in subclasses
260
+ this.renderTrajectories();
261
+ },
216
262
  },
217
263
  /**
218
- * If true. The layer will always use Date.now() on the next tick to render the trajectories.
219
- * When true, setting the time property has no effect.
264
+ * Custom options to pass as last parameter of the style function.
220
265
  */
221
- live: {
222
- value: live === false ? live : true,
223
- writable: true,
266
+ styleOptions: {
267
+ value: Object.assign(Object.assign({}, realtimeConfig), (styleOptions || {})),
224
268
  },
225
269
  /**
226
270
  * Time used to display the trajectories. Can be a Date or a number in ms representing a Date.
@@ -229,8 +273,8 @@ function RealtimeLayerMixin(Base) {
229
273
  time: {
230
274
  get: () => currTime,
231
275
  set: (newTime) => {
232
- currTime = newTime && newTime.getTime ? newTime : new Date(newTime);
233
- // @ts-ignore function without parameters is defined in subclasses
276
+ currTime = (newTime === null || newTime === void 0 ? void 0 : newTime.getTime) ? newTime : new Date(newTime);
277
+ // @ts-expect-error function without parameters is defined in subclasses
234
278
  this.renderTrajectories();
235
279
  },
236
280
  },
@@ -242,25 +286,10 @@ function RealtimeLayerMixin(Base) {
242
286
  writable: true,
243
287
  },
244
288
  /**
245
- * Id of the hovered vehicle.
246
- */
247
- hoverVehicleId: {
248
- value: hoverVehicleId,
249
- writable: true,
250
- },
251
- /**
252
- * Id of the selected vehicle.
253
- */
254
- selectedVehicleId: {
255
- value: selectedVehicleId,
256
- writable: true,
257
- },
258
- /**
259
- * Id of the selected vehicle.
289
+ * If true, encapsulates the renderTrajectories calls in a debounce function.
260
290
  */
261
- pixelRatio: {
262
- value: pixelRatio ||
263
- (typeof window !== 'undefined' ? window.devicePixelRatio : 1),
291
+ useDebounce: {
292
+ value: options.useDebounce || false,
264
293
  writable: true,
265
294
  },
266
295
  /**
@@ -277,13 +306,6 @@ function RealtimeLayerMixin(Base) {
277
306
  value: options.useThrottle !== false,
278
307
  writable: true,
279
308
  },
280
- /**
281
- * If true, encapsulates the renderTrajectories calls in a debounce function.
282
- */
283
- useDebounce: {
284
- value: options.useDebounce || false,
285
- writable: true,
286
- },
287
309
  /**
288
310
  * Debug properties.
289
311
  */
@@ -297,12 +319,6 @@ function RealtimeLayerMixin(Base) {
297
319
  // },
298
320
  });
299
321
  }
300
- attachToMap(map) {
301
- super.attachToMap(map);
302
- // To avoid browser hanging when the tab is not visible for a certain amount of time,
303
- // We stop the rendering and the websocket when hide and start again when show.
304
- document.addEventListener('visibilitychange', this.onDocumentVisibilityChange);
305
- }
306
322
  detachFromMap() {
307
323
  document.removeEventListener('visibilitychange', this.onDocumentVisibilityChange);
308
324
  this.stop();
@@ -315,99 +331,231 @@ function RealtimeLayerMixin(Base) {
315
331
  super.detachFromMap();
316
332
  }
317
333
  }
318
- start() {
319
- this.stop();
320
- // Before starting to update trajectories, we remove trajectories that have
321
- // a time_intervals in the past, it will
322
- // avoid phantom train that are at the end of their route because we never
323
- // received the deleted_vehicle event because we have changed the browser tab.
324
- this.purgeOutOfDateTrajectories();
325
- // @ts-ignore function without parameters must be define in subclasses
326
- this.renderTrajectories();
327
- this.startUpdateTime();
328
- this.api.open();
329
- this.api.subscribeTrajectory(this.mode, this.onTrajectoryMessage, undefined, this.isUpdateBboxOnMoveEnd);
330
- this.api.subscribeDeletedVehicles(this.mode, this.onDeleteTrajectoryMessage, undefined, this.isUpdateBboxOnMoveEnd);
331
- if (this.isUpdateBboxOnMoveEnd) {
332
- // Update the bbox on each move end
333
- // @ts-ignore function without parameters defined by subclasses
334
- this.setBbox();
335
- }
336
- if (this.onStart) {
337
- this.onStart(this);
338
- }
339
- }
340
334
  /**
341
- * Start the clock.
342
- * @private
335
+ * Request feature information for a given coordinate.
336
+ *
337
+ * @param {ol/coordinate~Coordinate} coordinate Coordinate.
338
+ * @param {Object} options Options See child classes to see which options are supported.
339
+ * @param {number} [options.resolution=1] The resolution of the map.
340
+ * @param {number} [options.nb=Infinity] The max number of vehicles to return.
341
+ * @return {Promise<FeatureInfo>} Promise with features, layer and coordinate.
343
342
  */
344
- startUpdateTime() {
345
- this.stopUpdateTime();
346
- this.updateTimeDelay = this.getRefreshTimeInMs() || 0;
347
- this.updateTimeInterval = window.setInterval(() => {
348
- // When live=true, we update the time with new Date();
349
- if (this.live) {
350
- this.time = new Date();
343
+ getFeatureInfoAtCoordinate(coordinate, options) {
344
+ const { nb, resolution } = options;
345
+ const ext = buffer([...coordinate, ...coordinate], this.hitTolerance * resolution);
346
+ let trajectories = Object.values(this.trajectories || {});
347
+ if (this.sort) {
348
+ // @ts-expect-error good type must be defined
349
+ trajectories = trajectories.sort(this.sort);
350
+ }
351
+ const vehicles = [];
352
+ for (let i = 0; i < trajectories.length; i += 1) {
353
+ // @ts-expect-error coordinate is added by the RealtimeLayer
354
+ const { coordinate: trajcoord } = trajectories[i].properties;
355
+ if (trajcoord && containsCoordinate(ext, trajcoord)) {
356
+ vehicles.push(trajectories[i]);
351
357
  }
352
- else if (this.time && this.updateTimeDelay && this.speed) {
353
- this.time = new Date(this.time.getTime() + this.updateTimeDelay * this.speed);
358
+ if (vehicles.length === nb) {
359
+ break;
354
360
  }
355
- }, this.updateTimeDelay);
356
- }
357
- stop() {
358
- this.api.unsubscribeTrajectory(this.onTrajectoryMessage);
359
- this.api.unsubscribeDeletedVehicles(this.onDeleteTrajectoryMessage);
360
- this.api.close();
361
- if (this.onStop) {
362
- this.onStop(this);
363
361
  }
362
+ return Promise.resolve({
363
+ coordinate,
364
+ features: vehicles.map((vehicle) => this.format.readFeature(vehicle)),
365
+ layer: this,
366
+ });
364
367
  }
365
368
  /**
366
- * Stop the clock.
369
+ * Get the duration before the next update depending on zoom level.
370
+ *
367
371
  * @private
372
+ * @param {number} zoom
368
373
  */
369
- stopUpdateTime() {
370
- if (this.updateTimeInterval) {
371
- clearInterval(this.updateTimeInterval);
372
- this.updateTimeInterval = undefined;
374
+ getRefreshTimeInMs(zoom = 0) {
375
+ var _a;
376
+ const roundedZoom = zoom !== undefined ? Math.round(zoom) : -1;
377
+ const timeStep = this.getRenderTimeIntervalByZoom(roundedZoom) || 25;
378
+ const nextTick = Math.max(25, timeStep / (this.speed || 1));
379
+ const nextThrottleTick = Math.min(nextTick, 500);
380
+ // TODO: see if this should go elsewhere.
381
+ if (this.useThrottle) {
382
+ this.throttleRenderTrajectories = throttle(this.renderTrajectoriesInternal, nextThrottleTick, { leading: true, trailing: true });
373
383
  }
374
- }
375
- /**
376
- * Launch renderTrajectories. it avoids duplicating code in renderTrajectories method.
377
- *
378
- * @param {object} viewState The view state of the map.
379
- * @param {number[2]} viewState.center Center coordinate of the map in mercator coordinate.
380
- * @param {number[4]} viewState.extent Extent of the map in mercator coordinates.
381
- * @param {number[2]} viewState.size Size ([width, height]) of the canvas to render.
382
- * @param {number} [viewState.rotation = 0] Rotation of the map to render.
383
- * @param {number} viewState.resolution Resolution of the map to render.
384
- * @param {boolean} noInterpolate If true trajectories are not interpolated but
385
- * drawn at the last known coordinate. Use this for performance optimization
386
- * during map navigation.
384
+ else if (this.useDebounce) {
385
+ this.debounceRenderTrajectories = debounce(this.renderTrajectoriesInternal, nextThrottleTick, { leading: true, maxWait: 5000, trailing: true });
386
+ }
387
+ if ((_a = this.api) === null || _a === void 0 ? void 0 : _a.buffer) {
388
+ const [, size] = this.api.buffer;
389
+ this.api.buffer = [nextThrottleTick, size];
390
+ }
391
+ return nextTick;
392
+ }
393
+ /**
394
+ * Request the stopSequence and the fullTrajectory informations for a vehicle.
395
+ *
396
+ * @param {string} id The vehicle identifier (the train_id property).
397
+ * @return {Promise<{stopSequence: RealtimeStopSequence, fullTrajectory: RealtimeFullTrajectory>} A promise that will be resolved with the trajectory informations.
398
+ */
399
+ getTrajectoryInfos(id) {
400
+ var _a, _b;
401
+ // When a vehicle is selected, we request the complete stop sequence and the complete full trajectory.
402
+ // Then we combine them in one response and send them to inherited layers.
403
+ const promises = [
404
+ this.api.getStopSequence(id),
405
+ this.api.getFullTrajectory(id, this.mode, this.getGeneralizationLevelByZoom(Math.floor(((_b = (_a = this.map) === null || _a === void 0 ? void 0 : _a.getView()) === null || _b === void 0 ? void 0 : _b.getZoom()) || 0))),
406
+ ];
407
+ return Promise.all(promises).then(([stopSequence, fullTrajectory]) => {
408
+ const response = {
409
+ fullTrajectory,
410
+ stopSequence,
411
+ };
412
+ return response;
413
+ });
414
+ }
415
+ /**
416
+ * Get vehicle.
417
+ * @param {function} filterFc A function use to filter results.
418
+ * @return {Array<Object>} Array of vehicle.
419
+ */
420
+ getVehicle(filterFc) {
421
+ return ((this.trajectories &&
422
+ // @ts-expect-error good type must be defined
423
+ Object.values(this.trajectories).filter(filterFc)) ||
424
+ []);
425
+ }
426
+ highlightVehicle(id) {
427
+ if (this.hoverVehicleId !== id) {
428
+ /** @private */
429
+ this.hoverVehicleId = id;
430
+ // @ts-expect-error good type must be defined
431
+ this.renderTrajectories(true);
432
+ }
433
+ }
434
+ /**
435
+ * Callback on websocket's deleted_vehicles channel events.
436
+ * It removes the trajectory from the list.
437
+ *
387
438
  * @private
439
+ * @override
388
440
  */
389
- renderTrajectoriesInternal(viewState, noInterpolate = false) {
390
- var _a;
391
- if (!this.map || !this.trajectories) {
392
- return false;
441
+ onDeleteTrajectoryMessage(data) {
442
+ if (!data.content) {
443
+ return;
393
444
  }
394
- const time = this.live ? Date.now() : (_a = this.time) === null || _a === void 0 ? void 0 : _a.getTime();
395
- const trajectories = Object.values(this.trajectories);
396
- // console.time('sort');
397
- if (this.sort) {
398
- // @ts-ignore
399
- trajectories.sort(this.sort);
445
+ this.removeTrajectory(data.content);
446
+ }
447
+ onDocumentVisibilityChange() {
448
+ if (document.hidden) {
449
+ this.stop();
450
+ // Since we don't receive deleted_vehicles event when docuement
451
+ // is hidden. We have to clean all the trajectories for a fresh
452
+ // start when the document is visible again.
453
+ this.trajectories = {};
400
454
  }
401
- // console.timeEnd('sort');
402
- if (!this.canvas || !this.style) {
455
+ else {
456
+ if (this.getVisible() === false) {
457
+ return;
458
+ }
459
+ this.start();
460
+ }
461
+ }
462
+ /**
463
+ * Callback on websocket's trajectory channel events.
464
+ * It adds a trajectory to the list.
465
+ *
466
+ * @private
467
+ */
468
+ onTrajectoryMessage(data) {
469
+ if (!data.content) {
470
+ return;
471
+ }
472
+ const trajectory = data.content;
473
+ const { geometry, properties: { raw_coordinates: rawCoordinates, time_since_update: timeSinceUpdate, train_id: id, }, } = trajectory;
474
+ // ignore old events [SBAHNM-97]
475
+ // @ts-expect-error can be undefined
476
+ if (timeSinceUpdate < 0) {
477
+ return;
478
+ }
479
+ // console.time(`onTrajectoryMessage${data.content.properties.train_id}`);
480
+ // @ts-expect-error default value for extentand zoom are provided by subclasses
481
+ if (this.purgeTrajectory(trajectory)) {
482
+ return;
483
+ }
484
+ if (this.debug &&
485
+ this.mode === RealtimeModes.TOPOGRAPHIC &&
486
+ rawCoordinates) {
487
+ // @ts-expect-error
488
+ trajectory.properties.olGeometry = this.format.readGeometry({
489
+ coordinates: fromLonLat(rawCoordinates, this.map.getView().getProjection()),
490
+ type: 'Point',
491
+ });
492
+ }
493
+ else {
494
+ // @ts-expect-error
495
+ trajectory.properties.olGeometry = this.format.readGeometry(geometry);
496
+ }
497
+ // TODO Make sure the timeOffset is useful. May be we can remove it.
498
+ // @ts-expect-error
499
+ trajectory.properties.timeOffset = Date.now() - data.timestamp;
500
+ this.addTrajectory(trajectory);
501
+ }
502
+ /**
503
+ * On zoomend we adjust the time interval of the update of vehicles positions.
504
+ *
505
+ * @param evt Event that triggered the function.
506
+ * @private
507
+ */
508
+ onZoomEnd() {
509
+ this.startUpdateTime();
510
+ }
511
+ /**
512
+ * Remove all trajectories that are in the past.
513
+ */
514
+ purgeOutOfDateTrajectories() {
515
+ Object.entries(this.trajectories || {}).forEach(([key, trajectory]) => {
516
+ var _a;
517
+ const timeIntervals = (_a = trajectory === null || trajectory === void 0 ? void 0 : trajectory.properties) === null || _a === void 0 ? void 0 : _a.time_intervals;
518
+ if (this.time && (timeIntervals === null || timeIntervals === void 0 ? void 0 : timeIntervals.length)) {
519
+ const lastTimeInterval = timeIntervals[timeIntervals.length - 1][0];
520
+ if (lastTimeInterval < this.time.getTime()) {
521
+ this.removeTrajectory(key);
522
+ }
523
+ }
524
+ });
525
+ }
526
+ /**
527
+ * Determine if the trajectory is useless and should be removed from the list or not.
528
+ * By default, this function exclude vehicles:
529
+ * - that have their trajectory outside the current extent and
530
+ * - that aren't in the MOT list.
531
+ *
532
+ * @param {RealtimeTrajectory} trajectory
533
+ * @param {Array<number>} extent
534
+ * @param {number} zoom
535
+ * @return {boolean} if the trajectory must be displayed or not.
536
+ * @private
537
+ */
538
+ purgeTrajectory(trajectory, extent, zoom) {
539
+ const { bounds, type } = trajectory.properties;
540
+ if ((this.isUpdateBboxOnMoveEnd && !intersects(extent, bounds)) ||
541
+ (this.mots && !this.mots.includes(type))) {
542
+ this.removeTrajectory(trajectory);
403
543
  return true;
404
544
  }
405
- // console.time('render');
406
- this.renderState = renderTrajectories(this.canvas, trajectories, this.style, Object.assign(Object.assign({}, viewState), { pixelRatio: this.pixelRatio || 1, time }), Object.assign({ filter: this.filter, noInterpolate: (viewState.zoom || 0) < this.minZoomInterpolation
407
- ? true
408
- : noInterpolate, hoverVehicleId: this.hoverVehicleId, selectedVehicleId: this.selectedVehicleId }, this.styleOptions));
409
- // console.timeEnd('render');
410
- return true;
545
+ return false;
546
+ }
547
+ removeTrajectory(trajectoryOrId) {
548
+ var _a;
549
+ let id;
550
+ if (typeof trajectoryOrId !== 'string') {
551
+ id = (_a = trajectoryOrId === null || trajectoryOrId === void 0 ? void 0 : trajectoryOrId.properties) === null || _a === void 0 ? void 0 : _a.train_id;
552
+ }
553
+ else {
554
+ id = trajectoryOrId;
555
+ }
556
+ if (id !== undefined && this.trajectories) {
557
+ delete this.trajectories[id];
558
+ }
411
559
  }
412
560
  /**
413
561
  * Render the trajectories requesting an animation frame and cancelling the previous one.
@@ -447,6 +595,51 @@ function RealtimeLayerMixin(Base) {
447
595
  this.renderTrajectoriesInternal(viewState, noInterpolate);
448
596
  }
449
597
  }
598
+ /**
599
+ * Launch renderTrajectories. it avoids duplicating code in renderTrajectories method.
600
+ *
601
+ * @param {object} viewState The view state of the map.
602
+ * @param {number[2]} viewState.center Center coordinate of the map in mercator coordinate.
603
+ * @param {number[4]} viewState.extent Extent of the map in mercator coordinates.
604
+ * @param {number[2]} viewState.size Size ([width, height]) of the canvas to render.
605
+ * @param {number} [viewState.rotation = 0] Rotation of the map to render.
606
+ * @param {number} viewState.resolution Resolution of the map to render.
607
+ * @param {boolean} noInterpolate If true trajectories are not interpolated but
608
+ * drawn at the last known coordinate. Use this for performance optimization
609
+ * during map navigation.
610
+ * @private
611
+ */
612
+ renderTrajectoriesInternal(viewState, noInterpolate = false) {
613
+ var _a;
614
+ if (!this.map || !this.trajectories) {
615
+ return false;
616
+ }
617
+ const time = this.live ? Date.now() : (_a = this.time) === null || _a === void 0 ? void 0 : _a.getTime();
618
+ const trajectories = Object.values(this.trajectories);
619
+ // console.time('sort');
620
+ if (this.sort) {
621
+ // @ts-expect-error type problem
622
+ trajectories.sort(this.sort);
623
+ }
624
+ // console.timeEnd('sort');
625
+ if (!this.canvas || !this.style) {
626
+ return true;
627
+ }
628
+ // console.time('render');
629
+ this.renderState = renderTrajectories(this.canvas, trajectories, this.style, Object.assign(Object.assign({}, viewState), { pixelRatio: this.pixelRatio || 1, time }), Object.assign({ filter: this.filter, hoverVehicleId: this.hoverVehicleId, noInterpolate: (viewState.zoom || 0) < this.minZoomInterpolation
630
+ ? true
631
+ : noInterpolate, selectedVehicleId: this.selectedVehicleId }, this.styleOptions));
632
+ // console.timeEnd('render');
633
+ return true;
634
+ }
635
+ selectVehicle(id) {
636
+ if (this.selectedVehicleId !== id) {
637
+ /** @private */
638
+ this.selectedVehicleId = id;
639
+ // @ts-expect-error
640
+ this.renderTrajectories(true);
641
+ }
642
+ }
450
643
  setBbox(extent, zoom) {
451
644
  // Clean trajectories before sending the new bbox
452
645
  // Purge trajectories:
@@ -473,9 +666,9 @@ function RealtimeLayerMixin(Base) {
473
666
  zoomFloor,
474
667
  ];
475
668
  /* @private */
476
- const generalizationLevel = this.getGeneralizationLevelByZoom(zoomFloor);
669
+ this.generalizationLevel = this.getGeneralizationLevelByZoom(zoomFloor);
477
670
  if (this.generalizationLevel) {
478
- bbox.push(`gen=${generalizationLevel}`);
671
+ bbox.push(`gen=${this.generalizationLevel}`);
479
672
  }
480
673
  /* @private */
481
674
  this.mots = this.getMotsByZoom(zoomFloor);
@@ -496,246 +689,61 @@ function RealtimeLayerMixin(Base) {
496
689
  // Extent and zoom level are mandatory.
497
690
  this.api.bbox = bbox;
498
691
  }
499
- /**
500
- * Get the duration before the next update depending on zoom level.
501
- *
502
- * @private
503
- * @param {number} zoom
504
- */
505
- getRefreshTimeInMs(zoom = 0) {
506
- var _a;
507
- const roundedZoom = zoom !== undefined ? Math.round(zoom) : -1;
508
- const timeStep = this.getRenderTimeIntervalByZoom(roundedZoom) || 25;
509
- const nextTick = Math.max(25, timeStep / (this.speed || 1));
510
- const nextThrottleTick = Math.min(nextTick, 500);
511
- // TODO: see if this should go elsewhere.
512
- if (this.useThrottle) {
513
- this.throttleRenderTrajectories = throttle(this.renderTrajectoriesInternal, nextThrottleTick, { leading: true, trailing: true });
514
- }
515
- else if (this.useDebounce) {
516
- this.debounceRenderTrajectories = debounce(this.renderTrajectoriesInternal, nextThrottleTick, { leading: true, trailing: true, maxWait: 5000 });
517
- }
518
- if ((_a = this.api) === null || _a === void 0 ? void 0 : _a.buffer) {
519
- const [, size] = this.api.buffer;
520
- this.api.buffer = [nextThrottleTick, size];
521
- }
522
- return nextTick;
523
- }
524
- /**
525
- * Get vehicle.
526
- * @param {function} filterFc A function use to filter results.
527
- * @return {Array<Object>} Array of vehicle.
528
- */
529
- getVehicle(filterFc) {
530
- return ((this.trajectories &&
531
- // @ts-ignore
532
- Object.values(this.trajectories).filter(filterFc)) ||
533
- []);
534
- }
535
- /**
536
- * Request feature information for a given coordinate.
537
- *
538
- * @param {ol/coordinate~Coordinate} coordinate Coordinate.
539
- * @param {Object} options Options See child classes to see which options are supported.
540
- * @param {number} [options.resolution=1] The resolution of the map.
541
- * @param {number} [options.nb=Infinity] The max number of vehicles to return.
542
- * @return {Promise<FeatureInfo>} Promise with features, layer and coordinate.
543
- */
544
- getFeatureInfoAtCoordinate(coordinate, options) {
545
- const { resolution, nb } = options;
546
- const ext = buffer([...coordinate, ...coordinate], this.hitTolerance * resolution);
547
- let trajectories = Object.values(this.trajectories || {});
548
- if (this.sort) {
549
- // @ts-ignore
550
- trajectories = trajectories.sort(this.sort);
551
- }
552
- const vehicles = [];
553
- for (let i = 0; i < trajectories.length; i += 1) {
554
- if (trajectories[i].properties.coordinate &&
555
- containsCoordinate(ext, trajectories[i].properties.coordinate)) {
556
- vehicles.push(trajectories[i]);
557
- }
558
- if (vehicles.length === nb) {
559
- break;
560
- }
561
- }
562
- return Promise.resolve({
563
- layer: this,
564
- features: vehicles,
565
- coordinate,
566
- });
567
- }
568
- /**
569
- * Request the stopSequence and the fullTrajectory informations for a vehicle.
570
- *
571
- * @param {string} id The vehicle identifier (the train_id property).
572
- * @return {Promise<{stopSequence: StopSequence, fullTrajectory: FullTrajectory>} A promise that will be resolved with the trajectory informations.
573
- */
574
- getTrajectoryInfos(id) {
575
- var _a, _b;
576
- // When a vehicle is selected, we request the complete stop sequence and the complete full trajectory.
577
- // Then we combine them in one response and send them to inherited layers.
578
- const promises = [
579
- this.api.getStopSequence(id),
580
- this.api.getFullTrajectory(id, this.mode, this.getGeneralizationLevelByZoom(Math.floor(((_b = (_a = this.map) === null || _a === void 0 ? void 0 : _a.getView()) === null || _b === void 0 ? void 0 : _b.getZoom()) || 0))),
581
- ];
582
- return Promise.all(promises).then(([stopSequence, fullTrajectory]) => {
583
- const response = {
584
- stopSequence,
585
- fullTrajectory,
586
- };
587
- return response;
588
- });
589
- }
590
- /**
591
- * Remove all trajectories that are in the past.
592
- */
593
- purgeOutOfDateTrajectories() {
594
- Object.entries(this.trajectories || {}).forEach(([key, trajectory]) => {
595
- var _a;
596
- const timeIntervals = (_a = trajectory === null || trajectory === void 0 ? void 0 : trajectory.properties) === null || _a === void 0 ? void 0 : _a.time_intervals;
597
- if (this.time && timeIntervals.length) {
598
- const lastTimeInterval = timeIntervals[timeIntervals.length - 1][0];
599
- if (lastTimeInterval < this.time) {
600
- this.removeTrajectory(key);
601
- }
602
- }
603
- });
604
- }
605
- /**
606
- * Determine if the trajectory is useless and should be removed from the list or not.
607
- * By default, this function exclude vehicles:
608
- * - that have their trajectory outside the current extent and
609
- * - that aren't in the MOT list.
610
- *
611
- * @param {RealtimeTrajectory} trajectory
612
- * @param {Array<number>} extent
613
- * @param {number} zoom
614
- * @return {boolean} if the trajectory must be displayed or not.
615
- * @private
616
- */
617
- purgeTrajectory(trajectory, extent, zoom) {
618
- const { type, bounds } = trajectory.properties;
619
- if ((this.isUpdateBboxOnMoveEnd && !intersects(extent, bounds)) ||
620
- (this.mots && !this.mots.includes(type))) {
621
- this.removeTrajectory(trajectory);
622
- return true;
623
- }
624
- return false;
625
- }
626
- /**
627
- * Add a trajectory.
628
- * @param {RealtimeTrajectory} trajectory The trajectory to add.
629
- * @private
630
- */
631
- addTrajectory(trajectory) {
632
- if (!this.trajectories) {
633
- this.trajectories = {};
634
- }
635
- this.trajectories[trajectory.properties.train_id] = trajectory;
636
- // @ts-ignore the parameter are set by subclasses
692
+ start() {
693
+ this.stop();
694
+ // Before starting to update trajectories, we remove trajectories that have
695
+ // a time_intervals in the past, it will
696
+ // avoid phantom train that are at the end of their route because we never
697
+ // received the deleted_vehicle event because we have changed the browser tab.
698
+ this.purgeOutOfDateTrajectories();
699
+ // @ts-expect-error function without parameters must be define in subclasses
637
700
  this.renderTrajectories();
638
- }
639
- removeTrajectory(trajectoryOrId) {
640
- var _a;
641
- let id;
642
- if (typeof trajectoryOrId !== 'string') {
643
- id = (_a = trajectoryOrId === null || trajectoryOrId === void 0 ? void 0 : trajectoryOrId.properties) === null || _a === void 0 ? void 0 : _a.train_id;
644
- }
645
- else {
646
- id = trajectoryOrId;
701
+ this.startUpdateTime();
702
+ this.api.open();
703
+ this.api.subscribeTrajectory(this.mode, this.onTrajectoryMessage, undefined, this.isUpdateBboxOnMoveEnd);
704
+ this.api.subscribeDeletedVehicles(this.mode, this.onDeleteTrajectoryMessage, undefined, this.isUpdateBboxOnMoveEnd);
705
+ if (this.isUpdateBboxOnMoveEnd) {
706
+ // Update the bbox on each move end
707
+ // @ts-expect-error function without parameters defined by subclasses
708
+ this.setBbox();
647
709
  }
648
- if (this.trajectories) {
649
- delete this.trajectories[id];
710
+ if (this.onStart) {
711
+ this.onStart(this);
650
712
  }
651
713
  }
652
714
  /**
653
- * On zoomend we adjust the time interval of the update of vehicles positions.
654
- *
655
- * @param evt Event that triggered the function.
715
+ * Start the clock.
656
716
  * @private
657
717
  */
658
- onZoomEnd() {
659
- this.startUpdateTime();
660
- }
661
- onDocumentVisibilityChange() {
662
- if (document.hidden) {
663
- this.stop();
664
- // Since we don't receive deleted_vehicles event when docuement
665
- // is hidden. We have to clean all the trajectories for a fresh
666
- // start when the document is visible again.
667
- this.trajectories = {};
668
- }
669
- else {
670
- if (this.visible === false) {
671
- return;
718
+ startUpdateTime() {
719
+ this.stopUpdateTime();
720
+ this.updateTimeDelay = this.getRefreshTimeInMs() || 0;
721
+ this.updateTimeInterval = window.setInterval(() => {
722
+ // When live=true, we update the time with new Date();
723
+ if (this.live) {
724
+ this.time = new Date();
672
725
  }
673
- this.start();
674
- }
726
+ else if (this.time && this.updateTimeDelay && this.speed) {
727
+ this.time = new Date(this.time.getTime() + this.updateTimeDelay * this.speed);
728
+ }
729
+ }, this.updateTimeDelay);
675
730
  }
676
- /**
677
- * Callback on websocket's trajectory channel events.
678
- * It adds a trajectory to the list.
679
- *
680
- * @private
681
- */
682
- onTrajectoryMessage(data) {
683
- if (!data.content) {
684
- return;
685
- }
686
- const trajectory = data.content;
687
- const { geometry, properties: { train_id: id, time_since_update: timeSinceUpdate, raw_coordinates: rawCoordinates, }, } = trajectory;
688
- // ignore old events [SBAHNM-97]
689
- if (timeSinceUpdate < 0) {
690
- return;
691
- }
692
- // console.time(`onTrajectoryMessage${data.content.properties.train_id}`);
693
- // @ts-ignore default value for extentand zoom are provided by subclasses
694
- if (this.purgeTrajectory(trajectory)) {
695
- return;
696
- }
697
- if (this.debug &&
698
- this.mode === RealtimeModes.TOPOGRAPHIC &&
699
- rawCoordinates) {
700
- trajectory.properties.olGeometry = this.format.readGeometry({
701
- type: 'Point',
702
- coordinates: fromLonLat(rawCoordinates, this.map.getView().getProjection()),
703
- });
704
- }
705
- else {
706
- trajectory.properties.olGeometry = this.format.readGeometry(geometry);
731
+ stop() {
732
+ this.api.unsubscribeTrajectory(this.onTrajectoryMessage);
733
+ this.api.unsubscribeDeletedVehicles(this.onDeleteTrajectoryMessage);
734
+ this.api.close();
735
+ if (this.onStop) {
736
+ this.onStop(this);
707
737
  }
708
- // TODO Make sure the timeOffset is useful. May be we can remove it.
709
- trajectory.properties.timeOffset = Date.now() - data.timestamp;
710
- this.addTrajectory(trajectory);
711
738
  }
712
739
  /**
713
- * Callback on websocket's deleted_vehicles channel events.
714
- * It removes the trajectory from the list.
715
- *
740
+ * Stop the clock.
716
741
  * @private
717
- * @override
718
742
  */
719
- onDeleteTrajectoryMessage(data) {
720
- if (!data.content) {
721
- return;
722
- }
723
- this.removeTrajectory(data.content);
724
- }
725
- highlightVehicle(id) {
726
- if (this.hoverVehicleId !== id) {
727
- /** @private */
728
- this.hoverVehicleId = id;
729
- // @ts-ignore
730
- this.renderTrajectories(true);
731
- }
732
- }
733
- selectVehicle(id) {
734
- if (this.selectedVehicleId !== id) {
735
- /** @private */
736
- this.selectedVehicleId = id;
737
- // @ts-ignore
738
- this.renderTrajectories(true);
743
+ stopUpdateTime() {
744
+ if (this.updateTimeInterval) {
745
+ clearInterval(this.updateTimeInterval);
746
+ this.updateTimeInterval = undefined;
739
747
  }
740
748
  }
741
749
  };