mobility-toolbox-js 2.0.0-beta.1 → 2.0.0

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 (67) hide show
  1. package/README.md +7 -4
  2. package/api/index.js +0 -1
  3. package/api/tralis/TralisAPI.js +1 -1
  4. package/common/controls/Control.js +4 -1
  5. package/common/layers/Layer.js +18 -49
  6. package/common/layers/Layer.test.js +2 -106
  7. package/common/mixins/SearchMixin.js +1 -1
  8. package/common/mixins/TralisLayerMixin.js +549 -21
  9. package/common/styles/index.js +4 -0
  10. package/common/{utils/delayTrackerStyle.js → styles/trackerDefaultStyle.js} +8 -8
  11. package/common/styles/trackerDelayStyle.js +17 -0
  12. package/common/styles/trackerSimpleStyle.js +22 -0
  13. package/common/trackerConfig.test.js +0 -13
  14. package/common/utils/getMapboxMapCopyrights.js +1 -0
  15. package/common/utils/index.js +2 -3
  16. package/common/utils/sortByDelay.js +23 -0
  17. package/index.js +1 -1
  18. package/index.js.map +1 -1
  19. package/mapbox/controls/CopyrightControl.js +5 -1
  20. package/mapbox/index.js +0 -2
  21. package/mapbox/layers/Layer.test.js +2 -2
  22. package/mapbox/layers/TralisLayer.js +270 -5
  23. package/mapbox/layers/TralisLayer.test.js +40 -0
  24. package/module.js +1 -9
  25. package/ol/controls/CopyrightControl.js +4 -4
  26. package/ol/controls/CopyrightControl.test.js +16 -16
  27. package/ol/controls/RoutingControl.js +9 -7
  28. package/ol/controls/RoutingControl.test.js +1 -1
  29. package/ol/controls/StopFinderControl.js +8 -6
  30. package/ol/controls/StopFinderControl.test.js +1 -1
  31. package/ol/index.js +3 -3
  32. package/ol/layers/Layer.js +9 -0
  33. package/ol/layers/Layer.test.js +22 -7
  34. package/ol/layers/MapboxLayer.js +39 -44
  35. package/ol/layers/MapboxLayer.test.js +5 -5
  36. package/ol/layers/MapboxStyleLayer.js +0 -6
  37. package/ol/layers/MapboxStyleLayer.test.js +22 -6
  38. package/ol/layers/MaplibreLayer.js +280 -0
  39. package/ol/layers/RoutingLayer.test.js +1 -1
  40. package/ol/layers/TralisLayer.js +258 -76
  41. package/ol/layers/TralisLayer.test.js +1 -49
  42. package/ol/layers/VectorLayer.test.js +1 -1
  43. package/ol/layers/WMSLayer.test.js +6 -2
  44. package/ol/styles/fullTrajectoryDelayStyle.js +35 -0
  45. package/ol/styles/fullTrajectoryStyle.js +51 -0
  46. package/ol/styles/index.js +2 -0
  47. package/package.json +16 -8
  48. package/api/trajserv/TrajservAPI.js +0 -71
  49. package/api/trajserv/TrajservAPI.test.js +0 -171
  50. package/api/trajserv/TrajservAPIUtils.js +0 -191
  51. package/api/trajserv/TrajservAPIUtils.test.js +0 -40
  52. package/api/trajserv/typedefs.js +0 -44
  53. package/common/mixins/MapMixin.js +0 -103
  54. package/common/mixins/TrackerLayerMixin.js +0 -745
  55. package/common/mixins/TrajservLayerMixin.js +0 -544
  56. package/common/utils/simpleTrackerStyle.js +0 -18
  57. package/mapbox/Map.js +0 -87
  58. package/mapbox/layers/TrackerLayer.js +0 -282
  59. package/mapbox/layers/TrackerLayer.test.js +0 -68
  60. package/mapbox/layers/TrajservLayer.js +0 -114
  61. package/mapbox/layers/TrajservLayer.test.js +0 -90
  62. package/ol/Map.js +0 -109
  63. package/ol/Map.test.js +0 -34
  64. package/ol/layers/TrackerLayer.js +0 -296
  65. package/ol/layers/TrackerLayer.test.js +0 -70
  66. package/ol/layers/TrajservLayer.js +0 -190
  67. package/ol/layers/TrajservLayer.test.js +0 -113
@@ -3,12 +3,25 @@
3
3
  /* eslint-disable no-unused-vars */
4
4
  /* eslint-disable class-methods-use-this */
5
5
  /* eslint-disable max-classes-per-file */
6
+ import qs from 'query-string';
7
+ import { buffer, containsCoordinate, intersects } from 'ol/extent';
8
+ import { unByKey } from 'ol/Observable';
6
9
  import GeoJSON from 'ol/format/GeoJSON';
7
10
  import Point from 'ol/geom/Point';
8
- import { intersects } from 'ol/extent';
11
+ import debounce from 'lodash.debounce';
12
+ import throttle from 'lodash.throttle';
9
13
  import { fromLonLat } from 'ol/proj';
14
+ import Tracker from '../Tracker';
15
+ import { timeSteps } from '../trackerConfig';
16
+ import createFilters from '../utils/createTrackerFilters';
17
+ import trackerDefaultStyle from '../styles/trackerDefaultStyle';
10
18
  import { TralisAPI, TralisModes } from '../../api';
11
19
 
20
+ /* Permalink parameter used to filters vehicles */
21
+ const LINE_FILTER = 'publishedlinename';
22
+ const ROUTE_FILTER = 'tripnumber';
23
+ const OPERATOR_FILTER = 'operator';
24
+
12
25
  /**
13
26
  * TralisLayerInterface.
14
27
  */
@@ -38,6 +51,16 @@ export class TralisLayerInterface {
38
51
  */
39
52
  terminate() {}
40
53
 
54
+ /**
55
+ * Start the clock.
56
+ */
57
+ start() {}
58
+
59
+ /**
60
+ * Stop the clock.
61
+ */
62
+ stop() {}
63
+
41
64
  /**
42
65
  * Set the Tralis api's bbox.
43
66
  *
@@ -61,28 +84,20 @@ export class TralisLayerInterface {
61
84
  * @return {Promise<{stopSequence: StopSequence, fullTrajectory: FullTrajectory>} A promise that will be resolved with the trajectory informations.
62
85
  */
63
86
  getTrajectoryInfos(id, mode) {}
64
-
65
- /**
66
- * Define the style of the vehicle.
67
- * Draw a blue circle with the id of the props parameter.
68
- *
69
- * @param {TralisTrajectory} trajectory A trajectory
70
- * @param {ViewState} viewState Map's view state (zoom, resolution, center, ...)
71
- */
72
- defaultStyle(trajectory, viewState) {}
73
87
  }
74
88
 
75
89
  /**
76
90
  * Mixin for TralisLayerInterface.
77
91
  *
78
- * @param {TrackerLayer} TrackerLayer A {TrackerLayer} class to extend with {TrajservLayerInterface} functionnalities.
92
+ * @param {Class} Base A class to extend with {TralisLayerInterface} functionnalities.
79
93
  * @return {Class} A class that implements {TralisLayerInterface} class and extends Base;
80
94
  * @private
81
95
  */
82
- const TralisLayerMixin = (TrackerLayer) =>
83
- class extends TrackerLayer {
96
+ const TralisLayerMixin = (Base) =>
97
+ class extends Base {
84
98
  constructor(options = {}) {
85
- super({ ...options });
99
+ super({ hitTolerance: 10, ...options });
100
+
86
101
  this.debug = options.debug;
87
102
  this.mode = options.mode || TralisModes.TOPOGRAPHIC;
88
103
  this.api = options.api || new TralisAPI(options);
@@ -110,7 +125,24 @@ const TralisLayerMixin = (TrackerLayer) =>
110
125
  // This property will call api.setBbox on each movend event
111
126
  this.isUpdateBboxOnMoveEnd = options.isUpdateBboxOnMoveEnd !== false;
112
127
 
128
+ // Define throttling nad debounce render function
129
+ this.throttleRenderTrajectories = throttle(
130
+ this.renderTrajectoriesInternal,
131
+ 50,
132
+ { leading: false, trailing: true },
133
+ );
134
+
135
+ this.debounceRenderTrajectories = debounce(
136
+ this.renderTrajectoriesInternal,
137
+ 50,
138
+ { leading: true, trailing: true, maxWait: 5000 },
139
+ );
140
+
113
141
  // Bind callbacks
142
+ this.onFeatureHover = this.onFeatureHover.bind(this);
143
+ this.onFeatureClick = this.onFeatureClick.bind(this);
144
+ this.renderTrajectoriesInternal =
145
+ this.renderTrajectoriesInternal.bind(this);
114
146
  this.onTrajectoryMessage = this.onTrajectoryMessage.bind(this);
115
147
  this.onDeleteTrajectoryMessage =
116
148
  this.onDeleteTrajectoryMessage.bind(this);
@@ -118,9 +150,268 @@ const TralisLayerMixin = (TrackerLayer) =>
118
150
  this.onDocumentVisibilityChange.bind(this);
119
151
  }
120
152
 
153
+ /**
154
+ * Define layer's properties.
155
+ *
156
+ * @ignore
157
+ */
158
+ defineProperties(options) {
159
+ // Tracker options use to build the tracker.
160
+ let { regexPublishedLineName, publishedLineName, tripNumber, operator } =
161
+ options;
162
+ const {
163
+ style,
164
+ speed,
165
+ pixelRatio,
166
+ hoverVehicleId,
167
+ selectedVehicleId,
168
+ filter,
169
+ sort,
170
+ time,
171
+ live,
172
+ } = options;
173
+
174
+ const initTrackerOptions = {
175
+ style,
176
+ };
177
+
178
+ Object.keys(initTrackerOptions).forEach(
179
+ (key) =>
180
+ initTrackerOptions[key] === undefined &&
181
+ delete initTrackerOptions[key],
182
+ );
183
+
184
+ let currSpeed = speed || 1;
185
+ let currTime = time || new Date();
186
+
187
+ super.defineProperties(options);
188
+
189
+ Object.defineProperties(this, {
190
+ isTrackerLayer: { value: true },
191
+
192
+ /**
193
+ * Style function used to render a vehicle.
194
+ */
195
+ style: {
196
+ value: (trajectory, viewState) =>
197
+ (style || trackerDefaultStyle)(trajectory, viewState, this),
198
+ },
199
+
200
+ /**
201
+ * Speed of the wheel of time.
202
+ * If live property is true. The speed is ignored.
203
+ */
204
+ speed: {
205
+ get: () => currSpeed,
206
+ set: (newSpeed) => {
207
+ currSpeed = newSpeed;
208
+ this.start();
209
+ },
210
+ },
211
+
212
+ /**
213
+ * Function to filter which vehicles to display.
214
+ */
215
+ filter: {
216
+ value: filter,
217
+ writable: true,
218
+ },
219
+
220
+ /**
221
+ * Function to sort the vehicles to display.
222
+ */
223
+ sort: {
224
+ value: sort,
225
+ writable: true,
226
+ },
227
+
228
+ /**
229
+ * The tracker that renders the trajectories.
230
+ */
231
+ tracker: { value: null, writable: true },
232
+
233
+ /**
234
+ * Canvas cache object for trajectories drawn.
235
+ */
236
+ styleCache: { value: {} },
237
+
238
+ /**
239
+ * If true. The layer will always use Date.now() on the next tick to render the trajectories.
240
+ * When true, setting the time property has no effect.
241
+ */
242
+ live: {
243
+ value: live === false ? live : true,
244
+ writable: true,
245
+ },
246
+
247
+ /**
248
+ * Time used to display the trajectories. Can be a Date or a number in ms representing a Date.
249
+ * If live property is true. The setter does nothing.
250
+ */
251
+ time: {
252
+ get: () => currTime,
253
+ set: (newTime) => {
254
+ currTime = newTime && newTime.getTime ? newTime : new Date(newTime);
255
+ this.renderTrajectories();
256
+ },
257
+ },
258
+
259
+ /**
260
+ * Keep track of which trajectories are stored.
261
+ */
262
+ trajectories: {
263
+ value: {},
264
+ writable: true,
265
+ },
266
+
267
+ /**
268
+ * Keep track of which trajectories are currently drawn.
269
+ */
270
+ renderedTrajectories: {
271
+ get: () => (this.tracker && this.tracker.renderedTrajectories) || [],
272
+ },
273
+
274
+ /**
275
+ * Id of the hovered vehicle.
276
+ */
277
+ hoverVehicleId: {
278
+ value: hoverVehicleId,
279
+ writable: true,
280
+ },
281
+
282
+ /**
283
+ * Id of the selected vehicle.
284
+ */
285
+ selectedVehicleId: {
286
+ value: selectedVehicleId,
287
+ writable: true,
288
+ },
289
+
290
+ /**
291
+ * Id of the selected vehicle.
292
+ */
293
+ pixelRatio: {
294
+ value:
295
+ pixelRatio ||
296
+ (typeof window !== 'undefined' ? window.devicePixelRatio : 1),
297
+ writable: true,
298
+ },
299
+
300
+ /**
301
+ * Options used by the constructor of the Tracker class.
302
+ */
303
+ initTrackerOptions: {
304
+ value: initTrackerOptions,
305
+ writable: false,
306
+ },
307
+
308
+ /**
309
+ * If true, encapsulates the renderTrajectories calls in a requestAnimationFrame.
310
+ */
311
+ useRequestAnimationFrame: {
312
+ value: options.useRequestAnimationFrame || false,
313
+ writable: true,
314
+ },
315
+
316
+ /**
317
+ * If true, encapsulates the renderTrajectories calls in a throttle function. Default to true.
318
+ */
319
+ useThrottle: {
320
+ value: options.useThrottle || true,
321
+ writable: true,
322
+ },
323
+
324
+ /**
325
+ * If true, encapsulates the renderTrajectories calls in a debounce function.
326
+ */
327
+ useDebounce: {
328
+ value: options.useDebounce || false,
329
+ writable: true,
330
+ },
331
+
332
+ /**
333
+ * Filter properties used in combination with permalink parameters.
334
+ */
335
+ publishedLineName: {
336
+ get: () => publishedLineName,
337
+ set: (newPublishedLineName) => {
338
+ publishedLineName = newPublishedLineName;
339
+ this.updateFilters();
340
+ },
341
+ },
342
+ tripNumber: {
343
+ get: () => tripNumber,
344
+ set: (newTripNumber) => {
345
+ tripNumber = newTripNumber;
346
+ this.updateFilters();
347
+ },
348
+ },
349
+ operator: {
350
+ get: () => operator,
351
+ set: (newOperator) => {
352
+ operator = newOperator;
353
+ this.updateFilters();
354
+ },
355
+ },
356
+ regexPublishedLineName: {
357
+ get: () => regexPublishedLineName,
358
+ set: (newRegex) => {
359
+ regexPublishedLineName = newRegex;
360
+ this.updateFilters();
361
+ },
362
+ },
363
+
364
+ /**
365
+ * Style properties.
366
+ */
367
+ delayDisplay: {
368
+ value: options.delayDisplay || 300000,
369
+ writable: true,
370
+ },
371
+ delayOutlineColor: {
372
+ value: options.delayOutlineColor || '#000000',
373
+ writable: true,
374
+ },
375
+
376
+ /**
377
+ * Debug properties.
378
+ */
379
+ // Not used anymore, but could be useful for debugging.
380
+ // showVehicleTraj: {
381
+ // value:
382
+ // options.showVehicleTraj !== undefined
383
+ // ? options.showVehicleTraj
384
+ // : true,
385
+ // writable: true,
386
+ // },
387
+ });
388
+
389
+ // Update filter function based on convenient properties
390
+ this.updateFilters();
391
+ }
392
+
121
393
  init(map) {
122
394
  super.init(map);
123
395
 
396
+ this.tracker = new Tracker({
397
+ style: (...args) => this.style(...args),
398
+ ...this.initTrackerOptions,
399
+ });
400
+
401
+ // If the layer is visible we start the rendering clock
402
+ if (this.visible) {
403
+ this.start();
404
+ }
405
+
406
+ // On change of visibility we start/stop the rendering clock
407
+ this.visibilityRef = this.on('change:visible', (evt) => {
408
+ if (evt.target.visible) {
409
+ this.start();
410
+ } else {
411
+ this.stop();
412
+ }
413
+ });
414
+
124
415
  // To avoid browser hanging when the tab is not visible for a certain amount of time,
125
416
  // We stop the rendering and the websocket when hide and start again when show.
126
417
  document.addEventListener(
@@ -134,11 +425,31 @@ const TralisLayerMixin = (TrackerLayer) =>
134
425
  'visibilitychange',
135
426
  this.onDocumentVisibilityChange,
136
427
  );
428
+
429
+ this.stop();
430
+ unByKey(this.visibilityRef);
431
+ if (this.tracker) {
432
+ const { canvas } = this.tracker;
433
+ const context = canvas.getContext('2d');
434
+ context.clearRect(0, 0, canvas.width, canvas.height);
435
+ this.tracker = null;
436
+ }
137
437
  super.terminate();
138
438
  }
139
439
 
140
440
  start() {
141
- super.start();
441
+ this.stop();
442
+ this.renderTrajectories();
443
+ this.startUpdateTime();
444
+
445
+ if (this.isClickActive) {
446
+ this.onClick(this.onFeatureClick);
447
+ }
448
+
449
+ if (this.isHoverActive) {
450
+ this.onHover(this.onFeatureHover);
451
+ }
452
+
142
453
  this.api.open();
143
454
  this.api.subscribeTrajectory(
144
455
  this.mode,
@@ -157,13 +468,119 @@ const TralisLayerMixin = (TrackerLayer) =>
157
468
  }
158
469
  }
159
470
 
471
+ /**
472
+ * Start the clock.
473
+ * @private
474
+ */
475
+ startUpdateTime() {
476
+ this.stopUpdateTime();
477
+ this.updateTimeDelay = this.getRefreshTimeInMs();
478
+ this.updateTimeInterval = setInterval(() => {
479
+ // When live=true, we update the time with new Date();
480
+ this.time = this.live
481
+ ? new Date()
482
+ : this.time.getTime() + this.updateTimeDelay * this.speed;
483
+ }, this.updateTimeDelay);
484
+ }
485
+
160
486
  stop() {
161
- super.stop();
162
487
  this.api.unsubscribeTrajectory(this.onTrajectoryMessage);
163
488
  this.api.unsubscribeDeletedVehicles(this.onDeleteTrajectoryMessage);
164
489
  this.api.close();
165
490
  }
166
491
 
492
+ /**
493
+ * Stop the clock.
494
+ * @private
495
+ */
496
+ stopUpdateTime() {
497
+ if (this.updateTimeInterval) {
498
+ clearInterval(this.updateTimeInterval);
499
+ }
500
+ }
501
+
502
+ /**
503
+ * Launch renderTrajectories. it avoids duplicating code in renderTrajectories method.
504
+ *
505
+ * @param {object} viewState The view state of the map.
506
+ * @param {number[2]} viewState.center Center coordinate of the map in mercator coordinate.
507
+ * @param {number[4]} viewState.extent Extent of the map in mercator coordinates.
508
+ * @param {number[2]} viewState.size Size ([width, height]) of the canvas to render.
509
+ * @param {number} [viewState.rotation = 0] Rotation of the map to render.
510
+ * @param {number} viewState.resolution Resolution of the map to render.
511
+ * @param {boolean} noInterpolate If true trajectories are not interpolated but
512
+ * drawn at the last known coordinate. Use this for performance optimization
513
+ * during map navigation.
514
+ * @private
515
+ */
516
+ renderTrajectoriesInternal(viewState, noInterpolate) {
517
+ if (!this.tracker) {
518
+ return false;
519
+ }
520
+
521
+ const time = this.live ? Date.now() : this.time;
522
+
523
+ const trajectories = Object.values(this.trajectories);
524
+
525
+ // console.time('sort');
526
+ if (this.sort) {
527
+ trajectories.sort(this.sort);
528
+ }
529
+ // console.timeEnd('sort');
530
+
531
+ // console.time('render');
532
+ this.renderState = this.tracker.renderTrajectories(
533
+ trajectories,
534
+ { ...viewState, pixelRatio: this.pixelRatio, time },
535
+ {
536
+ noInterpolate:
537
+ viewState.zoom < this.minZoomInterpolation ? true : noInterpolate,
538
+ hoverVehicleId: this.hoverVehicleId,
539
+ selectedVehicleId: this.selectedVehicleId,
540
+ iconScale: this.iconScale,
541
+ delayDisplay: this.delayDisplay,
542
+ delayOutlineColor: this.delayOutlineColor,
543
+ },
544
+ );
545
+
546
+ // console.timeEnd('render');
547
+ return true;
548
+ }
549
+
550
+ /**
551
+ * Render the trajectories requesting an animation frame and cancelling the previous one.
552
+ * This function must be overrided by children to provide the correct parameters.
553
+ *
554
+ * @param {object} viewState The view state of the map.
555
+ * @param {number[2]} viewState.center Center coordinate of the map in mercator coordinate.
556
+ * @param {number[4]} viewState.extent Extent of the map in mercator coordinates.
557
+ * @param {number[2]} viewState.size Size ([width, height]) of the canvas to render.
558
+ * @param {number} [viewState.rotation = 0] Rotation of the map to render.
559
+ * @param {number} viewState.resolution Resolution of the map to render.
560
+ * @param {boolean} noInterpolate If true trajectories are not interpolated but
561
+ * drawn at the last known coordinate. Use this for performance optimization
562
+ * during map navigation.
563
+ * @private
564
+ */
565
+ renderTrajectories(viewState, noInterpolate) {
566
+ if (this.requestId) {
567
+ cancelAnimationFrame(this.requestId);
568
+ this.requestId = null;
569
+ }
570
+
571
+ if (!noInterpolate && this.useRequestAnimationFrame) {
572
+ this.requestId = requestAnimationFrame(() => {
573
+ this.renderTrajectoriesInternal(viewState, noInterpolate);
574
+ });
575
+ } else if (!noInterpolate && this.useDebounce) {
576
+ this.debounceRenderTrajectories(viewState, noInterpolate);
577
+ } else if (!noInterpolate && this.useThrottle) {
578
+ this.throttleRenderTrajectories(viewState, noInterpolate);
579
+ } else {
580
+ this.renderTrajectoriesInternal(viewState, noInterpolate);
581
+ }
582
+ }
583
+
167
584
  setBbox(extent, zoom) {
168
585
  // Clean trajectories before sending the new bbox
169
586
  // Purge trajectories:
@@ -210,6 +627,88 @@ const TralisLayerMixin = (TrackerLayer) =>
210
627
  );
211
628
  }
212
629
 
630
+ /**
631
+ * Get the duration before the next update depending on zoom level.
632
+ *
633
+ * @private
634
+ * @param {number} zoom
635
+ */
636
+ getRefreshTimeInMs(zoom) {
637
+ const roundedZoom = Math.round(zoom);
638
+ const timeStep = timeSteps[roundedZoom] || 25;
639
+ const nextTick = Math.max(25, timeStep / this.speed);
640
+ const nextThrottleTick = Math.min(nextTick, 500);
641
+ // TODO: see if this should go elsewhere.
642
+ if (this.useThrottle) {
643
+ this.throttleRenderTrajectories = throttle(
644
+ this.renderTrajectoriesInternal,
645
+ nextThrottleTick,
646
+ { leading: true, trailing: true },
647
+ );
648
+ } else if (this.useDebounce) {
649
+ this.debounceRenderTrajectories = debounce(
650
+ this.renderTrajectoriesInternal,
651
+ nextThrottleTick,
652
+ { leading: true, trailing: true, maxWait: 5000 },
653
+ );
654
+ }
655
+ if (this.api?.buffer) {
656
+ const [, size] = this.api.buffer;
657
+ this.api.buffer = [nextThrottleTick, size];
658
+ }
659
+ return nextTick;
660
+ }
661
+
662
+ /**
663
+ * Get vehicle.
664
+ * @param {function} filterFc A function use to filter results.
665
+ * @return {Array<Object>} Array of vehicle.
666
+ */
667
+ getVehicle(filterFc) {
668
+ return Object.values(this.trajectories).filter(filterFc);
669
+ }
670
+
671
+ /**
672
+ * Request feature information for a given coordinate.
673
+ *
674
+ * @param {ol/coordinate~Coordinate} coordinate Coordinate.
675
+ * @param {Object} options Options See child classes to see which options are supported.
676
+ * @param {number} [options.resolution=1] The resolution of the map.
677
+ * @param {number} [options.nb=Infinity] The max number of vehicles to return.
678
+ * @return {Promise<FeatureInfo>} Promise with features, layer and coordinate.
679
+ */
680
+ getFeatureInfoAtCoordinate(coordinate, options = {}) {
681
+ const { resolution, nb } = options;
682
+ const ext = buffer(
683
+ [...coordinate, ...coordinate],
684
+ this.hitTolerance * resolution,
685
+ );
686
+ let trajectories = Object.values(this.trajectories);
687
+
688
+ if (this.sort) {
689
+ trajectories = trajectories.sort(this.sort);
690
+ }
691
+
692
+ const vehicles = [];
693
+ for (let i = 0; i < trajectories.length; i += 1) {
694
+ if (
695
+ trajectories[i].properties.coordinate &&
696
+ containsCoordinate(ext, trajectories[i].properties.coordinate)
697
+ ) {
698
+ vehicles.push(trajectories[i]);
699
+ }
700
+ if (vehicles.length === nb) {
701
+ break;
702
+ }
703
+ }
704
+
705
+ return Promise.resolve({
706
+ layer: this,
707
+ features: vehicles.map((vehicle) => this.format.readFeature(vehicle)),
708
+ coordinate,
709
+ });
710
+ }
711
+
213
712
  /**
214
713
  * Request the stopSequence and the fullTrajectory informations for a vehicle.
215
714
  *
@@ -274,9 +773,17 @@ const TralisLayerMixin = (TrackerLayer) =>
274
773
  delete this.trajectories[id];
275
774
  }
276
775
 
277
- // getRefreshTimeInMs() {
278
- // return 5000;
279
- // }
776
+ /**
777
+ * On zoomend we adjust the time interval of the update of vehicles positions.
778
+ *
779
+ * @param evt Event that triggered the function.
780
+ * @private
781
+ */
782
+ // eslint-disable-next-line no-unused-vars
783
+ onZoomEnd(evt) {
784
+ this.startUpdateTime();
785
+ }
786
+
280
787
  onDocumentVisibilityChange() {
281
788
  if (!this.visible) {
282
789
  return;
@@ -373,7 +880,6 @@ const TralisLayerMixin = (TrackerLayer) =>
373
880
  this.hoverVehicleId = id;
374
881
  this.renderTrajectories(true);
375
882
  }
376
- super.onFeatureHover(features, layer, coordinate);
377
883
  }
378
884
 
379
885
  /**
@@ -395,7 +901,29 @@ const TralisLayerMixin = (TrackerLayer) =>
395
901
  this.selectedVehicle = feature;
396
902
  this.renderTrajectories(true);
397
903
  }
398
- super.onFeatureClick(features, layer, coordinate);
904
+ }
905
+
906
+ /**
907
+ * Update filter provided by properties or permalink.
908
+ */
909
+ updateFilters() {
910
+ // Setting filters from the permalink if no values defined by the layer.
911
+ const parameters = qs.parse(window.location.search.toLowerCase());
912
+ const publishedName = this.publishedLineName || parameters[LINE_FILTER];
913
+ const tripNumber = this.tripNumber || parameters[ROUTE_FILTER];
914
+ const operator = this.operator || parameters[OPERATOR_FILTER];
915
+ const { regexPublishedLineName } = this;
916
+
917
+ // Only overrides filter function if one of this property exists.
918
+ if (publishedName || tripNumber || operator || regexPublishedLineName) {
919
+ // filter is the property in TrackerLayerMixin.
920
+ this.filter = createFilters(
921
+ publishedName,
922
+ tripNumber,
923
+ operator,
924
+ regexPublishedLineName,
925
+ );
926
+ }
399
927
  }
400
928
  };
401
929
 
@@ -0,0 +1,4 @@
1
+ export { default as trackerDefaultStyle } from './trackerDefaultStyle';
2
+ export { default as trackerDelayStyle } from './trackerDelayStyle';
3
+ export * from './trackerDefaultStyle';
4
+ export { default as trackerSimpleStyle } from './trackerSimpleStyle';
@@ -158,24 +158,24 @@ const style = (trajectory, viewState, options) => {
158
158
  } = options;
159
159
 
160
160
  const { zoom, pixelRatio } = viewState;
161
- let { line, type } = trajectory.properties;
161
+ let { type, cancelled } = trajectory.properties;
162
162
  const {
163
163
  train_id: id,
164
+ line,
164
165
  delay,
165
- cancelled = false,
166
+ state,
166
167
  operator_provides_realtime_journey: operatorProvidesRealtime,
167
168
  } = trajectory.properties;
169
+ let { name, text_color: textColor, color } = line || {};
170
+
171
+ // In the future, the cancelled property will be removed we still managed it
172
+ // until the backend change is on prod.
173
+ cancelled = cancelled === true || state === 'JOURNEY_CANCELLED';
168
174
 
169
175
  if (!type) {
170
176
  type = 'Rail';
171
177
  }
172
178
 
173
- if (!line) {
174
- line = {};
175
- }
176
-
177
- let { name, text_color: textColor, color } = line;
178
-
179
179
  if (!name) {
180
180
  name = 'I';
181
181
  }