@wemap/positioning 1.2.2 → 2.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/.eslintrc.json +4 -2
- package/debug/absolute-attitude.html +16 -0
- package/debug/gnss-wifi-pdr.html +16 -0
- package/debug/gnss-wifi.html +16 -0
- package/debug/imu.html +16 -0
- package/debug/inclination.html +16 -0
- package/debug/pdr.html +16 -0
- package/debug/pose.html +16 -0
- package/debug/positioning.html +16 -0
- package/debug/relative-attitude.html +16 -0
- package/package.json +6 -4
- package/src/PositioningHandler.js +124 -0
- package/src/components/AbsoluteAttitudeComponent.jsx +104 -0
- package/src/components/GnssWifiComponent.jsx +46 -0
- package/src/components/GnssWifiPdrComponent.jsx +85 -0
- package/src/components/ImuComponent.jsx +100 -0
- package/src/components/InclinationComponent.jsx +53 -0
- package/src/components/PdrComponent.jsx +88 -0
- package/src/components/PoseComponent.jsx +74 -0
- package/src/components/PositioningComponent.jsx +26 -0
- package/src/components/PositioningInclinationComponent.jsx +76 -0
- package/src/components/PositioningPoseComponent.jsx +111 -0
- package/src/components/RelativeAttitudeComponent.jsx +82 -0
- package/src/components/StartStopComponent.jsx +50 -0
- package/src/components/Utils.js +41 -2
- package/src/components/index.js +19 -2
- package/src/errors/AskImuOnDesktopError.js +9 -0
- package/src/errors/GeolocationApiMissingError.js +9 -0
- package/src/errors/GeolocationPermissionDeniedError.js +9 -0
- package/src/errors/GeolocationPositionUnavailableError.js +9 -0
- package/src/errors/IpResolveServerError.js +9 -0
- package/src/errors/MissingAccelerometerError.js +11 -0
- package/src/errors/MissingGyroscopeError.js +11 -0
- package/src/errors/MissingMagnetometerError.js +9 -0
- package/src/errors/MissingSensorError.js +14 -0
- package/src/events/EventType.js +20 -0
- package/src/events/ProviderError.js +52 -0
- package/src/events/ProviderEvent.js +35 -0
- package/src/index.js +3 -4
- package/src/providers/Constants.js +5 -0
- package/src/providers/FakeAbsolutePositionProvider.js +56 -0
- package/src/providers/Provider.js +218 -0
- package/src/providers/ProviderOptions.js +28 -0
- package/src/providers/ProvidersLogger.js +3 -3
- package/src/providers/attitude/AbsoluteAttitudeProvider.js +207 -0
- package/src/providers/attitude/EkfAttitude.js +238 -0
- package/src/providers/attitude/EkfAttitude.spec.js +116 -0
- package/src/providers/attitude/RelativeAttitudeProvider.js +129 -0
- package/src/providers/others/ImuProvider.js +186 -0
- package/src/providers/others/InclinationProvider.js +107 -0
- package/src/providers/others/MapMatchingProvider.js +147 -0
- package/src/providers/pose/GnssWifiPdrProvider.js +233 -0
- package/src/providers/pose/PoseProvider.js +90 -0
- package/src/providers/pose/pdr/PdrProvider.js +352 -0
- package/src/providers/pose/pdr/helpers/HeadingUnlocker.js +41 -0
- package/src/providers/pose/pdr/helpers/HeadingUnlocker.spec.js +26 -0
- package/src/providers/pose/pdr/helpers/Smoother.js +90 -0
- package/src/providers/pose/pdr/helpers/Smoother.spec.js +424 -0
- package/src/providers/pose/pdr/helpers/ThugDetector.js +37 -0
- package/src/providers/pose/pdr/steps/StepDetection.js +7 -0
- package/src/providers/pose/pdr/steps/StepDetectionLadetto.js +67 -0
- package/src/providers/pose/pdr/steps/StepDetectionMinMaxPeaks.js +80 -0
- package/src/providers/pose/pdr/steps/StepDetectionMinMaxPeaks2.js +108 -0
- package/src/providers/position/GnssWifiProvider.js +129 -0
- package/src/providers/position/IpProvider.js +75 -0
- package/src.old/components/Utils.js +35 -0
- package/src.old/components/index.js +13 -0
- package/src.old/index.js +7 -0
- package/{src → src.old}/providers/GnssPdrLocationSource.js +1 -1
- package/src.old/providers/ProvidersLogger.js +77 -0
- package/webpack/webpack.dev.js +1 -1
- package/debug/index.html +0 -15
- package/debug/index.old.html +0 -37
- package/scripts/release-github.js +0 -216
- package/src.new/NavigationHandler.js +0 -62
- package/src.new/index.js +0 -3
- package/src.new/providers/FakeLocationSource.js +0 -39
- /package/{src → src.old}/Constants.js +0 -0
- /package/{src → src.old}/NavigationHandler.js +0 -0
- /package/{src → src.old}/Pose.js +0 -0
- /package/{src → src.old}/attitude/AttitudeHandler.js +0 -0
- /package/{src → src.old}/attitude/EkfAttitude.js +0 -0
- /package/{src → src.old}/attitude/EkfAttitude.spec.js +0 -0
- /package/{src → src.old}/components/AbsoluteAttitude.jsx +0 -0
- /package/{src → src.old}/components/Imu.jsx +0 -0
- /package/{src → src.old}/components/LocationSource.jsx +0 -0
- /package/{src → src.old}/components/Logger.jsx +0 -0
- /package/{src → src.old}/components/NavigationDebugApp.jsx +0 -0
- /package/{src → src.old}/components/Others.jsx +0 -0
- /package/{src → src.old}/components/RelativeAttitude.jsx +0 -0
- /package/{src → src.old}/providers/FixedLocationImuLocationSource.js +0 -0
- /package/{src → src.old}/providers/GnssLocationSource.js +0 -0
- /package/{src → src.old}/providers/IPLocationSource.js +0 -0
- /package/{src → src.old}/providers/LocationSource.js +0 -0
- /package/{src → src.old}/providers/PdrLocationSource.js +0 -0
- /package/{src → src.old}/providers/pdr/HeadingUnlocker.js +0 -0
- /package/{src → src.old}/providers/pdr/HeadingUnlocker.spec.js +0 -0
- /package/{src → src.old}/providers/pdr/Smoother.js +0 -0
- /package/{src → src.old}/providers/pdr/Smoother.spec.js +0 -0
- /package/{src → src.old}/providers/pdr/ThugDetector.js +0 -0
- /package/{src → src.old}/providers/steps/StepDetection.js +0 -0
- /package/{src → src.old}/providers/steps/StepDetectionLadetto.js +0 -0
- /package/{src → src.old}/providers/steps/StepDetectionMinMaxPeaks.js +0 -0
- /package/{src → src.old}/providers/steps/StepDetectionMinMaxPeaks2.js +0 -0
- /package/{src → src.old}/sensors/SensorsCompatibility.js +0 -0
- /package/{src → src.old}/sensors/SensorsCompatibility.spec.js +0 -0
- /package/{src → src.old}/sensors/SensorsLogger.js +0 -0
- /package/{src → src.old}/sensors/SensorsLoggerUtils.js +0 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { deg2rad } from '@wemap/maths';
|
|
2
|
+
import {
|
|
3
|
+
Browser, BrowserUtils
|
|
4
|
+
} from '@wemap/utils';
|
|
5
|
+
|
|
6
|
+
import Provider from '../Provider';
|
|
7
|
+
import EventType from '../../events/EventType';
|
|
8
|
+
import MissingAccelerometerError from '../../errors/MissingAccelerometerError';
|
|
9
|
+
import MissingGyroscopeError from '../../errors/MissingGyroscopeError';
|
|
10
|
+
import AskImuOnDesktopError from '../../errors/AskImuOnDesktopError';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Imu (Inertial Measurement Unit) provider retrieve acceleration data
|
|
14
|
+
* and/or angular rate data from inertial sensors.
|
|
15
|
+
* opions.require has to be defined in order to work
|
|
16
|
+
*
|
|
17
|
+
* -----------------------------------
|
|
18
|
+
* Overview of compatibilities:
|
|
19
|
+
* -----------------------------------
|
|
20
|
+
*
|
|
21
|
+
* Chrome Android (v72.0.3626): YES (via devicemotion)
|
|
22
|
+
* Safari iOS (v12.0): YES (via devicemotion)
|
|
23
|
+
* Opera Android (v50.2.2426): NO {@link https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent}
|
|
24
|
+
* Firefox Android (v65.0.1): YES (via devicemotion)
|
|
25
|
+
*
|
|
26
|
+
* -----------------------------------
|
|
27
|
+
*/
|
|
28
|
+
class ImuProvider extends Provider {
|
|
29
|
+
|
|
30
|
+
requiredSensors = [];
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Constructor of ImuProvider
|
|
34
|
+
* options.require has to be filled by a list of EventType
|
|
35
|
+
* @example options = {require: [EventType.Acceleration, EventType.AngularRate]};
|
|
36
|
+
* @example options = {require: [EventType.Acceleration]};
|
|
37
|
+
*
|
|
38
|
+
* @param {Function} onEvent @see Provider#constructor
|
|
39
|
+
* @param {Function} onError @see Provider#constructor
|
|
40
|
+
* @param {Object} options @see Provider#constructor
|
|
41
|
+
*/
|
|
42
|
+
constructor(onEvent, onError, options) {
|
|
43
|
+
super(onEvent, onError, options);
|
|
44
|
+
|
|
45
|
+
if (!options.hasOwnProperty('require')) {
|
|
46
|
+
throw new Error('options.require is missing in ImuProvider constructor');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (options.require.length === 0) {
|
|
50
|
+
throw new Error('option.require is empty');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
options.require.forEach(elem => {
|
|
54
|
+
if (!ImuProvider.eventsType.includes(elem)) {
|
|
55
|
+
throw new Error(elem + ' is not recognised in options.require');
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
this.requiredSensors = options.require;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @override
|
|
65
|
+
*/
|
|
66
|
+
static get displayName() {
|
|
67
|
+
return 'Inertial Measurement Unit';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @override
|
|
72
|
+
*/
|
|
73
|
+
static get eventsType() {
|
|
74
|
+
return [EventType.AngularRate, EventType.Acceleration];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @override
|
|
79
|
+
*/
|
|
80
|
+
static checkAvailabilityErrors() {
|
|
81
|
+
|
|
82
|
+
if (BrowserUtils.isMobile) {
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return [
|
|
87
|
+
ImuProvider.createError(
|
|
88
|
+
EventType.AngularRate,
|
|
89
|
+
new AskImuOnDesktopError()
|
|
90
|
+
),
|
|
91
|
+
ImuProvider.createError(
|
|
92
|
+
EventType.Acceleration,
|
|
93
|
+
new AskImuOnDesktopError()
|
|
94
|
+
)
|
|
95
|
+
];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @override
|
|
100
|
+
*/
|
|
101
|
+
startInternal() {
|
|
102
|
+
window.addEventListener('devicemotion', this.parseDeviceMotionEvent, true);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @override
|
|
107
|
+
*/
|
|
108
|
+
stopInternal() {
|
|
109
|
+
window.removeEventListener('devicemotion', this.parseDeviceMotionEvent, true);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* devicemotion callback
|
|
114
|
+
* @param {DeviceMotionEvent} e device motion event
|
|
115
|
+
* @returns {ProviderEvent[]} an array of provider events
|
|
116
|
+
* @private
|
|
117
|
+
*/
|
|
118
|
+
parseDeviceMotionEvent = e => {
|
|
119
|
+
|
|
120
|
+
const events = [];
|
|
121
|
+
|
|
122
|
+
const timestamp = e.timeStamp / 1e3;
|
|
123
|
+
|
|
124
|
+
if (this.requiredSensors.includes(EventType.Acceleration)) {
|
|
125
|
+
|
|
126
|
+
let acc;
|
|
127
|
+
if (e.accelerationIncludingGravity) {
|
|
128
|
+
const {
|
|
129
|
+
x, y, z
|
|
130
|
+
} = e.accelerationIncludingGravity;
|
|
131
|
+
|
|
132
|
+
if (x && y && z) {
|
|
133
|
+
acc = [x, y, z];
|
|
134
|
+
|
|
135
|
+
if (BrowserUtils.name === Browser.SAFARI) {
|
|
136
|
+
acc[0] *= -1;
|
|
137
|
+
acc[1] *= -1;
|
|
138
|
+
acc[2] *= -1;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!acc) {
|
|
144
|
+
super.notifyError(this.createError(
|
|
145
|
+
EventType.AngularRate,
|
|
146
|
+
new MissingAccelerometerError().from('devicemotion'),
|
|
147
|
+
timestamp
|
|
148
|
+
));
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
events.push(this.createEvent(EventType.Acceleration, acc, timestamp));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
if (this.requiredSensors.includes(EventType.AngularRate)) {
|
|
157
|
+
|
|
158
|
+
let gyr;
|
|
159
|
+
if (e.rotationRate) {
|
|
160
|
+
const {
|
|
161
|
+
alpha, beta, gamma
|
|
162
|
+
} = e.rotationRate;
|
|
163
|
+
|
|
164
|
+
if (alpha && beta && gamma) {
|
|
165
|
+
gyr = [deg2rad(alpha), deg2rad(beta), deg2rad(gamma)];
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (!gyr) {
|
|
170
|
+
super.notifyError(this.createError(EventType.AngularRate,
|
|
171
|
+
new MissingGyroscopeError().from('devicemotion'),
|
|
172
|
+
timestamp));
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
events.push(this.createEvent(EventType.AngularRate, gyr, timestamp));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (events.length !== 0) {
|
|
180
|
+
super.notify(...events);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export default ImuProvider;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import Provider from '../Provider';
|
|
2
|
+
import EventType from '../../events/EventType';
|
|
3
|
+
import ImuProvider from './ImuProvider';
|
|
4
|
+
import ProviderError from '../../events/ProviderError';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Inclination provider gives the inclination of the device using Imu Sensor
|
|
8
|
+
* For example, when the top of the device is pointing the sky, inclination = Math.PI/2
|
|
9
|
+
* when the device is layed on a table, inclination = 0
|
|
10
|
+
* This provider use window.orientation to return a result in function of screen orientation
|
|
11
|
+
*/
|
|
12
|
+
class InclinationProvider extends Provider {
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @override
|
|
16
|
+
*/
|
|
17
|
+
constructor(onEvent, onError, options) {
|
|
18
|
+
super(onEvent, onError, options);
|
|
19
|
+
|
|
20
|
+
this.imuProvider = new ImuProvider(
|
|
21
|
+
this.onImuEvent,
|
|
22
|
+
this.onImuError,
|
|
23
|
+
{ require: [EventType.Acceleration] }
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @override
|
|
29
|
+
*/
|
|
30
|
+
static get displayName() {
|
|
31
|
+
return 'Inertial Measurement Unit';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @override
|
|
36
|
+
*/
|
|
37
|
+
static get eventsType() {
|
|
38
|
+
return [EventType.Inclination];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @override
|
|
43
|
+
*/
|
|
44
|
+
static get requiredProviders() {
|
|
45
|
+
return [ImuProvider];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @override
|
|
50
|
+
*/
|
|
51
|
+
startInternal() {
|
|
52
|
+
return this.imuProvider.start();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @override
|
|
57
|
+
*/
|
|
58
|
+
stopInternal() {
|
|
59
|
+
return this.imuProvider.stop();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @private
|
|
64
|
+
*/
|
|
65
|
+
onImuEvent = imuEvent => {
|
|
66
|
+
const accelerationEvent = imuEvent[0];
|
|
67
|
+
const acc = accelerationEvent.data;
|
|
68
|
+
|
|
69
|
+
const screenOrientation = window.orientation || 0;
|
|
70
|
+
|
|
71
|
+
const sizeAcc = Math.sqrt(acc[0] * acc[0] + acc[1] * acc[1] + acc[2] * acc[2]);
|
|
72
|
+
const accNormalized = [acc[0] / sizeAcc, acc[1] / sizeAcc, acc[2] / sizeAcc];
|
|
73
|
+
|
|
74
|
+
const q = [accNormalized[2] + 1, accNormalized[1], -accNormalized[0], 0];
|
|
75
|
+
const qSize = Math.sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2]);
|
|
76
|
+
const qNormalized = [q[0] / qSize, q[1] / qSize, q[2] / qSize, 0];
|
|
77
|
+
|
|
78
|
+
let inclination;
|
|
79
|
+
if (screenOrientation === 0) {
|
|
80
|
+
inclination = Math.asin(2 * qNormalized[1] * qNormalized[0]);
|
|
81
|
+
} else if (screenOrientation === 90) {
|
|
82
|
+
inclination = -Math.asin(2 * qNormalized[2] * qNormalized[0]);
|
|
83
|
+
} else if (screenOrientation === -90) {
|
|
84
|
+
inclination = Math.asin(2 * qNormalized[2] * qNormalized[0]);
|
|
85
|
+
} else if (screenOrientation === 180) {
|
|
86
|
+
inclination = -Math.asin(2 * qNormalized[1] * qNormalized[0]);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
this.notify(this.createEvent(EventType.Inclination, inclination));
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
onImuError = imuErrors => {
|
|
93
|
+
this.notifyError(...ProviderError.modifyArrayDataType(imuErrors, EventType.Inclination));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @override
|
|
98
|
+
*/
|
|
99
|
+
static checkAvailabilityErrors() {
|
|
100
|
+
return ProviderError.modifyArrayDataType(
|
|
101
|
+
super.checkAvailabilityErrors(),
|
|
102
|
+
EventType.Inclination
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export default InclinationProvider;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import Provider from '../Provider';
|
|
2
|
+
import {
|
|
3
|
+
WGS84, Edge, Itinerary, Network, MapMatching
|
|
4
|
+
} from '@wemap/geo';
|
|
5
|
+
|
|
6
|
+
class MapMatchingProvider extends Provider {
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Enable LocationSource mapmatching
|
|
10
|
+
* @param {*} network a network which will be used for map matching
|
|
11
|
+
* @param {*} maxDistance max distance between location and network to match. null disables maxDistance (default: null)
|
|
12
|
+
* @param {*} maxAngleBearing threshold to match a parallel segment for angle between location bearing and segment. null disables this threshold (default: null)
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
enableMapMatching(network, maxDistance, maxAngleBearing) {
|
|
16
|
+
this.mapMatching = new MapMatching();
|
|
17
|
+
this.mapMatching.maxDistance = maxDistance;
|
|
18
|
+
this.mapMatching.maxAngleBearing = maxAngleBearing;
|
|
19
|
+
|
|
20
|
+
if (network) {
|
|
21
|
+
this.setNetwork(network);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Update the LocationSource network for mapmatching
|
|
27
|
+
* @param {Network} network a network instance
|
|
28
|
+
* @public
|
|
29
|
+
*/
|
|
30
|
+
setNetwork(network) {
|
|
31
|
+
if (!(network instanceof Network)) {
|
|
32
|
+
throw new TypeError(network + ' is not an instance of Network');
|
|
33
|
+
}
|
|
34
|
+
if (!this.mapMatching) {
|
|
35
|
+
throw new Error('MapMatching is not enabled');
|
|
36
|
+
}
|
|
37
|
+
this.mapMatching.setNetwork(network);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Is mapmatching activated?
|
|
42
|
+
* @returns is mapmatching activated
|
|
43
|
+
* @public
|
|
44
|
+
*/
|
|
45
|
+
get hasMapMatching() {
|
|
46
|
+
return this.mapMatching !== null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Returns projection of a location on network
|
|
51
|
+
* @param {WGS84} location if location is null, projection of this.pose.location is used
|
|
52
|
+
* @returns The projected location {WGS84} or null.
|
|
53
|
+
* @public
|
|
54
|
+
*/
|
|
55
|
+
getProjectionOnNetwork(location = null) {
|
|
56
|
+
|
|
57
|
+
if (!this.mapMatching) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let locationToProject = location;
|
|
62
|
+
if (!location) {
|
|
63
|
+
locationToProject = this.pose.location;
|
|
64
|
+
}
|
|
65
|
+
if (!locationToProject) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return this.mapMatching.getProjection(locationToProject, false, false);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Itinerary
|
|
75
|
+
*/
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Update itinerary of Location Source.
|
|
79
|
+
* Itinerary is different from network, it is not used for mapmatching. It can be used for others tricks like HeadingUnlocker
|
|
80
|
+
* @param {Itinerary} itinerary a given itinerary
|
|
81
|
+
* @public
|
|
82
|
+
*/
|
|
83
|
+
setItinerary(itinerary) {
|
|
84
|
+
if (!(itinerary instanceof Itinerary)) {
|
|
85
|
+
throw new TypeError(itinerary + ' is not an instance of Itinerary');
|
|
86
|
+
}
|
|
87
|
+
this.itinerary = itinerary;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Returns projection and itinerary info of a location on network
|
|
93
|
+
* @param {WGS84} location if location is null, projection of this.pose.location is used
|
|
94
|
+
* @returns An object of the projected location and itinerary info or null.
|
|
95
|
+
* @public
|
|
96
|
+
*/
|
|
97
|
+
getItineraryInfo(location = null) {
|
|
98
|
+
|
|
99
|
+
if (!this.itinerary) {
|
|
100
|
+
throw new Error('No itinerary found');
|
|
101
|
+
}
|
|
102
|
+
const projection = this.getProjectionOnNetwork(location);
|
|
103
|
+
if (!projection) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const totalDistance = this.itinerary.distance;
|
|
108
|
+
let traveledDistance = 0;
|
|
109
|
+
const edges = this.itinerary.edges;
|
|
110
|
+
|
|
111
|
+
if (projection.nearestElement instanceof WGS84) {
|
|
112
|
+
|
|
113
|
+
for (let i = 0; i < edges.length; i++) {
|
|
114
|
+
const edge = edges[i];
|
|
115
|
+
if (edge.node1.equalsTo(projection.nearestElement)) {
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
traveledDistance += edge.getLength();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
} else if (projection.nearestElement instanceof Edge) {
|
|
123
|
+
|
|
124
|
+
for (let i = 0; i < edges.length; i++) {
|
|
125
|
+
const edge = edges[i];
|
|
126
|
+
if (edge.equalsTo(projection.nearestElement)) {
|
|
127
|
+
traveledDistance += edge.node1.distanceTo(projection.projection);
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
traveledDistance += edge.getLength();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
} else {
|
|
134
|
+
throw new Error('No projection found');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
projection: projection,
|
|
139
|
+
traveledDistance,
|
|
140
|
+
traveledPercentage: traveledDistance / totalDistance,
|
|
141
|
+
remainingDistance: totalDistance - traveledDistance,
|
|
142
|
+
remainingPercentage: 1 - traveledDistance / totalDistance
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export default MapMatchingProvider;
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/* eslint max-statements: ["error", 40]*/
|
|
2
|
+
|
|
3
|
+
import { WGS84UserPosition } from '@wemap/geo';
|
|
4
|
+
|
|
5
|
+
import MapMatchingProvider from '../others/MapMatchingProvider';
|
|
6
|
+
import PdrProvider from './pdr/PdrProvider';
|
|
7
|
+
import GnssWifiProvider from '../position/GnssWifiProvider';
|
|
8
|
+
import EventType from '../../events/EventType';
|
|
9
|
+
import AbsoluteAttitudeProvider from '../attitude/AbsoluteAttitudeProvider';
|
|
10
|
+
import Constants from '../Constants';
|
|
11
|
+
import ProviderError from '../../events/ProviderError';
|
|
12
|
+
|
|
13
|
+
const GPF_ACCURACY = 25;
|
|
14
|
+
const GPF_DISTANCE = 25;
|
|
15
|
+
|
|
16
|
+
const MM_GNSS_DIST = 20;
|
|
17
|
+
const MM_GNSS_ANGLE = 20;
|
|
18
|
+
|
|
19
|
+
class GnssWifiPdrProvider extends MapMatchingProvider {
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @override
|
|
23
|
+
*/
|
|
24
|
+
constructor(onEvent, onError, options) {
|
|
25
|
+
super(onEvent, onError, options);
|
|
26
|
+
|
|
27
|
+
this.pdrProvider = new PdrProvider(e => this.onPdrEvent(e), e => this.onProviderError(e), options);
|
|
28
|
+
this.gnssWifiProvider = new GnssWifiProvider(e => this.onGnssWifiEvent(e), e => this.onProviderError(e), options);
|
|
29
|
+
this.absoluteAttitudeProvider = new AbsoluteAttitudeProvider(
|
|
30
|
+
e => this.onAbsoluteAttitudeEvent(e),
|
|
31
|
+
e => this.onProviderError(e),
|
|
32
|
+
options
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
this.gpsLastUpdate = 0;
|
|
36
|
+
this.isFirstGnssUpdate = true;
|
|
37
|
+
this.isFirstAttitudeUpdate = true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @override
|
|
43
|
+
*/
|
|
44
|
+
static get displayName() {
|
|
45
|
+
return 'GnssWifiPdr';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @override
|
|
50
|
+
*/
|
|
51
|
+
static get eventsType() {
|
|
52
|
+
return [EventType.AbsoluteAttitude, EventType.AbsolutePosition];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @override
|
|
57
|
+
*/
|
|
58
|
+
static get requiredProviders() {
|
|
59
|
+
return [PdrProvider, GnssWifiProvider, AbsoluteAttitudeProvider];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @override
|
|
65
|
+
*/
|
|
66
|
+
startInternal() {
|
|
67
|
+
this.pdrProvider.start();
|
|
68
|
+
this.gnssWifiProvider.start();
|
|
69
|
+
this.absoluteAttitudeProvider.start();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @override
|
|
74
|
+
*/
|
|
75
|
+
stop() {
|
|
76
|
+
this.imuProvider.stop();
|
|
77
|
+
this.gnssWifiProvider.stop();
|
|
78
|
+
this.absoluteAttitudeProvider.stop();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @private
|
|
83
|
+
*/
|
|
84
|
+
onPdrEvent(pdrEvent) {
|
|
85
|
+
pdrEvent.forEach(event => {
|
|
86
|
+
if (event.dataType === EventType.AbsolutePosition) {
|
|
87
|
+
this.location = event.data;
|
|
88
|
+
} else if (event.dataType === EventType.AbsoluteAttitude) {
|
|
89
|
+
this.attitude = event.data;
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
this.notify(...pdrEvent);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @private
|
|
97
|
+
*/
|
|
98
|
+
onGnssWifiEvent(events) {
|
|
99
|
+
|
|
100
|
+
const gnssWifiEvent = events[0];
|
|
101
|
+
const location = gnssWifiEvent.data;
|
|
102
|
+
|
|
103
|
+
// This should be called to update True North / Magnetic North declination
|
|
104
|
+
this.absoluteAttitudeProvider.setLocation(location);
|
|
105
|
+
|
|
106
|
+
if (location.accuracy > GPF_ACCURACY) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
this.gnssLocation = location.clone();
|
|
111
|
+
this.gnssLocation.alt = Constants.DEFAULT_ALTITUDE;
|
|
112
|
+
|
|
113
|
+
if (!this.location || this.location
|
|
114
|
+
&& this.location.distanceTo(this.gnssLocation) > GPF_DISTANCE) {
|
|
115
|
+
|
|
116
|
+
if (!this.mapMatching || !this.attitude) {
|
|
117
|
+
this.pdrProvider.setLocation(this.gnssLocation);
|
|
118
|
+
} else {
|
|
119
|
+
|
|
120
|
+
this.gnssLocation.bearing = this.attitude.headingDegrees;
|
|
121
|
+
const projection = this.mapMatching.getProjection(this.gnssLocation);
|
|
122
|
+
|
|
123
|
+
if (projection && projection.projection) {
|
|
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);
|
|
128
|
+
|
|
129
|
+
// // If nearest element is an edge, use its orientation to set heading
|
|
130
|
+
// if (projection.nearestElement instanceof Edge) {
|
|
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);
|
|
134
|
+
// this.pdrProvider.setHeading(diff1 < diff2 ? edgeBearing : edgeBearing + Math.PI);
|
|
135
|
+
// }
|
|
136
|
+
|
|
137
|
+
if (this.lastAttitude) {
|
|
138
|
+
this.pdrProvider.setHeading(this.lastAttitude.heading);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
} else {
|
|
142
|
+
this.pdrProvider.setLocation(this.gnssLocation);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
onAbsoluteAttitudeEvent(events) {
|
|
150
|
+
|
|
151
|
+
const attitude = events[0].data;
|
|
152
|
+
|
|
153
|
+
if (this.isFirstAttitudeUpdate) {
|
|
154
|
+
this.pdrProvider.setHeading(attitude.heading);
|
|
155
|
+
this.isFirstAttitudeUpdate = false;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
this.lastAttitude = attitude;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
onProviderError(errors) {
|
|
162
|
+
this.notifyError(...ProviderError.modifyArrayDataType(errors, EventType.AbsoluteAttitude));
|
|
163
|
+
this.notifyError(...ProviderError.modifyArrayDataType(errors, EventType.AbsolutePosition));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* @override
|
|
168
|
+
*/
|
|
169
|
+
static checkAvailabilityErrors() {
|
|
170
|
+
const errors = super.checkAvailabilityErrors();
|
|
171
|
+
return ProviderError.modifyArrayDataType(
|
|
172
|
+
errors,
|
|
173
|
+
EventType.AbsolutePosition
|
|
174
|
+
).concat(
|
|
175
|
+
ProviderError.modifyArrayDataType(
|
|
176
|
+
errors,
|
|
177
|
+
EventType.AbsoluteAttitude
|
|
178
|
+
)
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* MapMatching
|
|
184
|
+
*/
|
|
185
|
+
|
|
186
|
+
enableMapMatching(network = null, maxDistance = MM_GNSS_DIST, maxAngleBearing = MM_GNSS_ANGLE) {
|
|
187
|
+
this.pdrProvider.enableMapMatching(network, maxDistance, maxAngleBearing);
|
|
188
|
+
super.enableMapMatching(network, maxDistance, maxAngleBearing);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
setNetwork(network) {
|
|
193
|
+
this.pdrProvider.setNetwork(network);
|
|
194
|
+
super.setNetwork(network);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Itinerary
|
|
199
|
+
*/
|
|
200
|
+
|
|
201
|
+
setItinerary(itinerary) {
|
|
202
|
+
|
|
203
|
+
const isFirstItinerary = !this.itinerary;
|
|
204
|
+
|
|
205
|
+
super.setItinerary(itinerary);
|
|
206
|
+
|
|
207
|
+
if (isFirstItinerary && itinerary.length > 0) {
|
|
208
|
+
|
|
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
|
+
|
|
211
|
+
if (!this.gnssLocation
|
|
212
|
+
|| itinerary.length < 2
|
|
213
|
+
|| !itinerary.points[0].equalsTo(this.gnssLocation)) {
|
|
214
|
+
console.warn('Itinerary has not been calculated from GnssWifiPdrProvider and these is not recommanded');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
let startEdge;
|
|
218
|
+
if (itinerary.firstEdge.getLength() <= MM_GNSS_DIST) {
|
|
219
|
+
startEdge = itinerary.firstEdge;
|
|
220
|
+
} else {
|
|
221
|
+
startEdge = itinerary.secondEdge;
|
|
222
|
+
}
|
|
223
|
+
const startPoint = WGS84UserPosition.fromWGS84(startEdge.node1);
|
|
224
|
+
startPoint.alt = Constants.DEFAULT_ALTITUDE;
|
|
225
|
+
this.pdrProvider.setLocation(startPoint);
|
|
226
|
+
this.pdrProvider.setStepDetectionLockerOrientation(startEdge.getBearing());
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export default GnssWifiPdrProvider;
|