mobility-toolbox-js 3.0.1-beta.0 → 3.0.1-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.
- package/README.md +11 -3
- package/api/HttpAPI.js +1 -3
- package/api/RealtimeAPI.d.ts +47 -47
- package/api/RealtimeAPI.js +74 -74
- package/api/WebSocketAPI.js +0 -1
- package/common/controls/StopFinderControlCommon.d.ts +1 -1
- package/common/controls/StopFinderControlCommon.js +1 -1
- package/common/styles/realtimeDefaultStyle.js +0 -5
- package/common/styles/realtimeHeadingStyle.js +0 -5
- package/common/styles/realtimeSimpleStyle.d.ts +0 -1
- package/common/styles/realtimeSimpleStyle.js +0 -1
- package/common/typedefs.d.ts +0 -117
- package/common/typedefs.js +0 -31
- package/common/utils/RealtimeEngine.d.ts +214 -0
- package/common/utils/RealtimeEngine.js +554 -0
- package/common/utils/getLayersAsFlatArray.d.ts +0 -1
- package/common/utils/getLayersAsFlatArray.js +0 -1
- package/common/utils/getVehiclePosition.js +1 -4
- package/common/utils/realtimeConfig.d.ts +1 -1
- package/common/utils/realtimeConfig.js +0 -1
- package/common/utils/renderTrajectories.d.ts +1 -0
- package/common/utils/renderTrajectories.js +1 -0
- package/common/utils/sortAndFilterDepartures.d.ts +1 -0
- package/common/utils/sortAndFilterDepartures.js +1 -0
- package/maplibre/controls/CopyrightControl.d.ts +9 -6
- package/maplibre/controls/CopyrightControl.js +11 -8
- package/maplibre/layers/Layer.d.ts +7 -6
- package/maplibre/layers/Layer.js +1 -2
- package/maplibre/layers/RealtimeLayer.d.ts +54 -111
- package/maplibre/layers/RealtimeLayer.js +126 -114
- package/maplibre/utils/getSourceCoordinates.d.ts +1 -0
- package/maplibre/utils/getSourceCoordinates.js +5 -4
- package/mbt.js +6960 -14605
- package/mbt.js.map +4 -4
- package/mbt.min.js +68 -71
- package/mbt.min.js.map +4 -4
- package/ol/controls/CopyrightControl.d.ts +13 -5
- package/ol/controls/CopyrightControl.js +13 -5
- package/ol/controls/RoutingControl.d.ts +29 -18
- package/ol/controls/RoutingControl.js +44 -56
- package/ol/controls/StopFinderControl.d.ts +21 -2
- package/ol/controls/StopFinderControl.js +22 -3
- package/ol/index.d.ts +0 -1
- package/ol/index.js +0 -1
- package/ol/layers/Layer.d.ts +17 -92
- package/ol/layers/Layer.js +17 -3
- package/ol/layers/MaplibreLayer.d.ts +47 -114
- package/ol/layers/MaplibreLayer.js +102 -46
- package/ol/layers/MaplibreStyleLayer.d.ts +67 -147
- package/ol/layers/MaplibreStyleLayer.js +170 -123
- package/ol/layers/RealtimeLayer.d.ts +85 -218
- package/ol/layers/RealtimeLayer.js +170 -181
- package/ol/layers/VectorLayer.d.ts +1 -2
- package/ol/layers/VectorLayer.js +7 -6
- package/ol/renderers/MaplibreLayerRenderer.d.ts +9 -0
- package/ol/renderers/MaplibreLayerRenderer.js +35 -137
- package/ol/renderers/MaplibreStyleLayerRenderer.js +2 -2
- package/ol/renderers/RealtimeLayerRenderer.d.ts +1 -1
- package/ol/renderers/RealtimeLayerRenderer.js +6 -31
- package/ol/styles/fullTrajectoryDelayStyle.js +5 -7
- package/ol/styles/fullTrajectoryStyle.d.ts +1 -2
- package/ol/styles/fullTrajectoryStyle.js +5 -7
- package/ol/styles/routingStyle.d.ts +0 -1
- package/ol/styles/routingStyle.js +13 -10
- package/ol/utils/defineDeprecatedProperties.d.ts +10 -0
- package/ol/utils/defineDeprecatedProperties.js +180 -0
- package/package.json +40 -39
- package/setupTests.js +14 -0
- package/types/common.d.ts +4 -27
- package/types/realtime.d.ts +7 -2
- package/common/mixins/RealtimeLayerMixin.d.ts +0 -267
- package/common/mixins/RealtimeLayerMixin.js +0 -751
- package/ol/mixins/MobilityLayerMixin.d.ts +0 -96
- package/ol/mixins/MobilityLayerMixin.js +0 -6
- package/ol/mixins/PropertiesLayerMixin.d.ts +0 -136
- package/ol/mixins/PropertiesLayerMixin.js +0 -178
- package/ol/mixins/index.d.ts +0 -1
- package/ol/mixins/index.js +0 -2
|
@@ -1,751 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-empty-function,@typescript-eslint/no-empty-function */
|
|
2
|
-
/* eslint-disable no-useless-constructor,@typescript-eslint/no-useless-constructor */
|
|
3
|
-
/* eslint-disable no-unused-vars,@typescript-eslint/no-unused-vars */
|
|
4
|
-
/* eslint-disable class-methods-use-this */
|
|
5
|
-
import debounce from 'lodash.debounce';
|
|
6
|
-
import throttle from 'lodash.throttle';
|
|
7
|
-
/* eslint-disable max-classes-per-file */
|
|
8
|
-
import { buffer, containsCoordinate, intersects } from 'ol/extent';
|
|
9
|
-
import GeoJSON from 'ol/format/GeoJSON';
|
|
10
|
-
import { unByKey } from 'ol/Observable';
|
|
11
|
-
import { fromLonLat } from 'ol/proj';
|
|
12
|
-
import { RealtimeAPI, RealtimeModes } from '../../api';
|
|
13
|
-
import realtimeDefaultStyle from '../styles/realtimeDefaultStyle';
|
|
14
|
-
import * as realtimeConfig from '../utils/realtimeConfig';
|
|
15
|
-
import renderTrajectories from '../utils/renderTrajectories';
|
|
16
|
-
/**
|
|
17
|
-
* RealtimeLayerInterface.
|
|
18
|
-
* @private
|
|
19
|
-
*/
|
|
20
|
-
export class RealtimeLayerInterface {
|
|
21
|
-
/**
|
|
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.
|
|
27
|
-
*/
|
|
28
|
-
getTrajectoryInfos(id, mode) { }
|
|
29
|
-
/**
|
|
30
|
-
* Render the trajectories
|
|
31
|
-
*/
|
|
32
|
-
renderTrajectories() { }
|
|
33
|
-
/**
|
|
34
|
-
* Set the Realtime api's bbox.
|
|
35
|
-
*
|
|
36
|
-
* @param {Array<number>} extent Extent to request, [minX, minY, maxX, maxY].
|
|
37
|
-
* @param {number} zoom Zoom level to request. Must be an integer.
|
|
38
|
-
*/
|
|
39
|
-
setBbox(extent, zoom) { }
|
|
40
|
-
/**
|
|
41
|
-
* Start the clock.
|
|
42
|
-
*/
|
|
43
|
-
start() { }
|
|
44
|
-
/**
|
|
45
|
-
* Stop the clock.
|
|
46
|
-
*/
|
|
47
|
-
stop() { }
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Mixin for RealtimeLayerInterface.
|
|
51
|
-
*
|
|
52
|
-
* @param {Class} Base A class to extend with {RealtimeLayerInterface} functionnalities.
|
|
53
|
-
* @return {Class} A class that implements {RealtimeLayerInterface} class and extends Base;
|
|
54
|
-
* @private
|
|
55
|
-
*/
|
|
56
|
-
function RealtimeLayerMixin(Base) {
|
|
57
|
-
// @ts-expect-error
|
|
58
|
-
return class Mixin extends Base {
|
|
59
|
-
constructor(options) {
|
|
60
|
-
super(Object.assign({ hitTolerance: 10 }, options));
|
|
61
|
-
this.defineProperties(options);
|
|
62
|
-
this.debug = options.debug || false;
|
|
63
|
-
this.mode = options.mode || RealtimeModes.TOPOGRAPHIC;
|
|
64
|
-
this.api = options.api || new RealtimeAPI(options);
|
|
65
|
-
this.tenant = options.tenant || ''; // sbb,sbh or sbm
|
|
66
|
-
this.minZoomInterpolation = options.minZoomInterpolation || 8; // Min zoom level from which trains positions are not interpolated.
|
|
67
|
-
this.format = new GeoJSON();
|
|
68
|
-
this.onStart = options.onStart;
|
|
69
|
-
this.onStop = options.onStop;
|
|
70
|
-
// Server will block non train before zoom 9
|
|
71
|
-
this.motsByZoom = options.motsByZoom || [
|
|
72
|
-
realtimeConfig.MOTS_ONLY_RAIL,
|
|
73
|
-
realtimeConfig.MOTS_ONLY_RAIL,
|
|
74
|
-
realtimeConfig.MOTS_ONLY_RAIL,
|
|
75
|
-
realtimeConfig.MOTS_ONLY_RAIL,
|
|
76
|
-
realtimeConfig.MOTS_ONLY_RAIL,
|
|
77
|
-
realtimeConfig.MOTS_ONLY_RAIL,
|
|
78
|
-
realtimeConfig.MOTS_ONLY_RAIL,
|
|
79
|
-
realtimeConfig.MOTS_ONLY_RAIL,
|
|
80
|
-
realtimeConfig.MOTS_ONLY_RAIL,
|
|
81
|
-
realtimeConfig.MOTS_WITHOUT_CABLE,
|
|
82
|
-
realtimeConfig.MOTS_WITHOUT_CABLE,
|
|
83
|
-
];
|
|
84
|
-
this.getMotsByZoom = (zoom) => {
|
|
85
|
-
if (options.getMotsByZoom) {
|
|
86
|
-
return options.getMotsByZoom(zoom, this.motsByZoom);
|
|
87
|
-
}
|
|
88
|
-
return this.motsByZoom[zoom];
|
|
89
|
-
};
|
|
90
|
-
// Generalization levels by zoom
|
|
91
|
-
this.generalizationLevelByZoom = options.generalizationLevelByZoom || [];
|
|
92
|
-
this.getGeneralizationLevelByZoom = (zoom) => {
|
|
93
|
-
if (options.getGeneralizationLevelByZoom) {
|
|
94
|
-
return options.getGeneralizationLevelByZoom(zoom, this.generalizationLevelByZoom);
|
|
95
|
-
}
|
|
96
|
-
return this.generalizationLevelByZoom[zoom];
|
|
97
|
-
};
|
|
98
|
-
// Render time interval by zoom
|
|
99
|
-
this.renderTimeIntervalByZoom = options.renderTimeIntervalByZoom || [
|
|
100
|
-
100000, 50000, 40000, 30000, 20000, 15000, 10000, 5000, 2000, 1000, 400,
|
|
101
|
-
300, 250, 180, 90, 60, 50, 50, 50, 50, 50,
|
|
102
|
-
];
|
|
103
|
-
this.getRenderTimeIntervalByZoom = (zoom) => {
|
|
104
|
-
if (options.getRenderTimeIntervalByZoom) {
|
|
105
|
-
return options.getRenderTimeIntervalByZoom(zoom, this.renderTimeIntervalByZoom);
|
|
106
|
-
}
|
|
107
|
-
return this.renderTimeIntervalByZoom[zoom];
|
|
108
|
-
};
|
|
109
|
-
// This property will call api.setBbox on each movend event
|
|
110
|
-
this.isUpdateBboxOnMoveEnd = options.isUpdateBboxOnMoveEnd !== false;
|
|
111
|
-
// Define throttling and debounce render function
|
|
112
|
-
this.throttleRenderTrajectories = throttle(this.renderTrajectoriesInternal, 50, { leading: false, trailing: true });
|
|
113
|
-
this.debounceRenderTrajectories = debounce(this.renderTrajectoriesInternal, 50, { leading: true, maxWait: 5000, trailing: true });
|
|
114
|
-
// Bind callbacks
|
|
115
|
-
// this.onFeatureHover = this.onFeatureHover.bind(this);
|
|
116
|
-
// this.onFeatureClick = this.onFeatureClick.bind(this);
|
|
117
|
-
this.renderTrajectoriesInternal =
|
|
118
|
-
this.renderTrajectoriesInternal.bind(this);
|
|
119
|
-
this.onTrajectoryMessage = this.onTrajectoryMessage.bind(this);
|
|
120
|
-
this.onDeleteTrajectoryMessage =
|
|
121
|
-
this.onDeleteTrajectoryMessage.bind(this);
|
|
122
|
-
this.onDocumentVisibilityChange =
|
|
123
|
-
this.onDocumentVisibilityChange.bind(this);
|
|
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
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Define layer's properties.
|
|
149
|
-
*
|
|
150
|
-
* @private
|
|
151
|
-
*/
|
|
152
|
-
defineProperties(options) {
|
|
153
|
-
(super.defineProperties || (() => { }))(options);
|
|
154
|
-
const { bboxParameters, canvas, filter, hoverVehicleId, live, mode, pixelRatio, selectedVehicleId, sort, speed, style, styleOptions, time, } = options;
|
|
155
|
-
let currCanvas = canvas;
|
|
156
|
-
let currSpeed = speed || 1;
|
|
157
|
-
let currTime = time || new Date();
|
|
158
|
-
let currMode = mode || RealtimeModes.TOPOGRAPHIC;
|
|
159
|
-
let currStyle = style || realtimeDefaultStyle;
|
|
160
|
-
Object.defineProperties(this, {
|
|
161
|
-
/**
|
|
162
|
-
* Custom parameters to send on each BBOX request.
|
|
163
|
-
*/
|
|
164
|
-
bboxParameters: {
|
|
165
|
-
value: bboxParameters,
|
|
166
|
-
writable: true,
|
|
167
|
-
},
|
|
168
|
-
canvas: {
|
|
169
|
-
get: () => {
|
|
170
|
-
if (!currCanvas) {
|
|
171
|
-
currCanvas = document.createElement('canvas');
|
|
172
|
-
}
|
|
173
|
-
return currCanvas;
|
|
174
|
-
},
|
|
175
|
-
set: (cnvas) => {
|
|
176
|
-
currCanvas = cnvas;
|
|
177
|
-
},
|
|
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
|
-
},
|
|
202
|
-
/**
|
|
203
|
-
* Style function used to render a vehicle.
|
|
204
|
-
*/
|
|
205
|
-
mode: {
|
|
206
|
-
get: () => currMode,
|
|
207
|
-
set: (newMode) => {
|
|
208
|
-
var _a, _b;
|
|
209
|
-
if (newMode === currMode) {
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
currMode = newMode;
|
|
213
|
-
if ((_b = (_a = this.api) === null || _a === void 0 ? void 0 : _a.wsApi) === null || _b === void 0 ? void 0 : _b.open) {
|
|
214
|
-
this.stop();
|
|
215
|
-
this.start();
|
|
216
|
-
}
|
|
217
|
-
},
|
|
218
|
-
},
|
|
219
|
-
/**
|
|
220
|
-
* Id of the selected vehicle.
|
|
221
|
-
*/
|
|
222
|
-
pixelRatio: {
|
|
223
|
-
value: pixelRatio ||
|
|
224
|
-
(typeof window !== 'undefined' ? window.devicePixelRatio : 1),
|
|
225
|
-
writable: true,
|
|
226
|
-
},
|
|
227
|
-
/**
|
|
228
|
-
* Id of the selected vehicle.
|
|
229
|
-
*/
|
|
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,
|
|
240
|
-
},
|
|
241
|
-
/**
|
|
242
|
-
* Speed of the wheel of time.
|
|
243
|
-
* If live property is true. The speed is ignored.
|
|
244
|
-
*/
|
|
245
|
-
speed: {
|
|
246
|
-
get: () => currSpeed,
|
|
247
|
-
set: (newSpeed) => {
|
|
248
|
-
currSpeed = newSpeed;
|
|
249
|
-
this.start();
|
|
250
|
-
},
|
|
251
|
-
},
|
|
252
|
-
/**
|
|
253
|
-
* Style function used to render a vehicle.
|
|
254
|
-
*/
|
|
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
|
-
},
|
|
262
|
-
},
|
|
263
|
-
/**
|
|
264
|
-
* Custom options to pass as last parameter of the style function.
|
|
265
|
-
*/
|
|
266
|
-
styleOptions: {
|
|
267
|
-
value: Object.assign(Object.assign({}, realtimeConfig), (styleOptions || {})),
|
|
268
|
-
},
|
|
269
|
-
/**
|
|
270
|
-
* Time used to display the trajectories. Can be a Date or a number in ms representing a Date.
|
|
271
|
-
* If live property is true. The setter does nothing.
|
|
272
|
-
*/
|
|
273
|
-
time: {
|
|
274
|
-
get: () => currTime,
|
|
275
|
-
set: (newTime) => {
|
|
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
|
|
278
|
-
this.renderTrajectories();
|
|
279
|
-
},
|
|
280
|
-
},
|
|
281
|
-
/**
|
|
282
|
-
* Keep track of which trajectories are stored.
|
|
283
|
-
*/
|
|
284
|
-
trajectories: {
|
|
285
|
-
value: {},
|
|
286
|
-
writable: true,
|
|
287
|
-
},
|
|
288
|
-
/**
|
|
289
|
-
* If true, encapsulates the renderTrajectories calls in a debounce function.
|
|
290
|
-
*/
|
|
291
|
-
useDebounce: {
|
|
292
|
-
value: options.useDebounce || false,
|
|
293
|
-
writable: true,
|
|
294
|
-
},
|
|
295
|
-
/**
|
|
296
|
-
* If true, encapsulates the renderTrajectories calls in a requestAnimationFrame.
|
|
297
|
-
*/
|
|
298
|
-
useRequestAnimationFrame: {
|
|
299
|
-
value: options.useRequestAnimationFrame || false,
|
|
300
|
-
writable: true,
|
|
301
|
-
},
|
|
302
|
-
/**
|
|
303
|
-
* If true, encapsulates the renderTrajectories calls in a throttle function. Default to true.
|
|
304
|
-
*/
|
|
305
|
-
useThrottle: {
|
|
306
|
-
value: options.useThrottle !== false,
|
|
307
|
-
writable: true,
|
|
308
|
-
},
|
|
309
|
-
/**
|
|
310
|
-
* Debug properties.
|
|
311
|
-
*/
|
|
312
|
-
// Not used anymore, but could be useful for debugging.
|
|
313
|
-
// showVehicleTraj: {
|
|
314
|
-
// value:
|
|
315
|
-
// options.showVehicleTraj !== undefined
|
|
316
|
-
// ? options.showVehicleTraj
|
|
317
|
-
// : true,
|
|
318
|
-
// writable: true,
|
|
319
|
-
// },
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
detachFromMap() {
|
|
323
|
-
document.removeEventListener('visibilitychange', this.onDocumentVisibilityChange);
|
|
324
|
-
this.stop();
|
|
325
|
-
unByKey(this.visibilityRef);
|
|
326
|
-
if (this.canvas) {
|
|
327
|
-
const context = this.canvas.getContext('2d');
|
|
328
|
-
if (context) {
|
|
329
|
-
context.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
330
|
-
}
|
|
331
|
-
super.detachFromMap();
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
/**
|
|
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.
|
|
342
|
-
*/
|
|
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]);
|
|
357
|
-
}
|
|
358
|
-
if (vehicles.length === nb) {
|
|
359
|
-
break;
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
return Promise.resolve({
|
|
363
|
-
coordinate,
|
|
364
|
-
features: vehicles.map((vehicle) => this.format.readFeature(vehicle)),
|
|
365
|
-
layer: this,
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
/**
|
|
369
|
-
* Get the duration before the next update depending on zoom level.
|
|
370
|
-
*
|
|
371
|
-
* @private
|
|
372
|
-
* @param {number} zoom
|
|
373
|
-
*/
|
|
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 });
|
|
383
|
-
}
|
|
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
|
-
*
|
|
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
|
-
}
|
|
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);
|
|
543
|
-
return true;
|
|
544
|
-
}
|
|
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
|
-
}
|
|
559
|
-
}
|
|
560
|
-
/**
|
|
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.
|
|
563
|
-
*
|
|
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.
|
|
573
|
-
* @private
|
|
574
|
-
*/
|
|
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);
|
|
593
|
-
}
|
|
594
|
-
else {
|
|
595
|
-
this.renderTrajectoriesInternal(viewState, noInterpolate);
|
|
596
|
-
}
|
|
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
|
-
}
|
|
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)) {
|
|
657
|
-
return;
|
|
658
|
-
}
|
|
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}`);
|
|
687
|
-
});
|
|
688
|
-
}
|
|
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);
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
/**
|
|
715
|
-
* Start the clock.
|
|
716
|
-
* @private
|
|
717
|
-
*/
|
|
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);
|
|
730
|
-
}
|
|
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);
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
/**
|
|
740
|
-
* Stop the clock.
|
|
741
|
-
* @private
|
|
742
|
-
*/
|
|
743
|
-
stopUpdateTime() {
|
|
744
|
-
if (this.updateTimeInterval) {
|
|
745
|
-
clearInterval(this.updateTimeInterval);
|
|
746
|
-
this.updateTimeInterval = undefined;
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
};
|
|
750
|
-
}
|
|
751
|
-
export default RealtimeLayerMixin;
|