@wemap/providers 3.0.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.
Files changed (102) hide show
  1. package/babel.config.js +11 -0
  2. package/config.json +4 -0
  3. package/debug/absolute-attitude.html +16 -0
  4. package/debug/absolute-position.html +16 -0
  5. package/debug/attitude.html +16 -0
  6. package/debug/components/AbsoluteAttitudeComponent.jsx +142 -0
  7. package/debug/components/AbsolutePositionComponent.jsx +79 -0
  8. package/debug/components/AttitudeComponent.jsx +40 -0
  9. package/debug/components/Common.css +27 -0
  10. package/debug/components/GnssWifiComponent.jsx +53 -0
  11. package/debug/components/ImuComponent.jsx +53 -0
  12. package/debug/components/InclinationComponent.jsx +68 -0
  13. package/debug/components/MapComponent.jsx +366 -0
  14. package/debug/components/NavigationConfig.js +112 -0
  15. package/debug/components/PoseComponent.jsx +168 -0
  16. package/debug/components/RelativeAttitudeComponent.jsx +85 -0
  17. package/debug/components/StartStopComponent.jsx +45 -0
  18. package/debug/components/StepDetectionComponent.jsx +39 -0
  19. package/debug/components/Utils.js +216 -0
  20. package/debug/components/index.js +30 -0
  21. package/debug/components/old/PositioningComponent.jsx +29 -0
  22. package/debug/components/old/PositioningInclinationComponent.jsx +82 -0
  23. package/debug/components/old/PositioningPoseComponent.jsx +117 -0
  24. package/debug/gnss-wifi.html +16 -0
  25. package/debug/imu.html +16 -0
  26. package/debug/inclination.html +16 -0
  27. package/debug/pose.html +16 -0
  28. package/debug/positioning-legacy.html +16 -0
  29. package/debug/relative-attitude.html +16 -0
  30. package/debug/step-detection.html +16 -0
  31. package/index.js +7 -0
  32. package/package.json +67 -0
  33. package/src/Providers.js +80 -0
  34. package/src/ProvidersInterface.js +125 -0
  35. package/src/ProvidersOptions.js +29 -0
  36. package/src/errors/AskImuOnDesktopError.js +9 -0
  37. package/src/errors/ContainsIgnoredProviderError.js +9 -0
  38. package/src/errors/GeolocationApiMissingError.js +9 -0
  39. package/src/errors/GeolocationPermissionDeniedError.js +9 -0
  40. package/src/errors/GeolocationPositionUnavailableError.js +9 -0
  41. package/src/errors/IpResolveServerError.js +9 -0
  42. package/src/errors/MissingAccelerometerError.js +11 -0
  43. package/src/errors/MissingArCoreError.js +9 -0
  44. package/src/errors/MissingGyroscopeError.js +11 -0
  45. package/src/errors/MissingMagnetometerError.js +9 -0
  46. package/src/errors/MissingNativeInterfaceError.js +9 -0
  47. package/src/errors/MissingSensorError.js +14 -0
  48. package/src/errors/NoProviderFoundError.js +9 -0
  49. package/src/events/Availability.js +44 -0
  50. package/src/events/EventType.js +33 -0
  51. package/src/events/ProviderEvent.js +32 -0
  52. package/src/events/ProvidersLogger.js +83 -0
  53. package/src/providers/Constants.js +5 -0
  54. package/src/providers/FakeProvider.spec.js +57 -0
  55. package/src/providers/MetaProvider.js +42 -0
  56. package/src/providers/Provider.js +314 -0
  57. package/src/providers/Provider.spec.js +136 -0
  58. package/src/providers/ProviderState.js +5 -0
  59. package/src/providers/attitude/AttitudeProvider.js +63 -0
  60. package/src/providers/attitude/EkfAttitude.js +224 -0
  61. package/src/providers/attitude/EkfAttitude.spec.js +114 -0
  62. package/src/providers/attitude/absolute/AbsoluteAttitudeFromBrowserProvider.js +224 -0
  63. package/src/providers/attitude/absolute/AbsoluteAttitudeFromRelAttProvider.js +134 -0
  64. package/src/providers/attitude/absolute/AbsoluteAttitudeProvider.js +143 -0
  65. package/src/providers/attitude/relative/RelativeAttitudeFromBrowserProvider.js +89 -0
  66. package/src/providers/attitude/relative/RelativeAttitudeFromEkfProvider.js +114 -0
  67. package/src/providers/attitude/relative/RelativeAttitudeProvider.js +103 -0
  68. package/src/providers/imu/AccelerometerProvider.js +61 -0
  69. package/src/providers/imu/GyroscopeProvider.js +61 -0
  70. package/src/providers/imu/ImuProvider.js +122 -0
  71. package/src/providers/inclination/InclinationFromAccProvider.js +87 -0
  72. package/src/providers/inclination/InclinationFromAttitudeProvider.js +77 -0
  73. package/src/providers/inclination/InclinationProvider.js +69 -0
  74. package/src/providers/legacy/AbsolutePdrProvider.js +258 -0
  75. package/src/providers/legacy/ArCoreAbsoluteProvider.js +230 -0
  76. package/src/providers/legacy/GnssWifiPdrProvider.js +217 -0
  77. package/src/providers/legacy/MapMatchingProvider.js +65 -0
  78. package/src/providers/legacy/PdrProvider.old.js +300 -0
  79. package/src/providers/legacy/PoseProvider.js +68 -0
  80. package/src/providers/legacy/helpers/HeadingUnlocker.js +47 -0
  81. package/src/providers/legacy/helpers/HeadingUnlocker.spec.js +53 -0
  82. package/src/providers/legacy/helpers/Smoother.js +92 -0
  83. package/src/providers/legacy/helpers/Smoother.spec.js +426 -0
  84. package/src/providers/legacy/helpers/ThugDetector.js +37 -0
  85. package/src/providers/others/CameraNativeProvider.js +44 -0
  86. package/src/providers/position/absolute/AbsolutePositionFromRelProvider.js +109 -0
  87. package/src/providers/position/absolute/AbsolutePositionProvider.js +172 -0
  88. package/src/providers/position/absolute/GnssWifiProvider.js +122 -0
  89. package/src/providers/position/absolute/IpProvider.js +68 -0
  90. package/src/providers/position/relative/ArCoreProvider.js +197 -0
  91. package/src/providers/position/relative/GeoRelativePositionFromArCoreProvider.js +85 -0
  92. package/src/providers/position/relative/GeoRelativePositionProvider.js +66 -0
  93. package/src/providers/position/relative/PdrProvider.js +132 -0
  94. package/src/providers/steps/StepDetectionLadetto.js +67 -0
  95. package/src/providers/steps/StepDetectionMinMaxPeaks.js +80 -0
  96. package/src/providers/steps/StepDetectionMinMaxPeaks2.js +108 -0
  97. package/src/providers/steps/StepDetectionProvider.js +100 -0
  98. package/src/smoothers/PositionSmoother.js +86 -0
  99. package/src/smoothers/PositionSmoother.spec.js +55 -0
  100. package/webpack/webpack.common.js +20 -0
  101. package/webpack/webpack.dev.js +24 -0
  102. package/webpack/webpack.prod.js +15 -0
@@ -0,0 +1,143 @@
1
+ import {
2
+ AbsoluteHeading, Attitude
3
+ } from '@wemap/geo';
4
+
5
+ import MetaProvider from '../../MetaProvider';
6
+ import EventType from '../../../events/EventType';
7
+ import Availability from '../../../events/Availability';
8
+ import {
9
+ AbsoluteAttitudeFromBrowser, AbsoluteAttitudeFromRelAtt
10
+ } from '../../../Providers';
11
+
12
+
13
+ /**
14
+ * Absolute attitude provider gives the device attitude in East-North-Up (ENU) frame
15
+ */
16
+ class AbsoluteAttitudeProvider extends MetaProvider {
17
+
18
+ constructor() {
19
+ super();
20
+
21
+ this.attitudeFromBrowserErrored = false;
22
+ this.attitudeFromRelAttErrored = false;
23
+ }
24
+
25
+ /**
26
+ * @override
27
+ */
28
+ static get displayName() {
29
+ return 'Absolute Attitude';
30
+ }
31
+
32
+ /**
33
+ * @override
34
+ */
35
+ static get eventsType() {
36
+ return [EventType.AbsoluteAttitude];
37
+ }
38
+
39
+ /**
40
+ * @override
41
+ */
42
+ get _availability() {
43
+ return Availability.union(
44
+ AbsoluteAttitudeFromBrowser.availability,
45
+ AbsoluteAttitudeFromRelAtt.availability
46
+ );
47
+ }
48
+
49
+ /**
50
+ * @override
51
+ */
52
+ start() {
53
+
54
+ this.fromBrowserProviderId = AbsoluteAttitudeFromBrowser.addEventListener(
55
+ events => this.onAttitudeFromBrowser(events[0]),
56
+ error => {
57
+ this.attitudeFromBrowserErrored = true;
58
+ this.onError(error);
59
+ },
60
+ this.name);
61
+
62
+ this.fromRelAttProviderId = AbsoluteAttitudeFromRelAtt.addEventListener(
63
+ events => this.onAttitudeFromRelAtt(events[0]),
64
+ error => {
65
+ this.attitudeFromRelAttErrored = true;
66
+ this.onError(error);
67
+ },
68
+ this.name);
69
+ }
70
+
71
+ onError(error) {
72
+ if (this.attitudeFromBrowserErrored && this.attitudeFromRelAttErrored) {
73
+ this.notifyError(error);
74
+ }
75
+ }
76
+
77
+ onAttitudeFromBrowser(event) {
78
+ this.eventFromBrowser = event;
79
+ if (!this.eventFromRelAtt
80
+ || event.data.accuracy <= this.eventFromRelAtt.data.accuracy) {
81
+ this.notify(event.clone());
82
+ }
83
+ }
84
+
85
+ onAttitudeFromRelAtt(event) {
86
+ this.eventFromRelAtt = event;
87
+ if (!this.eventFromBrowser
88
+ || event.data.accuracy <= this.eventFromBrowser.data.accuracy) {
89
+ this.notify(event.clone());
90
+ }
91
+ }
92
+
93
+ /**
94
+ * @override
95
+ */
96
+ stop() {
97
+ AbsoluteAttitudeFromBrowser.removeEventListener(this.fromBrowserProviderId);
98
+ AbsoluteAttitudeFromRelAtt.removeEventListener(this.fromRelAttProviderId);
99
+ }
100
+
101
+ /**
102
+ * @override
103
+ * @param {AbsoluteHeading|Attitude} data
104
+ */
105
+ feed(data) {
106
+
107
+ if (data instanceof AbsoluteHeading) {
108
+
109
+ if (data.time === null) {
110
+ throw Error('the time of the absolute heading is not defined');
111
+ }
112
+ if (data.accuracy === null) {
113
+ throw Error('the accuracy of the absolute heading is not defined');
114
+ }
115
+
116
+ this.notify(this.createEvent(
117
+ EventType.AbsoluteAttitude,
118
+ data.toAttitude(),
119
+ data.time
120
+ ));
121
+
122
+ } else if (data instanceof Attitude) {
123
+
124
+ if (data.time === null) {
125
+ throw Error('the time of the attitude is not defined');
126
+ }
127
+ if (data.accuracy === null) {
128
+ throw Error('the accuracy of the attitude is not defined');
129
+ }
130
+
131
+ this.notify(this.createEvent(
132
+ EventType.AbsoluteAttitude,
133
+ data,
134
+ data.time
135
+ ));
136
+
137
+ } else {
138
+ throw new Error('data is nor an AbsoluteHeading or an Attitude object');
139
+ }
140
+ }
141
+ }
142
+
143
+ export default AbsoluteAttitudeProvider;
@@ -0,0 +1,89 @@
1
+ import isnumber from 'lodash.isnumber';
2
+
3
+ import { Attitude } from '@wemap/geo';
4
+ import { Rotations } from '@wemap/maths';
5
+ import { BrowserUtils } from '@wemap/utils';
6
+
7
+ import Provider from '../../Provider';
8
+ import EventType from '../../../events/EventType';
9
+ import AskImuOnDesktopError from '../../../errors/AskImuOnDesktopError';
10
+ import MissingSensorError from '../../../errors/MissingSensorError';
11
+ import Availability from '../../../events/Availability';
12
+ import RelativeAttitudeProvider from './RelativeAttitudeProvider';
13
+
14
+
15
+ /**
16
+ * Relative attitude provider gives the device attitude in a custom frame (x-right, y-front, z-up) using
17
+ * browser deviceorientation
18
+ *
19
+ * -----------------------------------
20
+ * Overview of compatibilities:
21
+ * -----------------------------------
22
+ *
23
+ * Chrome Android (v72.0.3626): YES (via deviceorientation but deviceorientation.alpha is unreliable! Sometimes it starts at 0°, sometimes at 270°)
24
+ * Safari iOS (v12.0): YES (via deviceorientation)
25
+ * Opera Android (v50.2.2426): NO {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/ondeviceorientation}
26
+ * Firefox Android (v65.0.1): YES (via deviceorientation)
27
+ *
28
+ * -----------------------------------
29
+ */
30
+ class RelativeAttitudeFromBrowserProvider extends Provider {
31
+
32
+ /**
33
+ * @override
34
+ */
35
+ static get displayName() {
36
+ return 'Relative Attitude from Browser';
37
+ }
38
+
39
+ /**
40
+ * @override
41
+ */
42
+ static get eventsType() {
43
+ return [EventType.RelativeAttitude];
44
+ }
45
+
46
+ /**
47
+ * @override
48
+ */
49
+ get _availability() {
50
+ return BrowserUtils.isMobile
51
+ ? Availability.yes()
52
+ : Availability.no(new AskImuOnDesktopError());
53
+ }
54
+
55
+ /**
56
+ * @override
57
+ */
58
+ start() {
59
+ window.addEventListener('deviceorientation', this.onDeviceOrientationEvent, true);
60
+ }
61
+
62
+ /**
63
+ * @override
64
+ */
65
+ stop() {
66
+ window.removeEventListener('deviceorientation', this.onDeviceOrientationEvent, true);
67
+ }
68
+
69
+
70
+ onDeviceOrientationEvent = e => {
71
+
72
+ const timestamp = e.timeStamp / 1e3;
73
+
74
+ if (!isnumber(e.alpha) || !isnumber(e.beta) || !isnumber(e.gamma)) {
75
+ this.notifyError(new MissingSensorError().from('deviceorientation'));
76
+ return;
77
+ }
78
+
79
+ const quaternion = Rotations.eulerToQuaternionZXYDegrees([e.alpha, e.beta, e.gamma]);
80
+ const attitude = new Attitude(quaternion,
81
+ timestamp,
82
+ RelativeAttitudeProvider.DEFAULT_DRIFT,
83
+ this.name
84
+ );
85
+ this.notify(this.createEvent(EventType.RelativeAttitude, attitude, timestamp));
86
+ };
87
+ }
88
+
89
+ export default RelativeAttitudeFromBrowserProvider;
@@ -0,0 +1,114 @@
1
+ import { Attitude } from '@wemap/geo';
2
+
3
+ import Provider from '../../Provider';
4
+ import Availability from '../../../events/Availability';
5
+ import EventType from '../../../events/EventType';
6
+ import EkfAttitude from '../EkfAttitude';
7
+ import RelativeAttitudeProvider from './RelativeAttitudeProvider';
8
+ import {
9
+ Accelerometer, Gyroscope
10
+ } from '../../../Providers';
11
+
12
+
13
+ /**
14
+ * Relative attitude provider gives the device attitude in East-North-Up (ENU) frame using
15
+ * browser deviceorientation
16
+ * The provider does not work until an offset is given.
17
+ */
18
+ class RelativeAttitudeFromEkfProvider extends Provider {
19
+
20
+ lastTimestamp = 0;
21
+
22
+ /**
23
+ * @override
24
+ */
25
+ constructor(context) {
26
+ super(context);
27
+ this.ekfAttitude = new EkfAttitude();
28
+ }
29
+
30
+ /**
31
+ * @override
32
+ */
33
+ static get displayName() {
34
+ return 'Relative Attitude from Ekf';
35
+ }
36
+
37
+ /**
38
+ * @override
39
+ */
40
+ static get eventsType() {
41
+ return [EventType.RelativeAttitude];
42
+ }
43
+
44
+ /**
45
+ * @override
46
+ */
47
+ get _availability() {
48
+ return Availability.merge(
49
+ Accelerometer.availability,
50
+ Gyroscope.availability,
51
+ );
52
+ }
53
+
54
+ /**
55
+ * @override
56
+ */
57
+ start() {
58
+ this.accelerometerProviderId = Accelerometer.addEventListener(
59
+ events => this.onAccelerometerEvent(events[0]),
60
+ this.notifyError,
61
+ this.name);
62
+
63
+ this.gyroscopeProviderId = Gyroscope.addEventListener(
64
+ events => (this.gyroscopeEvent = events[0]),
65
+ this.notifyError,
66
+ this.name);
67
+ }
68
+
69
+ /**
70
+ * @override
71
+ */
72
+ stop() {
73
+ Accelerometer.removeEventListener(this.accelerometerProviderId);
74
+ Gyroscope.removeEventListener(this.gyroscopeProviderId);
75
+ }
76
+
77
+ /**
78
+ * @private
79
+ */
80
+ onAccelerometerEvent = accelerationEvent => {
81
+
82
+ if (!this.gyroscopeEvent) {
83
+ return;
84
+ }
85
+
86
+ const acceleration = accelerationEvent.data;
87
+ const { timestamp } = accelerationEvent;
88
+
89
+ // Handle timestamps and dt
90
+ if (this.lastTimestamp === 0) {
91
+ this.lastTimestamp = timestamp;
92
+ return;
93
+ }
94
+ const diffTime = timestamp - this.lastTimestamp;
95
+ this.lastTimestamp = timestamp;
96
+
97
+ const quaternion = this.ekfAttitude.update(diffTime, acceleration, this.gyroscopeEvent.data);
98
+
99
+ if (quaternion) {
100
+ const attitude = new Attitude(quaternion,
101
+ timestamp,
102
+ RelativeAttitudeProvider.DEFAULT_DRIFT,
103
+ this.name
104
+ );
105
+ this.notify(this.createEvent(
106
+ EventType.RelativeAttitude,
107
+ attitude,
108
+ timestamp,
109
+ [accelerationEvent, this.gyroscopeEvent]));
110
+ }
111
+ };
112
+ }
113
+
114
+ export default RelativeAttitudeFromEkfProvider;
@@ -0,0 +1,103 @@
1
+ import { deg2rad } from '@wemap/maths';
2
+
3
+ import Provider from '../../Provider';
4
+ import EventType from '../../../events/EventType';
5
+ import Availability from '../../../events/Availability';
6
+ import noop from 'lodash.noop';
7
+ import ProviderState from '../../ProviderState';
8
+ import {
9
+ RelativeAttitudeFromEkf, RelativeAttitudeFromBrowser, ArCore
10
+ } from '../../../Providers';
11
+
12
+
13
+ /**
14
+ * Relative attitude provider gives the device attitude in East-North-Up (ENU) frame using
15
+ * browser deviceorientation
16
+ * The provider does not work until an offset is given.
17
+ */
18
+ class RelativeAttitudeProvider extends Provider {
19
+ /**
20
+ * default relative attitude drift in rad.second-1
21
+ */
22
+ static DEFAULT_DRIFT = deg2rad(15) / 60;
23
+
24
+
25
+ lastTimestamp = 0;
26
+
27
+ /**
28
+ * @override
29
+ */
30
+ static get displayName() {
31
+ return 'Relative Attitude';
32
+ }
33
+
34
+ /**
35
+ * @override
36
+ */
37
+ static get eventsType() {
38
+ return [EventType.RelativeAttitude];
39
+ }
40
+
41
+ /**
42
+ * @override
43
+ */
44
+ get _availability() {
45
+ return Availability.union(
46
+ RelativeAttitudeFromEkf.availability,
47
+ RelativeAttitudeFromBrowser.availability
48
+ );
49
+ }
50
+
51
+ /**
52
+ * @override
53
+ */
54
+ start() {
55
+ this.provider = RelativeAttitudeFromEkf.availability.isSupported
56
+ ? RelativeAttitudeFromEkf
57
+ : RelativeAttitudeFromBrowser;
58
+
59
+ this.listenerId = this.provider.addEventListener(
60
+ events => {
61
+ if (ArCore.state === ProviderState.STOPPPED) {
62
+ this.notify(events[0].clone());
63
+ }
64
+ },
65
+ this.notifyError,
66
+ this.name);
67
+
68
+
69
+ this.arCoreMonitoringId = ArCore.addMonitoringListener(this.listenArCore, this.unlistenArCore);
70
+ if (ArCore.state === ProviderState.STARTED) {
71
+ this.listenArCore();
72
+ }
73
+ }
74
+
75
+ /**
76
+ * @override
77
+ */
78
+ stop() {
79
+ this.provider.removeEventListener(this.listenerId);
80
+ ArCore.removeMonitoringListener(this.arCoreMonitoringId);
81
+ this.unlistenArCore();
82
+ }
83
+
84
+ listenArCore = () => {
85
+ this.arCoreProviderId = ArCore.addEventListener(
86
+ events => {
87
+ const relativeAttitudeEvent = events.find(event => event.dataType === EventType.RelativeAttitude);
88
+ if (relativeAttitudeEvent) {
89
+ this.notify(relativeAttitudeEvent.clone());
90
+ }
91
+ },
92
+ noop,
93
+ this.name,
94
+ false
95
+ );
96
+ };
97
+
98
+ unlistenArCore = () => {
99
+ ArCore.removeEventListener(this.arCoreProviderId);
100
+ }
101
+ }
102
+
103
+ export default RelativeAttitudeProvider;
@@ -0,0 +1,61 @@
1
+ import Provider from '../Provider';
2
+ import EventType from '../../events/EventType';
3
+ import MissingAccelerometerError from '../../errors/MissingAccelerometerError';
4
+ import { Imu } from '../../Providers';
5
+
6
+ class AccelerometerProvider extends Provider {
7
+
8
+ /**
9
+ * @override
10
+ */
11
+ static get displayName() {
12
+ return 'Accelerometer';
13
+ }
14
+
15
+ /**
16
+ * @override
17
+ */
18
+ static get eventsType() {
19
+ return [EventType.Acceleration];
20
+ }
21
+
22
+ /**
23
+ * @override
24
+ */
25
+ get _availability() {
26
+ return Imu.availability;
27
+ }
28
+
29
+ /**
30
+ * @override
31
+ */
32
+ start() {
33
+ this.providerId = Imu.addEventListener(
34
+ events => this.parseImuEvents(events),
35
+ this.notifyError,
36
+ this.name);
37
+ }
38
+
39
+ /**
40
+ * @override
41
+ */
42
+ stop() {
43
+ Imu.removeEventListener(this.providerId);
44
+ }
45
+
46
+ /**
47
+ * @private
48
+ */
49
+ parseImuEvents = events => {
50
+
51
+ const accelerationEvent = events.find(event => event.dataType === EventType.Acceleration);
52
+ if (!accelerationEvent) {
53
+ this.notifyError(new MissingAccelerometerError().from('devicemotion'));
54
+ return;
55
+ }
56
+ this.notify(accelerationEvent.clone());
57
+ }
58
+
59
+ }
60
+
61
+ export default AccelerometerProvider;
@@ -0,0 +1,61 @@
1
+ import Provider from '../Provider';
2
+ import EventType from '../../events/EventType';
3
+ import MissingGyroscopeError from '../../errors/MissingGyroscopeError';
4
+ import { Imu } from '../../Providers';
5
+
6
+ class GyroscopeProvider extends Provider {
7
+
8
+ /**
9
+ * @override
10
+ */
11
+ static get displayName() {
12
+ return 'Gyroscope';
13
+ }
14
+
15
+ /**
16
+ * @override
17
+ */
18
+ static get eventsType() {
19
+ return [EventType.AngularRate];
20
+ }
21
+
22
+ /**
23
+ * @override
24
+ */
25
+ get _availability() {
26
+ return Imu.availability;
27
+ }
28
+
29
+ /**
30
+ * @override
31
+ */
32
+ start() {
33
+ this.providerId = Imu.addEventListener(
34
+ events => this.parseImuEvents(events),
35
+ this.notifyError,
36
+ this.name);
37
+ }
38
+
39
+ /**
40
+ * @override
41
+ */
42
+ stop() {
43
+ Imu.removeEventListener(this.providerId);
44
+ }
45
+
46
+ /**
47
+ * @private
48
+ */
49
+ parseImuEvents = events => {
50
+
51
+ const gyroscopeEvent = events.find(event => event.dataType === EventType.AngularRate);
52
+ if (!gyroscopeEvent) {
53
+ this.notifyError(new MissingGyroscopeError().from('devicemotion'));
54
+ return;
55
+ }
56
+ this.notify(gyroscopeEvent.clone());
57
+ }
58
+
59
+ }
60
+
61
+ export default GyroscopeProvider;
@@ -0,0 +1,122 @@
1
+ import isnumber from 'lodash.isnumber';
2
+
3
+ import { deg2rad } from '@wemap/maths';
4
+ import {
5
+ Browser, BrowserUtils
6
+ } from '@wemap/utils';
7
+
8
+ import Provider from '../Provider';
9
+ import EventType from '../../events/EventType';
10
+ import AskImuOnDesktopError from '../../errors/AskImuOnDesktopError';
11
+ import Availability from '../../events/Availability';
12
+
13
+ /**
14
+ * Imu (Inertial Measurement Unit) provider retrieve acceleration data
15
+ * and/or angular rate data from inertial sensors.
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
+ /**
31
+ * @override
32
+ */
33
+ static get displayName() {
34
+ return 'Inertial Measurement Unit';
35
+ }
36
+
37
+ /**
38
+ * @override
39
+ */
40
+ static get eventsType() {
41
+ return [EventType.AngularRate, EventType.Acceleration];
42
+ }
43
+
44
+ /**
45
+ * @override
46
+ */
47
+ get _availability() {
48
+ return BrowserUtils.isMobile
49
+ ? Availability.yes()
50
+ : Availability.no(new AskImuOnDesktopError());
51
+ }
52
+
53
+ /**
54
+ * @override
55
+ */
56
+ start() {
57
+ window.addEventListener('devicemotion', this.parseDeviceMotionEvent, true);
58
+ }
59
+
60
+ /**
61
+ * @override
62
+ */
63
+ stop() {
64
+ window.removeEventListener('devicemotion', this.parseDeviceMotionEvent, true);
65
+ }
66
+
67
+ /**
68
+ * devicemotion callback
69
+ * @param {DeviceMotionEvent} e device motion event
70
+ * @returns {ProviderEvent[]} an array of provider events
71
+ * @private
72
+ */
73
+ parseDeviceMotionEvent = e => {
74
+
75
+ const events = [];
76
+
77
+ const timestamp = e.timeStamp / 1e3;
78
+
79
+ let acc;
80
+ if (e.accelerationIncludingGravity) {
81
+ const {
82
+ x, y, z
83
+ } = e.accelerationIncludingGravity;
84
+
85
+ if (isnumber(x) && isnumber(y) && isnumber(z)) {
86
+ acc = [x, y, z];
87
+
88
+ if (BrowserUtils.name === Browser.SAFARI) {
89
+ acc[0] *= -1;
90
+ acc[1] *= -1;
91
+ acc[2] *= -1;
92
+ }
93
+ }
94
+ }
95
+
96
+ if (acc) {
97
+ events.push(this.createEvent(EventType.Acceleration, acc, timestamp));
98
+ }
99
+
100
+ let gyr;
101
+ if (e.rotationRate) {
102
+ const {
103
+ alpha, beta, gamma
104
+ } = e.rotationRate;
105
+
106
+ if (isnumber(alpha) && isnumber(beta) && isnumber(gamma)) {
107
+ gyr = [deg2rad(alpha), deg2rad(beta), deg2rad(gamma)];
108
+ }
109
+ }
110
+
111
+ if (gyr) {
112
+ events.push(this.createEvent(EventType.AngularRate, gyr, timestamp));
113
+ }
114
+
115
+ if (events.length !== 0) {
116
+ this.notify(...events);
117
+ }
118
+ }
119
+
120
+ }
121
+
122
+ export default ImuProvider;