@osmix/router 0.0.9 → 0.0.10
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 +82 -0
- package/package.json +10 -33
- package/test/algorithms.test.ts +466 -0
- package/test/router.bench.ts +257 -0
- package/test/router.test.ts +738 -0
- package/tsconfig.build.json +5 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { Osm } from "@osmix/core"
|
|
2
|
+
import { getFixtureFile, PBFs } from "@osmix/shared/fixtures"
|
|
3
|
+
import { bench, group, run } from "mitata"
|
|
4
|
+
import { fromPbf } from "osmix"
|
|
5
|
+
import { buildGraph } from "../src"
|
|
6
|
+
import { Router } from "../src/router"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Router Benchmark Suite using mitata
|
|
10
|
+
*
|
|
11
|
+
* Run with: bun test/router.bench.ts
|
|
12
|
+
*
|
|
13
|
+
* These benchmarks compare the performance of different routing algorithms
|
|
14
|
+
* using both real-world OSM data (Monaco) and synthetic grid networks.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Synthetic Grid Helper
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Creates a synthetic grid network for benchmarking.
|
|
23
|
+
*
|
|
24
|
+
* The grid consists of nodes arranged in a square pattern with
|
|
25
|
+
* horizontal and vertical ways connecting adjacent nodes.
|
|
26
|
+
*
|
|
27
|
+
* @param size - Number of nodes in each dimension (size x size total)
|
|
28
|
+
* @param spacing - Spacing between nodes in degrees (~100m per 0.001)
|
|
29
|
+
*/
|
|
30
|
+
function createSyntheticGrid(size: number, spacing: number): Osm {
|
|
31
|
+
const osm = new Osm({ id: "synthetic-grid" })
|
|
32
|
+
|
|
33
|
+
// Create nodes in a grid pattern
|
|
34
|
+
for (let row = 0; row < size; row++) {
|
|
35
|
+
for (let col = 0; col < size; col++) {
|
|
36
|
+
const nodeId = row * size + col + 1
|
|
37
|
+
osm.nodes.addNode({
|
|
38
|
+
id: nodeId,
|
|
39
|
+
lat: row * spacing,
|
|
40
|
+
lon: col * spacing,
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
osm.nodes.buildIndex()
|
|
46
|
+
osm.nodes.buildSpatialIndex()
|
|
47
|
+
|
|
48
|
+
// Create horizontal ways (connecting nodes in each row)
|
|
49
|
+
let wayId = 1
|
|
50
|
+
for (let row = 0; row < size; row++) {
|
|
51
|
+
const refs: number[] = []
|
|
52
|
+
for (let col = 0; col < size; col++) {
|
|
53
|
+
refs.push(row * size + col + 1)
|
|
54
|
+
}
|
|
55
|
+
osm.ways.addWay({
|
|
56
|
+
id: wayId++,
|
|
57
|
+
refs,
|
|
58
|
+
tags: { highway: "residential" },
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Create vertical ways (connecting nodes in each column)
|
|
63
|
+
for (let col = 0; col < size; col++) {
|
|
64
|
+
const refs: number[] = []
|
|
65
|
+
for (let row = 0; row < size; row++) {
|
|
66
|
+
refs.push(row * size + col + 1)
|
|
67
|
+
}
|
|
68
|
+
osm.ways.addWay({
|
|
69
|
+
id: wayId++,
|
|
70
|
+
refs,
|
|
71
|
+
tags: { highway: "residential" },
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
osm.ways.buildIndex()
|
|
76
|
+
osm.ways.buildSpatialIndex()
|
|
77
|
+
osm.buildIndexes()
|
|
78
|
+
|
|
79
|
+
return osm
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
// Setup
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
console.log("Setting up benchmark data...")
|
|
87
|
+
|
|
88
|
+
// Load Monaco PBF
|
|
89
|
+
const monacoPbf = PBFs["monaco"]!
|
|
90
|
+
const pbfData = await getFixtureFile(monacoPbf.url)
|
|
91
|
+
const monacoOsm = await fromPbf(pbfData, {
|
|
92
|
+
buildSpatialIndexes: ["node", "way"],
|
|
93
|
+
})
|
|
94
|
+
const monacoGraph = buildGraph(monacoOsm)
|
|
95
|
+
const monacoRouter = new Router(monacoOsm, monacoGraph)
|
|
96
|
+
|
|
97
|
+
// Create synthetic grid
|
|
98
|
+
const GRID_SIZE = 50
|
|
99
|
+
const GRID_SPACING = 0.001
|
|
100
|
+
const gridOsm = createSyntheticGrid(GRID_SIZE, GRID_SPACING)
|
|
101
|
+
const gridGraph = buildGraph(gridOsm)
|
|
102
|
+
const gridRouter = new Router(gridOsm, gridGraph)
|
|
103
|
+
|
|
104
|
+
// Pre-snap coordinates to node indexes for benchmarking
|
|
105
|
+
const monacoShortFrom = monacoGraph.findNearestRoutableNode(
|
|
106
|
+
monacoOsm,
|
|
107
|
+
[7.42, 43.735],
|
|
108
|
+
500,
|
|
109
|
+
)!.nodeIndex
|
|
110
|
+
const monacoShortTo = monacoGraph.findNearestRoutableNode(
|
|
111
|
+
monacoOsm,
|
|
112
|
+
[7.425, 43.738],
|
|
113
|
+
500,
|
|
114
|
+
)!.nodeIndex
|
|
115
|
+
const monacoLongFrom = monacoGraph.findNearestRoutableNode(
|
|
116
|
+
monacoOsm,
|
|
117
|
+
[7.41, 43.725],
|
|
118
|
+
500,
|
|
119
|
+
)!.nodeIndex
|
|
120
|
+
const monacoLongTo = monacoGraph.findNearestRoutableNode(
|
|
121
|
+
monacoOsm,
|
|
122
|
+
[7.44, 43.745],
|
|
123
|
+
500,
|
|
124
|
+
)!.nodeIndex
|
|
125
|
+
|
|
126
|
+
const gridShortFrom = gridGraph.findNearestRoutableNode(
|
|
127
|
+
gridOsm,
|
|
128
|
+
[0.01, 0.01],
|
|
129
|
+
200,
|
|
130
|
+
)!.nodeIndex
|
|
131
|
+
const gridShortTo = gridGraph.findNearestRoutableNode(
|
|
132
|
+
gridOsm,
|
|
133
|
+
[0.015, 0.015],
|
|
134
|
+
200,
|
|
135
|
+
)!.nodeIndex
|
|
136
|
+
const gridLongFrom = gridGraph.findNearestRoutableNode(
|
|
137
|
+
gridOsm,
|
|
138
|
+
[0, 0],
|
|
139
|
+
200,
|
|
140
|
+
)!.nodeIndex
|
|
141
|
+
const gridLongTo = gridGraph.findNearestRoutableNode(
|
|
142
|
+
gridOsm,
|
|
143
|
+
[(GRID_SIZE - 1) * GRID_SPACING, (GRID_SIZE - 1) * GRID_SPACING],
|
|
144
|
+
200,
|
|
145
|
+
)!.nodeIndex
|
|
146
|
+
|
|
147
|
+
console.log("Setup complete. Running benchmarks...\n")
|
|
148
|
+
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
// Monaco PBF Benchmarks
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
|
|
153
|
+
group("Monaco - Short Distance (~500m)", () => {
|
|
154
|
+
bench("Dijkstra", () => {
|
|
155
|
+
monacoRouter.route(monacoShortFrom, monacoShortTo, {
|
|
156
|
+
algorithm: "dijkstra",
|
|
157
|
+
})
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
bench("A*", () => {
|
|
161
|
+
monacoRouter.route(monacoShortFrom, monacoShortTo, {
|
|
162
|
+
algorithm: "astar",
|
|
163
|
+
})
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
bench("Bidirectional", () => {
|
|
167
|
+
monacoRouter.route(monacoShortFrom, monacoShortTo, {
|
|
168
|
+
algorithm: "bidirectional",
|
|
169
|
+
})
|
|
170
|
+
})
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
group("Monaco - Long Distance (~2km)", () => {
|
|
174
|
+
bench("Dijkstra", () => {
|
|
175
|
+
monacoRouter.route(monacoLongFrom, monacoLongTo, {
|
|
176
|
+
algorithm: "dijkstra",
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
bench("A*", () => {
|
|
181
|
+
monacoRouter.route(monacoLongFrom, monacoLongTo, {
|
|
182
|
+
algorithm: "astar",
|
|
183
|
+
})
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
bench("Bidirectional", () => {
|
|
187
|
+
monacoRouter.route(monacoLongFrom, monacoLongTo, {
|
|
188
|
+
algorithm: "bidirectional",
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
// Synthetic Grid Benchmarks
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
|
|
197
|
+
group("Grid 50x50 - Short Path", () => {
|
|
198
|
+
bench("Dijkstra", () => {
|
|
199
|
+
gridRouter.route(gridShortFrom, gridShortTo, {
|
|
200
|
+
algorithm: "dijkstra",
|
|
201
|
+
})
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
bench("A*", () => {
|
|
205
|
+
gridRouter.route(gridShortFrom, gridShortTo, {
|
|
206
|
+
algorithm: "astar",
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
bench("Bidirectional", () => {
|
|
211
|
+
gridRouter.route(gridShortFrom, gridShortTo, {
|
|
212
|
+
algorithm: "bidirectional",
|
|
213
|
+
})
|
|
214
|
+
})
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
group("Grid 50x50 - Long Path (corner to corner)", () => {
|
|
218
|
+
bench("Dijkstra", () => {
|
|
219
|
+
gridRouter.route(gridLongFrom, gridLongTo, {
|
|
220
|
+
algorithm: "dijkstra",
|
|
221
|
+
})
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
bench("A*", () => {
|
|
225
|
+
gridRouter.route(gridLongFrom, gridLongTo, {
|
|
226
|
+
algorithm: "astar",
|
|
227
|
+
})
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
bench("Bidirectional", () => {
|
|
231
|
+
gridRouter.route(gridLongFrom, gridLongTo, {
|
|
232
|
+
algorithm: "bidirectional",
|
|
233
|
+
})
|
|
234
|
+
})
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
// ---------------------------------------------------------------------------
|
|
238
|
+
// Router Initialization Benchmarks
|
|
239
|
+
// ---------------------------------------------------------------------------
|
|
240
|
+
|
|
241
|
+
group("Router Initialization", () => {
|
|
242
|
+
bench("Monaco OSM (14k nodes, 3k ways)", () => {
|
|
243
|
+
new Router(monacoOsm, buildGraph(monacoOsm))
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
bench("Grid 50x50 (2.5k nodes)", () => {
|
|
247
|
+
new Router(gridOsm, buildGraph(gridOsm))
|
|
248
|
+
})
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
// ---------------------------------------------------------------------------
|
|
252
|
+
// Run benchmarks
|
|
253
|
+
// ---------------------------------------------------------------------------
|
|
254
|
+
|
|
255
|
+
await run({
|
|
256
|
+
colors: true,
|
|
257
|
+
})
|