@wemap/routers 11.3.3 → 11.5.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/dist/index.js +116 -66
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +116 -66
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/remote/osrm/OsrmRemoteRouter.ts +12 -5
- package/src/wemap-multi/WemapMultiRouter.ts +5 -5
- package/src/wemap-osm/OsmGraph.spec.ts +5 -5
- package/src/wemap-osm/OsmGraph.ts +129 -62
- package/src/wemap-osm/OsmRouter.elevators.spec.ts +105 -0
- package/src/wemap-osm/OsmRouter.spec.ts +14 -14
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"directory": "packages/routers"
|
|
13
13
|
},
|
|
14
14
|
"name": "@wemap/routers",
|
|
15
|
-
"version": "11.
|
|
15
|
+
"version": "11.5.0",
|
|
16
16
|
"bugs": {
|
|
17
17
|
"url": "https://github.com/wemap/wemap-modules-js/issues"
|
|
18
18
|
},
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
"@turf/convex": "^6.5.0",
|
|
35
35
|
"@turf/helpers": "^6.5.0",
|
|
36
36
|
"@types/mapbox__polyline": "^1.0.2",
|
|
37
|
-
"@wemap/geo": "^11.
|
|
37
|
+
"@wemap/geo": "^11.5.0",
|
|
38
38
|
"@wemap/logger": "^11.0.1",
|
|
39
39
|
"@wemap/maths": "^11.0.1",
|
|
40
|
-
"@wemap/osm": "^11.
|
|
40
|
+
"@wemap/osm": "^11.5.0",
|
|
41
41
|
"@wemap/salesman.js": "^2.1.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
@@ -52,5 +52,5 @@
|
|
|
52
52
|
},
|
|
53
53
|
"./helpers/*": "./helpers/*"
|
|
54
54
|
},
|
|
55
|
-
"gitHead": "
|
|
55
|
+
"gitHead": "e3b51be235497315bfc799f70e01f08e0cadbcb4"
|
|
56
56
|
}
|
|
@@ -11,6 +11,7 @@ import RemoteRouter from '../RemoteRouter.js';
|
|
|
11
11
|
import RoutingModeCorrespondanceNotFound from '../RoutingModeCorrespondanceNotFound.js';
|
|
12
12
|
import RemoteRouterServerUnreachable from '../RemoteRouterServerUnreachable.js';
|
|
13
13
|
import { RoutingMode } from '../../model/RoutingMode.js';
|
|
14
|
+
import { RemoteRouterOptions } from '../RemoteRouterOptions.js';
|
|
14
15
|
|
|
15
16
|
type OsrmCoordinates = Position;
|
|
16
17
|
type OsrmModifier = 'sharp right' | 'sharp left' | 'slight right'
|
|
@@ -46,7 +47,7 @@ type OsrmJson = {
|
|
|
46
47
|
waypoints?: []
|
|
47
48
|
};
|
|
48
49
|
|
|
49
|
-
type OsrmMode = 'driving' | 'walking' | 'bike' | 'bus' | 'walking';
|
|
50
|
+
type OsrmMode = 'driving' | 'walking' | 'bike' | 'bus' | 'walking' | 'pmr';
|
|
50
51
|
|
|
51
52
|
/**
|
|
52
53
|
* Input mode correspondance
|
|
@@ -75,9 +76,10 @@ class OsrmRemoteRouter extends RemoteRouter {
|
|
|
75
76
|
async getItineraries(
|
|
76
77
|
endpointUrl: string,
|
|
77
78
|
mode: RoutingMode,
|
|
78
|
-
waypoints: Coordinates[]
|
|
79
|
+
waypoints: Coordinates[],
|
|
80
|
+
options: RemoteRouterOptions = {}
|
|
79
81
|
) {
|
|
80
|
-
const url = this.getURL(endpointUrl, mode, waypoints);
|
|
82
|
+
const url = this.getURL(endpointUrl, mode, waypoints, options);
|
|
81
83
|
|
|
82
84
|
const res = await (fetch(url).catch(() => {
|
|
83
85
|
throw new RemoteRouterServerUnreachable(this.rname, url);
|
|
@@ -101,14 +103,19 @@ class OsrmRemoteRouter extends RemoteRouter {
|
|
|
101
103
|
getURL(
|
|
102
104
|
endpointUrl: string,
|
|
103
105
|
mode: RoutingMode,
|
|
104
|
-
waypoints: Coordinates[]
|
|
106
|
+
waypoints: Coordinates[],
|
|
107
|
+
options: RemoteRouterOptions = {}
|
|
105
108
|
) {
|
|
106
109
|
|
|
107
|
-
|
|
110
|
+
let osrmMode = inputModeCorrespondance.get(mode);
|
|
108
111
|
if (!osrmMode) {
|
|
109
112
|
throw new RoutingModeCorrespondanceNotFound(this.rname, mode);
|
|
110
113
|
}
|
|
111
114
|
|
|
115
|
+
if ('useStairs' in options && !options.useStairs) {
|
|
116
|
+
osrmMode = 'pmr';
|
|
117
|
+
}
|
|
118
|
+
|
|
112
119
|
let url = endpointUrl + '/route/v1/' + osrmMode + '/';
|
|
113
120
|
url += waypoints.map(waypoint => [waypoint.longitude + ',' + waypoint.latitude]).join(';');
|
|
114
121
|
url += '?geometries=geojson&overview=full&steps=true';
|
|
@@ -150,7 +150,7 @@ class WemapMultiRouter {
|
|
|
150
150
|
*/
|
|
151
151
|
if (!ioMapsToTest.length) {
|
|
152
152
|
try {
|
|
153
|
-
return await RemoteRouterManager.getItinerariesWithFallback(remoteRouters, mode, waypoints);
|
|
153
|
+
return await RemoteRouterManager.getItinerariesWithFallback(remoteRouters, mode, waypoints, options || undefined);
|
|
154
154
|
} catch (e) {
|
|
155
155
|
if (!isRoutingError(e)) {
|
|
156
156
|
throw e;
|
|
@@ -215,7 +215,7 @@ class WemapMultiRouter {
|
|
|
215
215
|
*/
|
|
216
216
|
if (!mapWithStart && !mapWithEnd) {
|
|
217
217
|
try {
|
|
218
|
-
return await RemoteRouterManager.getItinerariesWithFallback(remoteRouters, mode, waypoints);
|
|
218
|
+
return await RemoteRouterManager.getItinerariesWithFallback(remoteRouters, mode, waypoints, options || undefined);
|
|
219
219
|
} catch (e) {
|
|
220
220
|
if (!isRoutingError(e)) {
|
|
221
221
|
throw e;
|
|
@@ -249,7 +249,7 @@ class WemapMultiRouter {
|
|
|
249
249
|
try {
|
|
250
250
|
ioMapItinerary = mapWithStart.getBestItineraryFromStartToEntryPoints(start, end, wemapOsmRouterOptions);
|
|
251
251
|
remoteRouterResponse = await RemoteRouterManager.getItinerariesWithFallback(
|
|
252
|
-
remoteRouters, mode, [ioMapItinerary.to, end]
|
|
252
|
+
remoteRouters, mode, [ioMapItinerary.to, end], options || undefined
|
|
253
253
|
);
|
|
254
254
|
if (!remoteRouterResponse.itineraries.length) {
|
|
255
255
|
throw new NoRouteFoundError(ioMapItinerary.to, end, remoteRouterResponse.error);
|
|
@@ -303,7 +303,7 @@ class WemapMultiRouter {
|
|
|
303
303
|
try {
|
|
304
304
|
ioMapItinerary = mapWithEnd.getBestItineraryFromEntryPointsToEnd(start, end, wemapOsmRouterOptions);
|
|
305
305
|
remoteRouterResponse = await RemoteRouterManager.getItinerariesWithFallback(
|
|
306
|
-
remoteRouters, mode, [start, ioMapItinerary.from]
|
|
306
|
+
remoteRouters, mode, [start, ioMapItinerary.from], options || undefined
|
|
307
307
|
);
|
|
308
308
|
if (!remoteRouterResponse.itineraries.length) {
|
|
309
309
|
throw new NoRouteFoundError(start, ioMapItinerary.from, remoteRouterResponse.error);
|
|
@@ -361,7 +361,7 @@ class WemapMultiRouter {
|
|
|
361
361
|
ioMapItinerary1 = mapWithStart.getBestItineraryFromStartToEntryPoints(start, end, wemapOsmRouterOptions);
|
|
362
362
|
ioMapItinerary2 = mapWithEnd.getBestItineraryFromEntryPointsToEnd(start, end, wemapOsmRouterOptions);
|
|
363
363
|
remoteRouterResponse = await RemoteRouterManager.getItinerariesWithFallback(
|
|
364
|
-
remoteRouters, mode, [ioMapItinerary1.to, ioMapItinerary2.from]
|
|
364
|
+
remoteRouters, mode, [ioMapItinerary1.to, ioMapItinerary2.from], options || undefined
|
|
365
365
|
);
|
|
366
366
|
if (!remoteRouterResponse.itineraries.length) {
|
|
367
367
|
throw new NoRouteFoundError(ioMapItinerary1.to, ioMapItinerary2.from, remoteRouterResponse.error);
|
|
@@ -72,12 +72,12 @@ describe('OsmGraph - simple', () => {
|
|
|
72
72
|
expect(() => OsmGraph.fromOsmModel(osmModel2)).throw(Error);
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
-
it('
|
|
76
|
-
let
|
|
77
|
-
expect(
|
|
75
|
+
it('getVertexByName', () => {
|
|
76
|
+
let vertex = graph.getVertexByName('p1');
|
|
77
|
+
expect(vertex).not.undefined;
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
expect(
|
|
79
|
+
vertex = graph.getVertexByName('p5');
|
|
80
|
+
expect(vertex).is.undefined;
|
|
81
81
|
});
|
|
82
82
|
});
|
|
83
83
|
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { Coordinates, GeoGraph, GeoGraphEdge, GeoGraphVertex, Level } from '@wemap/geo';
|
|
1
|
+
import { Coordinates, GeoGraph, GeoGraphEdge, GeoGraphVertex, Level, Level_t } from '@wemap/geo';
|
|
2
2
|
import { OsmModel, OsmNode, OsmWay } from '@wemap/osm';
|
|
3
3
|
|
|
4
4
|
const HIGHWAYS_PEDESTRIANS = ['footway', 'steps', 'pedestrian', 'living_street', 'path', 'track', 'sidewalk', 'elevator'];
|
|
5
5
|
|
|
6
6
|
const DEFAULT_WAY_SELECTOR = (way: OsmWay) => {
|
|
7
7
|
const isElevatorArea = way.tags.highway === 'elevator' && way.isArea
|
|
8
|
-
return HIGHWAYS_PEDESTRIANS.includes(way.tags.highway)
|
|
9
|
-
&& !isElevatorArea
|
|
8
|
+
return HIGHWAYS_PEDESTRIANS.includes(way.tags.highway)
|
|
9
|
+
&& !isElevatorArea
|
|
10
10
|
&& !['no', 'private'].includes(way.tags.access)
|
|
11
11
|
|| way.tags.footway === 'sidewalk'
|
|
12
12
|
|| way.tags.public_transport === 'platform'
|
|
13
13
|
|| way.tags.railway === 'platform';
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
export type OsmVertexData = OsmNode;
|
|
16
|
+
export type OsmVertexData = OsmNode | OsmWay;
|
|
17
17
|
export type OsmEdgeData = OsmWay | OsmNode;
|
|
18
18
|
|
|
19
19
|
export type OsmVertex = GeoGraphVertex<OsmVertexData, OsmEdgeData> & { data: OsmVertexData };
|
|
@@ -41,24 +41,62 @@ export default class OsmGraph extends GeoGraph<OsmVertexData, OsmEdgeData> {
|
|
|
41
41
|
|
|
42
42
|
static fromOsmModel(osmModel: OsmModel, waySelectionFilter = DEFAULT_WAY_SELECTOR) {
|
|
43
43
|
|
|
44
|
-
const
|
|
44
|
+
const vertices: OsmVertex[] = [];
|
|
45
45
|
const edges: OsmEdge[] = [];
|
|
46
46
|
|
|
47
|
-
const
|
|
48
|
-
const elevatorNodes: OsmVertex[] = [];
|
|
47
|
+
const elevatorVertices: OsmVertex[] = [];
|
|
49
48
|
|
|
50
|
-
const
|
|
51
|
-
let node = nodesCreated[osmNode.id];
|
|
52
|
-
if (!node) {
|
|
53
|
-
node = new GeoGraphVertex(osmNode.coords, { data: osmNode, name: osmNode.tags.name }) as OsmVertex;
|
|
54
|
-
nodesCreated[osmNode.id] = node;
|
|
55
|
-
nodes.push(node);
|
|
49
|
+
const getOrCreateVertex = (osmNode: OsmNode, forceLevel: Level_t = null) => {
|
|
56
50
|
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
// Due to `repeat_on` tags in OSM model, it's not possible to compare osmIds to find corresponding vertex
|
|
52
|
+
let vertex: OsmVertex | null = vertices.find(vertex =>
|
|
53
|
+
vertex.data.id === osmNode.id
|
|
54
|
+
&& (
|
|
55
|
+
(forceLevel !== null)
|
|
56
|
+
? Level.intersect(vertex.coords.level, forceLevel)
|
|
57
|
+
: true
|
|
58
|
+
)
|
|
59
|
+
) || null;
|
|
60
|
+
|
|
61
|
+
if (vertex) { return vertex; }
|
|
62
|
+
|
|
63
|
+
const otherVertex = vertices.find(v => v.data.id === osmNode.id)
|
|
64
|
+
if (otherVertex) {
|
|
65
|
+
if (otherVertex && otherVertex.coords.level !== null) {
|
|
66
|
+
throw new Error(`An error occured because repeat_on as not been set on node ${osmNode.coords} for level ${forceLevel}`);
|
|
67
|
+
}
|
|
68
|
+
otherVertex.coords.level = forceLevel
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let levelsToGenerate: Level_t[] = [null];
|
|
72
|
+
if ('level' in osmNode.tags) {
|
|
73
|
+
levelsToGenerate = [osmNode.coords.level];
|
|
74
|
+
if ('repeat_on' in osmNode.tags) {
|
|
75
|
+
levelsToGenerate.push(...osmNode.tags.repeat_on.split(';').map(Level.fromString));
|
|
59
76
|
}
|
|
77
|
+
} else if (forceLevel !== null) {
|
|
78
|
+
levelsToGenerate = [forceLevel];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
levelsToGenerate.forEach(level => {
|
|
82
|
+
const newVertex = new GeoGraphVertex(osmNode.coords.clone(), { data: osmNode, name: osmNode.tags.name }) as OsmVertex;
|
|
83
|
+
newVertex.coords.level = level;
|
|
84
|
+
vertices.push(newVertex);
|
|
85
|
+
|
|
86
|
+
if (Level.intersect(newVertex.coords.level, forceLevel)
|
|
87
|
+
|| forceLevel === null && 'level' in osmNode.tags) {
|
|
88
|
+
vertex = newVertex;
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
if (!vertex) {
|
|
93
|
+
throw new Error(`Unable to parse vertex {id: ${osmNode.id}, coords: ${osmNode.coords.lat}, ${osmNode.coords.lng}}. Please check "level" tag.`)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (osmNode.tags.highway === 'elevator' && vertex) {
|
|
97
|
+
elevatorVertices.push(vertex);
|
|
60
98
|
}
|
|
61
|
-
return
|
|
99
|
+
return vertex! as OsmVertex;
|
|
62
100
|
};
|
|
63
101
|
|
|
64
102
|
osmModel.ways.forEach(way => {
|
|
@@ -66,39 +104,69 @@ export default class OsmGraph extends GeoGraph<OsmVertexData, OsmEdgeData> {
|
|
|
66
104
|
return;
|
|
67
105
|
}
|
|
68
106
|
|
|
69
|
-
let
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
) as OsmEdge;
|
|
76
|
-
OsmGraph.manageOneWay(edge, way);
|
|
77
|
-
edges.push(edge);
|
|
78
|
-
firstNode = secondNode;
|
|
107
|
+
let levelsToGenerate: Level_t[] = [null];
|
|
108
|
+
if ('level' in way.tags) {
|
|
109
|
+
levelsToGenerate = [way.level];
|
|
110
|
+
if ('repeat_on' in way.tags) {
|
|
111
|
+
levelsToGenerate.push(...way.tags.repeat_on.split(';').map(Level.fromString));
|
|
112
|
+
}
|
|
79
113
|
}
|
|
80
114
|
|
|
115
|
+
levelsToGenerate.forEach(level => {
|
|
116
|
+
|
|
117
|
+
let firstVertex = getOrCreateVertex(way.nodes[0], level);
|
|
118
|
+
|
|
119
|
+
for (let i = 1; i < way.nodes.length; i++) {
|
|
120
|
+
|
|
121
|
+
const secondVertex = getOrCreateVertex(way.nodes[i], level);
|
|
122
|
+
|
|
123
|
+
const edge = new GeoGraphEdge(firstVertex, secondVertex,
|
|
124
|
+
{ data: way, name: way.tags.name, level }
|
|
125
|
+
) as OsmEdge;
|
|
126
|
+
OsmGraph.manageOneWay(edge, way);
|
|
127
|
+
edges.push(edge);
|
|
128
|
+
firstVertex = secondVertex;
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
81
132
|
});
|
|
82
133
|
|
|
134
|
+
// Manage elevators without explicit navigation graph inside.
|
|
83
135
|
osmModel.ways
|
|
84
136
|
.filter(way => way.isElevator)
|
|
85
137
|
.forEach(way => {
|
|
86
|
-
way.nodes.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
138
|
+
const entryVertices = way.nodes.map(node => vertices.filter(v => v.data.id === node.id)).flat();
|
|
139
|
+
const elevatorLevel = entryVertices.reduce((acc, v) => Level.union(acc, v.coords.level), null as Level_t);
|
|
140
|
+
|
|
141
|
+
// Horrible method but works for macro-problems
|
|
142
|
+
const elevatorCenter = way.nodes
|
|
143
|
+
.reduce((acc, node) => [acc[0] + node.coords.lat, acc[1] + node.coords.lng], [0, 0])
|
|
144
|
+
.map(val => val / way.nodes.length);
|
|
145
|
+
|
|
146
|
+
const elevatorCenterCoords = new Coordinates(elevatorCenter[0], elevatorCenter[1], null, elevatorLevel);
|
|
147
|
+
const elevatorCenterVertex = new GeoGraphVertex<OsmVertexData, OsmEdgeData>(elevatorCenterCoords, { data: way, name: way.tags.name }) as OsmVertex;
|
|
148
|
+
vertices.push(elevatorCenterVertex);
|
|
149
|
+
|
|
150
|
+
entryVertices.forEach(entryVertex => {
|
|
151
|
+
const newEdge = new GeoGraphEdge(
|
|
152
|
+
elevatorCenterVertex,
|
|
153
|
+
entryVertex,
|
|
154
|
+
{
|
|
155
|
+
level: Level.intersection(elevatorCenterVertex.coords.level, entryVertex.coords.level),
|
|
156
|
+
data: way
|
|
157
|
+
}
|
|
158
|
+
) as OsmEdge;
|
|
159
|
+
edges.push(newEdge);
|
|
160
|
+
});
|
|
161
|
+
elevatorVertices.push(elevatorCenterVertex);
|
|
94
162
|
});
|
|
95
163
|
|
|
96
|
-
|
|
164
|
+
elevatorVertices.forEach(node => {
|
|
97
165
|
// We have to clone this node for each connected edge
|
|
98
|
-
OsmGraph.createNodesAndEdgesFromElevator(
|
|
166
|
+
OsmGraph.createNodesAndEdgesFromElevator(vertices, edges, node);
|
|
99
167
|
});
|
|
100
168
|
|
|
101
|
-
return new OsmGraph(
|
|
169
|
+
return new OsmGraph(vertices, edges, true);
|
|
102
170
|
}
|
|
103
171
|
|
|
104
172
|
|
|
@@ -119,69 +187,68 @@ export default class OsmGraph extends GeoGraph<OsmVertexData, OsmEdgeData> {
|
|
|
119
187
|
|
|
120
188
|
|
|
121
189
|
private static createNodesAndEdgesFromElevator(
|
|
122
|
-
|
|
190
|
+
vertices: OsmVertex[],
|
|
123
191
|
edges: OsmEdge[],
|
|
124
|
-
|
|
192
|
+
elevatorVertex: OsmVertex
|
|
125
193
|
) {
|
|
126
194
|
|
|
127
|
-
const
|
|
195
|
+
const createdVertices: OsmVertex[] = [];
|
|
128
196
|
const getOrCreateLevelVertex = (level: number | null) => {
|
|
129
|
-
let levelVertex =
|
|
197
|
+
let levelVertex = createdVertices.find(({ coords }) => Level.equals(level, coords.level));
|
|
130
198
|
if (!levelVertex) {
|
|
131
|
-
levelVertex = new GeoGraphVertex(
|
|
132
|
-
data:
|
|
133
|
-
name: `${
|
|
199
|
+
levelVertex = new GeoGraphVertex(elevatorVertex.coords.clone(), {
|
|
200
|
+
data: elevatorVertex.data,
|
|
201
|
+
name: `${elevatorVertex.name} (elevator lvl: ${level})`
|
|
134
202
|
}) as OsmVertex;
|
|
135
203
|
levelVertex.coords.level = level;
|
|
136
|
-
|
|
137
|
-
|
|
204
|
+
createdVertices.push(levelVertex);
|
|
205
|
+
vertices.push(levelVertex);
|
|
138
206
|
}
|
|
139
207
|
return levelVertex;
|
|
140
208
|
};
|
|
141
209
|
|
|
142
210
|
// Create nodes from node.edges
|
|
143
|
-
|
|
211
|
+
elevatorVertex.edges.forEach(edge => {
|
|
144
212
|
if (Level.isRange(edge.level)) {
|
|
145
|
-
throw new Error(
|
|
213
|
+
throw new Error(`Cannot handle this elevator edge due to ambiguity (vertex: ${edge.vertex1.coords})`);
|
|
146
214
|
}
|
|
147
215
|
|
|
148
216
|
const levelVertex = getOrCreateLevelVertex(edge.level);
|
|
149
|
-
if (edge.vertex1 ===
|
|
217
|
+
if (edge.vertex1 === elevatorVertex) {
|
|
150
218
|
edge.vertex1 = levelVertex;
|
|
151
219
|
} else {
|
|
152
220
|
edge.vertex2 = levelVertex;
|
|
153
221
|
}
|
|
154
|
-
levelVertex.edges.push(edge);
|
|
155
222
|
});
|
|
156
223
|
|
|
157
224
|
// Create edges from createdNodes
|
|
158
|
-
for (let i = 0; i <
|
|
159
|
-
for (let j = i + 1; j <
|
|
225
|
+
for (let i = 0; i < createdVertices.length; i++) {
|
|
226
|
+
for (let j = i + 1; j < createdVertices.length; j++) {
|
|
160
227
|
|
|
161
|
-
const
|
|
162
|
-
const
|
|
228
|
+
const createdVertex1 = createdVertices[i];
|
|
229
|
+
const createdVertex2 = createdVertices[j];
|
|
163
230
|
|
|
164
|
-
if (
|
|
231
|
+
if (createdVertex1.coords.level === null || createdVertex2.coords.level === null) {
|
|
165
232
|
// TODO: not the best approach... but cannot do better with [number, number] range for levels
|
|
166
233
|
continue;
|
|
167
234
|
}
|
|
168
235
|
|
|
169
|
-
const minLevel = Math.min(
|
|
170
|
-
const maxLevel = Math.max(
|
|
236
|
+
const minLevel = Math.min(createdVertex1.coords.level as number, createdVertex2.coords.level as number);
|
|
237
|
+
const maxLevel = Math.max(createdVertex1.coords.level as number, createdVertex2.coords.level as number);
|
|
171
238
|
|
|
172
239
|
const newEdge = new GeoGraphEdge<OsmVertexData, OsmEdgeData>(
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
{ data:
|
|
240
|
+
createdVertex1,
|
|
241
|
+
createdVertex2,
|
|
242
|
+
{ data: elevatorVertex.data, name: elevatorVertex.name, level: [minLevel, maxLevel] }
|
|
176
243
|
) as OsmEdge;
|
|
177
244
|
edges.push(newEdge);
|
|
178
245
|
}
|
|
179
246
|
}
|
|
180
247
|
|
|
181
248
|
// Remove the historical elevator node from the network
|
|
182
|
-
const elevatorNodeIndex =
|
|
249
|
+
const elevatorNodeIndex = vertices.indexOf(elevatorVertex);
|
|
183
250
|
if (elevatorNodeIndex > -1) {
|
|
184
|
-
|
|
251
|
+
vertices.splice(elevatorNodeIndex, 1);
|
|
185
252
|
}
|
|
186
253
|
}
|
|
187
254
|
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import chai from 'chai';
|
|
2
|
+
import chaiAlmost from 'chai-almost';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
|
|
7
|
+
import { OsmParser } from '@wemap/osm';
|
|
8
|
+
|
|
9
|
+
import OsmRouter from './OsmRouter.js';
|
|
10
|
+
import OsmGraph from './OsmGraph.js';
|
|
11
|
+
|
|
12
|
+
const { expect } = chai;
|
|
13
|
+
chai.use(chaiAlmost(0.01));
|
|
14
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
const filePath = path.resolve(__dirname, '../../../osm/assets/elevator-models.osm');
|
|
18
|
+
const osmXmlString = fs.readFileSync(filePath, 'utf8');
|
|
19
|
+
const osmModel = OsmParser.parseOsmXmlString(osmXmlString);
|
|
20
|
+
const graph = OsmGraph.fromOsmModel(osmModel);
|
|
21
|
+
const router = new OsmRouter(graph);
|
|
22
|
+
|
|
23
|
+
const n = (vertexName: string) => graph.getVertexByName(vertexName)!.coords
|
|
24
|
+
|
|
25
|
+
describe('OsmRouter Elevators - Case 1', () => {
|
|
26
|
+
|
|
27
|
+
it('Itinerary 1', () => {
|
|
28
|
+
expect(router.getShortestPath(n('s1'), n('f1a')).edges.length).not.equals(0)
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('Itinerary 2', () => {
|
|
32
|
+
expect(router.getShortestPath(n('s1'), n('f1b')).edges.length).not.equals(0)
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('Itinerary 3', () => {
|
|
36
|
+
expect(router.getShortestPath(n('s1'), n('f1c')).edges.length).not.equals(0)
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
describe('OsmRouter Elevators - Case 2', () => {
|
|
43
|
+
|
|
44
|
+
it('Itinerary 1', () => {
|
|
45
|
+
expect(router.getShortestPath(n('s2'), n('f2a')).edges.length).not.equals(0)
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('Itinerary 2', () => {
|
|
49
|
+
expect(router.getShortestPath(n('s2'), n('f2b')).edges.length).not.equals(0)
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('Itinerary 3', () => {
|
|
53
|
+
expect(router.getShortestPath(n('s2'), n('f2c')).edges.length).not.equals(0)
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
describe('OsmRouter Elevators - Case 3', () => {
|
|
60
|
+
|
|
61
|
+
it('Itinerary 1', () => {
|
|
62
|
+
expect(router.getShortestPath(n('s3'), n('f3a')).edges.length).not.equals(0)
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('Itinerary 2', () => {
|
|
66
|
+
expect(router.getShortestPath(n('s3'), n('f3b')).edges.length).not.equals(0)
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('Itinerary 3', () => {
|
|
70
|
+
expect(router.getShortestPath(n('s3'), n('f3c')).edges.length).not.equals(0)
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('OsmRouter Elevators - Case 4', () => {
|
|
76
|
+
|
|
77
|
+
it('Itinerary 1', () => {
|
|
78
|
+
expect(router.getShortestPath(n('s4'), n('f4a')).edges.length).not.equals(0)
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('Itinerary 2', () => {
|
|
82
|
+
expect(router.getShortestPath(n('s4'), n('f4b')).edges.length).not.equals(0)
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('Itinerary 3', () => {
|
|
86
|
+
expect(router.getShortestPath(n('s4'), n('f4c')).edges.length).not.equals(0)
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('OsmRouter Elevators - Case 5', () => {
|
|
92
|
+
|
|
93
|
+
it('Itinerary 1', () => {
|
|
94
|
+
expect(router.getShortestPath(n('s5'), n('f5a')).edges.length).not.equals(0)
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('Itinerary 2', () => {
|
|
98
|
+
expect(router.getShortestPath(n('s5'), n('f5b')).edges.length).not.equals(0)
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('Itinerary 3', () => {
|
|
102
|
+
expect(router.getShortestPath(n('s5'), n('f5c')).edges.length).not.equals(0)
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
});
|
|
@@ -17,7 +17,7 @@ chai.use(chaiAlmost(0.01));
|
|
|
17
17
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
const
|
|
20
|
+
const getVerticesNames = (_itinerary: OsmItinerary) => {
|
|
21
21
|
return _itinerary.vertices.map(vertex => vertex.name || null);
|
|
22
22
|
};
|
|
23
23
|
|
|
@@ -33,7 +33,7 @@ const loadGraph = (fileName: string) => {
|
|
|
33
33
|
return OsmGraph.fromOsmModel(osmModel);
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
-
const
|
|
36
|
+
const generateVertexNames = (start: number, end: number) =>
|
|
37
37
|
new Array(end - start + 1).fill(null).map((_, idx) => 'p' + (idx + start));
|
|
38
38
|
|
|
39
39
|
describe('OsmRouter - Multi-level itinerary', () => {
|
|
@@ -46,7 +46,7 @@ describe('OsmRouter - Multi-level itinerary', () => {
|
|
|
46
46
|
|
|
47
47
|
const p: OsmVertex[] = [];
|
|
48
48
|
|
|
49
|
-
it('Search for
|
|
49
|
+
it('Search for vertices', () => {
|
|
50
50
|
for (let i = 1; i <= 16; i++) {
|
|
51
51
|
p[i] = graph.getVertexByName('p' + i) as OsmVertex;
|
|
52
52
|
expect(p[i]).instanceOf(GeoGraphVertex);
|
|
@@ -54,8 +54,8 @@ describe('OsmRouter - Multi-level itinerary', () => {
|
|
|
54
54
|
});
|
|
55
55
|
|
|
56
56
|
it('Router return shortest path', () => {
|
|
57
|
-
expect(
|
|
58
|
-
['proj on w2 (tmp)', ...
|
|
57
|
+
expect(getVerticesNames(graphItinerary)).deep.equals(
|
|
58
|
+
['proj on w2 (tmp)', ...generateVertexNames(7, 16)]
|
|
59
59
|
);
|
|
60
60
|
});
|
|
61
61
|
|
|
@@ -115,7 +115,7 @@ describe('OsmRouter - Multi-level itinerary', () => {
|
|
|
115
115
|
const end = new Coordinates(43.60917216742, 3.8842355275, null, 2);
|
|
116
116
|
const itinerary2 = router.getShortestPath(start, end);
|
|
117
117
|
|
|
118
|
-
expect(
|
|
118
|
+
expect(getVerticesNames(itinerary2)).deep.equals(['proj on w1 (tmp)', 'p6', 'p5']);
|
|
119
119
|
});
|
|
120
120
|
|
|
121
121
|
it('do not use stairs', () => {
|
|
@@ -136,8 +136,8 @@ describe('OsmRouter - Multi-level itinerary', () => {
|
|
|
136
136
|
|
|
137
137
|
// const itineraryBis = Itinerary.fromJson(json);
|
|
138
138
|
// verifyCoherence(itineraryBis);
|
|
139
|
-
// expect(itineraryBis.
|
|
140
|
-
//
|
|
139
|
+
// expect(itineraryBis.vertices.length).equal(11);
|
|
140
|
+
// verifyVerticesOrder(itineraryBis.vertices, ['w2', ...generateVertexNames(7, 16)]);
|
|
141
141
|
// });
|
|
142
142
|
|
|
143
143
|
});
|
|
@@ -154,11 +154,11 @@ describe('OsmRouter - One Way itinerary', () => {
|
|
|
154
154
|
|
|
155
155
|
const itinerary = router.getShortestPath(start, end);
|
|
156
156
|
expect(itinerary).is.not.undefined;
|
|
157
|
-
expect(
|
|
157
|
+
expect(getVerticesNames(itinerary)).deep.equals(['p0', 'p1', 'p2', 'p3']);
|
|
158
158
|
|
|
159
159
|
const itineraryOtherWay = router.getShortestPath(end, start);
|
|
160
160
|
expect(itineraryOtherWay).is.not.undefined;
|
|
161
|
-
expect(
|
|
161
|
+
expect(getVerticesNames(itineraryOtherWay)).deep.equals(['p3', 'p2', 'p5', 'p4', 'p1', 'p0']);
|
|
162
162
|
});
|
|
163
163
|
});
|
|
164
164
|
|
|
@@ -175,11 +175,11 @@ describe('OsmRouter - Conveying', () => {
|
|
|
175
175
|
|
|
176
176
|
const itinerary = router.getShortestPath(start, end);
|
|
177
177
|
expect(itinerary).is.not.undefined;
|
|
178
|
-
expect(
|
|
178
|
+
expect(getVerticesNames(itinerary)).deep.equals(['p11', 'p12', 'p9', 'p8', 'p6', 'proj on null (tmp)']);
|
|
179
179
|
|
|
180
180
|
const itineraryOtherWay = router.getShortestPath(end, start);
|
|
181
181
|
expect(itineraryOtherWay).is.not.undefined;
|
|
182
|
-
expect(
|
|
182
|
+
expect(getVerticesNames(itineraryOtherWay)).deep.equals(['proj on null (tmp)', 'p7', 'p10', 'p11']);
|
|
183
183
|
});
|
|
184
184
|
});
|
|
185
185
|
|
|
@@ -196,11 +196,11 @@ describe('OsmRouter - Conveying - backward', () => {
|
|
|
196
196
|
|
|
197
197
|
const itinerary = router.getShortestPath(start, end);
|
|
198
198
|
expect(itinerary).is.not.undefined;
|
|
199
|
-
expect(
|
|
199
|
+
expect(getVerticesNames(itinerary)).deep.equals(['p6', 'p7', 'p10', 'p11', 'p12', 'p13']);
|
|
200
200
|
|
|
201
201
|
const itineraryOtherWay = router.getShortestPath(end, start);
|
|
202
202
|
expect(itineraryOtherWay).is.not.undefined;
|
|
203
|
-
expect(
|
|
203
|
+
expect(getVerticesNames(itineraryOtherWay)).deep.equals(['p13', 'p12', 'p9', 'p8', 'p6']);
|
|
204
204
|
});
|
|
205
205
|
});
|
|
206
206
|
|