@wemap/providers 9.2.0 → 10.0.0-alpha.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.
@@ -1,9 +1,12 @@
1
+ import UAParser from 'ua-parser-js';
2
+
1
3
  import { CameraUtils } from '@wemap/camera';
2
4
  import { Attitude, Coordinates, UserPosition } from '@wemap/geo';
3
5
  import Logger from '@wemap/logger';
4
6
  import { TimeUtils } from '@wemap/utils';
5
7
 
6
- import VpsMetadata from './VpsMetadata.js';
8
+ import VpsRequest from './VpsRequest.js';
9
+ import VpsResponse from './VpsResponse.js';
7
10
 
8
11
  class ImageRelocalization {
9
12
 
@@ -11,49 +14,24 @@ class ImageRelocalization {
11
14
  * @param {HTMLCanvasElement} imageCanvas
12
15
  * @param {{intrinsic:number[], distortions:number[]}} calibration
13
16
  * @param {{position: Coordinates, positionAccuracy: number}} coarsePose
14
- * @returns {object}
17
+ * @returns {VpsRequest}
15
18
  */
16
19
  static _prepareRequest(imageCanvas, calibration, coarsePose) {
17
- const requestTime = TimeUtils.preciseTime() / 1e3;
20
+ const request = new VpsRequest();
21
+ const { metadata } = request;
22
+
23
+ metadata.time = TimeUtils.preciseTime() / 1e3;
18
24
 
19
25
  CameraUtils.convertToGrayscale(imageCanvas);
20
26
  const reducedImage = CameraUtils.reduceImageSize(imageCanvas, 1280);
21
- const base64Image = CameraUtils.canvasToBase64(reducedImage);
27
+ request.image = reducedImage;
22
28
 
23
- const metadata = new VpsMetadata();
24
- metadata.time = requestTime;
25
29
  metadata.size = { width: reducedImage.width, height: reducedImage.height };
26
30
  metadata.calibration = calibration;
27
31
  metadata.coarse = coarsePose;
32
+ metadata.userAgent = (new UAParser()).getResult();
28
33
 
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 };
34
+ return request;
57
35
  }
58
36
 
59
37
  /**
@@ -66,14 +44,14 @@ class ImageRelocalization {
66
44
  static async relocalize(endpointUrl, imageCanvas, calibration = null, coarsePose = null) {
67
45
 
68
46
  // 1. Prepare the request
69
- const jsonPayload = this._prepareRequest(imageCanvas, calibration, coarsePose);
47
+ const vpsRequest = this._prepareRequest(imageCanvas, calibration, coarsePose);
70
48
 
71
49
  // 2. Send the request
72
50
  let serverResponse;
73
51
  try {
74
52
  serverResponse = await fetch(endpointUrl, {
75
53
  method: 'POST',
76
- body: JSON.stringify(jsonPayload),
54
+ body: JSON.stringify(vpsRequest.toJson()),
77
55
  headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }
78
56
  });
79
57
  } catch (e) {
@@ -91,9 +69,7 @@ class ImageRelocalization {
91
69
  }
92
70
 
93
71
  // 3. Parse the response
94
- const { userPosition, attitude } = this._parseJsonResponse(json, jsonPayload.time);
95
-
96
- return { userPosition, attitude };
72
+ return VpsResponse.fromJson(json);
97
73
  }
98
74
 
99
75
  static getHeadingFromQuaternion(quaternion) {
@@ -5,20 +5,15 @@ import Logger from '@wemap/logger';
5
5
  import { Quaternion, deg2rad } from '@wemap/maths';
6
6
  import { TimeUtils } from '@wemap/utils';
7
7
 
8
- import EventType from '../../events/EventType.js';
9
- import Inclination from '../inclination/Inclination.js';
10
- import Provider from '../Provider.js';
11
- import ProviderState from '../ProviderState.js';
8
+ import EventType from '../../../events/EventType.js';
9
+ import Provider from '../../Provider.js';
10
+ import ProviderState from '../../ProviderState.js';
12
11
  import ImageRelocalization from './ImageRelocalization.js';
13
12
 
14
13
  class Vps extends Provider {
15
14
 
16
15
  static MIN_TIME_BETWEEN_TWO_REQUESTS = 1000;
17
16
 
18
- static DEFAULT_MIN_INCLINATION_FOR_REQUEST = deg2rad(60);
19
-
20
- static DEFAULT_WAIT_TIME_MIN_INCLINATION_FOR_REQUEST = 200;
21
-
22
17
  static CAMERA_TO_SMARTPHONE_ROT = Quaternion.fromAxisAngle([1, 0, 0], Math.PI);
23
18
 
24
19
  /** @type {boolean} */
@@ -33,14 +28,6 @@ class Vps extends Provider {
33
28
  /** @type {?string} */
34
29
  _endpoint = null;
35
30
 
36
- /** @type {?number} */
37
- _inclinationProviderId = null;
38
-
39
- /** @type {number} */
40
- _minInclinationForRequest = Vps.DEFAULT_MIN_INCLINATION_FOR_REQUEST;
41
-
42
- /** @type {number} */
43
- _waitTimeMinInclinationForRequest = Vps.DEFAULT_WAIT_TIME_MIN_INCLINATION_FOR_REQUEST;
44
31
 
45
32
  /**
46
33
  * @override
@@ -80,9 +67,6 @@ class Vps extends Provider {
80
67
  }
81
68
  this._useCamera(SharedCameras.list[0].camera);
82
69
  }
83
-
84
- // 3. Inclination
85
- this._inclinationProviderId = Inclination.addEventListener();
86
70
  }
87
71
 
88
72
  /**
@@ -93,8 +77,13 @@ class Vps extends Provider {
93
77
  SharedCameras.off('removed', this._onCameraRemoved);
94
78
 
95
79
  this._camera = null;
80
+ }
96
81
 
97
- Inclination.removeEventListener(this._inclinationProviderId);
82
+ /**
83
+ * @param {string} endpoint
84
+ */
85
+ setEndpoint(endpoint) {
86
+ this._endpoint = endpoint;
98
87
  }
99
88
 
100
89
  _onCameraDetected = ({ camera }) => {
@@ -153,14 +142,6 @@ class Vps extends Provider {
153
142
  break;
154
143
  }
155
144
 
156
- // 2.a. Do not send an image if smartphone looks at the floor
157
- const inclination = Inclination.lastEvent ? Inclination.lastEvent.data : null;
158
- if (inclination !== null
159
- && inclination < this._minInclinationForRequest) {
160
- await new Promise(resolve => setTimeout(resolve, this._waitTimeMinInclinationForRequest));
161
- continue;
162
- }
163
-
164
145
  // 3. Get current image from camera and relocalize it.
165
146
  const image = await this._camera.currentImage;
166
147
  const res = await ImageRelocalization.relocalize(this._endpoint, image);
@@ -182,14 +163,6 @@ class Vps extends Provider {
182
163
  );
183
164
  const attitude = new Attitude(deviceQuaternion, res.attitude.time, res.attitude.accuracy);
184
165
 
185
- // [16/06/22] Force VPS accuracy to 5m if the information does not exist
186
- // this allows to correctly fuse positions from different providers (VPS,
187
- // GnssWifi, Pole Star...)
188
- const devicePosition = res.userPosition.clone();
189
- if (devicePosition.accuracy === null) {
190
- devicePosition.accuracy = 5;
191
- }
192
-
193
166
  // 5. Finally, notify the listeners if the VPS is not stopped
194
167
  if (this.state === ProviderState.STOPPED) {
195
168
  break;
@@ -197,7 +170,7 @@ class Vps extends Provider {
197
170
 
198
171
  this.notify(
199
172
  this.createEvent(EventType.AbsoluteAttitude, attitude),
200
- this.createEvent(EventType.AbsolutePosition, devicePosition)
173
+ this.createEvent(EventType.AbsolutePosition, res.userPosition)
201
174
  );
202
175
 
203
176
  }
@@ -206,33 +179,6 @@ class Vps extends Provider {
206
179
  _internalStop = () => {
207
180
  // do nothing
208
181
  }
209
-
210
- get endpoint() {
211
- return this._endpoint;
212
- }
213
-
214
- /**
215
- * @param {string} endpoint
216
- */
217
- set endpoint(endpoint) {
218
- this._endpoint = endpoint;
219
- }
220
-
221
- get minInclinationForRequest() {
222
- return this._minInclinationForRequest;
223
- }
224
-
225
- set minInclinationForRequest(minInclinationForRequest) {
226
- this._minInclinationForRequest = minInclinationForRequest;
227
- }
228
-
229
- get waitTimeMinInclinationForRequest() {
230
- return this._waitTimeMinInclinationForRequest;
231
- }
232
-
233
- set waitTimeMinInclinationForRequest(waitTimeMinInclinationForRequest) {
234
- this._waitTimeMinInclinationForRequest = waitTimeMinInclinationForRequest;
235
- }
236
182
  }
237
183
 
238
184
  export default new Vps();
@@ -1,4 +1,4 @@
1
- import { Coordinates } from '@wemap/geo';
1
+ import { Attitude, UserPosition } from '@wemap/geo';
2
2
 
3
3
  class VpsMetadata {
4
4
 
@@ -11,11 +11,14 @@ class VpsMetadata {
11
11
  /** @type {?number} */
12
12
  time;
13
13
 
14
- /** @type {?{position: Coordinates, positionAccuracy: number, ?attitude: [number, number, number, number]}} */
14
+ /** @type {?{position?: UserPosition, attitude?: Attitude}} */
15
15
  coarse;
16
16
 
17
+ /** @type {object} */
18
+ userAgent;
19
+
17
20
  /**
18
- * @returns {object}
21
+ * @returns {VpsMetadata}
19
22
  */
20
23
  toJson() {
21
24
  const output = { size: [this.size.width, this.size.height] };
@@ -27,18 +30,16 @@ class VpsMetadata {
27
30
  }
28
31
  if (this.coarse) {
29
32
  output.coarse = {};
30
-
31
33
  if (this.coarse.attitude) {
32
- output.coarse.attitude = this.coarse.attitude;
34
+ output.coarse.attitude = this.coarse.attitude.toJson();
33
35
  }
34
36
 
35
37
  if (this.coarse.position) {
36
38
  output.coarse.position = this.coarse.position.toJson();
37
39
  }
38
-
39
- if (this.coarse.positionAccuracy) {
40
- output.coarse.positionAccuracy = this.coarse.positionAccuracy;
41
- }
40
+ }
41
+ if (typeof this.userAgent === 'object') {
42
+ output.userAgent = this.userAgent;
42
43
  }
43
44
 
44
45
  return output;
@@ -46,7 +47,7 @@ class VpsMetadata {
46
47
 
47
48
  /**
48
49
  * @param {object} json
49
- * @returns {Coordinates}
50
+ * @returns {VpsMetadata}
50
51
  */
51
52
  static fromJson(json) {
52
53
  const output = new VpsMetadata();
@@ -62,18 +63,16 @@ class VpsMetadata {
62
63
  }
63
64
  if (json.coarse) {
64
65
  output.coarse = {};
65
-
66
66
  if (json.coarse.attitude) {
67
- output.coarse.attitude = json.coarse.attitude;
67
+ output.coarse.attitude = Attitude.fromJson(json.coarse.attitude);
68
68
  }
69
-
70
69
  if (json.coarse.position) {
71
- output.coarse.position = Coordinates.fromJson(json.coarse.position);
72
- }
73
- if (json.coarse.positionAccuracy) {
74
- output.coarse.positionAccuracy = json.coarse.positionAccuracy;
70
+ output.coarse.position = UserPosition.fromJson(json.coarse.position);
75
71
  }
76
72
  }
73
+ if (typeof json.userAgent === 'object') {
74
+ output.userAgent = json.userAgent;
75
+ }
77
76
  return output;
78
77
  }
79
78
  }
@@ -0,0 +1,29 @@
1
+ import { CameraUtils } from '@wemap/camera';
2
+
3
+ import VpsMetadata from './VpsMetadata.js';
4
+
5
+ class VpsRequest {
6
+
7
+ /** @type {VpsMetadata} */
8
+ metadata = new VpsMetadata();
9
+
10
+ /** @type {HTMLCanvasElement} */
11
+ image;
12
+
13
+ toJson() {
14
+ const base64Image = CameraUtils.canvasToBase64(this.image);
15
+ return {
16
+ image: base64Image,
17
+ ...this.metadata.toJson()
18
+ };
19
+ }
20
+
21
+ static fromJson(json) {
22
+ const vpsRequest = new VpsRequest();
23
+ vpsRequest.metadata = VpsMetadata.fromJson(json);
24
+ vpsRequest.image = CameraUtils.base64ToCanvas(json.image);
25
+ return vpsRequest;
26
+ }
27
+ }
28
+
29
+ export default VpsRequest;
@@ -0,0 +1,23 @@
1
+ import { Attitude, UserPosition } from '@wemap/geo';
2
+
3
+ class VpsResponse {
4
+
5
+ /** @type {Attitude} */
6
+ attitude;
7
+
8
+ /** @type {UserPosition} */
9
+ position;
10
+
11
+ /**
12
+ * @param {object} json
13
+ * @returns {VpsResponse}
14
+ */
15
+ fromJson(json) {
16
+ const response = new VpsResponse();
17
+ response.attitude = Attitude.fromJson(json.attitude);
18
+ response.userPosition = UserPosition.fromJson(json.userPosition);
19
+ return response;
20
+ }
21
+ }
22
+
23
+ export default VpsResponse;
@@ -1,136 +0,0 @@
1
- /* eslint-disable max-statements */
2
- import chai from 'chai';
3
- import { Coordinates, Network } from '@wemap/geo';
4
- import { deg2rad } from '@wemap/maths';
5
- import { Itinerary } from '@wemap/routers';
6
- import MMH from './MapMatchingHandler.js';
7
-
8
-
9
- const { expect } = chai;
10
-
11
- describe('MapMatchingHandler', () => {
12
-
13
- const p0 = new Coordinates(0, 0, null, 0);
14
- const p1 = p0.destinationPoint(5, 0);
15
- const p2 = p1.destinationPoint(5, Math.PI / 2);
16
- const p3 = p1.destinationPoint(10, 0);
17
- const p4 = p0.destinationPoint(2, deg2rad(10));
18
-
19
- const pos1 = p0.clone();
20
- const pos2 = pos1.destinationPoint(3, 0);
21
- const pos3 = pos2.destinationPoint(10, 0);
22
-
23
- it('_hasTurnInCircle - itinerary', () => {
24
-
25
- expect(MMH._hasTurnInCircle(pos1, Number.MAX_VALUE)).is.false;
26
-
27
- // p4 -> p1 -> p2 is a turn
28
- MMH.itinerary = Itinerary.fromOrderedCoordinates([p0, p4, p1, p2]);
29
-
30
- expect(MMH._nodeHasTurn(MMH.network.getNodeByCoords(p0))).is.false;
31
- expect(MMH._nodeHasTurn(MMH.network.getNodeByCoords(p1))).is.true;
32
- expect(MMH._nodeHasTurn(MMH.network.getNodeByCoords(p2))).is.false;
33
- expect(MMH._nodeHasTurn(MMH.network.getNodeByCoords(p4))).is.false;
34
-
35
- expect(MMH._hasTurnInCircle(pos1, 1)).is.false;
36
- expect(MMH._hasTurnInCircle(pos1, 3)).is.false;
37
- expect(MMH._hasTurnInCircle(pos1, 10)).is.true;
38
- expect(MMH._hasTurnInCircle(pos2, 1)).is.false;
39
- expect(MMH._hasTurnInCircle(pos2, 3)).is.true;
40
- expect(MMH._hasTurnInCircle(pos2, 10)).is.true;
41
- expect(MMH._hasTurnInCircle(pos3, 1)).is.false;
42
- expect(MMH._hasTurnInCircle(pos3, 3)).is.false;
43
- expect(MMH._hasTurnInCircle(pos3, 10)).is.true;
44
-
45
- // Straight line
46
- MMH.itinerary = Itinerary.fromOrderedCoordinates([p0, p4, p1, p3]);
47
-
48
- expect(MMH._nodeHasTurn(MMH.network.getNodeByCoords(p1))).is.false;
49
- expect(MMH._nodeHasTurn(MMH.network.getNodeByCoords(p4))).is.false;
50
-
51
- expect(MMH._hasTurnInCircle(pos1, 10)).is.false;
52
- expect(MMH._hasTurnInCircle(pos2, 10)).is.false;
53
- expect(MMH._hasTurnInCircle(pos3, 10)).is.false;
54
-
55
- });
56
-
57
-
58
- it('_hasTurnInCircle - network', () => {
59
-
60
- // p1 has turns
61
- MMH.network = Network.fromCoordinates([[p0, p4, p1], [p1, p2], [p3, p1]]);
62
-
63
- expect(MMH._nodeHasTurn(MMH.network.getNodeByCoords(p1))).is.true;
64
- expect(MMH._nodeHasTurn(MMH.network.getNodeByCoords(p4))).is.false;
65
-
66
- expect(MMH._hasTurnInCircle(pos1, 1)).is.false;
67
- expect(MMH._hasTurnInCircle(pos1, 3)).is.false;
68
- expect(MMH._hasTurnInCircle(pos1, 10)).is.true;
69
- expect(MMH._hasTurnInCircle(pos2, 1)).is.false;
70
- expect(MMH._hasTurnInCircle(pos2, 3)).is.true;
71
- expect(MMH._hasTurnInCircle(pos2, 10)).is.true;
72
- expect(MMH._hasTurnInCircle(pos3, 1)).is.false;
73
- expect(MMH._hasTurnInCircle(pos3, 3)).is.false;
74
- expect(MMH._hasTurnInCircle(pos3, 10)).is.true;
75
-
76
- });
77
-
78
- const p10 = new Coordinates(0, 0, null, 1);
79
- const p11 = p10.destinationPoint(5, 0);
80
- const p12 = p11.destinationPoint(5, Math.PI / 2);
81
- const p13 = p11.destinationPoint(10, 0);
82
-
83
- const pos12 = p10.clone().destinationPoint(3, 0);
84
- const pos012 = new Coordinates(0, 0, null, [0, 1]).destinationPoint(3, 0);
85
-
86
- it('_hasTurnInCircle - multi-levels', () => {
87
-
88
- // level 1 is a straight line
89
- MMH.network = Network.fromCoordinates([
90
- [p0, p4, p1], [p1, p2], [p3, p1],
91
- [p10, p11, p13]
92
- ]);
93
-
94
- expect(MMH._nodeHasTurn(MMH.network.getNodeByCoords(p1))).is.true;
95
- expect(MMH._nodeHasTurn(MMH.network.getNodeByCoords(p11))).is.false;
96
-
97
- expect(MMH._hasTurnInCircle(pos2, 1)).is.false;
98
- expect(MMH._hasTurnInCircle(pos2, 3)).is.true;
99
- expect(MMH._hasTurnInCircle(pos2, 10)).is.true;
100
- expect(MMH._hasTurnInCircle(pos12, 1)).is.false;
101
- expect(MMH._hasTurnInCircle(pos12, 3)).is.false;
102
- expect(MMH._hasTurnInCircle(pos12, 10)).is.false;
103
- expect(MMH._hasTurnInCircle(pos012, 1)).is.false;
104
- expect(MMH._hasTurnInCircle(pos012, 3)).is.true;
105
- expect(MMH._hasTurnInCircle(pos012, 10)).is.true;
106
-
107
- // Changing level
108
- // straight line
109
- MMH.network = Network.fromCoordinates([
110
- [p0, p4, p11], [p11, p13]
111
- ]);
112
- expect(MMH._nodeHasTurn(MMH.network.getNodeByCoords(p4))).is.false;
113
- expect(MMH._hasTurnInCircle(pos2, Number.MAX_VALUE)).is.false;
114
- expect(MMH._hasTurnInCircle(pos12, Number.MAX_VALUE)).is.false;
115
- expect(MMH._hasTurnInCircle(pos012, Number.MAX_VALUE)).is.false;
116
-
117
- // Changing level
118
- // turn in p11
119
- MMH.network = Network.fromCoordinates([
120
- [p0, p4, p11], [p11, p12], [p11, p13]
121
- ]);
122
-
123
- expect(MMH._nodeHasTurn(MMH.network.getNodeByCoords(p11))).is.true;
124
- expect(MMH._hasTurnInCircle(pos2, 1)).is.false;
125
- expect(MMH._hasTurnInCircle(pos2, 3)).is.false;
126
- expect(MMH._hasTurnInCircle(pos2, 10)).is.false;
127
- expect(MMH._hasTurnInCircle(pos12, 1)).is.false;
128
- expect(MMH._hasTurnInCircle(pos12, 3)).is.true;
129
- expect(MMH._hasTurnInCircle(pos12, 10)).is.true;
130
- expect(MMH._hasTurnInCircle(pos012, 1)).is.false;
131
- expect(MMH._hasTurnInCircle(pos012, 3)).is.true;
132
- expect(MMH._hasTurnInCircle(pos012, 10)).is.true;
133
-
134
- });
135
-
136
- });