mobility-toolbox-js 3.0.0-beta.19 → 3.0.0-beta.20
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/HttpAPI.d.ts +5 -5
- package/api/RealtimeAPI.d.ts +204 -171
- package/api/RealtimeAPI.js +306 -258
- package/api/RoutingAPI.d.ts +4 -4
- package/api/StopsAPI.d.ts +4 -4
- package/api/WebSocketAPI.d.ts +60 -66
- package/api/WebSocketAPI.js +164 -164
- package/api/index.js +1 -1
- package/common/controls/StopFinderControlCommon.d.ts +11 -11
- package/common/controls/StopFinderControlCommon.js +30 -30
- package/common/index.d.ts +1 -1
- package/common/index.js +1 -1
- package/common/mixins/RealtimeLayerMixin.d.ts +149 -155
- package/common/mixins/RealtimeLayerMixin.js +395 -395
- package/common/styles/realtimeDefaultStyle.js +6 -6
- package/common/styles/realtimeHeadingStyle.js +5 -5
- package/common/utils/getMapGlCopyrights.d.ts +1 -1
- package/common/utils/getMapGlCopyrights.js +3 -3
- package/common/utils/getVehiclePosition.d.ts +2 -2
- package/common/utils/getVehiclePosition.js +7 -7
- package/common/utils/renderTrajectories.js +5 -5
- package/common/utils/sortByDelay.js +5 -5
- package/maplibre/layers/RealtimeLayer.d.ts +59 -64
- package/maplibre/layers/RealtimeLayer.js +8 -8
- package/maplibre/utils/getSourceCoordinates.js +5 -5
- package/mbt.js +7205 -7031
- package/mbt.js.map +4 -4
- package/mbt.min.js +25 -25
- package/mbt.min.js.map +4 -4
- package/ol/controls/RoutingControl.d.ts +81 -87
- package/ol/controls/RoutingControl.js +216 -218
- package/ol/layers/Layer.d.ts +9 -9
- package/ol/layers/MaplibreLayer.d.ts +10 -10
- package/ol/layers/MaplibreLayer.js +9 -3
- package/ol/layers/MaplibreStyleLayer.d.ts +77 -76
- package/ol/layers/MaplibreStyleLayer.js +237 -238
- package/ol/layers/RealtimeLayer.d.ts +92 -96
- package/ol/layers/RealtimeLayer.js +139 -131
- package/ol/mixins/MobilityLayerMixin.d.ts +9 -9
- package/ol/mixins/PropertiesLayerMixin.d.ts +33 -36
- package/ol/mixins/PropertiesLayerMixin.js +73 -72
- package/ol/renderers/MaplibreLayerRenderer.js +3 -3
- package/ol/renderers/MaplibreStyleLayerRenderer.d.ts +6 -6
- package/ol/renderers/MaplibreStyleLayerRenderer.js +14 -17
- package/ol/renderers/RealtimeLayerRenderer.d.ts +6 -6
- package/ol/renderers/RealtimeLayerRenderer.js +54 -52
- package/ol/utils/getFeatureInfoAtCoordinate.d.ts +1 -1
- package/ol/utils/getFeatureInfoAtCoordinate.js +10 -16
- package/package.json +6 -5
- package/setupTests.js +3 -4
- package/types/common.d.ts +53 -49
- package/types/index.d.ts +1 -1
- package/types/realtime.d.ts +91 -93
- package/types/routing.d.ts +60 -60
- package/types/stops.d.ts +62 -62
|
@@ -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
|
|
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
|
|
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
|
-
*
|
|
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
|
-
|
|
28
|
+
getTrajectoryInfos(id, mode) { }
|
|
25
29
|
/**
|
|
26
|
-
*
|
|
30
|
+
* Render the trajectories
|
|
27
31
|
*/
|
|
28
|
-
|
|
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
|
-
*
|
|
41
|
+
* Start the clock.
|
|
38
42
|
*/
|
|
39
|
-
|
|
43
|
+
start() { }
|
|
40
44
|
/**
|
|
41
|
-
*
|
|
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: RealtimeStopSequence, fullTrajectory: RealtimeFullTrajectory>} A promise that will be resolved with the trajectory informations.
|
|
45
|
+
* Stop the clock.
|
|
46
46
|
*/
|
|
47
|
-
|
|
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-
|
|
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,
|
|
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 {
|
|
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
|
-
|
|
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
|
-
*
|
|
220
|
+
* Id of the selected vehicle.
|
|
170
221
|
*/
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
*
|
|
228
|
+
* Id of the selected vehicle.
|
|
181
229
|
*/
|
|
182
|
-
|
|
183
|
-
value:
|
|
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
|
-
*
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
222
|
-
value:
|
|
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
|
|
233
|
-
// @ts-
|
|
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
|
-
*
|
|
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
|
-
|
|
262
|
-
value:
|
|
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,192 +331,45 @@ 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
|
-
*
|
|
342
|
-
*
|
|
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
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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
|
-
|
|
353
|
-
|
|
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
|
-
*
|
|
369
|
+
* Get the duration before the next update depending on zoom level.
|
|
370
|
+
*
|
|
367
371
|
* @private
|
|
368
|
-
|
|
369
|
-
stopUpdateTime() {
|
|
370
|
-
if (this.updateTimeInterval) {
|
|
371
|
-
clearInterval(this.updateTimeInterval);
|
|
372
|
-
this.updateTimeInterval = undefined;
|
|
373
|
-
}
|
|
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.
|
|
387
|
-
* @private
|
|
388
|
-
*/
|
|
389
|
-
renderTrajectoriesInternal(viewState, noInterpolate = false) {
|
|
390
|
-
var _a;
|
|
391
|
-
if (!this.map || !this.trajectories) {
|
|
392
|
-
return false;
|
|
393
|
-
}
|
|
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);
|
|
400
|
-
}
|
|
401
|
-
// console.timeEnd('sort');
|
|
402
|
-
if (!this.canvas || !this.style) {
|
|
403
|
-
return true;
|
|
404
|
-
}
|
|
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;
|
|
411
|
-
}
|
|
412
|
-
/**
|
|
413
|
-
* Render the trajectories requesting an animation frame and cancelling the previous one.
|
|
414
|
-
* This function must be overrided by children to provide the correct parameters.
|
|
415
|
-
*
|
|
416
|
-
* @param {object} viewState The view state of the map.
|
|
417
|
-
* @param {number[2]} viewState.center Center coordinate of the map in mercator coordinate.
|
|
418
|
-
* @param {number[4]} viewState.extent Extent of the map in mercator coordinates.
|
|
419
|
-
* @param {number[2]} viewState.size Size ([width, height]) of the canvas to render.
|
|
420
|
-
* @param {number} [viewState.rotation = 0] Rotation of the map to render.
|
|
421
|
-
* @param {number} viewState.resolution Resolution of the map to render.
|
|
422
|
-
* @param {boolean} noInterpolate If true trajectories are not interpolated but
|
|
423
|
-
* drawn at the last known coordinate. Use this for performance optimization
|
|
424
|
-
* during map navigation.
|
|
425
|
-
* @private
|
|
426
|
-
*/
|
|
427
|
-
renderTrajectories(viewState, noInterpolate) {
|
|
428
|
-
if (this.requestId) {
|
|
429
|
-
cancelAnimationFrame(this.requestId);
|
|
430
|
-
this.requestId = undefined;
|
|
431
|
-
}
|
|
432
|
-
if (!viewState) {
|
|
433
|
-
return;
|
|
434
|
-
}
|
|
435
|
-
if (!noInterpolate && this.useRequestAnimationFrame) {
|
|
436
|
-
this.requestId = requestAnimationFrame(() => {
|
|
437
|
-
this.renderTrajectoriesInternal(viewState, noInterpolate);
|
|
438
|
-
});
|
|
439
|
-
}
|
|
440
|
-
else if (!noInterpolate && this.useDebounce) {
|
|
441
|
-
this.debounceRenderTrajectories(viewState, noInterpolate);
|
|
442
|
-
}
|
|
443
|
-
else if (!noInterpolate && this.useThrottle) {
|
|
444
|
-
this.throttleRenderTrajectories(viewState, noInterpolate);
|
|
445
|
-
}
|
|
446
|
-
else {
|
|
447
|
-
this.renderTrajectoriesInternal(viewState, noInterpolate);
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
setBbox(extent, zoom) {
|
|
451
|
-
// Clean trajectories before sending the new bbox
|
|
452
|
-
// Purge trajectories:
|
|
453
|
-
// - which are outside the extent
|
|
454
|
-
// - when it's bus and zoom level is too low for them
|
|
455
|
-
if (this.trajectories && extent && zoom) {
|
|
456
|
-
const keys = Object.keys(this.trajectories);
|
|
457
|
-
for (let i = keys.length - 1; i >= 0; i -= 1) {
|
|
458
|
-
this.purgeTrajectory(this.trajectories[keys[i]], extent, zoom);
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
// The backend only supports non float value
|
|
462
|
-
const zoomFloor = Math.floor(zoom);
|
|
463
|
-
if (!extent || Number.isNaN(zoomFloor)) {
|
|
464
|
-
return;
|
|
465
|
-
}
|
|
466
|
-
// The extent does not need to be precise under meter, so we round floor/ceil the values.
|
|
467
|
-
const [minX, minY, maxX, maxY] = extent;
|
|
468
|
-
const bbox = [
|
|
469
|
-
Math.floor(minX),
|
|
470
|
-
Math.floor(minY),
|
|
471
|
-
Math.ceil(maxX),
|
|
472
|
-
Math.ceil(maxY),
|
|
473
|
-
zoomFloor,
|
|
474
|
-
];
|
|
475
|
-
/* @private */
|
|
476
|
-
this.generalizationLevel = this.getGeneralizationLevelByZoom(zoomFloor);
|
|
477
|
-
if (this.generalizationLevel) {
|
|
478
|
-
bbox.push(`gen=${this.generalizationLevel}`);
|
|
479
|
-
}
|
|
480
|
-
/* @private */
|
|
481
|
-
this.mots = this.getMotsByZoom(zoomFloor);
|
|
482
|
-
if (this.mots) {
|
|
483
|
-
bbox.push(`mots=${this.mots}`);
|
|
484
|
-
}
|
|
485
|
-
if (this.tenant) {
|
|
486
|
-
bbox.push(`tenant=${this.tenant}`);
|
|
487
|
-
}
|
|
488
|
-
if (this.mode !== 'topographic') {
|
|
489
|
-
bbox.push(`channel_prefix=${this.mode}`);
|
|
490
|
-
}
|
|
491
|
-
if (this.bboxParameters) {
|
|
492
|
-
Object.entries(this.bboxParameters).forEach(([key, value]) => {
|
|
493
|
-
bbox.push(`${key}=${value}`);
|
|
494
|
-
});
|
|
495
|
-
}
|
|
496
|
-
// Extent and zoom level are mandatory.
|
|
497
|
-
this.api.bbox = bbox;
|
|
498
|
-
}
|
|
499
|
-
/**
|
|
500
|
-
* Get the duration before the next update depending on zoom level.
|
|
501
|
-
*
|
|
502
|
-
* @private
|
|
503
|
-
* @param {number} zoom
|
|
372
|
+
* @param {number} zoom
|
|
504
373
|
*/
|
|
505
374
|
getRefreshTimeInMs(zoom = 0) {
|
|
506
375
|
var _a;
|
|
@@ -513,7 +382,7 @@ function RealtimeLayerMixin(Base) {
|
|
|
513
382
|
this.throttleRenderTrajectories = throttle(this.renderTrajectoriesInternal, nextThrottleTick, { leading: true, trailing: true });
|
|
514
383
|
}
|
|
515
384
|
else if (this.useDebounce) {
|
|
516
|
-
this.debounceRenderTrajectories = debounce(this.renderTrajectoriesInternal, nextThrottleTick, { leading: true,
|
|
385
|
+
this.debounceRenderTrajectories = debounce(this.renderTrajectoriesInternal, nextThrottleTick, { leading: true, maxWait: 5000, trailing: true });
|
|
517
386
|
}
|
|
518
387
|
if ((_a = this.api) === null || _a === void 0 ? void 0 : _a.buffer) {
|
|
519
388
|
const [, size] = this.api.buffer;
|
|
@@ -521,51 +390,6 @@ function RealtimeLayerMixin(Base) {
|
|
|
521
390
|
}
|
|
522
391
|
return nextTick;
|
|
523
392
|
}
|
|
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
|
-
// @ts-expect-error coordinate is added by the RealtimeLayer
|
|
555
|
-
const { coordinate: trajcoord } = trajectories[i].properties;
|
|
556
|
-
if (trajcoord && containsCoordinate(ext, trajcoord)) {
|
|
557
|
-
vehicles.push(trajectories[i]);
|
|
558
|
-
}
|
|
559
|
-
if (vehicles.length === nb) {
|
|
560
|
-
break;
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
return Promise.resolve({
|
|
564
|
-
layer: this,
|
|
565
|
-
features: vehicles.map((vehicle) => this.format.readFeature(vehicle)),
|
|
566
|
-
coordinate,
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
393
|
/**
|
|
570
394
|
* Request the stopSequence and the fullTrajectory informations for a vehicle.
|
|
571
395
|
*
|
|
@@ -582,12 +406,108 @@ function RealtimeLayerMixin(Base) {
|
|
|
582
406
|
];
|
|
583
407
|
return Promise.all(promises).then(([stopSequence, fullTrajectory]) => {
|
|
584
408
|
const response = {
|
|
585
|
-
stopSequence,
|
|
586
409
|
fullTrajectory,
|
|
410
|
+
stopSequence,
|
|
587
411
|
};
|
|
588
412
|
return response;
|
|
589
413
|
});
|
|
590
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
|
+
*
|
|
438
|
+
* @private
|
|
439
|
+
* @override
|
|
440
|
+
*/
|
|
441
|
+
onDeleteTrajectoryMessage(data) {
|
|
442
|
+
if (!data.content) {
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
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 = {};
|
|
454
|
+
}
|
|
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
|
+
}
|
|
591
511
|
/**
|
|
592
512
|
* Remove all trajectories that are in the past.
|
|
593
513
|
*/
|
|
@@ -616,7 +536,7 @@ function RealtimeLayerMixin(Base) {
|
|
|
616
536
|
* @private
|
|
617
537
|
*/
|
|
618
538
|
purgeTrajectory(trajectory, extent, zoom) {
|
|
619
|
-
const {
|
|
539
|
+
const { bounds, type } = trajectory.properties;
|
|
620
540
|
if ((this.isUpdateBboxOnMoveEnd && !intersects(extent, bounds)) ||
|
|
621
541
|
(this.mots && !this.mots.includes(type))) {
|
|
622
542
|
this.removeTrajectory(trajectory);
|
|
@@ -624,22 +544,6 @@ function RealtimeLayerMixin(Base) {
|
|
|
624
544
|
}
|
|
625
545
|
return false;
|
|
626
546
|
}
|
|
627
|
-
/**
|
|
628
|
-
* Add a trajectory.
|
|
629
|
-
* @param {RealtimeTrajectory} trajectory The trajectory to add.
|
|
630
|
-
* @private
|
|
631
|
-
*/
|
|
632
|
-
addTrajectory(trajectory) {
|
|
633
|
-
if (!this.trajectories) {
|
|
634
|
-
this.trajectories = {};
|
|
635
|
-
}
|
|
636
|
-
const id = trajectory.properties.train_id;
|
|
637
|
-
if (id !== undefined) {
|
|
638
|
-
this.trajectories[id] = trajectory;
|
|
639
|
-
}
|
|
640
|
-
// @ts-ignore the parameter are set by subclasses
|
|
641
|
-
this.renderTrajectories();
|
|
642
|
-
}
|
|
643
547
|
removeTrajectory(trajectoryOrId) {
|
|
644
548
|
var _a;
|
|
645
549
|
let id;
|
|
@@ -654,96 +558,192 @@ function RealtimeLayerMixin(Base) {
|
|
|
654
558
|
}
|
|
655
559
|
}
|
|
656
560
|
/**
|
|
657
|
-
*
|
|
561
|
+
* Render the trajectories requesting an animation frame and cancelling the previous one.
|
|
562
|
+
* This function must be overrided by children to provide the correct parameters.
|
|
658
563
|
*
|
|
659
|
-
* @param
|
|
564
|
+
* @param {object} viewState The view state of the map.
|
|
565
|
+
* @param {number[2]} viewState.center Center coordinate of the map in mercator coordinate.
|
|
566
|
+
* @param {number[4]} viewState.extent Extent of the map in mercator coordinates.
|
|
567
|
+
* @param {number[2]} viewState.size Size ([width, height]) of the canvas to render.
|
|
568
|
+
* @param {number} [viewState.rotation = 0] Rotation of the map to render.
|
|
569
|
+
* @param {number} viewState.resolution Resolution of the map to render.
|
|
570
|
+
* @param {boolean} noInterpolate If true trajectories are not interpolated but
|
|
571
|
+
* drawn at the last known coordinate. Use this for performance optimization
|
|
572
|
+
* during map navigation.
|
|
660
573
|
* @private
|
|
661
574
|
*/
|
|
662
|
-
|
|
663
|
-
this.
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
this.
|
|
575
|
+
renderTrajectories(viewState, noInterpolate) {
|
|
576
|
+
if (this.requestId) {
|
|
577
|
+
cancelAnimationFrame(this.requestId);
|
|
578
|
+
this.requestId = undefined;
|
|
579
|
+
}
|
|
580
|
+
if (!viewState) {
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
if (!noInterpolate && this.useRequestAnimationFrame) {
|
|
584
|
+
this.requestId = requestAnimationFrame(() => {
|
|
585
|
+
this.renderTrajectoriesInternal(viewState, noInterpolate);
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
else if (!noInterpolate && this.useDebounce) {
|
|
589
|
+
this.debounceRenderTrajectories(viewState, noInterpolate);
|
|
590
|
+
}
|
|
591
|
+
else if (!noInterpolate && this.useThrottle) {
|
|
592
|
+
this.throttleRenderTrajectories(viewState, noInterpolate);
|
|
672
593
|
}
|
|
673
594
|
else {
|
|
674
|
-
|
|
675
|
-
return;
|
|
676
|
-
}
|
|
677
|
-
this.start();
|
|
595
|
+
this.renderTrajectoriesInternal(viewState, noInterpolate);
|
|
678
596
|
}
|
|
679
597
|
}
|
|
680
598
|
/**
|
|
681
|
-
*
|
|
682
|
-
* It adds a trajectory to the list.
|
|
599
|
+
* Launch renderTrajectories. it avoids duplicating code in renderTrajectories method.
|
|
683
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.
|
|
684
610
|
* @private
|
|
685
611
|
*/
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
612
|
+
renderTrajectoriesInternal(viewState, noInterpolate = false) {
|
|
613
|
+
var _a;
|
|
614
|
+
if (!this.map || !this.trajectories) {
|
|
615
|
+
return false;
|
|
689
616
|
}
|
|
690
|
-
const
|
|
691
|
-
const
|
|
692
|
-
//
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
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);
|
|
696
623
|
}
|
|
697
|
-
// console.
|
|
698
|
-
|
|
699
|
-
|
|
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
|
+
}
|
|
643
|
+
setBbox(extent, zoom) {
|
|
644
|
+
// Clean trajectories before sending the new bbox
|
|
645
|
+
// Purge trajectories:
|
|
646
|
+
// - which are outside the extent
|
|
647
|
+
// - when it's bus and zoom level is too low for them
|
|
648
|
+
if (this.trajectories && extent && zoom) {
|
|
649
|
+
const keys = Object.keys(this.trajectories);
|
|
650
|
+
for (let i = keys.length - 1; i >= 0; i -= 1) {
|
|
651
|
+
this.purgeTrajectory(this.trajectories[keys[i]], extent, zoom);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
// The backend only supports non float value
|
|
655
|
+
const zoomFloor = Math.floor(zoom);
|
|
656
|
+
if (!extent || Number.isNaN(zoomFloor)) {
|
|
700
657
|
return;
|
|
701
658
|
}
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
659
|
+
// The extent does not need to be precise under meter, so we round floor/ceil the values.
|
|
660
|
+
const [minX, minY, maxX, maxY] = extent;
|
|
661
|
+
const bbox = [
|
|
662
|
+
Math.floor(minX),
|
|
663
|
+
Math.floor(minY),
|
|
664
|
+
Math.ceil(maxX),
|
|
665
|
+
Math.ceil(maxY),
|
|
666
|
+
zoomFloor,
|
|
667
|
+
];
|
|
668
|
+
/* @private */
|
|
669
|
+
this.generalizationLevel = this.getGeneralizationLevelByZoom(zoomFloor);
|
|
670
|
+
if (this.generalizationLevel) {
|
|
671
|
+
bbox.push(`gen=${this.generalizationLevel}`);
|
|
672
|
+
}
|
|
673
|
+
/* @private */
|
|
674
|
+
this.mots = this.getMotsByZoom(zoomFloor);
|
|
675
|
+
if (this.mots) {
|
|
676
|
+
bbox.push(`mots=${this.mots}`);
|
|
677
|
+
}
|
|
678
|
+
if (this.tenant) {
|
|
679
|
+
bbox.push(`tenant=${this.tenant}`);
|
|
680
|
+
}
|
|
681
|
+
if (this.mode !== 'topographic') {
|
|
682
|
+
bbox.push(`channel_prefix=${this.mode}`);
|
|
683
|
+
}
|
|
684
|
+
if (this.bboxParameters) {
|
|
685
|
+
Object.entries(this.bboxParameters).forEach(([key, value]) => {
|
|
686
|
+
bbox.push(`${key}=${value}`);
|
|
709
687
|
});
|
|
710
688
|
}
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
689
|
+
// Extent and zoom level are mandatory.
|
|
690
|
+
this.api.bbox = bbox;
|
|
691
|
+
}
|
|
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
|
|
700
|
+
this.renderTrajectories();
|
|
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();
|
|
709
|
+
}
|
|
710
|
+
if (this.onStart) {
|
|
711
|
+
this.onStart(this);
|
|
714
712
|
}
|
|
715
|
-
// TODO Make sure the timeOffset is useful. May be we can remove it.
|
|
716
|
-
// @ts-ignore
|
|
717
|
-
trajectory.properties.timeOffset = Date.now() - data.timestamp;
|
|
718
|
-
this.addTrajectory(trajectory);
|
|
719
713
|
}
|
|
720
714
|
/**
|
|
721
|
-
*
|
|
722
|
-
* It removes the trajectory from the list.
|
|
723
|
-
*
|
|
715
|
+
* Start the clock.
|
|
724
716
|
* @private
|
|
725
|
-
* @override
|
|
726
717
|
*/
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
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();
|
|
725
|
+
}
|
|
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);
|
|
732
730
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
this.
|
|
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);
|
|
739
737
|
}
|
|
740
738
|
}
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
739
|
+
/**
|
|
740
|
+
* Stop the clock.
|
|
741
|
+
* @private
|
|
742
|
+
*/
|
|
743
|
+
stopUpdateTime() {
|
|
744
|
+
if (this.updateTimeInterval) {
|
|
745
|
+
clearInterval(this.updateTimeInterval);
|
|
746
|
+
this.updateTimeInterval = undefined;
|
|
747
747
|
}
|
|
748
748
|
}
|
|
749
749
|
};
|