@wemap/geo 3.2.17 → 4.0.1

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.
@@ -1,5 +1,5 @@
1
1
  import {
2
- Utils, Vector3, Quaternion, rad2deg
2
+ deg2rad, Vector3, Quaternion, rad2deg, wrap
3
3
  } from '@wemap/maths';
4
4
 
5
5
  import Constants from '../Constants.js';
@@ -13,7 +13,7 @@ import Level from './Level.js';
13
13
  * distanceTo, bearingTo, toEcef...
14
14
  *
15
15
  * /!\ This class has been adapted to use earth as a sphere and not as an ellipsoid
16
- * /!\ So, this class does not stricly represent Coordinates coordinates anymore
16
+ * /!\ So, this class does not stricly represent WGS84 coordinates anymore
17
17
  * /!\ This modifications have been made for computational improvements.
18
18
  */
19
19
  class Coordinates {
@@ -65,6 +65,10 @@ class Coordinates {
65
65
  this._ecef = null;
66
66
  }
67
67
 
68
+ set latitude(lat) {
69
+ throw new Error('Please use Coordinate#lat setter instead of Coordinate#latitude');
70
+ }
71
+
68
72
  set lng(lng) {
69
73
  if (typeof lng === 'number') {
70
74
  this._lng = lng;
@@ -77,6 +81,10 @@ class Coordinates {
77
81
  this._ecef = null;
78
82
  }
79
83
 
84
+ set longitude(lng) {
85
+ throw new Error('Please use Coordinate#lng setter instead of Coordinate#longitude');
86
+ }
87
+
80
88
  set alt(alt) {
81
89
  if (typeof alt === 'number') {
82
90
  this._alt = alt;
@@ -110,7 +118,7 @@ class Coordinates {
110
118
 
111
119
  wrap() {
112
120
  if (this._lng <= -180 || this._lng > 180) {
113
- this._lng = Utils.wrap(this._lng, -180, 180);
121
+ this._lng = wrap(this._lng, -180, 180);
114
122
  }
115
123
  }
116
124
 
@@ -157,8 +165,8 @@ class Coordinates {
157
165
  const cosDr = Math.cos(dR);
158
166
  const sinDr = Math.sin(dR);
159
167
 
160
- const phi1 = Utils.deg2rad(this.lat);
161
- const lambda1 = Utils.deg2rad(this.lng);
168
+ const phi1 = deg2rad(this.lat);
169
+ const lambda1 = deg2rad(this.lng);
162
170
 
163
171
  const phi2 = Math.asin(
164
172
  Math.sin(phi1) * cosDr
@@ -169,8 +177,8 @@ class Coordinates {
169
177
  cosDr - Math.sin(phi1) * Math.sin(phi2)
170
178
  );
171
179
 
172
- this.lat = Utils.rad2deg(phi2);
173
- this.lng = Utils.rad2deg(lambda2);
180
+ this.lat = rad2deg(phi2);
181
+ this.lng = rad2deg(lambda2);
174
182
 
175
183
  if (typeof elevation === 'number') {
176
184
  if (this.alt === null) {
@@ -195,14 +203,14 @@ class Coordinates {
195
203
  const lat2 = location2.lat;
196
204
  const lng2 = location2.lng;
197
205
 
198
- const dlat = Utils.deg2rad(lat2 - lat1);
199
- const dlng = Utils.deg2rad(lng2 - lng1);
206
+ const dlat = deg2rad(lat2 - lat1);
207
+ const dlng = deg2rad(lng2 - lng1);
200
208
 
201
209
  const dlngsin = Math.sin(dlng / 2);
202
210
  const dlatsin = Math.sin(dlat / 2);
203
- const lat1rad = Utils.deg2rad(lat1);
211
+ const lat1rad = deg2rad(lat1);
204
212
  const lat1cos = Math.cos(lat1rad);
205
- const lat2rad = Utils.deg2rad(lat2);
213
+ const lat2rad = deg2rad(lat2);
206
214
  const lat2cos = Math.cos(lat2rad);
207
215
  const angle = dlatsin * dlatsin + lat1cos * lat2cos * dlngsin * dlngsin;
208
216
 
@@ -219,9 +227,9 @@ class Coordinates {
219
227
  }
220
228
 
221
229
  bearingTo(location2) {
222
- const lat1 = Utils.deg2rad(this.lat);
223
- const lat2 = Utils.deg2rad(location2.lat);
224
- const diffLng = Utils.deg2rad(location2.lng - this.lng);
230
+ const lat1 = deg2rad(this.lat);
231
+ const lat2 = deg2rad(location2.lat);
232
+ const diffLng = deg2rad(location2.lng - this.lng);
225
233
 
226
234
  return Math.atan2(Math.sin(diffLng) * Math.cos(lat2),
227
235
  Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(diffLng));
@@ -239,14 +247,14 @@ class Coordinates {
239
247
  */
240
248
 
241
249
  get enuToEcefRotation() {
242
- const rot1 = Quaternion.fromAxisAngle([0, 0, 1], Math.PI / 2 + Utils.deg2rad(this.lng));
243
- const rot2 = Quaternion.fromAxisAngle([1, 0, 0], Math.PI / 2 - Utils.deg2rad(this.lat));
250
+ const rot1 = Quaternion.fromAxisAngle([0, 0, 1], Math.PI / 2 + deg2rad(this.lng));
251
+ const rot2 = Quaternion.fromAxisAngle([1, 0, 0], Math.PI / 2 - deg2rad(this.lat));
244
252
  return Quaternion.multiply(rot1, rot2);
245
253
  }
246
254
 
247
255
  get ecefToEnuRotation() {
248
- const rot1 = Quaternion.fromAxisAngle([1, 0, 0], Utils.deg2rad(this.lat) - Math.PI / 2);
249
- const rot2 = Quaternion.fromAxisAngle([0, 0, 1], -Utils.deg2rad(this.lng) - Math.PI / 2);
256
+ const rot1 = Quaternion.fromAxisAngle([1, 0, 0], deg2rad(this.lat) - Math.PI / 2);
257
+ const rot2 = Quaternion.fromAxisAngle([0, 0, 1], -deg2rad(this.lng) - Math.PI / 2);
250
258
  return Quaternion.multiply(rot1, rot2);
251
259
  }
252
260
 
@@ -255,8 +263,8 @@ class Coordinates {
255
263
  get ecef() {
256
264
 
257
265
  if (!this._ecef) {
258
- const lat = Utils.deg2rad(this.lat);
259
- const lng = Utils.deg2rad(this.lng);
266
+ const lat = deg2rad(this.lat);
267
+ const lng = deg2rad(this.lng);
260
268
  const alt = this.alt || 0;
261
269
 
262
270
  const x = (Constants.R_MAJOR + alt) * Math.cos(lat) * Math.cos(lng);
@@ -361,11 +369,25 @@ class Coordinates {
361
369
  }
362
370
 
363
371
  toCompressedJson() {
364
- return [this.lat, this.lng, this.alt, this.level === null ? null : this.level.toString()];
372
+ const output = [this.lat, this.lng];
373
+ if (this.alt !== null || this.level !== null) {
374
+ output.push(this.alt);
375
+ }
376
+ if (this.level !== null) {
377
+ output.push(this.level.toString());
378
+ }
379
+ return output;
365
380
  }
366
381
 
367
382
  static fromCompressedJson(json) {
368
- return new Coordinates(json[0], json[1], json[2], Level.fromString(json[3]));
383
+ const coords = new Coordinates(json[0], json[1]);
384
+ if (json.length > 2) {
385
+ coords.alt = json[2];
386
+ }
387
+ if (json.length > 3) {
388
+ coords.level = Level.fromString(json[3]);
389
+ }
390
+ return coords;
369
391
  }
370
392
  }
371
393
 
@@ -33,7 +33,9 @@ describe('Coordinates', () => {
33
33
  const level = new Level(2);
34
34
  const position = new Coordinates(45, 5, 0, level);
35
35
  expect(position.lat).equals(45);
36
+ expect(position.latitude).equals(45);
36
37
  expect(position.lng).equals(5);
38
+ expect(position.longitude).equals(5);
37
39
  expect(position.alt).equals(0);
38
40
  expect(position.level).equals(level);
39
41
 
@@ -57,6 +59,9 @@ describe('Coordinates', () => {
57
59
 
58
60
  position.lng = -202;
59
61
  expect(position.lng).equals(158);
62
+
63
+ expect(() => (position.latitude = 45)).throw(Error);
64
+ expect(() => (position.longitude = 5)).throw(Error);
60
65
  });
61
66
 
62
67
  it('clone', () => {
@@ -25,7 +25,7 @@ class RelativePosition {
25
25
  set x(x) {
26
26
  if (typeof x === 'number') {
27
27
  this._x = x;
28
- } else if (typeof x !== 'undefined' && x !== null) {
28
+ } else {
29
29
  throw new Error('x argument is not a number');
30
30
  }
31
31
  }
@@ -37,7 +37,7 @@ class RelativePosition {
37
37
  set y(y) {
38
38
  if (typeof y === 'number') {
39
39
  this._y = y;
40
- } else if (typeof y !== 'undefined' && y !== null) {
40
+ } else {
41
41
  throw new Error('y argument is not a number');
42
42
  }
43
43
  }
@@ -49,7 +49,7 @@ class RelativePosition {
49
49
  set z(z) {
50
50
  if (typeof z === 'number') {
51
51
  this._z = z;
52
- } else if (typeof z !== 'undefined' && z !== null) {
52
+ } else {
53
53
  throw new Error('z argument is not a number');
54
54
  }
55
55
  }
@@ -0,0 +1,166 @@
1
+ /* eslint-disable max-statements */
2
+ import chai from 'chai';
3
+ import chaiAlmost from 'chai-almost';
4
+
5
+ import RelativePosition from './RelativePosition.js';
6
+
7
+ const expect = chai.expect;
8
+ chai.use(chaiAlmost());
9
+
10
+ describe('RelativePosition', () => {
11
+
12
+ it('creation', () => {
13
+
14
+ expect(() => new RelativePosition()).throw(Error);
15
+
16
+ expect(() => new RelativePosition(1)).throw(Error);
17
+ expect(() => new RelativePosition('a')).throw(Error);
18
+ expect(() => new RelativePosition(1, 2)).throw(Error);
19
+ expect(() => new RelativePosition(1, 'a')).throw(Error);
20
+ expect(() => new RelativePosition(1, 2, 3)).not.throw(Error);
21
+ expect(() => new RelativePosition(1, 2, 'a')).throw(Error);
22
+
23
+ expect(() => new RelativePosition(1, 2, 3, 123456)).not.throw(Error);
24
+ expect(() => new RelativePosition(1, 2, 3, 'a')).throw(Error);
25
+ expect(() => new RelativePosition(1, 2, 3, 123456, 10)).not.throw(Error);
26
+ expect(() => new RelativePosition(1, 2, 3, 123456, 'a')).throw(Error);
27
+ expect(() => new RelativePosition(1, 2, 3, 123456, 10, Math.PI)).not.throw(Error);
28
+ expect(() => new RelativePosition(1, 2, 3, 123456, 10, 'a')).throw(Error);
29
+
30
+ const position = new RelativePosition(1, 2, 3, 123456, 10, Math.PI);
31
+ expect(position.x).equals(1);
32
+ expect(position.y).equals(2);
33
+ expect(position.z).equals(3);
34
+ expect(position.time).equals(123456);
35
+ expect(position.accuracy).equals(10);
36
+ expect(position.bearing).equals(Math.PI);
37
+
38
+ });
39
+
40
+ it('update', () => {
41
+ const position = new RelativePosition(0, 0, 0);
42
+ position.x = 1;
43
+ position.y = 2;
44
+ position.z = 3;
45
+ position.time = 123456;
46
+ position.accuracy = 10;
47
+ position.bearing = Math.PI;
48
+ expect(position.x).equals(1);
49
+ expect(position.y).equals(2);
50
+ expect(position.z).equals(3);
51
+ expect(position.time).equals(123456);
52
+ expect(position.accuracy).equals(10);
53
+ expect(position.bearing).equals(Math.PI);
54
+
55
+ });
56
+
57
+ it('clone', () => {
58
+ const position = new RelativePosition(1, 2, 3, 123456, 10, Math.PI);
59
+ const clonePosition = position.clone();
60
+
61
+ expect(clonePosition.x).equals(1);
62
+ expect(clonePosition.y).equals(2);
63
+ expect(clonePosition.z).equals(3);
64
+ expect(clonePosition.time).equals(123456);
65
+ expect(clonePosition.accuracy).equals(10);
66
+ expect(clonePosition.bearing).equals(Math.PI);
67
+
68
+ expect(clonePosition).not.equal(position);
69
+ });
70
+
71
+
72
+ it('equalsTo', () => {
73
+ let position, position2;
74
+
75
+ expect(RelativePosition.equalsTo(null, null)).true;
76
+
77
+ position = new RelativePosition(1, 2, 3);
78
+ expect(RelativePosition.equalsTo(position, new RelativePosition(1, 2, 3))).true;
79
+ expect(RelativePosition.equalsTo(position, {})).false;
80
+ expect(RelativePosition.equalsTo({}, position)).false;
81
+
82
+ position = new RelativePosition(1, 2, 3, 123456, 10, Math.PI);
83
+ expect(RelativePosition.equalsTo(position,
84
+ new RelativePosition(1, 2, 3, 123456, 10, Math.PI)
85
+ )).true;
86
+
87
+ position2 = position.clone();
88
+ position2.x = 0;
89
+ expect(RelativePosition.equalsTo(position, position2)).false;
90
+
91
+ position2 = position.clone();
92
+ position2.y = 0;
93
+ expect(RelativePosition.equalsTo(position, position2)).false;
94
+
95
+ position2 = position.clone();
96
+ position2.z = 0;
97
+ expect(RelativePosition.equalsTo(position, position2)).false;
98
+
99
+ position2 = position.clone();
100
+ position2.time = 0;
101
+ expect(RelativePosition.equalsTo(position, position2)).false;
102
+
103
+ position2 = position.clone();
104
+ position2.accuracy = 0;
105
+ expect(RelativePosition.equalsTo(position, position2)).false;
106
+
107
+ position2 = position.clone();
108
+ position2.bearing = 0;
109
+ expect(RelativePosition.equalsTo(position, position2)).false;
110
+
111
+ expect(position.equalsTo(new RelativePosition(1, 2, 3, 123456, 10, Math.PI))).true;
112
+ expect(position.equalsTo(new RelativePosition(1, 2, 3, 123456, 0, Math.PI))).false;
113
+ });
114
+
115
+ it('json', () => {
116
+ let position;
117
+
118
+ position = new RelativePosition(1, 2, 3);
119
+ expect(position.toJson()).to.deep.equal({
120
+ x: 1,
121
+ y: 2,
122
+ z: 3
123
+ });
124
+
125
+ position = new RelativePosition(1, 2, 3);
126
+ position.time = 10;
127
+ expect(position.toJson()).to.deep.equal({
128
+ x: 1,
129
+ y: 2,
130
+ z: 3,
131
+ time: 10
132
+ });
133
+
134
+ position = new RelativePosition(1, 2, 3);
135
+ position.accuracy = 10;
136
+ expect(position.toJson()).to.deep.equal({
137
+ x: 1,
138
+ y: 2,
139
+ z: 3,
140
+ accuracy: 10
141
+ });
142
+
143
+ position = new RelativePosition(1, 2, 3);
144
+ position.bearing = Math.PI;
145
+ expect(position.toJson()).to.deep.equal({
146
+ x: 1,
147
+ y: 2,
148
+ z: 3,
149
+ bearing: Math.PI
150
+ });
151
+
152
+ position = new RelativePosition(1, 2, 3);
153
+ expect(RelativePosition.equalsTo(
154
+ RelativePosition.fromJson(position.toJson()),
155
+ position)
156
+ ).true;
157
+
158
+ position = new RelativePosition(1, 2, 3, 123456, 10, Math.PI);
159
+ expect(RelativePosition.equalsTo(
160
+ RelativePosition.fromJson(position.toJson()),
161
+ position)
162
+ ).true;
163
+
164
+ });
165
+
166
+ });
@@ -24,11 +24,13 @@ describe('UserPosition', () => {
24
24
  expect(() => new UserPosition(45, 5, null, null, 0)).not.throw(Error);
25
25
  expect(() => new UserPosition(45, 5, null, null, 10)).not.throw(Error);
26
26
  expect(() => new UserPosition(45, 5, null, null, -10)).not.throw(Error);
27
+ expect(() => new UserPosition(45, 5, null, null, 'a')).throw(Error);
27
28
 
28
29
  expect(() => new UserPosition(45, 5, null, null, null, null)).not.throw(Error);
29
30
  expect(() => new UserPosition(45, 5, null, null, null, 0)).not.throw(Error);
30
31
  expect(() => new UserPosition(45, 5, null, null, null, 10)).not.throw(Error);
31
32
  expect(() => new UserPosition(45, 5, null, null, null, -10)).throw(Error);
33
+ expect(() => new UserPosition(45, 5, null, null, null, 'a')).throw(Error);
32
34
 
33
35
  expect(() => new UserPosition(45, 5, null, null, null,
34
36
  null, null)).not.throw(Error);
@@ -38,6 +40,8 @@ describe('UserPosition', () => {
38
40
  null, 10)).not.throw(Error);
39
41
  expect(() => new UserPosition(45, 5, null, null, null,
40
42
  null, -10)).not.throw(Error);
43
+ expect(() => new UserPosition(45, 5, null, null, null,
44
+ null, 'a')).throw(Error);
41
45
 
42
46
  const position = new UserPosition(45, 5, 0, new Level(2), 123456, 10, Math.PI, 'foo');
43
47
  expect(position.lat).equals(45);
@@ -0,0 +1,235 @@
1
+ import Level from '../coordinates/Level.js';
2
+ import Node from './Node.js';
3
+
4
+ /**
5
+ * An Edge is a segment composed of two Node
6
+ * An edge is mostly issued from an OsmWay, but this is not always the case.
7
+ * For example, edges created by mapmatching.
8
+ */
9
+ class Edge {
10
+
11
+ /** @type {Node} */
12
+ _node1 = null;
13
+
14
+ /** @type {Node} */
15
+ _node2 = null;
16
+
17
+ /** @type {?Level} */
18
+ _level = null;
19
+
20
+ /** @type {?number} */
21
+ _bearing;
22
+
23
+ /** @type {?number} */
24
+ _length;
25
+
26
+ /** @type {boolean} */
27
+ _computedSizeAndBearing;
28
+
29
+ /** @type {boolean} */
30
+ isStairs = false;
31
+
32
+ /** @type {boolean} */
33
+ isElevator = false;
34
+
35
+ /** @type {boolean} */
36
+ isOneway = false;
37
+
38
+ /** @type {boolean} */
39
+ isConveying = false;
40
+
41
+ /** @type {string} */
42
+ name = null;
43
+
44
+ /**
45
+ * @param {!Node} node1
46
+ * @param {!Node} node2
47
+ * @param {?Level} level
48
+ * @param {?string} name
49
+ */
50
+ constructor(node1, node2, level, name = null) {
51
+
52
+ this.node1 = node1;
53
+ this.node2 = node2;
54
+ this.level = level;
55
+ this.name = name || null;
56
+
57
+ this._computedSizeAndBearing = false;
58
+ }
59
+
60
+ /** @type {!Node} */
61
+ get node1() {
62
+ return this._node1;
63
+ }
64
+
65
+ /** @type {!Node} */
66
+ set node1(node) {
67
+
68
+ if (!(node instanceof Node)) {
69
+ throw new TypeError('node1 is not a Node');
70
+ }
71
+
72
+ if (this._node1 !== null && this._node2 !== this._node1) {
73
+ this._node1.edges = this._node1.edges.filter(edge => edge !== this);
74
+ }
75
+
76
+ node.edges.push(this);
77
+
78
+ this._node1 = node;
79
+ this._computedSizeAndBearing = false;
80
+ }
81
+
82
+ /** @type {!Node} */
83
+ get node2() {
84
+ return this._node2;
85
+ }
86
+
87
+ /** @type {!Node} */
88
+ set node2(node) {
89
+
90
+ if (!(node instanceof Node)) {
91
+ throw new TypeError('node2 is not a Node');
92
+ }
93
+
94
+ if (this._node2 !== null && this._node2 !== this._node1) {
95
+ this._node2.edges = this._node2.edges.filter(edge => edge !== this);
96
+ }
97
+
98
+ node.edges.push(this);
99
+
100
+ this._node2 = node;
101
+ this._computedSizeAndBearing = false;
102
+ }
103
+
104
+ /** @type {?Level} */
105
+ get level() {
106
+ return this._level;
107
+ }
108
+
109
+ /** @type {?Level} */
110
+ set level(level) {
111
+ if (level instanceof Level) {
112
+ this._level = level;
113
+ } else {
114
+ if (typeof level !== 'undefined' && level !== null) {
115
+ throw new Error('level argument is not a Level object');
116
+ }
117
+ this._level = null;
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Get edge bearing from node1 to node2
123
+ * @type {number}
124
+ */
125
+ get bearing() {
126
+ if (!this._computedSizeAndBearing) {
127
+ this._computeSizeAndBearing();
128
+ }
129
+ return this._bearing;
130
+ }
131
+
132
+ /**
133
+ * get edge length
134
+ * @type {number}
135
+ */
136
+ get length() {
137
+ if (!this._computedSizeAndBearing) {
138
+ this._computeSizeAndBearing();
139
+ }
140
+ return this._length;
141
+ }
142
+
143
+ _computeSizeAndBearing() {
144
+ this._length = this.node1.distanceTo(this.node2);
145
+ this._bearing = this.node1.bearingTo(this.node2);
146
+ this._computedSizeAndBearing = true;
147
+ }
148
+
149
+ /**
150
+ * @param {Edge} other
151
+ * @returns {boolean}
152
+ */
153
+ equalsTo(other) {
154
+
155
+ if (!(other instanceof Edge)) {
156
+ return false;
157
+ }
158
+
159
+ return other.node1.equalsTo(this.node1)
160
+ && other.node2.equalsTo(this.node2)
161
+ && Level.equalsTo(other.level, this.level)
162
+ && other.name === this.name
163
+ && other.isConveying === this.isConveying
164
+ && other.isElevator === this.isElevator
165
+ && other.isOneway === this.isOneway
166
+ && other.isStairs === this.isStairs;
167
+ }
168
+
169
+ /**
170
+ * @returns {Edge}
171
+ */
172
+ clone() {
173
+ const edge = new Edge(this.node1, this.node2);
174
+ edge.name = this.name;
175
+ edge.level = this.level;
176
+ edge.isConveying = this.isConveying;
177
+ edge.isElevator = this.isElevator;
178
+ edge.isOneway = this.isOneway;
179
+ edge.isStairs = this.isStairs;
180
+ return edge;
181
+ }
182
+
183
+ /**
184
+ * @returns {object}
185
+ */
186
+ extractProperties() {
187
+ const output = {};
188
+ if (this.level !== null) {
189
+ output.level = this.level.toString();
190
+ }
191
+ if (this.name !== null) {
192
+ output.name = this.name;
193
+ }
194
+ if (this.isConveying) {
195
+ output.isConveying = true;
196
+ }
197
+ if (this.isElevator) {
198
+ output.isElevator = true;
199
+ }
200
+ if (this.isOneway) {
201
+ output.isOneway = true;
202
+ }
203
+ if (this.isStairs) {
204
+ output.isStairs = true;
205
+ }
206
+ return output;
207
+ }
208
+
209
+ /**
210
+ * @param {object} properties
211
+ */
212
+ applyProperties(properties) {
213
+ if (properties.level) {
214
+ this.level = Level.fromString(properties.level);
215
+ }
216
+ if (properties.name) {
217
+ this.name = properties.name;
218
+ }
219
+ if (properties.isConveying) {
220
+ this.isConveying = true;
221
+ }
222
+ if (properties.isElevator) {
223
+ this.isElevator = true;
224
+ }
225
+ if (properties.isOneway) {
226
+ this.isOneway = true;
227
+ }
228
+ if (properties.isStairs) {
229
+ this.isStairs = true;
230
+ }
231
+ return this;
232
+ }
233
+ }
234
+
235
+ export default Edge;