@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.
- package/index.js +22 -22
- package/package.json +5 -4
- package/src/Utils.js +129 -115
- package/src/Utils.spec.js +17 -17
- package/src/coordinates/BoundingBox.spec.js +45 -0
- package/src/coordinates/Coordinates.js +44 -22
- package/src/coordinates/Coordinates.spec.js +5 -0
- package/src/coordinates/RelativePosition.js +3 -3
- package/src/coordinates/RelativePosition.spec.js +166 -0
- package/src/coordinates/UserPosition.spec.js +4 -0
- package/src/graph/Edge.js +235 -0
- package/src/graph/Edge.spec.js +131 -0
- package/src/graph/GraphRouter.js +269 -0
- package/src/graph/GraphRouter.spec.js +316 -0
- package/src/graph/Itinerary.js +527 -0
- package/src/graph/Itinerary.spec.js +809 -0
- package/src/graph/MapMatching.js +220 -0
- package/src/graph/MapMatching.spec.js +272 -0
- package/src/graph/Network.js +168 -0
- package/src/graph/Network.spec.js +95 -0
- package/src/graph/NoRouteFoundError.js +39 -0
- package/src/graph/Node.js +238 -0
- package/src/graph/Node.spec.js +227 -0
- package/src/graph/Step.js +98 -0
- package/src/graph/StepsGeneration.js +122 -0
- package/src/graph/Utils.js +7 -0
- package/src/rotations/AbsoluteHeading.js +12 -6
- package/src/rotations/AbsoluteHeading.spec.js +108 -0
- package/src/rotations/Attitude.js +5 -2
- package/src/rotations/Attitude.spec.js +22 -2
- package/tests/CommonTest.js +91 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
161
|
-
const lambda1 =
|
|
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 =
|
|
173
|
-
this.lng =
|
|
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 =
|
|
199
|
-
const dlng =
|
|
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 =
|
|
211
|
+
const lat1rad = deg2rad(lat1);
|
|
204
212
|
const lat1cos = Math.cos(lat1rad);
|
|
205
|
-
const lat2rad =
|
|
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 =
|
|
223
|
-
const lat2 =
|
|
224
|
-
const diffLng =
|
|
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 +
|
|
243
|
-
const rot2 = Quaternion.fromAxisAngle([1, 0, 0], Math.PI / 2 -
|
|
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],
|
|
249
|
-
const rot2 = Quaternion.fromAxisAngle([0, 0, 1], -
|
|
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 =
|
|
259
|
-
const 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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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;
|