@wemap/providers 9.0.3 → 9.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/index.js CHANGED
@@ -36,7 +36,6 @@ export { default as Inclination } from './src/providers/inclination/Inclination.
36
36
  export { default as StepDetector } from './src/providers/steps/StepDetector.js';
37
37
  export { default as StraightLineDetector } from './src/providers/steps/StraightLineDetector.js';
38
38
 
39
- export { default as ArCore } from './src/providers/position/relative/ArCore.js';
40
39
  export { default as Pdr } from './src/providers/position/relative/Pdr.js';
41
40
  export { default as GeoRelativePositionFromArCore } from './src/providers/position/relative/GeoRelativePositionFromArCore.js';
42
41
  export { default as GeoRelativePosition } from './src/providers/position/relative/GeoRelativePosition.js';
@@ -46,8 +45,10 @@ export { default as PoleStar } from './src/providers/position/absolute/PoleStar.
46
45
  export { default as Ip } from './src/providers/position/absolute/Ip.js';
47
46
  export { default as AbsolutePosition } from './src/providers/position/absolute/AbsolutePosition.js';
48
47
 
48
+ export { default as ArCore } from './src/providers/vision/ArCore.js';
49
+ export { default as ImageRelocalization } from './src/providers/vision/ImageRelocalization.js';
49
50
  export { default as Vps } from './src/providers/vision/Vps.js';
51
+ export { default as Barcode } from './src/providers/vision/Barcode.js';
50
52
 
51
- export { default as Barcode } from './src/providers/others/Barcode.js';
52
53
  export { default as CameraNative } from './src/providers/others/CameraNative.js';
53
54
  export { default as CameraProjectionMatrix } from './src/providers/others/CameraProjectionMatrix.js';
package/package.json CHANGED
@@ -9,13 +9,13 @@
9
9
  ],
10
10
  "dependencies": {
11
11
  "@wemap/camera": "^9.0.0",
12
- "@wemap/geo": "^9.0.3",
12
+ "@wemap/geo": "^9.0.4",
13
13
  "@wemap/geomagnetism": "^0.1.1",
14
14
  "@wemap/logger": "^9.0.0",
15
- "@wemap/map": "^9.0.3",
15
+ "@wemap/map": "^9.0.4",
16
16
  "@wemap/maths": "^9.0.0",
17
- "@wemap/osm": "^9.0.3",
18
- "@wemap/routers": "^9.0.3",
17
+ "@wemap/osm": "^9.0.4",
18
+ "@wemap/routers": "^9.0.4",
19
19
  "@wemap/utils": "^9.0.0"
20
20
  },
21
21
  "description": "A package using different geoloc systems",
@@ -42,6 +42,6 @@
42
42
  "url": "git+https://github.com/wemap/wemap-modules-js.git"
43
43
  },
44
44
  "type": "module",
45
- "version": "9.0.3",
46
- "gitHead": "ba88e7b5fa491af11700ba59f793ccf571e8f39d"
45
+ "version": "9.0.4",
46
+ "gitHead": "9d6b6776b86ae16dfa3fb71c3303d48a8289ceed"
47
47
  }
@@ -10,7 +10,7 @@ import MapMatchingHandler from './mapmatching/MapMatchingHandler.js';
10
10
 
11
11
  import AbsoluteAttitude from './providers/attitude/absolute/AbsoluteAttitude.js';
12
12
  import AbsolutePosition from './providers/position/absolute/AbsolutePosition.js';
13
- import Barcode from './providers/others/Barcode.js';
13
+ import Barcode from './providers/vision/Barcode.js';
14
14
  import CameraNative from './providers/others/CameraNative.js';
15
15
  import CameraProjectionMatrix from './providers/others/CameraProjectionMatrix.js';
16
16
  import Inclination from './providers/inclination/Inclination.js';
@@ -3,7 +3,7 @@ import EventType from '../../../events/EventType.js';
3
3
  import ProviderState from '../../ProviderState.js';
4
4
 
5
5
  import RelativeAttitudeFromInertial from './RelativeAttitudeFromInertial.js';
6
- import ArCore from '../../position/relative/ArCore.js';
6
+ import ArCore from '../../vision/ArCore.js';
7
7
 
8
8
  /**
9
9
  * Relative attitude provider gives the device attitude in East-North-Up (ENU) frame using
@@ -1,6 +1,6 @@
1
1
  import Provider from '../Provider.js';
2
2
  import EventType from '../../events/EventType.js';
3
- import ArCore from '../position/relative/ArCore.js';
3
+ import ArCore from '../vision/ArCore.js';
4
4
  import ProviderState from '../ProviderState.js';
5
5
 
6
6
  class CameraNative extends Provider {
@@ -1,6 +1,6 @@
1
1
  import Provider from '../Provider.js';
2
2
  import EventType from '../../events/EventType.js';
3
- import ArCore from '../position/relative/ArCore.js';
3
+ import ArCore from '../vision/ArCore.js';
4
4
 
5
5
  class CameraProjectionMatrix extends Provider {
6
6
 
@@ -20,13 +20,6 @@ class AbsolutePosition extends Provider {
20
20
  /** @type {boolean} */
21
21
  static USE_MM_FOR_FEED = true;
22
22
 
23
- // /** @type {number} */
24
- // static POLESTAR_TIME_DISCARD_GNSSWIFI_FROM_LAST_FIX = 3000;
25
-
26
- // /** @type {number} */
27
- // static GNSSWIFI_DISCARD_POLESTAR_IF_ACCURACY_RATIO_BETTER_THAN = 2;
28
-
29
-
30
23
  /** @type {number?} */
31
24
  _gnssWifiProviderId;
32
25
 
@@ -39,9 +32,6 @@ class AbsolutePosition extends Provider {
39
32
  /** @type {boolean?} */
40
33
  _waitUntilNextVpsPosition = false;
41
34
 
42
- // /** @type {?number} */
43
- // _lastPolestarFix;
44
-
45
35
  /**
46
36
  * @override
47
37
  */
@@ -81,45 +71,26 @@ class AbsolutePosition extends Provider {
81
71
  // do nothing
82
72
  });
83
73
 
84
- if (ProvidersOptions.hasVps) {
85
-
86
- this._vpsProviderId = Vps.addEventListener(events => {
87
- this._onAbsolutePosition(events.find(event => event.dataType === EventType.AbsolutePosition));
88
- Vps.removeEventListener(this._vpsProviderId);
89
- this._vpsProviderId = null;
90
- });
91
74
 
92
- } else {
75
+ // GnssWifi
76
+ this._gnssWifiProviderId = GnssWifi.addEventListener(
77
+ events => {
78
+ // bearing from GnssWifi is not reliable for our usecase
79
+ events[0].data.bearing = null;
80
+ this._onAbsolutePosition(events[0], false);
81
+ }
82
+ );
93
83
 
94
- this._gnssWifiProviderId = GnssWifi.addEventListener(
95
- events => {
96
- // const useLevel = false;
97
- // if (typeof this._lastPolestarFix !== 'undefined'
98
- // && TimeUtils.preciseTime() - this._lastPolestarFix < AbsolutePosition.POLESTAR_TIME_DISCARD_GNSSWIFI_FROM_LAST_FIX) {
99
- // // Discard the GNSSWifi position if the last Polestar position is too recent
100
- // console.log(`[Dev] Formula - PS: ${PoleStar.lastEvent.data.accuracy},
101
- // GNSSWifi: ${events[0].data.accuracy},
102
- // ratio: ${PoleStar.lastEvent.data.accuracy / events[0].data.accuracy}`);
103
- // if (PoleStar.lastEvent.data.accuracy / events[0].data.accuracy > AbsolutePosition.GNSSWIFI_DISCARD_POLESTAR_IF_ACCURACY_RATIO_BETTER_THAN) {
104
- // // Except if the accuracy of the Polestar position is really better than the GNSSWifi position
105
- // // In this case we add the level provided by Pole Star to the position
106
- // console.log(`[Dev] Apply PS level ${PoleStar.lastEvent.data.level} to GnssWifi`);
107
- // events[0].data.level = PoleStar.lastEvent.data.level;
108
- // useLevel = true;
109
- // } else {
110
- // console.log('[Dev] Discard GnssWifi fix with accuracy: ', events[0].data.accuracy);
111
- // return;
112
- // }
113
- // }
114
- // console.log('[Dev] Use GnssWifi fix with accuracy: ', events[0].data.accuracy);
115
- // bearing from GnssWifi is not reliable for our usecase
116
- events[0].data.bearing = null;
117
- this._onAbsolutePosition(events[0], false);
118
- }
84
+ // Vps
85
+ if (ProvidersOptions.hasVps) {
86
+ this._vpsProviderId = Vps.addEventListener(
87
+ events => this._onAbsolutePosition(events.find(event => event.dataType === EventType.AbsolutePosition)),
88
+ error => this.notifyError(error),
89
+ false
119
90
  );
120
-
121
91
  }
122
92
 
93
+ // PoleStar
123
94
  if (ProvidersOptions.hasPoleStar) {
124
95
  this._polestarProviderId = PoleStar.addEventListener(events => {
125
96
 
@@ -3,7 +3,7 @@ import { GeoRelativePosition } from '@wemap/geo';
3
3
  import EventType from '../../../events/EventType.js';
4
4
  import AbsoluteAttitude from '../../attitude/absolute/AbsoluteAttitude.js';
5
5
  import Provider from '../../Provider.js';
6
- import ArCore from './ArCore.js';
6
+ import ArCore from '../../vision/ArCore.js';
7
7
 
8
8
  class GeoRelativePositionFromArCore extends Provider {
9
9
 
@@ -5,11 +5,12 @@ import {
5
5
  import { deg2rad } from '@wemap/maths';
6
6
  import { TimeUtils } from '@wemap/utils';
7
7
 
8
- import Provider from '../../Provider.js';
9
- import EventType from '../../../events/EventType.js';
10
- import MissingArCoreError from '../../../errors/MissingArCoreError.js';
11
- import MissingNativeInterfaceError from '../../../errors/MissingNativeInterfaceError.js';
12
- import ProviderState from '../../ProviderState.js';
8
+ import Provider from '../Provider.js';
9
+ import EventType from '../../events/EventType.js';
10
+ import MissingArCoreError from '../../errors/MissingArCoreError.js';
11
+ import MissingNativeInterfaceError from '../../errors/MissingNativeInterfaceError.js';
12
+ import ProviderState from '../ProviderState.js';
13
+
13
14
  class ArCore extends Provider {
14
15
 
15
16
  static Payload = {
@@ -1,6 +1,6 @@
1
1
  import Provider from '../Provider.js';
2
2
  import EventType from '../../events/EventType.js';
3
- import ArCore from '../position/relative/ArCore.js';
3
+ import ArCore from './ArCore.js';
4
4
 
5
5
  class Barcode extends Provider {
6
6
 
@@ -0,0 +1,120 @@
1
+ import { CameraUtils } from '@wemap/camera';
2
+ import { Attitude, Coordinates, UserPosition } from '@wemap/geo';
3
+ import Logger from '@wemap/logger';
4
+ import { TimeUtils } from '@wemap/utils';
5
+
6
+ import VpsMetadata from './VpsMetadata.js';
7
+
8
+ class ImageRelocalization {
9
+
10
+ /**
11
+ * @param {HTMLCanvasElement} imageCanvas
12
+ * @param {{intrinsic:number[], distortions:number[]}} calibration
13
+ * @param {{position: Coordinates, positionAccuracy: number}} coarsePose
14
+ * @returns {object}
15
+ */
16
+ static _prepareRequest(imageCanvas, calibration, coarsePose) {
17
+ const requestTime = TimeUtils.preciseTime() / 1e3;
18
+
19
+ CameraUtils.convertToGrayscale(imageCanvas);
20
+ const reducedImage = CameraUtils.reduceImageSize(imageCanvas, 1280);
21
+ const base64Image = CameraUtils.canvasToBase64(reducedImage);
22
+
23
+ const metadata = new VpsMetadata();
24
+ metadata.time = requestTime;
25
+ metadata.size = { width: reducedImage.width, height: reducedImage.height };
26
+ metadata.calibration = calibration;
27
+ metadata.coarse = coarsePose;
28
+
29
+ return {
30
+ image: base64Image,
31
+ ...metadata.toJson()
32
+ };
33
+ }
34
+
35
+ /**
36
+ * @param {object} json
37
+ * @returns {{userPosition: UserPosition, attitude: Attitude}}
38
+ */
39
+ static _parseJsonResponse(json, requestTime = null) {
40
+
41
+ const attitude = new Attitude([
42
+ json.attitude.w, json.attitude.x,
43
+ json.attitude.y, json.attitude.z
44
+ ], requestTime, 0);
45
+
46
+ const userPosition = new UserPosition(
47
+ json.coordinates.lat,
48
+ json.coordinates.lng,
49
+ json.coordinates.alt,
50
+ typeof json.coordinates.level === 'undefined' ? null : json.coordinates.level,
51
+ requestTime,
52
+ 0,
53
+ attitude.heading
54
+ );
55
+
56
+ return { userPosition, attitude };
57
+ }
58
+
59
+ /**
60
+ * @param {string} endpointUrl
61
+ * @param {HTMLCanvasElement} imageCanvas
62
+ * @param {{intrinsic:number[], distortions:number[]}} calibration
63
+ * @param {{position: Coordinates, positionAccuracy: number}} coarsePose
64
+ * @returns {{userPosition: UserPosition, attitude: Attitude}|null}
65
+ */
66
+ static async relocalize(endpointUrl, imageCanvas, calibration = null, coarsePose = null) {
67
+
68
+ // 1. Prepare the request
69
+ const jsonPayload = this._prepareRequest(imageCanvas, calibration, coarsePose);
70
+
71
+ // 2. Send the request
72
+ let serverResponse;
73
+ try {
74
+ serverResponse = await fetch(endpointUrl, {
75
+ method: 'POST',
76
+ body: JSON.stringify(jsonPayload),
77
+ headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }
78
+ });
79
+ } catch (e) {
80
+ return null;
81
+ }
82
+
83
+ if (serverResponse.status !== 200) {
84
+ return null;
85
+ }
86
+
87
+ const json = await serverResponse.json();
88
+ if (json.error) {
89
+ Logger.warn(json.error);
90
+ return null;
91
+ }
92
+
93
+ // 3. Parse the response
94
+ const { userPosition, attitude } = this._parseJsonResponse(json, jsonPayload.time);
95
+
96
+ return { userPosition, attitude };
97
+ }
98
+
99
+ static getHeadingFromQuaternion(quaternion) {
100
+
101
+ const [qw, qx, qy, qz] = quaternion;
102
+
103
+ const s = Math.sqrt(2) / 2;
104
+ const C = [s * (qw - qx), s * (qw + qx), s * (qy + qz), s * (qz - qy)];
105
+ return -Math.atan2(2 * C[3] * C[0] - 2 * C[2] * C[1], 1 - 2 * C[1] ** 2 - 2 * C[3] ** 2);
106
+
107
+ // ---- Not optimized version ----
108
+
109
+ // const eulerAnglesAR = Rotations.quaternionToEulerZXY(
110
+ // Quaternion.multiply(
111
+ // quaternion,
112
+ // Quaternion.fromAxisAngle([1, 0, 0], Math.PI / 2)
113
+ // )
114
+ // );
115
+
116
+ // return -eulerAnglesAR[0];
117
+ }
118
+ }
119
+
120
+ export default ImageRelocalization;
@@ -1,19 +1,21 @@
1
1
  /* eslint-disable max-statements */
2
- import { SharedCameras, CameraUtils, Camera } from '@wemap/camera';
3
- import { Attitude, UserPosition } from '@wemap/geo';
2
+ import { SharedCameras, Camera } from '@wemap/camera';
3
+ import { Attitude } from '@wemap/geo';
4
4
  import Logger from '@wemap/logger';
5
- import { deg2rad, Quaternion } from '@wemap/maths';
5
+ import { Quaternion, deg2rad } from '@wemap/maths';
6
6
  import { TimeUtils } from '@wemap/utils';
7
7
 
8
8
  import EventType from '../../events/EventType.js';
9
- import ProviderEvent from '../../events/ProviderEvent.js';
10
9
  import Provider from '../Provider.js';
11
10
  import ProviderState from '../ProviderState.js';
11
+ import ImageRelocalization from './ImageRelocalization.js';
12
12
 
13
13
  class Vps extends Provider {
14
14
 
15
15
  static MIN_TIME_BETWEEN_TWO_REQUESTS = 1000;
16
16
 
17
+ static CAMERA_TO_SMARTPHONE_ROT = Quaternion.fromAxisAngle([1, 0, 0], Math.PI);
18
+
17
19
  /** @type {boolean} */
18
20
  _serverError = false;
19
21
 
@@ -25,7 +27,7 @@ class Vps extends Provider {
25
27
 
26
28
  /** @type {?string} */
27
29
  _endpoint = null;
28
- // _endpoint = 'https://vps.maaap.it/wemap';
30
+
29
31
 
30
32
  /**
31
33
  * @override
@@ -119,170 +121,64 @@ class Vps extends Provider {
119
121
 
120
122
  _internalStart = async () => {
121
123
 
122
- this._serverError = false;
123
-
124
124
  if (!this._endpoint) {
125
- this._serverError = true;
126
- Logger.error('VPS endpoint has not been set before calling start()');
127
- this.stop();
125
+ this.notifyError(new Error('VPS endpoint has not been set before calling start()'));
128
126
  return;
129
127
  }
130
128
 
131
129
  while (this.state !== ProviderState.STOPPPED) {
132
130
 
131
+ // 1. Handle the time to wait between two requests (MIN_TIME_BETWEEN_TWO_REQUESTS)
133
132
  if (this.lastEvent) {
134
133
  const diffTime = TimeUtils.preciseTime() - this.lastEvent.data.time * 1e3;
135
134
  const timeToWait = Math.max(0, Vps.MIN_TIME_BETWEEN_TWO_REQUESTS - diffTime);
136
135
  await new Promise(resolve => setTimeout(resolve, timeToWait));
137
136
  }
138
137
 
139
- if (this.state === ProviderState.STOPPPED) {
138
+ // 2. Check if Vps is not stopped and camera is still started
139
+ if (this.state === ProviderState.STOPPPED
140
+ || !this._camera
141
+ || this._camera.state !== Camera.State.STARTED) {
140
142
  break;
141
143
  }
142
144
 
143
- // const url = Vps.SERVICE_URL + Vps.MAP_ID;
145
+ // 3. Get current image from camera and relocalize it.
146
+ const image = await this._camera.currentImage;
147
+ const res = await ImageRelocalization.relocalize(this._endpoint, image);
144
148
 
145
- // 1. Prepare the request
146
- if (!this._camera || this._camera.state !== Camera.State.STARTED) {
147
- break;
149
+ if (!res) {
150
+ continue;
148
151
  }
149
- const payload = await this._prepareRequest();
150
152
 
151
- if (this.state === ProviderState.STOPPPED) {
152
- break;
153
- }
153
+ // 4. If relocalization is successful, apply the transforms
154
+ const enuToCameraRot = res.attitude.quaternion;
155
+ const enuToSmartphoneRot = Quaternion.multiply(
156
+ enuToCameraRot,
157
+ Vps.CAMERA_TO_SMARTPHONE_ROT
158
+ );
154
159
 
155
- // 2. Send the request
156
- const serverResponse = await fetch(this._endpoint, {
157
- method: 'POST',
158
- body: JSON.stringify(payload),
159
- headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }
160
- });
160
+ const deviceQuaternion = Quaternion.multiply(
161
+ Quaternion.fromAxisAngle([0, 0, 1], deg2rad(window.orientation || 0)),
162
+ enuToSmartphoneRot
163
+ );
164
+ const attitude = new Attitude(deviceQuaternion, res.attitude.time, res.attitude.accuracy);
161
165
 
166
+ // 5. Finally, notify the listeners if the VPS is not stopped
162
167
  if (this.state === ProviderState.STOPPPED) {
163
168
  break;
164
169
  }
165
170
 
166
- // 3. Parse the response
167
- const events = await this._parseResponse(serverResponse, this._endpoint, payload.time);
168
- if (this._serverError || this.state === ProviderState.STOPPPED) {
169
- break;
170
- }
171
+ this.notify(
172
+ this.createEvent(EventType.AbsoluteAttitude, attitude),
173
+ this.createEvent(EventType.AbsolutePosition, res.userPosition)
174
+ );
171
175
 
172
- // 4. Notify events
173
- if (events.length) {
174
- this.notify(...events);
175
- }
176
- }
177
-
178
- if (this.state !== ProviderState.STOPPPED) {
179
- this.stop();
180
176
  }
181
177
  }
182
178
 
183
179
  _internalStop = () => {
184
180
  // do nothing
185
181
  }
186
-
187
-
188
- /**
189
- * @returns {Object}
190
- */
191
- async _prepareRequest() {
192
-
193
- const camera = this._camera;
194
-
195
- // Retrieve the image
196
- const image = await camera.currentImage;
197
- const time = TimeUtils.preciseTime() / 1e3;
198
- // TODO: move the grayscale conversion in the currentImage getter
199
- CameraUtils.convertToGrayscale(image);
200
- const reducedImage = CameraUtils.reduceImageSize(image, 1280);
201
- // const reducedImage = CameraUtils.reduceImageSize(image, 720, 720);
202
- const { height, width } = reducedImage;
203
- const base64Image = CameraUtils.canvasToBase64(reducedImage);
204
-
205
- // Retrieve the calibration matrix
206
- const calibration = CameraUtils.createCameraCalibrationFromWidthHeightFov(
207
- width, height, deg2rad(camera.hardwareVerticalFov)
208
- );
209
-
210
- // Build the payload
211
- return {
212
- calibration,
213
- size: [width, height],
214
- image: base64Image,
215
- time
216
- };
217
- }
218
-
219
- /**
220
- * @param {Response} res
221
- * @param {String} url
222
- * @param {Number} requestTime
223
- * @returns {ProviderEvent[]}
224
- */
225
- async _parseResponse(res, url, requestTime) {
226
- if (res.status !== 200) {
227
- Logger.warn(`The VPS server (${url}) has encountered a problem`);
228
- this._serverError = true;
229
- return [];
230
- }
231
-
232
- const json = await res.json();
233
- if (json.error) {
234
- return [];
235
- }
236
-
237
- const { attitude, userPosition } = Vps._parseJsonResponse(json, requestTime);
238
-
239
- const events = [
240
- this.createEvent(EventType.AbsoluteAttitude, attitude),
241
- this.createEvent(EventType.AbsolutePosition, userPosition)
242
- ];
243
-
244
- return events;
245
- }
246
-
247
-
248
- static _parseJsonResponse(json, requestTime) {
249
-
250
- const enuToCameraRot = [
251
- json.attitude.w,
252
- json.attitude.x,
253
- json.attitude.y,
254
- json.attitude.z
255
- ];
256
-
257
- const cameraToSmartphoneRot = Quaternion.fromAxisAngle(
258
- [1, 0, 0],
259
- Math.PI
260
- );
261
- const enuToSmartphoneRot = Quaternion.multiply(
262
- enuToCameraRot,
263
- cameraToSmartphoneRot
264
- );
265
-
266
- const quaternionWithScreenRotation = Quaternion.multiply(
267
- Quaternion.fromAxisAngle([0, 0, 1], deg2rad(window.orientation || 0)),
268
- enuToSmartphoneRot
269
- );
270
-
271
- const attitude = new Attitude(quaternionWithScreenRotation, requestTime, 0);
272
-
273
- const userPosition = new UserPosition(
274
- json.coordinates.lat,
275
- json.coordinates.lng,
276
- json.coordinates.alt,
277
- typeof json.coordinates.level === 'undefined' ? null : json.coordinates.level,
278
- requestTime,
279
- 0,
280
- attitude.heading
281
- );
282
-
283
-
284
- return { userPosition, attitude };
285
- }
286
182
  }
287
183
 
288
184
  export default new Vps();