mobility-toolbox-js 3.0.0-beta.8 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -3
- package/api/HttpAPI.d.ts +20 -0
- package/api/HttpAPI.js +1 -14
- package/api/RealtimeAPI.d.ts +404 -0
- package/api/RealtimeAPI.js +265 -206
- package/api/RoutingAPI.d.ts +47 -0
- package/api/RoutingAPI.js +17 -7
- package/api/StopsAPI.d.ts +44 -0
- package/api/StopsAPI.js +17 -11
- package/api/WebSocketAPI.d.ts +147 -0
- package/api/WebSocketAPI.js +164 -165
- package/api/index.d.ts +3 -0
- package/api/index.js +1 -1
- package/api/typedefs.d.ts +76 -0
- package/api/typedefs.js +27 -42
- package/common/controls/StopFinderControlCommon.d.ts +53 -0
- package/common/controls/StopFinderControlCommon.js +32 -32
- package/common/index.d.ts +2 -0
- package/common/index.js +1 -1
- package/common/styles/index.d.ts +4 -0
- package/common/styles/realtimeDefaultStyle.d.ts +36 -0
- package/common/styles/realtimeDefaultStyle.js +6 -11
- package/common/styles/realtimeDelayStyle.d.ts +12 -0
- package/common/styles/realtimeHeadingStyle.d.ts +12 -0
- package/common/styles/realtimeHeadingStyle.js +5 -10
- package/common/styles/realtimeSimpleStyle.d.ts +3 -0
- package/common/styles/realtimeSimpleStyle.js +0 -1
- package/common/typedefs.d.ts +102 -0
- package/common/typedefs.js +6 -31
- package/common/utils/RealtimeEngine.d.ts +214 -0
- package/common/utils/RealtimeEngine.js +554 -0
- package/common/utils/compareDepartures.d.ts +10 -0
- package/common/utils/compareDepartures.js +2 -2
- package/common/utils/constants.d.ts +5 -0
- package/common/utils/createCanvas.d.ts +10 -0
- package/common/utils/createDefaultCopyrightElt.d.ts +5 -0
- package/common/utils/createDefaultStopFinderElt.d.ts +5 -0
- package/common/utils/createRealtimeFilters.d.ts +12 -0
- package/common/utils/debounceDeparturesMessages.d.ts +12 -0
- package/common/utils/debounceWebsocketMessages.d.ts +11 -0
- package/common/utils/getLayersAsFlatArray.d.ts +2 -0
- package/common/utils/getLayersAsFlatArray.js +0 -1
- package/common/utils/getMapGlCopyrights.d.ts +17 -0
- package/common/utils/getMapGlCopyrights.js +3 -3
- package/common/utils/getRealtimeModeSuffix.d.ts +10 -0
- package/common/utils/getRealtimeModeSuffix.js +1 -0
- package/common/utils/getUrlWithParams.d.ts +8 -0
- package/common/utils/getVehiclePosition.d.ts +17 -0
- package/common/utils/getVehiclePosition.js +6 -3
- package/common/utils/index.d.ts +16 -0
- package/common/utils/realtimeConfig.d.ts +64 -0
- package/common/utils/realtimeConfig.js +0 -1
- package/common/utils/removeDuplicate.d.ts +9 -0
- package/common/utils/renderTrajectories.d.ts +17 -0
- package/common/utils/renderTrajectories.js +7 -6
- package/common/utils/sortAndFilterDepartures.d.ts +16 -0
- package/common/utils/sortAndFilterDepartures.js +2 -1
- package/common/utils/sortByDelay.d.ts +3 -0
- package/common/utils/sortByDelay.js +5 -1
- package/common/utils/timeUtils.d.ts +23 -0
- package/common/utils/toMercatorExtent.d.ts +5 -0
- package/iife.d.ts +2 -0
- package/index.d.ts +9 -0
- package/maplibre/controls/CopyrightControl.d.ts +38 -0
- package/maplibre/controls/CopyrightControl.js +11 -8
- package/maplibre/controls/index.d.ts +1 -0
- package/maplibre/index.d.ts +5 -0
- package/maplibre/layers/Layer.d.ts +29 -0
- package/maplibre/layers/Layer.js +2 -3
- package/maplibre/layers/RealtimeLayer.d.ts +124 -0
- package/maplibre/layers/RealtimeLayer.js +154 -118
- package/maplibre/layers/index.d.ts +2 -0
- package/maplibre/utils/getMercatorResolution.d.ts +7 -0
- package/maplibre/utils/getSourceCoordinates.d.ts +8 -0
- package/maplibre/utils/getSourceCoordinates.js +6 -5
- package/maplibre/utils/index.d.ts +2 -0
- package/mbt.js +14585 -14557
- package/mbt.js.map +4 -4
- package/mbt.min.js +75 -75
- package/mbt.min.js.map +4 -4
- package/ol/controls/CopyrightControl.d.ts +39 -0
- package/ol/controls/CopyrightControl.js +13 -5
- package/ol/controls/RoutingControl.d.ts +213 -0
- package/ol/controls/RoutingControl.js +250 -264
- package/ol/controls/StopFinderControl.d.ts +56 -0
- package/ol/controls/StopFinderControl.js +24 -5
- package/ol/controls/index.d.ts +3 -0
- package/ol/index.d.ts +6 -0
- package/ol/layers/Layer.d.ts +26 -0
- package/ol/layers/Layer.js +39 -0
- package/ol/layers/MaplibreLayer.d.ts +91 -0
- package/ol/layers/MaplibreLayer.js +154 -31
- package/ol/layers/MaplibreStyleLayer.d.ts +157 -0
- package/ol/layers/MaplibreStyleLayer.js +281 -210
- package/ol/layers/RealtimeLayer.d.ts +150 -0
- package/ol/layers/RealtimeLayer.js +210 -206
- package/ol/layers/VectorLayer.d.ts +17 -0
- package/ol/layers/VectorLayer.js +33 -0
- package/ol/layers/index.d.ts +5 -0
- package/ol/layers/index.js +3 -0
- package/ol/renderers/MaplibreLayerRenderer.d.ts +0 -0
- package/ol/renderers/MaplibreLayerRenderer.js +142 -114
- package/ol/renderers/MaplibreStyleLayerRenderer.d.ts +20 -0
- package/ol/renderers/MaplibreStyleLayerRenderer.js +20 -23
- package/ol/renderers/RealtimeLayerRenderer.d.ts +22 -0
- package/ol/renderers/RealtimeLayerRenderer.js +50 -70
- package/ol/styles/fullTrajectoryDelayStyle.d.ts +6 -0
- package/ol/styles/fullTrajectoryDelayStyle.js +5 -7
- package/ol/styles/fullTrajectoryStyle.d.ts +4 -0
- package/ol/styles/fullTrajectoryStyle.js +5 -7
- package/ol/styles/index.d.ts +3 -0
- package/ol/styles/routingStyle.d.ts +3 -0
- package/ol/styles/routingStyle.js +13 -10
- package/ol/utils/defineDeprecatedProperties.d.ts +10 -0
- package/ol/utils/defineDeprecatedProperties.js +180 -0
- package/ol/utils/getFeatureInfoAtCoordinate.d.ts +8 -0
- package/ol/utils/getFeatureInfoAtCoordinate.js +11 -17
- package/ol/utils/index.d.ts +1 -0
- package/package.json +44 -44
- package/setupTests.d.ts +1 -0
- package/setupTests.js +17 -4
- package/types/common.d.ts +53 -69
- package/types/index.d.ts +1 -1
- package/types/realtime.d.ts +98 -95
- package/types/routing.d.ts +60 -60
- package/types/stops.d.ts +62 -62
- package/common/mixins/RealtimeLayerMixin.js +0 -743
- package/ol/layers/MapGlLayer.js +0 -142
- package/ol/mixins/MobilityLayerMixin.js +0 -9
- package/ol/mixins/PropertiesLayerMixin.js +0 -142
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
import debounce from 'lodash.debounce';
|
|
2
|
+
import throttle from 'lodash.throttle';
|
|
3
|
+
import { buffer, containsCoordinate, intersects } from 'ol/extent';
|
|
4
|
+
import GeoJSON from 'ol/format/GeoJSON';
|
|
5
|
+
import { fromLonLat } from 'ol/proj';
|
|
6
|
+
import { RealtimeAPI, RealtimeModes } from '../../api';
|
|
7
|
+
import realtimeDefaultStyle from '../styles/realtimeDefaultStyle';
|
|
8
|
+
import * as realtimeConfig from './realtimeConfig';
|
|
9
|
+
import renderTrajectories from './renderTrajectories';
|
|
10
|
+
/**
|
|
11
|
+
* This class is responsible for drawing trajectories from a realtime API in a canvas,
|
|
12
|
+
* depending on the map's view state and at a specific time.
|
|
13
|
+
*
|
|
14
|
+
* This class is totally agnostic from Maplibre or OpenLayers and must stay taht way.
|
|
15
|
+
*/
|
|
16
|
+
class RealtimeEngine {
|
|
17
|
+
constructor(options) {
|
|
18
|
+
this.getViewState = () => ({});
|
|
19
|
+
this.shouldRender = () => true;
|
|
20
|
+
this._mode = options.mode || RealtimeModes.TOPOGRAPHIC;
|
|
21
|
+
this._speed = options.speed || 1; // If live property is true. The speed is ignored.
|
|
22
|
+
this._style = options.style || realtimeDefaultStyle;
|
|
23
|
+
this._time = options.time || new Date();
|
|
24
|
+
this.api = options.api || new RealtimeAPI(options);
|
|
25
|
+
this.bboxParameters = options.bboxParameters;
|
|
26
|
+
this.canvas = options.canvas || document.createElement('canvas');
|
|
27
|
+
this.debug = options.debug || false;
|
|
28
|
+
this.filter = options.filter;
|
|
29
|
+
this.hoverVehicleId = options.hoverVehicleId;
|
|
30
|
+
/**
|
|
31
|
+
* If true. The layer will always use Date.now() on the next tick to render the trajectories.
|
|
32
|
+
* When true, setting the time property has no effect.
|
|
33
|
+
*/
|
|
34
|
+
this.live = options.live !== false;
|
|
35
|
+
this.minZoomInterpolation = options.minZoomInterpolation || 8; // Min zoom level from which trains positions are not interpolated.
|
|
36
|
+
this.pixelRatio =
|
|
37
|
+
options.pixelRatio ||
|
|
38
|
+
(typeof window !== 'undefined' ? window.devicePixelRatio : 1);
|
|
39
|
+
this.selectedVehicleId = options.selectedVehicleId;
|
|
40
|
+
this.sort = options.sort;
|
|
41
|
+
/**
|
|
42
|
+
* Custom options to pass as last parameter of the style function.
|
|
43
|
+
*/
|
|
44
|
+
// @ts-expect-error good type must be defined
|
|
45
|
+
this.styleOptions = Object.assign(Object.assign({}, realtimeConfig), (options.styleOptions || {}));
|
|
46
|
+
this.tenant = options.tenant || ''; // sbb,sbh or sbm
|
|
47
|
+
this.trajectories = {};
|
|
48
|
+
this.useDebounce = options.useDebounce || false;
|
|
49
|
+
this.useRequestAnimationFrame = options.useRequestAnimationFrame || false;
|
|
50
|
+
this.useThrottle = options.useThrottle !== false; // the default behavior
|
|
51
|
+
this.getViewState = options.getViewState || (() => ({}));
|
|
52
|
+
this.shouldRender = options.shouldRender || (() => true);
|
|
53
|
+
this.onRender = options.onRender;
|
|
54
|
+
this.onStart = options.onStart;
|
|
55
|
+
this.onStop = options.onStop;
|
|
56
|
+
this.format = new GeoJSON();
|
|
57
|
+
// Server will block non train before zoom 9
|
|
58
|
+
this.motsByZoom = options.motsByZoom || [
|
|
59
|
+
realtimeConfig.MOTS_ONLY_RAIL,
|
|
60
|
+
realtimeConfig.MOTS_ONLY_RAIL,
|
|
61
|
+
realtimeConfig.MOTS_ONLY_RAIL,
|
|
62
|
+
realtimeConfig.MOTS_ONLY_RAIL,
|
|
63
|
+
realtimeConfig.MOTS_ONLY_RAIL,
|
|
64
|
+
realtimeConfig.MOTS_ONLY_RAIL,
|
|
65
|
+
realtimeConfig.MOTS_ONLY_RAIL,
|
|
66
|
+
realtimeConfig.MOTS_ONLY_RAIL,
|
|
67
|
+
realtimeConfig.MOTS_ONLY_RAIL,
|
|
68
|
+
realtimeConfig.MOTS_WITHOUT_CABLE,
|
|
69
|
+
realtimeConfig.MOTS_WITHOUT_CABLE,
|
|
70
|
+
];
|
|
71
|
+
// Mots by zoom
|
|
72
|
+
this.getMotsByZoom = (zoom) => {
|
|
73
|
+
if (options.getMotsByZoom) {
|
|
74
|
+
return options.getMotsByZoom(zoom, this.motsByZoom);
|
|
75
|
+
}
|
|
76
|
+
return this.motsByZoom[zoom];
|
|
77
|
+
};
|
|
78
|
+
// Generalization levels by zoom
|
|
79
|
+
this.generalizationLevelByZoom = options.generalizationLevelByZoom || [];
|
|
80
|
+
this.getGeneralizationLevelByZoom = (zoom) => {
|
|
81
|
+
if (options.getGeneralizationLevelByZoom) {
|
|
82
|
+
return options.getGeneralizationLevelByZoom(zoom, this.generalizationLevelByZoom);
|
|
83
|
+
}
|
|
84
|
+
return this.generalizationLevelByZoom[zoom];
|
|
85
|
+
};
|
|
86
|
+
// Render time interval by zoom
|
|
87
|
+
this.renderTimeIntervalByZoom = options.renderTimeIntervalByZoom || [
|
|
88
|
+
100000, 50000, 40000, 30000, 20000, 15000, 10000, 5000, 2000, 1000, 400,
|
|
89
|
+
300, 250, 180, 90, 60, 50, 50, 50, 50, 50,
|
|
90
|
+
];
|
|
91
|
+
this.getRenderTimeIntervalByZoom = (zoom) => {
|
|
92
|
+
if (options.getRenderTimeIntervalByZoom) {
|
|
93
|
+
return options.getRenderTimeIntervalByZoom(zoom, this.renderTimeIntervalByZoom);
|
|
94
|
+
}
|
|
95
|
+
return this.renderTimeIntervalByZoom[zoom];
|
|
96
|
+
};
|
|
97
|
+
// This property will call api.setBbox on each movend event
|
|
98
|
+
this.isUpdateBboxOnMoveEnd = options.isUpdateBboxOnMoveEnd !== false;
|
|
99
|
+
// Define throttling and debounce render function
|
|
100
|
+
this.throttleRenderTrajectories = throttle(this.renderTrajectoriesInternal, 50, { leading: false, trailing: true });
|
|
101
|
+
this.debounceRenderTrajectories = debounce(this.renderTrajectoriesInternal, 50, { leading: true, maxWait: 5000, trailing: true });
|
|
102
|
+
this.renderState = {
|
|
103
|
+
center: [0, 0],
|
|
104
|
+
rotation: 0,
|
|
105
|
+
zoom: undefined,
|
|
106
|
+
};
|
|
107
|
+
this.onTrajectoryMessage = this.onTrajectoryMessage.bind(this);
|
|
108
|
+
this.onDeleteTrajectoryMessage = this.onDeleteTrajectoryMessage.bind(this);
|
|
109
|
+
this.onDocumentVisibilityChange =
|
|
110
|
+
this.onDocumentVisibilityChange.bind(this);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Add a trajectory.
|
|
114
|
+
* @param {RealtimeTrajectory} trajectory The trajectory to add.
|
|
115
|
+
* @private
|
|
116
|
+
*/
|
|
117
|
+
addTrajectory(trajectory) {
|
|
118
|
+
if (!this.trajectories) {
|
|
119
|
+
this.trajectories = {};
|
|
120
|
+
}
|
|
121
|
+
const id = trajectory.properties.train_id;
|
|
122
|
+
if (id !== undefined) {
|
|
123
|
+
this.trajectories[id] = trajectory;
|
|
124
|
+
}
|
|
125
|
+
this.renderTrajectories();
|
|
126
|
+
}
|
|
127
|
+
attachToMap() {
|
|
128
|
+
// To avoid browser hanging when the tab is not visible for a certain amount of time,
|
|
129
|
+
// We stop the rendering and the websocket when hide and start again when show.
|
|
130
|
+
document.addEventListener('visibilitychange', this.onDocumentVisibilityChange);
|
|
131
|
+
}
|
|
132
|
+
detachFromMap() {
|
|
133
|
+
document.removeEventListener('visibilitychange', this.onDocumentVisibilityChange);
|
|
134
|
+
this.stop();
|
|
135
|
+
if (this.canvas) {
|
|
136
|
+
const context = this.canvas.getContext('2d');
|
|
137
|
+
if (context) {
|
|
138
|
+
context.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Get the duration before the next update depending on zoom level.
|
|
144
|
+
*
|
|
145
|
+
* @private
|
|
146
|
+
*/
|
|
147
|
+
getRefreshTimeInMs() {
|
|
148
|
+
var _a;
|
|
149
|
+
const viewState = this.getViewState();
|
|
150
|
+
const zoom = viewState.zoom || 0;
|
|
151
|
+
const roundedZoom = zoom !== undefined ? Math.round(zoom) : -1;
|
|
152
|
+
const timeStep = this.getRenderTimeIntervalByZoom(roundedZoom) || 25;
|
|
153
|
+
const nextTick = Math.max(25, timeStep / (this.speed || 1));
|
|
154
|
+
const nextThrottleTick = Math.min(nextTick, 500);
|
|
155
|
+
// TODO: see if this should go elsewhere.
|
|
156
|
+
if (this.useThrottle) {
|
|
157
|
+
this.throttleRenderTrajectories = throttle(this.renderTrajectoriesInternal, nextThrottleTick, { leading: true, trailing: true });
|
|
158
|
+
}
|
|
159
|
+
else if (this.useDebounce) {
|
|
160
|
+
this.debounceRenderTrajectories = debounce(this.renderTrajectoriesInternal, nextThrottleTick, { leading: true, maxWait: 5000, trailing: true });
|
|
161
|
+
}
|
|
162
|
+
if ((_a = this.api) === null || _a === void 0 ? void 0 : _a.buffer) {
|
|
163
|
+
const [, size] = this.api.buffer;
|
|
164
|
+
this.api.buffer = [nextThrottleTick, size];
|
|
165
|
+
}
|
|
166
|
+
return nextTick;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get vehicle.
|
|
170
|
+
* @param {function} filterFc A function use to filter results.
|
|
171
|
+
* @return {Array<Object>} Array of vehicle.
|
|
172
|
+
*/
|
|
173
|
+
getVehicles(filterFc) {
|
|
174
|
+
return ((this.trajectories &&
|
|
175
|
+
// @ts-expect-error good type must be defined
|
|
176
|
+
Object.values(this.trajectories).filter(filterFc)) ||
|
|
177
|
+
[]);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Request feature information for a given coordinate.
|
|
181
|
+
*
|
|
182
|
+
* @param {ol/coordinate~Coordinate} coordinate Coordinate.
|
|
183
|
+
* @param {Object} options Options See child classes to see which options are supported.
|
|
184
|
+
* @param {number} [options.resolution=1] The resolution of the map.
|
|
185
|
+
* @param {number} [options.nb=Infinity] The max number of vehicles to return.
|
|
186
|
+
* @return {Promise<FeatureInfo>} Promise with features, layer and coordinate.
|
|
187
|
+
*/
|
|
188
|
+
getVehiclesAtCoordinate(coordinate, options) {
|
|
189
|
+
const { resolution } = this.getViewState();
|
|
190
|
+
const { hitTolerance, nb } = options || {};
|
|
191
|
+
const ext = buffer([...coordinate, ...coordinate], (hitTolerance || 5) * (resolution || 1));
|
|
192
|
+
let trajectories = Object.values(this.trajectories || {});
|
|
193
|
+
if (this.sort) {
|
|
194
|
+
// @ts-expect-error good type must be defined
|
|
195
|
+
trajectories = trajectories.sort(this.sort);
|
|
196
|
+
}
|
|
197
|
+
const vehicles = [];
|
|
198
|
+
for (let i = 0; i < trajectories.length; i += 1) {
|
|
199
|
+
const { coordinate: trajcoord } = trajectories[i].properties;
|
|
200
|
+
if (trajcoord && containsCoordinate(ext, trajcoord)) {
|
|
201
|
+
vehicles.push(trajectories[i]);
|
|
202
|
+
}
|
|
203
|
+
if (vehicles.length === nb) {
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return { features: vehicles, type: 'FeatureCollection' };
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Callback on websocket's deleted_vehicles channel events.
|
|
211
|
+
* It removes the trajectory from the list.
|
|
212
|
+
*
|
|
213
|
+
* @private
|
|
214
|
+
* @override
|
|
215
|
+
*/
|
|
216
|
+
onDeleteTrajectoryMessage(data) {
|
|
217
|
+
if (!data.content) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
this.removeTrajectory(data.content);
|
|
221
|
+
}
|
|
222
|
+
onDocumentVisibilityChange() {
|
|
223
|
+
if (document.hidden) {
|
|
224
|
+
this.stop();
|
|
225
|
+
// Since we don't receive deleted_vehicles event when docuement
|
|
226
|
+
// is hidden. We have to clean all the trajectories for a fresh
|
|
227
|
+
// start when the document is visible again.
|
|
228
|
+
this.trajectories = {};
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
const viewState = this.getViewState();
|
|
232
|
+
if (!viewState.visible) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
this.start();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Callback on websocket's trajectory channel events.
|
|
240
|
+
* It adds a trajectory to the list.
|
|
241
|
+
*
|
|
242
|
+
* @private
|
|
243
|
+
*/
|
|
244
|
+
onTrajectoryMessage(data) {
|
|
245
|
+
if (!data.content) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const trajectory = data.content;
|
|
249
|
+
const { geometry, properties: { raw_coordinates: rawCoordinates, time_since_update: timeSinceUpdate, }, } = trajectory;
|
|
250
|
+
// ignore old events [SBAHNM-97]
|
|
251
|
+
// @ts-expect-error can be undefined
|
|
252
|
+
if (timeSinceUpdate < 0) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
// console.time(`onTrajectoryMessage${data.content.properties.train_id}`);
|
|
256
|
+
if (this.purgeTrajectory(trajectory)) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
if (this.debug &&
|
|
260
|
+
this.mode === RealtimeModes.TOPOGRAPHIC &&
|
|
261
|
+
rawCoordinates) {
|
|
262
|
+
// @ts-expect-error missing type definition
|
|
263
|
+
trajectory.properties.olGeometry = this.format.readGeometry({
|
|
264
|
+
coordinates: fromLonLat(rawCoordinates),
|
|
265
|
+
type: 'Point',
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
// @ts-expect-error missing type definition
|
|
270
|
+
trajectory.properties.olGeometry = this.format.readGeometry(geometry);
|
|
271
|
+
}
|
|
272
|
+
// TODO Make sure the timeOffset is useful. May be we can remove it.
|
|
273
|
+
// @ts-expect-error missing type definition
|
|
274
|
+
trajectory.properties.timeOffset = Date.now() - data.timestamp;
|
|
275
|
+
this.addTrajectory(trajectory);
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* On zoomend we adjust the time interval of the update of vehicles positions.
|
|
279
|
+
*
|
|
280
|
+
* @private
|
|
281
|
+
*/
|
|
282
|
+
onZoomEnd() {
|
|
283
|
+
this.startUpdateTime();
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Remove all trajectories that are in the past.
|
|
287
|
+
*/
|
|
288
|
+
purgeOutOfDateTrajectories() {
|
|
289
|
+
Object.entries(this.trajectories || {}).forEach(([key, trajectory]) => {
|
|
290
|
+
var _a;
|
|
291
|
+
const timeIntervals = (_a = trajectory === null || trajectory === void 0 ? void 0 : trajectory.properties) === null || _a === void 0 ? void 0 : _a.time_intervals;
|
|
292
|
+
if (this.time && (timeIntervals === null || timeIntervals === void 0 ? void 0 : timeIntervals.length)) {
|
|
293
|
+
const lastTimeInterval = timeIntervals[timeIntervals.length - 1][0];
|
|
294
|
+
if (lastTimeInterval < this.time.getTime()) {
|
|
295
|
+
this.removeTrajectory(key);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Determine if the trajectory is useless and should be removed from the list or not.
|
|
302
|
+
* By default, this function exclude vehicles:
|
|
303
|
+
* - that have their trajectory outside the current extent and
|
|
304
|
+
* - that aren't in the MOT list.
|
|
305
|
+
*
|
|
306
|
+
* @param {RealtimeTrajectory} trajectory
|
|
307
|
+
* @return {boolean} if the trajectory must be displayed or not.
|
|
308
|
+
* @private
|
|
309
|
+
*/
|
|
310
|
+
purgeTrajectory(trajectory) {
|
|
311
|
+
const viewState = this.getViewState();
|
|
312
|
+
const extent = viewState.extent;
|
|
313
|
+
const { bounds, type } = trajectory.properties;
|
|
314
|
+
if ((this.isUpdateBboxOnMoveEnd && extent && !intersects(extent, bounds)) ||
|
|
315
|
+
(this.mots && !this.mots.includes(type))) {
|
|
316
|
+
this.removeTrajectory(trajectory);
|
|
317
|
+
return true;
|
|
318
|
+
}
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
removeTrajectory(trajectoryOrId) {
|
|
322
|
+
var _a;
|
|
323
|
+
let id;
|
|
324
|
+
if (typeof trajectoryOrId !== 'string') {
|
|
325
|
+
id = (_a = trajectoryOrId === null || trajectoryOrId === void 0 ? void 0 : trajectoryOrId.properties) === null || _a === void 0 ? void 0 : _a.train_id;
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
id = trajectoryOrId;
|
|
329
|
+
}
|
|
330
|
+
if (id !== undefined && this.trajectories) {
|
|
331
|
+
delete this.trajectories[id];
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Render the trajectories requesting an animation frame and cancelling the previous one.
|
|
336
|
+
* This function must be overrided by children to provide the correct parameters.
|
|
337
|
+
*
|
|
338
|
+
* @param {boolean} noInterpolate If true trajectories are not interpolated but
|
|
339
|
+
* drawn at the last known coordinate. Use this for performance optimization
|
|
340
|
+
* during map navigation.
|
|
341
|
+
* @private
|
|
342
|
+
*/
|
|
343
|
+
renderTrajectories(noInterpolate) {
|
|
344
|
+
const viewState = this.getViewState();
|
|
345
|
+
if (this.requestId) {
|
|
346
|
+
cancelAnimationFrame(this.requestId);
|
|
347
|
+
this.requestId = undefined;
|
|
348
|
+
}
|
|
349
|
+
if (!(viewState === null || viewState === void 0 ? void 0 : viewState.center) || !(viewState === null || viewState === void 0 ? void 0 : viewState.extent) || !(viewState === null || viewState === void 0 ? void 0 : viewState.size)) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
if (!noInterpolate && this.useRequestAnimationFrame) {
|
|
353
|
+
this.requestId = requestAnimationFrame(() => {
|
|
354
|
+
this.renderTrajectoriesInternal(viewState, noInterpolate);
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
else if (!noInterpolate && this.useDebounce) {
|
|
358
|
+
this.debounceRenderTrajectories(viewState, noInterpolate);
|
|
359
|
+
}
|
|
360
|
+
else if (!noInterpolate && this.useThrottle) {
|
|
361
|
+
this.throttleRenderTrajectories(viewState, noInterpolate);
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
this.renderTrajectoriesInternal(viewState, noInterpolate);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Launch renderTrajectories. it avoids duplicating code in renderTrajectories method.
|
|
369
|
+
*
|
|
370
|
+
* @param {object} viewState The view state of the map.
|
|
371
|
+
* @param {number[2]} viewState.center Center coordinate of the map in mercator coordinate.
|
|
372
|
+
* @param {number[4]} viewState.extent Extent of the map in mercator coordinates.
|
|
373
|
+
* @param {number[2]} viewState.size Size ([width, height]) of the canvas to render.
|
|
374
|
+
* @param {number} [viewState.rotation = 0] Rotation of the map to render.
|
|
375
|
+
* @param {number} viewState.resolution Resolution of the map to render.
|
|
376
|
+
* @param {boolean} noInterpolate If true trajectories are not interpolated but
|
|
377
|
+
* drawn at the last known coordinate. Use this for performance optimization
|
|
378
|
+
* during map navigation.
|
|
379
|
+
* @private
|
|
380
|
+
*/
|
|
381
|
+
renderTrajectoriesInternal(viewState, noInterpolate = false) {
|
|
382
|
+
var _a, _b;
|
|
383
|
+
if (!this.trajectories || !this.shouldRender()) {
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
const time = this.live ? Date.now() : (_a = this.time) === null || _a === void 0 ? void 0 : _a.getTime();
|
|
387
|
+
const trajectories = Object.values(this.trajectories);
|
|
388
|
+
// console.time('sort');
|
|
389
|
+
if (this.sort) {
|
|
390
|
+
// @ts-expect-error type problem
|
|
391
|
+
trajectories.sort(this.sort);
|
|
392
|
+
}
|
|
393
|
+
// console.timeEnd('sort');
|
|
394
|
+
if (!this.canvas || !this.style) {
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
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
|
|
398
|
+
? true
|
|
399
|
+
: noInterpolate, selectedVehicleId: this.selectedVehicleId }, this.styleOptions));
|
|
400
|
+
(_b = this.onRender) === null || _b === void 0 ? void 0 : _b.call(this, this.renderState, viewState);
|
|
401
|
+
// console.timeEnd('render');
|
|
402
|
+
return true;
|
|
403
|
+
}
|
|
404
|
+
setBbox() {
|
|
405
|
+
const viewState = this.getViewState();
|
|
406
|
+
const extent = viewState.extent;
|
|
407
|
+
const zoom = viewState.zoom || 0;
|
|
408
|
+
if (!extent || Number.isNaN(zoom)) {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
// Clean trajectories before sending the new bbox
|
|
412
|
+
// Purge trajectories:
|
|
413
|
+
// - which are outside the extent
|
|
414
|
+
// - when it's bus and zoom level is too low for them
|
|
415
|
+
if (this.trajectories && extent && zoom) {
|
|
416
|
+
const keys = Object.keys(this.trajectories);
|
|
417
|
+
for (let i = keys.length - 1; i >= 0; i -= 1) {
|
|
418
|
+
this.purgeTrajectory(this.trajectories[keys[i]]);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
// The backend only supports non float value
|
|
422
|
+
const zoomFloor = Math.floor(zoom);
|
|
423
|
+
if (!extent || Number.isNaN(zoomFloor)) {
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
// The extent does not need to be precise under meter, so we round floor/ceil the values.
|
|
427
|
+
const [minX, minY, maxX, maxY] = extent;
|
|
428
|
+
const bbox = [
|
|
429
|
+
Math.floor(minX),
|
|
430
|
+
Math.floor(minY),
|
|
431
|
+
Math.ceil(maxX),
|
|
432
|
+
Math.ceil(maxY),
|
|
433
|
+
zoomFloor,
|
|
434
|
+
];
|
|
435
|
+
/* @private */
|
|
436
|
+
this.generalizationLevel = this.getGeneralizationLevelByZoom(zoomFloor);
|
|
437
|
+
if (this.generalizationLevel) {
|
|
438
|
+
bbox.push(`gen=${this.generalizationLevel}`);
|
|
439
|
+
}
|
|
440
|
+
/* @private */
|
|
441
|
+
this.mots = this.getMotsByZoom(zoomFloor);
|
|
442
|
+
if (this.mots) {
|
|
443
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
444
|
+
bbox.push(`mots=${this.mots}`);
|
|
445
|
+
}
|
|
446
|
+
if (this.tenant) {
|
|
447
|
+
bbox.push(`tenant=${this.tenant}`);
|
|
448
|
+
}
|
|
449
|
+
if (this.mode !== 'topographic') {
|
|
450
|
+
bbox.push(`channel_prefix=${this.mode}`);
|
|
451
|
+
}
|
|
452
|
+
if (this.bboxParameters) {
|
|
453
|
+
Object.entries(this.bboxParameters).forEach(([key, value]) => {
|
|
454
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
455
|
+
bbox.push(`${key}=${value}`);
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
// Extent and zoom level are mandatory.
|
|
459
|
+
this.api.bbox = bbox;
|
|
460
|
+
}
|
|
461
|
+
start() {
|
|
462
|
+
this.stop();
|
|
463
|
+
// Before starting to update trajectories, we remove trajectories that have
|
|
464
|
+
// a time_intervals in the past, it will
|
|
465
|
+
// avoid phantom train that are at the end of their route because we never
|
|
466
|
+
// received the deleted_vehicle event because we have changed the browser tab.
|
|
467
|
+
this.purgeOutOfDateTrajectories();
|
|
468
|
+
this.renderTrajectories();
|
|
469
|
+
this.startUpdateTime();
|
|
470
|
+
this.api.open();
|
|
471
|
+
this.api.subscribeTrajectory(this.mode, this.onTrajectoryMessage, undefined, this.isUpdateBboxOnMoveEnd);
|
|
472
|
+
this.api.subscribeDeletedVehicles(this.mode, this.onDeleteTrajectoryMessage, undefined, this.isUpdateBboxOnMoveEnd);
|
|
473
|
+
// Update the bbox on each move end
|
|
474
|
+
if (this.isUpdateBboxOnMoveEnd) {
|
|
475
|
+
this.setBbox();
|
|
476
|
+
}
|
|
477
|
+
if (this.onStart) {
|
|
478
|
+
this.onStart(this);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Start the clock.
|
|
483
|
+
* @private
|
|
484
|
+
*/
|
|
485
|
+
startUpdateTime() {
|
|
486
|
+
this.stopUpdateTime();
|
|
487
|
+
this.updateTimeDelay = this.getRefreshTimeInMs() || 0;
|
|
488
|
+
this.updateTimeInterval = window.setInterval(() => {
|
|
489
|
+
// When live=true, we update the time with new Date();
|
|
490
|
+
if (this.live) {
|
|
491
|
+
this.time = new Date();
|
|
492
|
+
}
|
|
493
|
+
else if (this.time && this.updateTimeDelay && this.speed) {
|
|
494
|
+
this.time = new Date(this.time.getTime() + this.updateTimeDelay * this.speed);
|
|
495
|
+
}
|
|
496
|
+
}, this.updateTimeDelay);
|
|
497
|
+
}
|
|
498
|
+
stop() {
|
|
499
|
+
this.api.unsubscribeTrajectory(this.onTrajectoryMessage);
|
|
500
|
+
this.api.unsubscribeDeletedVehicles(this.onDeleteTrajectoryMessage);
|
|
501
|
+
this.api.close();
|
|
502
|
+
if (this.onStop) {
|
|
503
|
+
this.onStop(this);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Stop the clock.
|
|
508
|
+
* @private
|
|
509
|
+
*/
|
|
510
|
+
stopUpdateTime() {
|
|
511
|
+
if (this.updateTimeInterval) {
|
|
512
|
+
clearInterval(this.updateTimeInterval);
|
|
513
|
+
this.updateTimeInterval = undefined;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
get mode() {
|
|
517
|
+
return this._mode;
|
|
518
|
+
}
|
|
519
|
+
set mode(newMode) {
|
|
520
|
+
var _a, _b;
|
|
521
|
+
if (newMode === this._mode) {
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
this._mode = newMode;
|
|
525
|
+
if ((_b = (_a = this.api) === null || _a === void 0 ? void 0 : _a.wsApi) === null || _b === void 0 ? void 0 : _b.open) {
|
|
526
|
+
this.stop();
|
|
527
|
+
this.start();
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
get speed() {
|
|
531
|
+
return this._speed;
|
|
532
|
+
}
|
|
533
|
+
set speed(newSpeed) {
|
|
534
|
+
this._speed = newSpeed;
|
|
535
|
+
this.start();
|
|
536
|
+
}
|
|
537
|
+
get style() {
|
|
538
|
+
return this._style;
|
|
539
|
+
}
|
|
540
|
+
set style(newStyle) {
|
|
541
|
+
this._style = newStyle;
|
|
542
|
+
this.renderTrajectories();
|
|
543
|
+
}
|
|
544
|
+
get time() {
|
|
545
|
+
return this._time;
|
|
546
|
+
}
|
|
547
|
+
set time(newTime) {
|
|
548
|
+
this._time = (newTime === null || newTime === void 0 ? void 0 : newTime.getTime)
|
|
549
|
+
? newTime
|
|
550
|
+
: new Date(newTime);
|
|
551
|
+
this.renderTrajectories();
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
export default RealtimeEngine;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { RealtimeDeparture } from '../../types';
|
|
2
|
+
/**
|
|
3
|
+
* Compare two given departures for sort alogithm,
|
|
4
|
+
* @param {RealtimeDeparture} a First departure.
|
|
5
|
+
* @param {RealtimeDeparture} b Second departure.
|
|
6
|
+
* @param {boolean} [sortByMinArrivalTime=false] Sort departures by arrival time.
|
|
7
|
+
* @private
|
|
8
|
+
*/
|
|
9
|
+
declare const compareDepartures: (a: RealtimeDeparture, b: RealtimeDeparture, sortByMinArrivalTime?: boolean) => number;
|
|
10
|
+
export default compareDepartures;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Compare two given departures for sort alogithm,
|
|
3
|
-
* @param {
|
|
4
|
-
* @param {
|
|
3
|
+
* @param {RealtimeDeparture} a First departure.
|
|
4
|
+
* @param {RealtimeDeparture} b Second departure.
|
|
5
5
|
* @param {boolean} [sortByMinArrivalTime=false] Sort departures by arrival time.
|
|
6
6
|
* @private
|
|
7
7
|
*/
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AnyCanvas } from '../../types';
|
|
2
|
+
/**
|
|
3
|
+
* This function try to create a canvas element and return it.
|
|
4
|
+
* it uses document.createElement('canvas') if document is available
|
|
5
|
+
* or new OffscreenCanvas(width, height) if OffscreenCanvas is avalaible (for web worker)
|
|
6
|
+
* or it returns null if neither is available.
|
|
7
|
+
* @private
|
|
8
|
+
*/
|
|
9
|
+
declare const createCanvas: (width: number, height: number) => AnyCanvas | null;
|
|
10
|
+
export default createCanvas;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { RealtimeTrajectory } from '../../types';
|
|
2
|
+
/**
|
|
3
|
+
* Return a filter functions based on some parameters of a vehicle.
|
|
4
|
+
*
|
|
5
|
+
* @param {string|Array<string>} line - A list of vehicle's name to filter. Names can be separated by a comma. Ex: 'S1,S2,S3'
|
|
6
|
+
* @param {string|Array<string} route - A list of vehicle's route (contained in route_identifier property) to filter. Indentifiers can be separated by a comma. Ex: 'id1,id2,id3'
|
|
7
|
+
* @param {string|Array<string} operator A list of vehicle's operator to filter. Operators can be separated by a comma. Ex: 'SBB,DB'
|
|
8
|
+
* @param {Regexp} regexLine - A regex aplly of vehcile's name.
|
|
9
|
+
* @private
|
|
10
|
+
*/
|
|
11
|
+
declare const createRealtimeFilters: (line: string | string[], route: string | string[], operator: string | string[], regexLine: string | string[]) => ((trajectory: RealtimeTrajectory) => boolean) | null;
|
|
12
|
+
export default createRealtimeFilters;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RealtimeDeparture, RealtimeDepartureExtended } from '../../types';
|
|
2
|
+
import type { WebSocketAPIMessageCallback } from '../../api/WebSocketAPI';
|
|
3
|
+
/**
|
|
4
|
+
* This function returns a WebSocket api callback, and call the onDeparturesUpdate function with the list of current departures to display.
|
|
5
|
+
* @param {function(departures: RealtimeDeparture[])} onDeparturesUpdate callback when list of departures changes, called after 100 ms
|
|
6
|
+
* @param {boolean} [sortByMinArrivalTime = true] Sort departures by arrival time
|
|
7
|
+
* @param {number} [maxDepartureAge = 30] max departure age of departures in minutes
|
|
8
|
+
* @param {number} [timeout = 100] debounce timeout in ms
|
|
9
|
+
* @private
|
|
10
|
+
*/
|
|
11
|
+
declare const debounceDeparturesMessages: (onDeparturesUpdate: (departures: RealtimeDepartureExtended[]) => {}, sortByMinArrivalTime?: boolean, maxDepartureAge?: number, timeout?: number) => WebSocketAPIMessageCallback<RealtimeDeparture>;
|
|
12
|
+
export default debounceDeparturesMessages;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { WebSocketAPIMessageCallback } from '../../api/WebSocketAPI';
|
|
2
|
+
/**
|
|
3
|
+
* This function returns a WebSocket api callback, and call the onUpdate function with the list of of subscribed objects changes.
|
|
4
|
+
*
|
|
5
|
+
* @param {function(objects: any[])} onUpdate callback when list of subscribed objects changes, called after 100 ms
|
|
6
|
+
* @param {function(object: any)} [getObjectId = true] function returning the id of an object
|
|
7
|
+
* @param {number} [timeout = 100] debounce timeout in ms
|
|
8
|
+
* @private
|
|
9
|
+
*/
|
|
10
|
+
declare const debounceWebsocketMessages: (onUpdate: (objects: any[]) => void, getObjectId?: (object: any) => string, timeout?: number) => WebSocketAPIMessageCallback<any>;
|
|
11
|
+
export default debounceWebsocketMessages;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface Source {
|
|
2
|
+
attribution: string;
|
|
3
|
+
options: {
|
|
4
|
+
attribution: string;
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
export interface SourceCache {
|
|
8
|
+
getSource: () => Source;
|
|
9
|
+
used: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Return the copyright a Maplibre map.
|
|
13
|
+
* @param {maplibregl.Map} map A Maplibre map
|
|
14
|
+
* @private
|
|
15
|
+
*/
|
|
16
|
+
declare const getMapGlCopyrights: (map: maplibregl.Map) => any[];
|
|
17
|
+
export default getMapGlCopyrights;
|