@wemap/providers 4.0.0 → 4.0.3

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 (66) hide show
  1. package/debug/dist/index.html +1 -0
  2. package/debug/dist/turn-detection.html +19 -0
  3. package/debug/index.js +2 -0
  4. package/debug/src/AbsoluteAttitudeComponent.jsx +3 -10
  5. package/debug/src/StepDetectionComponent.jsx +3 -3
  6. package/debug/src/TurnDetectionComponent.jsx +47 -0
  7. package/debug/src/Utils.js +20 -0
  8. package/package.json +7 -9
  9. package/src/Providers.js +26 -81
  10. package/src/ProvidersInterface.js +5 -6
  11. package/src/errors/AskImuOnDesktopError.js +4 -3
  12. package/src/errors/ContainsIgnoredProviderError.js +4 -3
  13. package/src/errors/GeolocationApiMissingError.js +4 -3
  14. package/src/errors/GeolocationPermissionDeniedError.js +4 -3
  15. package/src/errors/GeolocationPositionUnavailableError.js +4 -3
  16. package/src/errors/IpResolveServerError.js +4 -3
  17. package/src/errors/MissingAccelerometerError.js +3 -3
  18. package/src/errors/MissingArCoreError.js +4 -3
  19. package/src/errors/MissingGyroscopeError.js +3 -3
  20. package/src/errors/MissingNativeInterfaceError.js +4 -3
  21. package/src/errors/MissingSensorError.js +4 -3
  22. package/src/errors/NoProviderFoundError.js +4 -3
  23. package/src/events/EventType.js +5 -1
  24. package/src/events/ProviderEvent.js +7 -0
  25. package/src/events/ProvidersLoggerOld.js +35 -34
  26. package/src/mapmatching/MapMatchingHandler.js +335 -69
  27. package/src/providers/FakeProvider.spec.js +15 -15
  28. package/src/providers/Provider.js +44 -27
  29. package/src/providers/Provider.spec.js +18 -29
  30. package/src/providers/attitude/TurnDectector.js +71 -0
  31. package/src/providers/attitude/absolute/AbsoluteAttitude.js +232 -0
  32. package/src/providers/attitude/absolute/{AbsoluteAttitudeFromBrowserProvider.js → AbsoluteAttitudeFromBrowser.js} +4 -4
  33. package/src/providers/attitude/relative/{RelativeAttitudeProvider.js → RelativeAttitude.js} +5 -8
  34. package/src/providers/attitude/relative/{RelativeAttitudeFromBrowserProvider.js → RelativeAttitudeFromBrowser.js} +4 -5
  35. package/src/providers/attitude/relative/{RelativeAttitudeFromEkfProvider.js → RelativeAttitudeFromEkf.js} +7 -8
  36. package/src/providers/attitude/relative/{RelativeAttitudeFromInertialProvider.js → RelativeAttitudeFromInertial.js} +24 -7
  37. package/src/providers/imu/{AccelerometerProvider.js → Accelerometer.js} +3 -3
  38. package/src/providers/imu/{GyroscopeProvider.js → Gyroscope.js} +3 -3
  39. package/src/providers/imu/HighRotationsDetector.js +62 -0
  40. package/src/providers/imu/{ImuProvider.js → Imu.js} +3 -3
  41. package/src/providers/inclination/{InclinationProvider.js → Inclination.js} +5 -6
  42. package/src/providers/inclination/{InclinationFromAccProvider.js → InclinationFromAcc.js} +4 -3
  43. package/src/providers/inclination/{InclinationFromRelativeAttitudeProvider.js → InclinationFromRelativeAttitude.js} +4 -3
  44. package/src/providers/legacy/helpers/ThugDetector.js +7 -7
  45. package/src/providers/others/{BarcodeProvider.js → Barcode.js} +3 -3
  46. package/src/providers/others/{CameraNativeProvider.js → CameraNative.js} +3 -3
  47. package/src/providers/others/{CameraProjectionMatrixProvider.js → CameraProjectionMatrix.js} +4 -4
  48. package/src/providers/position/absolute/AbsolutePosition.js +217 -0
  49. package/src/providers/position/absolute/{GnssWifiProvider.js → GnssWifi.js} +9 -8
  50. package/src/providers/position/absolute/{IpProvider.js → Ip.js} +2 -2
  51. package/src/providers/position/relative/{ArCoreProvider.js → ArCore.js} +28 -30
  52. package/src/providers/position/relative/{GeoRelativePositionProvider.js → GeoRelativePosition.js} +6 -6
  53. package/src/providers/position/relative/{GeoRelativePositionFromArCoreProvider.js → GeoRelativePositionFromArCore.js} +4 -5
  54. package/src/providers/position/relative/{PdrProvider.js → Pdr.js} +8 -8
  55. package/src/providers/steps/StepDetectionLadetto.js +12 -13
  56. package/src/providers/steps/StepDetectionMinMaxPeaks.js +19 -17
  57. package/src/providers/steps/StepDetectionMinMaxPeaks2.js +17 -18
  58. package/src/providers/steps/{StepDetectionProvider.js → StepDetector.js} +8 -7
  59. package/src/providers/steps/StraightLineDetector.js +80 -0
  60. package/src/smoothers/AttitudeSmoother.js +94 -59
  61. package/src/providers/MetaProvider.js +0 -44
  62. package/src/providers/attitude/absolute/AbsoluteAttitudeFromRelAttProvider.js +0 -132
  63. package/src/providers/attitude/absolute/AbsoluteAttitudeFusedProvider.js +0 -166
  64. package/src/providers/attitude/absolute/AbsoluteAttitudeProvider.js +0 -175
  65. package/src/providers/position/absolute/AbsolutePositionFromRelProvider.js +0 -107
  66. package/src/providers/position/absolute/AbsolutePositionProvider.js +0 -189
@@ -1,83 +1,84 @@
1
1
  import Logger from '@wemap/logger';
2
2
 
3
- let currentId = 0;
4
- const objectsIdMap = new WeakMap();
3
+ class ProvidersLoggerOld {
5
4
 
6
- let pushEvents = {};
7
- let pushEventsRef = {};
5
+ _enabled = false;
8
6
 
9
- let interval;
10
- let initDate;
7
+ currentId = 0;
8
+ objectsIdMap = new WeakMap();
11
9
 
12
- class ProvidersLoggerOld {
10
+ pushEvents = {};
11
+ pushEventsRef = {};
12
+
13
+ interval;
14
+ initDate;
13
15
 
14
- static _enabled = false;
15
16
 
16
- static get enabled() {
17
+ get enabled() {
17
18
  return this._enabled;
18
19
  }
19
20
 
20
- static set enabled(_newVal) {
21
+ set enabled(_newVal) {
21
22
  this._enabled = _newVal;
22
23
  }
23
24
 
24
- static initializeInterval() {
25
+ initializeInterval() {
25
26
 
26
- if (interval) {
27
+ if (this.interval) {
27
28
  return;
28
29
  }
29
30
 
30
- interval = setInterval(() => {
31
+ this.interval = setInterval(() => {
31
32
 
32
- for (const [key, value] of Object.entries(pushEvents)) {
33
- Logger.debug('Received ' + value + ' notifications from ' + pushEventsRef[key].pname + ' last second');
33
+ for (const [key, value] of Object.entries(this.pushEvents)) {
34
+ Logger.debug('Received ' + value + ' notifications from ' + this.pushEventsRef[key].pname + ' last second');
34
35
  }
35
36
 
36
- pushEvents = {};
37
- pushEventsRef = {};
37
+ this.pushEvents = {};
38
+ this.pushEventsRef = {};
38
39
  }, 1000);
39
40
  }
40
41
 
41
- static getObjectId(object) {
42
- if (!objectsIdMap.has(object)) {
43
- objectsIdMap.set(object, ++currentId);
42
+ getObjectId(object) {
43
+ if (!this.objectsIdMap.has(object)) {
44
+ this.objectsIdMap.set(object, ++this.currentId);
44
45
  }
45
- return objectsIdMap.get(object);
46
+ return this.objectsIdMap.get(object);
46
47
  }
47
48
 
48
- static addEvent(object, method) {
49
+ addEvent(object, method) {
49
50
 
50
- if (!ProvidersLoggerOld.enabled) {
51
+ if (!this.enabled) {
51
52
  return;
52
53
  }
53
54
 
54
- if (!initDate) {
55
- initDate = Date.now();
55
+ if (!this.initDate) {
56
+ this.initDate = Date.now();
56
57
  }
57
58
 
58
- ProvidersLoggerOld.initializeInterval();
59
+ this.initializeInterval();
59
60
 
60
- const objectId = ProvidersLoggerOld.getObjectId(object);
61
+ const objectId = this.getObjectId(object);
61
62
  const objectClassName = object.pname;
62
63
 
63
64
  Logger.debug(objectClassName + '[' + objectId + '].' + method);
64
65
  }
65
66
 
66
- static incrementNotifications(object) {
67
+ incrementNotifications(object) {
67
68
 
68
- if (!ProvidersLoggerOld.enabled) {
69
+ if (!this.enabled) {
69
70
  return;
70
71
  }
71
72
 
72
- const objectId = ProvidersLoggerOld.getObjectId(object);
73
+ const objectId = this.getObjectId(object);
73
74
 
74
- let counter = pushEvents[objectId];
75
+ let counter = this.pushEvents[objectId];
75
76
  if (!counter) {
76
77
  counter = 0;
77
- pushEventsRef[objectId] = object;
78
+ this.pushEventsRef[objectId] = object;
78
79
  }
79
- pushEvents[objectId] = counter + 1;
80
+ this.pushEvents[objectId] = counter + 1;
80
81
  }
81
82
 
82
83
  }
83
- export default ProvidersLoggerOld;
84
+ export default new ProvidersLoggerOld();
@@ -1,19 +1,46 @@
1
- import { UserPosition } from '@wemap/geo';
1
+ /* eslint-disable max-statements */
2
2
  import {
3
- MapMatching, Network
3
+ AbsoluteHeading, Edge, Itinerary, MapMatching, Network, Projection, UserPosition
4
4
  } from '@wemap/geo';
5
- import { deg2rad } from '@wemap/maths';
5
+ import { deg2rad, diffAngle } from '@wemap/maths';
6
+ import { TimeUtils } from '@wemap/utils';
6
7
 
7
- import {
8
- AbsolutePosition, AbsoluteAttitudeFused
9
- } from '../Providers.js';
8
+ import EventType from '../events/EventType.js';
9
+ import ProviderEvent from '../events/ProviderEvent.js';
10
+
11
+ import AbsoluteAttitude from '../providers/attitude/absolute/AbsoluteAttitude.js';
12
+ import AbsoluteAttitudeFromBrowser from '../providers/attitude/absolute/AbsoluteAttitudeFromBrowser.js';
13
+ import TurnDectector from '../providers/attitude/TurnDectector.js';
14
+ import Constants from '../providers/Constants.js';
15
+ import AbsolutePosition from '../providers/position/absolute/AbsolutePosition.js';
16
+ import Provider from '../providers/Provider.js';
17
+ import ProviderState from '../providers/ProviderState.js';
18
+ import StepDetector from '../providers/steps/StepDetector.js';
19
+ import StraightLineDetector from '../providers/steps/StraightLineDetector.js';
10
20
  import ProvidersOptions from '../ProvidersOptions.js';
11
21
 
12
- const MM_MAX_ANGLE = deg2rad(20);
13
- const MM_MAX_DIST = 30;
14
- const MM_MIN_DIST = 0;
22
+ class MapMatchingHandler extends Provider {
23
+
24
+ /** @type {number} in radians */
25
+ static MM_MAX_ANGLE = deg2rad(30);
26
+
27
+ /** @type {number} in meters */
28
+ static MM_MAX_DIST = 30;
29
+
30
+ /** @type {number} in meters */
31
+ static MM_MIN_DIST = 0;
32
+
33
+ /** @type {boolean} */
34
+ static ORIENTATION_MATCHING = true;
35
+
36
+ /** @type {boolean} */
37
+ static USE_ITINERARY_START_AS_POSITION = true;
38
+
39
+ /** @type {number} in meters */
40
+ static MM_HUGE_JUMP_DISTANCE = 3;
15
41
 
16
- class MapMatchingHandler {
42
+ /** @type {number} */
43
+ static MIN_STEPS_BETWEEN_ORIENTATION_MATCHING = 3;
17
44
 
18
45
  /** @type {MapMatching} */
19
46
  _mapMatching;
@@ -21,105 +48,344 @@ class MapMatchingHandler {
21
48
  /** @type {number} */
22
49
  _mapMatchingMinDistance;
23
50
 
51
+ /** @type {boolean} */
52
+ _internalProvidersStarted = false;
53
+
54
+ /** @type {number?} */
55
+ _straightLineDetectorProviderId;
56
+
57
+ /** @type {number?} */
58
+ _turnDetectorProviderId;
59
+
60
+ /** @type {number?} */
61
+ _stepDetectorProviderId;
62
+
63
+ /** @type {Projection[]} */
64
+ _projectionsWithAbsAndWithoutRelAttitudeInARow = [];
65
+
66
+ /** @type {number} */
67
+ _countStepsFromLastMatching = 0;
68
+
69
+ constructor() {
70
+ super();
71
+
72
+ this._mapMatching = new MapMatching();
73
+ this._mapMatching.maxDistance = MapMatchingHandler.MM_MAX_DIST;
74
+ this._mapMatching.maxAngleBearing = MapMatchingHandler.MM_MAX_ANGLE;
75
+ this._mapMatchingMinDistance = MapMatchingHandler.MM_MIN_DIST;
76
+ }
77
+
24
78
  /**
25
- * Singleton pattern using reflection.
26
- * @returns {MapMatchingHandler}
79
+ * @override
27
80
  */
28
- static get instance() {
29
- if (!this._instance) {
30
- this._instance = Reflect.construct(this, []);
81
+ start() {
82
+ if (this.network) {
83
+ this._startInternalProviders();
31
84
  }
32
- return this._instance;
33
85
  }
34
86
 
35
- constructor() {
87
+ /**
88
+ * @override
89
+ */
90
+ stop() {
91
+ this._stopInternalProviders();
92
+ }
36
93
 
37
- if (this.constructor._instance) {
38
- throw new Error(`Cannot instantiate ${this.name} twice`);
94
+ _manageStartStop = () => {
95
+ if (this.network && !this._internalProvidersStarted) {
96
+ this._startInternalProviders();
97
+ } else if (!this.network && this._internalProvidersStarted) {
98
+ this._stopInternalProviders();
39
99
  }
100
+ }
40
101
 
41
- this._mapMatching = new MapMatching();
42
- this._mapMatching.maxDistance = MM_MAX_DIST;
43
- this._mapMatching.maxAngleBearing = MM_MAX_ANGLE;
44
- this._mapMatchingMinDistance = MM_MIN_DIST;
102
+ _startInternalProviders() {
103
+
104
+ if (this.enabled && this._internalProvidersStarted) {
105
+ return;
106
+ }
107
+
108
+ this._straightLineDetectorProviderId = StraightLineDetector.addEventListener();
109
+ this._turnDetectorProviderId = TurnDectector.addEventListener();
110
+ this._stepDetectorProviderId = StepDetector.addEventListener(() => (this._countStepsFromLastMatching++));
111
+
112
+ this._internalProvidersStarted = true;
113
+ }
114
+
115
+ _stopInternalProviders() {
116
+
117
+ if (this.enabled && !this._internalProvidersStarted) {
118
+ return;
119
+ }
120
+
121
+ StraightLineDetector.removeEventListener(this._straightLineDetectorProviderId);
122
+ TurnDectector.removeEventListener(this._turnDetectorProviderId);
123
+ StepDetector.removeEventListener(this._stepDetectorProviderId);
124
+
125
+ this._internalProvidersStarted = false;
45
126
  }
46
127
 
47
128
  get enabled() {
48
129
  return ProvidersOptions.useMapMatching;
49
130
  }
50
131
 
51
- /**
52
- * @param {Network} network
53
- */
132
+ /** @type {Network} */
133
+ get network() {
134
+ return this._mapMatching.network;
135
+ }
136
+
137
+ /** @type {Network} */
54
138
  set network(network) {
139
+
55
140
  this._mapMatching.network = network;
141
+ this.notify(this.createEvent(EventType.Network, network));
56
142
 
57
- if (!this.enabled) {
58
- return;
143
+ this._manageStartStop();
144
+
145
+ if (this.canUseMapMatching()) {
146
+ this._notifyPositionFromNetworkInput(network);
59
147
  }
148
+ }
149
+
150
+ /**
151
+ * @returns {boolean}
152
+ */
153
+ canUseMapMatching() {
154
+ return this.enabled && this.network;
155
+ }
156
+
157
+ /**
158
+ * @param {Network} network
159
+ */
160
+ _notifyPositionFromNetworkInput(network) {
60
161
 
61
162
  const lastEvent = AbsolutePosition.lastEvent;
62
- if (network !== null && lastEvent) {
63
- const projectedPosition = this.calcProjection(lastEvent.data, true);
64
- if (projectedPosition) {
65
- const newEvent = lastEvent.clone();
66
- newEvent.data = projectedPosition;
67
- AbsolutePosition.notify(newEvent);
163
+ const lastPosition = lastEvent ? lastEvent.data : null;
164
+
165
+ const notify = (pos) => {
166
+ const newEvent = lastEvent
167
+ ? lastEvent.clone()
168
+ : new ProviderEvent(EventType.AbsolutePosition);
169
+ newEvent.data = pos;
170
+ AbsolutePosition.notify(newEvent);
171
+ };
172
+
173
+ let newPosition;
174
+ if (MapMatchingHandler.USE_ITINERARY_START_AS_POSITION
175
+ && network instanceof Itinerary && network.start) {
176
+
177
+ // In case of an itinerary, use itinerary start as new position,
178
+ // but add the distance between lastPosition and itinerary start for the accuracy
179
+ newPosition = UserPosition.fromCoordinates(network.start);
180
+
181
+ if (lastPosition) {
182
+ newPosition.alt = lastPosition.alt;
183
+ newPosition.time = lastPosition.time;
184
+ newPosition.accuracy = lastPosition.accuracy + newPosition.distanceTo(lastPosition);
185
+ newPosition.bearing = lastPosition.bearing;
186
+ } else {
187
+ newPosition.alt = Constants.DEFAULT_ALTITUDE;
188
+ newPosition.time = TimeUtils.preciseTime();
189
+ newPosition.accuracy = 0;
190
+ newPosition.bearing = network.edges[0].bearing;
68
191
  }
192
+
193
+ // if the distance between itinerary.start and itinerary.nodes[0] is less than MM_MAX_DIST,
194
+ // projection.projection is itinerary.nodes[0]
195
+ const projection = this.getProjection(newPosition, true);
196
+ if (projection) {
197
+ notify(projection.projection);
198
+ // Do not notify for attitude projection because bearing has not been used.
199
+ } else {
200
+ // This means the first position is far from the network and the user has
201
+ // to reach itinerary.nodes[0] before to continue
202
+ notify(newPosition);
203
+ }
204
+ return;
69
205
  }
206
+
207
+ if (!lastPosition) {
208
+ return;
209
+ }
210
+
211
+ newPosition = lastPosition.clone();
212
+ // TODO in function of the lastEvent, if it is from relative or absolute
213
+ }
214
+
215
+ /**
216
+ * @param {ProviderEvent<UserPosition>} newPositionEvent
217
+ */
218
+ notifyPositionFromFeed(newPositionEvent) {
219
+
220
+ const projection = this.getProjection(newPositionEvent.data, true, false);
221
+ if (!projection) {
222
+ AbsolutePosition.notify(newPositionEvent);
223
+ return;
224
+ }
225
+
226
+ AbsolutePosition.notify(this.createEvent(
227
+ EventType.AbsolutePosition,
228
+ projection.projection,
229
+ [newPositionEvent]
230
+ ));
70
231
  }
71
232
 
233
+
72
234
  /**
73
- * @param {UserPosition} position
74
- * @param {boolean} shouldTryMmOnNodes
75
- * @returns {UserPosition}
235
+ * @param {ProviderEvent<UserPosition>} positionEvent
76
236
  */
77
- calcProjection(position, shouldTryMmOnNodes = false) {
237
+ notifyPositionFromAbsolute(positionEvent) {
78
238
 
79
- let projection = null;
80
- let bearingUsedForProjection = false;
239
+ const newPosition = positionEvent.data;
81
240
 
82
- if (!this.enabled || !this._mapMatching.network) {
83
- return null;
241
+ let projectionWithBearing = null;
242
+ if (newPosition.bearing !== null) {
243
+ projectionWithBearing = this.getProjection(newPosition, true, true);
84
244
  }
85
245
 
86
- // Firstly, if new position bearing is known, try to use map-matching with bearing
87
- // That is not always the case, for e.g. the first position returned by the GnssWifi provider.
88
- if (position.bearing !== null) {
89
- projection = this._mapMatching.getProjection(position, true, true);
90
- bearingUsedForProjection = true;
246
+ if (!projectionWithBearing) {
247
+ // Verify if the newPosition is far enough from the network to be used by the system.
248
+ const projectionWithoutBearing = this.getProjection(newPosition, true, false);
249
+ if (!projectionWithoutBearing) {
250
+ // In this case, the newPosition is far enough and can be used safely.
251
+ AbsolutePosition.notify(positionEvent);
252
+ }
253
+ return;
91
254
  }
92
255
 
93
- // Secondly, if map-matching with bearing did not work, try MM on nodes (without bearing).
94
- // Note: this is not allowed for positions from AbsolutePositionFromRel to allow
95
- // user to leave the network.
96
- if (!projection && shouldTryMmOnNodes) {
97
- projection = this._mapMatching.getProjection(position, true, false);
98
- bearingUsedForProjection = false;
256
+ // newPosition must not be used after this line
257
+
258
+ const thisWillBeAHugeJump = projectionWithBearing.distanceFromNearestElement > MapMatchingHandler.MM_HUGE_JUMP_DISTANCE;
259
+
260
+ // In case of a huge jump, be sure the user is in a straight line
261
+ if (thisWillBeAHugeJump && !StraightLineDetector.isStraight()) {
262
+ return;
99
263
  }
100
264
 
101
- // No projection found
102
- if (!projection) {
103
- return null;
265
+ AbsolutePosition.notify(this.createEvent(
266
+ EventType.AbsolutePosition,
267
+ projectionWithBearing.projection,
268
+ [positionEvent]
269
+ ));
270
+
271
+ this.tryOrientationMatching(projectionWithBearing);
272
+
273
+ }
274
+
275
+
276
+ /**
277
+ * @param {ProviderEvent<UserPosition>} positionEvent
278
+ */
279
+ notifyPositionFromRelative(positionEvent) {
280
+
281
+ if (TurnDectector.isTurning()) {
282
+ this._projectionsWithAbsAndWithoutRelAttitudeInARow = [];
283
+ AbsolutePosition.notify(positionEvent);
284
+ return;
285
+ }
286
+
287
+ const newPosition = positionEvent.data;
288
+ const projection = this.getProjection(newPosition, true, true);
289
+
290
+ if (projection) {
291
+ this._projectionsWithAbsAndWithoutRelAttitudeInARow = [];
292
+
293
+ const thisWillBeAHugeJump = projection.distanceFromNearestElement > MapMatchingHandler.MM_HUGE_JUMP_DISTANCE;
294
+
295
+ // In case of a huge jump, be sure the user is in a straight line
296
+ if (thisWillBeAHugeJump && !StraightLineDetector.isStraight()) {
297
+ AbsolutePosition.notify(positionEvent);
298
+ return;
299
+ }
300
+
301
+ AbsolutePosition.notify(this.createEvent(
302
+ EventType.AbsolutePosition,
303
+ projection.projection,
304
+ [positionEvent]
305
+ ));
306
+ this.tryOrientationMatching(projection);
307
+
308
+ } else {
309
+
310
+ // Sometimes, the newPosition.bearing diverges due to the Absolute Attitude offset
311
+ // and the Orientation Matching. So, we created this detector to check if projection
312
+ // with bearing from "AbsoluteAttitudeFromBrowser" is better than the current bearing.
313
+ // /!\ This works only if the user is waking in the same direction than the smartphone orientation /!\
314
+ if (StraightLineDetector.isStraight()) {
315
+
316
+ const testedPosition = newPosition.clone();
317
+ testedPosition.bearing = AbsoluteAttitudeFromBrowser.lastEvent.data.heading;
318
+ const projectionWithAbs = this.getProjection(testedPosition, true, true);
319
+
320
+ if (projectionWithAbs) {
321
+
322
+ this._projectionsWithAbsAndWithoutRelAttitudeInARow.push(projectionWithAbs);
323
+ if (this._projectionsWithAbsAndWithoutRelAttitudeInARow.length < 3) {
324
+ return;
325
+ }
326
+
327
+ AbsolutePosition.notify(this.createEvent(
328
+ EventType.AbsolutePosition,
329
+ projectionWithAbs.projection,
330
+ [positionEvent]
331
+ ));
332
+
333
+ this.tryOrientationMatching(projectionWithAbs);
334
+ return;
335
+ }
336
+ }
337
+
338
+ this._projectionsWithAbsAndWithoutRelAttitudeInARow = [];
339
+
340
+ // If no projection found with both projection methods, simply use the newPosition.
341
+ AbsolutePosition.notify(positionEvent);
104
342
  }
105
343
 
106
- // Do not use projection if it too close from itinerary,
107
- // this allows left/right movements (ie with ArCore)
108
- if (projection.distanceFromNearestElement < this._mapMatchingMinDistance) {
109
- return null;
344
+ }
345
+
346
+ /**
347
+ * @param {Projection} projection
348
+ */
349
+ tryOrientationMatching(projection) {
350
+
351
+ if (!MapMatchingHandler.ORIENTATION_MATCHING) {
352
+ return;
110
353
  }
111
354
 
112
- if (bearingUsedForProjection) {
113
- AbsoluteAttitudeFused.mapMatching(projection);
355
+ if (this.state !== ProviderState.STARTED
356
+ || this._countStepsFromLastMatching < MapMatchingHandler.MIN_STEPS_BETWEEN_ORIENTATION_MATCHING) {
357
+ return;
114
358
  }
115
359
 
116
- // Do not use projection.projection directly, because position has some specific properties like time, bearing and altitude
117
- const projectedPosition = position.clone();
118
- projectedPosition.lat = projection.projection.lat;
119
- projectedPosition.lng = projection.projection.lng;
120
- projectedPosition.level = projection.projection.level;
360
+ const { nearestElement, origin } = projection;
361
+ if (!(nearestElement instanceof Edge)) {
362
+ return;
363
+ }
364
+
365
+ let matchingDirection;
366
+ const matchingDirectionAngle1 = diffAngle(nearestElement.bearing, origin.bearing);
367
+ const matchingDirectionAngle2 = diffAngle(nearestElement.bearing + Math.PI, origin.bearing);
368
+
369
+ if (Math.abs(matchingDirectionAngle1) < Math.abs(matchingDirectionAngle2)) {
370
+ matchingDirection = nearestElement.bearing;
371
+ } else {
372
+ matchingDirection = (nearestElement.bearing + Math.PI) % (2 * Math.PI);
373
+ }
374
+
375
+ const matchedHeading = new AbsoluteHeading(
376
+ matchingDirection,
377
+ origin.time,
378
+ 0
379
+ // Math.min(Math.abs(matchingDirectionAngle1), Math.abs(matchingDirectionAngle2))
380
+ );
381
+
382
+ AbsoluteAttitude.feed(new ProviderEvent(EventType.AbsoluteHeading, matchedHeading));
383
+
384
+ this._countStepsFromLastMatching = 0;
385
+ }
121
386
 
122
- return projectedPosition;
387
+ getProjection(position, useDistance, useBearing) {
388
+ return this._mapMatching.getProjection(position, useDistance, useBearing);
123
389
  }
124
390
 
125
391
  get maxDistance() {
@@ -147,4 +413,4 @@ class MapMatchingHandler {
147
413
  }
148
414
  }
149
415
 
150
- export default MapMatchingHandler.instance;
416
+ export default new MapMatchingHandler();
@@ -3,13 +3,15 @@ import { UserPosition } from '@wemap/geo';
3
3
  import Provider from './Provider.js';
4
4
  import EventType from '../events/EventType.js';
5
5
 
6
- class FakeProvider1 extends Provider {
6
+ class FakeProvider1Clazz extends Provider {
7
7
  static get pname() {
8
8
  return 'FakeProvider1';
9
9
  }
10
10
  }
11
+ const FakeProvider1 = new FakeProvider1Clazz();
12
+ export { FakeProvider1 };
11
13
 
12
- class FakeProvider2 extends Provider {
14
+ class FakeProvider2Clazz extends Provider {
13
15
  static get pname() {
14
16
  return 'FakeProvider2';
15
17
  }
@@ -19,8 +21,10 @@ class FakeProvider2 extends Provider {
19
21
  start() { }
20
22
  stop() { }
21
23
  }
24
+ const FakeProvider2 = new FakeProvider2Clazz();
25
+ export { FakeProvider2 };
22
26
 
23
- class FakeProvider3 extends Provider {
27
+ class FakeProvider3Clazz extends Provider {
24
28
  static get pname() {
25
29
  return 'FakeProvider3';
26
30
  }
@@ -34,31 +38,27 @@ class FakeProvider3 extends Provider {
34
38
  clearInterval(this.intervalId);
35
39
  }
36
40
  }
41
+ const FakeProvider3 = new FakeProvider3Clazz();
42
+ export { FakeProvider3 };
37
43
 
38
- class FakeProvider4 extends Provider {
44
+ class FakeProvider4Clazz extends Provider {
39
45
 
40
46
  static get pname() {
41
47
  return 'FakeProvider4';
42
48
  }
43
49
 
44
- constructor() {
45
- super();
46
- this.fp3 = FakeProvider3.instance;
47
- }
48
50
  get _availability() {
49
- return this.fp3.availability;
51
+ return FakeProvider3.availability;
50
52
  }
51
53
  start() {
52
- this.fp3id = this.fp3.addEventListener(
54
+ this.fp3id = FakeProvider3.addEventListener(
53
55
  events => this.notify(events[0].clone()),
54
56
  this.notifyError.bind(this)
55
57
  );
56
58
  }
57
59
  stop() {
58
- this.fp3.removeEventListener(this.fp3id);
60
+ FakeProvider3.removeEventListener(this.fp3id);
59
61
  }
60
62
  }
61
-
62
- export {
63
- FakeProvider1, FakeProvider2, FakeProvider3, FakeProvider4
64
- };
63
+ const FakeProvider4 = new FakeProvider4Clazz();
64
+ export { FakeProvider4 };