@wemap/geo 2.7.8 → 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.
@@ -0,0 +1,220 @@
1
+ /* eslint-disable max-statements */
2
+ import chai from 'chai';
3
+ import chaiAlmost from 'chai-almost';
4
+
5
+ import UserPosition from './UserPosition';
6
+ import Level from './Level';
7
+ import Coordinates from './Coordinates';
8
+
9
+ const expect = chai.expect;
10
+ chai.use(chaiAlmost());
11
+
12
+ describe('UserPosition', () => {
13
+
14
+ it('creation', () => {
15
+
16
+ expect(() => new UserPosition()).throw(Error);
17
+
18
+ expect(() => new UserPosition(45)).throw(Error);
19
+ expect(() => new UserPosition(45, 5)).not.throw(Error);
20
+ expect(() => new UserPosition(45, 5, null)).not.throw(Error);
21
+ expect(() => new UserPosition(45, 5, null, null)).not.throw(Error);
22
+
23
+ expect(() => new UserPosition(45, 5, null, null, null)).not.throw(Error);
24
+ expect(() => new UserPosition(45, 5, null, null, 0)).not.throw(Error);
25
+ expect(() => new UserPosition(45, 5, null, null, 10)).not.throw(Error);
26
+ expect(() => new UserPosition(45, 5, null, null, -10)).not.throw(Error);
27
+ expect(() => new UserPosition(45, 5, null, null, Number.POSITIVE_INFINITY)).throw(Error);
28
+
29
+ expect(() => new UserPosition(45, 5, null, null, null, null)).not.throw(Error);
30
+ expect(() => new UserPosition(45, 5, null, null, null, 0)).not.throw(Error);
31
+ expect(() => new UserPosition(45, 5, null, null, null, 10)).not.throw(Error);
32
+ expect(() => new UserPosition(45, 5, null, null, null, -10)).throw(Error);
33
+ expect(() => new UserPosition(45, 5, null, null, null, Number.POSITIVE_INFINITY)).throw(Error);
34
+
35
+ expect(() => new UserPosition(45, 5, null, null, null,
36
+ null, null)).not.throw(Error);
37
+ expect(() => new UserPosition(45, 5, null, null, null,
38
+ null, 0)).not.throw(Error);
39
+ expect(() => new UserPosition(45, 5, null, null, null,
40
+ null, 10)).not.throw(Error);
41
+ expect(() => new UserPosition(45, 5, null, null, null,
42
+ null, -10)).not.throw(Error);
43
+ expect(() => new UserPosition(45, 5, null, null, null,
44
+ null, Number.POSITIVE_INFINITY)).throw(Error);
45
+
46
+ const position = new UserPosition(45, 5, 0, new Level(2), 123456, 10, Math.PI, 'foo');
47
+ expect(position.lat).equals(45);
48
+ expect(position.lng).equals(5);
49
+ expect(position.alt).equals(0);
50
+ expect(Level.equalsTo(position.level, new Level(2))).true;
51
+ expect(position.time).equals(123456);
52
+ expect(position.accuracy).equals(10);
53
+ expect(position.bearing).equals(Math.PI);
54
+
55
+ });
56
+
57
+ it('update', () => {
58
+ const position = new UserPosition(0, 0);
59
+ position.lat = 45;
60
+ position.lng = 5;
61
+ position.alt = 0;
62
+ position.level = new Level(2);
63
+ position.time = 123456;
64
+ position.accuracy = 10;
65
+ position.bearing = Math.PI;
66
+ expect(position.lat).equals(45);
67
+ expect(position.lng).equals(5);
68
+ expect(position.alt).equals(0);
69
+ expect(Level.equalsTo(position.level, new Level(2))).true;
70
+ expect(position.time).equals(123456);
71
+ expect(position.accuracy).equals(10);
72
+ expect(position.bearing).equals(Math.PI);
73
+
74
+ });
75
+
76
+ it('fromCoordinates', () => {
77
+ const level = new Level(2);
78
+ const coordinates = new Coordinates(45, 5, 0, level, 123456);
79
+ const position = UserPosition.fromCoordinates(coordinates);
80
+ expect(position).instanceOf(UserPosition);
81
+ expect(position.lat).equals(45);
82
+ expect(position.lng).equals(5);
83
+ expect(position.alt).equals(0);
84
+ expect(Level.equalsTo(position.level, level)).true;
85
+ expect(position.time).is.null;
86
+ expect(position.accuracy).is.null;
87
+ expect(position.bearing).is.null;
88
+ });
89
+
90
+
91
+ it('clone', () => {
92
+ const level = new Level(2);
93
+ const position = new UserPosition(45, 5, 0, level, 123456, 10, Math.PI);
94
+ const clonePosition = position.clone();
95
+
96
+ expect(clonePosition.lat).equals(45);
97
+ expect(clonePosition.lng).equals(5);
98
+ expect(clonePosition.alt).equals(0);
99
+ expect(Level.equalsTo(clonePosition.level, level)).true;
100
+ expect(clonePosition.level).not.equals(level);
101
+ expect(clonePosition.time).equals(123456);
102
+ expect(clonePosition.accuracy).equals(10);
103
+ expect(clonePosition.bearing).equals(Math.PI);
104
+ });
105
+
106
+
107
+ it('equalsTo', () => {
108
+ let position, position2;
109
+
110
+ expect(UserPosition.equalsTo(null, null)).true;
111
+
112
+ position = new UserPosition(0, 0);
113
+ expect(UserPosition.equalsTo(position, new UserPosition(0, 0))).true;
114
+ expect(UserPosition.equalsTo(position, new Coordinates(0, 0))).false;
115
+
116
+ position = new UserPosition(45, 5, 0, new Level(2), 123456, 10, Math.PI);
117
+ expect(UserPosition.equalsTo(position,
118
+ new UserPosition(45, 5, 0, new Level(2), 123456, 10, Math.PI)
119
+ )).true;
120
+
121
+ position2 = position.clone();
122
+ position2.lat = 0;
123
+ expect(UserPosition.equalsTo(position, position2)).false;
124
+
125
+ position2 = position.clone();
126
+ position2.lng = 0;
127
+ expect(UserPosition.equalsTo(position, position2)).false;
128
+
129
+ position2 = position.clone();
130
+ position2.alt = 10;
131
+ expect(UserPosition.equalsTo(position, position2)).false;
132
+
133
+ position2 = position.clone();
134
+ position2.level = null;
135
+ expect(UserPosition.equalsTo(position, position2)).false;
136
+
137
+ position2 = position.clone();
138
+ position2.time = 0;
139
+ expect(UserPosition.equalsTo(position, position2)).false;
140
+
141
+ position2 = position.clone();
142
+ position2.accuracy = 0;
143
+ expect(UserPosition.equalsTo(position, position2)).false;
144
+
145
+ position2 = position.clone();
146
+ position2.bearing = 0;
147
+ expect(UserPosition.equalsTo(position, position2)).false;
148
+
149
+ expect(position.equalsTo(
150
+ new UserPosition(45, 5, 0, new Level(2), 123456, 10, Math.PI)
151
+ )).true;
152
+ expect(position.equalsTo(
153
+ new UserPosition(45, 5, 0, new Level(2), 123456, 0, Math.PI)
154
+ )).false;
155
+ });
156
+
157
+ it('json', () => {
158
+ let position;
159
+
160
+ position = new UserPosition(5, -10);
161
+ expect(position.toJson()).to.deep.equal({
162
+ lat: 5,
163
+ lng: -10
164
+ });
165
+
166
+ position = new UserPosition(5, -10);
167
+ position.alt = 2;
168
+ expect(position.toJson()).to.deep.equal({
169
+ lat: 5,
170
+ lng: -10,
171
+ alt: 2
172
+ });
173
+
174
+ position = new UserPosition(5, -10);
175
+ position.level = new Level(2);
176
+ expect(position.toJson()).to.deep.equal({
177
+ lat: 5,
178
+ lng: -10,
179
+ level: '2'
180
+ });
181
+
182
+ position = new UserPosition(5, -10);
183
+ position.time = 10;
184
+ expect(position.toJson()).to.deep.equal({
185
+ lat: 5,
186
+ lng: -10,
187
+ time: 10
188
+ });
189
+
190
+ position = new UserPosition(5, -10);
191
+ position.accuracy = 10;
192
+ expect(position.toJson()).to.deep.equal({
193
+ lat: 5,
194
+ lng: -10,
195
+ accuracy: 10
196
+ });
197
+
198
+ position = new UserPosition(5, -10);
199
+ position.bearing = Math.PI;
200
+ expect(position.toJson()).to.deep.equal({
201
+ lat: 5,
202
+ lng: -10,
203
+ bearing: Math.PI
204
+ });
205
+
206
+ position = new UserPosition(5, -10);
207
+ expect(UserPosition.equalsTo(
208
+ UserPosition.fromJson(position.toJson()),
209
+ position)
210
+ ).true;
211
+
212
+ position = new UserPosition(45, 5, 0, new Level(2), 123456, 10, Math.PI);
213
+ expect(UserPosition.equalsTo(
214
+ UserPosition.fromJson(position.toJson()),
215
+ position)
216
+ ).true;
217
+
218
+ });
219
+
220
+ });
@@ -0,0 +1,138 @@
1
+ import isNumber from 'lodash.isnumber';
2
+ import isFinite from 'lodash.isfinite';
3
+
4
+ import { Quaternion } from '@wemap/maths';
5
+ import Attitude from './Attitude';
6
+
7
+ class AbsoluteHeading {
8
+
9
+ _heading = null;
10
+ _time = null;
11
+ _accuracy = null;
12
+
13
+ /**
14
+ *
15
+ * @param {Number} heading
16
+ * @param {Number} time
17
+ * @param {Number} accuracy
18
+ */
19
+ constructor(heading, time, accuracy) {
20
+ this.heading = heading;
21
+ this.time = time;
22
+ this.accuracy = accuracy;
23
+ }
24
+
25
+ /**
26
+ * @returns {Number}
27
+ */
28
+ get heading() {
29
+ return this._heading;
30
+ }
31
+
32
+ /**
33
+ * @param {Number} heading
34
+ */
35
+ set heading(heading) {
36
+ if (isNumber(heading) && isFinite(heading)) {
37
+ this._heading = heading;
38
+ } else {
39
+ throw new Error('heading argument is not a number');
40
+ }
41
+ }
42
+
43
+ /**
44
+ * @returns {Number}
45
+ */
46
+ get time() {
47
+ return this._time;
48
+ }
49
+
50
+ /**
51
+ * @param {Number} time
52
+ */
53
+ set time(time) {
54
+ if (isNumber(time) && isFinite(time)) {
55
+ this._time = time;
56
+ } else {
57
+ if (typeof time !== 'undefined' && time !== null) {
58
+ throw new Error('time argument is not a number');
59
+ }
60
+ this._time = null;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * @returns {Number}
66
+ */
67
+ get accuracy() {
68
+ return this._accuracy;
69
+ }
70
+
71
+ /**
72
+ * @param {Number} accuracy
73
+ */
74
+ set accuracy(accuracy) {
75
+ if (isNumber(accuracy) && accuracy >= 0 && accuracy <= Math.PI) {
76
+ this._accuracy = accuracy;
77
+ } else {
78
+ if (typeof accuracy !== 'undefined' && accuracy !== null) {
79
+ throw new Error('accuracy argument (' + accuracy + ') is not in range [0; PI]');
80
+ }
81
+ this._accuracy = null;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * @returns {Attitude}
87
+ */
88
+ toAttitude() {
89
+ /**
90
+ * Heading is given around z-axis in NED frame and our attitude in ENU frame, that is why
91
+ * -1* is applied to heading.
92
+ */
93
+ return new Attitude(
94
+ Quaternion.fromAxisAngle([0, 0, 1], -this.heading),
95
+ this.time,
96
+ this.accuracy
97
+ );
98
+ }
99
+
100
+
101
+ /**
102
+ * Compares two AbsoluteHeading
103
+ * @param {AbsoluteHeading} heading1 heading 1
104
+ * @param {AbsoluteHeading} heading2 heading 2
105
+ */
106
+ static equalsTo(heading1, heading2) {
107
+
108
+ // Handle null comparison
109
+ if (heading1 === null && heading1 === heading2) {
110
+ return true;
111
+ }
112
+
113
+ if (!(heading1 instanceof AbsoluteHeading) || !(heading2 instanceof AbsoluteHeading)) {
114
+ return false;
115
+ }
116
+
117
+ return Math.abs(heading1.heading, heading2.heading) < 1e-8;
118
+ }
119
+
120
+ equalsTo(other) {
121
+ return AbsoluteHeading.equalsTo(this, other);
122
+ }
123
+
124
+ toJson() {
125
+ return {
126
+ accuracy: this.accuracy,
127
+ heading: this.heading,
128
+ time: this.time
129
+ };
130
+ }
131
+
132
+ static fromJson(json) {
133
+ return new AbsoluteHeading(json);
134
+ }
135
+
136
+ }
137
+
138
+ export default AbsoluteHeading;
@@ -1,3 +1,6 @@
1
+ import isNumber from 'lodash.isnumber';
2
+ import isFinite from 'lodash.isfinite';
3
+
1
4
  import {
2
5
  Rotations, Quaternion, rad2deg, deg2rad
3
6
  } from '@wemap/maths';
@@ -7,19 +10,35 @@ class Attitude {
7
10
  _quaternion = [1, 0, 0, 0];
8
11
  _heading = null;
9
12
  _eulerAngles = null;
13
+ _time = null;
14
+ _accuracy = null;
10
15
 
11
- constructor(quaternion) {
16
+ /**
17
+ *
18
+ * @param {Number[]} quaternion
19
+ * @param {Number} time
20
+ * @param {Number} accuracy
21
+ */
22
+ constructor(quaternion, time, accuracy) {
12
23
  this.quaternion = quaternion;
24
+ this.time = time;
25
+ this.accuracy = accuracy;
13
26
  }
14
27
 
15
28
  static unitary() {
16
29
  return new Attitude([1, 0, 0, 0]);
17
30
  }
18
31
 
32
+ /**
33
+ * @returns {Number[]}
34
+ */
19
35
  get quaternion() {
20
36
  return this._quaternion;
21
37
  }
22
38
 
39
+ /**
40
+ * @param {Number[]} quaternion
41
+ */
23
42
  set quaternion(quaternion) {
24
43
  if (!Array.isArray(quaternion)
25
44
  || quaternion.length !== 4
@@ -27,6 +46,50 @@ class Attitude {
27
46
  throw new Error('quaternion is not a unit quaternion');
28
47
  }
29
48
  this._quaternion = quaternion;
49
+ this._heading = null;
50
+ this._eulerAngles = null;
51
+ }
52
+
53
+ /**
54
+ * @returns {Number}
55
+ */
56
+ get time() {
57
+ return this._time;
58
+ }
59
+
60
+ /**
61
+ * @param {Number} time
62
+ */
63
+ set time(time) {
64
+ if (isNumber(time) && isFinite(time)) {
65
+ this._time = time;
66
+ } else {
67
+ if (typeof time !== 'undefined' && time !== null) {
68
+ throw new Error('time argument is not a number');
69
+ }
70
+ this._time = null;
71
+ }
72
+ }
73
+
74
+ /**
75
+ * @returns {Number}
76
+ */
77
+ get accuracy() {
78
+ return this._accuracy;
79
+ }
80
+
81
+ /**
82
+ * @param {Number} accuracy
83
+ */
84
+ set accuracy(accuracy) {
85
+ if (isNumber(accuracy) && accuracy >= 0 && accuracy <= Math.PI) {
86
+ this._accuracy = accuracy;
87
+ } else {
88
+ if (typeof accuracy !== 'undefined' && accuracy !== null) {
89
+ throw new Error('accuracy argument (' + accuracy + ') is not in range [0; PI]');
90
+ }
91
+ this._accuracy = null;
92
+ }
30
93
  }
31
94
 
32
95
  get eulerAngles() {
@@ -1,7 +1,9 @@
1
+ /* eslint-disable max-statements */
1
2
  import chai from 'chai';
2
3
  import chaiAlmost from 'chai-almost';
3
4
 
4
5
  import Attitude from './Attitude';
6
+ import { deg2rad } from '@wemap/maths';
5
7
 
6
8
  const expect = chai.expect;
7
9
  chai.use(chaiAlmost());
@@ -20,6 +22,42 @@ describe('Attitude', () => {
20
22
  expect(() => new Attitude([0.5, 0.5, 0.5, 0.5])).not.throw(Error);
21
23
  expect(() => new Attitude([Math.sqrt(2) / 2, Math.sqrt(2) / 2, 0, 0])).not.throw(Error);
22
24
  expect(Attitude.unitary().quaternion).deep.equals([1, 0, 0, 0]);
25
+
26
+ expect(() => new Attitude([1, 0, 0, 0])).not.throw(Error);
27
+
28
+ expect(() => new Attitude([1, 0, 0, 0], 10)).not.throw(Error);
29
+ expect(() => new Attitude([1, 0, 0, 0], -1)).not.throw(Error);
30
+ expect(() => new Attitude([1, 0, 0, 0], false)).throw(Error);
31
+ expect(() => new Attitude([1, 0, 0, 0], true)).throw(Error);
32
+ expect(() => new Attitude([1, 0, 0, 0], null)).not.throw(Error);
33
+ expect(() => new Attitude([1, 0, 0, 0], Number.POSITIVE_INFINITY)).throw(Error);
34
+
35
+ expect(() => new Attitude([1, 0, 0, 0], 10, deg2rad(10))).not.throw(Error);
36
+ expect(() => new Attitude([1, 0, 0, 0], 10, -1)).throw(Error);
37
+ expect(() => new Attitude([1, 0, 0, 0], 10, 0)).not.throw(Error);
38
+ expect(() => new Attitude([1, 0, 0, 0], 10, 10)).throw(Error);
39
+ expect(() => new Attitude([1, 0, 0, 0], 10, Math.PI)).not.throw(Error);
40
+ expect(() => new Attitude([1, 0, 0, 0], 10, true)).throw(Error);
41
+ expect(() => new Attitude([1, 0, 0, 0], 10, false)).throw(Error);
42
+ expect(() => new Attitude([1, 0, 0, 0], 10, null)).not.throw(Error);
43
+
44
+
45
+ const attitude = new Attitude([1, 0, 0, 0], 10, deg2rad(10), 'foo');
46
+ expect(attitude.quaternion).deep.equals([1, 0, 0, 0]);
47
+ expect(attitude.time).equals(10);
48
+ expect(attitude.accuracy).equals(deg2rad(10));
49
+ });
50
+
51
+
52
+ it('update', () => {
53
+ const attitude = new Attitude([1, 0, 0, 0]);
54
+ attitude.quaternion = [0, 1, 0, 0];
55
+ attitude.time = 123456;
56
+ attitude.accuracy = 2;
57
+ expect(attitude.quaternion).deep.equals([0, 1, 0, 0]);
58
+ expect(attitude.time).equals(123456);
59
+ expect(attitude.accuracy).equals(2);
60
+
23
61
  });
24
62
 
25
63
  it('eulerAngles', () => {