@wemap/geo 0.3.9 → 0.4.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/index.js +2 -0
- package/package.json +2 -2
- package/src/Utils.js +12 -0
- package/src/graph/GraphRouter.js +29 -7
- package/src/graph/GraphRouter.spec.js +96 -9
- package/src/graph/Itinerary.js +5 -12
- package/src/graph/Itinerary.spec.js +3 -2
- package/src/graph/Network.js +5 -5
- package/src/graph/Step.js +2 -2
- package/tests/CommonTest.js +32 -3
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"directory": "packages/geo"
|
|
13
13
|
},
|
|
14
14
|
"name": "@wemap/geo",
|
|
15
|
-
"version": "0.
|
|
15
|
+
"version": "0.4.0",
|
|
16
16
|
"bugs": {
|
|
17
17
|
"url": "https://github.com/wemap/wemap-utils-js/issues"
|
|
18
18
|
},
|
|
@@ -31,5 +31,5 @@
|
|
|
31
31
|
"lodash.isnumber": "^3.0.3",
|
|
32
32
|
"lodash.isstring": "^4.0.1"
|
|
33
33
|
},
|
|
34
|
-
"gitHead": "
|
|
34
|
+
"gitHead": "08d7d843add321d899aef07f9e2ec7739270dc70"
|
|
35
35
|
}
|
package/src/Utils.js
ADDED
package/src/graph/GraphRouter.js
CHANGED
|
@@ -7,16 +7,31 @@ import Edge from './Edge';
|
|
|
7
7
|
import Itinerary from './Itinerary';
|
|
8
8
|
import Node from './Node';
|
|
9
9
|
import MapMatching from './MapMatching';
|
|
10
|
+
import Utils from '../Utils';
|
|
11
|
+
|
|
12
|
+
const DEFAULT_ACCEPT_EDGE_FN = () => true;
|
|
13
|
+
const DEFAULT_ACCEPT_ONEWAY_FN = () => true;
|
|
14
|
+
const DEFAULT_WEIGHT_FN = edge => Utils.getDurationFromLength(edge.length);
|
|
10
15
|
|
|
11
16
|
class GraphRouter {
|
|
12
17
|
|
|
13
|
-
constructor(network) {
|
|
18
|
+
constructor(network, mapMatchingMaxDistance = 50) {
|
|
14
19
|
this.network = network;
|
|
15
20
|
this.mapMatching = new MapMatching(network);
|
|
16
|
-
this.mapMatching.maxDistance =
|
|
21
|
+
this.mapMatching.maxDistance = mapMatchingMaxDistance;
|
|
17
22
|
}
|
|
18
23
|
|
|
19
|
-
getShortestPath(
|
|
24
|
+
getShortestPath(
|
|
25
|
+
_start,
|
|
26
|
+
_end,
|
|
27
|
+
_acceptEdgeFn,
|
|
28
|
+
_weightFn,
|
|
29
|
+
_acceptOneWayFn) {
|
|
30
|
+
|
|
31
|
+
const acceptEdgeFn = _acceptEdgeFn || DEFAULT_ACCEPT_EDGE_FN;
|
|
32
|
+
const weightFn = _weightFn || DEFAULT_WEIGHT_FN;
|
|
33
|
+
const acceptOneWayFn = _acceptOneWayFn || DEFAULT_ACCEPT_ONEWAY_FN;
|
|
34
|
+
|
|
20
35
|
let start;
|
|
21
36
|
let startNodeCreated;
|
|
22
37
|
|
|
@@ -74,7 +89,8 @@ class GraphRouter {
|
|
|
74
89
|
throw new Error('Cannot provide path because no end point has been found');
|
|
75
90
|
}
|
|
76
91
|
|
|
77
|
-
const path = this.getShortestPathInternal(start, end,
|
|
92
|
+
const path = this.getShortestPathInternal(start, end,
|
|
93
|
+
acceptEdgeFn, weightFn, acceptOneWayFn);
|
|
78
94
|
|
|
79
95
|
let route;
|
|
80
96
|
if (path && path.length !== 0) {
|
|
@@ -138,7 +154,7 @@ class GraphRouter {
|
|
|
138
154
|
);
|
|
139
155
|
}
|
|
140
156
|
|
|
141
|
-
getShortestPathInternal(start, end, acceptEdgeFn) {
|
|
157
|
+
getShortestPathInternal(start, end, acceptEdgeFn, weightFn, acceptOneWayFn) {
|
|
142
158
|
const distanceMap = {},
|
|
143
159
|
checking = {},
|
|
144
160
|
vertexList = {},
|
|
@@ -189,7 +205,7 @@ class GraphRouter {
|
|
|
189
205
|
})
|
|
190
206
|
// for each vertex we can reach
|
|
191
207
|
.forEach(edge => {
|
|
192
|
-
let to, from;
|
|
208
|
+
let to, from, reversed = false;
|
|
193
209
|
// determine the direction of travel
|
|
194
210
|
if (edge.node1.uniqueRouterId === current) {
|
|
195
211
|
to = edge.node2.uniqueRouterId;
|
|
@@ -197,10 +213,16 @@ class GraphRouter {
|
|
|
197
213
|
} else {
|
|
198
214
|
to = edge.node1.uniqueRouterId;
|
|
199
215
|
from = edge.node2.uniqueRouterId;
|
|
216
|
+
reversed = true;
|
|
200
217
|
}
|
|
218
|
+
|
|
219
|
+
if (!acceptOneWayFn(edge, reversed)) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
201
223
|
// distance is how far we travelled to reach the
|
|
202
224
|
// current vertex, plus cost of travel the next(to)
|
|
203
|
-
const distance = distanceMap[current] + edge
|
|
225
|
+
const distance = distanceMap[current] + weightFn(edge);
|
|
204
226
|
|
|
205
227
|
// if we have found a cheaper path
|
|
206
228
|
// update the hash of costs
|
|
@@ -1,23 +1,30 @@
|
|
|
1
1
|
import { expect } from 'chai';
|
|
2
2
|
|
|
3
|
+
import WGS84 from '../coordinates/WGS84';
|
|
3
4
|
import GraphRouter from './GraphRouter';
|
|
5
|
+
import Node from './Node';
|
|
6
|
+
import Edge from './Edge';
|
|
4
7
|
|
|
5
8
|
import {
|
|
6
|
-
network, nodes, itineraryStart, itineraryEnd
|
|
9
|
+
network, nodes, itineraryStart, itineraryEnd, routingOptions
|
|
7
10
|
} from '../../tests/CommonTest';
|
|
11
|
+
import Network from './Network';
|
|
8
12
|
|
|
9
|
-
describe('GraphRouter', () => {
|
|
10
13
|
|
|
14
|
+
const {
|
|
15
|
+
acceptEdgeWithoutStairsFn, weightFn, acceptOneWayFn
|
|
16
|
+
} = routingOptions;
|
|
17
|
+
|
|
18
|
+
describe('GraphRouter', () => {
|
|
11
19
|
|
|
12
20
|
const router = new GraphRouter(network);
|
|
13
21
|
let itinerary;
|
|
14
22
|
|
|
15
|
-
|
|
16
23
|
it('network did not change', () => {
|
|
17
24
|
const nodesBefore = network.nodes.slice(0);
|
|
18
25
|
const edgesBefore = network.edges.slice(0);
|
|
19
26
|
|
|
20
|
-
itinerary = router.getShortestPath(itineraryStart, itineraryEnd);
|
|
27
|
+
itinerary = router.getShortestPath(itineraryStart, itineraryEnd, null, weightFn);
|
|
21
28
|
|
|
22
29
|
const isEdgeInArray = (edgeArray, edge) => edgeArray.find(_edge =>
|
|
23
30
|
edge.node1 === _edge.node1
|
|
@@ -62,11 +69,91 @@ describe('GraphRouter', () => {
|
|
|
62
69
|
}
|
|
63
70
|
|
|
64
71
|
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
describe('GraphRouter - Itinerary without stairs', () => {
|
|
76
|
+
|
|
77
|
+
const router = new GraphRouter(network);
|
|
78
|
+
const itinerary = router.getShortestPath(
|
|
79
|
+
itineraryStart,
|
|
80
|
+
itineraryEnd,
|
|
81
|
+
acceptEdgeWithoutStairsFn,
|
|
82
|
+
weightFn
|
|
83
|
+
);
|
|
84
|
+
const itineraryWithoutRestriction = router.getShortestPath(
|
|
85
|
+
itineraryStart,
|
|
86
|
+
itineraryEnd,
|
|
87
|
+
null,
|
|
88
|
+
weightFn
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
it('no stairs found', () => {
|
|
92
|
+
expect(itinerary).is.not.undefined;
|
|
93
|
+
expect(itinerary.edges.some(node => node.data && node.data.stairs)).false;
|
|
94
|
+
|
|
95
|
+
expect(itineraryWithoutRestriction).is.not.undefined;
|
|
96
|
+
expect(itineraryWithoutRestriction.edges.some(node => node.data && node.data.stairs)).true;
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
describe('GraphRouter - Itinerary one way', () => {
|
|
102
|
+
|
|
103
|
+
const owNodes = [
|
|
104
|
+
new Node(new WGS84(43.6094442, 3.8842049), { name: 'p0' }),
|
|
105
|
+
new Node(new WGS84(43.6094286, 3.8842011), { name: 'p1' }),
|
|
106
|
+
new Node(new WGS84(43.6094058, 3.8841955), { name: 'p2' }),
|
|
107
|
+
new Node(new WGS84(43.6093893, 3.8841914), { name: 'p3' }),
|
|
108
|
+
new Node(new WGS84(43.6094256, 3.8842247), { name: 'p4' }),
|
|
109
|
+
new Node(new WGS84(43.6094028, 3.8842191), { name: 'p5' })
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
const owEdges = [
|
|
113
|
+
new Edge(owNodes[0], owNodes[1], { name: 'e0' }),
|
|
114
|
+
new Edge(owNodes[1], owNodes[2], { name: 'e1', oneWay: true }),
|
|
115
|
+
new Edge(owNodes[2], owNodes[3], { name: 'e2' }),
|
|
116
|
+
new Edge(owNodes[1], owNodes[4], { name: 'e3' }),
|
|
117
|
+
new Edge(owNodes[4], owNodes[5], { name: 'e4' }),
|
|
118
|
+
new Edge(owNodes[5], owNodes[2], { name: 'e5' })
|
|
119
|
+
];
|
|
120
|
+
const owNetwork = new Network(owNodes, owEdges);
|
|
121
|
+
const owRouter = new GraphRouter(owNetwork);
|
|
122
|
+
|
|
123
|
+
const start = new WGS84(43.6094542, 3.8842072);
|
|
124
|
+
const end = new WGS84(43.6093792, 3.8841889);
|
|
125
|
+
|
|
126
|
+
const itinerary = owRouter.getShortestPath(
|
|
127
|
+
start,
|
|
128
|
+
end,
|
|
129
|
+
null,
|
|
130
|
+
weightFn,
|
|
131
|
+
acceptOneWayFn
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const itineraryOtherWay = owRouter.getShortestPath(
|
|
135
|
+
end,
|
|
136
|
+
start,
|
|
137
|
+
null,
|
|
138
|
+
weightFn,
|
|
139
|
+
acceptOneWayFn
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
it('do not use oneway segments', () => {
|
|
143
|
+
|
|
144
|
+
expect(itinerary).is.not.undefined;
|
|
145
|
+
expect(itinerary.nodes[0]).equals(owNodes[0]);
|
|
146
|
+
expect(itinerary.nodes[1]).equals(owNodes[1]);
|
|
147
|
+
expect(itinerary.nodes[2]).equals(owNodes[2]);
|
|
148
|
+
expect(itinerary.nodes[3]).equals(owNodes[3]);
|
|
149
|
+
|
|
150
|
+
expect(itineraryOtherWay).is.not.undefined;
|
|
151
|
+
expect(itineraryOtherWay.nodes[0]).equals(owNodes[3]);
|
|
152
|
+
expect(itineraryOtherWay.nodes[1]).equals(owNodes[2]);
|
|
153
|
+
expect(itineraryOtherWay.nodes[2]).equals(owNodes[5]);
|
|
154
|
+
expect(itineraryOtherWay.nodes[3]).equals(owNodes[4]);
|
|
155
|
+
expect(itineraryOtherWay.nodes[4]).equals(owNodes[1]);
|
|
156
|
+
expect(itineraryOtherWay.nodes[5]).equals(owNodes[0]);
|
|
65
157
|
|
|
66
|
-
it('no route found', () => {
|
|
67
|
-
const itineraryWithoutStairs = router.getShortestPath(itineraryStart, itineraryEnd,
|
|
68
|
-
edge => !edge.data || !edge.data.hasOwnProperty('stairs') || !edge.data.stairs
|
|
69
|
-
);
|
|
70
|
-
expect(itineraryWithoutStairs).is.undefined;
|
|
71
158
|
});
|
|
72
159
|
});
|
package/src/graph/Itinerary.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/* eslint-disable max-statements */
|
|
2
2
|
import { WGS84, Level } from '@wemap/geo';
|
|
3
|
+
import {
|
|
4
|
+
diffAngle, deg2rad
|
|
5
|
+
} from '@wemap/maths';
|
|
3
6
|
|
|
4
7
|
import Network from './Network';
|
|
5
8
|
import Edge from './Edge';
|
|
6
9
|
import Node from './Node';
|
|
7
10
|
import Step from './Step';
|
|
8
|
-
import {
|
|
9
|
-
diffAngle, deg2rad
|
|
10
|
-
} from '@wemap/maths';
|
|
11
11
|
import MapMatching from './MapMatching';
|
|
12
|
+
import Utils from '../Utils';
|
|
12
13
|
|
|
13
14
|
const SKIP_STEP_ANGLE_MAX = deg2rad(20);
|
|
14
15
|
|
|
@@ -67,15 +68,7 @@ class Itinerary extends Network {
|
|
|
67
68
|
* @param {Number} speed in km/h
|
|
68
69
|
*/
|
|
69
70
|
getDuration(speed) {
|
|
70
|
-
return
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Get route duration
|
|
75
|
-
* @param {Number} speed in km/h
|
|
76
|
-
*/
|
|
77
|
-
static getDurationFromLength(length, speed = 4) {
|
|
78
|
-
return length / (speed * 1000 / 3600);
|
|
71
|
+
return Utils.getDurationFromLength(this.length, speed);
|
|
79
72
|
}
|
|
80
73
|
|
|
81
74
|
get steps() {
|
|
@@ -4,7 +4,7 @@ import { expect } from 'chai';
|
|
|
4
4
|
import GraphRouter from './GraphRouter';
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
|
-
network, nodes, itineraryStart, itineraryEnd, edges
|
|
7
|
+
network, nodes, itineraryStart, itineraryEnd, edges, routingOptions
|
|
8
8
|
} from '../../tests/CommonTest';
|
|
9
9
|
import Level from '../coordinates/Level';
|
|
10
10
|
import WGS84 from '../coordinates/WGS84';
|
|
@@ -12,7 +12,8 @@ import WGS84 from '../coordinates/WGS84';
|
|
|
12
12
|
describe('Itinerary', () => {
|
|
13
13
|
|
|
14
14
|
const router = new GraphRouter(network);
|
|
15
|
-
const itinerary = router.getShortestPath(itineraryStart, itineraryEnd
|
|
15
|
+
const itinerary = router.getShortestPath(itineraryStart, itineraryEnd, null,
|
|
16
|
+
routingOptions.weightFn);
|
|
16
17
|
|
|
17
18
|
it('is readable', () => {
|
|
18
19
|
|
package/src/graph/Network.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*/
|
|
4
4
|
class Network {
|
|
5
5
|
|
|
6
|
-
constructor() {
|
|
7
|
-
this.nodes = [];
|
|
8
|
-
this.edges = [];
|
|
6
|
+
constructor(nodes, edges) {
|
|
7
|
+
this.nodes = Array.isArray(nodes) ? nodes : [];
|
|
8
|
+
this.edges = Array.isArray(edges) ? edges : [];
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
getNodeByCoords(coords) {
|
|
@@ -27,12 +27,12 @@ class Network {
|
|
|
27
27
|
|
|
28
28
|
let nodeToStringFn = _nodeToStringFn;
|
|
29
29
|
if (!nodeToStringFn) {
|
|
30
|
-
nodeToStringFn = node => `${node.data
|
|
30
|
+
nodeToStringFn = node => `${node.data}`;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
let edgeToStringFn = _edgeToStringFn;
|
|
34
34
|
if (!_edgeToStringFn) {
|
|
35
|
-
edgeToStringFn = edge => `${edge.data
|
|
35
|
+
edgeToStringFn = edge => `${edge.data}`;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
let output
|
package/src/graph/Step.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import Utils from '../Utils';
|
|
2
2
|
|
|
3
3
|
class Step {
|
|
4
4
|
|
|
@@ -49,7 +49,7 @@ class Step {
|
|
|
49
49
|
* @param {Number} speed in km/h
|
|
50
50
|
*/
|
|
51
51
|
getDuration(speed) {
|
|
52
|
-
return
|
|
52
|
+
return Utils.getDurationFromLength(this.length, speed);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
}
|
package/tests/CommonTest.js
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
import Edge from '../src/graph/Edge';
|
|
6
6
|
import Node from '../src/graph/Node';
|
|
7
7
|
import Network from '../src/graph/Network';
|
|
8
|
+
import Utils from '../src/Utils';
|
|
8
9
|
|
|
9
10
|
const nodes = [
|
|
10
11
|
new Node(new WGS84(43.6092404, 3.884099), { name: 'p0' }),
|
|
@@ -23,7 +24,11 @@ const nodes = [
|
|
|
23
24
|
new Node(new WGS84(43.6093597, 3.8842336), { name: 'p13' }),
|
|
24
25
|
new Node(new WGS84(43.6093202, 3.8842218), { name: 'p14' }),
|
|
25
26
|
new Node(new WGS84(43.6093123, 3.8842731), { name: 'p15' }),
|
|
26
|
-
new Node(new WGS84(43.6092681, 3.8842604), { name: 'p16' })
|
|
27
|
+
new Node(new WGS84(43.6092681, 3.8842604), { name: 'p16' }),
|
|
28
|
+
new Node(new WGS84(43.6093279, 3.8842777), { name: 'p17' }),
|
|
29
|
+
new Node(new WGS84(43.6093279, 3.8842777), { name: 'p18' }),
|
|
30
|
+
new Node(new WGS84(43.6093323, 3.8842483), { name: 'p19a' }),
|
|
31
|
+
new Node(new WGS84(43.6093323, 3.8842483), { name: 'p19b' })
|
|
27
32
|
];
|
|
28
33
|
|
|
29
34
|
const edges = [
|
|
@@ -42,7 +47,12 @@ const edges = [
|
|
|
42
47
|
new Edge(nodes[12], nodes[13], { name: 'e12', stairs: true }, new Level(1, 2)),
|
|
43
48
|
new Edge(nodes[13], nodes[14], { name: 'e13', stairs: true }, new Level(1, 2)),
|
|
44
49
|
new Edge(nodes[14], nodes[15], { name: 'e14' }, new Level(1)),
|
|
45
|
-
new Edge(nodes[15], nodes[16], { name: 'e15' }, new Level(1))
|
|
50
|
+
new Edge(nodes[15], nodes[16], { name: 'e15' }, new Level(1)),
|
|
51
|
+
new Edge(nodes[15], nodes[17], { name: 'e16' }, new Level(1)),
|
|
52
|
+
new Edge(nodes[19], nodes[17], { name: 'e17' }, new Level(1)),
|
|
53
|
+
new Edge(nodes[10], nodes[18], { name: 'e18' }, new Level(2)),
|
|
54
|
+
new Edge(nodes[20], nodes[18], { name: 'e19' }, new Level(2)),
|
|
55
|
+
new Edge(nodes[19], nodes[20], { name: 'e20', elevator: true }, new Level(1, 2))
|
|
46
56
|
];
|
|
47
57
|
|
|
48
58
|
const network = new Network();
|
|
@@ -52,6 +62,25 @@ network.nodes = nodes;
|
|
|
52
62
|
const itineraryStart = new WGS84(43.6092754, 3.8842306, null, new Level(2));
|
|
53
63
|
const itineraryEnd = new WGS84(43.6092602, 3.8842669, null, new Level(1));
|
|
54
64
|
|
|
65
|
+
const detailedStringArgs = [
|
|
66
|
+
node => !node.data ? 'created' : `${node.data.name}`,
|
|
67
|
+
edge => !edge.data ? 'created' : `${edge.data.name}`
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
const routingOptions = {
|
|
71
|
+
|
|
72
|
+
acceptOneWayFn: (edge, reversed) => !(edge.data && edge.data.oneWay && reversed),
|
|
73
|
+
|
|
74
|
+
acceptEdgeWithoutStairsFn: edge => !(edge.data && edge.data.stairs),
|
|
75
|
+
|
|
76
|
+
weightFn: edge => {
|
|
77
|
+
if (edge.data && edge.data.elevator) {
|
|
78
|
+
return 30;
|
|
79
|
+
}
|
|
80
|
+
return Utils.getDurationFromLength(edge.length);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
55
84
|
export {
|
|
56
|
-
nodes, edges, network, itineraryStart, itineraryEnd
|
|
85
|
+
nodes, edges, network, itineraryStart, itineraryEnd, routingOptions, detailedStringArgs
|
|
57
86
|
};
|