mobility-toolbox-js 2.0.0-beta.34 → 2.0.0-beta.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/README.md +14 -8
  2. package/api/RealtimeAPI.d.ts +6 -6
  3. package/api/RealtimeAPI.d.ts.map +1 -1
  4. package/api/RealtimeAPI.js +170 -197
  5. package/api/RealtimeAPI.test.d.ts +2 -0
  6. package/api/RealtimeAPI.test.d.ts.map +1 -0
  7. package/api/RealtimeAPI.test.js +67 -0
  8. package/api/RoutingAPI.d.ts +6 -2
  9. package/api/RoutingAPI.d.ts.map +1 -1
  10. package/api/RoutingAPI.js +8 -38
  11. package/api/RoutingAPI.test.d.ts +2 -0
  12. package/api/RoutingAPI.test.d.ts.map +1 -0
  13. package/api/RoutingAPI.test.js +29 -0
  14. package/api/StopsAPI.d.ts +1 -1
  15. package/api/StopsAPI.d.ts.map +1 -1
  16. package/api/StopsAPI.js +8 -40
  17. package/api/StopsAPI.test.d.ts +2 -0
  18. package/api/StopsAPI.test.d.ts.map +1 -0
  19. package/api/StopsAPI.test.js +26 -0
  20. package/api/index.d.ts +3 -4
  21. package/api/index.d.ts.map +1 -1
  22. package/api/index.js +3 -10
  23. package/api/typedefs.js +1 -0
  24. package/common/api/HttpAPI.d.ts +2 -2
  25. package/common/api/HttpAPI.d.ts.map +1 -1
  26. package/common/api/HttpAPI.js +21 -48
  27. package/common/api/HttpAPI.test.d.ts +2 -0
  28. package/common/api/HttpAPI.test.d.ts.map +1 -0
  29. package/common/api/HttpAPI.test.js +54 -0
  30. package/common/api/WebSocketAPI.d.ts +8 -4
  31. package/common/api/WebSocketAPI.d.ts.map +1 -1
  32. package/common/api/WebSocketAPI.js +125 -129
  33. package/common/api/WebSocketAPI.test.d.ts +2 -0
  34. package/common/api/WebSocketAPI.test.d.ts.map +1 -0
  35. package/common/api/WebSocketAPI.test.js +380 -0
  36. package/common/controls/Control.d.ts +5 -5
  37. package/common/controls/Control.d.ts.map +1 -1
  38. package/common/controls/Control.js +44 -77
  39. package/common/controls/Control.test.d.ts +2 -0
  40. package/common/controls/Control.test.d.ts.map +1 -0
  41. package/common/controls/Control.test.js +89 -0
  42. package/common/index.js +2 -18
  43. package/common/layers/Layer.d.ts +11 -11
  44. package/common/layers/Layer.d.ts.map +1 -1
  45. package/common/layers/Layer.js +70 -104
  46. package/common/layers/Layer.test.d.ts +2 -0
  47. package/common/layers/Layer.test.d.ts.map +1 -0
  48. package/common/layers/Layer.test.js +137 -0
  49. package/common/mixins/CopyrightMixin.js +23 -52
  50. package/common/mixins/MapboxLayerMixin.js +162 -204
  51. package/common/mixins/RealtimeLayerMixin.js +580 -635
  52. package/common/mixins/StopFinderMixin.d.ts +3 -3
  53. package/common/mixins/StopFinderMixin.d.ts.map +1 -1
  54. package/common/mixins/StopFinderMixin.js +115 -157
  55. package/common/mixins/UserInteractionsLayerMixin.js +142 -175
  56. package/common/mixins/UserInteractionsLayerMixin.test.d.ts +2 -0
  57. package/common/mixins/UserInteractionsLayerMixin.test.d.ts.map +1 -0
  58. package/common/mixins/UserInteractionsLayerMixin.test.js +214 -0
  59. package/common/styles/index.js +4 -24
  60. package/common/styles/realtimeDefaultStyle.d.ts.map +1 -1
  61. package/common/styles/realtimeDefaultStyle.js +67 -78
  62. package/common/styles/realtimeDelayStyle.js +4 -17
  63. package/common/styles/realtimeSimpleStyle.js +5 -7
  64. package/common/typedefs.js +1 -0
  65. package/common/utils/cleanStopTime.js +3 -5
  66. package/common/utils/compareDepartures.d.ts +1 -1
  67. package/common/utils/compareDepartures.d.ts.map +1 -1
  68. package/common/utils/compareDepartures.js +8 -11
  69. package/common/utils/createCanvas.js +3 -5
  70. package/common/utils/createTrackerFilters.d.ts +1 -1
  71. package/common/utils/createTrackerFilters.d.ts.map +1 -1
  72. package/common/utils/createTrackerFilters.js +22 -32
  73. package/common/utils/createTrackerFilters.test.d.ts +2 -0
  74. package/common/utils/createTrackerFilters.test.d.ts.map +1 -0
  75. package/common/utils/createTrackerFilters.test.js +79 -0
  76. package/common/utils/getLayersAsFlatArray.js +6 -8
  77. package/common/utils/getMapboxMapCopyrights.js +9 -11
  78. package/common/utils/getMapboxMapCopyrights.test.d.ts +2 -0
  79. package/common/utils/getMapboxMapCopyrights.test.d.ts.map +1 -0
  80. package/common/utils/getMapboxMapCopyrights.test.js +40 -0
  81. package/common/utils/getMapboxRender.js +12 -15
  82. package/common/utils/getMaplibreRender.js +10 -13
  83. package/common/utils/getRealtimeModeSuffix.js +2 -6
  84. package/common/utils/getUrlWithParams.js +5 -8
  85. package/common/utils/getVehiclePosition.js +20 -23
  86. package/common/utils/index.js +12 -37
  87. package/common/utils/removeDuplicate.d.ts +1 -1
  88. package/common/utils/removeDuplicate.d.ts.map +1 -1
  89. package/common/utils/removeDuplicate.js +6 -21
  90. package/common/utils/removeDuplicate.test.d.ts +2 -0
  91. package/common/utils/removeDuplicate.test.d.ts.map +1 -0
  92. package/common/utils/removeDuplicate.test.js +19 -0
  93. package/common/utils/renderTrajectories.js +31 -43
  94. package/common/utils/sortByDelay.js +4 -6
  95. package/common/utils/timeUtils.js +14 -24
  96. package/common/utils/timeUtils.test.d.ts +2 -0
  97. package/common/utils/timeUtils.test.d.ts.map +1 -0
  98. package/common/utils/timeUtils.test.js +10 -0
  99. package/common/utils/trackerConfig.js +27 -39
  100. package/common/utils/trackerConfig.test.d.ts +2 -0
  101. package/common/utils/trackerConfig.test.d.ts.map +1 -0
  102. package/common/utils/trackerConfig.test.js +23 -0
  103. package/iife.js +3 -5
  104. package/index.d.ts +4 -0
  105. package/index.js +9 -10
  106. package/mapbox/controls/CopyrightControl.d.ts +0 -1
  107. package/mapbox/controls/CopyrightControl.d.ts.map +1 -1
  108. package/mapbox/controls/CopyrightControl.js +18 -38
  109. package/mapbox/controls/index.js +1 -5
  110. package/mapbox/index.js +4 -20
  111. package/mapbox/layers/Layer.d.ts +1 -1
  112. package/mapbox/layers/Layer.d.ts.map +1 -1
  113. package/mapbox/layers/Layer.js +29 -71
  114. package/mapbox/layers/Layer.test.d.ts +2 -0
  115. package/mapbox/layers/Layer.test.d.ts.map +1 -0
  116. package/mapbox/layers/Layer.test.js +204 -0
  117. package/mapbox/layers/RealtimeLayer.d.ts +4 -4
  118. package/mapbox/layers/RealtimeLayer.d.ts.map +1 -1
  119. package/mapbox/layers/RealtimeLayer.js +83 -125
  120. package/mapbox/layers/RealtimeLayer.test.d.ts +2 -0
  121. package/mapbox/layers/RealtimeLayer.test.d.ts.map +1 -0
  122. package/mapbox/layers/RealtimeLayer.test.js +10 -0
  123. package/mapbox/layers/index.js +2 -7
  124. package/mapbox/utils.js +19 -33
  125. package/mbt.js +59 -37
  126. package/mbt.js.map +2 -2
  127. package/mbt.min.js +10 -10
  128. package/mbt.min.js.map +2 -2
  129. package/ol/controls/CopyrightControl.js +26 -47
  130. package/ol/controls/CopyrightControl.test.d.ts +2 -0
  131. package/ol/controls/CopyrightControl.test.d.ts.map +1 -0
  132. package/ol/controls/CopyrightControl.test.js +177 -0
  133. package/ol/controls/RoutingControl.d.ts +6 -5
  134. package/ol/controls/RoutingControl.d.ts.map +1 -1
  135. package/ol/controls/RoutingControl.js +209 -270
  136. package/ol/controls/RoutingControl.test.d.ts +2 -0
  137. package/ol/controls/RoutingControl.test.d.ts.map +1 -0
  138. package/ol/controls/RoutingControl.test.js +150 -0
  139. package/ol/controls/StopFinderControl.js +9 -32
  140. package/ol/controls/StopFinderControl.test.d.ts +2 -0
  141. package/ol/controls/StopFinderControl.test.d.ts.map +1 -0
  142. package/ol/controls/StopFinderControl.test.js +49 -0
  143. package/ol/controls/index.js +3 -9
  144. package/ol/index.js +5 -21
  145. package/ol/layers/Layer.d.ts +1 -1
  146. package/ol/layers/Layer.d.ts.map +1 -1
  147. package/ol/layers/Layer.js +40 -72
  148. package/ol/layers/Layer.test.d.ts +2 -0
  149. package/ol/layers/Layer.test.d.ts.map +1 -0
  150. package/ol/layers/Layer.test.js +196 -0
  151. package/ol/layers/MapboxLayer.d.ts +7 -7
  152. package/ol/layers/MapboxLayer.d.ts.map +1 -1
  153. package/ol/layers/MapboxLayer.js +30 -66
  154. package/ol/layers/MapboxLayer.test.d.ts +2 -0
  155. package/ol/layers/MapboxLayer.test.d.ts.map +1 -0
  156. package/ol/layers/MapboxLayer.test.js +164 -0
  157. package/ol/layers/MapboxStyleLayer.d.ts +3 -3
  158. package/ol/layers/MapboxStyleLayer.d.ts.map +1 -1
  159. package/ol/layers/MapboxStyleLayer.js +92 -135
  160. package/ol/layers/MapboxStyleLayer.test.d.ts +2 -0
  161. package/ol/layers/MapboxStyleLayer.test.d.ts.map +1 -0
  162. package/ol/layers/MapboxStyleLayer.test.js +232 -0
  163. package/ol/layers/MaplibreLayer.d.ts +1 -1
  164. package/ol/layers/MaplibreLayer.d.ts.map +1 -1
  165. package/ol/layers/MaplibreLayer.js +14 -48
  166. package/ol/layers/RealtimeLayer.d.ts +2 -2
  167. package/ol/layers/RealtimeLayer.d.ts.map +1 -1
  168. package/ol/layers/RealtimeLayer.js +111 -147
  169. package/ol/layers/RealtimeLayer.test.d.ts +2 -0
  170. package/ol/layers/RealtimeLayer.test.d.ts.map +1 -0
  171. package/ol/layers/RealtimeLayer.test.js +71 -0
  172. package/ol/layers/RoutingLayer.d.ts +2 -2
  173. package/ol/layers/RoutingLayer.d.ts.map +1 -1
  174. package/ol/layers/RoutingLayer.js +29 -61
  175. package/ol/layers/RoutingLayer.test.d.ts +2 -0
  176. package/ol/layers/RoutingLayer.test.d.ts.map +1 -0
  177. package/ol/layers/RoutingLayer.test.js +39 -0
  178. package/ol/layers/VectorLayer.d.ts +1 -1
  179. package/ol/layers/VectorLayer.d.ts.map +1 -1
  180. package/ol/layers/VectorLayer.js +14 -48
  181. package/ol/layers/VectorLayer.test.d.ts +2 -0
  182. package/ol/layers/VectorLayer.test.d.ts.map +1 -0
  183. package/ol/layers/VectorLayer.test.js +87 -0
  184. package/ol/layers/WMSLayer.d.ts +1 -1
  185. package/ol/layers/WMSLayer.d.ts.map +1 -1
  186. package/ol/layers/WMSLayer.js +34 -68
  187. package/ol/layers/WMSLayer.test.d.ts +2 -0
  188. package/ol/layers/WMSLayer.test.d.ts.map +1 -0
  189. package/ol/layers/WMSLayer.test.js +66 -0
  190. package/ol/layers/index.js +8 -19
  191. package/ol/styles/fullTrajectoryDelayStyle.js +11 -13
  192. package/ol/styles/fullTrajectoryStyle.js +16 -18
  193. package/ol/styles/index.js +2 -7
  194. package/package.json +5 -2
  195. package/setupTests.js +12 -14
@@ -1,60 +1,22 @@
1
- "use strict";
2
- var __extends = (this && this.__extends) || (function () {
3
- var extendStatics = function (d, b) {
4
- extendStatics = Object.setPrototypeOf ||
5
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
- function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
- return extendStatics(d, b);
8
- };
9
- return function (d, b) {
10
- if (typeof b !== "function" && b !== null)
11
- throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12
- extendStatics(d, b);
13
- function __() { this.constructor = d; }
14
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
- };
16
- })();
17
- var __assign = (this && this.__assign) || function () {
18
- __assign = Object.assign || function(t) {
19
- for (var s, i = 1, n = arguments.length; i < n; i++) {
20
- s = arguments[i];
21
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
22
- t[p] = s[p];
23
- }
24
- return t;
25
- };
26
- return __assign.apply(this, arguments);
27
- };
28
- var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
29
- if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
30
- if (ar || !(i in from)) {
31
- if (!ar) ar = Array.prototype.slice.call(from, 0, i);
32
- ar[i] = from[i];
33
- }
34
- }
35
- return to.concat(ar || Array.prototype.slice.call(from));
36
- };
37
- Object.defineProperty(exports, "__esModule", { value: true });
38
- exports.RealtimeLayerInterface = void 0;
39
1
  /* eslint-disable no-empty-function,@typescript-eslint/no-empty-function */
40
2
  /* eslint-disable no-useless-constructor,@typescript-eslint/no-useless-constructor */
41
3
  /* eslint-disable no-unused-vars,@typescript-eslint/no-unused-vars */
42
4
  /* eslint-disable class-methods-use-this */
43
5
  /* eslint-disable max-classes-per-file */
44
- var extent_1 = require("ol/extent");
45
- var Observable_1 = require("ol/Observable");
46
- var GeoJSON_1 = require("ol/format/GeoJSON");
47
- var lodash_debounce_1 = require("lodash.debounce");
48
- var lodash_throttle_1 = require("lodash.throttle");
49
- var proj_1 = require("ol/proj");
50
- var realtimeDefaultStyle_1 = require("../styles/realtimeDefaultStyle");
51
- var api_1 = require("../../api");
52
- var renderTrajectories_1 = require("../utils/renderTrajectories");
53
- var trackerConfig = require("../utils/trackerConfig");
6
+ import { buffer, containsCoordinate, intersects } from 'ol/extent';
7
+ import { unByKey } from 'ol/Observable';
8
+ import GeoJSON from 'ol/format/GeoJSON';
9
+ import debounce from 'lodash.debounce';
10
+ import throttle from 'lodash.throttle';
11
+ import { fromLonLat } from 'ol/proj';
12
+ import realtimeDefaultStyle from '../styles/realtimeDefaultStyle';
13
+ import { RealtimeAPI, RealtimeModes } from '../../api';
14
+ import renderTrajectories from '../utils/renderTrajectories';
15
+ import * as trackerConfig from '../utils/trackerConfig';
54
16
  /**
55
17
  * RealtimeLayerInterface.
56
18
  */
57
- var RealtimeLayerInterface = /** @class */ (function () {
19
+ export class RealtimeLayerInterface {
58
20
  /*
59
21
  * Constructor
60
22
 
@@ -66,40 +28,38 @@ var RealtimeLayerInterface = /** @class */ (function () {
66
28
  * @param {number} [options.minZoomInterpolation=8] Minimal zoom when trains positions start to be interpolated.
67
29
  * @param {number} [options.minZoomNonTrain=9] Minimal zoom when non trains vehicles are allowed to be displayed.
68
30
  */
69
- function RealtimeLayerInterface(options) {
70
- if (options === void 0) { options = {}; }
71
- }
31
+ constructor(options = {}) { }
72
32
  /**
73
33
  * Initialize the layer subscribing to the Realtime api.
74
34
  *
75
35
  * @param {ol/Map~Map} map
76
36
  */
77
- RealtimeLayerInterface.prototype.attachToMap = function (map) { };
37
+ attachToMap(map) { }
78
38
  /**
79
39
  * Terminate the layer unsubscribing to the Realtime api.
80
40
  */
81
- RealtimeLayerInterface.prototype.detachFromMap = function () { };
41
+ detachFromMap() { }
82
42
  /**
83
43
  * Start the clock.
84
44
  */
85
- RealtimeLayerInterface.prototype.start = function () { };
45
+ start() { }
86
46
  /**
87
47
  * Stop the clock.
88
48
  */
89
- RealtimeLayerInterface.prototype.stop = function () { };
49
+ stop() { }
90
50
  /**
91
51
  * Set the Realtime api's bbox.
92
52
  *
93
53
  * @param {Array<number>} extent Extent to request, [minX, minY, maxX, maxY, zoom].
94
54
  * @param {number} zoom Zoom level to request. Must be an integer.
95
55
  */
96
- RealtimeLayerInterface.prototype.setBbox = function (extent, zoom) { };
56
+ setBbox(extent, zoom) { }
97
57
  /**
98
58
  * Set the Realtime api's mode.
99
59
  *
100
60
  * @param {RealtimeMode} mode Realtime mode
101
61
  */
102
- RealtimeLayerInterface.prototype.setMode = function (mode) { };
62
+ setMode(mode) { }
103
63
  /**
104
64
  * Request the stopSequence and the fullTrajectory informations for a vehicle.
105
65
  *
@@ -107,10 +67,8 @@ var RealtimeLayerInterface = /** @class */ (function () {
107
67
  * @param {RealtimeMode} mode The mode to request. If not defined, the layer´s mode propetrty will be used.
108
68
  * @return {Promise<{stopSequence: StopSequence, fullTrajectory: FullTrajectory>} A promise that will be resolved with the trajectory informations.
109
69
  */
110
- RealtimeLayerInterface.prototype.getTrajectoryInfos = function (id, mode) { };
111
- return RealtimeLayerInterface;
112
- }());
113
- exports.RealtimeLayerInterface = RealtimeLayerInterface;
70
+ getTrajectoryInfos(id, mode) { }
71
+ }
114
72
  /**
115
73
  * Mixin for RealtimeLayerInterface.
116
74
  *
@@ -118,588 +76,575 @@ exports.RealtimeLayerInterface = RealtimeLayerInterface;
118
76
  * @return {Class} A class that implements {RealtimeLayerInterface} class and extends Base;
119
77
  * @private
120
78
  */
121
- var RealtimeLayerMixin = function (Base) {
122
- return /** @class */ (function (_super) {
123
- __extends(class_1, _super);
124
- function class_1(options) {
125
- if (options === void 0) { options = {}; }
126
- var _this = _super.call(this, __assign({ hitTolerance: 10 }, options)) || this;
127
- _this.debug = options.debug;
128
- _this.mode = options.mode || api_1.RealtimeModes.TOPOGRAPHIC;
129
- _this.api = options.api || new api_1.RealtimeAPI(options);
130
- _this.tenant = options.tenant || ''; // sbb,sbh or sbm
131
- _this.minZoomNonTrain = options.minZoomNonTrain || 9; // Min zoom level from which non trains are allowed to be displayed. Min value is 9 (as configured by the server
132
- _this.minZoomInterpolation = options.minZoomInterpolation || 8; // Min zoom level from which trains positions are not interpolated.
133
- _this.format = new GeoJSON_1.default();
134
- _this.generalizationLevelByZoom = options.generalizationLevelByZoom || [
135
- 5, 5, 5, 5, 5, 5, 5, 5, 10, 30, 30, 100, 100, 100,
136
- ];
137
- _this.getGeneralizationLevelByZoom = function (zoom) {
138
- return ((options.getGeneralizationLevelByZoom &&
139
- options.getGeneralizationLevelByZoom(zoom, _this.generalizationLevelByZoom)) ||
140
- _this.generalizationLevelByZoom[zoom]);
141
- };
142
- _this.renderTimeIntervalByZoom = options.renderTimeIntervalByZoom || [
143
- 100000, 50000, 40000, 30000, 20000, 15000, 10000, 5000, 2000, 1000, 400,
144
- 300, 250, 180, 90, 60, 50, 50, 50, 50, 50,
145
- ];
146
- _this.getRenderTimeIntervalByZoom = function (zoom) {
147
- return ((options.getRenderTimeIntervalByZoom &&
148
- options.getRenderTimeIntervalByZoom(zoom, _this.renderTimeIntervalByZoom)) ||
149
- _this.renderTimeIntervalByZoom[zoom]);
150
- };
151
- // This property will call api.setBbox on each movend event
152
- _this.isUpdateBboxOnMoveEnd = options.isUpdateBboxOnMoveEnd !== false;
153
- // Define throttling and debounce render function
154
- _this.throttleRenderTrajectories = (0, lodash_throttle_1.default)(_this.renderTrajectoriesInternal, 50, { leading: false, trailing: true });
155
- _this.debounceRenderTrajectories = (0, lodash_debounce_1.default)(_this.renderTrajectoriesInternal, 50, { leading: true, trailing: true, maxWait: 5000 });
156
- // Bind callbacks
157
- _this.onFeatureHover = _this.onFeatureHover.bind(_this);
158
- _this.onFeatureClick = _this.onFeatureClick.bind(_this);
159
- _this.renderTrajectoriesInternal =
160
- _this.renderTrajectoriesInternal.bind(_this);
161
- _this.onTrajectoryMessage = _this.onTrajectoryMessage.bind(_this);
162
- _this.onDeleteTrajectoryMessage =
163
- _this.onDeleteTrajectoryMessage.bind(_this);
164
- _this.onDocumentVisibilityChange =
165
- _this.onDocumentVisibilityChange.bind(_this);
166
- return _this;
167
- }
168
- /**
169
- * Define layer's properties.
170
- *
171
- * @ignore
172
- */
173
- class_1.prototype.defineProperties = function (options) {
174
- var _this = this;
175
- var style = options.style, speed = options.speed, pixelRatio = options.pixelRatio, hoverVehicleId = options.hoverVehicleId, selectedVehicleId = options.selectedVehicleId, filter = options.filter, sort = options.sort, time = options.time, live = options.live, canvas = options.canvas, styleOptions = options.styleOptions;
176
- var currSpeed = speed || 1;
177
- var currTime = time || new Date();
178
- _super.prototype.defineProperties.call(this, options);
179
- Object.defineProperties(this, {
180
- isTrackerLayer: { value: true },
181
- canvas: {
182
- value: canvas || document.createElement('canvas'),
183
- },
184
- /**
185
- * Style function used to render a vehicle.
186
- */
187
- style: {
188
- value: style || realtimeDefaultStyle_1.default,
189
- },
190
- /**
191
- * Custom options to pass as last parameter of the style function.
192
- */
193
- styleOptions: {
194
- value: __assign(__assign({}, trackerConfig), (styleOptions || {})),
195
- },
196
- /**
197
- * Speed of the wheel of time.
198
- * If live property is true. The speed is ignored.
199
- */
200
- speed: {
201
- get: function () { return currSpeed; },
202
- set: function (newSpeed) {
203
- currSpeed = newSpeed;
204
- _this.start();
205
- },
206
- },
207
- /**
208
- * Function to filter which vehicles to display.
209
- */
210
- filter: {
211
- value: filter,
212
- writable: true,
213
- },
214
- /**
215
- * Function to sort the vehicles to display.
216
- */
217
- sort: {
218
- value: sort,
219
- writable: true,
220
- },
221
- /**
222
- * If true. The layer will always use Date.now() on the next tick to render the trajectories.
223
- * When true, setting the time property has no effect.
224
- */
225
- live: {
226
- value: live === false ? live : true,
227
- writable: true,
228
- },
229
- /**
230
- * Time used to display the trajectories. Can be a Date or a number in ms representing a Date.
231
- * If live property is true. The setter does nothing.
232
- */
233
- time: {
234
- get: function () { return currTime; },
235
- set: function (newTime) {
236
- currTime = newTime && newTime.getTime ? newTime : new Date(newTime);
237
- _this.renderTrajectories();
238
- },
239
- },
240
- /**
241
- * Keep track of which trajectories are stored.
242
- */
243
- trajectories: {
244
- value: {},
245
- writable: true,
246
- },
247
- /**
248
- * Id of the hovered vehicle.
249
- */
250
- hoverVehicleId: {
251
- value: hoverVehicleId,
252
- writable: true,
253
- },
254
- /**
255
- * Id of the selected vehicle.
256
- */
257
- selectedVehicleId: {
258
- value: selectedVehicleId,
259
- writable: true,
260
- },
261
- /**
262
- * Id of the selected vehicle.
263
- */
264
- pixelRatio: {
265
- value: pixelRatio ||
266
- (typeof window !== 'undefined' ? window.devicePixelRatio : 1),
267
- writable: true,
268
- },
269
- /**
270
- * If true, encapsulates the renderTrajectories calls in a requestAnimationFrame.
271
- */
272
- useRequestAnimationFrame: {
273
- value: options.useRequestAnimationFrame || false,
274
- writable: true,
275
- },
276
- /**
277
- * If true, encapsulates the renderTrajectories calls in a throttle function. Default to true.
278
- */
279
- useThrottle: {
280
- value: options.useThrottle !== false,
281
- writable: true,
79
+ const RealtimeLayerMixin = (Base) => class extends Base {
80
+ constructor(options = {}) {
81
+ super(Object.assign({ hitTolerance: 10 }, options));
82
+ this.debug = options.debug;
83
+ this.mode = options.mode || RealtimeModes.TOPOGRAPHIC;
84
+ this.api = options.api || new RealtimeAPI(options);
85
+ this.tenant = options.tenant || ''; // sbb,sbh or sbm
86
+ this.minZoomNonTrain = options.minZoomNonTrain || 9; // Min zoom level from which non trains are allowed to be displayed. Min value is 9 (as configured by the server
87
+ this.minZoomInterpolation = options.minZoomInterpolation || 8; // Min zoom level from which trains positions are not interpolated.
88
+ this.format = new GeoJSON();
89
+ this.generalizationLevelByZoom = options.generalizationLevelByZoom || [
90
+ 5, 5, 5, 5, 5, 5, 5, 5, 10, 30, 30, 100, 100, 100,
91
+ ];
92
+ this.getGeneralizationLevelByZoom = (zoom) => {
93
+ return ((options.getGeneralizationLevelByZoom &&
94
+ options.getGeneralizationLevelByZoom(zoom, this.generalizationLevelByZoom)) ||
95
+ this.generalizationLevelByZoom[zoom]);
96
+ };
97
+ this.renderTimeIntervalByZoom = options.renderTimeIntervalByZoom || [
98
+ 100000, 50000, 40000, 30000, 20000, 15000, 10000, 5000, 2000, 1000, 400,
99
+ 300, 250, 180, 90, 60, 50, 50, 50, 50, 50,
100
+ ];
101
+ this.getRenderTimeIntervalByZoom = (zoom) => {
102
+ return ((options.getRenderTimeIntervalByZoom &&
103
+ options.getRenderTimeIntervalByZoom(zoom, this.renderTimeIntervalByZoom)) ||
104
+ this.renderTimeIntervalByZoom[zoom]);
105
+ };
106
+ // This property will call api.setBbox on each movend event
107
+ this.isUpdateBboxOnMoveEnd = options.isUpdateBboxOnMoveEnd !== false;
108
+ // Define throttling and debounce render function
109
+ this.throttleRenderTrajectories = throttle(this.renderTrajectoriesInternal, 50, { leading: false, trailing: true });
110
+ this.debounceRenderTrajectories = debounce(this.renderTrajectoriesInternal, 50, { leading: true, trailing: true, maxWait: 5000 });
111
+ // Bind callbacks
112
+ this.onFeatureHover = this.onFeatureHover.bind(this);
113
+ this.onFeatureClick = this.onFeatureClick.bind(this);
114
+ this.renderTrajectoriesInternal =
115
+ this.renderTrajectoriesInternal.bind(this);
116
+ this.onTrajectoryMessage = this.onTrajectoryMessage.bind(this);
117
+ this.onDeleteTrajectoryMessage =
118
+ this.onDeleteTrajectoryMessage.bind(this);
119
+ this.onDocumentVisibilityChange =
120
+ this.onDocumentVisibilityChange.bind(this);
121
+ }
122
+ /**
123
+ * Define layer's properties.
124
+ *
125
+ * @ignore
126
+ */
127
+ defineProperties(options) {
128
+ const { style, speed, pixelRatio, hoverVehicleId, selectedVehicleId, filter, sort, time, live, canvas, styleOptions, } = options;
129
+ let currSpeed = speed || 1;
130
+ let currTime = time || new Date();
131
+ super.defineProperties(options);
132
+ Object.defineProperties(this, {
133
+ isTrackerLayer: { value: true },
134
+ canvas: {
135
+ value: canvas || document.createElement('canvas'),
136
+ },
137
+ /**
138
+ * Style function used to render a vehicle.
139
+ */
140
+ style: {
141
+ value: style || realtimeDefaultStyle,
142
+ },
143
+ /**
144
+ * Custom options to pass as last parameter of the style function.
145
+ */
146
+ styleOptions: {
147
+ value: Object.assign(Object.assign({}, trackerConfig), (styleOptions || {})),
148
+ },
149
+ /**
150
+ * Speed of the wheel of time.
151
+ * If live property is true. The speed is ignored.
152
+ */
153
+ speed: {
154
+ get: () => currSpeed,
155
+ set: (newSpeed) => {
156
+ currSpeed = newSpeed;
157
+ this.start();
282
158
  },
283
- /**
284
- * If true, encapsulates the renderTrajectories calls in a debounce function.
285
- */
286
- useDebounce: {
287
- value: options.useDebounce || false,
288
- writable: true,
159
+ },
160
+ /**
161
+ * Function to filter which vehicles to display.
162
+ */
163
+ filter: {
164
+ value: filter,
165
+ writable: true,
166
+ },
167
+ /**
168
+ * Function to sort the vehicles to display.
169
+ */
170
+ sort: {
171
+ value: sort,
172
+ writable: true,
173
+ },
174
+ /**
175
+ * If true. The layer will always use Date.now() on the next tick to render the trajectories.
176
+ * When true, setting the time property has no effect.
177
+ */
178
+ live: {
179
+ value: live === false ? live : true,
180
+ writable: true,
181
+ },
182
+ /**
183
+ * Time used to display the trajectories. Can be a Date or a number in ms representing a Date.
184
+ * If live property is true. The setter does nothing.
185
+ */
186
+ time: {
187
+ get: () => currTime,
188
+ set: (newTime) => {
189
+ currTime = newTime && newTime.getTime ? newTime : new Date(newTime);
190
+ this.renderTrajectories();
289
191
  },
290
- /**
291
- * Debug properties.
292
- */
293
- // Not used anymore, but could be useful for debugging.
294
- // showVehicleTraj: {
295
- // value:
296
- // options.showVehicleTraj !== undefined
297
- // ? options.showVehicleTraj
298
- // : true,
299
- // writable: true,
300
- // },
301
- });
302
- };
303
- class_1.prototype.attachToMap = function (map) {
304
- var _this = this;
305
- _super.prototype.attachToMap.call(this, map);
306
- // If the layer is visible we start the rendering clock
307
- if (this.visible) {
192
+ },
193
+ /**
194
+ * Keep track of which trajectories are stored.
195
+ */
196
+ trajectories: {
197
+ value: {},
198
+ writable: true,
199
+ },
200
+ /**
201
+ * Id of the hovered vehicle.
202
+ */
203
+ hoverVehicleId: {
204
+ value: hoverVehicleId,
205
+ writable: true,
206
+ },
207
+ /**
208
+ * Id of the selected vehicle.
209
+ */
210
+ selectedVehicleId: {
211
+ value: selectedVehicleId,
212
+ writable: true,
213
+ },
214
+ /**
215
+ * Id of the selected vehicle.
216
+ */
217
+ pixelRatio: {
218
+ value: pixelRatio ||
219
+ (typeof window !== 'undefined' ? window.devicePixelRatio : 1),
220
+ writable: true,
221
+ },
222
+ /**
223
+ * If true, encapsulates the renderTrajectories calls in a requestAnimationFrame.
224
+ */
225
+ useRequestAnimationFrame: {
226
+ value: options.useRequestAnimationFrame || false,
227
+ writable: true,
228
+ },
229
+ /**
230
+ * If true, encapsulates the renderTrajectories calls in a throttle function. Default to true.
231
+ */
232
+ useThrottle: {
233
+ value: options.useThrottle !== false,
234
+ writable: true,
235
+ },
236
+ /**
237
+ * If true, encapsulates the renderTrajectories calls in a debounce function.
238
+ */
239
+ useDebounce: {
240
+ value: options.useDebounce || false,
241
+ writable: true,
242
+ },
243
+ /**
244
+ * Debug properties.
245
+ */
246
+ // Not used anymore, but could be useful for debugging.
247
+ // showVehicleTraj: {
248
+ // value:
249
+ // options.showVehicleTraj !== undefined
250
+ // ? options.showVehicleTraj
251
+ // : true,
252
+ // writable: true,
253
+ // },
254
+ });
255
+ }
256
+ attachToMap(map) {
257
+ super.attachToMap(map);
258
+ // If the layer is visible we start the rendering clock
259
+ if (this.visible) {
260
+ this.start();
261
+ }
262
+ // On change of visibility we start/stop the rendering clock
263
+ this.visibilityRef = this.on('change:visible', (evt) => {
264
+ if (evt.target.visible) {
308
265
  this.start();
309
266
  }
310
- // On change of visibility we start/stop the rendering clock
311
- this.visibilityRef = this.on('change:visible', function (evt) {
312
- if (evt.target.visible) {
313
- _this.start();
314
- }
315
- else {
316
- _this.stop();
317
- }
318
- });
319
- // To avoid browser hanging when the tab is not visible for a certain amount of time,
320
- // We stop the rendering and the websocket when hide and start again when show.
321
- document.addEventListener('visibilitychange', this.onDocumentVisibilityChange);
322
- };
323
- class_1.prototype.detachFromMap = function () {
324
- document.removeEventListener('visibilitychange', this.onDocumentVisibilityChange);
325
- this.stop();
326
- (0, Observable_1.unByKey)(this.visibilityRef);
327
- var context = this.canvas.getContext('2d');
328
- context.clearRect(0, 0, this.canvas.width, this.canvas.height);
329
- _super.prototype.detachFromMap.call(this);
330
- };
331
- class_1.prototype.start = function () {
332
- this.stop();
333
- this.renderTrajectories();
334
- this.startUpdateTime();
335
- this.api.open();
336
- this.api.subscribeTrajectory(this.mode, this.onTrajectoryMessage, this.isUpdateBboxOnMoveEnd);
337
- this.api.subscribeDeletedVehicles(this.mode, this.onDeleteTrajectoryMessage, this.isUpdateBboxOnMoveEnd);
338
- if (this.isUpdateBboxOnMoveEnd) {
339
- // Update the bbox on each move end
340
- this.setBbox();
341
- }
342
- };
343
- /**
344
- * Start the clock.
345
- * @private
346
- */
347
- class_1.prototype.startUpdateTime = function () {
348
- var _this = this;
349
- this.stopUpdateTime();
350
- this.updateTimeDelay = this.getRefreshTimeInMs();
351
- this.updateTimeInterval = setInterval(function () {
352
- // When live=true, we update the time with new Date();
353
- _this.time = _this.live
354
- ? new Date()
355
- : _this.time.getTime() + _this.updateTimeDelay * _this.speed;
356
- }, this.updateTimeDelay);
357
- };
358
- class_1.prototype.stop = function () {
359
- this.api.unsubscribeTrajectory(this.onTrajectoryMessage);
360
- this.api.unsubscribeDeletedVehicles(this.onDeleteTrajectoryMessage);
361
- this.api.close();
362
- };
363
- /**
364
- * Stop the clock.
365
- * @private
366
- */
367
- class_1.prototype.stopUpdateTime = function () {
368
- if (this.updateTimeInterval) {
369
- clearInterval(this.updateTimeInterval);
370
- }
371
- };
372
- /**
373
- * Launch renderTrajectories. it avoids duplicating code in renderTrajectories method.
374
- *
375
- * @param {object} viewState The view state of the map.
376
- * @param {number[2]} viewState.center Center coordinate of the map in mercator coordinate.
377
- * @param {number[4]} viewState.extent Extent of the map in mercator coordinates.
378
- * @param {number[2]} viewState.size Size ([width, height]) of the canvas to render.
379
- * @param {number} [viewState.rotation = 0] Rotation of the map to render.
380
- * @param {number} viewState.resolution Resolution of the map to render.
381
- * @param {boolean} noInterpolate If true trajectories are not interpolated but
382
- * drawn at the last known coordinate. Use this for performance optimization
383
- * during map navigation.
384
- * @private
385
- */
386
- class_1.prototype.renderTrajectoriesInternal = function (viewState, noInterpolate) {
387
- if (!this.map) {
388
- return false;
389
- }
390
- var time = this.live ? Date.now() : this.time;
391
- var trajectories = Object.values(this.trajectories);
392
- // console.time('sort');
393
- if (this.sort) {
394
- trajectories.sort(this.sort);
395
- }
396
- // console.timeEnd('sort');
397
- // console.time('render');
398
- this.renderState = (0, renderTrajectories_1.default)(this.canvas, trajectories, this.style, __assign(__assign({}, viewState), { pixelRatio: this.pixelRatio, time: time }), __assign({ noInterpolate: viewState.zoom < this.minZoomInterpolation ? true : noInterpolate, hoverVehicleId: this.hoverVehicleId, selectedVehicleId: this.selectedVehicleId }, this.styleOptions));
399
- // console.timeEnd('render');
400
- return true;
401
- };
402
- /**
403
- * Render the trajectories requesting an animation frame and cancelling the previous one.
404
- * This function must be overrided by children to provide the correct parameters.
405
- *
406
- * @param {object} viewState The view state of the map.
407
- * @param {number[2]} viewState.center Center coordinate of the map in mercator coordinate.
408
- * @param {number[4]} viewState.extent Extent of the map in mercator coordinates.
409
- * @param {number[2]} viewState.size Size ([width, height]) of the canvas to render.
410
- * @param {number} [viewState.rotation = 0] Rotation of the map to render.
411
- * @param {number} viewState.resolution Resolution of the map to render.
412
- * @param {boolean} noInterpolate If true trajectories are not interpolated but
413
- * drawn at the last known coordinate. Use this for performance optimization
414
- * during map navigation.
415
- * @private
416
- */
417
- class_1.prototype.renderTrajectories = function (viewState, noInterpolate) {
418
- var _this = this;
419
- if (this.requestId) {
420
- cancelAnimationFrame(this.requestId);
421
- this.requestId = null;
422
- }
423
- if (!noInterpolate && this.useRequestAnimationFrame) {
424
- this.requestId = requestAnimationFrame(function () {
425
- _this.renderTrajectoriesInternal(viewState, noInterpolate);
426
- });
427
- }
428
- else if (!noInterpolate && this.useDebounce) {
429
- this.debounceRenderTrajectories(viewState, noInterpolate);
430
- }
431
- else if (!noInterpolate && this.useThrottle) {
432
- this.throttleRenderTrajectories(viewState, noInterpolate);
433
- }
434
267
  else {
435
- this.renderTrajectoriesInternal(viewState, noInterpolate);
436
- }
437
- };
438
- class_1.prototype.setBbox = function (extent, zoom) {
439
- // Clean trajectories before sending the new bbox
440
- // Purge trajectories:
441
- // - which are outside the extent
442
- // - when it's bus and zoom level is too low for them
443
- var keys = Object.keys(this.trajectories);
444
- for (var i = keys.length - 1; i >= 0; i -= 1) {
445
- this.purgeTrajectory(this.trajectories[keys[i]], extent, zoom);
446
- }
447
- var bbox = __spreadArray([], extent, true);
448
- if (this.isUpdateBboxOnMoveEnd) {
449
- bbox.push(zoom);
450
- if (this.tenant) {
451
- bbox.push("tenant=".concat(this.tenant));
452
- }
453
- /* @ignore */
454
- this.generalizationLevel = this.getGeneralizationLevelByZoom(zoom);
455
- if (this.generalizationLevel) {
456
- bbox.push("gen=".concat(this.generalizationLevel));
457
- }
458
- }
459
- this.api.bbox = bbox;
460
- };
461
- class_1.prototype.setMode = function (mode) {
462
- if (this.mode === mode) {
463
- return;
464
- }
465
- this.mode = mode;
466
- this.api.subscribeTrajectory(this.mode, this.onTrajectoryMessage, this.isUpdateBboxOnMoveEnd);
467
- this.api.subscribeDeletedVehicles(this.mode, this.onDeleteTrajectoryMessage, this.isUpdateBboxOnMoveEnd);
468
- };
469
- /**
470
- * Get the duration before the next update depending on zoom level.
471
- *
472
- * @private
473
- * @param {number} zoom
474
- */
475
- class_1.prototype.getRefreshTimeInMs = function (zoom) {
476
- var _a;
477
- var roundedZoom = Math.round(zoom);
478
- var timeStep = this.getRenderTimeIntervalByZoom(roundedZoom) || 25;
479
- var nextTick = Math.max(25, timeStep / this.speed);
480
- var nextThrottleTick = Math.min(nextTick, 500);
481
- // TODO: see if this should go elsewhere.
482
- if (this.useThrottle) {
483
- this.throttleRenderTrajectories = (0, lodash_throttle_1.default)(this.renderTrajectoriesInternal, nextThrottleTick, { leading: true, trailing: true });
484
- }
485
- else if (this.useDebounce) {
486
- this.debounceRenderTrajectories = (0, lodash_debounce_1.default)(this.renderTrajectoriesInternal, nextThrottleTick, { leading: true, trailing: true, maxWait: 5000 });
487
- }
488
- if ((_a = this.api) === null || _a === void 0 ? void 0 : _a.buffer) {
489
- var _b = this.api.buffer, size = _b[1];
490
- this.api.buffer = [nextThrottleTick, size];
491
- }
492
- return nextTick;
493
- };
494
- /**
495
- * Get vehicle.
496
- * @param {function} filterFc A function use to filter results.
497
- * @return {Array<Object>} Array of vehicle.
498
- */
499
- class_1.prototype.getVehicle = function (filterFc) {
500
- return Object.values(this.trajectories).filter(filterFc);
501
- };
502
- /**
503
- * Request feature information for a given coordinate.
504
- *
505
- * @param {ol/coordinate~Coordinate} coordinate Coordinate.
506
- * @param {Object} options Options See child classes to see which options are supported.
507
- * @param {number} [options.resolution=1] The resolution of the map.
508
- * @param {number} [options.nb=Infinity] The max number of vehicles to return.
509
- * @return {Promise<FeatureInfo>} Promise with features, layer and coordinate.
510
- */
511
- class_1.prototype.getFeatureInfoAtCoordinate = function (coordinate, options) {
512
- var _this = this;
513
- if (options === void 0) { options = {}; }
514
- var resolution = options.resolution, nb = options.nb;
515
- var ext = (0, extent_1.buffer)(__spreadArray(__spreadArray([], coordinate, true), coordinate, true), this.hitTolerance * resolution);
516
- var trajectories = Object.values(this.trajectories);
517
- if (this.sort) {
518
- trajectories = trajectories.sort(this.sort);
519
- }
520
- var vehicles = [];
521
- for (var i = 0; i < trajectories.length; i += 1) {
522
- if (trajectories[i].properties.coordinate &&
523
- (0, extent_1.containsCoordinate)(ext, trajectories[i].properties.coordinate)) {
524
- vehicles.push(trajectories[i]);
525
- }
526
- if (vehicles.length === nb) {
527
- break;
528
- }
529
- }
530
- return Promise.resolve({
531
- layer: this,
532
- features: vehicles.map(function (vehicle) { return _this.format.readFeature(vehicle); }),
533
- coordinate: coordinate,
534
- });
535
- };
536
- /**
537
- * Request the stopSequence and the fullTrajectory informations for a vehicle.
538
- *
539
- * @param {string} id The vehicle identifier (the train_id property).
540
- * @return {Promise<{stopSequence: StopSequence, fullTrajectory: FullTrajectory>} A promise that will be resolved with the trajectory informations.
541
- */
542
- class_1.prototype.getTrajectoryInfos = function (id) {
543
- // When a vehicle is selected, we request the complete stop sequence and the complete full trajectory.
544
- // Then we combine them in one response and send them to inherited layers.
545
- var promises = [
546
- this.api.getStopSequence(id, this.mode),
547
- this.api.getFullTrajectory(id, this.mode, this.generalizationLevel),
548
- ];
549
- return Promise.all(promises).then(function (_a) {
550
- var stopSequence = _a[0], fullTrajectory = _a[1];
551
- var response = {
552
- stopSequence: stopSequence,
553
- fullTrajectory: fullTrajectory,
554
- };
555
- return response;
556
- });
557
- };
558
- /**
559
- * Determine if the trajectory is useless and should be removed from the list or not.
560
- * By default, this function exclude vehicles:
561
- * - that have their trajectory outside the current extent and
562
- * - that are not a train and zoom level is lower than layer's minZoomNonTrain property.
563
- *
564
- * @param {RealtimeTrajectory} trajectory
565
- * @param {Array<number>} extent
566
- * @param {number} zoom
567
- * @return {boolean} if the trajectory must be displayed or not.
568
- * @ignore
569
- */
570
- class_1.prototype.purgeTrajectory = function (trajectory, extent, zoom) {
571
- var _a = trajectory.properties, type = _a.type, bounds = _a.bounds, id = _a.train_id;
572
- if (!(0, extent_1.intersects)(extent, bounds) ||
573
- (type !== 'rail' && zoom < (this.minZoomNonTrain || 9))) {
574
- this.removeTrajectory(id);
575
- return true;
576
- }
577
- return false;
578
- };
579
- /**
580
- * Add a trajectory.
581
- * @param {RealtimeTrajectory} trajectory The trajectory to add.
582
- * @private
583
- */
584
- class_1.prototype.addTrajectory = function (trajectory) {
585
- if (this.filter && !this.filter(trajectory)) {
586
- return;
587
- }
588
- this.trajectories[trajectory.properties.train_id] = trajectory;
589
- this.renderTrajectories();
590
- };
591
- class_1.prototype.removeTrajectory = function (id) {
592
- delete this.trajectories[id];
593
- };
594
- /**
595
- * On zoomend we adjust the time interval of the update of vehicles positions.
596
- *
597
- * @param evt Event that triggered the function.
598
- * @private
599
- */
600
- // eslint-disable-next-line no-unused-vars
601
- class_1.prototype.onZoomEnd = function (evt) {
602
- this.startUpdateTime();
603
- };
604
- class_1.prototype.onDocumentVisibilityChange = function () {
605
- if (!this.visible) {
606
- return;
607
- }
608
- if (document.hidden) {
609
268
  this.stop();
610
269
  }
611
- else {
612
- this.start();
613
- }
614
- };
615
- /**
616
- * Callback on websocket's trajectory channel events.
617
- * It adds a trajectory to the list.
618
- *
619
- * @private
620
- */
621
- class_1.prototype.onTrajectoryMessage = function (data) {
622
- if (!data.content) {
623
- return;
624
- }
625
- var trajectory = data.content;
626
- var geometry = trajectory.geometry, _a = trajectory.properties, id = _a.train_id, timeSinceUpdate = _a.time_since_update, rawCoordinates = _a.raw_coordinates;
627
- // ignore old events [SBAHNM-97]
628
- if (timeSinceUpdate < 0) {
629
- return;
630
- }
631
- // console.time(`onTrajectoryMessage${data.content.properties.train_id}`);
632
- if (this.purgeTrajectory(trajectory)) {
633
- return;
634
- }
635
- if (this.debug &&
636
- this.mode === api_1.RealtimeModes.TOPOGRAPHIC &&
637
- rawCoordinates) {
638
- trajectory.properties.olGeometry = {
639
- type: 'Point',
640
- coordinates: (0, proj_1.fromLonLat)(rawCoordinates, this.map.getView().getProjection()),
641
- };
642
- }
643
- else {
644
- trajectory.properties.olGeometry = this.format.readGeometry(geometry);
645
- }
646
- // TODO Make sure the timeOffset is useful. May be we can remove it.
647
- trajectory.properties.timeOffset = Date.now() - data.timestamp;
648
- this.addTrajectory(trajectory);
649
- };
650
- /**
651
- * Callback on websocket's deleted_vehicles channel events.
652
- * It removes the trajectory from the list.
653
- *
654
- * @private
655
- * @override
656
- */
657
- class_1.prototype.onDeleteTrajectoryMessage = function (data) {
658
- if (!data.content) {
659
- return;
660
- }
661
- this.removeTrajectory(data.content);
662
- };
663
- /**
664
- * Callback when user moves the mouse/pointer over the map.
665
- * It sets the layer's hoverVehicleId property with the current hovered vehicle's id.
666
- *
667
- * @private
668
- * @override
669
- */
670
- class_1.prototype.onFeatureHover = function (features, layer, coordinate) {
671
- var feature = features[0];
672
- var id = null;
673
- if (feature) {
674
- id = feature.get('train_id');
270
+ });
271
+ // To avoid browser hanging when the tab is not visible for a certain amount of time,
272
+ // We stop the rendering and the websocket when hide and start again when show.
273
+ document.addEventListener('visibilitychange', this.onDocumentVisibilityChange);
274
+ }
275
+ detachFromMap() {
276
+ document.removeEventListener('visibilitychange', this.onDocumentVisibilityChange);
277
+ this.stop();
278
+ unByKey(this.visibilityRef);
279
+ const context = this.canvas.getContext('2d');
280
+ context.clearRect(0, 0, this.canvas.width, this.canvas.height);
281
+ super.detachFromMap();
282
+ }
283
+ start() {
284
+ this.stop();
285
+ this.renderTrajectories();
286
+ this.startUpdateTime();
287
+ this.api.open();
288
+ this.api.subscribeTrajectory(this.mode, this.onTrajectoryMessage, this.isUpdateBboxOnMoveEnd);
289
+ this.api.subscribeDeletedVehicles(this.mode, this.onDeleteTrajectoryMessage, this.isUpdateBboxOnMoveEnd);
290
+ if (this.isUpdateBboxOnMoveEnd) {
291
+ // Update the bbox on each move end
292
+ this.setBbox();
293
+ }
294
+ }
295
+ /**
296
+ * Start the clock.
297
+ * @private
298
+ */
299
+ startUpdateTime() {
300
+ this.stopUpdateTime();
301
+ this.updateTimeDelay = this.getRefreshTimeInMs();
302
+ this.updateTimeInterval = setInterval(() => {
303
+ // When live=true, we update the time with new Date();
304
+ this.time = this.live
305
+ ? new Date()
306
+ : this.time.getTime() + this.updateTimeDelay * this.speed;
307
+ }, this.updateTimeDelay);
308
+ }
309
+ stop() {
310
+ this.api.unsubscribeTrajectory(this.onTrajectoryMessage);
311
+ this.api.unsubscribeDeletedVehicles(this.onDeleteTrajectoryMessage);
312
+ this.api.close();
313
+ }
314
+ /**
315
+ * Stop the clock.
316
+ * @private
317
+ */
318
+ stopUpdateTime() {
319
+ if (this.updateTimeInterval) {
320
+ clearInterval(this.updateTimeInterval);
321
+ }
322
+ }
323
+ /**
324
+ * Launch renderTrajectories. it avoids duplicating code in renderTrajectories method.
325
+ *
326
+ * @param {object} viewState The view state of the map.
327
+ * @param {number[2]} viewState.center Center coordinate of the map in mercator coordinate.
328
+ * @param {number[4]} viewState.extent Extent of the map in mercator coordinates.
329
+ * @param {number[2]} viewState.size Size ([width, height]) of the canvas to render.
330
+ * @param {number} [viewState.rotation = 0] Rotation of the map to render.
331
+ * @param {number} viewState.resolution Resolution of the map to render.
332
+ * @param {boolean} noInterpolate If true trajectories are not interpolated but
333
+ * drawn at the last known coordinate. Use this for performance optimization
334
+ * during map navigation.
335
+ * @private
336
+ */
337
+ renderTrajectoriesInternal(viewState, noInterpolate) {
338
+ if (!this.map) {
339
+ return false;
340
+ }
341
+ const time = this.live ? Date.now() : this.time;
342
+ const trajectories = Object.values(this.trajectories);
343
+ // console.time('sort');
344
+ if (this.sort) {
345
+ trajectories.sort(this.sort);
346
+ }
347
+ // console.timeEnd('sort');
348
+ // console.time('render');
349
+ this.renderState = renderTrajectories(this.canvas, trajectories, this.style, Object.assign(Object.assign({}, viewState), { pixelRatio: this.pixelRatio, time }), Object.assign({ noInterpolate: viewState.zoom < this.minZoomInterpolation ? true : noInterpolate, hoverVehicleId: this.hoverVehicleId, selectedVehicleId: this.selectedVehicleId }, this.styleOptions));
350
+ // console.timeEnd('render');
351
+ return true;
352
+ }
353
+ /**
354
+ * Render the trajectories requesting an animation frame and cancelling the previous one.
355
+ * This function must be overrided by children to provide the correct parameters.
356
+ *
357
+ * @param {object} viewState The view state of the map.
358
+ * @param {number[2]} viewState.center Center coordinate of the map in mercator coordinate.
359
+ * @param {number[4]} viewState.extent Extent of the map in mercator coordinates.
360
+ * @param {number[2]} viewState.size Size ([width, height]) of the canvas to render.
361
+ * @param {number} [viewState.rotation = 0] Rotation of the map to render.
362
+ * @param {number} viewState.resolution Resolution of the map to render.
363
+ * @param {boolean} noInterpolate If true trajectories are not interpolated but
364
+ * drawn at the last known coordinate. Use this for performance optimization
365
+ * during map navigation.
366
+ * @private
367
+ */
368
+ renderTrajectories(viewState, noInterpolate) {
369
+ if (this.requestId) {
370
+ cancelAnimationFrame(this.requestId);
371
+ this.requestId = null;
372
+ }
373
+ if (!noInterpolate && this.useRequestAnimationFrame) {
374
+ this.requestId = requestAnimationFrame(() => {
375
+ this.renderTrajectoriesInternal(viewState, noInterpolate);
376
+ });
377
+ }
378
+ else if (!noInterpolate && this.useDebounce) {
379
+ this.debounceRenderTrajectories(viewState, noInterpolate);
380
+ }
381
+ else if (!noInterpolate && this.useThrottle) {
382
+ this.throttleRenderTrajectories(viewState, noInterpolate);
383
+ }
384
+ else {
385
+ this.renderTrajectoriesInternal(viewState, noInterpolate);
386
+ }
387
+ }
388
+ setBbox(extent, zoom) {
389
+ // Clean trajectories before sending the new bbox
390
+ // Purge trajectories:
391
+ // - which are outside the extent
392
+ // - when it's bus and zoom level is too low for them
393
+ const keys = Object.keys(this.trajectories);
394
+ for (let i = keys.length - 1; i >= 0; i -= 1) {
395
+ this.purgeTrajectory(this.trajectories[keys[i]], extent, zoom);
396
+ }
397
+ const bbox = [...extent];
398
+ if (this.isUpdateBboxOnMoveEnd) {
399
+ bbox.push(zoom);
400
+ if (this.tenant) {
401
+ bbox.push(`tenant=${this.tenant}`);
675
402
  }
676
- if (this.hoverVehicleId !== id) {
677
- /** @ignore */
678
- this.hoverVehicleId = id;
679
- this.renderTrajectories(true);
403
+ /* @ignore */
404
+ this.generalizationLevel = this.getGeneralizationLevelByZoom(zoom);
405
+ if (this.generalizationLevel) {
406
+ bbox.push(`gen=${this.generalizationLevel}`);
680
407
  }
681
- };
682
- /**
683
- * Callback when user clicks on the map.
684
- * It sets the layer's selectedVehicleId property with the current selected vehicle's id.
685
- *
686
- * @private
687
- * @override
688
- */
689
- class_1.prototype.onFeatureClick = function (features, layer, coordinate) {
690
- var feature = features[0];
691
- var id = null;
692
- if (feature) {
693
- id = feature.get('train_id');
408
+ }
409
+ this.api.bbox = bbox;
410
+ }
411
+ setMode(mode) {
412
+ if (this.mode === mode) {
413
+ return;
414
+ }
415
+ this.mode = mode;
416
+ this.api.subscribeTrajectory(this.mode, this.onTrajectoryMessage, this.isUpdateBboxOnMoveEnd);
417
+ this.api.subscribeDeletedVehicles(this.mode, this.onDeleteTrajectoryMessage, this.isUpdateBboxOnMoveEnd);
418
+ }
419
+ /**
420
+ * Get the duration before the next update depending on zoom level.
421
+ *
422
+ * @private
423
+ * @param {number} zoom
424
+ */
425
+ getRefreshTimeInMs(zoom) {
426
+ var _a;
427
+ const roundedZoom = Math.round(zoom);
428
+ const timeStep = this.getRenderTimeIntervalByZoom(roundedZoom) || 25;
429
+ const nextTick = Math.max(25, timeStep / this.speed);
430
+ const nextThrottleTick = Math.min(nextTick, 500);
431
+ // TODO: see if this should go elsewhere.
432
+ if (this.useThrottle) {
433
+ this.throttleRenderTrajectories = throttle(this.renderTrajectoriesInternal, nextThrottleTick, { leading: true, trailing: true });
434
+ }
435
+ else if (this.useDebounce) {
436
+ this.debounceRenderTrajectories = debounce(this.renderTrajectoriesInternal, nextThrottleTick, { leading: true, trailing: true, maxWait: 5000 });
437
+ }
438
+ if ((_a = this.api) === null || _a === void 0 ? void 0 : _a.buffer) {
439
+ const [, size] = this.api.buffer;
440
+ this.api.buffer = [nextThrottleTick, size];
441
+ }
442
+ return nextTick;
443
+ }
444
+ /**
445
+ * Get vehicle.
446
+ * @param {function} filterFc A function use to filter results.
447
+ * @return {Array<Object>} Array of vehicle.
448
+ */
449
+ getVehicle(filterFc) {
450
+ return Object.values(this.trajectories).filter(filterFc);
451
+ }
452
+ /**
453
+ * Request feature information for a given coordinate.
454
+ *
455
+ * @param {ol/coordinate~Coordinate} coordinate Coordinate.
456
+ * @param {Object} options Options See child classes to see which options are supported.
457
+ * @param {number} [options.resolution=1] The resolution of the map.
458
+ * @param {number} [options.nb=Infinity] The max number of vehicles to return.
459
+ * @return {Promise<FeatureInfo>} Promise with features, layer and coordinate.
460
+ */
461
+ getFeatureInfoAtCoordinate(coordinate, options = {}) {
462
+ const { resolution, nb } = options;
463
+ const ext = buffer([...coordinate, ...coordinate], this.hitTolerance * resolution);
464
+ let trajectories = Object.values(this.trajectories);
465
+ if (this.sort) {
466
+ trajectories = trajectories.sort(this.sort);
467
+ }
468
+ const vehicles = [];
469
+ for (let i = 0; i < trajectories.length; i += 1) {
470
+ if (trajectories[i].properties.coordinate &&
471
+ containsCoordinate(ext, trajectories[i].properties.coordinate)) {
472
+ vehicles.push(trajectories[i]);
694
473
  }
695
- if (this.selectedVehicleId !== id) {
696
- /** @ignore */
697
- this.selectedVehicleId = id;
698
- this.selectedVehicle = feature;
699
- this.renderTrajectories(true);
474
+ if (vehicles.length === nb) {
475
+ break;
700
476
  }
701
- };
702
- return class_1;
703
- }(Base));
477
+ }
478
+ return Promise.resolve({
479
+ layer: this,
480
+ features: vehicles.map((vehicle) => this.format.readFeature(vehicle)),
481
+ coordinate,
482
+ });
483
+ }
484
+ /**
485
+ * Request the stopSequence and the fullTrajectory informations for a vehicle.
486
+ *
487
+ * @param {string} id The vehicle identifier (the train_id property).
488
+ * @return {Promise<{stopSequence: StopSequence, fullTrajectory: FullTrajectory>} A promise that will be resolved with the trajectory informations.
489
+ */
490
+ getTrajectoryInfos(id) {
491
+ // When a vehicle is selected, we request the complete stop sequence and the complete full trajectory.
492
+ // Then we combine them in one response and send them to inherited layers.
493
+ const promises = [
494
+ this.api.getStopSequence(id, this.mode),
495
+ this.api.getFullTrajectory(id, this.mode, this.generalizationLevel),
496
+ ];
497
+ return Promise.all(promises).then(([stopSequence, fullTrajectory]) => {
498
+ const response = {
499
+ stopSequence,
500
+ fullTrajectory,
501
+ };
502
+ return response;
503
+ });
504
+ }
505
+ /**
506
+ * Determine if the trajectory is useless and should be removed from the list or not.
507
+ * By default, this function exclude vehicles:
508
+ * - that have their trajectory outside the current extent and
509
+ * - that are not a train and zoom level is lower than layer's minZoomNonTrain property.
510
+ *
511
+ * @param {RealtimeTrajectory} trajectory
512
+ * @param {Array<number>} extent
513
+ * @param {number} zoom
514
+ * @return {boolean} if the trajectory must be displayed or not.
515
+ * @ignore
516
+ */
517
+ purgeTrajectory(trajectory, extent, zoom) {
518
+ const { type, bounds, train_id: id } = trajectory.properties;
519
+ if (!intersects(extent, bounds) ||
520
+ (type !== 'rail' && zoom < (this.minZoomNonTrain || 9))) {
521
+ this.removeTrajectory(id);
522
+ return true;
523
+ }
524
+ return false;
525
+ }
526
+ /**
527
+ * Add a trajectory.
528
+ * @param {RealtimeTrajectory} trajectory The trajectory to add.
529
+ * @private
530
+ */
531
+ addTrajectory(trajectory) {
532
+ if (this.filter && !this.filter(trajectory)) {
533
+ return;
534
+ }
535
+ this.trajectories[trajectory.properties.train_id] = trajectory;
536
+ this.renderTrajectories();
537
+ }
538
+ removeTrajectory(id) {
539
+ delete this.trajectories[id];
540
+ }
541
+ /**
542
+ * On zoomend we adjust the time interval of the update of vehicles positions.
543
+ *
544
+ * @param evt Event that triggered the function.
545
+ * @private
546
+ */
547
+ // eslint-disable-next-line no-unused-vars
548
+ onZoomEnd(evt) {
549
+ this.startUpdateTime();
550
+ }
551
+ onDocumentVisibilityChange() {
552
+ if (!this.visible) {
553
+ return;
554
+ }
555
+ if (document.hidden) {
556
+ this.stop();
557
+ }
558
+ else {
559
+ this.start();
560
+ }
561
+ }
562
+ /**
563
+ * Callback on websocket's trajectory channel events.
564
+ * It adds a trajectory to the list.
565
+ *
566
+ * @private
567
+ */
568
+ onTrajectoryMessage(data) {
569
+ if (!data.content) {
570
+ return;
571
+ }
572
+ const trajectory = data.content;
573
+ const { geometry, properties: { train_id: id, time_since_update: timeSinceUpdate, raw_coordinates: rawCoordinates, }, } = trajectory;
574
+ // ignore old events [SBAHNM-97]
575
+ if (timeSinceUpdate < 0) {
576
+ return;
577
+ }
578
+ // console.time(`onTrajectoryMessage${data.content.properties.train_id}`);
579
+ if (this.purgeTrajectory(trajectory)) {
580
+ return;
581
+ }
582
+ if (this.debug &&
583
+ this.mode === RealtimeModes.TOPOGRAPHIC &&
584
+ rawCoordinates) {
585
+ trajectory.properties.olGeometry = {
586
+ type: 'Point',
587
+ coordinates: fromLonLat(rawCoordinates, this.map.getView().getProjection()),
588
+ };
589
+ }
590
+ else {
591
+ trajectory.properties.olGeometry = this.format.readGeometry(geometry);
592
+ }
593
+ // TODO Make sure the timeOffset is useful. May be we can remove it.
594
+ trajectory.properties.timeOffset = Date.now() - data.timestamp;
595
+ this.addTrajectory(trajectory);
596
+ }
597
+ /**
598
+ * Callback on websocket's deleted_vehicles channel events.
599
+ * It removes the trajectory from the list.
600
+ *
601
+ * @private
602
+ * @override
603
+ */
604
+ onDeleteTrajectoryMessage(data) {
605
+ if (!data.content) {
606
+ return;
607
+ }
608
+ this.removeTrajectory(data.content);
609
+ }
610
+ /**
611
+ * Callback when user moves the mouse/pointer over the map.
612
+ * It sets the layer's hoverVehicleId property with the current hovered vehicle's id.
613
+ *
614
+ * @private
615
+ * @override
616
+ */
617
+ onFeatureHover(features, layer, coordinate) {
618
+ const [feature] = features;
619
+ let id = null;
620
+ if (feature) {
621
+ id = feature.get('train_id');
622
+ }
623
+ if (this.hoverVehicleId !== id) {
624
+ /** @ignore */
625
+ this.hoverVehicleId = id;
626
+ this.renderTrajectories(true);
627
+ }
628
+ }
629
+ /**
630
+ * Callback when user clicks on the map.
631
+ * It sets the layer's selectedVehicleId property with the current selected vehicle's id.
632
+ *
633
+ * @private
634
+ * @override
635
+ */
636
+ onFeatureClick(features, layer, coordinate) {
637
+ const [feature] = features;
638
+ let id = null;
639
+ if (feature) {
640
+ id = feature.get('train_id');
641
+ }
642
+ if (this.selectedVehicleId !== id) {
643
+ /** @ignore */
644
+ this.selectedVehicleId = id;
645
+ this.selectedVehicle = feature;
646
+ this.renderTrajectories(true);
647
+ }
648
+ }
704
649
  };
705
- exports.default = RealtimeLayerMixin;
650
+ export default RealtimeLayerMixin;