@wemap/geo 4.0.14 → 5.0.3
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 +18 -11
- package/package.json +4 -4
- package/src/Utils.spec.js +38 -0
- package/src/graph/{Edge.js → GraphEdge.js} +33 -94
- package/src/graph/GraphEdge.spec.js +91 -0
- package/src/graph/{Node.js → GraphNode.js} +29 -67
- package/src/graph/GraphNode.spec.js +203 -0
- package/src/graph/{Projection.js → GraphProjection.js} +4 -4
- package/src/graph/GraphUtils.js +17 -0
- package/src/graph/MapMatching.js +7 -5
- package/src/graph/MapMatching.spec.js +26 -26
- package/src/graph/Network.js +85 -48
- package/src/graph/Network.spec.js +66 -15
- package/src/router/GraphItinerary.js +70 -0
- package/src/router/GraphItinerary.spec.js +18 -0
- package/src/{graph → router}/GraphRouter.js +52 -52
- package/src/{graph → router}/GraphRouter.spec.js +71 -98
- package/src/router/GraphRouterOptions.js +19 -0
- package/src/{graph → router}/NoRouteFoundError.js +1 -1
- package/tests/CommonTest.js +6 -6
- package/src/graph/Edge.spec.js +0 -131
- package/src/graph/Itinerary.js +0 -529
- package/src/graph/Itinerary.spec.js +0 -811
- package/src/graph/ItineraryInfo.js +0 -29
- package/src/graph/LevelChange.js +0 -43
- package/src/graph/Node.spec.js +0 -227
- package/src/graph/Step.js +0 -105
- package/src/graph/StepsGeneration.js +0 -123
- package/src/graph/Utils.js +0 -7
|
@@ -3,6 +3,10 @@ import chai from 'chai';
|
|
|
3
3
|
import Constants from '../Constants.js';
|
|
4
4
|
import Coordinates from '../coordinates/Coordinates.js';
|
|
5
5
|
import Network from './Network.js';
|
|
6
|
+
import GraphNode from './GraphNode.js';
|
|
7
|
+
import GraphEdge from './GraphEdge.js';
|
|
8
|
+
|
|
9
|
+
import Level from '../coordinates/Level.js';
|
|
6
10
|
|
|
7
11
|
import {
|
|
8
12
|
nodes, edges, network
|
|
@@ -26,7 +30,7 @@ describe('Network', () => {
|
|
|
26
30
|
|
|
27
31
|
let node = network.getNodeByCoords(nodes[2].coords.clone());
|
|
28
32
|
expect(node).not.null;
|
|
29
|
-
expect(node.
|
|
33
|
+
expect(node.builtFrom).equals('p2');
|
|
30
34
|
|
|
31
35
|
node = network.getNodeByCoords(new Coordinates(43.601888, 3.8841263));
|
|
32
36
|
expect(node).undefined;
|
|
@@ -36,23 +40,12 @@ describe('Network', () => {
|
|
|
36
40
|
|
|
37
41
|
let edge = network.getEdgeByNodes(nodes[1], nodes[2]);
|
|
38
42
|
expect(edge).not.null;
|
|
39
|
-
expect(edge.
|
|
43
|
+
expect(edge.builtFrom).equals('e0');
|
|
40
44
|
|
|
41
45
|
edge = network.getEdgeByNodes(nodes[0], nodes[2]);
|
|
42
46
|
expect(edge).undefined;
|
|
43
47
|
});
|
|
44
48
|
|
|
45
|
-
it('getEdgeByName', () => {
|
|
46
|
-
|
|
47
|
-
let edge = network.getEdgeByName('e1');
|
|
48
|
-
expect(edge).not.null;
|
|
49
|
-
expect(edge.name).equals('e1');
|
|
50
|
-
expect(edge).equals(edges[1]);
|
|
51
|
-
|
|
52
|
-
edge = network.getEdgeByName('abc');
|
|
53
|
-
expect(edge).undefined;
|
|
54
|
-
});
|
|
55
|
-
|
|
56
49
|
it('getBoundingBox', () => {
|
|
57
50
|
|
|
58
51
|
let boundingBox = network.getBoundingBox();
|
|
@@ -85,11 +78,69 @@ describe('Network', () => {
|
|
|
85
78
|
const networkBis = Network.fromCompressedJson(networkJson);
|
|
86
79
|
|
|
87
80
|
networkBis.nodes.forEach((node, i) => {
|
|
88
|
-
expect(node.equalsTo(network.nodes[i])).is.true;
|
|
81
|
+
expect(node.coords.equalsTo(network.nodes[i].coords)).is.true;
|
|
89
82
|
});
|
|
90
83
|
|
|
91
84
|
networkBis.edges.forEach((edge, i) => {
|
|
92
|
-
expect(edge.equalsTo(network.edges[i])).is.true;
|
|
85
|
+
expect(edge.node1.coords.equalsTo(network.edges[i].node1.coords)).is.true;
|
|
86
|
+
expect(edge.node2.coords.equalsTo(network.edges[i].node2.coords)).is.true;
|
|
87
|
+
expect(edge.isOneway).equals(network.edges[i].isOneway);
|
|
93
88
|
});
|
|
94
89
|
});
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
it('getEdgesAtLevel', () => {
|
|
93
|
+
|
|
94
|
+
const _nodes = [
|
|
95
|
+
new GraphNode(new Coordinates(43.6091194, 3.884099, null, new Level(0)), 'p0'),
|
|
96
|
+
new GraphNode(new Coordinates(43.6093629, 3.8842777, null, new Level(0)), 'p1'),
|
|
97
|
+
new GraphNode(new Coordinates(43.6094654, 3.8842167, null, new Level(0, 1)), 'p2'),
|
|
98
|
+
new GraphNode(new Coordinates(43.6094902, 3.8843416, null, new Level(1)), 'p3'),
|
|
99
|
+
new GraphNode(new Coordinates(43.6095463, 3.8843837, null, new Level(1)), 'p4'),
|
|
100
|
+
new GraphNode(new Coordinates(43.6095552, 3.8844971, null, new Level(2)), 'p5'),
|
|
101
|
+
new GraphNode(new Coordinates(43.6095908, 3.8844662, null, new Level(1)), 'p6'),
|
|
102
|
+
new GraphNode(new Coordinates(43.6096412, 3.8844706, null, new Level(1)), 'p7')
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
const _edges = [
|
|
106
|
+
new GraphEdge(_nodes[0], _nodes[1], new Level(0), 'e0'),
|
|
107
|
+
new GraphEdge(_nodes[1], _nodes[2], new Level(0), 'e1'),
|
|
108
|
+
new GraphEdge(_nodes[2], _nodes[3], new Level(1), 'e2'),
|
|
109
|
+
new GraphEdge(_nodes[3], _nodes[4], new Level(1), 'e3'),
|
|
110
|
+
new GraphEdge(_nodes[4], _nodes[5], new Level(1, 2), 'e4'),
|
|
111
|
+
new GraphEdge(_nodes[5], _nodes[6], new Level(1, 2), 'e5'),
|
|
112
|
+
new GraphEdge(_nodes[6], _nodes[7], new Level(1), 'e6')
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
const _network = new Network(_nodes, _edges);
|
|
116
|
+
|
|
117
|
+
const edgesAtLevel0 = _network.getEdgesAtLevel(new Level(0), false);
|
|
118
|
+
expect(edgesAtLevel0.length).equals(2);
|
|
119
|
+
expect(edgesAtLevel0[0].builtFrom).equals('e0');
|
|
120
|
+
expect(edgesAtLevel0[1].builtFrom).equals('e1');
|
|
121
|
+
|
|
122
|
+
const segmentsAtLevel1 = _network.getEdgesAtLevel(new Level(1), false);
|
|
123
|
+
expect(segmentsAtLevel1.length).equals(3);
|
|
124
|
+
expect(segmentsAtLevel1[0].builtFrom).equals('e2');
|
|
125
|
+
expect(segmentsAtLevel1[1].builtFrom).equals('e3');
|
|
126
|
+
expect(segmentsAtLevel1[2].builtFrom).equals('e6');
|
|
127
|
+
|
|
128
|
+
const segmentsAtLevel2 = _network.getEdgesAtLevel(new Level(2), false);
|
|
129
|
+
expect(segmentsAtLevel2.length).equals(0);
|
|
130
|
+
const segmentsAtLevel2b = _network.getEdgesAtLevel(new Level(2), true);
|
|
131
|
+
expect(segmentsAtLevel2b.length).equals(2);
|
|
132
|
+
|
|
133
|
+
const edgesAtLevel01 = _network.getEdgesAtLevel(new Level(0, 1), false);
|
|
134
|
+
expect(edgesAtLevel01.length).equals(5);
|
|
135
|
+
const edgesAtLevel01b = _network.getEdgesAtLevel(new Level(0, 1), true);
|
|
136
|
+
expect(edgesAtLevel01b.length).equals(7);
|
|
137
|
+
|
|
138
|
+
const edgesAtLevel12 = _network.getEdgesAtLevel(new Level(1, 2), false);
|
|
139
|
+
expect(edgesAtLevel12.length).equals(5);
|
|
140
|
+
|
|
141
|
+
const edgesAtLevel02 = _network.getEdgesAtLevel(new Level(0, 2), false);
|
|
142
|
+
expect(edgesAtLevel02.length).equals(7);
|
|
143
|
+
|
|
144
|
+
});
|
|
145
|
+
|
|
95
146
|
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import Coordinates from '../coordinates/Coordinates.js';
|
|
2
|
+
import GraphNode from '../graph/GraphNode.js';
|
|
3
|
+
import GraphEdge from '../graph/GraphEdge.js';
|
|
4
|
+
import { getEdgeByNodes } from '../graph/GraphUtils.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @template T
|
|
8
|
+
*/
|
|
9
|
+
class GraphItinerary {
|
|
10
|
+
|
|
11
|
+
/** @type {Coordinates} */
|
|
12
|
+
start;
|
|
13
|
+
|
|
14
|
+
/** @type {Coordinates} */
|
|
15
|
+
end;
|
|
16
|
+
|
|
17
|
+
/** @type {GraphNode<T>[]} */
|
|
18
|
+
nodes;
|
|
19
|
+
|
|
20
|
+
/** @type {GraphEdge<T>[]} */
|
|
21
|
+
edges;
|
|
22
|
+
|
|
23
|
+
/** @type {number[]} */
|
|
24
|
+
edgesWeights;
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @template T
|
|
29
|
+
* @param {GraphNode<T>[]} networkNodes
|
|
30
|
+
* @param {number[]} edgesWeights
|
|
31
|
+
* @returns {GraphItinerary<T>}
|
|
32
|
+
*/
|
|
33
|
+
static fromNetworkNodes(networkNodes, edgesWeights) {
|
|
34
|
+
const itinerary = new GraphItinerary();
|
|
35
|
+
itinerary.edgesWeights = edgesWeights;
|
|
36
|
+
|
|
37
|
+
itinerary.nodes = networkNodes.map(node => {
|
|
38
|
+
const newNode = node.clone();
|
|
39
|
+
// Remove node edges, they will be added later.
|
|
40
|
+
newNode.edges = [];
|
|
41
|
+
return newNode;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
itinerary.edges = [];
|
|
45
|
+
networkNodes.forEach((node, idx, arr) => {
|
|
46
|
+
if (idx === 0) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Retrieve network edge
|
|
51
|
+
const prevNode = arr[idx - 1];
|
|
52
|
+
const edge = getEdgeByNodes(prevNode.edges, prevNode, node);
|
|
53
|
+
|
|
54
|
+
// Create itinerary edge
|
|
55
|
+
const newEdge = new GraphEdge(
|
|
56
|
+
itinerary.nodes[idx - 1],
|
|
57
|
+
itinerary.nodes[idx],
|
|
58
|
+
edge.level,
|
|
59
|
+
edge.builtFrom
|
|
60
|
+
);
|
|
61
|
+
newEdge.isOneway = edge.isOneway;
|
|
62
|
+
itinerary.edges.push(newEdge);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return itinerary;
|
|
66
|
+
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export default GraphItinerary;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import chai from 'chai';
|
|
2
|
+
|
|
3
|
+
import GraphItinerary from './GraphItinerary.js';
|
|
4
|
+
|
|
5
|
+
const { expect } = chai;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param {GraphItinerary} itinerary
|
|
9
|
+
*/
|
|
10
|
+
export function isReadable(itinerary) {
|
|
11
|
+
for (let i = 0; i < itinerary.nodes.length; i++) {
|
|
12
|
+
const node = itinerary.nodes[i];
|
|
13
|
+
if (i !== itinerary.nodes.length - 1) {
|
|
14
|
+
expect(itinerary.edges[i].node1).equal(node);
|
|
15
|
+
expect(itinerary.edges[i].node2).equal(itinerary.nodes[i + 1]);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -1,59 +1,55 @@
|
|
|
1
1
|
import Coordinates from '../coordinates/Coordinates.js';
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import MapMatching from './MapMatching.js';
|
|
8
|
-
import { getDurationFromLength } from './Utils.js';
|
|
9
|
-
import NoRouteFoundError from './NoRouteFoundError.js';
|
|
3
|
+
import GraphEdge from '../graph/GraphEdge.js';
|
|
4
|
+
import Network from '../graph/Network.js';
|
|
5
|
+
import GraphNode from '../graph/GraphNode.js';
|
|
6
|
+
import MapMatching from '../graph/MapMatching.js';
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
import NoRouteFoundError from './NoRouteFoundError.js';
|
|
9
|
+
import GraphRouterOptions from './GraphRouterOptions.js';
|
|
10
|
+
import GraphItinerary from './GraphItinerary.js';
|
|
13
11
|
|
|
12
|
+
/**
|
|
13
|
+
* @template T
|
|
14
|
+
* @abstract
|
|
15
|
+
*/
|
|
14
16
|
class GraphRouter {
|
|
15
17
|
|
|
16
|
-
/** @type {
|
|
17
|
-
static defaultWeightFn = edge => edge.isElevator ? 30 : getDurationFromLength(edge.length);
|
|
18
|
-
|
|
19
|
-
/** @type {!Network} */
|
|
18
|
+
/** @type {!Network<T>} */
|
|
20
19
|
_network;
|
|
21
20
|
|
|
22
21
|
/**
|
|
23
|
-
* @param {!Network} network
|
|
24
|
-
* @param {number} mapMatchingMaxDistance
|
|
22
|
+
* @param {!Network<T>} network
|
|
25
23
|
*/
|
|
26
|
-
constructor(network
|
|
24
|
+
constructor(network) {
|
|
27
25
|
this._network = network;
|
|
28
26
|
this._mapMatching = new MapMatching(network);
|
|
29
|
-
this._mapMatching.maxDistance = mapMatchingMaxDistance;
|
|
30
27
|
}
|
|
31
28
|
|
|
32
29
|
/**
|
|
33
|
-
*
|
|
34
|
-
* @param {!
|
|
35
|
-
* @param {!
|
|
36
|
-
* @param {
|
|
37
|
-
* @returns {
|
|
30
|
+
* @template T
|
|
31
|
+
* @param {!GraphNode<T>|!Coordinates} start
|
|
32
|
+
* @param {!GraphNode<T>|!Coordinates} end
|
|
33
|
+
* @param {GraphRouterOptions} _options
|
|
34
|
+
* @returns {GraphItinerary<T>}
|
|
38
35
|
*/
|
|
39
|
-
getShortestPath(start, end,
|
|
36
|
+
getShortestPath(start, end, options = new GraphRouterOptions()) {
|
|
40
37
|
|
|
41
|
-
if (!(start instanceof
|
|
38
|
+
if (!(start instanceof GraphNode) && !(start instanceof Coordinates)) {
|
|
42
39
|
throw new Error('Unknown start type');
|
|
43
40
|
}
|
|
44
41
|
|
|
45
|
-
if (!(end instanceof
|
|
42
|
+
if (!(end instanceof GraphNode) && !(end instanceof Coordinates)) {
|
|
46
43
|
throw new Error('Unknown end type');
|
|
47
44
|
}
|
|
48
45
|
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
const weightEdgeFn = GraphRouter.defaultWeightFn;
|
|
46
|
+
const { acceptEdgeFn, weightEdgeFn, projectionMaxDistance } = options;
|
|
47
|
+
this._mapMatching.maxDistance = projectionMaxDistance;
|
|
52
48
|
|
|
53
49
|
const createdNodes = [];
|
|
54
50
|
|
|
55
51
|
const retrieveOrCreateNearestNode = point => {
|
|
56
|
-
if (point instanceof
|
|
52
|
+
if (point instanceof GraphNode) {
|
|
57
53
|
return point;
|
|
58
54
|
}
|
|
59
55
|
|
|
@@ -69,7 +65,7 @@ class GraphRouter {
|
|
|
69
65
|
+ `> ${this._mapMatching.maxDistance.toFixed(0)} meters`
|
|
70
66
|
);
|
|
71
67
|
}
|
|
72
|
-
if (proj.nearestElement instanceof
|
|
68
|
+
if (proj.nearestElement instanceof GraphNode) {
|
|
73
69
|
return proj.nearestElement;
|
|
74
70
|
}
|
|
75
71
|
// if (proj.nearestElement instanceof Edge)
|
|
@@ -87,39 +83,39 @@ class GraphRouter {
|
|
|
87
83
|
}
|
|
88
84
|
};
|
|
89
85
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const endNode = retrieveOrCreateNearestNode(end);
|
|
86
|
+
const startNode = retrieveOrCreateNearestNode(start);
|
|
87
|
+
const endNode = retrieveOrCreateNearestNode(end);
|
|
93
88
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
89
|
+
const graphItinerary = this.getShortestPathBetweenGraphNodes(
|
|
90
|
+
startNode,
|
|
91
|
+
endNode,
|
|
92
|
+
acceptEdgeFn,
|
|
93
|
+
weightEdgeFn
|
|
94
|
+
);
|
|
95
|
+
graphItinerary.start = start instanceof GraphNode ? start.coords : start;
|
|
96
|
+
graphItinerary.end = end instanceof GraphNode ? end.coords : end;
|
|
99
97
|
|
|
100
|
-
|
|
101
|
-
start instanceof Node ? start._coords : start,
|
|
102
|
-
end instanceof Node ? end.coords : end);
|
|
98
|
+
removeCreatedNodes();
|
|
103
99
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
} catch (e) {
|
|
107
|
-
removeCreatedNodes();
|
|
108
|
-
throw e;
|
|
100
|
+
if (!graphItinerary.nodes.length) {
|
|
101
|
+
throw new NoRouteFoundError(start, end);
|
|
109
102
|
}
|
|
103
|
+
|
|
104
|
+
return graphItinerary;
|
|
105
|
+
|
|
110
106
|
}
|
|
111
107
|
|
|
112
108
|
/**
|
|
113
|
-
* @param {
|
|
109
|
+
* @param {GraphEdge<T>} edge
|
|
114
110
|
* @param {Coordinates} point
|
|
115
111
|
*/
|
|
116
112
|
createNodeInsideEdge(edge, point) {
|
|
117
113
|
const a = edge.node1;
|
|
118
114
|
const b = edge.node2;
|
|
119
115
|
|
|
120
|
-
const m = new
|
|
116
|
+
const m = new GraphNode(point);
|
|
121
117
|
m.coords.level = edge.level;
|
|
122
|
-
m.
|
|
118
|
+
m.builtFrom = edge.builtFrom;
|
|
123
119
|
|
|
124
120
|
const u = edge.clone();
|
|
125
121
|
u.node1 = a;
|
|
@@ -143,7 +139,7 @@ class GraphRouter {
|
|
|
143
139
|
}
|
|
144
140
|
|
|
145
141
|
/**
|
|
146
|
-
* @param {
|
|
142
|
+
* @param {GraphNode<T>} _node
|
|
147
143
|
*/
|
|
148
144
|
removeNodeFromPreviouslyCreatedEdge(_node) {
|
|
149
145
|
const u = _node.edges[0];
|
|
@@ -163,7 +159,7 @@ class GraphRouter {
|
|
|
163
159
|
);
|
|
164
160
|
}
|
|
165
161
|
|
|
166
|
-
|
|
162
|
+
getShortestPathBetweenGraphNodes(start, end, acceptEdgeFn, weightFn) {
|
|
167
163
|
const distanceMap = {},
|
|
168
164
|
checking = {},
|
|
169
165
|
vertexList = {},
|
|
@@ -247,11 +243,14 @@ class GraphRouter {
|
|
|
247
243
|
delete vertexList[current];
|
|
248
244
|
}
|
|
249
245
|
|
|
246
|
+
const edgesWeights = [];
|
|
247
|
+
|
|
250
248
|
// now we have the most efficient paths for all vertices
|
|
251
249
|
// build the path for the user specified vertex(end)
|
|
252
250
|
let endId = end.uniqueRouterId;
|
|
253
251
|
while (parentVertices[endId]) {
|
|
254
252
|
path.unshift(vertexNodes[endId]);
|
|
253
|
+
edgesWeights.unshift(distanceMap[endId] - distanceMap[parentVertices[endId]]);
|
|
255
254
|
endId = parentVertices[endId];
|
|
256
255
|
}
|
|
257
256
|
if (path.length !== 0) {
|
|
@@ -263,7 +262,8 @@ class GraphRouter {
|
|
|
263
262
|
delete vertex.uniqueRouterId;
|
|
264
263
|
});
|
|
265
264
|
|
|
266
|
-
|
|
265
|
+
// This clone the itinerary and temporary nodes
|
|
266
|
+
return GraphItinerary.fromNetworkNodes(path, edgesWeights);
|
|
267
267
|
}
|
|
268
268
|
}
|
|
269
269
|
export default GraphRouter;
|