@wemap/positioning 1.2.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 +479 -0
- package/.nvmrc +1 -0
- package/babel.config.js +11 -0
- package/config.json +7 -0
- package/debug/index.html +15 -0
- package/debug/index.old.html +37 -0
- package/package.json +82 -0
- package/scripts/release-github.js +216 -0
- package/src/Constants.js +11 -0
- package/src/NavigationHandler.js +244 -0
- package/src/Pose.js +8 -0
- package/src/attitude/Attitude.js +65 -0
- package/src/attitude/AttitudeHandler.js +343 -0
- package/src/attitude/EkfAttitude.js +238 -0
- package/src/attitude/EkfAttitude.spec.js +116 -0
- package/src/components/AbsoluteAttitude.jsx +136 -0
- package/src/components/Imu.jsx +89 -0
- package/src/components/LocationSource.jsx +434 -0
- package/src/components/Logger.jsx +113 -0
- package/src/components/NavigationDebugApp.jsx +106 -0
- package/src/components/Others.jsx +121 -0
- package/src/components/RelativeAttitude.jsx +104 -0
- package/src/components/Utils.js +35 -0
- package/src/components/index.js +13 -0
- package/src/index.js +9 -0
- package/src/providers/FixedLocationImuLocationSource.js +66 -0
- package/src/providers/GnssLocationSource.js +118 -0
- package/src/providers/GnssPdrLocationSource.js +182 -0
- package/src/providers/IPLocationSource.js +96 -0
- package/src/providers/LocationSource.js +290 -0
- package/src/providers/PdrLocationSource.js +312 -0
- package/src/providers/ProvidersLogger.js +77 -0
- package/src/providers/pdr/HeadingUnlocker.js +41 -0
- package/src/providers/pdr/HeadingUnlocker.spec.js +26 -0
- package/src/providers/pdr/Smoother.js +90 -0
- package/src/providers/pdr/Smoother.spec.js +424 -0
- package/src/providers/pdr/ThugDetector.js +37 -0
- package/src/providers/steps/StepDetection.js +7 -0
- package/src/providers/steps/StepDetectionLadetto.js +67 -0
- package/src/providers/steps/StepDetectionMinMaxPeaks.js +80 -0
- package/src/providers/steps/StepDetectionMinMaxPeaks2.js +108 -0
- package/src/sensors/SensorsCompatibility.js +484 -0
- package/src/sensors/SensorsCompatibility.spec.js +270 -0
- package/src/sensors/SensorsLogger.js +94 -0
- package/src/sensors/SensorsLoggerUtils.js +35 -0
- package/src.new/NavigationHandler.js +62 -0
- package/src.new/index.js +3 -0
- package/src.new/providers/FakeLocationSource.js +39 -0
- package/webpack/webpack.common.js +20 -0
- package/webpack/webpack.dev.js +24 -0
- package/webpack/webpack.prod.js +15 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { WGS84 } from '@wemap/geo';
|
|
4
|
+
|
|
5
|
+
import AttitudeHandler from '../attitude/AttitudeHandler';
|
|
6
|
+
import Utils from './Utils';
|
|
7
|
+
|
|
8
|
+
const userLocation = new WGS84(43.609275, 3.884236);
|
|
9
|
+
|
|
10
|
+
class AbsoluteAttitude extends React.Component {
|
|
11
|
+
|
|
12
|
+
constructor(props, context) {
|
|
13
|
+
super(props, context);
|
|
14
|
+
|
|
15
|
+
this.attitudeHandlerBrowser = new AttitudeHandler();
|
|
16
|
+
this.attitudeHandlerEkf = new AttitudeHandler();
|
|
17
|
+
this.attitudeHandlerCustom = new AttitudeHandler();
|
|
18
|
+
// this.attitudeHandlerAutomatic = new AttitudeHandler();
|
|
19
|
+
|
|
20
|
+
this.state = {
|
|
21
|
+
attitudeBrowser: null,
|
|
22
|
+
attitudeEkf: null,
|
|
23
|
+
attitudeCustom: null,
|
|
24
|
+
// attitudeAuto: null,
|
|
25
|
+
deviceorientation: null,
|
|
26
|
+
deviceorientationabsolute: null
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
componentDidMount() {
|
|
32
|
+
const browserPromise = this.attitudeHandlerBrowser.startAbsolute(attitude => this.setState({ attitudeBrowser: attitude }), AttitudeHandler.AbsoluteMethod.BROWSER);
|
|
33
|
+
this.attitudeHandlerBrowser.setUserLocationForAbsolute(userLocation);
|
|
34
|
+
browserPromise.catch(() =>
|
|
35
|
+
this.setState({ attitudeBrowser: -1 })
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const ekfPromise = this.attitudeHandlerEkf.startAbsolute(attitude => this.setState({ attitudeEkf: attitude }), AttitudeHandler.AbsoluteMethod.INTERNAL_EKF);
|
|
39
|
+
this.attitudeHandlerEkf.setUserLocationForAbsolute(userLocation);
|
|
40
|
+
ekfPromise.catch(() =>
|
|
41
|
+
this.setState({ attitudeEkf: -1 })
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const customPromise = this.attitudeHandlerCustom.startAbsolute(attitude => this.setState({ attitudeCustom: attitude }), AttitudeHandler.AbsoluteMethod.INTERNAL_CUSTOM);
|
|
45
|
+
this.attitudeHandlerCustom.setUserLocationForAbsolute(userLocation);
|
|
46
|
+
customPromise.catch(() =>
|
|
47
|
+
this.setState({ attitudeCustom: -1 })
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
// const autoPromise = this.attitudeHandlerAutomatic.startAbsolute(attitude => this.setState({ attitudeAuto: attitude }), AttitudeHandler.AbsoluteMethod.AUTOMATIC);
|
|
51
|
+
// this.attitudeHandlerAutomatic.setUserLocationForAbsolute(userLocation);
|
|
52
|
+
// autoPromise.catch(() =>
|
|
53
|
+
// this.setState({ attitudeAuto: -1 })
|
|
54
|
+
// );
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
this.onDeviceOrientationEventListener = (e) =>
|
|
58
|
+
this.setState({ deviceorientation: e });
|
|
59
|
+
|
|
60
|
+
window.addEventListener('deviceorientation', this.onDeviceOrientationEventListener, true);
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
this.onDeviceOrientationAbsoluteEventListener = (e) =>
|
|
64
|
+
this.setState({ deviceorientationabsolute: e });
|
|
65
|
+
|
|
66
|
+
window.addEventListener('deviceorientationabsolute', this.onDeviceOrientationAbsoluteEventListener, true);
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
componentWillUnmount() {
|
|
71
|
+
this.attitudeHandlerBrowser.stopAbsolute();
|
|
72
|
+
this.attitudeHandlerEkf.stopAbsolute();
|
|
73
|
+
this.attitudeHandlerCustom.stopAbsolute();
|
|
74
|
+
// this.attitudeHandlerAutomatic.stopAbsolute();
|
|
75
|
+
window.removeEventListener('deviceorientation', this.onDeviceOrientationEventListener, true);
|
|
76
|
+
window.removeEventListener('deviceorientationabsolute', this.onDeviceOrientationAbsoluteEventListener, true);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
render() {
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
let rawRender = <span>Not available</span>;
|
|
84
|
+
|
|
85
|
+
if (this.state.deviceorientation && this.state.deviceorientation.webkitCompassHeading) {
|
|
86
|
+
const alpha = this.state.deviceorientation.alpha;
|
|
87
|
+
const beta = this.state.deviceorientation.beta;
|
|
88
|
+
const gamma = this.state.deviceorientation.gamma;
|
|
89
|
+
const webkitCompassHeading = this.state.deviceorientation.webkitCompassHeading;
|
|
90
|
+
|
|
91
|
+
if (alpha && beta && gamma && webkitCompassHeading) {
|
|
92
|
+
rawRender = <p>alpha: {alpha.toFixed(2)}, <br />
|
|
93
|
+
beta: {beta.toFixed(2)}, <br />
|
|
94
|
+
gamma: {gamma.toFixed(2)}, <br />
|
|
95
|
+
webkitCompassHeading: {webkitCompassHeading.toFixed(2)}</p>;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
} else if (this.state.deviceorientationabsolute) {
|
|
99
|
+
const alpha = this.state.deviceorientationabsolute.alpha;
|
|
100
|
+
const beta = this.state.deviceorientationabsolute.beta;
|
|
101
|
+
const gamma = this.state.deviceorientationabsolute.gamma;
|
|
102
|
+
|
|
103
|
+
if (alpha && beta && gamma) {
|
|
104
|
+
rawRender = <p>alpha: {alpha.toFixed(2)}, <br />
|
|
105
|
+
beta: {beta.toFixed(2)}, <br />
|
|
106
|
+
gamma: {gamma.toFixed(2)}</p>;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const browserRender = Utils.renderAttitude(this.state.attitudeBrowser);
|
|
111
|
+
const ekfRender = Utils.renderAttitude(this.state.attitudeEkf);
|
|
112
|
+
const customRender = Utils.renderAttitude(this.state.attitudeCustom);
|
|
113
|
+
// const autoRender = Utils.renderAttitude(this.state.attitudeAuto);
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<div>
|
|
117
|
+
<h3>Raw:</h3>
|
|
118
|
+
{rawRender}
|
|
119
|
+
<h3>From browser:</h3>
|
|
120
|
+
{browserRender}
|
|
121
|
+
<h3>From Ekf:</h3>
|
|
122
|
+
{ekfRender}
|
|
123
|
+
<h3>From Custom:</h3>
|
|
124
|
+
{customRender}
|
|
125
|
+
{
|
|
126
|
+
|
|
127
|
+
/* <h3>From Automatic:</h3>
|
|
128
|
+
{autoRender} */
|
|
129
|
+
}
|
|
130
|
+
</div>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export default AbsoluteAttitude;
|
|
136
|
+
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import SensorsCompatibility from '../sensors/SensorsCompatibility';
|
|
4
|
+
|
|
5
|
+
class Imu extends React.Component {
|
|
6
|
+
|
|
7
|
+
constructor(props, context) {
|
|
8
|
+
super(props, context);
|
|
9
|
+
|
|
10
|
+
this.brAcc = new SensorsCompatibility();
|
|
11
|
+
this.brGry = new SensorsCompatibility();
|
|
12
|
+
this.brMag = new SensorsCompatibility();
|
|
13
|
+
|
|
14
|
+
this.state = {
|
|
15
|
+
acc: null,
|
|
16
|
+
gyr: null,
|
|
17
|
+
mag: null
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
componentDidMount() {
|
|
22
|
+
this.brAcc.startImu(imu => {
|
|
23
|
+
this.setState({ acc: imu.acc });
|
|
24
|
+
}, { accelerometer: true }, 10).catch(() => this.setState({ acc: -1 }));
|
|
25
|
+
|
|
26
|
+
this.brGry.startImu(imu => {
|
|
27
|
+
this.setState({ gyr: imu.gyr });
|
|
28
|
+
}, { gyroscope: true }, 10).catch(() => this.setState({ gyr: -1 }));
|
|
29
|
+
|
|
30
|
+
this.brMag.startImu(imu => {
|
|
31
|
+
this.setState({ mag: imu.mag });
|
|
32
|
+
}, { magnetometer: true }, 10).catch(() => this.setState({ mag: -1 }));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
componentWillUnmount() {
|
|
36
|
+
this.brAcc.stopImu();
|
|
37
|
+
this.brGry.stopImu();
|
|
38
|
+
this.brMag.stopImu();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
render() {
|
|
42
|
+
|
|
43
|
+
let accelerometerString = 'Acceleration: ';
|
|
44
|
+
const acc = this.state.acc;
|
|
45
|
+
if (!acc) {
|
|
46
|
+
accelerometerString += 'Waiting';
|
|
47
|
+
} else if (acc === -1) {
|
|
48
|
+
accelerometerString += 'Not available';
|
|
49
|
+
} else {
|
|
50
|
+
accelerometerString += '[' + acc[0].toFixed(2)
|
|
51
|
+
+ ', ' + acc[1].toFixed(2)
|
|
52
|
+
+ ', ' + acc[2].toFixed(2)
|
|
53
|
+
+ ']';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let gyroscopeString = 'Angular Velocity: ';
|
|
57
|
+
const gyr = this.state.gyr;
|
|
58
|
+
if (!gyr) {
|
|
59
|
+
gyroscopeString += 'Waiting';
|
|
60
|
+
} else if (gyr === -1) {
|
|
61
|
+
gyroscopeString += 'Not available';
|
|
62
|
+
} else {
|
|
63
|
+
gyroscopeString += '[' + gyr[0].toFixed(2)
|
|
64
|
+
+ ', ' + gyr[1].toFixed(2)
|
|
65
|
+
+ ', ' + gyr[2].toFixed(2)
|
|
66
|
+
+ ']';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let magnetometerString = 'Magnetic Field: ';
|
|
70
|
+
const mag = this.state.mag;
|
|
71
|
+
if (!mag) {
|
|
72
|
+
magnetometerString += 'Waiting';
|
|
73
|
+
} else if (mag === -1) {
|
|
74
|
+
magnetometerString += 'Not available';
|
|
75
|
+
} else {
|
|
76
|
+
magnetometerString += '[' + mag[0].toFixed(2)
|
|
77
|
+
+ ', ' + mag[1].toFixed(2)
|
|
78
|
+
+ ', ' + mag[2].toFixed(2)
|
|
79
|
+
+ ']';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return (<p>{accelerometerString}<br />
|
|
83
|
+
{gyroscopeString}<br />
|
|
84
|
+
{magnetometerString}</p>);
|
|
85
|
+
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export default Imu;
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import {
|
|
4
|
+
FormControl, Select, MenuItem
|
|
5
|
+
} from '@material-ui/core';
|
|
6
|
+
import { withStyles } from '@material-ui/core/styles';
|
|
7
|
+
import mapboxgl from 'mapbox-gl';
|
|
8
|
+
import 'mapbox-gl/dist/mapbox-gl.css';
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
WGS84UserPosition, Itinerary, Edge
|
|
12
|
+
} from '@wemap/geo';
|
|
13
|
+
import { Utils as MathUtils } from '@wemap/maths';
|
|
14
|
+
|
|
15
|
+
import IPLocationSource from '../providers/IPLocationSource';
|
|
16
|
+
import GnssLocationSource from '../providers/GnssLocationSource';
|
|
17
|
+
import PdrLocationSource from '../providers/PdrLocationSource';
|
|
18
|
+
import GnssPdrLocationSource from '../providers/GnssPdrLocationSource';
|
|
19
|
+
import Pose from '../Pose';
|
|
20
|
+
import Utils from './Utils';
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
const INITIAL_LOCATION = new WGS84UserPosition(43.6091955, 3.8841255);
|
|
24
|
+
const INITIAL_HEADING = 191.9;
|
|
25
|
+
const ITINERARY = Itinerary.fromPoints([
|
|
26
|
+
[INITIAL_LOCATION.lat, INITIAL_LOCATION.lng],
|
|
27
|
+
[43.6091883, 3.8841242],
|
|
28
|
+
[43.6091709, 3.8842382],
|
|
29
|
+
[43.6091288, 3.884226],
|
|
30
|
+
[43.6091461, 3.884112]
|
|
31
|
+
], false);
|
|
32
|
+
|
|
33
|
+
const NOT_AVAILABLE_STR = 'Not available';
|
|
34
|
+
|
|
35
|
+
mapboxgl.accessToken = 'pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4M29iazA2Z2gycXA4N2pmbDZmangifQ.-g_vE53SD2WrJ6tFX7QHmA';
|
|
36
|
+
|
|
37
|
+
const styles = (theme) => {
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
formControl: {
|
|
41
|
+
margin: theme.spacing(1),
|
|
42
|
+
minWidth: 120
|
|
43
|
+
},
|
|
44
|
+
map: {
|
|
45
|
+
width: '90%',
|
|
46
|
+
height: '300px',
|
|
47
|
+
marginLeft: 'auto',
|
|
48
|
+
marginRight: 'auto'
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const COMPASS_STYLE = {
|
|
54
|
+
'width': '15px',
|
|
55
|
+
'height': '15px',
|
|
56
|
+
'border-radius': '50%',
|
|
57
|
+
'z-index': '10000001',
|
|
58
|
+
'position': 'absolute',
|
|
59
|
+
'top': '-14px',
|
|
60
|
+
'left': '-14px',
|
|
61
|
+
'border': '7px solid',
|
|
62
|
+
'border-color': 'transparent',
|
|
63
|
+
'border-top-color': '#008DF1',
|
|
64
|
+
'opacity': 0.5
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const LOCATION_STYLE = {
|
|
68
|
+
'width': '15px',
|
|
69
|
+
'height': '15px',
|
|
70
|
+
'top': '-7px',
|
|
71
|
+
'left': '-7px',
|
|
72
|
+
'position': 'absolute',
|
|
73
|
+
'border-radius': '50%',
|
|
74
|
+
'background': '#008DF1'
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class LocationSource extends React.Component {
|
|
79
|
+
|
|
80
|
+
constructor(props, context) {
|
|
81
|
+
super(props, context);
|
|
82
|
+
this.state = { pose: new Pose() };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
generateProviders() {
|
|
86
|
+
|
|
87
|
+
const fn = pose => this.setState({ pose: pose });
|
|
88
|
+
|
|
89
|
+
const pdr1LocationSource = new PdrLocationSource(fn, {
|
|
90
|
+
'stepdetectionlocker': false,
|
|
91
|
+
'smoother': false
|
|
92
|
+
});
|
|
93
|
+
pdr1LocationSource.setLocation(INITIAL_LOCATION);
|
|
94
|
+
pdr1LocationSource.setHeading(MathUtils.deg2rad(INITIAL_HEADING));
|
|
95
|
+
|
|
96
|
+
const pdr2LocationSource = new PdrLocationSource(fn, {
|
|
97
|
+
'stepdetectionlocker': false,
|
|
98
|
+
'smoother': false
|
|
99
|
+
});
|
|
100
|
+
pdr2LocationSource.setLocation(INITIAL_LOCATION);
|
|
101
|
+
pdr2LocationSource.setHeading(MathUtils.deg2rad(INITIAL_HEADING));
|
|
102
|
+
pdr2LocationSource.enableMapMatching(ITINERARY);
|
|
103
|
+
|
|
104
|
+
const pdr3LocationSource = new PdrLocationSource(fn, {
|
|
105
|
+
'stepdetectionlocker': true,
|
|
106
|
+
'smoother': false
|
|
107
|
+
});
|
|
108
|
+
pdr3LocationSource.setLocation(INITIAL_LOCATION);
|
|
109
|
+
pdr3LocationSource.setHeading(MathUtils.deg2rad(INITIAL_HEADING));
|
|
110
|
+
pdr3LocationSource.enableMapMatching(ITINERARY);
|
|
111
|
+
pdr3LocationSource.setItinerary(ITINERARY);
|
|
112
|
+
|
|
113
|
+
const pdr4LocationSource = new PdrLocationSource(fn, {
|
|
114
|
+
'stepdetectionlocker': true,
|
|
115
|
+
'smoother': true
|
|
116
|
+
});
|
|
117
|
+
pdr4LocationSource.setLocation(INITIAL_LOCATION);
|
|
118
|
+
pdr4LocationSource.setHeading(MathUtils.deg2rad(INITIAL_HEADING));
|
|
119
|
+
pdr4LocationSource.enableMapMatching(ITINERARY);
|
|
120
|
+
pdr4LocationSource.setItinerary(ITINERARY);
|
|
121
|
+
|
|
122
|
+
const gnssPdrLocationSource = new GnssPdrLocationSource(fn);
|
|
123
|
+
gnssPdrLocationSource.enableMapMatching(ITINERARY);
|
|
124
|
+
gnssPdrLocationSource.setItinerary(ITINERARY);
|
|
125
|
+
|
|
126
|
+
const providers = [
|
|
127
|
+
new GnssLocationSource(fn),
|
|
128
|
+
gnssPdrLocationSource,
|
|
129
|
+
pdr1LocationSource,
|
|
130
|
+
pdr2LocationSource,
|
|
131
|
+
pdr3LocationSource,
|
|
132
|
+
pdr4LocationSource,
|
|
133
|
+
new IPLocationSource(fn)
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
return new Promise(resolve => {
|
|
137
|
+
this.setState({ providers: providers }, resolve);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
componentDidMount() {
|
|
143
|
+
this.generateProviders()
|
|
144
|
+
.then(() => {
|
|
145
|
+
this.updateProvider(null, this.state.providers[0]);
|
|
146
|
+
});
|
|
147
|
+
this.map = new mapboxgl.Map({
|
|
148
|
+
container: this.mapContainer,
|
|
149
|
+
style: 'mapbox://styles/mapbox/streets-v9'
|
|
150
|
+
});
|
|
151
|
+
this.map.on('load', () => {
|
|
152
|
+
const provider = this.state.provider;
|
|
153
|
+
if (provider && provider.itinerary) {
|
|
154
|
+
this.networkLayer = this.map.addLayer(this.buildMapboxLayerFromNetwork(ITINERARY));
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
componentWillUnmount() {
|
|
160
|
+
if (this.itineraryInfoInterval) {
|
|
161
|
+
clearInterval(this.itineraryInfoInterval);
|
|
162
|
+
this.itineraryInfoInterval = null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (this.state.provider) {
|
|
166
|
+
this.state.provider.stop();
|
|
167
|
+
}
|
|
168
|
+
this.map.remove();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
handleChange = event => {
|
|
173
|
+
|
|
174
|
+
const prevProvider = this.state.provider;
|
|
175
|
+
|
|
176
|
+
const { providers } = this.state;
|
|
177
|
+
for (let i = 0; i < providers.length; i++) {
|
|
178
|
+
const providerNameWithOptions = providers[i].providerNameWithOptions;
|
|
179
|
+
if (providerNameWithOptions === event.target.value) {
|
|
180
|
+
this.updateProvider(prevProvider, providers[i]);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
updateProvider(prevProvider, newProvider) {
|
|
187
|
+
|
|
188
|
+
if (prevProvider) {
|
|
189
|
+
prevProvider.stop();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (this.mapMarker) {
|
|
193
|
+
this.mapMarker.remove();
|
|
194
|
+
this.mapMarker = null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (prevProvider && prevProvider.itinerary) {
|
|
198
|
+
this.map.removeLayer('network');
|
|
199
|
+
this.map.removeSource('network');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (newProvider.itinerary) {
|
|
203
|
+
if (this.itineraryInfoInterval) {
|
|
204
|
+
clearInterval(this.itineraryInfoInterval);
|
|
205
|
+
this.itineraryInfoInterval = null;
|
|
206
|
+
}
|
|
207
|
+
this.itineraryInfoInterval = setInterval(() => {
|
|
208
|
+
this.setState({ itineraryInfo: this.state.provider.getItineraryInfo() });
|
|
209
|
+
}, 1000);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (prevProvider && newProvider.itinerary) {
|
|
213
|
+
this.map.addLayer(this.buildMapboxLayerFromNetwork(ITINERARY));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
newProvider.start().catch(() => this.setState({ pose: -1 }));
|
|
217
|
+
|
|
218
|
+
this.setState({ provider: newProvider });
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
renderProviders() {
|
|
222
|
+
const { classes } = this.props;
|
|
223
|
+
const {
|
|
224
|
+
provider, providers
|
|
225
|
+
} = this.state;
|
|
226
|
+
|
|
227
|
+
if (!providers || !provider) {
|
|
228
|
+
return <p>Loading</p>;
|
|
229
|
+
}
|
|
230
|
+
const locationSourceItems = [];
|
|
231
|
+
for (let i = 0; i < providers.length; i++) {
|
|
232
|
+
const providerNameWithOptions = providers[i].providerNameWithOptions;
|
|
233
|
+
locationSourceItems.push(
|
|
234
|
+
<MenuItem key={providerNameWithOptions}
|
|
235
|
+
value={providerNameWithOptions}>
|
|
236
|
+
{providerNameWithOptions}
|
|
237
|
+
</MenuItem>
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return (
|
|
242
|
+
<FormControl className={classes.formControl}>
|
|
243
|
+
<Select
|
|
244
|
+
value={provider ? provider.providerNameWithOptions : providers[0].providerNameWithOptions}
|
|
245
|
+
onChange={this.handleChange}
|
|
246
|
+
>
|
|
247
|
+
{locationSourceItems}
|
|
248
|
+
</Select>
|
|
249
|
+
</FormControl>);
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
createMarker(options) {
|
|
255
|
+
var elem, marker;
|
|
256
|
+
|
|
257
|
+
elem = document.createElement('div');
|
|
258
|
+
elem.style.marginLeft = '-' + options.iconAnchor[0] + 'px';
|
|
259
|
+
elem.style.marginTop = '-' + options.iconAnchor[1] + 'px';
|
|
260
|
+
elem.style.width = 0;
|
|
261
|
+
elem.style.height = 0;
|
|
262
|
+
elem.appendChild(options.dom);
|
|
263
|
+
|
|
264
|
+
marker = new mapboxgl.Marker(elem);
|
|
265
|
+
marker.setLngLat([options.longitude, options.latitude]);
|
|
266
|
+
return marker;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
createLocationMarker(location) {
|
|
270
|
+
const coreIcon = document.createElement('div');
|
|
271
|
+
const compassIcon = document.createElement('div');
|
|
272
|
+
this.applyStyleToDomElement(coreIcon, LOCATION_STYLE);
|
|
273
|
+
this.applyStyleToDomElement(compassIcon, COMPASS_STYLE);
|
|
274
|
+
|
|
275
|
+
this.locationIcon = document.createElement('div');
|
|
276
|
+
this.locationIcon.appendChild(coreIcon);
|
|
277
|
+
this.locationIcon.appendChild(compassIcon);
|
|
278
|
+
|
|
279
|
+
return this.createMarker({
|
|
280
|
+
dom: this.locationIcon,
|
|
281
|
+
iconAnchor: [0, 0],
|
|
282
|
+
latitude: location[1],
|
|
283
|
+
longitude: location[0]
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
applyStyleToDomElement(domElement, style) {
|
|
288
|
+
for (const key in style) {
|
|
289
|
+
if (style.hasOwnProperty(key)) {
|
|
290
|
+
domElement.style[key] = style[key];
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
renderMap() {
|
|
296
|
+
|
|
297
|
+
const { classes } = this.props;
|
|
298
|
+
const { pose } = this.state;
|
|
299
|
+
|
|
300
|
+
if (pose.location) {
|
|
301
|
+
const location = pose.location;
|
|
302
|
+
|
|
303
|
+
const lngLat = [location.lng, location.lat];
|
|
304
|
+
// Update map marker
|
|
305
|
+
if (!this.mapMarker) {
|
|
306
|
+
|
|
307
|
+
this.mapMarker = this.createLocationMarker(lngLat)
|
|
308
|
+
.addTo(this.map);
|
|
309
|
+
|
|
310
|
+
} else {
|
|
311
|
+
this.mapMarker.setLngLat(lngLat);
|
|
312
|
+
}
|
|
313
|
+
if (this.map.getZoom() < 3) {
|
|
314
|
+
this.map.setZoom(21);
|
|
315
|
+
}
|
|
316
|
+
this.map.jumpTo({ center: lngLat });
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (pose.attitude && this.locationIcon && this.locationIcon.style) {
|
|
320
|
+
this.locationIcon.style.transform = 'rotate(' + pose.attitude.headingDegrees + 'deg)';
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return (
|
|
324
|
+
<div className={classes.map}
|
|
325
|
+
ref={map => {
|
|
326
|
+
this.mapContainer = map;
|
|
327
|
+
}} />
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
buildMapboxLayerFromNetwork(network) {
|
|
332
|
+
const layer = {
|
|
333
|
+
id: 'network',
|
|
334
|
+
type: 'line',
|
|
335
|
+
source: {
|
|
336
|
+
type: 'geojson',
|
|
337
|
+
data: {
|
|
338
|
+
type: 'Feature',
|
|
339
|
+
properties: {},
|
|
340
|
+
geometry: {
|
|
341
|
+
type: 'MultiLineString',
|
|
342
|
+
coordinates: []
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
paint: {
|
|
347
|
+
'line-color': '#0000FF',
|
|
348
|
+
'line-width': 3
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
for (let i = 0; i < network.length; i++) {
|
|
353
|
+
layer.source.data.geometry.coordinates.push(
|
|
354
|
+
[
|
|
355
|
+
[network.edges[i].node1.lng, network.edges[i].node1.lat],
|
|
356
|
+
[network.edges[i].node2.lng, network.edges[i].node2.lat]
|
|
357
|
+
]
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
return layer;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
render() {
|
|
364
|
+
|
|
365
|
+
const {
|
|
366
|
+
pose, itineraryInfo
|
|
367
|
+
} = this.state;
|
|
368
|
+
|
|
369
|
+
const selectControl = this.renderProviders();
|
|
370
|
+
const mapRendered = this.renderMap();
|
|
371
|
+
|
|
372
|
+
let contents;
|
|
373
|
+
if (!pose) {
|
|
374
|
+
contents = <p>Waiting</p>;
|
|
375
|
+
} else if (pose === -1) {
|
|
376
|
+
contents = <p>{NOT_AVAILABLE_STR}</p>;
|
|
377
|
+
} else {
|
|
378
|
+
|
|
379
|
+
let locationString = <span>{NOT_AVAILABLE_STR}</span>;
|
|
380
|
+
let itineraryInfoString = <span>{NOT_AVAILABLE_STR}</span>;
|
|
381
|
+
if (pose.location) {
|
|
382
|
+
const location = pose.location;
|
|
383
|
+
|
|
384
|
+
locationString = (
|
|
385
|
+
<p>
|
|
386
|
+
Latitude: {location.lat.toFixed(7)}<br />
|
|
387
|
+
Longitude: {location.lng.toFixed(7)}<br />
|
|
388
|
+
Altitude: {location.alt ? location.alt.toFixed(2) : NOT_AVAILABLE_STR}<br />
|
|
389
|
+
Bearing: {location.bearing ? location.bearing.toFixed(2) : NOT_AVAILABLE_STR}<br />
|
|
390
|
+
Accuracy: {location.accuracy ? location.accuracy.toFixed(2) : NOT_AVAILABLE_STR}<br />
|
|
391
|
+
Time: {location.time ? location.time.toFixed(2) : NOT_AVAILABLE_STR}<br />
|
|
392
|
+
Provider: {location.provider ? location.provider : NOT_AVAILABLE_STR}
|
|
393
|
+
</p>);
|
|
394
|
+
|
|
395
|
+
if (itineraryInfo) {
|
|
396
|
+
const projection = itineraryInfo.projection;
|
|
397
|
+
const nearestElement = projection.nearestElement;
|
|
398
|
+
itineraryInfoString = (
|
|
399
|
+
<p>
|
|
400
|
+
Origin: {projection.origin.toString()} <br />
|
|
401
|
+
Distance: {projection.distanceFromNearestElement.toFixed(2)} m<br />
|
|
402
|
+
Projection: {projection.projection.toString()}<br />
|
|
403
|
+
Nearest element: {nearestElement instanceof Edge ? 'Edge' : 'Node'}<br />
|
|
404
|
+
Remaining Distance: {itineraryInfo.remainingDistance.toFixed(2)}m<br />
|
|
405
|
+
Remaining Percentage: {(itineraryInfo.remainingPercentage * 100).toFixed(1)}%
|
|
406
|
+
</p>
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
contents = (
|
|
412
|
+
<div>
|
|
413
|
+
<h3>Location</h3>
|
|
414
|
+
{locationString}
|
|
415
|
+
<h3>Attitude</h3>
|
|
416
|
+
{Utils.renderAttitude(pose.attitude)}
|
|
417
|
+
<h3>Projection</h3>
|
|
418
|
+
{itineraryInfoString}
|
|
419
|
+
</div>);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return (
|
|
423
|
+
<div>
|
|
424
|
+
{selectControl}
|
|
425
|
+
{contents}
|
|
426
|
+
<h3>Map</h3>
|
|
427
|
+
{mapRendered}
|
|
428
|
+
</div>);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
LocationSource.propTypes = { classes: PropTypes.object.isRequired };
|
|
433
|
+
|
|
434
|
+
export default withStyles(styles)(LocationSource);
|