roadgraph-router-v2 0.1.0 → 0.3.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.d.ts CHANGED
@@ -38,6 +38,37 @@ interface RoadGraph {
38
38
  edgeDistanceScale?: number;
39
39
  /** Edge duration values are multiplied by this scale. Omitted means seconds. */
40
40
  edgeDurationScale?: number;
41
+ /**
42
+ * The routing profile this graph was compiled for, as a `ProfileId`. Absent
43
+ * means the graph predates profile metadata, and every such graph is a car
44
+ * graph. A graph only answers for the vehicle it was compiled for: a car
45
+ * graph has no footpaths in it to walk along.
46
+ */
47
+ profileId?: number;
48
+ /**
49
+ * The profile's speed ceiling in km/h, carried so a reader can bound its A*
50
+ * heuristic and its tile budget without knowing the profile's rules. Absent
51
+ * falls back to the profile's own ceiling.
52
+ */
53
+ maxSpeedKph?: number;
54
+ /**
55
+ * One bit per edge saying whether this profile may use it, present only on a
56
+ * graph assembled from a shared topology that several profiles share. Absent
57
+ * means every edge is usable. See `./access`.
58
+ */
59
+ edgeAccess?: Uint8Array;
60
+ /**
61
+ * The physical limits in force on the few edges that state any. Present only
62
+ * for a profile whose routes depend on the vehicle's size. See `./limits`.
63
+ */
64
+ edgeLimits?: EdgeLimits;
65
+ /** Conditional restrictions for the edges that carry one; absent when none do. */
66
+ edgeConditions?: EdgeConditions;
67
+ /**
68
+ * One bit per turn restriction, saying whether this vehicle obeys it. The
69
+ * pairs come first, then the sequences. Absent means all of them apply.
70
+ */
71
+ restrictionMask?: Uint8Array;
41
72
  /** Optional intermediate vertices per directed edge, stored using coordinateScale. */
42
73
  edgeShapeOffsets?: Uint32Array;
43
74
  edgeShapeLatitudes?: Int32Array;
@@ -71,6 +102,14 @@ interface RouteSnap {
71
102
  point: Coordinate;
72
103
  /** How far the requested coordinate was from that road. */
73
104
  distanceMeters: number;
105
+ /**
106
+ * How far the route actually travels along that road, for an end that
107
+ * begins or finishes part-way along one. Zero where the end sits on a
108
+ * junction, and zero at the destination of a route that never leaves a
109
+ * single road, whose whole length is reported at the origin instead so
110
+ * that adding the two up gives the route and not twice the route.
111
+ */
112
+ travelledMeters?: number;
74
113
  }
75
114
  interface RouteResult {
76
115
  geometry: GeoJsonLineString;
@@ -92,6 +131,71 @@ declare class RouterError extends Error {
92
131
  readonly code: RouterErrorCode;
93
132
  constructor(code: RouterErrorCode, message: string);
94
133
  }
134
+ /**
135
+ * Signed height, width, length and weight limits, for the edges that have them.
136
+ *
137
+ * A side table rather than four columns over every edge: the overwhelming
138
+ * majority of roads state no limit at all, so columns would spend four bytes
139
+ * per edge saying nothing nearly everywhere. `edges` is sorted, and the four
140
+ * arrays run alongside it. A zero means the limit is not stated, which is not
141
+ * the same as a limit of zero.
142
+ */
143
+ interface EdgeLimits {
144
+ edges: Uint32Array;
145
+ /** Tenths of a metre. */
146
+ heightDecimetres: Uint8Array;
147
+ widthDecimetres: Uint8Array;
148
+ lengthDecimetres: Uint8Array;
149
+ /** Half-tonnes. */
150
+ weightHalfTonnes: Uint8Array;
151
+ }
152
+ /**
153
+ * Conditional restrictions for the few edges carrying one: the raw
154
+ * `value @ condition` text, kept as written and parsed when a bundle opens.
155
+ *
156
+ * A side table for the same reason the limits are one — a country has a few
157
+ * thousand of these among millions of edges. The text is deduplicated into
158
+ * `values`, because the same working-hours ban is repeated along every segment
159
+ * of the street it applies to. One edge may have several rows: a lorry ban and
160
+ * a night speed limit are both conditional and both apply.
161
+ */
162
+ interface EdgeConditions {
163
+ /** Sorted by edge, then by kind; an edge may appear more than once. */
164
+ edges: Uint32Array;
165
+ /** What the value means: 0 is access, 1 is a speed limit in km/h. */
166
+ kinds: Uint8Array;
167
+ /** Index into `values`. */
168
+ rules: Uint16Array;
169
+ /** The deduplicated conditional values, as tagged. */
170
+ values: string[];
171
+ }
172
+ /**
173
+ * A tile's vehicle-independent half: everything a v7 `shared` chunk holds.
174
+ * It is not routable on its own — a profile supplies the durations.
175
+ */
176
+ type SharedRoadGraph = Omit<RoadGraph, "edgeDurationsSeconds" | "edgeDurationScale" | "profileId" | "maxSpeedKph" | "edgeAccess">;
177
+ /** A tile's vehicle-dependent half: what a v7 `profile` chunk holds. */
178
+ interface ProfileChunk {
179
+ profileId: number;
180
+ maxSpeedKph: number;
181
+ edgeDurationScale: number;
182
+ edgeDurationsSeconds: Uint16Array;
183
+ /** One bit per edge; absent means this profile may use every road in the tile. */
184
+ edgeAccess?: Uint8Array;
185
+ /** Signed limits for the few edges that state any; absent for vehicles whose size does not matter. */
186
+ edgeLimits?: EdgeLimits;
187
+ /** Conditional restrictions for the edges that carry one; absent when none do. */
188
+ edgeConditions?: EdgeConditions;
189
+ /**
190
+ * One bit per turn restriction the shared chunk holds, saying whether this
191
+ * vehicle obeys it. `except=hgv` lifts a restriction for lorries and not for
192
+ * cars, so a shared restriction list needs a per-vehicle answer. Absent
193
+ * means every restriction applies.
194
+ */
195
+ restrictionMask?: Uint8Array;
196
+ /** How many restrictions that mask covers: the pairs, then the sequences. */
197
+ restrictionCount?: number;
198
+ }
95
199
 
96
200
  declare function encodeGraph(graph: RoadGraph): ArrayBuffer;
97
201
  declare function decodeGraph(buffer: ArrayBuffer): RoadGraph;
@@ -110,6 +214,27 @@ type CompactEncodeOptions = {
110
214
  layout?: "packed" | "legacy";
111
215
  };
112
216
  declare function encodeCompactGraph(graph: RoadGraph, options?: CompactEncodeOptions): ArrayBuffer;
217
+ /**
218
+ * The parts of a tile that do not depend on the vehicle. Identical to the v6
219
+ * layout except that the duration column is left out, because that is the one
220
+ * per-edge value a profile changes.
221
+ */
222
+ declare function encodeSharedGraph(graph: RoadGraph): ArrayBuffer;
223
+ /**
224
+ * The vehicle-dependent half of a tile: the duration of every edge for this
225
+ * profile, and which edges it may use at all. An access mask that lets
226
+ * everything through is left out, which is the usual case for the profile a
227
+ * tile's topology was built around.
228
+ */
229
+ declare function encodeProfileChunk(chunk: ProfileChunk): ArrayBuffer;
230
+ declare function decodeProfileChunk(buffer: ArrayBuffer): ProfileChunk;
231
+ /**
232
+ * The vehicle-independent half of a tile. Not routable on its own: every
233
+ * duration is zero until a profile chunk supplies them.
234
+ */
235
+ declare function decodeSharedGraph(buffer: ArrayBuffer): SharedRoadGraph;
236
+ /** Joins a tile's two halves into the routable graph the rest of the library uses. */
237
+ declare function combineProfiledGraph(shared: SharedRoadGraph, profile: ProfileChunk): RoadGraph;
113
238
 
114
239
  /**
115
240
  * LEB128 variable-length integers, with zigzag for signed values.
@@ -155,6 +280,529 @@ type CompactGraphOptions = {
155
280
  */
156
281
  declare function compactGraph(graph: RoadGraph, options?: CompactGraphOptions): RoadGraph;
157
282
 
283
+ /**
284
+ * Which edges a profile may use, as one bit per edge.
285
+ *
286
+ * A multi-profile bundle stores the road network once and the vehicle-specific
287
+ * parts separately, so the shared topology holds every road any of its
288
+ * profiles can drive. A profile that cannot use a road marks its bit clear
289
+ * rather than having the edge removed: edge numbers are what turn restrictions
290
+ * and shape references are written against, so they have to stay put.
291
+ */
292
+ declare function accessMaskFor(edges: number, allowed: (edge: number) => boolean): Uint8Array<ArrayBuffer>;
293
+ /** An absent mask means every edge is usable, which is what a single-profile graph holds. */
294
+ declare function edgeAllowed(mask: Uint8Array | undefined, edge: number): boolean;
295
+ /** True when the mask lets every edge through, in which case it need not be stored. */
296
+ declare function allowsEverything(mask: Uint8Array, edges: number): boolean;
297
+
298
+ /**
299
+ * The two constants an A* search needs from a graph, measured once per graph
300
+ * rather than assumed.
301
+ *
302
+ * Both exist to keep the heuristic admissible — never larger than the true
303
+ * remaining cost — while making it as cheap and as tight as the data allows.
304
+ * An inadmissible heuristic does not merely slow a search down; it lets A*
305
+ * settle for a route that is not the best one.
306
+ */
307
+
308
+ type SearchBounds = {
309
+ /**
310
+ * The fastest any edge in this graph is actually travelled. Quantising a
311
+ * short edge's duration downwards can imply a speed above the profile's
312
+ * own ceiling, and dividing by the lower number would overestimate what
313
+ * remains, so the measured value is used rather than the declared one.
314
+ */
315
+ maxSpeedMps: number;
316
+ /**
317
+ * Scales a longitude difference into metres using the highest latitude in
318
+ * the graph, where a degree of longitude is shortest. Under-estimating that
319
+ * everywhere keeps the straight-line estimate below the true distance.
320
+ */
321
+ longitudeMetresPerDegree: number;
322
+ };
323
+ declare function searchBoundsFor(graph: RoadGraph): SearchBounds;
324
+ /**
325
+ * A lower bound on the great-circle distance between two points, without the
326
+ * four trigonometric calls a haversine needs. The heuristic asks for this once
327
+ * per relaxation — millions of times on a long route — and only ever needs a
328
+ * number that cannot exceed the truth.
329
+ */
330
+ declare function straightLineAtMost(latitudeA: number, longitudeA: number, latitudeB: number, longitudeB: number, longitudeMetresPerDegree: number): number;
331
+
332
+ /**
333
+ * Which parts of a graph are joined to which.
334
+ *
335
+ * A road network is not one piece. Service roads inside a compound, a path
336
+ * fragment behind a fence, a stretch clipped by the edge of a tile — all sit
337
+ * apart from the rest, and a point a couple of metres from one of them snaps
338
+ * to it rather than to the road just beyond. The route then fails although a
339
+ * perfectly good road was in reach.
340
+ *
341
+ * Labels are weak: an edge joins its two nodes whichever way it points. That
342
+ * is deliberately the cheap question. Two nodes in different pieces certainly
343
+ * cannot reach each other, which is what the snapper needs to know; two in the
344
+ * same piece usually can, and where a one-way trap says otherwise the search
345
+ * finds out for itself.
346
+ */
347
+
348
+ /** One label per node. Nodes sharing a label are joined, ignoring direction. */
349
+ declare function componentsFor(graph: RoadGraph): Int32Array;
350
+ /** How many nodes carry each label, so the smaller piece can be recognised. */
351
+ declare function componentSizes(labels: Int32Array): Map<number, number>;
352
+
353
+ /**
354
+ * Roads the caller knows are shut.
355
+ *
356
+ * A compiled bundle is as current as the extract it came from: roadworks, a
357
+ * burst main, a festival closing the square are all things that happened
358
+ * after it was built, and nothing inside it can know about them. What it can
359
+ * do is let whoever does know say so at the time of asking.
360
+ *
361
+ * An area rather than a road, because a caller reading an incident feed has a
362
+ * place and a radius, not an edge number. Anything the circle touches is
363
+ * closed to the search and to snapping, exactly as a tagged restriction would
364
+ * be, so a route neither drives through a closure nor starts in the middle of
365
+ * one.
366
+ */
367
+
368
+ /** A circle of road that cannot be driven, whatever the map says. */
369
+ type AvoidArea = {
370
+ lat: number;
371
+ lng: number;
372
+ /** How far the closure reaches from that point. */
373
+ radiusMeters: number;
374
+ };
375
+ /**
376
+ * Whether any part of this edge lies inside the circle.
377
+ *
378
+ * The whole of each segment counts, not only its ends: a motorway crossing a
379
+ * closed junction may have no shape point anywhere near it, and testing the
380
+ * ends alone would drive a route straight through.
381
+ */
382
+ declare function touchesArea(graph: RoadGraph, edge: number, area: AvoidArea, centre?: Coordinate): boolean;
383
+ /**
384
+ * The access mask with every road the caller has closed cleared as well.
385
+ *
386
+ * Only the roads near each circle are looked at, so closing a junction costs
387
+ * the same whether the graph holds a town or a country.
388
+ */
389
+ declare function accessAvoiding(graph: RoadGraph, areas: AvoidArea[] | undefined, base?: Uint8Array): Uint8Array | undefined;
390
+ /** How many roads these closures take away, for reporting rather than routing. */
391
+ declare function countAvoided(graph: RoadGraph, areas: AvoidArea[] | undefined): number;
392
+
393
+ /**
394
+ * Reading a speed off an OSM tag.
395
+ *
396
+ * It lives on its own because both the profiles and the conditional
397
+ * restrictions need it, and a conditional limit is written in the same units
398
+ * as a plain one: `maxspeed:conditional = 30 mph @ (Mo-Fr 07:00-19:00)` means
399
+ * 30 mph, not 30 km/h.
400
+ */
401
+ /** Kilometres per hour, or null when the value is not a speed this reads. */
402
+ declare function parseMaxSpeedKph(value: string | undefined): number | null;
403
+
404
+ /**
405
+ * Routing profiles turn raw OSM tags into routable facts: may a vehicle use
406
+ * this way, in which directions, and how fast. Keeping the rules behind one
407
+ * interface lets the compiler stay generic — it never inspects a tag itself —
408
+ * and lets the rules be unit-tested without a PBF file.
409
+ */
410
+
411
+ type OsmTags = Record<string, string | undefined>;
412
+ /** How a profile may traverse one OSM way. */
413
+ type WayTraversal = {
414
+ /**
415
+ * Conditional restrictions tagged on the way, kept as written for the
416
+ * router to evaluate against a departure time. They apply to both
417
+ * directions: OSM states these per vehicle class, not per direction.
418
+ */
419
+ conditions?: {
420
+ kind: number;
421
+ value: string;
422
+ }[];
423
+ /** Travel from the first node reference towards the last is allowed. */
424
+ forward: boolean;
425
+ /** Travel from the last node reference towards the first is allowed. */
426
+ backward: boolean;
427
+ /** Travel speed in km/h in each direction; both are clamped to the profile range. */
428
+ forwardSpeedKph: number;
429
+ backwardSpeedKph: number;
430
+ /** Physical limits in force along this way, where the data states any. */
431
+ forwardLimits?: VehicleLimits;
432
+ backwardLimits?: VehicleLimits;
433
+ };
434
+ /** A turn restriction relation reduced to the only two cases routing cares about. */
435
+ type TurnRestrictionKind = "no" | "only";
436
+ /**
437
+ * A physical limit OSM states for a way. Every field is optional because an
438
+ * untagged limit is unknown, not absent: most bridges carry no `maxheight` at
439
+ * all, and treating silence as "no limit" is the assumption this library makes
440
+ * and says so, rather than inventing a number.
441
+ */
442
+ type VehicleLimits = {
443
+ heightMeters?: number;
444
+ widthMeters?: number;
445
+ lengthMeters?: number;
446
+ weightTonnes?: number;
447
+ };
448
+ type ParsedTurnRestriction = {
449
+ kind: TurnRestrictionKind;
450
+ /** The raw OSM value, kept for diagnostics only. */
451
+ value: string;
452
+ };
453
+ /**
454
+ * Stable numeric ids written into compiled graphs. They are part of the file
455
+ * format, so a value is never reused for a different profile; 0 is reserved
456
+ * for graphs compiled before profiles were recorded, which were all cars.
457
+ */
458
+ declare const ProfileId: {
459
+ readonly unspecified: 0;
460
+ readonly car: 1;
461
+ readonly truck: 2;
462
+ readonly motorcycle: 3;
463
+ readonly bicycle: 4;
464
+ readonly pedestrian: 5;
465
+ };
466
+ type ProfileId = (typeof ProfileId)[keyof typeof ProfileId];
467
+ interface RoutingProfile {
468
+ readonly name: string;
469
+ /** The id this profile is stored under. See `ProfileId`. */
470
+ readonly id: ProfileId;
471
+ /** Way tag keys the compiler must request from the PBF reader. */
472
+ readonly wayTagKeys: readonly string[];
473
+ /** Relation tag keys the compiler must request from the PBF reader. */
474
+ readonly relationTagKeys: readonly string[];
475
+ /** The fastest speed the profile can ever return, used for admissible A* heuristics. */
476
+ readonly maxSpeedKph: number;
477
+ /** Returns null when the way is not usable by this profile at all. */
478
+ evaluateWay(tags: OsmTags): WayTraversal | null;
479
+ /** Returns null when the relation is not a turn restriction this profile obeys. */
480
+ evaluateTurnRestriction(tags: OsmTags): ParsedTurnRestriction | null;
481
+ /**
482
+ * Whether this profile's routes depend on the vehicle's physical size, which
483
+ * tells the compiler to keep the limits `evaluateWay` reports.
484
+ */
485
+ readonly usesPhysicalLimits?: boolean;
486
+ /** Node tag keys the compiler must request, for barriers. */
487
+ readonly nodeTagKeys?: readonly string[];
488
+ /**
489
+ * Whether this traveller gets past a barrier on the road. Returns true when
490
+ * the node is not a barrier at all, so an absent answer means "carry on".
491
+ */
492
+ evaluateBarrier?(tags: OsmTags): boolean;
493
+ }
494
+ /**
495
+ * Reads a `maxheight`-style value in metres. Feet and inches are accepted
496
+ * because they are how the limit is written wherever it is signed that way;
497
+ * anything that is not a measurement reads as unknown rather than as zero,
498
+ * which would close the road to everything.
499
+ */
500
+ declare function parseLengthMeters(value: string | undefined): number | null;
501
+ /** Reads a `maxweight`-style value in tonnes. A bare number is tonnes, as OSM defines it. */
502
+ declare function parseWeightTonnes(value: string | undefined): number | null;
503
+ declare const carRoutingProfile: RoutingProfile;
504
+ /**
505
+ * Motorcycles use the same roads and the same speed limits as cars almost
506
+ * everywhere, so the difference is which tags apply to them: `motorcycle=no`
507
+ * closes a road a car may still use, `oneway:motorcycle=no` opens one, and
508
+ * `except=motorcar` on a turn restriction does not let a motorcycle through.
509
+ */
510
+ declare const motorcycleRoutingProfile: RoutingProfile;
511
+ /**
512
+ * Heavy goods vehicles. `hgv` is the OSM key for them, and it sits below
513
+ * `motor_vehicle` in the same ladder every motor vehicle uses.
514
+ *
515
+ * Two things set a lorry apart. It is slower: a heavy vehicle is capped below
516
+ * the posted limit almost everywhere, so a truck graph that reused car
517
+ * durations would give wrong arrival times on roads it is perfectly entitled
518
+ * to use. And it is large: height, width, length and weight limits decide
519
+ * whether a road is usable at all, which depends on the lorry rather than on
520
+ * the map, so those limits are carried through to be applied at routing time.
521
+ */
522
+ declare const truckRoutingProfile: RoutingProfile;
523
+ declare const routingProfiles: Record<string, RoutingProfile>;
524
+ declare function getRoutingProfile(name: string): RoutingProfile;
525
+ /** The profile a stored id refers to, or null when this build does not know it. */
526
+ declare function getRoutingProfileById(id: number): RoutingProfile | null;
527
+ /**
528
+ * A graph compiled before profiles were recorded is a car graph, which is what
529
+ * every bundle published up to v0.1.0 holds.
530
+ */
531
+ declare function profileOf(graph: {
532
+ profileId?: number;
533
+ }): RoutingProfile;
534
+ /**
535
+ * The speed ceiling a search may assume, in metres per second. The value
536
+ * stored in the graph wins over this build's table, so a bundle compiled by a
537
+ * newer version still bounds an older reader's heuristic correctly.
538
+ */
539
+ declare function maxSpeedMpsFor(graph: {
540
+ profileId?: number;
541
+ maxSpeedKph?: number;
542
+ }): number;
543
+
544
+ /**
545
+ * The lorry a truck route iscalculated for.
546
+ *
547
+ * A caller who says nothing still gets a vehicle, because refusing to route
548
+ * until a form is filled in is worse than routing for a stated default. The
549
+ * danger is not having a default, it is a default nobody can see, so the
550
+ * resolved dimensions ride on every truck route.
551
+ *
552
+ * There is no globally neutral lorry. EU general circulation is 2.55m wide,
553
+ * 16.5m long articulated and 40t on a five-axle vehicle no taller than 4m;
554
+ * a US semi is wider and taller at 2.59m and about 4.11m but lighter at
555
+ * 36.3t, and Nordic combinations run longer and heavier still. Guessing large
556
+ * on every axis is not safe either: `maxheight=4.0` and `maxweight=40` are
557
+ * among the commonly tagged limits in Europe, so a 4.2m default would refuse
558
+ * bridges an ordinary lorry crosses daily. The presets below are therefore
559
+ * vehicle classes a caller picks, not a guess about where they are.
560
+ */
561
+
562
+ type VehicleDimensions = {
563
+ heightMeters: number;
564
+ widthMeters: number;
565
+ lengthMeters: number;
566
+ weightTonnes: number;
567
+ };
568
+ /**
569
+ * Stable ids, so a bundle can name its default vehicle in one byte. They are
570
+ * part of the file format: a value is never reused for a different class, and
571
+ * 0 means the bundle states no default, which falls back to the library's.
572
+ */
573
+ declare const VehiclePresetId: Record<string, number>;
574
+ declare function vehiclePresetName(id: number): string | null;
575
+ declare const VEHICLE_PRESETS: Record<string, VehicleDimensions>;
576
+ /**
577
+ * What a truck route uses when nothing else says otherwise. An articulated
578
+ * lorry is what most people mean by "truck" for freight, and it matches the
579
+ * envelope European road data is implicitly tagged against.
580
+ */
581
+ declare const DEFAULT_VEHICLE_PRESET = "articulated";
582
+ /** A preset name, a full set of dimensions, or a preset with fields overridden. */
583
+ type VehicleInput = string | Partial<VehicleDimensions> & {
584
+ preset?: string;
585
+ };
586
+ declare function vehiclePreset(name: string): VehicleDimensions;
587
+ /**
588
+ * Resolves what a caller asked for against a preset, so `{ preset:
589
+ * "articulated", heightMeters: 4.3 }` is a standard lorry with a taller load
590
+ * rather than a vehicle with three dimensions missing.
591
+ */
592
+ declare function resolveVehicle(input: VehicleInput | undefined, fallbackPreset?: string): VehicleDimensions;
593
+
594
+ /**
595
+ * Turning signed limits into the handful of bytes a bundle stores, and back
596
+ * into a yes or no for one lorry.
597
+ *
598
+ * Quantisation always rounds a limit *down*, so a stored value never permits
599
+ * what the sign forbids. A limit too large to store is stored as "not stated"
600
+ * rather than clamped, because a clamped value would forbid what the sign
601
+ * permits, and a limit that high restricts nothing any lorry cares about.
602
+ */
603
+
604
+ /** Zero means "not stated"; a limit above what fits is treated the same way. */
605
+ declare function toDecimetres(metres: number | undefined): number;
606
+ declare function toHalfTonnes(tonnes: number | undefined): number;
607
+ declare const fromDecimetres: (value: number) => number;
608
+ declare const fromHalfTonnes: (value: number) => number;
609
+ /** Collects the per-edge limits a compiler gathered into the stored side table. */
610
+ declare function packEdgeLimits(entries: {
611
+ edge: number;
612
+ limits: VehicleLimits;
613
+ }[]): EdgeLimits | undefined;
614
+ /**
615
+ * The access mask a search should use for one lorry: the profile's own mask
616
+ * with the roads this vehicle is too big or too heavy for cleared as well.
617
+ * Roads stating no limit stay open, which is the assumption this library makes
618
+ * and documents: an untagged bridge is not a measured one.
619
+ */
620
+ declare function accessForVehicle(graph: RoadGraph, vehicle: VehicleDimensions | undefined): Uint8Array | undefined;
621
+ /** How many edges this vehicle may not use, for reporting rather than routing. */
622
+ declare function countBlocked(graph: RoadGraph, vehicle: VehicleDimensions): number;
623
+
624
+ /**
625
+ * Conditional restrictions: `hgv:conditional = no @ (Mo-Fr 07:00-19:00)`.
626
+ *
627
+ * A road can be closed overnight, a square pedestrianised on Sundays, a bridge
628
+ * limited to 7.5 tonnes only for vehicles over a certain length. Until now
629
+ * every one of those read as permanently open, which is the limitation most
630
+ * likely to make a route wrong rather than merely longer.
631
+ *
632
+ * The value is a list of `value @ condition` pairs separated by semicolons,
633
+ * and where several match, OpenStreetMap says the **last** one wins — the
634
+ * convention being to write the general rule first and the specific one last.
635
+ *
636
+ * Not every condition can be answered. Weather, a driver's occupation and a
637
+ * stay duration are not things a compiled graph knows, and a rule resting on
638
+ * one is kept but marked, so a caller can count what was not understood
639
+ * instead of being told silently that a road is open.
640
+ */
641
+
642
+ /**
643
+ * How many distinct conditional values one table can hold. The stored index
644
+ * is sixteen bits wide, so the values are numbered 0 to 65535.
645
+ */
646
+ declare const MAX_STORED_VALUES = 65536;
647
+ /** When a journey passes, in the local time of the road it passes on. */
648
+ type TravelTime = {
649
+ /** 0 is Monday, 6 is Sunday. */
650
+ weekday: number;
651
+ /** Minutes since midnight, 0 to 1439. */
652
+ minutes: number;
653
+ /** 1 to 12, for a seasonal rule. */
654
+ month: number;
655
+ /**
656
+ * The day of the month, 1 to 31. Only a span running past midnight needs
657
+ * it, to know whether the morning it ends on began in the month before.
658
+ * Leaving it out treats such a morning as belonging to the current month,
659
+ * which is right on every day but the first.
660
+ */
661
+ dayOfMonth?: number;
662
+ /** Whether it is a public holiday there, which only a caller can know. */
663
+ publicHoliday?: boolean;
664
+ };
665
+ /** A span of minutes within a day, which may run past midnight. */
666
+ type MinuteSpan = {
667
+ from: number;
668
+ to: number;
669
+ };
670
+ /** An inclusive range of weekdays, months, or anything else counted in order. */
671
+ type Range = {
672
+ from: number;
673
+ to: number;
674
+ };
675
+ /** A measurement of the vehicle compared against a number, as `weight>7.5`. */
676
+ type VehicleComparison = {
677
+ property: "weight" | "height" | "width" | "length";
678
+ operator: "<" | "<=" | ">" | ">=" | "=";
679
+ amount: number;
680
+ };
681
+ /** One `(...)` group: every part present must hold for the group to match. */
682
+ type Condition = {
683
+ days?: Range[];
684
+ times?: MinuteSpan[];
685
+ months?: Range[];
686
+ publicHoliday?: boolean;
687
+ vehicle?: VehicleComparison[];
688
+ /**
689
+ * Circumstances this library cannot know by itself: the weather, who is
690
+ * driving, what for. They are read from the tag and answered only by a
691
+ * caller who says so; unanswered, the condition does not hold.
692
+ */
693
+ circumstances?: string[];
694
+ /**
695
+ * The part of the condition this parser could not read at all — a stay
696
+ * duration, a form of words nobody has taught it. A condition carrying one
697
+ * is never matched, and is counted so it can be reported rather than
698
+ * quietly ignored.
699
+ */
700
+ unreadable?: string;
701
+ };
702
+ /**
703
+ * What the caller knows that the map does not: whether it is raining, who is
704
+ * at the wheel, what the journey is for. A conditional rule resting on one of
705
+ * these is ignored unless the answer is given here.
706
+ */
707
+ type Circumstances = {
708
+ /** Named states of the road or sky, such as `wet`, `snow`, `ice`. */
709
+ conditions?: string[];
710
+ /** Who is driving, such as `disabled`, `resident`, `emergency`. */
711
+ userGroups?: string[];
712
+ /** Why, such as `delivery`, `destination`, `agricultural`. */
713
+ purposes?: string[];
714
+ };
715
+ type ConditionalRule = {
716
+ /** What applies when the condition holds: `no`, `destination`, `30`, `7.5`. */
717
+ value: string;
718
+ /** Any of these matching makes the rule apply, as `Mo-Fr 07:00-19:00, Sa 08:00-12:00`. */
719
+ conditions: Condition[];
720
+ };
721
+ /**
722
+ * Reads a `*:conditional` value into its rules. An empty list means the value
723
+ * was not a conditional expression at all.
724
+ */
725
+ declare function parseConditional(value: string | undefined): ConditionalRule[];
726
+ /** Whether one condition group holds at this moment, for this vehicle. */
727
+ declare function conditionHolds(condition: Condition, when: TravelTime, vehicle?: VehicleDimensions, circumstances?: Circumstances): boolean;
728
+ /**
729
+ * What a conditional value comes to at this moment, or null when none of its
730
+ * rules applies. Where several match, the last wins, which is what
731
+ * OpenStreetMap says and why the convention is general first, specific last.
732
+ */
733
+ declare function conditionalValue(rules: ConditionalRule[], when: TravelTime, vehicle?: VehicleDimensions, circumstances?: Circumstances): string | null;
734
+ /**
735
+ * How many of these rules rest on something this parser cannot read at all.
736
+ * A rule resting on the weather or on who is driving is readable and is not
737
+ * counted here; it simply needs a caller to answer it.
738
+ */
739
+ declare function unreadableConditions(rules: ConditionalRule[]): number;
740
+ /**
741
+ * The moment a journey is travelled.
742
+ *
743
+ * Without a time zone the date is read as the machine reads it, which is
744
+ * right when the roads and the reader are in the same place and wrong by
745
+ * however many hours when they are not. Naming the zone the roads are in —
746
+ * `"Europe/Lisbon"` — reads the date there instead, which is what a
747
+ * restriction written in local time means.
748
+ *
749
+ * The zone names are the IANA ones the platform already knows; nothing is
750
+ * bundled for this.
751
+ */
752
+ declare function travelTimeFrom(date: Date, publicHoliday?: boolean, timeZone?: string): TravelTime;
753
+ /** What a stored conditional row says: an access rule or a speed limit. */
754
+ declare const ConditionKind: {
755
+ readonly access: 0;
756
+ readonly maxspeed: 1;
757
+ };
758
+ /** Collects what a compiler found into the stored side table. */
759
+ declare function packEdgeConditions(entries: {
760
+ edge: number;
761
+ kind: number;
762
+ value: string;
763
+ }[]): EdgeConditions | undefined;
764
+ /** A bundle's conditional values parsed once, rather than per edge per search. */
765
+ type ParsedConditions = {
766
+ table: EdgeConditions;
767
+ parsed: ConditionalRule[][];
768
+ };
769
+ declare function parseEdgeConditions(table: EdgeConditions | undefined): ParsedConditions | undefined;
770
+ /**
771
+ * What the conditional rules say about one edge at one moment: whether it is
772
+ * closed, and any speed limit that applies.
773
+ *
774
+ * `destination` and the permit-style values count as closed because this
775
+ * library has no notion of a journey's purpose, and treating "only if you live
776
+ * here" as open would route strangers down a residents' street.
777
+ */
778
+ declare function conditionsOnEdge(conditions: ParsedConditions, edge: number, when: TravelTime, vehicle?: VehicleDimensions, circumstances?: Circumstances): {
779
+ closed: boolean;
780
+ speedKph: number | undefined;
781
+ };
782
+ /**
783
+ * The access mask for a departure time: the graph's own with the roads shut at
784
+ * that moment cleared as well.
785
+ *
786
+ * Only closures are applied. A road whose plain tags already forbid it stays
787
+ * forbidden even where a conditional value would open it, because the stored
788
+ * mask has already dropped it and reopening one would need the compiler to
789
+ * keep roads it currently discards. That errs towards refusing a road that is
790
+ * in fact usable, which is the direction this library errs in everywhere else.
791
+ */
792
+ declare function accessAtTime(graph: Pick<RoadGraph, "edgeTargets" | "edgeAccess">, conditions: ParsedConditions | undefined, when: TravelTime | undefined, vehicle?: VehicleDimensions, base?: Uint8Array, circumstances?: Circumstances): Uint8Array | undefined;
793
+ /** How many edges this moment closes, for reporting rather than routing. */
794
+ declare function countClosedAt(graph: Pick<RoadGraph, "edgeTargets" | "edgeAccess">, conditions: ParsedConditions | undefined, when: TravelTime, vehicle?: VehicleDimensions): number;
795
+ declare function conditionsOf(graph: Pick<RoadGraph, "edgeConditions">): ParsedConditions | undefined;
796
+ /**
797
+ * The edges a conditional speed limit slows at this moment, as whole seconds.
798
+ *
799
+ * A conditional limit may only slow an edge, never speed it up. A faster
800
+ * stored duration is left alone, because the A* bound is built from the
801
+ * profile's ceiling and an edge quicker than the graph says would break the
802
+ * guarantee that the route found is the best one.
803
+ */
804
+ declare function conditionalDurations(graph: Pick<RoadGraph, "edgeTargets" | "edgeDistancesMeters" | "edgeDistanceScale" | "edgeDurationsSeconds" | "edgeDurationScale">, conditions: ParsedConditions | undefined, when: TravelTime, vehicle?: VehicleDimensions, circumstances?: Circumstances): Map<number, number> | undefined;
805
+
158
806
  /**
159
807
  * The nearest routing node to a coordinate. After topology simplification a
160
808
  * node is a junction rather than an arbitrary point on a road, so on a long
@@ -170,9 +818,235 @@ declare function findNearestNode(graph: RoadGraph, coordinate: Coordinate, maxDi
170
818
  */
171
819
  type RouteOptions = {
172
820
  costBound?: number;
821
+ /**
822
+ * The lorry being routed, where the graph states physical limits. Roads it
823
+ * is too large or too heavy for are closed to the search and to snapping.
824
+ */
825
+ vehicle?: VehicleDimensions;
826
+ /**
827
+ * When the journey starts, in the local time of the roads being travelled.
828
+ *
829
+ * Leaving it out ignores every conditional restriction, which is what this
830
+ * library did before it could read them: a road closed only at certain hours
831
+ * counts as open. Supplying it closes the roads shut at that moment and
832
+ * slows the ones a conditional speed limit slows.
833
+ *
834
+ * One moment is used for the whole route rather than an arrival time per
835
+ * edge. A route crossing a boundary as a restriction begins is therefore
836
+ * judged by where it set out, which is the honest limit of a departure time.
837
+ */
838
+ departAt?: Date | TravelTime;
839
+ /** Whether that day is a public holiday, which only a caller can know. */
840
+ publicHoliday?: boolean;
841
+ /**
842
+ * The time zone the roads are in, as an IANA name such as
843
+ * `"Europe/Lisbon"`. A restriction is written in the local time of the road
844
+ * it is on, so a caller routing somewhere other than where it is running
845
+ * should say where. Left out, a `Date` is read as this machine reads it.
846
+ */
847
+ timeZone?: string;
848
+ /**
849
+ * What the map cannot know: whether it is raining, who is driving, what the
850
+ * journey is for. A conditional rule resting on one of these is ignored
851
+ * unless it is answered here, so a road shut `@ (wet)` stays open until a
852
+ * caller says it is wet.
853
+ */
854
+ circumstances?: Circumstances;
855
+ /**
856
+ * Roads the caller knows are shut — roadworks, an incident, an event. A
857
+ * compiled bundle cannot know about any of them; whoever is reading a feed
858
+ * of them can say so here, and the search will neither drive through a
859
+ * closure nor begin in the middle of one.
860
+ */
861
+ avoid?: AvoidArea[];
862
+ /**
863
+ * Multipliers making certain edges dearer to the search than they really
864
+ * are, used to push a search away from a route already found. The route
865
+ * reported back is always priced at the graph's own cost, so a penalty
866
+ * changes which roads are chosen and never what they are said to cost.
867
+ */
868
+ costPenalties?: ReadonlyMap<number, number>;
869
+ /**
870
+ * Whether a route that cannot reach the road nearest its destination may
871
+ * finish on one a little further away. On by default, because the nearest
872
+ * road is sometimes one the network cannot legally enter — a pocket of
873
+ * one-way streets behind banned turns, an island of service roads — and
874
+ * refusing to route at all is a worse answer than finishing nearby.
875
+ *
876
+ * Turn it off to be told plainly that the point asked for cannot be
877
+ * reached. `RouteResult.destinationSnap.distanceMeters` reports how far
878
+ * short a route finished either way.
879
+ */
880
+ finishOnANearbyRoad?: boolean;
173
881
  };
174
882
  declare function route(graph: RoadGraph, from: Coordinate, to: Coordinate, preference?: RoutePreference, options?: RouteOptions): RouteResult;
175
883
 
884
+ /**
885
+ * Several ways to get there, not just the best one.
886
+ *
887
+ * A second route is only worth offering if it is genuinely different and not
888
+ * much worse. The method here is the simple one: find the best route, make the
889
+ * roads it used dearer to the search than they really are, and search again.
890
+ * A road that is the only sensible way through survives the penalty and is
891
+ * used again; one that was merely convenient gives way to its neighbour.
892
+ *
893
+ * The penalty never reaches the answer. A route is always reported at the
894
+ * graph's own cost, so an alternative's distance and duration are what it
895
+ * would really take, not what the search was told to believe.
896
+ *
897
+ * Candidates are then filtered rather than trusted: one that reuses most of a
898
+ * route already offered, or that is far worse than the best, is not an
899
+ * alternative anybody wants. What comes back is in order, best first.
900
+ */
901
+
902
+ type AlternativeRoutesOptions = Omit<RouteOptions, "costPenalties" | "costBound"> & {
903
+ /** How many routes to return at most, the best one included. Default 3. */
904
+ count?: number;
905
+ /**
906
+ * How much worse than the best route an alternative may be. 1.4 allows one
907
+ * taking 40% longer; raising it offers more routes and worse ones.
908
+ */
909
+ maxStretch?: number;
910
+ /**
911
+ * How much of the routes already offered an alternative may reuse, measured
912
+ * by distance. 0.75 rejects one that is three-quarters the same road.
913
+ */
914
+ maxSharing?: number;
915
+ /**
916
+ * How much dearer a road already used is made to look. Too small and the
917
+ * search returns the same route again; too large and it flees onto
918
+ * absurdities. Default 2.
919
+ */
920
+ penaltyFactor?: number;
921
+ /**
922
+ * Whether to download the wider corridor an alternative might lie in.
923
+ *
924
+ * On by default, because an alternative is allowed to be worse than the
925
+ * best by `maxStretch` and the tiles holding one therefore reach further
926
+ * than the tiles that proved the best route. On a long journey that is
927
+ * about three megabytes more map. Turning it off keeps the download to
928
+ * what one route needed and offers only the alternatives already inside
929
+ * it, which on a dense network is usually most of them.
930
+ */
931
+ searchWiderCorridor?: boolean;
932
+ };
933
+ /**
934
+ * Rejects options that would make the search unbounded or meaningless.
935
+ *
936
+ * Exported so the public entry point can refuse them before it downloads
937
+ * anything: a caller who passed `count: NaN` should be told so, not billed
938
+ * for a corridor first.
939
+ */
940
+ declare function checkAlternativeOptions(options: AlternativeRoutesOptions): void;
941
+ /**
942
+ * Routes from one point to another, best first, with up to `count - 1`
943
+ * genuinely different alternatives after it.
944
+ *
945
+ * Returns at least the best route; fewer than `count` means the graph held
946
+ * nothing else worth offering, which is the usual answer on a road network
947
+ * with one obvious way through.
948
+ */
949
+ declare function routeAlternatives(graph: RoadGraph, from: Coordinate, to: Coordinate, preference?: RoutePreference, options?: AlternativeRoutesOptions): RouteResult[];
950
+
951
+ type FetchLike$2 = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
952
+ /**
953
+ * Caps how far off the straight line the confirmation pass will reach, as a
954
+ * multiple of the direct distance. `Infinity` leaves the search bounded only
955
+ * by the route already found, which makes the answer the best the compiled
956
+ * graph holds; a finite value trades that proof for a smaller download and
957
+ * still ignores tile size, since the limit is a ground distance.
958
+ */
959
+ type RoadRouterOptions = {
960
+ maxDetourFactor?: number;
961
+ /** How many stitched corridors a bundle keeps; see `BundleOpenOptions`. */
962
+ mergedCacheSize?: number;
963
+ /**
964
+ * Which vehicle to route as. Only a multi-profile bundle offers a choice;
965
+ * leaving it out uses the vehicle the bundle was compiled around.
966
+ */
967
+ profile?: string;
968
+ /**
969
+ * The lorry's size and weight, for a profile whose routes depend on them. A
970
+ * preset name, a set of dimensions, or a preset with fields overridden.
971
+ * `null` turns the filtering off, which is a decision to accept routes no
972
+ * real lorry may be able to drive.
973
+ */
974
+ vehicle?: VehicleInput | null;
975
+ };
976
+ /**
977
+ * High-level route entry point. `open()` accepts an EFRB bundle, an EFRT
978
+ * manifest, or an already-decoded graph.
979
+ */
980
+ /** When a journey starts, for the conditional restrictions that depend on it. */
981
+ type DepartureOptions = Pick<RouteOptions, "departAt" | "publicHoliday" | "timeZone" | "circumstances" | "avoid">;
982
+ declare class RoadRouter {
983
+ private readonly graph?;
984
+ private readonly loader?;
985
+ private readonly options;
986
+ private readonly profileId?;
987
+ private readonly bundleVehicle?;
988
+ private constructor();
989
+ /** The lorry these routes are for, where the profile depends on one. */
990
+ readonly vehicle?: VehicleDimensions;
991
+ static fromGraph(graph: RoadGraph): RoadRouter;
992
+ /**
993
+ * Asking for dimensions a profile cannot act on is a mistake worth saying
994
+ * out loud: accepting them silently would look like they had been applied.
995
+ * It is checked where the caller made the pairing, not when switching
996
+ * profiles, where carrying them over is deliberate.
997
+ */
998
+ private static assertVehicleApplies;
999
+ static open(source: string | RoadGraph, fetcher?: FetchLike$2, options?: RoadRouterOptions): Promise<RoadRouter>;
1000
+ /** The vehicle this router is routing as, when the source names one. */
1001
+ get profile(): string | undefined;
1002
+ /**
1003
+ * Every vehicle this source can be read as, so a caller can offer a choice
1004
+ * without knowing how the bundle was compiled. A bundle holding one vehicle
1005
+ * reports that one, and a plain graph reports none.
1006
+ */
1007
+ get profiles(): string[];
1008
+ /**
1009
+ * The same bundle read as another vehicle. The shared half of every tile is
1010
+ * already downloaded and stays so, which is the point of storing the road
1011
+ * network once: switching vehicle costs only the small per-vehicle chunks.
1012
+ */
1013
+ withProfile(name: string): RoadRouter;
1014
+ /**
1015
+ * `when` says when the journey starts, so conditional restrictions apply.
1016
+ * Leaving it out ignores them, which is how this library behaved before it
1017
+ * could read them.
1018
+ */
1019
+ route(from: Coordinate, to: Coordinate, preference?: RoutePreference, when?: DepartureOptions): Promise<RouteResult>;
1020
+ /**
1021
+ * Downloads a corridor, routes, then asks whether any tile left out could
1022
+ * still hold something better. A route through a tile costs at least the
1023
+ * detour to reach it, so the answer is a geometric test against the route
1024
+ * already found: tiles that fail it are provably useless and stay
1025
+ * undownloaded, and when none passes, the route is the best the compiled
1026
+ * graph contains. Because the test measures ground distance rather than
1027
+ * counting neighbours, the answer does not depend on the tile size.
1028
+ */
1029
+ /**
1030
+ * Several ways there, best first, with up to `count - 1` alternatives after
1031
+ * it. Fewer than asked for means the network held nothing else worth
1032
+ * offering, which is the usual answer where there is one obvious way.
1033
+ *
1034
+ * Over a bundle this downloads a wider corridor than a single route needs:
1035
+ * an alternative may be worse than the best by as much as `maxStretch`, and
1036
+ * the tiles holding it lie outside the corridor that proved the best route.
1037
+ */
1038
+ routeAlternatives(from: Coordinate, to: Coordinate, preference?: RoutePreference, options?: AlternativeRoutesOptions): Promise<RouteResult[]>;
1039
+ private routeOverBundle;
1040
+ /**
1041
+ * The same corridor walk, stopping with the graph it settled on as well as
1042
+ * the route. Alternatives need the graph, and need to be able to widen it
1043
+ * once more: a second route may be worse than the best by more than the
1044
+ * corridor was ever sized to hold.
1045
+ */
1046
+ private bestOverBundle;
1047
+ private routeOverLoader;
1048
+ }
1049
+
176
1050
  /** Where a coordinate lands on one directed edge. */
177
1051
  type EdgeProjection = {
178
1052
  edge: number;
@@ -210,7 +1084,7 @@ type GraphSnap = {
210
1084
  * lies within `maxDistanceMeters` of the coordinate — measured to the road
211
1085
  * surface, not to the nearest junction.
212
1086
  */
213
- declare function snapToGraph(graph: RoadGraph, coordinate: Coordinate, maxDistanceMeters?: number): GraphSnap | null;
1087
+ declare function snapToGraph(graph: RoadGraph, coordinate: Coordinate, maxDistanceMeters?: number, access?: Uint8Array | undefined, accept?: (node: number) => boolean, everyNearbyRoad?: boolean): GraphSnap | null;
214
1088
 
215
1089
  /**
216
1090
  * A uniform-grid index over directed edges. Snapping needs the nearest *road*,
@@ -239,42 +1113,6 @@ declare function nearbyEdgeIds(index: EdgeSpatialIndex, coordinate: Coordinate,
239
1113
  declare function createSpatialIndex(graph: RoadGraph, cellSizeDegrees?: number): NodeSpatialIndex;
240
1114
  declare function withSpatialIndex(graph: RoadGraph, cellSizeDegrees?: number): RoadGraph;
241
1115
 
242
- type FetchLike$2 = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
243
- /**
244
- * Caps how far off the straight line the confirmation pass will reach, as a
245
- * multiple of the direct distance. `Infinity` leaves the search bounded only
246
- * by the route already found, which makes the answer the best the compiled
247
- * graph holds; a finite value trades that proof for a smaller download and
248
- * still ignores tile size, since the limit is a ground distance.
249
- */
250
- type RoadRouterOptions = {
251
- maxDetourFactor?: number;
252
- };
253
- /**
254
- * High-level route entry point. `open()` accepts an EFRB bundle, an EFRT
255
- * manifest, or an already-decoded graph.
256
- */
257
- declare class RoadRouter {
258
- private readonly graph?;
259
- private readonly loader?;
260
- private readonly options;
261
- private constructor();
262
- static fromGraph(graph: RoadGraph): RoadRouter;
263
- static open(source: string | RoadGraph, fetcher?: FetchLike$2, options?: RoadRouterOptions): Promise<RoadRouter>;
264
- route(from: Coordinate, to: Coordinate, preference?: RoutePreference): Promise<RouteResult>;
265
- /**
266
- * Downloads a corridor, routes, then asks whether any tile left out could
267
- * still hold something better. A route through a tile costs at least the
268
- * detour to reach it, so the answer is a geometric test against the route
269
- * already found: tiles that fail it are provably useless and stay
270
- * undownloaded, and when none passes, the route is the best the compiled
271
- * graph contains. Because the test measures ground distance rather than
272
- * counting neighbours, the answer does not depend on the tile size.
273
- */
274
- private routeOverBundle;
275
- private routeOverLoader;
276
- }
277
-
278
1116
  declare const BundleCompression: {
279
1117
  readonly none: 0;
280
1118
  readonly gzip: 1;
@@ -287,17 +1125,42 @@ type BundleBounds = {
287
1125
  maxLat: number;
288
1126
  maxLng: number;
289
1127
  };
290
- type BundleTile = {
291
- id: number;
292
- bounds: BundleBounds;
1128
+ /** Where one chunk sits in the file. */
1129
+ type BundleChunkRef = {
293
1130
  offset: number;
294
1131
  byteLength: number;
295
1132
  rawByteLength: number;
296
1133
  compression: BundleCompression;
297
1134
  };
1135
+ /**
1136
+ * A tile. The flat chunk fields address its graph in a v1 bundle and its
1137
+ * shared chunk in a v2 one; `profileChunks` is present only in v2 and holds
1138
+ * the per-vehicle half, keyed by profile id.
1139
+ */
1140
+ type BundleTile = BundleChunkRef & {
1141
+ id: number;
1142
+ bounds: BundleBounds;
1143
+ profileChunks?: Map<number, BundleChunkRef>;
1144
+ };
298
1145
  type RoadGraphBundle = {
299
1146
  url: string;
300
1147
  tiles: BundleTile[];
1148
+ /**
1149
+ * The profile every tile in this bundle was compiled for, read from the
1150
+ * header so a caller knows the vehicle before downloading a single tile.
1151
+ * `0` means a bundle written before profiles were recorded: a car bundle.
1152
+ */
1153
+ profileId: number;
1154
+ /** The profile's speed ceiling in km/h, or 0 when the bundle predates it. */
1155
+ maxSpeedKph: number;
1156
+ /** Every profile this bundle can route, by id. A v1 bundle lists its one. */
1157
+ profiles: number[];
1158
+ /**
1159
+ * The vehicle class this bundle was compiled to suggest, where it names one.
1160
+ * Whoever compiled the extract chose it knowingly; a caller's own `vehicle`
1161
+ * still wins, and the library's default applies when neither says anything.
1162
+ */
1163
+ defaultVehicle?: string;
301
1164
  loadForRoute(origin: Coordinate, destination: Coordinate): Promise<RoadGraph>;
302
1165
  loadExpandedForRoute(origin: Coordinate, destination: Coordinate): Promise<RoadGraph>;
303
1166
  /**
@@ -308,14 +1171,43 @@ type RoadGraphBundle = {
308
1171
  * distance, which is what makes it independent of how the bundle was tiled.
309
1172
  */
310
1173
  tilesWithinBudget(origin: Coordinate, destination: Coordinate, budgetMeters: number): BundleTile[];
311
- /** Merges the given tiles, reusing any chunk already downloaded. */
312
- loadTiles(tiles: BundleTile[]): Promise<RoadGraph>;
1174
+ /**
1175
+ * Merges the given tiles, reusing any chunk already downloaded. `profileId`
1176
+ * picks the vehicle in a multi-profile bundle; the bundle's own default is
1177
+ * used when it is left out.
1178
+ */
1179
+ loadTiles(tiles: BundleTile[], profileId?: number): Promise<RoadGraph>;
313
1180
  };
314
1181
  type BundleGraphTile = {
315
1182
  id: number;
316
1183
  bounds: BundleBounds;
317
1184
  graph: RoadGraph;
318
1185
  };
1186
+ /**
1187
+ * One tile of a multi-profile bundle: the road network once, and the
1188
+ * vehicle-dependent columns once per vehicle.
1189
+ */
1190
+ type BundleProfiledTile = {
1191
+ id: number;
1192
+ bounds: BundleBounds;
1193
+ graph: RoadGraph;
1194
+ profiles: ProfileChunk[];
1195
+ };
1196
+ /**
1197
+ * `mergedCacheSize` is how many stitched corridors to keep. Stitching is the
1198
+ * most expensive part of answering a route, and consecutive routes usually ask
1199
+ * for the same tiles, so one is worth holding. Each entry is a whole merged
1200
+ * corridor, so raising this trades memory for repeat speed; `0` keeps none and
1201
+ * restores the behaviour of releasing a corridor as soon as the route is done.
1202
+ */
1203
+ type BundleOpenOptions = {
1204
+ mergedCacheSize?: number;
1205
+ /**
1206
+ * Which vehicle to route as, by profile name. Only a multi-profile bundle
1207
+ * offers a choice; leaving it out uses the bundle's own default.
1208
+ */
1209
+ profile?: string;
1210
+ };
319
1211
  type BundleEncodeOptions = {
320
1212
  compression?: "none" | "gzip";
321
1213
  };
@@ -325,7 +1217,14 @@ declare function distanceToBounds(coordinate: Coordinate, bounds: BundleBounds):
325
1217
  declare function encodeRoadGraphBundle(tiles: BundleGraphTile[]): ArrayBuffer;
326
1218
  /** Creates a bundle with independently gzip-compressed EFR2 chunks. */
327
1219
  declare function encodeCompressedRoadGraphBundle(tiles: BundleGraphTile[], options?: BundleEncodeOptions): Promise<ArrayBuffer>;
328
- declare function openRoadGraphBundle(url: string, fetcher?: FetchLike$1): Promise<RoadGraphBundle>;
1220
+ /**
1221
+ * Creates a bundle serving several vehicles from one copy of the road network.
1222
+ * Each tile contributes its shared chunk once and a small chunk per vehicle,
1223
+ * and the directory lists them separately so a reader downloads only the
1224
+ * vehicle it asked for.
1225
+ */
1226
+ declare function encodeProfiledRoadGraphBundle(tiles: BundleProfiledTile[], options?: BundleEncodeOptions): Promise<ArrayBuffer>;
1227
+ declare function openRoadGraphBundle(url: string, fetcher?: FetchLike$1, options?: BundleOpenOptions): Promise<RoadGraphBundle>;
329
1228
 
330
1229
  type GraphBounds = {
331
1230
  minLat: number;
@@ -426,53 +1325,31 @@ declare function packTurnSequences(sequences: number[][]): {
426
1325
  turnRestrictionSequenceOffsets: Uint32Array<ArrayBuffer>;
427
1326
  turnRestrictionSequenceEdges: Uint32Array<ArrayBuffer>;
428
1327
  };
429
-
430
1328
  /**
431
- * Routing profiles turn raw OSM tags into routable facts: may a vehicle use
432
- * this way, in which directions, and how fast. Keeping the rules behind one
433
- * interface lets the compiler stay generic — it never inspects a tag itself —
434
- * and lets the rules be unit-tested without a PBF file.
1329
+ * Which junctions a search has to remember its approach at.
1330
+ *
1331
+ * Obeying a banned turn means knowing which road you arrived on, and the only
1332
+ * state that can express that is "arrived at this node along that edge" — one
1333
+ * state per directed edge rather than one per node. Carrying that everywhere
1334
+ * doubles the search for the sake of the few junctions that need it: in a
1335
+ * Portugal corridor, 16,620 junctions of 1,244,262 carry a restriction, which
1336
+ * is 1.3%.
1337
+ *
1338
+ * So the approach is remembered only where it can change the answer. A node is
1339
+ * guarded when a banned pair turns on it, or when any edge of a banned run
1340
+ * ends there — a run spans several junctions, and the search has to know how
1341
+ * far along it has driven at each of them.
435
1342
  */
436
- type OsmTags = Record<string, string | undefined>;
437
- /** How a profile may traverse one OSM way. */
438
- type WayTraversal = {
439
- /** Travel from the first node reference towards the last is allowed. */
440
- forward: boolean;
441
- /** Travel from the last node reference towards the first is allowed. */
442
- backward: boolean;
443
- /** Travel speed in km/h in each direction; both are clamped to the profile range. */
444
- forwardSpeedKph: number;
445
- backwardSpeedKph: number;
446
- };
447
- /** A turn restriction relation reduced to the only two cases routing cares about. */
448
- type TurnRestrictionKind = "no" | "only";
449
- type ParsedTurnRestriction = {
450
- kind: TurnRestrictionKind;
451
- /** The raw OSM value, kept for diagnostics only. */
452
- value: string;
1343
+ type GuardedJunctions = {
1344
+ /** One byte per node: does arriving matter here? */
1345
+ guarded: Uint8Array;
1346
+ /** For an edge ending at a guarded node, its own state; -1 otherwise. */
1347
+ stateOfEdge: Int32Array;
1348
+ /** The edge each guarded state arrived along. */
1349
+ edgeOfState: Int32Array;
1350
+ /** How many states the guarded junctions take up. */
1351
+ count: number;
453
1352
  };
454
- interface RoutingProfile {
455
- readonly name: string;
456
- /** Way tag keys the compiler must request from the PBF reader. */
457
- readonly wayTagKeys: readonly string[];
458
- /** Relation tag keys the compiler must request from the PBF reader. */
459
- readonly relationTagKeys: readonly string[];
460
- /** The fastest speed the profile can ever return, used for admissible A* heuristics. */
461
- readonly maxSpeedKph: number;
462
- /** Returns null when the way is not usable by this profile at all. */
463
- evaluateWay(tags: OsmTags): WayTraversal | null;
464
- /** Returns null when the relation is not a turn restriction this profile obeys. */
465
- evaluateTurnRestriction(tags: OsmTags): ParsedTurnRestriction | null;
466
- }
467
- /**
468
- * Reads a `maxspeed`-style value. Returns null for every value that is not a
469
- * plain number with an optional unit — country codes such as `PT:urban`,
470
- * `none`, `signals` and `variable` included — so the caller can fall back to a
471
- * class default instead of inventing a country-specific table.
472
- */
473
- declare function parseMaxSpeedKph(value: string | undefined): number | null;
474
- declare const carRoutingProfile: RoutingProfile;
475
- declare const routingProfiles: Record<string, RoutingProfile>;
476
- declare function getRoutingProfile(name: string): RoutingProfile;
1353
+ declare function guardedJunctionsFor(graph: RoadGraph, restrictions: TurnRestrictionIndex | null, sequences: TurnSequenceIndex | null): GuardedJunctions;
477
1354
 
478
- export { type BundleBounds, BundleCompression, type BundleEncodeOptions, type BundleGraphTile, type BundleTile, type CompactEncodeOptions, type CompactGraphOptions, type Coordinate, type CoordinateStorage, type EdgeProjection, type EdgeSpatialIndex, type EdgeWeightStorage, type GeoJsonLineString, type GeoJsonPosition, type GraphBounds, type GraphSnap, type GraphTile, type NodeSpatialIndex, type OsmTags, type ParsedTurnRestriction, type RoadGraph, type RoadGraphBundle, RoadRouter, type RoadRouterOptions, type RouteGraphLoader, type RouteGraphManifest, type RouteOptions, type RoutePreference, type RouteResult, type RouteSnap, RouterError, type RouterErrorCode, type RoutingProfile, type TurnRestrictionIndex, type TurnRestrictionKind, type TurnSequenceIndex, VarintReader, VarintWriter, type WayTraversal, buildTurnRestrictionIndex, buildTurnSequenceIndex, carRoutingProfile, compactGraph, createEdgeSpatialIndex, createRouteGraphLoader, createSpatialIndex, decodeGraph, distanceToBounds, edgePolyline, edgeSource, edgeSpatialIndexFor, encodeCompactGraph, encodeCompressedRoadGraphBundle, encodeGraph, encodeRoadGraphBundle, findNearestNode, getRoutingProfile, isTurnBanned, mergeRoadGraphs, nearbyEdgeIds, openRoadGraphBundle, packTurnRestrictions, packTurnSequences, parseMaxSpeedKph, route, routingProfiles, snapToGraph, unzigzag, withSpatialIndex, zigzag };
1355
+ export { type AlternativeRoutesOptions, type AvoidArea, type BundleBounds, type BundleChunkRef, BundleCompression, type BundleEncodeOptions, type BundleGraphTile, type BundleOpenOptions, type BundleProfiledTile, type BundleTile, type Circumstances, type CompactEncodeOptions, type CompactGraphOptions, type Condition, ConditionKind, type ConditionalRule, type Coordinate, type CoordinateStorage, DEFAULT_VEHICLE_PRESET, type DepartureOptions, type EdgeConditions, type EdgeLimits, type EdgeProjection, type EdgeSpatialIndex, type EdgeWeightStorage, type GeoJsonLineString, type GeoJsonPosition, type GraphBounds, type GraphSnap, type GraphTile, type GuardedJunctions, MAX_STORED_VALUES, type MinuteSpan, type NodeSpatialIndex, type OsmTags, type ParsedConditions, type ParsedTurnRestriction, type ProfileChunk, ProfileId, type RoadGraph, type RoadGraphBundle, RoadRouter, type RoadRouterOptions, type RouteGraphLoader, type RouteGraphManifest, type RouteOptions, type RoutePreference, type RouteResult, type RouteSnap, RouterError, type RouterErrorCode, type RoutingProfile, type SearchBounds, type SharedRoadGraph, type TravelTime, type TurnRestrictionIndex, type TurnRestrictionKind, type TurnSequenceIndex, VEHICLE_PRESETS, VarintReader, VarintWriter, type VehicleComparison, type VehicleDimensions, type VehicleInput, type VehicleLimits, VehiclePresetId, type WayTraversal, accessAtTime, accessAvoiding, accessForVehicle, accessMaskFor, allowsEverything, buildTurnRestrictionIndex, buildTurnSequenceIndex, carRoutingProfile, checkAlternativeOptions, combineProfiledGraph, compactGraph, componentSizes, componentsFor, conditionHolds, conditionalDurations, conditionalValue, conditionsOf, conditionsOnEdge, countAvoided, countBlocked, countClosedAt, createEdgeSpatialIndex, createRouteGraphLoader, createSpatialIndex, decodeGraph, decodeProfileChunk, decodeSharedGraph, distanceToBounds, edgeAllowed, edgePolyline, edgeSource, edgeSpatialIndexFor, encodeCompactGraph, encodeCompressedRoadGraphBundle, encodeGraph, encodeProfileChunk, encodeProfiledRoadGraphBundle, encodeRoadGraphBundle, encodeSharedGraph, findNearestNode, fromDecimetres, fromHalfTonnes, getRoutingProfile, getRoutingProfileById, guardedJunctionsFor, isTurnBanned, maxSpeedMpsFor, mergeRoadGraphs, motorcycleRoutingProfile, nearbyEdgeIds, openRoadGraphBundle, packEdgeConditions, packEdgeLimits, packTurnRestrictions, packTurnSequences, parseConditional, parseEdgeConditions, parseLengthMeters, parseMaxSpeedKph, parseWeightTonnes, profileOf, resolveVehicle, route, routeAlternatives, routingProfiles, searchBoundsFor, snapToGraph, straightLineAtMost, toDecimetres, toHalfTonnes, touchesArea, travelTimeFrom, truckRoutingProfile, unreadableConditions, unzigzag, vehiclePreset, vehiclePresetName, withSpatialIndex, zigzag };