mobility-toolbox-js 1.6.1 → 1.6.4-beta.2

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 (36) hide show
  1. package/api/routing/RoutingAPI.js +1 -1
  2. package/api/stops/StopsAPI.js +1 -1
  3. package/api/trajserv/TrajservAPI.js +3 -3
  4. package/api/trajserv/TrajservAPIUtils.js +3 -3
  5. package/api/tralis/TralisAPI.js +174 -69
  6. package/api/tralis/TralisAPI.test.js +75 -0
  7. package/api/tralis/WebSocketConnector.js +144 -61
  8. package/api/tralis/WebSocketConnector.test.js +82 -17
  9. package/common/Tracker.js +17 -5
  10. package/common/layers/Layer.js +5 -5
  11. package/common/mixins/CopyrightMixin.js +1 -1
  12. package/common/mixins/MapMixin.js +3 -3
  13. package/common/mixins/SearchMixin.js +1 -1
  14. package/common/mixins/TrackerLayerMixin.js +4 -7
  15. package/common/mixins/TrajservLayerMixin.js +2 -2
  16. package/common/mixins/TralisLayerMixin.js +206 -131
  17. package/common/utils/delayTrackerStyle.js +26 -21
  18. package/common/utils/index.js +1 -0
  19. package/common/{timeUtils.js → utils/timeUtils.js} +0 -0
  20. package/common/{timeUtils.test.js → utils/timeUtils.test.js} +0 -0
  21. package/index.js +1 -1
  22. package/index.js.map +1 -1
  23. package/mapbox/layers/Layer.js +1 -1
  24. package/mapbox/layers/TrackerLayer.js +1 -1
  25. package/mapbox/layers/TrajservLayer.js +1 -1
  26. package/mapbox/layers/TralisLayer.js +1 -14
  27. package/ol/layers/Layer.js +1 -1
  28. package/ol/layers/MapboxLayer.js +2 -2
  29. package/ol/layers/MapboxStyleLayer.js +2 -2
  30. package/ol/layers/RoutingLayer.js +1 -1
  31. package/ol/layers/TrackerLayer.js +8 -3
  32. package/ol/layers/TrajservLayer.js +2 -2
  33. package/ol/layers/TralisLayer.js +19 -18
  34. package/ol/layers/VectorLayer.js +2 -2
  35. package/ol/layers/WMSLayer.js +3 -3
  36. package/package.json +2 -2
@@ -32,7 +32,7 @@ class RoutingAPI extends API {
32
32
  *
33
33
  * @param {RoutingSearchParams} params Request parameters. See [Routing service documentation](https://developer.geops.io/apis/routing/).
34
34
  * @param {AbortController} abortController Abort controller used to cancel the request.
35
- * @returns {Promise<GeoJSONFeatureCollection>} An GeoJSON feature collection with coordinates in [EPSG:4326](http://epsg.io/4326).
35
+ * @return {Promise<GeoJSONFeatureCollection>} An GeoJSON feature collection with coordinates in [EPSG:4326](http://epsg.io/4326).
36
36
  */
37
37
  route(params, abortController = new AbortController()) {
38
38
  return this.fetch('', params, {
@@ -29,7 +29,7 @@ class StopsAPI extends API {
29
29
  *
30
30
  * @param {StopsSearchParams} params Request parameters. See [Stops service documentation](https://developer.geops.io/apis/5dcbd702a256d90001cf1361/).
31
31
  * @param {AbortController} abortController Abort controller used to cancel the request.
32
- * @returns {Promise<Array<GeoJSONFeature>>} An array of GeoJSON features with coordinates in [EPSG:4326](http://epsg.io/4326).
32
+ * @return {Promise<Array<GeoJSONFeature>>} An array of GeoJSON features with coordinates in [EPSG:4326](http://epsg.io/4326).
33
33
  */
34
34
  search(params, abortController = {}) {
35
35
  return this.fetch('', params, {
@@ -33,7 +33,7 @@ class TrajservAPI extends API {
33
33
  *
34
34
  * @param {GetTrajectoryByIdParams} params Request parameters. See [Realtime service documentation](https://developer.geops.io/apis/5dcbd5c9a256d90001cf1360/#/default/get_trajectorybyid).
35
35
  * @param {AbortController} abortController Abort controller used to cancel the request.
36
- * @returns {Promise<TrajservTrajectory>} A trajectory.
36
+ * @return {Promise<TrajservTrajectory>} A trajectory.
37
37
  */
38
38
  fetchTrajectoryById(params, abortController = {}) {
39
39
  return this.fetch(`/trajectorybyid`, params, {
@@ -46,7 +46,7 @@ class TrajservAPI extends API {
46
46
  *
47
47
  * @param {GetTrajectoriesParams} params Request parameters. See [Realtime service documentation](https://developer.geops.io/apis/5dcbd5c9a256d90001cf1360/#/default/get_trajectory_collection).
48
48
  * @param {AbortController} abortController Abort controller used to cancel the request.
49
- * @returns {Promise<Array<Trajectory>>} A list of trajectories.
49
+ * @return {Promise<Array<Trajectory>>} A list of trajectories.
50
50
  */
51
51
  fetchTrajectories(params, abortController = {}) {
52
52
  return this.fetch(`/trajectory_collection`, params, {
@@ -61,7 +61,7 @@ class TrajservAPI extends API {
61
61
  *
62
62
  * @param {GetTrajectoryStationsParams} params Request parameters. See [Realtime service documentation](https://developer.geops.io/apis/5dcbd5c9a256d90001cf1360/#/default/get_trajstations).
63
63
  * @param {AbortController} abortController Abort controller used to cancel the request.
64
- * @returns {Promise<Array<TrajectoryStation>>} A list of stations.
64
+ * @return {Promise<Array<TrajectoryStation>>} A list of stations.
65
65
  */
66
66
  fetchTrajectoryStations(params, abortController = {}) {
67
67
  return this.fetch(`/trajstations`, params, {
@@ -2,7 +2,7 @@ import { LineString } from 'ol/geom';
2
2
 
3
3
  /**
4
4
  * Translate the response date object into a readable object.
5
- * @returns {Date[]}
5
+ * @return {Date[]}
6
6
  * @ignore
7
7
  */
8
8
  const translateDates = (dates = []) => {
@@ -21,7 +21,7 @@ const translateDates = (dates = []) => {
21
21
 
22
22
  /**
23
23
  * Translate the trajstations response into a readable object.
24
- * @returns {Object} returns a readable object
24
+ * @return {Object} returns a readable object
25
25
  * @private
26
26
  */
27
27
  export const translateTrajStationsResp = (data) => {
@@ -125,7 +125,7 @@ export const translateTrajStationsResp = (data) => {
125
125
 
126
126
  /**
127
127
  * Translate the trajectory_collection response into a js usable object.
128
- * @returns {Array} returns an array of trajectories.
128
+ * @return {Array} returns an array of trajectories.
129
129
  * @ignore
130
130
  */
131
131
  export const translateTrajCollResponse = (features = []) => {
@@ -21,7 +21,7 @@ export const TralisModes = {
21
21
  };
22
22
 
23
23
  /**
24
- * Access to Tralis service.
24
+ * This class provides convenience methods to access to the [geOps realtime api](https://developer.geops.io/apis/realtime/).
25
25
  *
26
26
  * @example
27
27
  * import { TralisAPI } from 'mobility-toolbox-js/api';
@@ -41,30 +41,14 @@ class TralisAPI {
41
41
  * Constructor
42
42
  *
43
43
  * @param {Object|string} options A string representing the url of the service or an object containing the url and the apiKey.
44
- * @param {string} options.url Service url.
45
- * @param {string} options.apiKey Access key for [geOps services](https://developer.geops.io/).
44
+ * @param {string} options.url Url to the [geOps realtime api](https://developer.geops.io/apis/realtime/).
45
+ * @param {string} options.apiKey Access key for [geOps apis](https://developer.geops.io/).
46
46
  * @param {string} [options.prefix=''] Service prefix to specify tenant.
47
- * @param {string} [options.projection] The epsg code of the projection for features.
48
- * @param {number[4]} [options.bbox=[minX, minY, maxX, maxY] The bounding box to receive data from.
47
+ * @param {string} [options.projection] The epsg code of the projection for features. Default to EPSG:3857.
48
+ * @param {number[4]} [options.bbox=[minX, minY, maxX, maxY, zoom, tenant] The bounding box to receive data from.
49
49
  */
50
50
  constructor(options = {}) {
51
- let wsUrl = null;
52
-
53
- if (typeof options === 'string') {
54
- wsUrl = options;
55
- } else {
56
- wsUrl = options.url;
57
- }
58
-
59
- if (options.apiKey) {
60
- wsUrl = `${wsUrl}?key=${options.apiKey}`;
61
- }
62
-
63
- /** @ignore */
64
- this.bbox = options.bbox;
65
-
66
- /** @ignore */
67
- this.projection = options.projection || 'EPSG:3857';
51
+ this.defineProperties(options);
68
52
 
69
53
  /** @ignore */
70
54
  this.subscribedStationUic = null;
@@ -80,38 +64,113 @@ class TralisAPI {
80
64
 
81
65
  /** @ignore */
82
66
  this.prefix = options.prefix || '';
67
+ }
83
68
 
84
- this.isUpdateBboxOnMoveEnd = options.isUpdateBboxOnMoveEnd || false;
69
+ defineProperties(options) {
70
+ let opt = options;
85
71
 
86
- /** @ignore */
87
- this.conn = new WebSocketConnector(wsUrl);
72
+ if (typeof options === 'string') {
73
+ opt = { url: options };
74
+ }
75
+
76
+ const { apiKey } = opt;
77
+ let { url, projection, bbox } = opt;
78
+ const conn = new WebSocketConnector();
88
79
 
89
- if (options.projection) {
90
- this.setProjection(options.projection);
80
+ if (apiKey) {
81
+ url = `${url || 'wss://tralis-tracker-api.geops.io/ws'}?key=${apiKey}`;
91
82
  }
83
+
84
+ Object.defineProperties(this, {
85
+ url: {
86
+ get: () => {
87
+ return url;
88
+ },
89
+ set: (newUrl) => {
90
+ url = newUrl;
91
+ this.open();
92
+ },
93
+ },
94
+ projection: {
95
+ get: () => {
96
+ return projection;
97
+ },
98
+ set: (newProjection) => {
99
+ if (newProjection !== projection) {
100
+ projection = newProjection;
101
+ if (this.conn) {
102
+ this.conn.send(`PROJECTION ${projection}`);
103
+ }
104
+ }
105
+ },
106
+ },
107
+ bbox: {
108
+ get: () => {
109
+ return bbox;
110
+ },
111
+ set: (newBbox) => {
112
+ if (JSON.stringify(newBbox) !== JSON.stringify(bbox)) {
113
+ bbox = newBbox;
114
+ if (this.conn) {
115
+ this.conn.send(`BBOX ${bbox.join(' ')}`);
116
+ }
117
+ }
118
+ },
119
+ },
120
+ /**
121
+ * The websocket helper class to connect the websocket.
122
+ *
123
+ * @private
124
+ */
125
+ conn: {
126
+ value: conn,
127
+ writable: true,
128
+ },
129
+ /**
130
+ * Interval between PING request in ms.
131
+ * If equal to 0, no PING request are sent.
132
+ * @type {number}
133
+ * @private
134
+ */
135
+ pingIntervalMs: {
136
+ value: options.pingIntervalMs || 10000,
137
+ writable: true,
138
+ },
139
+ /**
140
+ * Timeout in ms after an automatic reconnection when the websoscket has been closed by the server.
141
+ * @type {number}
142
+ */
143
+ reconnectTimeoutMs: {
144
+ value: options.pingIntervalMs || 100,
145
+ writable: true,
146
+ },
147
+ });
92
148
  }
93
149
 
94
- /**
95
- * Send the projection to use. Default to EPSG:3857.
96
- *
97
- * @param {string} epsgCode The EPSG code of the projection.
98
- */
99
- setProjection(epsgCode) {
100
- this.conn.send(`PROJECTION ${epsgCode}`);
150
+ open() {
151
+ this.close();
152
+ this.conn.connect(this.url);
153
+
154
+ // Register BBOX and PROJECTION messages on open.
155
+ if (this.conn.connecting) {
156
+ this.conn.websocket.addEventListener('open', () => {
157
+ this.onOpen();
158
+ });
159
+ } else {
160
+ this.onOpen();
161
+ }
162
+
163
+ // Register reconnection on close.
164
+ this.conn.websocket.onclose = () => {
165
+ this.onClose();
166
+ };
101
167
  }
102
168
 
103
169
  /**
104
- * Send the bbox to the service, if the first time it will also subscribe to trajectory and deleted_vehicles channels.
105
- *
106
- * @param {number[5]} bbox The bounding box and the zoom level to receive data from.
170
+ * Close the websocket connection without reconnection.
107
171
  */
108
- setBbox(bbox) {
109
- /**
110
- * The BBOX for websocket responses
111
- * @type {Array<number>}
112
- */
113
- this.bbox = bbox;
114
- this.conn.send(`BBOX ${bbox.join(' ')}`);
172
+ close() {
173
+ this.conn.close();
115
174
  }
116
175
 
117
176
  /**
@@ -122,6 +181,47 @@ class TralisAPI {
122
181
  this.conn.send('RESET');
123
182
  }
124
183
 
184
+ /**
185
+ * Callback when the websocket is opened and ready.
186
+ * It applies the bbox and the projection.
187
+ */
188
+ onOpen() {
189
+ if (this.projection) {
190
+ this.conn.send(`PROJECTION ${this.projection}`);
191
+ }
192
+ if (this.bbox) {
193
+ this.conn.send(`BBOX ${this.bbox.join(' ')}`);
194
+ }
195
+
196
+ /**
197
+ * Keep websocket alive
198
+ */
199
+ if (this.pingIntervalMs) {
200
+ window.clearInterval(this.pingInterval);
201
+ /** @ignore */
202
+ this.pingInterval = setInterval(() => {
203
+ this.conn.send('PING');
204
+ }, this.pingIntervalMs);
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Callback when the websocket is closed by the server.
210
+ * It auto reconnects after a timeout.
211
+ */
212
+ onClose() {
213
+ window.clearTimeout(this.pingInterval);
214
+ window.clearTimeout(this.reconnectTimeout);
215
+
216
+ if (this.reconnectTimeoutMs) {
217
+ /** @ignore */
218
+ this.reconnectTimeout = window.setTimeout(
219
+ () => this.open(),
220
+ this.reconnectTimeoutMs,
221
+ );
222
+ }
223
+ }
224
+
125
225
  /**
126
226
  * Subscribe to a channel.
127
227
  *
@@ -162,7 +262,7 @@ class TralisAPI {
162
262
  *
163
263
  * @param {Object} depObject The object containing departures by id.
164
264
  * @param {boolean} [sortByMinArrivalTime=false] If true sort departures by arrival time.
165
- * @returns {Array<departure>} Return departures array.
265
+ * @return {Array<departure>} Return departures array.
166
266
  * @private
167
267
  */
168
268
  filterDepartures(depObject, sortByMinArrivalTime = false) {
@@ -294,7 +394,7 @@ class TralisAPI {
294
394
  *
295
395
  * @param {number} uic UIC of the station.
296
396
  * @param {TralisMode} mode Tralis mode.
297
- * @returns {Promise<Station>} A station.
397
+ * @return {Promise<Station>} A station.
298
398
  */
299
399
  getStation(uic, mode) {
300
400
  const params = {
@@ -317,14 +417,10 @@ class TralisAPI {
317
417
  * Update the model's station list for a given mode and a bbox.
318
418
  *
319
419
  * @param {TralisMode} mode Tralis mode.
320
- * @param {number[4]} bbox The extent where to request.
321
- * @returns {Promise<Array<Station>>} An array of stations.
420
+ * @return {Promise<Array<Station>>} An array of stations.
322
421
  */
323
- getStations(mode, bbox) {
422
+ getStations(mode) {
324
423
  const stations = [];
325
- if (bbox) {
326
- this.conn.setBbox(bbox);
327
- }
328
424
  const params = {
329
425
  channel: `station${getModeSuffix(mode, TralisModes)}`,
330
426
  };
@@ -350,14 +446,10 @@ class TralisAPI {
350
446
  * One message pro station.
351
447
  *
352
448
  * @param {TralisMode} mode Tralis mode.
353
- * @param {number[4]} bbox The extent where to request.
354
449
  * @param {function(station: Station)} onMessage Function called on each message of the channel.
355
450
  */
356
- subscribeStations(mode, bbox, onMessage) {
451
+ subscribeStations(mode, onMessage) {
357
452
  this.unsubscribeStations();
358
- if (bbox) {
359
- this.conn.setBbox(bbox);
360
- }
361
453
  this.subscribe(`station${getModeSuffix(mode, TralisModes)}`, (data) => {
362
454
  if (data.content) {
363
455
  onMessage(data.content);
@@ -412,14 +504,15 @@ class TralisAPI {
412
504
  *
413
505
  * @param {TralisMode} mode Tralis mode.
414
506
  * @param {function(trajectory: TralisTrajectory)} onMessage Function called on each message of the channel.
507
+ * @param {boolean} quiet If true, the subscription will not send GET and SUB requests to the websocket.
415
508
  */
416
- subscribeTrajectory(mode, onMessage) {
509
+ subscribeTrajectory(mode, onMessage, quiet = false) {
417
510
  this.unsubscribeTrajectory(onMessage);
418
511
  this.subscribe(
419
512
  `trajectory${getModeSuffix(mode, TralisModes)}`,
420
513
  onMessage,
421
514
  null,
422
- this.isUpdateBboxOnMoveEnd,
515
+ quiet,
423
516
  );
424
517
  }
425
518
 
@@ -436,14 +529,15 @@ class TralisAPI {
436
529
  *
437
530
  * @param {TralisMode} mode Tralis mode.
438
531
  * @param {function(response: { content: Vehicle })} onMessage Function called on each message of the channel.
532
+ * @param {boolean} quiet If true, the subscription will not send GET and SUB requests to the websocket.
439
533
  */
440
- subscribeDeletedVehicles(mode, onMessage) {
534
+ subscribeDeletedVehicles(mode, onMessage, quiet = false) {
441
535
  this.unsubscribeDeletedVehicles(onMessage);
442
536
  this.subscribe(
443
537
  `deleted_vehicles${getModeSuffix(mode, TralisModes)}`,
444
538
  onMessage,
445
539
  null,
446
- this.isUpdateBboxOnMoveEnd,
540
+ quiet,
447
541
  );
448
542
  }
449
543
 
@@ -460,11 +554,21 @@ class TralisAPI {
460
554
  *
461
555
  * @param {string} id A vehicle id.
462
556
  * @param {TralisMode} mode Tralis mode.
463
- * @returns {Promise<FullTrajectory>} Return a full trajectory.
557
+ * @param {string} generalizationLevel The generalization level to request. Can be one of 5 (more generalized), 10, 30, 100, undefined (less generalized).
558
+ * @return {Promise<FullTrajectory>} Return a full trajectory.
464
559
  */
465
- getFullTrajectory(id, mode) {
560
+ getFullTrajectory(id, mode, generalizationLevel) {
561
+ const channel = [`full_trajectory${getModeSuffix(mode, TralisModes)}`];
562
+ if (id) {
563
+ channel.push(id);
564
+ }
565
+
566
+ if ((!mode || mode === TralisModes.TOPOGRAPHIC) && generalizationLevel) {
567
+ channel.push(`gen${generalizationLevel}`);
568
+ }
569
+
466
570
  const params = {
467
- channel: `full_trajectory${getModeSuffix(mode, TralisModes)}_${id}`,
571
+ channel: channel.join('_'),
468
572
  };
469
573
 
470
574
  return new Promise((resolve) => {
@@ -481,11 +585,12 @@ class TralisAPI {
481
585
  *
482
586
  * @param {string[]} ids List of vehicles ids.
483
587
  * @param {TralisMode} mode Tralis mode.
484
- * @returns {Promise<Array<FullTrajectory>>} Return an array of full trajectories.
588
+ * @param {string} generalizationLevel The generalization level to request. Can be one of '', 'gen5', 'gen10', 'gen30', 'gen100'.
589
+ * @return {Promise<Array<FullTrajectory>>} Return an array of full trajectories.
485
590
  */
486
- getFullTrajectories(ids, mode) {
591
+ getFullTrajectories(ids, mode, generalizationLevel) {
487
592
  const promises = ids.map((id) => {
488
- return this.getFullTrajectory(id, mode);
593
+ return this.getFullTrajectory(id, mode, generalizationLevel);
489
594
  });
490
595
  return Promise.all(promises);
491
596
  }
@@ -526,7 +631,7 @@ class TralisAPI {
526
631
  * Get the list of stops for this vehicle.
527
632
  *
528
633
  * @param {string} id A vehicle id.
529
- * @returns {Promise<StopSequence>} Returns a stop sequence object.
634
+ * @return {Promise<StopSequence>} Returns a stop sequence object.
530
635
  */
531
636
  getStopSequence(id) {
532
637
  const params = {
@@ -557,7 +662,7 @@ class TralisAPI {
557
662
  * Get a list of stops for a list of vehicles.
558
663
  *
559
664
  * @param {string[]} ids List of vehicles ids.
560
- * @returns {Promise<Array<StopSequence>>} Return an array of stop sequences.
665
+ * @return {Promise<Array<StopSequence>>} Return an array of stop sequences.
561
666
  */
562
667
  getStopSequences(ids) {
563
668
  const promises = ids.map((id) => {
@@ -0,0 +1,75 @@
1
+ import { TralisAPI, TralisModes } from '..';
2
+
3
+ describe('TralisAPI', () => {
4
+ let tralisAPI;
5
+ let get;
6
+
7
+ beforeEach(() => {
8
+ get = jest.fn((params, cb) => {
9
+ cb({ content: 'content' });
10
+ });
11
+ tralisAPI = new TralisAPI();
12
+ tralisAPI.conn = {
13
+ get,
14
+ };
15
+ });
16
+
17
+ describe('#getFullTrajectory() calls fullTrajectory channel', () => {
18
+ test('without parameters', (done) => {
19
+ tralisAPI.getFullTrajectory().then(() => {
20
+ expect(get.mock.calls.length).toBe(1);
21
+ expect(get.mock.calls[0][0]).toEqual({
22
+ channel: 'full_trajectory',
23
+ });
24
+ done();
25
+ });
26
+ });
27
+
28
+ [null, TralisModes.TOPOGRAPHIC].forEach((mode) => {
29
+ describe(`using mode ${mode}`, () => {
30
+ test('using id', (done) => {
31
+ tralisAPI.getFullTrajectory('foo', mode).then(() => {
32
+ expect(get.mock.calls.length).toBe(1);
33
+ expect(get.mock.calls[0][0]).toEqual({
34
+ channel: 'full_trajectory_foo',
35
+ });
36
+ done();
37
+ });
38
+ });
39
+
40
+ test('using id and generalizationLevel param', (done) => {
41
+ tralisAPI.getFullTrajectory('foo', mode, 5).then(() => {
42
+ expect(get.mock.calls.length).toBe(1);
43
+ expect(get.mock.calls[0][0]).toEqual({
44
+ channel: 'full_trajectory_foo_gen5',
45
+ });
46
+ done();
47
+ });
48
+ });
49
+ });
50
+ });
51
+
52
+ describe('using schematic mode ', () => {
53
+ test('using id', (done) => {
54
+ tralisAPI.getFullTrajectory('foo', TralisModes.SCHEMATIC).then(() => {
55
+ expect(get.mock.calls.length).toBe(1);
56
+ expect(get.mock.calls[0][0]).toEqual({
57
+ channel: 'full_trajectory_schematic_foo',
58
+ });
59
+ done();
60
+ });
61
+ });
62
+ test("doesn't use generalizationLevel param", (done) => {
63
+ tralisAPI
64
+ .getFullTrajectory('foo', TralisModes.SCHEMATIC, 10)
65
+ .then(() => {
66
+ expect(get.mock.calls.length).toBe(1);
67
+ expect(get.mock.calls[0][0]).toEqual({
68
+ channel: 'full_trajectory_schematic_foo',
69
+ });
70
+ done();
71
+ });
72
+ });
73
+ });
74
+ });
75
+ });