@osmix/router 0.0.13 → 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/CHANGELOG.md +64 -0
- package/LICENSE +21 -0
- package/README.md +138 -53
- package/dist/algorithms/bidirectional.d.ts +6 -9
- package/dist/algorithms/bidirectional.d.ts.map +1 -1
- package/dist/algorithms/bidirectional.js +84 -104
- package/dist/algorithms/bidirectional.js.map +1 -1
- package/dist/algorithms/index.d.ts +1 -1
- package/dist/algorithms/index.d.ts.map +1 -1
- package/dist/algorithms/index.js +1 -1
- package/dist/algorithms/index.js.map +1 -1
- package/dist/algorithms/shortest-path.d.ts +2 -1
- package/dist/algorithms/shortest-path.d.ts.map +1 -1
- package/dist/algorithms/shortest-path.js +9 -7
- package/dist/algorithms/shortest-path.js.map +1 -1
- package/dist/binary-heap.d.ts +5 -0
- package/dist/binary-heap.d.ts.map +1 -1
- package/dist/binary-heap.js +11 -4
- package/dist/binary-heap.js.map +1 -1
- package/dist/graph.d.ts +35 -7
- package/dist/graph.d.ts.map +1 -1
- package/dist/graph.js +161 -119
- package/dist/graph.js.map +1 -1
- package/dist/index.d.ts +9 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -4
- package/dist/index.js.map +1 -1
- package/dist/router.d.ts +1 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +6 -3
- package/dist/router.js.map +1 -1
- package/dist/topology-stats.d.ts +37 -0
- package/dist/topology-stats.d.ts.map +1 -0
- package/dist/topology-stats.js +62 -0
- package/dist/topology-stats.js.map +1 -0
- package/dist/types.d.ts +15 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +1 -7
- package/dist/utils.js.map +1 -1
- package/package.json +32 -29
- package/src/algorithms/bidirectional.ts +124 -136
- package/src/algorithms/index.ts +10 -10
- package/src/algorithms/shortest-path.ts +96 -92
- package/src/binary-heap.ts +133 -129
- package/src/graph.ts +389 -355
- package/src/index.ts +14 -9
- package/src/router.ts +221 -233
- package/src/topology-stats.ts +84 -0
- package/src/types.ts +102 -87
- package/src/utils.ts +67 -74
- package/test/algorithms.test.ts +396 -353
- package/test/binary-heap.test.ts +27 -0
- package/test/limits.test.ts +82 -0
- package/test/no-shared-array-buffer.test.ts +37 -0
- package/test/router.bench.ts +171 -188
- package/test/router.test.ts +685 -726
- package/test/spatial-capability.test.ts +23 -0
- package/test/topology-stats.test.ts +82 -0
- package/tsconfig.build.json +6 -6
- package/tsconfig.json +7 -7
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"topology-stats.d.ts","sourceRoot":"","sources":["../src/topology-stats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAG5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,sDAAsD;AACtD,MAAM,WAAW,oBAAoB;IACnC,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,aAAa,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,uFAAuF;AACvF,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,IAAI,QAAQ,CAAC;QAAE,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CAC3E;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,EAC5D,MAAM,EAAE,aAAa,GACpB,MAAM,GAAG,IAAI,CAUf;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,qBAAqB,EAC7B,MAAM,EAAE,aAAa,GACpB,oBAAoB,CAgCtB"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { normalizedWayDirection } from "@osmix/types/way-direction";
|
|
2
|
+
/**
|
|
3
|
+
* Directed edges one way adds for `filter` (two per segment, one when one-way), or null when
|
|
4
|
+
* the filter does not route it. Direction follows the router: an unsupported one-way value
|
|
5
|
+
* counts as forward on a roundabout and as two-way elsewhere.
|
|
6
|
+
*/
|
|
7
|
+
export function routingWayEdges(way, filter) {
|
|
8
|
+
if (!filter(way.tags) || way.refs.length < 2)
|
|
9
|
+
return null;
|
|
10
|
+
const normalized = normalizedWayDirection(way.tags);
|
|
11
|
+
const direction = normalized === "unsupported"
|
|
12
|
+
? way.tags?.["junction"] === "roundabout"
|
|
13
|
+
? "forward"
|
|
14
|
+
: "both"
|
|
15
|
+
: normalized;
|
|
16
|
+
return (direction === "both" ? 2 : 1) * (way.refs.length - 1);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The same counts `RoutingGraph` would give for `filter`, computed from ways alone, without
|
|
20
|
+
* building the graph (no coordinates, speeds or CSR arrays). Direction follows the router: an
|
|
21
|
+
* unsupported one-way value counts as forward on a roundabout and as two-way elsewhere.
|
|
22
|
+
*/
|
|
23
|
+
export function routingTopologyStats(source, filter) {
|
|
24
|
+
const parent = new Map();
|
|
25
|
+
const find = (id) => {
|
|
26
|
+
let root = id;
|
|
27
|
+
while (parent.get(root) !== root)
|
|
28
|
+
root = parent.get(root);
|
|
29
|
+
let cursor = id;
|
|
30
|
+
while (parent.get(cursor) !== cursor) {
|
|
31
|
+
const next = parent.get(cursor);
|
|
32
|
+
parent.set(cursor, root);
|
|
33
|
+
cursor = next;
|
|
34
|
+
}
|
|
35
|
+
return root;
|
|
36
|
+
};
|
|
37
|
+
const union = (a, b) => {
|
|
38
|
+
if (!parent.has(a))
|
|
39
|
+
parent.set(a, a);
|
|
40
|
+
if (!parent.has(b))
|
|
41
|
+
parent.set(b, b);
|
|
42
|
+
const left = find(a);
|
|
43
|
+
const right = find(b);
|
|
44
|
+
if (left !== right)
|
|
45
|
+
parent.set(right, left);
|
|
46
|
+
};
|
|
47
|
+
let edges = 0;
|
|
48
|
+
for (const way of source.ways()) {
|
|
49
|
+
const wayEdges = routingWayEdges(way, filter);
|
|
50
|
+
if (wayEdges == null)
|
|
51
|
+
continue;
|
|
52
|
+
for (let index = 0; index < way.refs.length - 1; index++) {
|
|
53
|
+
union(way.refs[index], way.refs[index + 1]);
|
|
54
|
+
}
|
|
55
|
+
edges += wayEdges;
|
|
56
|
+
}
|
|
57
|
+
const roots = new Set();
|
|
58
|
+
for (const id of parent.keys())
|
|
59
|
+
roots.add(find(id));
|
|
60
|
+
return { nodes: source.nodeCount, routableNodes: parent.size, edges, components: roots.size };
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=topology-stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"topology-stats.js","sourceRoot":"","sources":["../src/topology-stats.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @module
|
|
4
4
|
*/
|
|
5
5
|
import type { BufferType } from "@osmix/core";
|
|
6
|
-
import type { LonLat, OsmTags } from "@osmix/
|
|
6
|
+
import type { LonLat, OsmTags } from "@osmix/types";
|
|
7
7
|
/** Per-way segment with distance and time (consecutive same-name ways merged). */
|
|
8
8
|
export interface WaySegment {
|
|
9
9
|
/** OSM way IDs included in this segment (multiple if merged). */
|
|
@@ -113,6 +113,19 @@ export interface PathSegment {
|
|
|
113
113
|
/** Cost to reach this node. */
|
|
114
114
|
cost: number;
|
|
115
115
|
}
|
|
116
|
+
/** Extra graph data that some routing algorithms need. */
|
|
117
|
+
export interface RoutingAlgorithmContext {
|
|
118
|
+
/**
|
|
119
|
+
* Incoming edges of a node. In each returned edge, `targetNodeIndex` is the
|
|
120
|
+
* edge's source node. Required by `bidirectional`.
|
|
121
|
+
*/
|
|
122
|
+
reverseGraph?: (nodeIndex: number) => GraphEdge[];
|
|
123
|
+
/**
|
|
124
|
+
* Fastest edge speed in the graph, in meters per second. Bounds the A* time
|
|
125
|
+
* heuristic so it never overestimates. Default: 130 km/h.
|
|
126
|
+
*/
|
|
127
|
+
maxSpeedMps?: number;
|
|
128
|
+
}
|
|
116
129
|
/** Function signature for routing algorithms. */
|
|
117
|
-
export type RoutingAlgorithmFn = (graph: (nodeIndex: number) => GraphEdge[], startNodeIndex: number, endNodeIndex: number, getEdgeWeight: (edge: GraphEdge) => number, getNodeCoord?: (nodeIndex: number) => LonLat | undefined, metric?: RoutingMetric) => PathSegment[] | null;
|
|
130
|
+
export type RoutingAlgorithmFn = (graph: (nodeIndex: number) => GraphEdge[], startNodeIndex: number, endNodeIndex: number, getEdgeWeight: (edge: GraphEdge) => number, getNodeCoord?: (nodeIndex: number) => LonLat | undefined, metric?: RoutingMetric, context?: RoutingAlgorithmContext) => PathSegment[] | null;
|
|
118
131
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/utils.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @module
|
|
7
7
|
*/
|
|
8
|
-
import type { OsmTags } from "@osmix/
|
|
8
|
+
import type { OsmTags } from "@osmix/types";
|
|
9
9
|
import type { DefaultSpeeds } from "./types.ts";
|
|
10
10
|
/** Default speeds (km/h) by highway type. */
|
|
11
11
|
export declare const DEFAULT_SPEEDS: DefaultSpeeds;
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,6CAA6C;AAC7C,eAAO,MAAM,cAAc,EAAE,aAoB5B,CAAC;AAuBF,qEAAqE;AACrE,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAqB/E;AAED,wCAAwC;AACxC,wBAAgB,aAAa,CAC3B,IAAI,EAAE,OAAO,GAAG,SAAS,EACzB,aAAa,GAAE,aAA8B,GAC5C,MAAM,CAUR;AAED,+CAA+C;AAC/C,wBAAgB,oBAAoB,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAG5D;AAED,gCAAgC;AAChC,wBAAgB,uBAAuB,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAG/D"}
|
package/dist/utils.js
CHANGED
|
@@ -45,13 +45,7 @@ const VEHICLE_HIGHWAYS = new Set([
|
|
|
45
45
|
"service",
|
|
46
46
|
]);
|
|
47
47
|
/** Footways included in default pedestrian routing. */
|
|
48
|
-
const FOOT_HIGHWAYS = new Set([
|
|
49
|
-
"footway",
|
|
50
|
-
"path",
|
|
51
|
-
"cycleway",
|
|
52
|
-
"bridleway",
|
|
53
|
-
"steps",
|
|
54
|
-
]);
|
|
48
|
+
const FOOT_HIGHWAYS = new Set(["footway", "path", "cycleway", "bridleway", "steps"]);
|
|
55
49
|
/** Parse maxspeed tag to km/h. Handles "50", "50 km/h", "30 mph". */
|
|
56
50
|
export function parseMaxSpeed(value) {
|
|
57
51
|
if (!value)
|
package/dist/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,6CAA6C;AAC7C,MAAM,CAAC,MAAM,cAAc,GAAkB;IAC3C,QAAQ,EAAE,GAAG;IACb,aAAa,EAAE,EAAE;IACjB,KAAK,EAAE,GAAG;IACV,UAAU,EAAE,EAAE;IACd,OAAO,EAAE,EAAE;IACX,YAAY,EAAE,EAAE;IAChB,SAAS,EAAE,EAAE;IACb,cAAc,EAAE,EAAE;IAClB,QAAQ,EAAE,EAAE;IACZ,aAAa,EAAE,EAAE;IACjB,YAAY,EAAE,EAAE;IAChB,WAAW,EAAE,EAAE;IACf,aAAa,EAAE,EAAE;IACjB,OAAO,EAAE,EAAE;IACX,OAAO,EAAE,CAAC;IACV,IAAI,EAAE,CAAC;IACP,QAAQ,EAAE,EAAE;IACZ,SAAS,EAAE,EAAE;IACb,KAAK,EAAE,CAAC;CACT,CAAC;AAEF,oDAAoD;AACpD,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC;IAC/B,UAAU;IACV,eAAe;IACf,OAAO;IACP,YAAY;IACZ,SAAS;IACT,cAAc;IACd,WAAW;IACX,gBAAgB;IAChB,UAAU;IACV,eAAe;IACf,cAAc;IACd,aAAa;IACb,eAAe;IACf,SAAS;CACV,CAAC,CAAC;AAEH,uDAAuD;AACvD,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;AAErF,qEAAqE;AACrE,MAAM,UAAU,aAAa,CAAC,KAAkC;IAC9D,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAE/C,uBAAuB;IACvB,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACrC,IAAI,OAAO;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAE,EAAE,EAAE,CAAC,CAAC;IAErD,gBAAgB;IAChB,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;IACnD,IAAI,GAAG;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAE,EAAE,EAAE,CAAC,CAAC;IAE7C,cAAc;IACd,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IACvC,IAAI,GAAG;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAE,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC;IAEnE,iBAAiB;IACjB,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,MAAM;QAAE,OAAO,CAAC,CAAC;IAE/C,OAAO,IAAI,CAAC;AACd,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,aAAa,CAC3B,IAAyB,EACzB,aAAa,GAAkB,cAAc;IAE7C,IAAI,IAAI,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAC/C,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,MAAM,CAAC;IACrC,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACxE,IAAI,OAAO,IAAI,aAAa,CAAC,OAAO,CAAC;QAAE,OAAO,aAAa,CAAC,OAAO,CAAE,CAAC;IAEtE,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,oBAAoB,CAAC,IAAc;IACjD,IAAI,CAAC,IAAI,EAAE,CAAC,SAAS,CAAC;QAAE,OAAO,KAAK,CAAC;IACrC,OAAO,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;AACvD,CAAC;AAED,gCAAgC;AAChC,MAAM,UAAU,uBAAuB,CAAC,IAAc;IACpD,IAAI,CAAC,IAAI,EAAE,CAAC,SAAS,CAAC;QAAE,OAAO,KAAK,CAAC;IACrC,OAAO,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;AACpD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,30 +1,33 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
2
|
+
"$schema": "https://json.schemastore.org/package.json",
|
|
3
|
+
"name": "@osmix/router",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "A routing engine for OSM data built on top of @osmix/core",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/conveyal/osmix.git"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@osmix/core": "0.3.0",
|
|
15
|
+
"@osmix/geo": "0.1.1",
|
|
16
|
+
"@osmix/shared": "0.2.0",
|
|
17
|
+
"@osmix/types": "0.2.0"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@osmix/load": "0.1.0",
|
|
21
|
+
"@osmix/test-utils": "0.1.0",
|
|
22
|
+
"mitata": "^1.0.34",
|
|
23
|
+
"typescript": "^7.0.2"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=24"
|
|
27
|
+
},
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc -p tsconfig.build.json",
|
|
31
|
+
"bench": "node test/router.bench.ts"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -1,147 +1,135 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Bidirectional
|
|
2
|
+
* Bidirectional Dijkstra.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Runs Dijkstra forward from the start over outgoing edges and backward from
|
|
5
|
+
* the end over incoming edges, and stops when neither search can improve the
|
|
6
|
+
* best meeting point. Returns an optimal path and respects one-way edges.
|
|
7
7
|
*
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import
|
|
11
|
+
import { BinaryHeap } from "../binary-heap.ts";
|
|
12
|
+
import type { GraphEdge, PathSegment, RoutingAlgorithmFn } from "../types.ts";
|
|
13
|
+
|
|
14
|
+
/** How the backward search reached a node: the next node toward the end. */
|
|
15
|
+
interface BackwardStep {
|
|
16
|
+
nextNodeIndex: number;
|
|
17
|
+
wayIndex: number;
|
|
18
|
+
}
|
|
12
19
|
|
|
13
20
|
/**
|
|
14
|
-
* Bidirectional
|
|
15
|
-
*
|
|
16
|
-
* Uses breadth-first search from both start and end, terminating when
|
|
17
|
-
* the frontiers meet. Very fast for finding any path, though not always
|
|
18
|
-
* the optimal shortest path. Use Dijkstra or A* if optimality is required.
|
|
21
|
+
* Bidirectional Dijkstra. Requires `context.reverseGraph`, which `Router`
|
|
22
|
+
* supplies from `RoutingGraph.getIncomingEdges`.
|
|
19
23
|
*/
|
|
20
24
|
export const bidirectional: RoutingAlgorithmFn = (
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
graph,
|
|
26
|
+
start,
|
|
27
|
+
end,
|
|
28
|
+
getWeight,
|
|
29
|
+
_getCoord,
|
|
30
|
+
_metric,
|
|
31
|
+
context,
|
|
25
32
|
) => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const meetForwardCost = fDist.get(meetNode)!
|
|
130
|
-
const meetBackwardCost = bDist.get(meetNode)!
|
|
131
|
-
curr = meetNode
|
|
132
|
-
while (curr !== undefined && curr !== end) {
|
|
133
|
-
const seg = bPrev.get(curr)
|
|
134
|
-
if (!seg) break
|
|
135
|
-
curr = seg.previousNodeIndex
|
|
136
|
-
if (curr !== undefined) {
|
|
137
|
-
forward.push({
|
|
138
|
-
nodeIndex: curr,
|
|
139
|
-
wayIndex: seg.wayIndex,
|
|
140
|
-
previousNodeIndex: seg.nodeIndex,
|
|
141
|
-
cost: meetForwardCost + meetBackwardCost - (bDist.get(curr) ?? 0),
|
|
142
|
-
})
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return forward
|
|
147
|
-
}
|
|
33
|
+
if (start === end) return [{ nodeIndex: start, cost: 0 }];
|
|
34
|
+
const reverseGraph = context?.reverseGraph;
|
|
35
|
+
if (!reverseGraph) {
|
|
36
|
+
throw Error(
|
|
37
|
+
"Bidirectional routing needs context.reverseGraph (incoming edges). " +
|
|
38
|
+
"Router passes RoutingGraph.getIncomingEdges.",
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const fDist = new Map<number, number>([[start, 0]]);
|
|
43
|
+
const bDist = new Map<number, number>([[end, 0]]);
|
|
44
|
+
const fPrev = new Map<number, PathSegment>();
|
|
45
|
+
const bNext = new Map<number, BackwardStep>();
|
|
46
|
+
const fHeap = new BinaryHeap();
|
|
47
|
+
const bHeap = new BinaryHeap();
|
|
48
|
+
const fClosed = new Set<number>();
|
|
49
|
+
const bClosed = new Set<number>();
|
|
50
|
+
fHeap.push(start, 0);
|
|
51
|
+
bHeap.push(end, 0);
|
|
52
|
+
|
|
53
|
+
let best = Number.POSITIVE_INFINITY;
|
|
54
|
+
let meet = -1;
|
|
55
|
+
|
|
56
|
+
const relax = (
|
|
57
|
+
current: number,
|
|
58
|
+
edges: GraphEdge[],
|
|
59
|
+
dist: Map<number, number>,
|
|
60
|
+
otherDist: Map<number, number>,
|
|
61
|
+
closed: Set<number>,
|
|
62
|
+
heap: BinaryHeap,
|
|
63
|
+
record: (neighbor: number, edge: GraphEdge, cost: number) => void,
|
|
64
|
+
) => {
|
|
65
|
+
const currentD = dist.get(current)!;
|
|
66
|
+
for (const edge of edges) {
|
|
67
|
+
const neighbor = edge.targetNodeIndex;
|
|
68
|
+
if (closed.has(neighbor)) continue;
|
|
69
|
+
const cost = currentD + getWeight(edge);
|
|
70
|
+
const existing = dist.get(neighbor);
|
|
71
|
+
if (existing !== undefined && cost >= existing) continue;
|
|
72
|
+
dist.set(neighbor, cost);
|
|
73
|
+
record(neighbor, edge, cost);
|
|
74
|
+
heap.push(neighbor, cost);
|
|
75
|
+
const other = otherDist.get(neighbor);
|
|
76
|
+
if (other !== undefined && cost + other < best) {
|
|
77
|
+
best = cost + other;
|
|
78
|
+
meet = neighbor;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
while (fHeap.size > 0 && bHeap.size > 0) {
|
|
84
|
+
// Stop once no unsettled node can lie on a path shorter than `best`.
|
|
85
|
+
if (fHeap.peekPriority() + bHeap.peekPriority() >= best) break;
|
|
86
|
+
|
|
87
|
+
if (fHeap.peekPriority() <= bHeap.peekPriority()) {
|
|
88
|
+
const current = fHeap.pop()!;
|
|
89
|
+
if (fClosed.has(current)) continue;
|
|
90
|
+
fClosed.add(current);
|
|
91
|
+
relax(current, graph(current), fDist, bDist, fClosed, fHeap, (neighbor, edge, cost) => {
|
|
92
|
+
fPrev.set(neighbor, {
|
|
93
|
+
nodeIndex: neighbor,
|
|
94
|
+
wayIndex: edge.wayIndex,
|
|
95
|
+
previousNodeIndex: current,
|
|
96
|
+
cost,
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
} else {
|
|
100
|
+
const current = bHeap.pop()!;
|
|
101
|
+
if (bClosed.has(current)) continue;
|
|
102
|
+
bClosed.add(current);
|
|
103
|
+
relax(current, reverseGraph(current), bDist, fDist, bClosed, bHeap, (neighbor, edge) => {
|
|
104
|
+
bNext.set(neighbor, { nextNodeIndex: current, wayIndex: edge.wayIndex });
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (meet === -1) return null;
|
|
110
|
+
|
|
111
|
+
// Forward half: start → meet.
|
|
112
|
+
const path: PathSegment[] = [];
|
|
113
|
+
let node = meet;
|
|
114
|
+
while (node !== start) {
|
|
115
|
+
const segment = fPrev.get(node)!;
|
|
116
|
+
path.unshift(segment);
|
|
117
|
+
node = segment.previousNodeIndex!;
|
|
118
|
+
}
|
|
119
|
+
path.unshift({ nodeIndex: start, cost: 0 });
|
|
120
|
+
|
|
121
|
+
// Backward half: meet → end, with costs measured from the start.
|
|
122
|
+
node = meet;
|
|
123
|
+
while (node !== end) {
|
|
124
|
+
const step = bNext.get(node)!;
|
|
125
|
+
path.push({
|
|
126
|
+
nodeIndex: step.nextNodeIndex,
|
|
127
|
+
wayIndex: step.wayIndex,
|
|
128
|
+
previousNodeIndex: node,
|
|
129
|
+
cost: best - bDist.get(step.nextNodeIndex)!,
|
|
130
|
+
});
|
|
131
|
+
node = step.nextNodeIndex;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return path;
|
|
135
|
+
};
|
package/src/algorithms/index.ts
CHANGED
|
@@ -4,20 +4,20 @@
|
|
|
4
4
|
* Provides three algorithms:
|
|
5
5
|
* - `dijkstra`: Optimal shortest path, explores all directions equally.
|
|
6
6
|
* - `astar`: Optimal with heuristic guidance, faster for point-to-point.
|
|
7
|
-
* - `bidirectional`:
|
|
7
|
+
* - `bidirectional`: Optimal Dijkstra from both ends; needs `context.reverseGraph`.
|
|
8
8
|
*
|
|
9
9
|
* @module
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import type { RoutingAlgorithm, RoutingAlgorithmFn } from "../types.ts"
|
|
13
|
-
import { bidirectional } from "./bidirectional.ts"
|
|
14
|
-
import { astar, dijkstra } from "./shortest-path.ts"
|
|
12
|
+
import type { RoutingAlgorithm, RoutingAlgorithmFn } from "../types.ts";
|
|
13
|
+
import { bidirectional } from "./bidirectional.ts";
|
|
14
|
+
import { astar, dijkstra } from "./shortest-path.ts";
|
|
15
15
|
|
|
16
|
-
export type { RoutingAlgorithmFn }
|
|
17
|
-
export { astar, bidirectional, dijkstra }
|
|
16
|
+
export type { RoutingAlgorithmFn };
|
|
17
|
+
export { astar, bidirectional, dijkstra };
|
|
18
18
|
|
|
19
19
|
export const routingAlgorithms: Record<RoutingAlgorithm, RoutingAlgorithmFn> = {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
20
|
+
dijkstra,
|
|
21
|
+
astar,
|
|
22
|
+
bidirectional,
|
|
23
|
+
};
|