mobility-toolbox-js 1.7.6 → 1.7.8-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/common/Tracker.js CHANGED
@@ -1,5 +1,6 @@
1
- import GeomType from 'ol/geom/GeometryType';
1
+ /* eslint-disable no-param-reassign */
2
2
  import { compose, apply, create } from 'ol/transform';
3
+ import getVehiclePosition from './utils/getVehiclePosition';
3
4
 
4
5
  /**
5
6
  * Tracker. This class stores and allows to draw trajectories on a canvas.
@@ -12,142 +13,32 @@ export default class Tracker {
12
13
  * @private
13
14
  */
14
15
  constructor(options) {
15
- const opts = {
16
- interpolate: true,
17
- ...options,
18
- };
19
-
20
- /**
21
- * Pixel ratio to use to draw the canvas. Default to window.devicePixelRatio
22
- * @type {Array<trajectory>}
23
- */
24
- this.pixelRatio = options.pixelRatio || window.devicePixelRatio || 1;
25
-
26
- /**
27
- * Array of trajectories.
28
- * @type {Array<trajectory>}
29
- */
30
- this.trajectories = [];
31
-
32
- /**
33
- * Array of trajectories that are currently drawn.
34
- * @type {Array<key>}
35
- */
36
- this.renderedTrajectories = [];
37
-
38
- /**
39
- * Active interpolation calculation or not. If false, the train will not move until we receive the next message for the websocket.
40
- * @type {boolean}
41
- */
42
- this.interpolate = !!opts.interpolate;
43
-
44
- /**
45
- * Id of the trajectory which is hovered.
46
- * @type {string}
47
- */
48
- this.hoverVehicleId = opts.hoverVehicleId;
49
-
50
- /**
51
- * Id of the trajectory which is selected.
52
- * @type {string}
53
- */
54
- this.selectedVehicleId = opts.selectedVehicleId;
55
-
56
- /**
57
- * Function use to filter the features displayed.
58
- * @type {function}
59
- */
60
- this.filter = opts.filter;
61
-
62
- /**
63
- * Function use to sort the features displayed.
64
- * @type {function}
65
- */
66
- this.sort = opts.sort;
67
-
68
16
  /**
69
17
  * Function use to style the features displayed.
70
18
  * @type {function}
71
19
  */
72
- this.style = opts.style;
20
+ this.style = options.style;
73
21
 
74
22
  // we draw directly on the canvas since openlayers is too slow.
75
23
  /**
76
24
  * HTML <canvas> element.
77
25
  * @type {Canvas}
78
26
  */
79
- this.canvas = opts.canvas || document.createElement('canvas');
80
- this.canvas.width = opts.width * this.pixelRatio;
81
- this.canvas.height = opts.height * this.pixelRatio;
82
- this.canvas.setAttribute(
83
- 'style',
84
- [
85
- 'position: absolute',
86
- 'top: 0',
87
- 'bottom: 0',
88
- `width: ${opts.width}px`,
89
- `height: ${opts.height}px`,
90
- 'pointer-events: none',
91
- 'visibility: visible',
92
- 'margin-top: inherit', // for scrolling behavior.
93
- ].join(';'),
94
- );
95
- /**
96
- * 2d drawing context on the canvas.
97
- * @type {CanvasRenderingContext2D}
98
- */
99
- this.canvasContext = this.canvas.getContext('2d');
100
- }
101
-
102
- /**
103
- * Set visibility of the canvas.
104
- * @param {boolean} visible The visibility of the layer
105
- */
106
- setVisible(visible) {
107
- if (this.canvas) {
108
- this.canvas.style.visibility = visible ? 'visible' : 'hidden';
109
- }
110
- }
111
-
112
- /**
113
- * Define the trajectories.
114
- * @param {array<ol/Feature~Feature>} trajectories
115
- */
116
- setTrajectories(trajectories = []) {
117
- if (this.sort) {
118
- trajectories.sort(this.sort);
119
- }
120
-
121
- this.trajectories = trajectories;
122
- }
123
-
124
- /**
125
- * Return the trajectories.
126
- * @return {array<trajectory>} trajectories
127
- */
128
- getTrajectories() {
129
- return this.trajectories || [];
130
- }
131
-
132
- /**
133
- * Clear the canvas.
134
- * @private
135
- */
136
- clear() {
137
- if (this.canvasContext) {
138
- this.canvasContext.clearRect(0, 0, this.canvas.width, this.canvas.height);
139
- }
27
+ this.canvas = options.canvas || document.createElement('canvas');
140
28
  }
141
29
 
142
30
  /**
143
31
  * Draw all the trajectories available to the canvas.
32
+ * @param {ViewState} trajectories An array of trajectories.
144
33
  * @param {ViewState} viewState The view state of the map.
145
- * @param {boolean} noInterpolate If true trajectories are not interpolated but
34
+ * @param {boolean} options.hoverVehicleId The id of the vehicle to highlight.
35
+ * @param {boolean} options.selectedVehicleId The id of the vehicle to select.
36
+ * @param {boolean} options.noInterpolate If true trajectories are not interpolated but
146
37
  * drawn at the last known coordinate. Use this for performance optimization
147
38
  * during map navigation.
148
39
  * @private
149
40
  */
150
- renderTrajectories(viewState, noInterpolate = false) {
41
+ renderTrajectories(trajectories, viewState, options) {
151
42
  const {
152
43
  time = Date.now(),
153
44
  size = [],
@@ -156,18 +47,23 @@ export default class Tracker {
156
47
  rotation = 0,
157
48
  pixelRatio,
158
49
  } = viewState;
159
- this.clear();
50
+ const {
51
+ noInterpolate = false,
52
+ hoverVehicleId,
53
+ selectedVehicleId,
54
+ } = options;
55
+
56
+ const { canvas } = this;
57
+ const context = canvas.getContext('2d');
58
+ context.clearRect(0, 0, canvas.width, canvas.height);
160
59
 
161
60
  const [width, height] = size;
162
61
  if (
163
62
  width &&
164
63
  height &&
165
- (this.canvas.width !== width || this.canvas.height !== height)
64
+ (canvas.width !== width || canvas.height !== height)
166
65
  ) {
167
- [this.canvas.width, this.canvas.height] = [
168
- width * pixelRatio,
169
- height * pixelRatio,
170
- ];
66
+ [canvas.width, canvas.height] = [width * pixelRatio, height * pixelRatio];
171
67
  }
172
68
 
173
69
  const coordinateToPixelTransform = compose(
@@ -181,14 +77,12 @@ export default class Tracker {
181
77
  -center[1],
182
78
  );
183
79
 
184
- this.canvas.style.width = `${this.canvas.width / pixelRatio}px`;
185
- this.canvas.style.height = `${this.canvas.height / pixelRatio}px`;
80
+ // Offscreen canvas has not style attribute
81
+ if (canvas.style) {
82
+ canvas.style.width = `${canvas.width / pixelRatio}px`;
83
+ canvas.style.height = `${canvas.height / pixelRatio}px`;
84
+ }
186
85
 
187
- /**
188
- * Current resolution.
189
- * @type {number}
190
- */
191
- this.currResolution = resolution || this.currResolution;
192
86
  let hoverVehicleImg;
193
87
  let hoverVehiclePx;
194
88
  let hoverVehicleWidth;
@@ -197,157 +91,89 @@ export default class Tracker {
197
91
  let selectedVehiclePx;
198
92
  let selectedVehicleWidth;
199
93
  let selectedVehicleHeight;
94
+ let nbRendered = 0;
200
95
 
201
- this.renderedTrajectories = [];
96
+ for (let i = trajectories.length - 1; i >= 0; i -= 1) {
97
+ const trajectory = trajectories[i];
202
98
 
203
- for (let i = (this.trajectories || []).length - 1; i >= 0; i -= 1) {
204
- const traj = this.trajectories[i];
99
+ // We simplify the trajectory object
100
+ const { train_id: id, timeOffset } = trajectory.properties;
205
101
 
206
- this.trajectories[i].rendered = false;
102
+ // We set the rotation and the timeFraction of the trajectory (used by tralis).
103
+ // if rotation === null that seems there is no rotation available.
104
+ const { coord, rotation: rotationIcon } = getVehiclePosition(
105
+ time - (timeOffset || 0),
106
+ trajectory,
107
+ noInterpolate,
108
+ );
207
109
 
208
- // We simplify the traj object
209
- const { geometry, timeIntervals, timeOffset } = traj;
110
+ // We store the current vehicle position to the trajectory.
111
+ trajectories[i].properties.coordinate = coord;
112
+ trajectories[i].properties.rotation = rotationIcon;
210
113
 
211
- if (this.filter && !this.filter(traj, i, this.trajectories)) {
114
+ if (!coord) {
212
115
  // eslint-disable-next-line no-continue
213
116
  continue;
214
117
  }
215
118
 
216
- let coord = null;
217
- let rotationIcon;
218
-
219
- if (traj.coordinate && (noInterpolate || !this.interpolate)) {
220
- coord = traj.coordinate;
221
- } else if (timeIntervals && timeIntervals.length > 1) {
222
- const now = time - (timeOffset || 0);
223
- let start;
224
- let end;
225
- let startFrac;
226
- let endFrac;
227
- let timeFrac;
228
-
229
- // Search th time interval.
230
- for (let j = 0; j < timeIntervals.length - 1; j += 1) {
231
- // Rotation only available in tralis layer.
232
- [start, startFrac, rotationIcon] = timeIntervals[j];
233
- [end, endFrac] = timeIntervals[j + 1];
234
-
235
- if (start <= now && now <= end) {
236
- break;
237
- } else {
238
- start = null;
239
- end = null;
240
- }
241
- }
242
- // The geometry can also be a Point
243
- if (geometry.getType() === GeomType.POINT) {
244
- coord = geometry.getCoordinates();
245
- } else if (geometry.getType() === GeomType.LINE_STRING) {
246
- if (start && end) {
247
- // interpolate position inside the time interval.
248
- timeFrac = this.interpolate
249
- ? Math.min((now - start) / (end - start), 1)
250
- : 0;
251
-
252
- const geomFrac = this.interpolate
253
- ? timeFrac * (endFrac - startFrac) + startFrac
254
- : 0;
255
-
256
- coord = geometry.getCoordinateAt(geomFrac);
257
-
258
- // We set the rotation and the timeFraction of the trajectory (used by tralis).
259
- this.trajectories[i].rotation = rotationIcon;
260
- this.trajectories[i].endFraction = timeFrac;
261
-
262
- // It happens that the now date was some ms before the first timeIntervals we have.
263
- } else if (now < timeIntervals[0][0]) {
264
- [[, , rotationIcon]] = timeIntervals;
265
- timeFrac = 0;
266
- coord = geometry.getFirstCoordinate();
267
- } else if (now > timeIntervals[timeIntervals.length - 1][0]) {
268
- [, , rotationIcon] = timeIntervals[timeIntervals.length - 1];
269
- timeFrac = 1;
270
- coord = geometry.getLastCoordinate();
271
- }
272
- } else {
273
- // eslint-disable-next-line no-console
274
- console.error(
275
- 'This geometry type is not supported. Only Point or LineString are. Current geometry: ',
276
- geometry,
277
- );
278
- }
279
- // We set the rotation and the timeFraction of the trajectory (used by tralis).
280
- // if rotation === null that seems there is no rotation available.
281
- this.trajectories[i].rotation = rotationIcon;
282
- this.trajectories[i].endFraction = timeFrac || 0;
119
+ let px = apply(coordinateToPixelTransform, [...coord]);
120
+ if (!px) {
121
+ // eslint-disable-next-line no-continue
122
+ continue;
283
123
  }
284
124
 
285
- if (coord) {
286
- // We set the rotation of the trajectory (used by tralis).
287
- this.trajectories[i].coordinate = coord;
288
- let px = apply(coordinateToPixelTransform, [...coord]);
289
- if (!px) {
290
- // eslint-disable-next-line no-continue
291
- continue;
292
- }
125
+ px = px.map((p) => p * pixelRatio);
293
126
 
294
- px = px.map((p) => p * pixelRatio);
127
+ if (
128
+ px[0] < 0 ||
129
+ px[0] > canvas.width ||
130
+ px[1] < 0 ||
131
+ px[1] > canvas.height
132
+ ) {
133
+ // eslint-disable-next-line no-continue
134
+ continue;
135
+ }
295
136
 
296
- if (
297
- px[0] < 0 ||
298
- px[0] > this.canvas.width ||
299
- px[1] < 0 ||
300
- px[1] > this.canvas.height
301
- ) {
302
- // eslint-disable-next-line no-continue
303
- continue;
304
- }
137
+ const vehicleImg = this.style(trajectory, viewState, options);
138
+ if (!vehicleImg) {
139
+ // eslint-disable-next-line no-continue
140
+ continue;
141
+ }
305
142
 
306
- const vehicleImg = this.style(traj, viewState);
307
- if (!vehicleImg) {
308
- // eslint-disable-next-line no-continue
309
- continue;
310
- }
143
+ nbRendered += 1;
311
144
 
312
- // Trajectory with pixel (i.e. within map extent) will be in renderedTrajectories.
313
- this.trajectories[i].rendered = true;
314
- this.renderedTrajectories.push(this.trajectories[i]);
145
+ const imgWidth = vehicleImg.width;
146
+ const imgHeight = vehicleImg.height;
315
147
 
316
- const imgWidth = vehicleImg.width;
317
- const imgHeight = vehicleImg.height;
148
+ if (hoverVehicleId !== id && selectedVehicleId !== id) {
149
+ context.drawImage(
150
+ vehicleImg,
151
+ px[0] - imgWidth / 2,
152
+ px[1] - imgHeight / 2,
153
+ imgWidth,
154
+ imgHeight,
155
+ );
156
+ }
318
157
 
319
- if (
320
- this.hoverVehicleId !== traj.id &&
321
- this.selectedVehicleId !== traj.id
322
- ) {
323
- this.canvasContext.drawImage(
324
- vehicleImg,
325
- px[0] - imgWidth / 2,
326
- px[1] - imgHeight / 2,
327
- imgWidth,
328
- imgHeight,
329
- );
330
- }
331
- if (this.hoverVehicleId === traj.id) {
332
- // Store the canvas to draw it at the end
333
- hoverVehicleImg = vehicleImg;
334
- hoverVehiclePx = px;
335
- hoverVehicleWidth = imgWidth;
336
- hoverVehicleHeight = imgHeight;
337
- }
158
+ if (hoverVehicleId && hoverVehicleId === id) {
159
+ // Store the canvas to draw it at the end
160
+ hoverVehicleImg = vehicleImg;
161
+ hoverVehiclePx = px;
162
+ hoverVehicleWidth = imgWidth;
163
+ hoverVehicleHeight = imgHeight;
164
+ }
338
165
 
339
- if (this.selectedVehicleId === traj.id) {
340
- // Store the canvas to draw it at the end
341
- selectedVehicleImg = vehicleImg;
342
- selectedVehiclePx = px;
343
- selectedVehicleWidth = imgWidth;
344
- selectedVehicleHeight = imgHeight;
345
- }
166
+ if (selectedVehicleId && selectedVehicleId === id) {
167
+ // Store the canvas to draw it at the end
168
+ selectedVehicleImg = vehicleImg;
169
+ selectedVehiclePx = px;
170
+ selectedVehicleWidth = imgWidth;
171
+ selectedVehicleHeight = imgHeight;
346
172
  }
347
173
  }
348
174
 
349
175
  if (selectedVehicleImg) {
350
- this.canvasContext.drawImage(
176
+ context.drawImage(
351
177
  selectedVehicleImg,
352
178
  selectedVehiclePx[0] - selectedVehicleWidth / 2,
353
179
  selectedVehiclePx[1] - selectedVehicleHeight / 2,
@@ -357,7 +183,7 @@ export default class Tracker {
357
183
  }
358
184
 
359
185
  if (hoverVehicleImg) {
360
- this.canvasContext.drawImage(
186
+ context.drawImage(
361
187
  hoverVehicleImg,
362
188
  hoverVehiclePx[0] - hoverVehicleWidth / 2,
363
189
  hoverVehiclePx[1] - hoverVehicleHeight / 2,
@@ -365,14 +191,8 @@ export default class Tracker {
365
191
  hoverVehicleHeight,
366
192
  );
367
193
  }
368
- }
369
-
370
- /**
371
- * Clean the canvas and the events the tracker.
372
- * @private
373
- */
374
- destroy() {
375
- this.renderedTrajectories = [];
376
- this.clear();
194
+ return {
195
+ nbTrajectoriesRendered: nbRendered,
196
+ };
377
197
  }
378
198
  }