@wemap/osm 0.1.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/assets/bureaux-wemap-montpellier-network.osm +111 -0
- package/index.js +11 -0
- package/package.json +35 -0
- package/src/model/OsmElement.js +9 -0
- package/src/model/OsmModel.js +24 -0
- package/src/model/OsmNode.js +22 -0
- package/src/model/OsmParser.js +100 -0
- package/src/model/OsmWay.js +18 -0
- package/src/network/OsmNetwork.js +64 -0
- package/src/network/OsmRouter.js +25 -0
- package/src/network/OsmRouter.spec.js +84 -0
- package/src/osrm/OsrmItinerary.js +5 -0
- package/src/osrm/OsrmUtils.js +154 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<?xml version='1.0' encoding='UTF-8'?>
|
|
2
|
+
<osm version='0.6' generator='JOSM'>
|
|
3
|
+
<node id='-440460' action='modify' visible='true' lat='43.60927603206' lon='3.88424673798'>
|
|
4
|
+
<tag k='name' v='p7' />
|
|
5
|
+
</node>
|
|
6
|
+
<node id='-440462' action='modify' visible='true' lat='43.60913609818' lon='3.88411091795'>
|
|
7
|
+
<tag k='name' v='p3' />
|
|
8
|
+
</node>
|
|
9
|
+
<node id='-440464' action='modify' visible='true' lat='43.60929353543' lon='3.88425183223'>
|
|
10
|
+
<tag k='name' v='p8' />
|
|
11
|
+
</node>
|
|
12
|
+
<node id='-440466' action='modify' visible='true' lat='43.60930219142' lon='3.88427017086'>
|
|
13
|
+
<tag k='name' v='p9' />
|
|
14
|
+
</node>
|
|
15
|
+
<node id='-440468' action='modify' visible='true' lat='43.60911942685' lon='3.88422017773'>
|
|
16
|
+
<tag k='name' v='p4' />
|
|
17
|
+
</node>
|
|
18
|
+
<node id='-440470' action='modify' visible='true' lat='43.60935581261' lon='3.88428577699' />
|
|
19
|
+
<node id='-440472' action='modify' visible='true' lat='43.60919651074' lon='3.88412853633'>
|
|
20
|
+
<tag k='name' v='p1' />
|
|
21
|
+
</node>
|
|
22
|
+
<node id='-440474' action='modify' visible='true' lat='43.60931199709' lon='3.88413083832' />
|
|
23
|
+
<node id='-440476' action='modify' visible='true' lat='43.60917494597' lon='3.88421731753'>
|
|
24
|
+
<tag k='name' v='p6' />
|
|
25
|
+
</node>
|
|
26
|
+
<node id='-440478' action='modify' visible='true' lat='43.60918883875' lon='3.88412626772'>
|
|
27
|
+
<tag k='name' v='p2' />
|
|
28
|
+
</node>
|
|
29
|
+
<node id='-440480' action='modify' visible='true' lat='43.60936726345' lon='3.88427546746' />
|
|
30
|
+
<node id='-440482' action='modify' visible='true' lat='43.60917216742' lon='3.8842355275'>
|
|
31
|
+
<tag k='name' v='p5' />
|
|
32
|
+
</node>
|
|
33
|
+
<node id='-440484' action='modify' visible='true' lat='43.60931231268' lon='3.8842731166'>
|
|
34
|
+
<tag k='name' v='p10' />
|
|
35
|
+
</node>
|
|
36
|
+
<node id='-440486' action='modify' visible='true' lat='43.60932336323' lon='3.884200898'>
|
|
37
|
+
<tag k='name' v='p11' />
|
|
38
|
+
</node>
|
|
39
|
+
<node id='-440488' action='modify' visible='true' lat='43.6093629216' lon='3.884212726'>
|
|
40
|
+
<tag k='name' v='p12' />
|
|
41
|
+
</node>
|
|
42
|
+
<node id='-440490' action='modify' visible='true' lat='43.60935965678' lon='3.8842335535'>
|
|
43
|
+
<tag k='name' v='p13' />
|
|
44
|
+
</node>
|
|
45
|
+
<node id='-440492' action='modify' visible='true' lat='43.60932022256' lon='3.88422176261'>
|
|
46
|
+
<tag k='name' v='p14' />
|
|
47
|
+
</node>
|
|
48
|
+
<node id='-440494' action='modify' visible='true' lat='43.60931232238' lon='3.88427309743'>
|
|
49
|
+
<tag k='name' v='p15' />
|
|
50
|
+
</node>
|
|
51
|
+
<node id='-440496' action='modify' visible='true' lat='43.60936673756' lon='3.88428907078' />
|
|
52
|
+
<node id='-440498' action='modify' visible='true' lat='43.60926807571' lon='3.884260391'>
|
|
53
|
+
<tag k='name' v='p16' />
|
|
54
|
+
</node>
|
|
55
|
+
<way id='-440499' action='modify' visible='true'>
|
|
56
|
+
<nd ref='-440476' />
|
|
57
|
+
<nd ref='-440460' />
|
|
58
|
+
<nd ref='-440464' />
|
|
59
|
+
<nd ref='-440474' />
|
|
60
|
+
<tag k='highway' v='footway' />
|
|
61
|
+
<tag k='level' v='2' />
|
|
62
|
+
</way>
|
|
63
|
+
<way id='-440500' action='modify' visible='true'>
|
|
64
|
+
<nd ref='-440464' />
|
|
65
|
+
<nd ref='-440466' />
|
|
66
|
+
<nd ref='-440484' />
|
|
67
|
+
<nd ref='-440470' />
|
|
68
|
+
<nd ref='-440480' />
|
|
69
|
+
<tag k='highway' v='footway' />
|
|
70
|
+
<tag k='level' v='2' />
|
|
71
|
+
</way>
|
|
72
|
+
<way id='-440501' action='modify' visible='true'>
|
|
73
|
+
<nd ref='-440472' />
|
|
74
|
+
<nd ref='-440478' />
|
|
75
|
+
<nd ref='-440462' />
|
|
76
|
+
<nd ref='-440468' />
|
|
77
|
+
<nd ref='-440482' />
|
|
78
|
+
<nd ref='-440476' />
|
|
79
|
+
<nd ref='-440478' />
|
|
80
|
+
<tag k='highway' v='footway' />
|
|
81
|
+
<tag k='level' v='2' />
|
|
82
|
+
</way>
|
|
83
|
+
<way id='-440502' action='modify' visible='true'>
|
|
84
|
+
<nd ref='-440486' />
|
|
85
|
+
<nd ref='-440488' />
|
|
86
|
+
<nd ref='-440490' />
|
|
87
|
+
<nd ref='-440492' />
|
|
88
|
+
<tag k='highway' v='footway' />
|
|
89
|
+
<tag k='level' v='1;2' />
|
|
90
|
+
<tag k='stairs' v='yes' />
|
|
91
|
+
</way>
|
|
92
|
+
<way id='-440503' action='modify' visible='true'>
|
|
93
|
+
<nd ref='-440484' />
|
|
94
|
+
<nd ref='-440486' />
|
|
95
|
+
<tag k='highway' v='footway' />
|
|
96
|
+
<tag k='level' v='2' />
|
|
97
|
+
</way>
|
|
98
|
+
<way id='-440504' action='modify' visible='true'>
|
|
99
|
+
<nd ref='-440492' />
|
|
100
|
+
<nd ref='-440494' />
|
|
101
|
+
<nd ref='-440496' />
|
|
102
|
+
<tag k='highway' v='footway' />
|
|
103
|
+
<tag k='level' v='1' />
|
|
104
|
+
</way>
|
|
105
|
+
<way id='-440505' action='modify' visible='true'>
|
|
106
|
+
<nd ref='-440494' />
|
|
107
|
+
<nd ref='-440498' />
|
|
108
|
+
<tag k='highway' v='footway' />
|
|
109
|
+
<tag k='level' v='1' />
|
|
110
|
+
</way>
|
|
111
|
+
</osm>
|
package/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import OsmNetwork from './src/network/OsmNetwork';
|
|
2
|
+
import OsmParser from './src/model/OsmParser';
|
|
3
|
+
import OsmRouter from './src/network/OsmRouter';
|
|
4
|
+
import OsrmUtils from './src/osrm/OsrmUtils';
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
OsmNetwork,
|
|
8
|
+
OsmParser,
|
|
9
|
+
OsmRouter,
|
|
10
|
+
OsrmUtils
|
|
11
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"author": "Wemap",
|
|
3
|
+
"contributors": [
|
|
4
|
+
"Thibaud Michel <thibaud@getwemap.com>"
|
|
5
|
+
],
|
|
6
|
+
"description": "Wemap OSM utils package",
|
|
7
|
+
"main": "index.js",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/wemap/wemap-utils-js.git",
|
|
11
|
+
"directory": "packages/osm"
|
|
12
|
+
},
|
|
13
|
+
"name": "@wemap/osm",
|
|
14
|
+
"version": "0.1.0",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/wemap/wemap-utils-js/issues"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://github.com/wemap/wemap-utils-js#readme",
|
|
19
|
+
"scripts": {
|
|
20
|
+
"test": "mocha -r esm \\\"src/**/*.spec.js\\\""
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"utils",
|
|
24
|
+
"osm",
|
|
25
|
+
"wemap"
|
|
26
|
+
],
|
|
27
|
+
"license": "ISC",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@wemap/geo": "^0.2.0",
|
|
30
|
+
"@wemap/logger": "^0.1.5",
|
|
31
|
+
"lodash.isnumber": "^3.0.3",
|
|
32
|
+
"sax": "^1.2.4"
|
|
33
|
+
},
|
|
34
|
+
"gitHead": "6673bc4d0f4b6ff404534e0642122c340fe58483"
|
|
35
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import OsmParser from './OsmParser';
|
|
2
|
+
|
|
3
|
+
class OsmModel {
|
|
4
|
+
|
|
5
|
+
constructor() {
|
|
6
|
+
this.nodes = [];
|
|
7
|
+
this.ways = [];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
getNodeById(id) {
|
|
11
|
+
return this.nodes.find(node => node.id === id);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
getNodeByName(name) {
|
|
15
|
+
return this.nodes.find(node => node.tags.name === name);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static fromXmlString(xmlString) {
|
|
19
|
+
return OsmParser.parseOsmXmlString(xmlString);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default OsmModel;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { WGS84 } from '@wemap/geo';
|
|
2
|
+
|
|
3
|
+
import OsmElement from './OsmElement';
|
|
4
|
+
|
|
5
|
+
class OsmNode extends OsmElement {
|
|
6
|
+
|
|
7
|
+
constructor(id, coords) {
|
|
8
|
+
super(id);
|
|
9
|
+
this.ways = [];
|
|
10
|
+
this.coords = coords;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
static fromSax(attr) {
|
|
14
|
+
const node = new OsmNode(
|
|
15
|
+
Number(attr.id),
|
|
16
|
+
new WGS84(Number(attr.lat), Number(attr.lon)));
|
|
17
|
+
node.action = attr.action;
|
|
18
|
+
return node;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default OsmNode;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import sax from 'sax';
|
|
2
|
+
|
|
3
|
+
import { Level } from '@wemap/geo';
|
|
4
|
+
import Logger from '@wemap/logger';
|
|
5
|
+
|
|
6
|
+
import OsmModel from './OsmModel';
|
|
7
|
+
import OsmNode from './OsmNode';
|
|
8
|
+
import OsmWay from './OsmWay';
|
|
9
|
+
|
|
10
|
+
class OsmParser {
|
|
11
|
+
|
|
12
|
+
static parseOsmXmlString(osmXmlString) {
|
|
13
|
+
|
|
14
|
+
const model = new OsmModel();
|
|
15
|
+
const parser = sax.parser(true);
|
|
16
|
+
|
|
17
|
+
let buffer;
|
|
18
|
+
|
|
19
|
+
parser.onopentag = (node) => {
|
|
20
|
+
|
|
21
|
+
switch (node.name) {
|
|
22
|
+
case 'node': {
|
|
23
|
+
const osmNode = OsmNode.fromSax(node.attributes);
|
|
24
|
+
buffer = osmNode;
|
|
25
|
+
model.nodes.push(osmNode);
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
case 'way': {
|
|
29
|
+
const osmWay = OsmWay.fromSax(node.attributes);
|
|
30
|
+
buffer = osmWay;
|
|
31
|
+
model.ways.push(osmWay);
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
case 'tag': {
|
|
35
|
+
if (!buffer) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const {
|
|
39
|
+
k, v
|
|
40
|
+
} = node.attributes;
|
|
41
|
+
buffer.tags[k] = v;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
case 'nd': {
|
|
45
|
+
if (!buffer || !(buffer instanceof OsmWay)) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const nodeId = Number(node.attributes.ref);
|
|
49
|
+
const refNode = model.getNodeById(nodeId);
|
|
50
|
+
if (!refNode) {
|
|
51
|
+
Logger.warn('Node: ' + nodeId + ' in way ' + buffer.id + ' not found');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
buffer.nodes.push(refNode);
|
|
56
|
+
refNode.ways.push(buffer);
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
parser.write(osmXmlString);
|
|
63
|
+
|
|
64
|
+
for (let i = 0; i < model.ways.length; i++) {
|
|
65
|
+
const way = model.ways[i];
|
|
66
|
+
if (way.tags.level) {
|
|
67
|
+
way.level = Level.fromString(way.tags.level);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for (let i = 0; i < model.nodes.length; i++) {
|
|
72
|
+
const node = model.nodes[i];
|
|
73
|
+
if (node.ways.length === 0) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Get node level from ways connected to this node.
|
|
78
|
+
// Node level is intersection of all levels of connected ways.
|
|
79
|
+
let tmpLevel = node.coords.level;
|
|
80
|
+
node.ways.forEach(way => {
|
|
81
|
+
if (way.level) {
|
|
82
|
+
if (!tmpLevel) {
|
|
83
|
+
tmpLevel = way.level.clone();
|
|
84
|
+
} else {
|
|
85
|
+
tmpLevel = tmpLevel.intersect(way.level);
|
|
86
|
+
if (!tmpLevel) {
|
|
87
|
+
Logger.error('Error: Something bad happend during parsing: We cannot retrieve node level from adjacent ways: ' + node.coords);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
node.coords.level = tmpLevel;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return model;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export default OsmParser;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import OsmElement from './OsmElement';
|
|
2
|
+
|
|
3
|
+
class OsmWay extends OsmElement {
|
|
4
|
+
|
|
5
|
+
constructor(id) {
|
|
6
|
+
super(id);
|
|
7
|
+
this.nodes = [];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
static fromSax(attr) {
|
|
11
|
+
const way = new OsmWay(Number(attr.id));
|
|
12
|
+
way.action = attr.action;
|
|
13
|
+
return way;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default OsmWay;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Edge, Node, Network
|
|
3
|
+
} from '@wemap/geo';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
const HIGHWAYS_PEDESTRIANS = ['footway', 'steps'];
|
|
7
|
+
const DEFAULT_WAY_SELECTOR = way => HIGHWAYS_PEDESTRIANS.includes(way.tags.highway);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A typical network with nodes (Node) and edges (Edge)
|
|
11
|
+
*/
|
|
12
|
+
class OsmNetwork extends Network {
|
|
13
|
+
|
|
14
|
+
static fromOsmModel(osmModel, _waySelectionFilter) {
|
|
15
|
+
|
|
16
|
+
const waySelectionFilter = _waySelectionFilter || DEFAULT_WAY_SELECTOR;
|
|
17
|
+
|
|
18
|
+
const networkModel = new OsmNetwork();
|
|
19
|
+
|
|
20
|
+
const getOrCreateNode = osmNode => {
|
|
21
|
+
let node = networkModel.nodes.find(graphNode => osmNode.id === graphNode.data.id);
|
|
22
|
+
if (!node) {
|
|
23
|
+
node = new Node(osmNode.coords, osmNode);
|
|
24
|
+
networkModel.nodes.push(node);
|
|
25
|
+
}
|
|
26
|
+
return node;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
osmModel.ways.forEach(way => {
|
|
30
|
+
if (!waySelectionFilter(way)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let firstNode = getOrCreateNode(way.nodes[0]);
|
|
35
|
+
for (let i = 1; i < way.nodes.length; i++) {
|
|
36
|
+
const secondNode = getOrCreateNode(way.nodes[i]);
|
|
37
|
+
|
|
38
|
+
const edge = new Edge(firstNode, secondNode);
|
|
39
|
+
edge.data = way;
|
|
40
|
+
edge.level = way.level;
|
|
41
|
+
networkModel.edges.push(edge);
|
|
42
|
+
firstNode.edges.push(edge);
|
|
43
|
+
secondNode.edges.push(edge);
|
|
44
|
+
|
|
45
|
+
firstNode = secondNode;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return networkModel;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
getNodeById(id) {
|
|
54
|
+
return this.nodes.find(node => node.data.id === id);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
getEdgeById(id) {
|
|
59
|
+
return this.edges.find(edge => edge.data.id === id);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export default OsmNetwork;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { GraphRouter } from '@wemap/geo';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_OPTIONS = { useStairs: true };
|
|
4
|
+
|
|
5
|
+
class OsmRouter extends GraphRouter {
|
|
6
|
+
|
|
7
|
+
getShortestPath(start, end, _options) {
|
|
8
|
+
|
|
9
|
+
const options = Object.assign({}, DEFAULT_OPTIONS, _options);
|
|
10
|
+
|
|
11
|
+
const edgeSelectionFilter = edge => {
|
|
12
|
+
|
|
13
|
+
return options.useStairs
|
|
14
|
+
|| (
|
|
15
|
+
edge.data.tags.stairs !== 'yes'
|
|
16
|
+
&& edge.data.tags.highway !== 'steps'
|
|
17
|
+
);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
return super.getShortestPath(start, end, edgeSelectionFilter);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default OsmRouter;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import chai from 'chai';
|
|
2
|
+
import chaiAlmost from 'chai-almost';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
WGS84, Level
|
|
8
|
+
} from '@wemap/geo';
|
|
9
|
+
|
|
10
|
+
import OsmParser from '../model/OsmParser';
|
|
11
|
+
import OsmRouter from './OsmRouter';
|
|
12
|
+
import OsmNetwork from './OsmNetwork';
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
const expect = chai.expect;
|
|
16
|
+
chai.use(chaiAlmost());
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
describe('OsmRouter', () => {
|
|
20
|
+
|
|
21
|
+
const filePath = path.resolve(__dirname, '../../assets/bureaux-wemap-montpellier-network.osm');
|
|
22
|
+
const osmXmlString = fs.readFileSync(filePath, 'utf8');
|
|
23
|
+
|
|
24
|
+
const osmModel = OsmParser.parseOsmXmlString(osmXmlString);
|
|
25
|
+
const networkModel = OsmNetwork.fromOsmModel(osmModel);
|
|
26
|
+
const router = new OsmRouter(networkModel);
|
|
27
|
+
|
|
28
|
+
it('Multi-level itinerary', () => {
|
|
29
|
+
|
|
30
|
+
const start = new WGS84(43.6092754, 3.8842306);
|
|
31
|
+
start.level = Level.fromString(2);
|
|
32
|
+
|
|
33
|
+
const end = new WGS84(43.6092602, 3.8842669);
|
|
34
|
+
end.level = Level.fromString(1);
|
|
35
|
+
|
|
36
|
+
const itinerary = router.getShortestPath(start, end);
|
|
37
|
+
|
|
38
|
+
expect(itinerary.nodes.length).equal(11);
|
|
39
|
+
|
|
40
|
+
expect(itinerary.nodes[1].data.tags.name).equal('p7');
|
|
41
|
+
expect(itinerary.nodes[2].data.tags.name).equal('p8');
|
|
42
|
+
expect(itinerary.nodes[3].data.tags.name).equal('p9');
|
|
43
|
+
expect(itinerary.nodes[4].data.tags.name).equal('p10');
|
|
44
|
+
expect(itinerary.nodes[5].data.tags.name).equal('p11');
|
|
45
|
+
expect(itinerary.nodes[6].data.tags.name).equal('p12');
|
|
46
|
+
expect(itinerary.nodes[7].data.tags.name).equal('p13');
|
|
47
|
+
expect(itinerary.nodes[8].data.tags.name).equal('p14');
|
|
48
|
+
expect(itinerary.nodes[9].data.tags.name).equal('p15');
|
|
49
|
+
expect(itinerary.nodes[10].data.tags.name).equal('p16');
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Verify if itinerary is ordered
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
for (let i = 0; i < itinerary.nodes.length; i++) {
|
|
57
|
+
const node = itinerary.nodes[i];
|
|
58
|
+
|
|
59
|
+
expect(node.edges.length).equal(i === 0 || i === itinerary.nodes.length - 1 ? 1 : 2);
|
|
60
|
+
|
|
61
|
+
if (i !== itinerary.nodes.length - 1) {
|
|
62
|
+
expect(itinerary.edges[i].node1).equal(node);
|
|
63
|
+
expect(itinerary.edges[i].node2).equal(itinerary.nodes[i + 1]);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
it('No route itinerary', () => {
|
|
72
|
+
|
|
73
|
+
const start = new WGS84(43.6092754, 3.8842306);
|
|
74
|
+
start.level = Level.fromString(2);
|
|
75
|
+
|
|
76
|
+
const end = new WGS84(43.6092602, 3.8842669);
|
|
77
|
+
end.level = Level.fromString(1);
|
|
78
|
+
|
|
79
|
+
const itinerary = router.getShortestPath(start, end, { useStairs: false });
|
|
80
|
+
|
|
81
|
+
expect(itinerary).is.undefined;
|
|
82
|
+
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/* eslint-disable max-statements */
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Network, WGS84
|
|
5
|
+
} from '@wemap/geo';
|
|
6
|
+
import { Utils as MathUtils } from '@wemap/maths';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class OsrmUtils {
|
|
10
|
+
|
|
11
|
+
static itineraryToOsrmJson(itinerary, altitudeFactor = 1) {
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
edges, nodes, start, end, length
|
|
15
|
+
} = itinerary;
|
|
16
|
+
|
|
17
|
+
const coordinates = [];
|
|
18
|
+
const steps = [];
|
|
19
|
+
|
|
20
|
+
nodes.forEach(node => {
|
|
21
|
+
const nodeCoordsModified = node.coords.clone();
|
|
22
|
+
if (altitudeFactor !== 1 && nodeCoordsModified.level) {
|
|
23
|
+
nodeCoordsModified.level.multiplyBy(altitudeFactor);
|
|
24
|
+
}
|
|
25
|
+
coordinates.push(nodeCoordsModified.toJson(WGS84.FORMAT_LNG_LAT_STRLEVEL));
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
let previousBearing;
|
|
29
|
+
|
|
30
|
+
for (let i = -1; i < nodes.length; i++) {
|
|
31
|
+
|
|
32
|
+
let node;
|
|
33
|
+
let nextNode;
|
|
34
|
+
|
|
35
|
+
let nodeCoords;
|
|
36
|
+
let nodeTags;
|
|
37
|
+
let nextNodeCoords;
|
|
38
|
+
let nextEdgeTags;
|
|
39
|
+
|
|
40
|
+
if (i === -1) {
|
|
41
|
+
nodeCoords = start;
|
|
42
|
+
previousBearing = 0;
|
|
43
|
+
} else {
|
|
44
|
+
node = nodes[i];
|
|
45
|
+
nodeCoords = node.coords;
|
|
46
|
+
nodeTags = node.tags;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (i + 1 === nodes.length) {
|
|
50
|
+
nextNodeCoords = end;
|
|
51
|
+
} else {
|
|
52
|
+
nextNode = nodes[i + 1];
|
|
53
|
+
nextNodeCoords = nextNode.coords;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (node && nextNode) {
|
|
57
|
+
const edge = Network.getEdgeByNodes(edges, nextNode, node);
|
|
58
|
+
nextEdgeTags = edge.data.tags;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const bearing = MathUtils.positiveMod(nodeCoords.bearingTo(nextNodeCoords), 2 * Math.PI);
|
|
62
|
+
const distance = nodeCoords.distanceTo(nextNodeCoords);
|
|
63
|
+
const angle = MathUtils.positiveMod(MathUtils.diffAngle(previousBearing, bearing + Math.PI), 2 * Math.PI) * 180 / Math.PI;
|
|
64
|
+
const modifier = i === -1 ? 'start' : OsrmUtils.getModifierFromAngle(angle);
|
|
65
|
+
|
|
66
|
+
const nodeCoordsModified = nodeCoords.clone();
|
|
67
|
+
if (altitudeFactor !== 1 && nodeCoordsModified.level) {
|
|
68
|
+
nodeCoordsModified.level.multiplyBy(altitudeFactor);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
steps.push({
|
|
72
|
+
maneuver: {
|
|
73
|
+
bearing_before: previousBearing * 180 / Math.PI,
|
|
74
|
+
bearing_after: bearing * 180 / Math.PI,
|
|
75
|
+
location: nodeCoordsModified.toJson(WGS84.FORMAT_LNG_LAT_STRLEVEL),
|
|
76
|
+
modifier
|
|
77
|
+
},
|
|
78
|
+
distance,
|
|
79
|
+
nodeTags,
|
|
80
|
+
nextEdgeTags
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
previousBearing = bearing;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const endNodeCoordsModified = end.clone();
|
|
87
|
+
if (altitudeFactor !== 1 && endNodeCoordsModified.level) {
|
|
88
|
+
endNodeCoordsModified.level.multiplyBy(altitudeFactor);
|
|
89
|
+
}
|
|
90
|
+
steps.push({maneuver: {
|
|
91
|
+
bearing_before: previousBearing * 180 / Math.PI,
|
|
92
|
+
bearing_after: 0,
|
|
93
|
+
location: endNodeCoordsModified.toJson(WGS84.FORMAT_LNG_LAT_STRLEVEL),
|
|
94
|
+
modifier: 'arrive'
|
|
95
|
+
}});
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
'code': 'Ok',
|
|
100
|
+
'routes': [
|
|
101
|
+
{
|
|
102
|
+
'geometry': {
|
|
103
|
+
'type': 'LineString',
|
|
104
|
+
'coordinates': coordinates
|
|
105
|
+
},
|
|
106
|
+
'legs': [
|
|
107
|
+
{
|
|
108
|
+
'duration': itinerary.getDuration(),
|
|
109
|
+
'distance': length,
|
|
110
|
+
'steps': steps
|
|
111
|
+
}
|
|
112
|
+
],
|
|
113
|
+
'distance': length,
|
|
114
|
+
'duration': itinerary.getDuration(),
|
|
115
|
+
'weight_name': 'routability',
|
|
116
|
+
'weight': 0
|
|
117
|
+
}
|
|
118
|
+
],
|
|
119
|
+
'waypoints': []
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
static get noRouteFoundJson() {
|
|
124
|
+
return { 'code': 'NoRoute' };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
static getModifierFromAngle(angle) {
|
|
129
|
+
if (angle > 0 && angle < 60) {
|
|
130
|
+
return 'sharp right';
|
|
131
|
+
}
|
|
132
|
+
if (angle >= 60 && angle < 140) {
|
|
133
|
+
return 'right';
|
|
134
|
+
}
|
|
135
|
+
if (angle >= 140 && angle < 160) {
|
|
136
|
+
return 'slight right';
|
|
137
|
+
}
|
|
138
|
+
if (angle >= 160 && angle <= 200) {
|
|
139
|
+
return 'straight';
|
|
140
|
+
}
|
|
141
|
+
if (angle > 200 && angle <= 220) {
|
|
142
|
+
return 'slight left';
|
|
143
|
+
}
|
|
144
|
+
if (angle > 220 && angle <= 300) {
|
|
145
|
+
return 'left';
|
|
146
|
+
}
|
|
147
|
+
if (angle > 300 && angle < 360) {
|
|
148
|
+
return 'sharp left';
|
|
149
|
+
}
|
|
150
|
+
return 'u turn';
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export default OsrmUtils;
|