@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.
Files changed (62) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/LICENSE +21 -0
  3. package/README.md +138 -53
  4. package/dist/algorithms/bidirectional.d.ts +6 -9
  5. package/dist/algorithms/bidirectional.d.ts.map +1 -1
  6. package/dist/algorithms/bidirectional.js +84 -104
  7. package/dist/algorithms/bidirectional.js.map +1 -1
  8. package/dist/algorithms/index.d.ts +1 -1
  9. package/dist/algorithms/index.d.ts.map +1 -1
  10. package/dist/algorithms/index.js +1 -1
  11. package/dist/algorithms/index.js.map +1 -1
  12. package/dist/algorithms/shortest-path.d.ts +2 -1
  13. package/dist/algorithms/shortest-path.d.ts.map +1 -1
  14. package/dist/algorithms/shortest-path.js +9 -7
  15. package/dist/algorithms/shortest-path.js.map +1 -1
  16. package/dist/binary-heap.d.ts +5 -0
  17. package/dist/binary-heap.d.ts.map +1 -1
  18. package/dist/binary-heap.js +11 -4
  19. package/dist/binary-heap.js.map +1 -1
  20. package/dist/graph.d.ts +35 -7
  21. package/dist/graph.d.ts.map +1 -1
  22. package/dist/graph.js +161 -119
  23. package/dist/graph.js.map +1 -1
  24. package/dist/index.d.ts +9 -4
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +9 -4
  27. package/dist/index.js.map +1 -1
  28. package/dist/router.d.ts +1 -1
  29. package/dist/router.d.ts.map +1 -1
  30. package/dist/router.js +6 -3
  31. package/dist/router.js.map +1 -1
  32. package/dist/topology-stats.d.ts +37 -0
  33. package/dist/topology-stats.d.ts.map +1 -0
  34. package/dist/topology-stats.js +62 -0
  35. package/dist/topology-stats.js.map +1 -0
  36. package/dist/types.d.ts +15 -2
  37. package/dist/types.d.ts.map +1 -1
  38. package/dist/utils.d.ts +1 -1
  39. package/dist/utils.d.ts.map +1 -1
  40. package/dist/utils.js +1 -7
  41. package/dist/utils.js.map +1 -1
  42. package/package.json +32 -29
  43. package/src/algorithms/bidirectional.ts +124 -136
  44. package/src/algorithms/index.ts +10 -10
  45. package/src/algorithms/shortest-path.ts +96 -92
  46. package/src/binary-heap.ts +133 -129
  47. package/src/graph.ts +389 -355
  48. package/src/index.ts +14 -9
  49. package/src/router.ts +221 -233
  50. package/src/topology-stats.ts +84 -0
  51. package/src/types.ts +102 -87
  52. package/src/utils.ts +67 -74
  53. package/test/algorithms.test.ts +396 -353
  54. package/test/binary-heap.test.ts +27 -0
  55. package/test/limits.test.ts +82 -0
  56. package/test/no-shared-array-buffer.test.ts +37 -0
  57. package/test/router.bench.ts +171 -188
  58. package/test/router.test.ts +685 -726
  59. package/test/spatial-capability.test.ts +23 -0
  60. package/test/topology-stats.test.ts +82 -0
  61. package/tsconfig.build.json +6 -6
  62. 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/shared/types";
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
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
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/shared/types";
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;
@@ -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,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,6CAA6C;AAC7C,eAAO,MAAM,cAAc,EAAE,aAoB5B,CAAA;AA6BD,qEAAqE;AACrE,wBAAgB,aAAa,CAC5B,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAChC,MAAM,GAAG,IAAI,CAqBf;AAED,wCAAwC;AACxC,wBAAgB,aAAa,CAC5B,IAAI,EAAE,OAAO,GAAG,SAAS,EACzB,aAAa,GAAE,aAA8B,GAC3C,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"}
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":"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"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,30 +1,33 @@
1
1
  {
2
- "$schema": "https://json.schemastore.org/package.json",
3
- "name": "@osmix/router",
4
- "type": "module",
5
- "version": "0.0.13",
6
- "description": "A routing engine for OSM data built on top of @osmix/core",
7
- "license": "MIT",
8
- "repository": {
9
- "type": "git",
10
- "url": "git+https://github.com/conveyal/osmix.git"
11
- },
12
- "main": "./dist/index.js",
13
- "scripts": {
14
- "build": "tsc -p tsconfig.build.json",
15
- "bench": "bun run test/router.bench.ts",
16
- "test": "bun test",
17
- "typecheck": "tsc --noEmit"
18
- },
19
- "devDependencies": {
20
- "@types/bun": "^1.3.14",
21
- "mitata": "^1.0.34",
22
- "osmix": "0.2.2",
23
- "typescript": "npm:typescript@rc"
24
- },
25
- "dependencies": {
26
- "@osmix/core": "0.1.10",
27
- "@osmix/shared": "0.0.13"
28
- },
29
- "types": "./dist/index.d.ts"
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 breadth-first search algorithm.
2
+ * Bidirectional Dijkstra.
3
3
  *
4
- * Searches from both start and end simultaneously, terminating when the
5
- * frontiers meet. Very fast for finding any path, but does not guarantee
6
- * the shortest path. Use Dijkstra or A* when optimality is required.
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 type { PathSegment, RoutingAlgorithmFn } from "../types.ts"
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 BFS - fast path finding from both ends.
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
- graph,
22
- start,
23
- end,
24
- getWeight,
25
+ graph,
26
+ start,
27
+ end,
28
+ getWeight,
29
+ _getCoord,
30
+ _metric,
31
+ context,
25
32
  ) => {
26
- if (start === end) return [{ nodeIndex: start, cost: 0 }]
27
-
28
- // Forward search state
29
- const fDist = new Map<number, number>()
30
- const fPrev = new Map<number, PathSegment>()
31
- const fQueue: number[] = [start]
32
-
33
- // Backward search state
34
- const bDist = new Map<number, number>()
35
- const bPrev = new Map<number, PathSegment>()
36
- const bQueue: number[] = [end]
37
-
38
- fDist.set(start, 0)
39
- bDist.set(end, 0)
40
-
41
- let meetNode: number | null = null
42
-
43
- while (fQueue.length > 0 && bQueue.length > 0) {
44
- // Expand forward
45
- if (fQueue.length > 0) {
46
- const current = fQueue.shift()!
47
- const currentD = fDist.get(current)!
48
-
49
- // Check if backward search reached this node
50
- if (bDist.has(current)) {
51
- meetNode = current
52
- break
53
- }
54
-
55
- for (const edge of graph(current)) {
56
- const neighbor = edge.targetNodeIndex
57
- if (fDist.has(neighbor)) continue
58
-
59
- const newD = currentD + getWeight(edge)
60
- fDist.set(neighbor, newD)
61
- fPrev.set(neighbor, {
62
- nodeIndex: neighbor,
63
- wayIndex: edge.wayIndex,
64
- previousNodeIndex: current,
65
- cost: newD,
66
- })
67
- fQueue.push(neighbor)
68
-
69
- if (bDist.has(neighbor)) {
70
- meetNode = neighbor
71
- break
72
- }
73
- }
74
-
75
- if (meetNode !== null) break
76
- }
77
-
78
- // Expand backward
79
- if (bQueue.length > 0) {
80
- const current = bQueue.shift()!
81
- const currentD = bDist.get(current)!
82
-
83
- // Check if forward search reached this node
84
- if (fDist.has(current)) {
85
- meetNode = current
86
- break
87
- }
88
-
89
- for (const edge of graph(current)) {
90
- const neighbor = edge.targetNodeIndex
91
- if (bDist.has(neighbor)) continue
92
-
93
- const newD = currentD + getWeight(edge)
94
- bDist.set(neighbor, newD)
95
- bPrev.set(neighbor, {
96
- nodeIndex: neighbor,
97
- wayIndex: edge.wayIndex,
98
- previousNodeIndex: current,
99
- cost: newD,
100
- })
101
- bQueue.push(neighbor)
102
-
103
- if (fDist.has(neighbor)) {
104
- meetNode = neighbor
105
- break
106
- }
107
- }
108
-
109
- if (meetNode !== null) break
110
- }
111
- }
112
-
113
- if (meetNode === null) return null
114
-
115
- // Build forward path: start -> meet
116
- const forward: PathSegment[] = []
117
- let curr: number | undefined = meetNode
118
- while (curr !== undefined && curr !== start) {
119
- const seg = fPrev.get(curr)
120
- if (!seg) break
121
- forward.unshift(seg)
122
- curr = seg.previousNodeIndex
123
- }
124
- forward.unshift({ nodeIndex: start, cost: 0 })
125
-
126
- // Build backward path: meet -> end (reverse direction)
127
- // bDist represents distance FROM end TO node, so it decreases as we approach end.
128
- // Correct cost = forward cost to meet + (backward dist at meet - backward dist at curr)
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
+ };
@@ -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`: Fast BFS from both ends, finds *a* path (not always optimal).
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
- dijkstra,
21
- astar,
22
- bidirectional,
23
- }
20
+ dijkstra,
21
+ astar,
22
+ bidirectional,
23
+ };