@wemap/providers 4.0.1 → 4.0.4
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/debug/dist/index.html +1 -0
- package/debug/dist/turn-detection.html +19 -0
- package/debug/index.js +2 -0
- package/debug/src/AbsoluteAttitudeComponent.jsx +0 -10
- package/debug/src/StepDetectionComponent.jsx +3 -3
- package/debug/src/TurnDetectionComponent.jsx +47 -0
- package/debug/src/Utils.js +24 -0
- package/package.json +7 -9
- package/src/Providers.js +5 -4
- package/src/ProvidersInterface.js +5 -6
- package/src/events/EventType.js +5 -1
- package/src/events/ProviderEvent.js +7 -0
- package/src/mapmatching/MapMatchingHandler.js +370 -53
- package/src/providers/Provider.js +42 -8
- package/src/providers/Provider.spec.js +3 -4
- package/src/providers/attitude/TurnDectector.js +71 -0
- package/src/providers/attitude/absolute/AbsoluteAttitude.js +123 -65
- package/src/providers/attitude/relative/RelativeAttitude.js +1 -3
- package/src/providers/attitude/relative/RelativeAttitudeFromBrowser.js +1 -2
- package/src/providers/attitude/relative/RelativeAttitudeFromEkf.js +1 -2
- package/src/providers/attitude/relative/RelativeAttitudeFromInertial.js +19 -2
- package/src/providers/imu/HighRotationsDetector.js +62 -0
- package/src/providers/inclination/Inclination.js +2 -2
- package/src/providers/others/CameraNative.js +2 -2
- package/src/providers/others/CameraProjectionMatrix.js +2 -2
- package/src/providers/position/absolute/AbsolutePosition.js +112 -82
- package/src/providers/position/relative/GeoRelativePosition.js +2 -2
- package/src/providers/position/relative/Pdr.js +4 -4
- package/src/providers/steps/{StepDetection.js → StepDetector.js} +4 -4
- package/src/providers/steps/StraightLineDetector.js +84 -0
- package/src/smoothers/AttitudeSmoother.js +94 -59
- package/src/providers/MetaProvider.js +0 -44
- package/src/providers/attitude/absolute/AbsoluteAttitudeFromRelAtt.js +0 -133
- package/src/providers/attitude/absolute/AbsoluteAttitudeFused.js +0 -166
- package/src/providers/position/absolute/AbsolutePositionFromRel.js +0 -106
|
@@ -1,30 +1,76 @@
|
|
|
1
|
+
/* eslint-disable max-statements */
|
|
1
2
|
import {
|
|
2
|
-
MapMatching, Network, UserPosition
|
|
3
|
+
AbsoluteHeading, Edge, Itinerary, MapMatching, Network, Projection, UserPosition
|
|
3
4
|
} from '@wemap/geo';
|
|
4
|
-
import { deg2rad } from '@wemap/maths';
|
|
5
|
+
import { deg2rad, diffAngle } from '@wemap/maths';
|
|
6
|
+
import { TimeUtils } from '@wemap/utils';
|
|
5
7
|
|
|
6
|
-
import
|
|
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';
|
|
7
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';
|
|
8
20
|
import ProvidersOptions from '../ProvidersOptions.js';
|
|
9
21
|
|
|
10
|
-
class MapMatchingHandler {
|
|
22
|
+
class MapMatchingHandler extends Provider {
|
|
23
|
+
|
|
24
|
+
/** @type {number} in radians */
|
|
25
|
+
static MM_MAX_ANGLE = deg2rad(30);
|
|
11
26
|
|
|
12
|
-
|
|
27
|
+
/** @type {number} in meters */
|
|
13
28
|
static MM_MAX_DIST = 30;
|
|
29
|
+
|
|
30
|
+
/** @type {number} in meters */
|
|
14
31
|
static MM_MIN_DIST = 0;
|
|
15
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;
|
|
41
|
+
|
|
42
|
+
/** @type {number} */
|
|
43
|
+
static MIN_STEPS_BETWEEN_ORIENTATION_MATCHING = 3;
|
|
44
|
+
|
|
45
|
+
/** @type {number} */
|
|
46
|
+
static MIN_STEPS_FOR_ORIENTATION_MATCHING = 5;
|
|
47
|
+
|
|
16
48
|
/** @type {MapMatching} */
|
|
17
49
|
_mapMatching;
|
|
18
50
|
|
|
19
51
|
/** @type {number} */
|
|
20
52
|
_mapMatchingMinDistance;
|
|
21
53
|
|
|
54
|
+
/** @type {boolean} */
|
|
55
|
+
_internalProvidersStarted = false;
|
|
22
56
|
|
|
23
|
-
|
|
57
|
+
/** @type {number?} */
|
|
58
|
+
_straightLineDetectorProviderId;
|
|
24
59
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
60
|
+
/** @type {number?} */
|
|
61
|
+
_turnDetectorProviderId;
|
|
62
|
+
|
|
63
|
+
/** @type {number?} */
|
|
64
|
+
_stepDetectorProviderId;
|
|
65
|
+
|
|
66
|
+
/** @type {Projection[]} */
|
|
67
|
+
_projectionsWithAbsAndWithoutRelAttitudeInARow = [];
|
|
68
|
+
|
|
69
|
+
/** @type {number} */
|
|
70
|
+
_countStepsFromLastMatching = 0;
|
|
71
|
+
|
|
72
|
+
constructor() {
|
|
73
|
+
super();
|
|
28
74
|
|
|
29
75
|
this._mapMatching = new MapMatching();
|
|
30
76
|
this._mapMatching.maxDistance = MapMatchingHandler.MM_MAX_DIST;
|
|
@@ -32,82 +78,353 @@ class MapMatchingHandler {
|
|
|
32
78
|
this._mapMatchingMinDistance = MapMatchingHandler.MM_MIN_DIST;
|
|
33
79
|
}
|
|
34
80
|
|
|
81
|
+
/**
|
|
82
|
+
* @override
|
|
83
|
+
*/
|
|
84
|
+
start() {
|
|
85
|
+
if (this.network) {
|
|
86
|
+
this._startInternalProviders();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @override
|
|
92
|
+
*/
|
|
93
|
+
stop() {
|
|
94
|
+
this._stopInternalProviders();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
_manageStartStop = () => {
|
|
98
|
+
if (this.network && !this._internalProvidersStarted) {
|
|
99
|
+
this._startInternalProviders();
|
|
100
|
+
} else if (!this.network && this._internalProvidersStarted) {
|
|
101
|
+
this._stopInternalProviders();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
_startInternalProviders() {
|
|
106
|
+
|
|
107
|
+
if (this.enabled && this._internalProvidersStarted) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
this._straightLineDetectorProviderId = StraightLineDetector.addEventListener();
|
|
112
|
+
this._turnDetectorProviderId = TurnDectector.addEventListener();
|
|
113
|
+
this._stepDetectorProviderId = StepDetector.addEventListener(() => (this._countStepsFromLastMatching++));
|
|
114
|
+
|
|
115
|
+
this._internalProvidersStarted = true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
_stopInternalProviders() {
|
|
119
|
+
|
|
120
|
+
if (this.enabled && !this._internalProvidersStarted) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
StraightLineDetector.removeEventListener(this._straightLineDetectorProviderId);
|
|
125
|
+
TurnDectector.removeEventListener(this._turnDetectorProviderId);
|
|
126
|
+
StepDetector.removeEventListener(this._stepDetectorProviderId);
|
|
127
|
+
|
|
128
|
+
this._internalProvidersStarted = false;
|
|
129
|
+
}
|
|
130
|
+
|
|
35
131
|
get enabled() {
|
|
36
132
|
return ProvidersOptions.useMapMatching;
|
|
37
133
|
}
|
|
38
134
|
|
|
135
|
+
/** @type {Network} */
|
|
136
|
+
get network() {
|
|
137
|
+
return this._mapMatching.network;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** @type {Network} */
|
|
141
|
+
set network(network) {
|
|
142
|
+
|
|
143
|
+
this._mapMatching.network = network;
|
|
144
|
+
this.notify(this.createEvent(EventType.Network, network));
|
|
145
|
+
|
|
146
|
+
this._manageStartStop();
|
|
147
|
+
|
|
148
|
+
if (this.canUseMapMatching()) {
|
|
149
|
+
this._notifyPositionFromNetworkInput(network);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* @returns {boolean}
|
|
155
|
+
*/
|
|
156
|
+
canUseMapMatching() {
|
|
157
|
+
return this.enabled && this.network;
|
|
158
|
+
}
|
|
159
|
+
|
|
39
160
|
/**
|
|
40
161
|
* @param {Network} network
|
|
41
162
|
*/
|
|
42
|
-
|
|
43
|
-
|
|
163
|
+
_notifyPositionFromNetworkInput(network) {
|
|
164
|
+
|
|
165
|
+
const lastEvent = AbsolutePosition.lastEvent;
|
|
166
|
+
const lastPosition = lastEvent ? lastEvent.data : null;
|
|
167
|
+
|
|
168
|
+
const notify = (pos) => {
|
|
169
|
+
const newEvent = lastEvent
|
|
170
|
+
? lastEvent.clone()
|
|
171
|
+
: new ProviderEvent(EventType.AbsolutePosition);
|
|
172
|
+
newEvent.data = pos;
|
|
173
|
+
AbsolutePosition.notify(newEvent);
|
|
174
|
+
};
|
|
44
175
|
|
|
45
|
-
|
|
176
|
+
let newPosition;
|
|
177
|
+
if (MapMatchingHandler.USE_ITINERARY_START_AS_POSITION
|
|
178
|
+
&& network instanceof Itinerary && network.start) {
|
|
179
|
+
|
|
180
|
+
// In case of an itinerary, use itinerary start as new position,
|
|
181
|
+
// but add the distance between lastPosition and itinerary start for the accuracy
|
|
182
|
+
newPosition = UserPosition.fromCoordinates(network.start);
|
|
183
|
+
|
|
184
|
+
if (lastPosition) {
|
|
185
|
+
newPosition.alt = lastPosition.alt;
|
|
186
|
+
newPosition.time = lastPosition.time;
|
|
187
|
+
newPosition.accuracy = lastPosition.accuracy + newPosition.distanceTo(lastPosition);
|
|
188
|
+
newPosition.bearing = lastPosition.bearing;
|
|
189
|
+
} else {
|
|
190
|
+
newPosition.alt = Constants.DEFAULT_ALTITUDE;
|
|
191
|
+
newPosition.time = TimeUtils.preciseTime();
|
|
192
|
+
newPosition.accuracy = 0;
|
|
193
|
+
newPosition.bearing = network.edges[0].bearing;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// if the distance between itinerary.start and itinerary.nodes[0] is less than MM_MAX_DIST,
|
|
197
|
+
// projection.projection is itinerary.nodes[0]
|
|
198
|
+
const projection = this.getProjection(newPosition, true);
|
|
199
|
+
if (projection) {
|
|
200
|
+
notify(projection.projection);
|
|
201
|
+
// Do not notify for attitude projection because bearing has not been used.
|
|
202
|
+
} else {
|
|
203
|
+
// This means the first position is far from the network and the user has
|
|
204
|
+
// to reach itinerary.nodes[0] before to continue
|
|
205
|
+
notify(newPosition);
|
|
206
|
+
}
|
|
46
207
|
return;
|
|
47
208
|
}
|
|
48
209
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
210
|
+
if (!lastPosition) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
newPosition = lastPosition.clone();
|
|
215
|
+
// TODO in function of the lastEvent, if it is from relative or absolute
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* @param {ProviderEvent<UserPosition>} newPositionEvent
|
|
220
|
+
*/
|
|
221
|
+
notifyPositionFromFeed(newPositionEvent) {
|
|
222
|
+
|
|
223
|
+
const projection = this.getProjection(newPositionEvent.data, true, false);
|
|
224
|
+
if (!projection) {
|
|
225
|
+
AbsolutePosition.notify(newPositionEvent);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
AbsolutePosition.notify(this.createEvent(
|
|
230
|
+
EventType.AbsolutePosition,
|
|
231
|
+
projection.projection,
|
|
232
|
+
[newPositionEvent]
|
|
233
|
+
));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* @param {ProviderEvent<UserPosition>} positionEvent
|
|
239
|
+
*/
|
|
240
|
+
notifyPositionFromAbsolute(positionEvent) {
|
|
241
|
+
|
|
242
|
+
const newPosition = positionEvent.data;
|
|
243
|
+
|
|
244
|
+
let projectionWithBearing = null;
|
|
245
|
+
if (newPosition.bearing !== null) {
|
|
246
|
+
projectionWithBearing = this.getProjection(newPosition, true, true);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (!projectionWithBearing) {
|
|
250
|
+
// Verify if the newPosition is far enough from the network to be used by the system.
|
|
251
|
+
const projectionWithoutBearing = this.getProjection(newPosition, true, false);
|
|
252
|
+
if (!projectionWithoutBearing) {
|
|
253
|
+
// In this case, the newPosition is far enough and can be used safely.
|
|
254
|
+
AbsolutePosition.notify(positionEvent);
|
|
56
255
|
}
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// newPosition must not be used after this line
|
|
260
|
+
|
|
261
|
+
const thisWillBeAHugeJump = projectionWithBearing.distanceFromNearestElement > MapMatchingHandler.MM_HUGE_JUMP_DISTANCE;
|
|
262
|
+
|
|
263
|
+
// In case of a huge jump, be sure the user is in a straight line
|
|
264
|
+
if (thisWillBeAHugeJump && !StraightLineDetector.isStraight()) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Detector to avoid big jumps in the wrong direction
|
|
269
|
+
if (thisWillBeAHugeJump && this._detectWrongBigJump(projectionWithBearing)) {
|
|
270
|
+
return;
|
|
57
271
|
}
|
|
272
|
+
|
|
273
|
+
AbsolutePosition.notify(this.createEvent(
|
|
274
|
+
EventType.AbsolutePosition,
|
|
275
|
+
projectionWithBearing.projection,
|
|
276
|
+
[positionEvent]
|
|
277
|
+
));
|
|
278
|
+
|
|
279
|
+
this.tryOrientationMatching(projectionWithBearing);
|
|
280
|
+
|
|
58
281
|
}
|
|
59
282
|
|
|
283
|
+
|
|
60
284
|
/**
|
|
61
|
-
* @param {UserPosition}
|
|
62
|
-
* @param {boolean} shouldTryMmOnNodes
|
|
63
|
-
* @returns {UserPosition}
|
|
285
|
+
* @param {ProviderEvent<UserPosition>} positionEvent
|
|
64
286
|
*/
|
|
65
|
-
|
|
287
|
+
notifyPositionFromRelative(positionEvent) {
|
|
288
|
+
|
|
289
|
+
if (TurnDectector.isTurning()) {
|
|
290
|
+
this._projectionsWithAbsAndWithoutRelAttitudeInARow = [];
|
|
291
|
+
AbsolutePosition.notify(positionEvent);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const newPosition = positionEvent.data;
|
|
296
|
+
const projection = this.getProjection(newPosition, true, true);
|
|
297
|
+
|
|
298
|
+
if (projection) {
|
|
299
|
+
this._projectionsWithAbsAndWithoutRelAttitudeInARow = [];
|
|
66
300
|
|
|
67
|
-
|
|
68
|
-
let bearingUsedForProjection = false;
|
|
301
|
+
const thisWillBeAHugeJump = projection.distanceFromNearestElement > MapMatchingHandler.MM_HUGE_JUMP_DISTANCE;
|
|
69
302
|
|
|
70
|
-
|
|
71
|
-
|
|
303
|
+
// In case of a huge jump, be sure the user is in a straight line
|
|
304
|
+
if (thisWillBeAHugeJump && !StraightLineDetector.isStraight()) {
|
|
305
|
+
AbsolutePosition.notify(positionEvent);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Detector to avoid big jumps in the wrong direction
|
|
310
|
+
if (thisWillBeAHugeJump && this._detectWrongBigJump(projection)) {
|
|
311
|
+
AbsolutePosition.notify(positionEvent);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
AbsolutePosition.notify(this.createEvent(
|
|
316
|
+
EventType.AbsolutePosition,
|
|
317
|
+
projection.projection,
|
|
318
|
+
[positionEvent]
|
|
319
|
+
));
|
|
320
|
+
this.tryOrientationMatching(projection);
|
|
321
|
+
|
|
322
|
+
} else {
|
|
323
|
+
|
|
324
|
+
// Sometimes, the newPosition.bearing diverges due to the Absolute Attitude offset
|
|
325
|
+
// and the Orientation Matching. So, we created this detector to check if projection
|
|
326
|
+
// with bearing from "AbsoluteAttitudeFromBrowser" is better than the current bearing.
|
|
327
|
+
// /!\ This works only if the user is waking in the same direction than the smartphone orientation /!\
|
|
328
|
+
if (StraightLineDetector.isStraight()) {
|
|
329
|
+
|
|
330
|
+
const testedPosition = newPosition.clone();
|
|
331
|
+
testedPosition.bearing = AbsoluteAttitudeFromBrowser.lastEvent.data.heading;
|
|
332
|
+
const projectionWithAbs = this.getProjection(testedPosition, true, true);
|
|
333
|
+
|
|
334
|
+
if (projectionWithAbs) {
|
|
335
|
+
|
|
336
|
+
this._projectionsWithAbsAndWithoutRelAttitudeInARow.push(projectionWithAbs);
|
|
337
|
+
if (this._projectionsWithAbsAndWithoutRelAttitudeInARow.length < 3) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
AbsolutePosition.notify(this.createEvent(
|
|
342
|
+
EventType.AbsolutePosition,
|
|
343
|
+
projectionWithAbs.projection,
|
|
344
|
+
[positionEvent]
|
|
345
|
+
));
|
|
346
|
+
|
|
347
|
+
this.tryOrientationMatching(projectionWithAbs);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
this._projectionsWithAbsAndWithoutRelAttitudeInARow = [];
|
|
353
|
+
|
|
354
|
+
// If no projection found with both projection methods, simply use the newPosition.
|
|
355
|
+
AbsolutePosition.notify(positionEvent);
|
|
72
356
|
}
|
|
73
357
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* @param {Projection} projection
|
|
362
|
+
*/
|
|
363
|
+
_detectWrongBigJump(projection) {
|
|
364
|
+
|
|
365
|
+
if (this.network instanceof Itinerary && AbsolutePosition.lastEvent) {
|
|
366
|
+
const itinerary = this.network;
|
|
367
|
+
const infoPrevious = itinerary.getInfo(AbsolutePosition.lastEvent.data);
|
|
368
|
+
const infoProjection = itinerary.getInfo(projection.projection);
|
|
369
|
+
if (infoPrevious
|
|
370
|
+
&& infoProjection
|
|
371
|
+
&& infoPrevious.traveledDistance > infoProjection.traveledDistance
|
|
372
|
+
&& (infoPrevious.traveledDistance - infoProjection.traveledDistance) > projection.origin.accuracy
|
|
373
|
+
&& projection.distanceFromNearestElement > projection.origin.accuracy
|
|
374
|
+
&& projection.origin.distanceTo(AbsolutePosition.lastEvent.data) < projection.origin.accuracy + AbsolutePosition.lastEvent.data.accuracy) {
|
|
375
|
+
|
|
376
|
+
return true;
|
|
377
|
+
}
|
|
79
378
|
}
|
|
80
379
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* @param {Projection} projection
|
|
386
|
+
*/
|
|
387
|
+
tryOrientationMatching(projection) {
|
|
388
|
+
|
|
389
|
+
if (!MapMatchingHandler.ORIENTATION_MATCHING) {
|
|
390
|
+
return;
|
|
87
391
|
}
|
|
88
392
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
393
|
+
if (this.state !== ProviderState.STARTED
|
|
394
|
+
|| this._countStepsFromLastMatching < MapMatchingHandler.MIN_STEPS_BETWEEN_ORIENTATION_MATCHING
|
|
395
|
+
|| StraightLineDetector.numStepsDetectedFromLastTurn < MapMatchingHandler.MIN_STEPS_FOR_ORIENTATION_MATCHING) {
|
|
396
|
+
return;
|
|
92
397
|
}
|
|
93
398
|
|
|
94
|
-
|
|
95
|
-
|
|
399
|
+
const { nearestElement, origin } = projection;
|
|
400
|
+
if (!(nearestElement instanceof Edge)) {
|
|
401
|
+
return;
|
|
96
402
|
}
|
|
97
403
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
404
|
+
let matchingDirection;
|
|
405
|
+
const matchingDirectionAngle1 = diffAngle(nearestElement.bearing, origin.bearing);
|
|
406
|
+
const matchingDirectionAngle2 = diffAngle(nearestElement.bearing + Math.PI, origin.bearing);
|
|
407
|
+
|
|
408
|
+
if (Math.abs(matchingDirectionAngle1) < Math.abs(matchingDirectionAngle2)) {
|
|
409
|
+
matchingDirection = nearestElement.bearing;
|
|
410
|
+
} else {
|
|
411
|
+
matchingDirection = (nearestElement.bearing + Math.PI) % (2 * Math.PI);
|
|
102
412
|
}
|
|
103
413
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
414
|
+
const matchedHeading = new AbsoluteHeading(
|
|
415
|
+
matchingDirection,
|
|
416
|
+
origin.time,
|
|
417
|
+
0
|
|
418
|
+
// Math.min(Math.abs(matchingDirectionAngle1), Math.abs(matchingDirectionAngle2))
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
AbsoluteAttitude.feed(new ProviderEvent(EventType.AbsoluteHeading, matchedHeading));
|
|
422
|
+
|
|
423
|
+
this._countStepsFromLastMatching = 0;
|
|
424
|
+
}
|
|
109
425
|
|
|
110
|
-
|
|
426
|
+
getProjection(position, useDistance, useBearing) {
|
|
427
|
+
return this._mapMatching.getProjection(position, useDistance, useBearing);
|
|
111
428
|
}
|
|
112
429
|
|
|
113
430
|
get maxDistance() {
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import noop from 'lodash.noop';
|
|
2
|
-
|
|
3
1
|
import EventType from '../events/EventType.js';
|
|
4
2
|
import ProviderEvent from '../events/ProviderEvent.js';
|
|
5
3
|
import ProvidersLoggerOld from '../events/ProvidersLoggerOld.js';
|
|
@@ -14,11 +12,22 @@ import ProviderState from './ProviderState.js';
|
|
|
14
12
|
*/
|
|
15
13
|
class Provider {
|
|
16
14
|
|
|
15
|
+
/** @type {number} */
|
|
17
16
|
static _callbackUniqueId = 0;
|
|
17
|
+
|
|
18
|
+
/** @type {number} */
|
|
18
19
|
static _uniqueId = 1;
|
|
19
20
|
|
|
21
|
+
|
|
22
|
+
/** @type {number} */
|
|
23
|
+
id;
|
|
24
|
+
|
|
25
|
+
/** @type {number} */
|
|
20
26
|
state = ProviderState.STOPPPED;
|
|
21
27
|
|
|
28
|
+
/** @type {ProviderEvent[]} */
|
|
29
|
+
_lastEvents = null;
|
|
30
|
+
|
|
22
31
|
_eventsCallbacks = [];
|
|
23
32
|
_monitoringCallbacks = [];
|
|
24
33
|
|
|
@@ -38,12 +47,13 @@ class Provider {
|
|
|
38
47
|
|
|
39
48
|
/**
|
|
40
49
|
* Get the provider name
|
|
41
|
-
* @
|
|
50
|
+
* @type {String} the provider name
|
|
42
51
|
*/
|
|
43
52
|
static get pname() {
|
|
44
53
|
return 'Unknown';
|
|
45
54
|
}
|
|
46
55
|
|
|
56
|
+
/** @type {String} */
|
|
47
57
|
get pname() {
|
|
48
58
|
return this.constructor.pname;
|
|
49
59
|
}
|
|
@@ -59,7 +69,7 @@ class Provider {
|
|
|
59
69
|
}
|
|
60
70
|
|
|
61
71
|
/**
|
|
62
|
-
* @
|
|
72
|
+
* @type {Promise} returns an availability promise
|
|
63
73
|
*/
|
|
64
74
|
get availability() {
|
|
65
75
|
if (ProvidersOptions.ignoreProviders.includes(this.pname)) {
|
|
@@ -69,13 +79,14 @@ class Provider {
|
|
|
69
79
|
return this._availability;
|
|
70
80
|
}
|
|
71
81
|
|
|
82
|
+
/** @type {Promise} */
|
|
72
83
|
get _availability() {
|
|
73
84
|
return Promise.resolve();
|
|
74
85
|
}
|
|
75
86
|
|
|
76
87
|
/**
|
|
77
88
|
* Create an event from current provider
|
|
78
|
-
* @param {
|
|
89
|
+
* @param {String} dataType type of ProviderEvent (from EventType)
|
|
79
90
|
* @param {Object} data data exported to ProviderEvent
|
|
80
91
|
* @returns {ProviderEvent}
|
|
81
92
|
* @protected
|
|
@@ -119,7 +130,7 @@ class Provider {
|
|
|
119
130
|
* @param {Boolean} startIfNecessary
|
|
120
131
|
* @returns {Number}
|
|
121
132
|
*/
|
|
122
|
-
addEventListener(onEvents =
|
|
133
|
+
addEventListener(onEvents = () => {}, onError = () => {}, startIfNecessary = true) {
|
|
123
134
|
const id = ++Provider._callbackUniqueId;
|
|
124
135
|
|
|
125
136
|
/**
|
|
@@ -168,7 +179,7 @@ class Provider {
|
|
|
168
179
|
this.notifyError(e);
|
|
169
180
|
})
|
|
170
181
|
// notifyError can throw an error if onStop is not defined
|
|
171
|
-
.catch(() =>
|
|
182
|
+
.catch(() => {});
|
|
172
183
|
|
|
173
184
|
return id;
|
|
174
185
|
}
|
|
@@ -221,7 +232,7 @@ class Provider {
|
|
|
221
232
|
* @param {Function} onStopped
|
|
222
233
|
* @returns {Number}
|
|
223
234
|
*/
|
|
224
|
-
addMonitoringListener(onStarted =
|
|
235
|
+
addMonitoringListener(onStarted = () => {}, onStopped = () => {}) {
|
|
225
236
|
const id = ++this.constructor._callbackUniqueId;
|
|
226
237
|
this._monitoringCallbacks.push({
|
|
227
238
|
id,
|
|
@@ -269,6 +280,9 @@ class Provider {
|
|
|
269
280
|
|
|
270
281
|
// Notify callbacks
|
|
271
282
|
this._eventsCallbacks.forEach(({ onEvents }) => onEvents(events));
|
|
283
|
+
|
|
284
|
+
// Keep a trace of the last events.
|
|
285
|
+
this._lastEvents = events;
|
|
272
286
|
}
|
|
273
287
|
|
|
274
288
|
/**
|
|
@@ -285,6 +299,26 @@ class Provider {
|
|
|
285
299
|
onError(error);
|
|
286
300
|
});
|
|
287
301
|
}
|
|
302
|
+
|
|
303
|
+
/** @type {ProviderEvent} */
|
|
304
|
+
get lastEvent() {
|
|
305
|
+
return this._lastEvents ? this._lastEvents[0] : null;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** @type {ProviderEvent[]} */
|
|
309
|
+
get lastEvents() {
|
|
310
|
+
return this._lastEvents;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Input data from external interface
|
|
315
|
+
* @param {ProviderEvent} event
|
|
316
|
+
* @param {EventType} eventType
|
|
317
|
+
*/
|
|
318
|
+
// eslint-disable-next-line no-unused-vars
|
|
319
|
+
feed(event, eventType) {
|
|
320
|
+
throw new Error('Not implemented');
|
|
321
|
+
}
|
|
288
322
|
}
|
|
289
323
|
|
|
290
324
|
export default Provider;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/* eslint-disable max-nested-callbacks */
|
|
2
2
|
import chai from 'chai';
|
|
3
3
|
import chaiAsPromised from 'chai-as-promised';
|
|
4
|
-
import noop from 'lodash.noop';
|
|
5
4
|
|
|
6
5
|
import Logger from '@wemap/logger';
|
|
7
6
|
|
|
@@ -55,7 +54,7 @@ describe('Provider', () => {
|
|
|
55
54
|
* checkAvailabilityOnStart = true
|
|
56
55
|
*/
|
|
57
56
|
let errored = false;
|
|
58
|
-
FakeProvider2.addEventListener(
|
|
57
|
+
FakeProvider2.addEventListener(() => {}, () => (errored = true));
|
|
59
58
|
|
|
60
59
|
await sleep(2);
|
|
61
60
|
|
|
@@ -69,7 +68,7 @@ describe('Provider', () => {
|
|
|
69
68
|
ProvidersOptions.checkAvailabilityOnStart = false;
|
|
70
69
|
|
|
71
70
|
errored = false;
|
|
72
|
-
const id = FakeProvider2.addEventListener(
|
|
71
|
+
const id = FakeProvider2.addEventListener(() => {}, () => (errored = true));
|
|
73
72
|
|
|
74
73
|
await sleep(2);
|
|
75
74
|
|
|
@@ -112,7 +111,7 @@ describe('Provider', () => {
|
|
|
112
111
|
|
|
113
112
|
it('start/stop', async () => {
|
|
114
113
|
let errored = false;
|
|
115
|
-
FakeProvider1.addEventListener(
|
|
114
|
+
FakeProvider1.addEventListener(() => {}, (errored = true));
|
|
116
115
|
await sleep(2);
|
|
117
116
|
expect(errored).is.true;
|
|
118
117
|
|