@wemap/providers 6.0.2 → 6.1.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.
- package/dist/wemap-providers.es.js +5692 -0
- package/dist/wemap-providers.es.js.map +1 -0
- package/package.json +3 -2
- package/src/Providers.js +2 -0
- package/src/ProvidersOptions.js +14 -2
- package/src/providers/Provider.js +8 -1
- package/src/providers/position/absolute/AbsolutePosition.js +25 -7
- package/src/providers/vision/VisionRelocalization.js +272 -0
package/package.json
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"Guillaume Pannetier <guillaume.pannetier@getwemap.com>"
|
|
9
9
|
],
|
|
10
10
|
"dependencies": {
|
|
11
|
+
"@wemap/camera": "^6.1.0",
|
|
11
12
|
"@wemap/geo": "^6.0.0",
|
|
12
13
|
"@wemap/geomagnetism": "^0.1.1",
|
|
13
14
|
"@wemap/logger": "^6.0.0",
|
|
@@ -39,6 +40,6 @@
|
|
|
39
40
|
"url": "git+https://github.com/wemap/wemap-modules-js.git"
|
|
40
41
|
},
|
|
41
42
|
"type": "module",
|
|
42
|
-
"version": "6.0
|
|
43
|
-
"gitHead": "
|
|
43
|
+
"version": "6.1.0",
|
|
44
|
+
"gitHead": "367774b6d8cbfa864886d9cc3344244c7da20639"
|
|
44
45
|
}
|
package/src/Providers.js
CHANGED
|
@@ -32,6 +32,8 @@ export { default as GnssWifi } from './providers/position/absolute/GnssWifi.js';
|
|
|
32
32
|
export { default as Ip } from './providers/position/absolute/Ip.js';
|
|
33
33
|
export { default as AbsolutePosition } from './providers/position/absolute/AbsolutePosition.js';
|
|
34
34
|
|
|
35
|
+
export { default as VisionRelocalization } from './providers/vision/VisionRelocalization.js';
|
|
36
|
+
|
|
35
37
|
export { default as Barcode } from './providers/others/Barcode.js';
|
|
36
38
|
export { default as CameraNative } from './providers/others/CameraNative.js';
|
|
37
39
|
export { default as CameraProjectionMatrix } from './providers/others/CameraProjectionMatrix.js';
|
package/src/ProvidersOptions.js
CHANGED
|
@@ -7,10 +7,18 @@ const ProvidersOptions = {
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Providers listed here will not be used by the system
|
|
10
|
-
* List of {@link Provider#
|
|
10
|
+
* List of {@link Provider#pname}
|
|
11
11
|
*/
|
|
12
12
|
ignoreProviders: [],
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Some providers are not used by default (VPS, QRCodeScanner) because they
|
|
16
|
+
* require data from an optional external service or because they drain more
|
|
17
|
+
* battery. They can be added to this list to be used
|
|
18
|
+
* List of {@link Provider#pname}
|
|
19
|
+
*/
|
|
20
|
+
optionalProviders: ['VisionRelocalization'],
|
|
21
|
+
|
|
14
22
|
/**
|
|
15
23
|
* Define the list of EventType that are optionals
|
|
16
24
|
* List of {@link EventType}
|
|
@@ -23,7 +31,11 @@ const ProvidersOptions = {
|
|
|
23
31
|
|
|
24
32
|
stepdetectionlocker: true,
|
|
25
33
|
|
|
26
|
-
smoother: true
|
|
34
|
+
smoother: true,
|
|
35
|
+
|
|
36
|
+
get hasVps() {
|
|
37
|
+
return this.optionalProviders.includes('VisionRelocalization');
|
|
38
|
+
}
|
|
27
39
|
};
|
|
28
40
|
|
|
29
41
|
export default ProvidersOptions;
|
|
@@ -123,9 +123,16 @@ class Provider {
|
|
|
123
123
|
return false;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Description of the function
|
|
128
|
+
* @name onEventsFunction
|
|
129
|
+
* @function
|
|
130
|
+
* @param {ProviderEvent[]} events the array of provider events
|
|
131
|
+
*/
|
|
132
|
+
|
|
126
133
|
/**
|
|
127
134
|
*
|
|
128
|
-
* @param {
|
|
135
|
+
* @param {onEventsFunction} onEvents
|
|
129
136
|
* @param {Function} onError
|
|
130
137
|
* @param {Boolean} startIfNecessary
|
|
131
138
|
* @returns {Number}
|
|
@@ -7,6 +7,8 @@ import ProviderEvent from '../../../events/ProviderEvent.js';
|
|
|
7
7
|
import MapMatchingHandler from '../../../mapmatching/MapMatchingHandler.js';
|
|
8
8
|
import GnssWifi from './GnssWifi.js';
|
|
9
9
|
import { default as GeoRelativePositionProvider } from '../relative/GeoRelativePosition.js';
|
|
10
|
+
import ProvidersOptions from '../../../ProvidersOptions.js';
|
|
11
|
+
import VisionRelocalization from '../../vision/VisionRelocalization.js';
|
|
10
12
|
|
|
11
13
|
class AbsolutePosition extends Provider {
|
|
12
14
|
|
|
@@ -65,13 +67,29 @@ class AbsolutePosition extends Provider {
|
|
|
65
67
|
// do nothing
|
|
66
68
|
});
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
events
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
if (ProvidersOptions.hasVps) {
|
|
71
|
+
|
|
72
|
+
const vpsProviderId = VisionRelocalization.addEventListener(events => {
|
|
73
|
+
for (const providerEvent of events) {
|
|
74
|
+
if (providerEvent.dataType === EventType.AbsolutePosition) {
|
|
75
|
+
this._onAbsolutePosition(providerEvent);
|
|
76
|
+
VisionRelocalization.removeEventListener(vpsProviderId);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
} else {
|
|
83
|
+
|
|
84
|
+
this._gnssWifiProviderId = GnssWifi.addEventListener(
|
|
85
|
+
events => {
|
|
86
|
+
// bearing from GnssWifi is not reliable for our usecase
|
|
87
|
+
events[0].data.bearing = null;
|
|
88
|
+
this._onAbsolutePosition(events[0], false);
|
|
89
|
+
}
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
}
|
|
75
93
|
|
|
76
94
|
this._mapMatchingHandlerId = MapMatchingHandler.addEventListener();
|
|
77
95
|
}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/* eslint-disable max-statements */
|
|
2
|
+
import { SharedCameras, CameraUtils, Camera } from '@wemap/camera';
|
|
3
|
+
import { Attitude, Level, UserPosition } from '@wemap/geo';
|
|
4
|
+
import Logger from '@wemap/logger';
|
|
5
|
+
import { deg2rad, Quaternion } from '@wemap/maths';
|
|
6
|
+
import { TimeUtils } from '../../../../utils/index.js';
|
|
7
|
+
|
|
8
|
+
import EventType from '../../events/EventType.js';
|
|
9
|
+
import ProviderEvent from '../../events/ProviderEvent.js';
|
|
10
|
+
import Provider from '../Provider.js';
|
|
11
|
+
import ProviderState from '../ProviderState.js';
|
|
12
|
+
|
|
13
|
+
class VisionRelocalization extends Provider {
|
|
14
|
+
|
|
15
|
+
// static SERVICE_URL = 'http://localhost:45678/';
|
|
16
|
+
static SERVICE_URL = 'https://vps.maaap.it/';
|
|
17
|
+
static MAP_ID = 'wemap';
|
|
18
|
+
static MIN_TIME_BETWEEN_TWO_REQUESTS = 1000;
|
|
19
|
+
|
|
20
|
+
/** @type {boolean} */
|
|
21
|
+
_serverError = false;
|
|
22
|
+
|
|
23
|
+
/** @type {boolean} */
|
|
24
|
+
_cameraError = false;
|
|
25
|
+
|
|
26
|
+
/** @type {Camera} */
|
|
27
|
+
_camera = null;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @override
|
|
31
|
+
*/
|
|
32
|
+
static get pname() {
|
|
33
|
+
return 'VisionRelocalization';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @override
|
|
38
|
+
*/
|
|
39
|
+
static get eventsType() {
|
|
40
|
+
return [EventType.AbsoluteAttitude, EventType.AbsolutePosition];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @override
|
|
45
|
+
*/
|
|
46
|
+
get _availability() {
|
|
47
|
+
return Promise.resolve();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @override
|
|
52
|
+
*/
|
|
53
|
+
start() {
|
|
54
|
+
|
|
55
|
+
// 1. Add listeners on shared cameras to detect new cameras
|
|
56
|
+
SharedCameras.on('added', this._onCameraDetected);
|
|
57
|
+
SharedCameras.on('removed', this._onCameraRemoved);
|
|
58
|
+
|
|
59
|
+
// 2. If a camera already exists, use it
|
|
60
|
+
if (SharedCameras.list.length) {
|
|
61
|
+
if (SharedCameras.list.length > 1) {
|
|
62
|
+
Logger.warn('It seems that more than 1 camera has been detected'
|
|
63
|
+
+ ' for VPS. Taking the first...');
|
|
64
|
+
}
|
|
65
|
+
this._useCamera(SharedCameras.list[0].camera);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @override
|
|
71
|
+
*/
|
|
72
|
+
stop() {
|
|
73
|
+
SharedCameras.off('added', this._onCameraDetected);
|
|
74
|
+
SharedCameras.off('removed', this._onCameraRemoved);
|
|
75
|
+
|
|
76
|
+
this._camera = null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
_onCameraDetected = ({ camera }) => {
|
|
81
|
+
if (this._camera) {
|
|
82
|
+
Logger.warn('It seems that more than 1 camera has been detected'
|
|
83
|
+
+ ' for VPS. Taking the first...');
|
|
84
|
+
}
|
|
85
|
+
this._useCamera(camera);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
_onCameraRemoved = () => {
|
|
89
|
+
if (this._camera) {
|
|
90
|
+
this._camera.off('started', this._internalStart);
|
|
91
|
+
this._camera.off('stopped', this._internalStop);
|
|
92
|
+
} else {
|
|
93
|
+
Logger.warn('There is no previously detected camera but once has stopped');
|
|
94
|
+
}
|
|
95
|
+
this._camera = null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @param {Camera} camera
|
|
100
|
+
*/
|
|
101
|
+
_useCamera(camera) {
|
|
102
|
+
this._camera = camera;
|
|
103
|
+
|
|
104
|
+
camera.on('started', this._internalStart);
|
|
105
|
+
camera.on('stopped', this._internalStop);
|
|
106
|
+
|
|
107
|
+
if (camera.isStarted) {
|
|
108
|
+
this._internalStart();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
_internalStart = async () => {
|
|
114
|
+
|
|
115
|
+
this._serverError = false;
|
|
116
|
+
|
|
117
|
+
let lastTimestamp = -1;
|
|
118
|
+
|
|
119
|
+
while (this.state !== ProviderState.STOPPPED) {
|
|
120
|
+
|
|
121
|
+
const diffTime = TimeUtils.preciseTime() - lastTimestamp;
|
|
122
|
+
const timeToWait = Math.max(0, VisionRelocalization.MIN_TIME_BETWEEN_TWO_REQUESTS - diffTime);
|
|
123
|
+
await new Promise(resolve => setTimeout(resolve, timeToWait));
|
|
124
|
+
|
|
125
|
+
if (this.state === ProviderState.STOPPPED) {
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const url = VisionRelocalization.SERVICE_URL + VisionRelocalization.MAP_ID;
|
|
130
|
+
|
|
131
|
+
// 1. Prepare the request
|
|
132
|
+
if (!this._camera || this._camera.state !== Camera.State.STARTED) {
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
const payload = await this._prepareRequest();
|
|
136
|
+
|
|
137
|
+
if (this.state === ProviderState.STOPPPED) {
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 2. Send the request
|
|
142
|
+
const serverResponse = await fetch(url, {
|
|
143
|
+
method: 'POST',
|
|
144
|
+
body: JSON.stringify(payload),
|
|
145
|
+
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
if (this.state === ProviderState.STOPPPED) {
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 3. Parse the response
|
|
153
|
+
const events = await this._parseResponse(serverResponse, url, payload.time);
|
|
154
|
+
if (this._serverError || this.state === ProviderState.STOPPPED) {
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// 4. Notify events
|
|
159
|
+
if (events.length) {
|
|
160
|
+
this.notify(...events);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
lastTimestamp = TimeUtils.preciseTime();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (this.state !== ProviderState.STOPPPED) {
|
|
167
|
+
this.stop();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
_internalStop = () => {
|
|
172
|
+
// do nothing
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* @returns {Object}
|
|
178
|
+
*/
|
|
179
|
+
async _prepareRequest() {
|
|
180
|
+
|
|
181
|
+
const camera = this._camera;
|
|
182
|
+
|
|
183
|
+
// Retrieve the image
|
|
184
|
+
const image = await camera.currentImage;
|
|
185
|
+
const time = TimeUtils.preciseTime();
|
|
186
|
+
// TODO: move the grayscale conversion in the currentImage getter
|
|
187
|
+
CameraUtils.convertToGrayscale(image);
|
|
188
|
+
const reducedImage = CameraUtils.reduceImageSize(image, 1280);
|
|
189
|
+
// const reducedImage = CameraUtils.reduceImageSize(image, 720, 720);
|
|
190
|
+
const { height, width } = reducedImage;
|
|
191
|
+
const base64Image = CameraUtils.canvasToBase64(reducedImage);
|
|
192
|
+
|
|
193
|
+
// Retrieve the calibration matrix
|
|
194
|
+
const calibration = CameraUtils.createCameraCalibrationFromWidthHeightFov(
|
|
195
|
+
width, height, deg2rad(camera.hardwareVerticalFov)
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
// Build the payload
|
|
199
|
+
return {
|
|
200
|
+
calibration,
|
|
201
|
+
size: [width, height],
|
|
202
|
+
image: base64Image,
|
|
203
|
+
time
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* @param {Response} res
|
|
209
|
+
* @param {String} url
|
|
210
|
+
* @param {Number} requestTime
|
|
211
|
+
* @returns {ProviderEvent[]}
|
|
212
|
+
*/
|
|
213
|
+
async _parseResponse(res, url, requestTime) {
|
|
214
|
+
if (res.status !== 200) {
|
|
215
|
+
Logger.warn(`The VPS server (${url}) has encountered a problem`);
|
|
216
|
+
this._serverError = true;
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const json = await res.json();
|
|
221
|
+
if (json.error) {
|
|
222
|
+
return [];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const { attitude, userPosition } = VisionRelocalization._parseJsonResponse(json, requestTime);
|
|
226
|
+
|
|
227
|
+
const events = [
|
|
228
|
+
this.createEvent(EventType.AbsoluteAttitude, attitude),
|
|
229
|
+
this.createEvent(EventType.AbsolutePosition, userPosition)
|
|
230
|
+
];
|
|
231
|
+
|
|
232
|
+
return events;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
static _parseJsonResponse(json, requestTime) {
|
|
237
|
+
|
|
238
|
+
const quaternion = [
|
|
239
|
+
json.attitude.w,
|
|
240
|
+
json.attitude.x,
|
|
241
|
+
json.attitude.y,
|
|
242
|
+
json.attitude.z
|
|
243
|
+
];
|
|
244
|
+
const quaternionNorm = Quaternion.norm(quaternion);
|
|
245
|
+
quaternion[0] /= quaternionNorm;
|
|
246
|
+
quaternion[1] /= quaternionNorm;
|
|
247
|
+
quaternion[2] /= quaternionNorm;
|
|
248
|
+
quaternion[3] /= quaternionNorm;
|
|
249
|
+
|
|
250
|
+
const quaternionWithScreenRotation = Quaternion.multiply(
|
|
251
|
+
Quaternion.fromAxisAngle([0, 0, 1], deg2rad(window.orientation || 0)), quaternion
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
const attitude = new Attitude(quaternionWithScreenRotation, requestTime, 0);
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
const userPosition = new UserPosition(
|
|
258
|
+
json.coordinates.lat,
|
|
259
|
+
json.coordinates.lng,
|
|
260
|
+
json.coordinates.alt,
|
|
261
|
+
json.coordinates.level ? new Level(json.coordinates.level) : null,
|
|
262
|
+
requestTime,
|
|
263
|
+
0,
|
|
264
|
+
attitude.heading
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
return { userPosition, attitude };
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export default new VisionRelocalization();
|