@wemap/positioning 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,186 @@
1
+ /* eslint-disable max-statements */
2
+ import { Attitude } from '@wemap/geo';
3
+
4
+ import Provider from '../Provider';
5
+ import EventType from '../../events/EventType';
6
+ import ArCoreProvider from './ArCoreProvider';
7
+ import {
8
+ Quaternion, Vector3
9
+ } from '@wemap/maths';
10
+ import ImuProvider from '../others/ImuProvider';
11
+ import ProviderError from '../../events/ProviderError';
12
+
13
+ /**
14
+ * Pose provider is the provider used by the PositioningHandler. It uses the best fusion
15
+ * of what he can and provides an AbsoluteAttitude and an AbsolutePosition as an output.
16
+ */
17
+ class ArCoreAbsoluteProvider extends Provider {
18
+
19
+ /**
20
+ * @override
21
+ */
22
+ constructor(onEvent, onError, options) {
23
+ super(onEvent, onError, options);
24
+
25
+ this.arCoreProvider = new ArCoreProvider(
26
+ e => this.onArCoreEvent(e),
27
+ e => this.onArCoreError(e),
28
+ options);
29
+
30
+ this.imuProvider = new ImuProvider(
31
+ this.onImuEvent,
32
+ this.onImuError,
33
+ Object.assign(options || {}, { require: [EventType.AngularRate] })
34
+ );
35
+ }
36
+
37
+ /**
38
+ * @override
39
+ */
40
+ static get displayName() {
41
+ return 'ArCore absolute provider';
42
+ }
43
+
44
+ /**
45
+ * @override
46
+ */
47
+ static get eventsType() {
48
+ return [EventType.AbsoluteAttitude, EventType.AbsolutePosition];
49
+ }
50
+
51
+ /**
52
+ * Return the list of required providers
53
+ */
54
+ static get requiredProviders() {
55
+ return [ArCoreProvider, ImuProvider];
56
+ }
57
+
58
+ /**
59
+ * @override
60
+ */
61
+ startInternal() {
62
+ this.arCoreProvider.start();
63
+ this.imuProvider.start();
64
+ }
65
+
66
+ /**
67
+ * @override
68
+ */
69
+ stopInternal() {
70
+ this.arCoreProvider.stop();
71
+ this.imuProvider.stop();
72
+ }
73
+
74
+ /**
75
+ * @override
76
+ */
77
+ static checkAvailabilityErrors() {
78
+ return ArCoreProvider.checkAvailabilityErrors().concat(
79
+ ImuProvider.checkAvailabilityErrors()
80
+ );
81
+ }
82
+
83
+ onArCoreEvent = events => {
84
+ events.forEach(event => {
85
+ if (event.dataType === EventType.RelativeAttitude) {
86
+
87
+ // const previousAttitude = this.relativeAttitude;
88
+ this.relativeAttitude = event.data;
89
+
90
+ /**
91
+ * The two following blocks cannot be called in the same call because this.waitingForceHeading
92
+ * if exists, is called before the first this.relativeAttitude assigment. So, this.waitingForceHeading
93
+ * and previousAttitude should not be defined in the same time.
94
+ */
95
+
96
+ if (typeof this.waitingForceHeading !== 'undefined') {
97
+ /**
98
+ * waitingForceHeading is set by setHeading() if this.relativeAttitude does not exist
99
+ * At this moment, this.relativeAttitude is defined, so we can call setHeading again
100
+ */
101
+ this.setHeading(this.waitingForceHeading);
102
+ delete this.waitingForceHeading;
103
+
104
+ }
105
+
106
+ // if (previousAttitude && this.absoluteAttitude) {
107
+ // /**
108
+ // * Sometimes ArCore has "orientation jumps" around z-axis.
109
+ // * Here we try to detect and remove them.
110
+ // */
111
+ // const dist = Quaternion.distance(event.data.quaternion, previousAttitude.quaternion);
112
+ // if (typeof this.angularRate !== 'undefined') {
113
+ // const velocity = Vector3.norm(this.angularRate);
114
+ // if (dist > deg2rad(10) && velocity < deg2rad(4)) {
115
+ // this.setHeading(this.absoluteAttitude.heading);
116
+ // }
117
+ // }
118
+ // }
119
+
120
+ if (this.offsetQuat) {
121
+ const absoluteQuaternion = Quaternion.multiply(this.offsetQuat, this.relativeAttitude.quaternion);
122
+ this.absoluteAttitude = new Attitude(absoluteQuaternion);
123
+ this.notify(this.createEvent(EventType.AbsoluteAttitude, this.absoluteAttitude, event.timestamp));
124
+ }
125
+ }
126
+
127
+ if (event.dataType === EventType.RelativePosition) {
128
+
129
+ if (this.oldRelativePos && this.offsetAngle
130
+ && !ArCoreAbsoluteProvider.positionEquals(this.oldRelativePos, event.data)) {
131
+
132
+ const diffPos = Vector3.subtract(event.data, this.oldRelativePos);
133
+ const dist = Math.sqrt(diffPos[0] ** 2 + diffPos[2] ** 2);
134
+ const bearing = Math.atan2(diffPos[0], -diffPos[2]) - this.offsetAngle;
135
+ this.position = this.position.destinationPoint(dist, bearing, diffPos[1]);
136
+ this.notify(this.createEvent(EventType.AbsolutePosition, this.position, event.timestamp));
137
+ }
138
+
139
+ this.oldRelativePos = event.data;
140
+ }
141
+ });
142
+ }
143
+
144
+ static positionEquals(pos1, pos2) {
145
+ return pos1[0] === pos2[0] && pos1[1] === pos2[1] && pos1[2] === pos2[2];
146
+ }
147
+
148
+ onArCoreError = arCoreErrors => {
149
+ this.notifyError(...(
150
+ ProviderError.modifyArrayDataType(arCoreErrors, EventType.AbsoluteAttitude).concat(
151
+ ProviderError.modifyArrayDataType(arCoreErrors, EventType.AbsolutePosition)
152
+ ))
153
+ );
154
+ };
155
+
156
+ onImuEvent = events => {
157
+ this.angularRate = events[0].data;
158
+ }
159
+
160
+ onImuError = imuErrors => {
161
+ this.notifyError(...(
162
+ ProviderError.modifyArrayDataType(imuErrors, EventType.AbsoluteAttitude).concat(
163
+ ProviderError.modifyArrayDataType(imuErrors, EventType.AbsolutePosition)
164
+ ))
165
+ );
166
+ }
167
+
168
+ setHeading(heading) {
169
+
170
+ if (!this.relativeAttitude) {
171
+ this.waitingForceHeading = heading;
172
+ return;
173
+ }
174
+
175
+ this.offsetAngle = this.relativeAttitude.heading - heading;
176
+ this.offsetQuat = Quaternion.fromAxisAngle([0, 0, 1], this.offsetAngle);
177
+ }
178
+
179
+ setPosition(position) {
180
+ this.position = position.clone();
181
+ this.position.provider = this.constructor.name;
182
+ this.notify(this.createEvent(EventType.AbsolutePosition, this.position));
183
+ }
184
+ }
185
+
186
+ export default ArCoreAbsoluteProvider;
@@ -3,6 +3,7 @@ import { Attitude } from '@wemap/geo';
3
3
  import Provider from '../Provider';
4
4
  import EventType from '../../events/EventType';
5
5
  import MissingArCoreError from '../../errors/MissingArCoreError';
6
+ import MissingNativeInterfaceError from '../../errors/MissingNativeInterfaceError';
6
7
 
7
8
  /**
8
9
  * Pose provider is the provider used by the PositioningHandler. It uses the best fusion
@@ -10,14 +11,6 @@ import MissingArCoreError from '../../errors/MissingArCoreError';
10
11
  */
11
12
  class ArCoreProvider extends Provider {
12
13
 
13
- /**
14
- * @override
15
- */
16
- constructor(onEvent, onError, options) {
17
- super(onEvent, onError, options);
18
-
19
- }
20
-
21
14
  /**
22
15
  * @override
23
16
  */
@@ -43,12 +36,9 @@ class ArCoreProvider extends Provider {
43
36
  * @override
44
37
  */
45
38
  startInternal() {
46
- const nativeProvider = this.constructor.getNativeProvider();
47
- if (Array.isArray(nativeProvider)) {
48
- this.notifyError(nativeProvider);
49
- return;
39
+ if (ArCoreProvider.nativeProvider) {
40
+ ArCoreProvider.nativeProvider.start();
50
41
  }
51
- nativeProvider.start();
52
42
 
53
43
  this.pullDataLoop();
54
44
  }
@@ -57,12 +47,9 @@ class ArCoreProvider extends Provider {
57
47
  * @override
58
48
  */
59
49
  stopInternal() {
60
- const nativeProvider = this.constructor.getNativeProvider();
61
- if (Array.isArray(nativeProvider)) {
62
- this.notifyError(nativeProvider);
63
- return;
50
+ if (ArCoreProvider.nativeProvider) {
51
+ ArCoreProvider.nativeProvider.stop();
64
52
  }
65
- nativeProvider.stop();
66
53
  }
67
54
 
68
55
  pullDataLoop = () => {
@@ -71,13 +58,7 @@ class ArCoreProvider extends Provider {
71
58
  return;
72
59
  }
73
60
 
74
- const nativeProvider = this.constructor.getNativeProvider();
75
- if (Array.isArray(nativeProvider)) {
76
- this.notifyError(...nativeProvider);
77
- return;
78
- }
79
-
80
- const pose = JSON.parse(nativeProvider.getInfo());
61
+ const pose = JSON.parse(ArCoreProvider.nativeProvider.getInfo());
81
62
  if (pose.length !== 0) {
82
63
  const attitude = new Attitude(pose.slice(0, 4));
83
64
  const position = pose.slice(4, 7);
@@ -90,17 +71,22 @@ class ArCoreProvider extends Provider {
90
71
  requestAnimationFrame(this.pullDataLoop);
91
72
  }
92
73
 
93
- static getNativeProvider() {
94
- const nativeInterface = this.getNativeInterface();
74
+ static get nativeProvider() {
75
+
76
+ if (!this._nativeProvider) {
77
+
78
+ if (!this.nativeInterface) {
79
+ throw new MissingNativeInterfaceError();
80
+ }
81
+
82
+ this._nativeProvider = this.nativeInterface.getArCoreProvider();
83
+ if (!this._nativeProvider) {
84
+ throw new MissingArCoreError();
85
+ }
95
86
 
96
- if (!nativeInterface) {
97
- return [
98
- this.createMissingNativeInterfaceError(EventType.RelativeAttitude),
99
- this.createMissingNativeInterfaceError(EventType.RelativePosition)
100
- ];
101
87
  }
102
88
 
103
- return nativeInterface.getArCoreProvider();
89
+ return this._nativeProvider;
104
90
  }
105
91
 
106
92
  /**
@@ -108,19 +94,21 @@ class ArCoreProvider extends Provider {
108
94
  */
109
95
  static checkAvailabilityErrors() {
110
96
 
111
- const nativeProvider = this.getNativeProvider();
112
- if (Array.isArray(nativeProvider)) {
113
- return nativeProvider;
114
- }
97
+ try {
98
+ const nativeProvider = this.nativeProvider;
99
+
100
+ if (!nativeProvider.checkAvailability()) {
101
+ throw new MissingArCoreError();
102
+ }
115
103
 
116
- if (nativeProvider.checkAvailability()) {
117
- return [];
104
+ } catch (e) {
105
+ return [
106
+ this.createError(EventType.RelativeAttitude, e),
107
+ this.createError(EventType.RelativePosition, e)
108
+ ];
118
109
  }
119
110
 
120
- return [
121
- this.createError(EventType.RelativeAttitude, new MissingArCoreError()),
122
- this.createError(EventType.RelativePosition, new MissingArCoreError())
123
- ];
111
+ return [];
124
112
  }
125
113
  }
126
114
 
@@ -84,7 +84,7 @@ class GnssWifiPdrProvider extends MapMatchingProvider {
84
84
  onPdrEvent(pdrEvent) {
85
85
  pdrEvent.forEach(event => {
86
86
  if (event.dataType === EventType.AbsolutePosition) {
87
- this.location = event.data;
87
+ this.position = event.data;
88
88
  } else if (event.dataType === EventType.AbsoluteAttitude) {
89
89
  this.attitude = event.data;
90
90
  }
@@ -98,39 +98,39 @@ class GnssWifiPdrProvider extends MapMatchingProvider {
98
98
  onGnssWifiEvent(events) {
99
99
 
100
100
  const gnssWifiEvent = events[0];
101
- const location = gnssWifiEvent.data;
101
+ const position = gnssWifiEvent.data;
102
102
 
103
103
  // This should be called to update True North / Magnetic North declination
104
- this.absoluteAttitudeProvider.setLocation(location);
104
+ this.absoluteAttitudeProvider.setPosition(position);
105
105
 
106
- if (location.accuracy > GPF_ACCURACY) {
106
+ if (position.accuracy > GPF_ACCURACY) {
107
107
  return;
108
108
  }
109
109
 
110
- this.gnssLocation = location.clone();
111
- this.gnssLocation.alt = Constants.DEFAULT_ALTITUDE;
110
+ this.gnssPosition = position.clone();
111
+ this.gnssPosition.alt = Constants.DEFAULT_ALTITUDE;
112
112
 
113
- if (!this.location || this.location
114
- && this.location.distanceTo(this.gnssLocation) > GPF_DISTANCE) {
113
+ if (!this.position || this.position
114
+ && this.position.distanceTo(this.gnssPosition) > GPF_DISTANCE) {
115
115
 
116
116
  if (!this.mapMatching || !this.attitude) {
117
- this.pdrProvider.setLocation(this.gnssLocation);
117
+ this.pdrProvider.setPosition(this.gnssPosition);
118
118
  } else {
119
119
 
120
- this.gnssLocation.bearing = this.attitude.headingDegrees;
121
- const projection = this.mapMatching.getProjection(this.gnssLocation);
120
+ this.gnssPosition.bearing = this.attitude.headingDegrees;
121
+ const projection = this.mapMatching.getProjection(this.gnssPosition);
122
122
 
123
123
  if (projection && projection.projection) {
124
124
 
125
- // Create a new location from projection and new GNSS location.
126
- const projectedLocation = WGS84UserPosition.fromWGS84(projection.projection, this.gnssLocation);
127
- this.pdrProvider.setLocation(projectedLocation);
125
+ // Create a new position from projection and new GNSS position.
126
+ const projectedPosition = WGS84UserPosition.fromWGS84(projection.projection, this.gnssPosition);
127
+ this.pdrProvider.setPosition(projectedPosition);
128
128
 
129
129
  // // If nearest element is an edge, use its orientation to set heading
130
130
  // if (projection.nearestElement instanceof Edge) {
131
131
  // const edgeBearing = projection.nearestElement.bearing;
132
- // const diff1 = MathUtils.diffAngle(MathUtils.deg2rad(this.gnssLocation.bearing), edgeBearing);
133
- // const diff2 = MathUtils.diffAngle(MathUtils.deg2rad(this.gnssLocation.bearing), edgeBearing + Math.PI);
132
+ // const diff1 = MathUtils.diffAngle(MathUtils.deg2rad(this.gnssPosition.bearing), edgeBearing);
133
+ // const diff2 = MathUtils.diffAngle(MathUtils.deg2rad(this.gnssPosition.bearing), edgeBearing + Math.PI);
134
134
  // this.pdrProvider.setHeading(diff1 < diff2 ? edgeBearing : edgeBearing + Math.PI);
135
135
  // }
136
136
 
@@ -139,7 +139,7 @@ class GnssWifiPdrProvider extends MapMatchingProvider {
139
139
  }
140
140
 
141
141
  } else {
142
- this.pdrProvider.setLocation(this.gnssLocation);
142
+ this.pdrProvider.setPosition(this.gnssPosition);
143
143
  }
144
144
  }
145
145
  }
@@ -208,9 +208,9 @@ class GnssWifiPdrProvider extends MapMatchingProvider {
208
208
 
209
209
  // When the first itinerary is received, first or second node (depending on MM_GNSS_DIST) is used as a starting point. No map-matching is needed here as router already provide the projection in the itinerary (node2 is node1 projection on OSRM network).
210
210
 
211
- if (!this.gnssLocation
211
+ if (!this.gnssPosition
212
212
  || itinerary.length < 2
213
- || !itinerary.points[0].equalsTo(this.gnssLocation)) {
213
+ || !itinerary.points[0].equalsTo(this.gnssPosition)) {
214
214
  console.warn('Itinerary has not been calculated from GnssWifiPdrProvider and these is not recommanded');
215
215
  }
216
216
 
@@ -222,7 +222,7 @@ class GnssWifiPdrProvider extends MapMatchingProvider {
222
222
  }
223
223
  const startPoint = WGS84UserPosition.fromWGS84(startEdge.node1);
224
224
  startPoint.alt = Constants.DEFAULT_ALTITUDE;
225
- this.pdrProvider.setLocation(startPoint);
225
+ this.pdrProvider.setPosition(startPoint);
226
226
  this.pdrProvider.setStepDetectionLockerOrientation(startEdge.getBearing());
227
227
  }
228
228
 
@@ -19,7 +19,7 @@ class PoseProvider extends Provider {
19
19
 
20
20
  this.absoluteAttitudeProvider = new AbsoluteAttitudeProvider(onEvent, e => this.onAbsoluteAttitudeError(e));
21
21
  this.gnssWifiProvider = new GnssWifiProvider((events) => {
22
- this.absoluteAttitudeProvider.setLocation(events[0].data);
22
+ this.absoluteAttitudeProvider.setPosition(events[0].data);
23
23
  onEvent(events);
24
24
  }, e => this.onGnssWifiError(e));
25
25
  }
@@ -32,10 +32,10 @@ const DEFAULT_OPTIONS = {
32
32
 
33
33
  class PdrProvider extends MapMatchingProvider {
34
34
 
35
- // pdrLocation can be different from this.location
36
- // pdrLocation is raw location calculated by PdrProvider where
37
- // this.location is smoothed location.
38
- pdrLocation = null;
35
+ // pdrPosition can be different from this.position
36
+ // pdrPosition is raw position calculated by PdrProvider where
37
+ // this.position is smoothed position.
38
+ pdrPosition = null;
39
39
 
40
40
  /**
41
41
  * @override
@@ -127,18 +127,18 @@ class PdrProvider extends MapMatchingProvider {
127
127
  this.relativeAttitudeProvider.setOffset(heading);
128
128
  }
129
129
 
130
- setLocation(location) {
131
- this.pdrLocation = location;
130
+ setPosition(position) {
131
+ this.pdrPosition = position;
132
132
 
133
133
  if (this.options.smoother) {
134
- this.smoother.generateNextLocations(location, true);
135
- // this.smoother.pullLocation(location.time) should never return null
136
- this.location = this.smoother.pullLocation(location.time);
134
+ this.smoother.generateNextPositions(position, true);
135
+ // this.smoother.pullPosition(position.time) should never return null
136
+ this.position = this.smoother.pullPosition(position.time);
137
137
  } else {
138
- this.location = location.clone();
138
+ this.position = position.clone();
139
139
  }
140
140
 
141
- this.notify(this.createEvent(EventType.AbsolutePosition, this.location));
141
+ this.notify(this.createEvent(EventType.AbsolutePosition, this.position));
142
142
  }
143
143
 
144
144
  /**
@@ -163,8 +163,8 @@ class PdrProvider extends MapMatchingProvider {
163
163
  return;
164
164
  }
165
165
 
166
- if (!this.pdrLocation) {
167
- // We should wait at least an input location for PDR
166
+ if (!this.pdrPosition) {
167
+ // We should wait at least an input position for PDR
168
168
  return;
169
169
  }
170
170
 
@@ -201,36 +201,36 @@ class PdrProvider extends MapMatchingProvider {
201
201
 
202
202
  if (stepDetected) {
203
203
 
204
- this.pdrLocation = this.calculateNewLocation(this.pdrLocation, timestamp,
204
+ this.pdrPosition = this.calculateNewPosition(this.pdrPosition, timestamp,
205
205
  heading, this.stepDetection.lastStepSize);
206
206
 
207
207
  /**
208
208
  * Update current_position
209
209
  */
210
210
  if (this.options.smoother) {
211
- this.smoother.generateNextLocations(this.pdrLocation);
212
- const newLocation = this.smoother.pullLocation(timestamp);
213
- // At this time, newLocation can be null if a new step has been detected
211
+ this.smoother.generateNextPositions(this.pdrPosition);
212
+ const newPosition = this.smoother.pullPosition(timestamp);
213
+ // At this time, newPosition can be null if a new step has been detected
214
214
  // and smoother has not been completely consumed.
215
- if (newLocation) {
216
- this.location = newLocation;
215
+ if (newPosition) {
216
+ this.position = newPosition;
217
217
  }
218
218
  } else {
219
- this.location = this.pdrLocation;
219
+ this.position = this.pdrPosition;
220
220
  }
221
221
 
222
222
  } else if (this.options.smoother) {
223
- // If no step is detected, we pull last known location from smoother until now (timestamp).
224
- const smoothedLocation = this.smoother.pullLocation(timestamp);
225
- if (!smoothedLocation) {
223
+ // If no step is detected, we pull last known position from smoother until now (timestamp).
224
+ const smoothedPosition = this.smoother.pullPosition(timestamp);
225
+ if (!smoothedPosition) {
226
226
  return;
227
227
  }
228
- this.location = smoothedLocation;
228
+ this.position = smoothedPosition;
229
229
  } else {
230
230
  return;
231
231
  }
232
232
 
233
- this.notify(this.createEvent(EventType.AbsolutePosition, this.location));
233
+ this.notify(this.createEvent(EventType.AbsolutePosition, this.position));
234
234
  }
235
235
 
236
236
  onProviderError(errors) {
@@ -254,39 +254,39 @@ class PdrProvider extends MapMatchingProvider {
254
254
  );
255
255
  }
256
256
 
257
- calculateNewLocation(previousLocation, timestamp, heading, stepSize) {
257
+ calculateNewPosition(previousPosition, timestamp, heading, stepSize) {
258
258
 
259
259
  /**
260
260
  * Compute new_position
261
261
  */
262
- const newLocationWithoutMM = previousLocation.clone();
263
- newLocationWithoutMM.move(stepSize, heading);
264
- newLocationWithoutMM.bearing = rad2deg(heading);
265
- newLocationWithoutMM.time = timestamp;
262
+ const newPositionWithoutMM = previousPosition.clone();
263
+ newPositionWithoutMM.move(stepSize, heading);
264
+ newPositionWithoutMM.bearing = rad2deg(heading);
265
+ newPositionWithoutMM.time = timestamp;
266
266
 
267
267
  if (!this.mapMatching) {
268
- return newLocationWithoutMM;
268
+ return newPositionWithoutMM;
269
269
  }
270
270
 
271
- const projection = this.mapMatching.getProjection(newLocationWithoutMM);
271
+ const projection = this.mapMatching.getProjection(newPositionWithoutMM);
272
272
 
273
273
  if (!projection || !projection.projection) {
274
- return newLocationWithoutMM;
274
+ return newPositionWithoutMM;
275
275
  }
276
276
 
277
277
  if (projection.distanceFromNearestElement < stepSize) {
278
- return WGS84UserPosition.fromWGS84(projection.projection, newLocationWithoutMM);
278
+ return WGS84UserPosition.fromWGS84(projection.projection, newPositionWithoutMM);
279
279
  }
280
280
 
281
281
  /**
282
282
  * Update new_position
283
283
  */
284
284
  const smoothedDistance = projection.distanceFromNearestElement * MM_CONV_SPEED;
285
- const smoothedBearing = previousLocation.bearingTo(projection.projection);
286
- const smoothedLocation = previousLocation.clone();
287
- smoothedLocation.move(smoothedDistance, smoothedBearing);
285
+ const smoothedBearing = previousPosition.bearingTo(projection.projection);
286
+ const smoothedPosition = previousPosition.clone();
287
+ smoothedPosition.move(smoothedDistance, smoothedBearing);
288
288
 
289
- return WGS84UserPosition.fromWGS84(smoothedLocation, newLocationWithoutMM);
289
+ return WGS84UserPosition.fromWGS84(smoothedPosition, newPositionWithoutMM);
290
290
  }
291
291
 
292
292