roadgraph-router-v2 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,176 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project aims
5
+ to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.2.0] - 2026-10-15
10
+
11
+ Multi-vehicle routing. One bundle can now serve a car, a motorcycle and a
12
+ lorry, and a lorry's routes depend on its size and weight. Walking and cycling
13
+ ship too, in a bundle of their own. Alternative routes moved to 0.3.0.
14
+
15
+ ### Added
16
+
17
+ - **Coordinates in a shared chunk are written as differences**, which takes
18
+ about 6% off a multi-vehicle bundle and loses nothing: Portugal for walking
19
+ and cycling went from 102.3 MB to 96.1 MB, and the three motor vehicles from
20
+ 42.8 MB to 40.2 MB. The single-chunk layout a one-vehicle bundle uses is
21
+ untouched, so those stay readable by 0.1.0.
22
+ - **Barriers.** A gate, bollard or stile across a road is read from the node
23
+ and becomes a ban on passing through it, which still lets you turn back the
24
+ way you came. The same barrier stops different travellers differently: a
25
+ bollard is nothing on foot and the end of the road in a car, a stile is the
26
+ reverse. An unfamiliar barrier is left passable rather than guessed at.
27
+ - **`bicycle` and `pedestrian` profiles.** Not motor vehicles with different
28
+ numbers: a posted limit never makes anyone pedal harder, so the way class
29
+ decides the pace and `maxspeed` can only lower it; a one-way sign restrains
30
+ traffic, which a pedestrian is not and a cyclist often has a signed
31
+ contraflow out of; and footways, paths, steps and cycleways are the network
32
+ rather than roads left out of it. They need their own bundle — Portugal is
33
+ 102.3 MB for both against 42.8 MB for the three motor vehicles — because
34
+ folding them in would make every driver download the footpaths.
35
+ - **`motorcycle` and `truck` profiles.** The car profile became a factory
36
+ parameterised by the mode name, so all three read the same tags in the same
37
+ order and differ in the key at the bottom of each hierarchy: `motorcar`,
38
+ `motorcycle`, `hgv`. `truck` caps every speed at 90 km/h rather than only
39
+ bounding the A* heuristic, because a heavy vehicle is held below the posted
40
+ limit nearly everywhere.
41
+ - **Physical limits for lorries.** `maxheight`, `maxwidth`, `maxlength` and
42
+ `maxweight` are read per direction, preferring a limit signed for this
43
+ direction, then one signed for lorries, then the general one, and taking the
44
+ stricter of a signed and a `:physical` value. Feet and inches parse. A value
45
+ that states no number reads as unknown rather than as zero.
46
+ - **Vehicle presets** — `van`, `rigid`, `articulated`, `road-train`,
47
+ `us-semi` — resolved from the caller, then the bundle's own
48
+ `--default-vehicle`, then `articulated`. The resolved dimensions are on
49
+ `router.vehicle`, so a default is visible rather than hidden.
50
+ - **EFR2 v7 and EFRB v2.** A tile is written as a shared chunk holding the road
51
+ network and one small chunk per vehicle holding its durations, access mask,
52
+ restriction mask and limits. The directory lists them by `(tile, profile)`,
53
+ so a reader downloads the shared half plus the one vehicle it wants.
54
+ - **`--profile car,truck,motorcycle`** compiles one bundle for several
55
+ vehicles, reading the extract once.
56
+ - **`RoadRouter.profiles`, `RoadRouter.profile`, `RoadRouter.withProfile()`**
57
+ for reading a bundle as another vehicle, reusing every shared chunk already
58
+ downloaded.
59
+ - **Profile metadata in both formats**, in header bytes that were already
60
+ reserved, so nothing published grew.
61
+
62
+ ### Changed
63
+
64
+ - **Repeated routes are about four times faster.** The stitched corridor is
65
+ cached between routes, and the stitch itself no longer builds a string key
66
+ per node copy. Porto to Lisbon went from 2107 ms to 534 ms with nothing left
67
+ to download. `mergedCacheSize` controls it; `0` restores the old behaviour.
68
+ - **The A* heuristic and the tile budget take their speed ceiling from the
69
+ graph** rather than from the car profile. The heuristic stayed admissible
70
+ with the wrong ceiling but collapsed towards zero; the tile budget would have
71
+ over-downloaded badly for a slower vehicle.
72
+ - The car profile also reads `oneway:motor_vehicle`, `oneway:motorcar` and
73
+ `maxspeed:motorcar`, which only matter where those tags exist.
74
+
75
+ ### Fixed
76
+
77
+ - **`engines.node` was `>=22.5`**, the compiler's requirement, applied to the
78
+ whole package — so installing the library on Node 18 or 20 warned, and failed
79
+ under `engine-strict`. It is now `>=18`, and the CLI checks its own
80
+ requirement at startup rather than after streaming the whole extract.
81
+ - **Switching vehicle threw when the first one had dimensions.** Opening as a
82
+ lorry and asking for the car refused the switch, because the dimensions
83
+ travelled with the options and a car cannot use them. The check moved to
84
+ where a caller pairs a vehicle with a profile; past that, dimensions travel
85
+ with the reader and apply only where they mean something, so truck to car to
86
+ motorcycle and back ends with the dimensions it started with.
87
+ - **Switching vehicle discarded the stitched corridor.** A bundle kept two
88
+ corridors however many vehicles it held, so three vehicles evicted each
89
+ other and every switch re-stitched. It keeps one per vehicle now: a repeated
90
+ Porto to Lisbon fell from about 1500 ms to 257 ms by car, 140 ms by lorry.
91
+ - **A route could fail because an end snapped to a disconnected stub.** The
92
+ nearest road to a point is not always one that leads anywhere: a path behind
93
+ a fence, a service road in a compound, a stretch clipped at a tile edge. A
94
+ failed route is now retried against the piece of the network the other end
95
+ is in. It runs only after a failure, so an ordinary route never pays for it.
96
+ Walking and cycling networks feel this most, being full of small fragments.
97
+ - **Merging tiles dropped per-vehicle data.** Limits, access masks and
98
+ restriction masks are rebased onto the merged numbering, keeping the stricter
99
+ limit where two tiles disagree. A corridor built from more than one tile
100
+ would otherwise have routed a lorry under a bridge it does not fit through.
101
+ - A truck bundle written in the single-chunk layout, which has nowhere to keep
102
+ limits, is refused rather than routed with the dimensions silently ignored.
103
+
104
+ ### Documentation
105
+
106
+ - **The README now states plainly what a route does not guarantee.** A route
107
+ is a path over roads the data does not say are forbidden, which is weaker
108
+ than legal or physical suitability. Time-dependent access, seasonal and
109
+ weather rules, temporary closures, zone-level lorry bans and low-emission
110
+ zones, axle load and hazardous goods rules, and untagged limits are all
111
+ listed as unread, with the consequence for a heavy vehicle said directly:
112
+ a route may pass under a bridge too low for it or along a street it is
113
+ banned from at the hour you travel.
114
+
115
+ ### Known limitations
116
+
117
+ - An active-travel bundle at country scale cannot be compiled with small
118
+ tiles: Portugal for walking and cycling at 40 km tiles exceeds the
119
+ compiler's 3 GB ceiling and is refused. The default 120 km works, at the
120
+ cost of a larger first download for a short urban route.
121
+ - Alternative routes are not in this release; they are planned for 0.3.0.
122
+
123
+ ### Compatibility
124
+
125
+ Bundles compiled by 0.1.0 open and route identically. A single-vehicle compile
126
+ with no physical limits still writes the one-chunk layout byte for byte. The
127
+ new formats appear only when a bundle holds several vehicles or a vehicle whose
128
+ routes depend on its size, and a 0.1.0 reader cannot open those.
129
+
130
+ ## [0.1.0] - 2026-10-12
131
+
132
+ First public release. Everything below is new, so this entry describes what the
133
+ package does rather than how it changed.
134
+
135
+ ### Added
136
+
137
+ - **Generic OSM compiler.** `roadgraph-compile` turns any `.osm.pbf` extract
138
+ into an EFRB bundle in two passes over the file, with no assumptions about
139
+ which region it describes. Coordinates are indexed on disk through
140
+ `node:sqlite`, tiles are encoded and spilled one at a time, and the bundle is
141
+ streamed out, so peak memory tracks the largest tile rather than the region.
142
+ - **Routing profiles.** Tag handling lives behind a `RoutingProfile`. The
143
+ bundled `car` profile resolves the OSM access hierarchy, `oneway`,
144
+ `junction`, and `maxspeed` including `mph`, falling back to highway-class
145
+ defaults for values it cannot parse.
146
+ - **Turn restrictions.** `no_*` and `only_*` relations are enforced, both the
147
+ `via`-node form and the `via`-way form. A graph carrying restrictions is
148
+ searched with an edge-labelled A*, so a banned turn cannot appear in a result.
149
+ - **Snapping to roads.** A coordinate is projected onto the nearest road rather
150
+ than the nearest junction, which matters after topology simplification, where
151
+ a rural road can run for a kilometre without a node.
152
+ - **Tile selection by distance.** The router loads the tiles that could hold a
153
+ better route, judged by ground distance, so results do not depend on the tile
154
+ size a region was compiled at. `maxDetourFactor` caps how far it will look.
155
+ - **Compact storage.** EFR2 v6 stores each road's geometry once and mirrors it
156
+ for the reverse direction, writes coordinates and edge targets as
157
+ variable-length differences, and rebuilds node offsets from degrees.
158
+ - **Range-addressable bundles.** An `.efrb` holds independently gzipped tiles
159
+ behind a fixed directory, so a route downloads only the tiles it needs.
160
+ - **Formats.** Reads EFRT v1 and EFR2 v2 through v6; writes EFR2 v6 by default
161
+ and the v2-v5 layout on request.
162
+
163
+ ### Documentation
164
+
165
+ - **The README now states plainly what a route does not guarantee.** A route
166
+ is a path over roads the data does not say are forbidden, which is weaker
167
+ than legal or physical suitability. Time-dependent access, seasonal and
168
+ weather rules, temporary closures, zone-level lorry bans and low-emission
169
+ zones, axle load and hazardous goods rules, and untagged limits are all
170
+ listed as unread, with the consequence for a heavy vehicle said directly:
171
+ a route may pass under a bridge too low for it or along a street it is
172
+ banned from at the hour you travel.
173
+
174
+ ### Known limitations
175
+
176
+ See the "Supported OSM features and limitations" section of the README.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Daniel Sá
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.