Background for the rules in the README. File references are to the CoMaps revision pinned there.
intermediate_data/cross_mwm_osm_ways/<region>). It records such a way
only when the neighbouring polygon is part of the build.BUBS_ALL_BORDERS=1 (the generator patch) links all polygons. Crossings
into regions that are not built yet are still recorded, so a later release
can add them.The engine keeps maps in a directory named after their data version,
<v>/<region>.mwm.
CHECK in the A* invariant).maps_generator names the version after the build date. Two
builds on the same day from different planets would therefore collide.
build-packs.sh instead names the build after the extracts’ replication
timestamp (YYMMDD) and continues the generator into it.releases.py refuses to register a version twice.verify_crossings.py compares the crossing lists of every pair of neighbouring
regions (regions whose border polygons share at least three vertices). Some
neighbours legitimately share no crossing way:
A “water border” rule would be wrong on both counts. Lakes carry ferries, and a river can be a land border without a bridge. So a pair with no recorded crossing passes only on evidence:
border_roads.py scans the build’s own planet for routable ways
(highway=*, route=ferry) with nodes inside or on (≤ 1 m) both polygons.
crossing-exceptions.json lists the known pairs, with reasons and evidence.
In the 2026-09-27 US build, one region (US_Missouri_Kansas) crashed the
generator in the polygon tessellator (libtess2, WalkDirtyRegions →
tessMeshAddEdgeVertex).
landuse=grass ring whose
outline doubles back about 2.4 m along itself (a zero-width spike).-ffast-math lets the compiler reassociate
and fuse the tessellator’s orientation tests, so near-zero results disagree
and the mesh becomes inconsistent.
-O3 -ffast-math crashes; adding
-ffp-contract=off or -fno-associative-math makes it pass.-fno-fast-math -ffp-contract=off
(the generator patch).The engine’s catalogue has about 1,150 regions (“leaves”). App packs bundle them:
Pack ids are stable and never encode the data version. Once a pack ships, its
id is never removed or reused. plan_groups.py check --previous enforces that.
Some US places can only be reached by road through another country: Point Roberts, Washington, is reachable only through British Columbia. The packs hold US maps only, so the unpatched router answers “no route” there, which reads as if no road existed.
The router change in patches/comaps-smoke-router-probe.patch checks, after a failed
search, whether every road path between start and destination runs through maps that
aren’t loaded. If so, it reports “needs more maps” (result code 9) and names them. It
changes nothing in the packs. The offline smoke test routes Seattle → Point Roberts and
back and expects code 9, so those two lines need the patch.
The generator reads elevation tiles as <tile>.SRTMGL1.hgt.zip or as plain <tile>.hgt.
generator/srtm_parser.cpp decided “zip or plain” once per process
(static bool const loadFromZip), from the first tile it looked up. If that tile
didn’t exist (open sea, or Alaska north of 60°N before 3DEP), every later zip was
read as missing, and a region whose first road point fell there got no
altitudes section at all.static).The pipeline still keeps one tile directory all-zip and covers every road cell, so the result doesn’t depend on the fix.
From release 260929, every region carries an altitudes section: one height per
road point, used for walking and cycling times and grades.
Sources:
fetch-srtm.sh takes every 1° cell
a region touches, plus one ring of cells across borders with foreign regions
(Canada, Mexico, the Bahamas, the British Virgin Islands, Kiribati), because roads
run on past the border and a road feature with one point off the tiles gets no
heights at all.Conversion (dem3dep.py): 3DEP tiles are GeoTIFFs whose pixel centres sit half
an arc second off SRTM’s posts. Each SRTM post gets the bilinear value of the four
nearest pixel centres, rounded to whole metres. 3DEP leaves the sea as “no data”
where SRTM has 0 m, so the coast is first grown outwards by 30 rounds of
neighbour means and the remaining open water is set to 0 m. The result is written in
SRTM’s own tile format, so the generator reads it unchanged.
Checks:
elevation.py validates the merged tile set before generation: every tile’s
product, latitude band and checksum against its manifest and provenance, and no
void posts. The build records the declaration in bubs-build.json and the
registry.altitudes section must log zero missing road points,
or the release fails. 260929: zero, in all 155 regions.The app downloads packs as Apple-hosted Background Assets. Packaging never changes the data; it only arranges it.
stage-release.sh copies a registered, smoke-tested build into a release folder:
the engine’s runtime layout, bubs-packs.json (each pack’s region files with size
and SHA-1, which regions carry heights, and the neighbouring packs) and one folder
per pack with its region files and NOTICE.txt.pack_notice.py writes each pack’s NOTICE.txt: the ODbL notice, the OSM data
date, a link to this repository, and the elevation credits for the products under
that pack’s regions only.ba_package.py builds one archive per pack with Xcode’s ba-package:
<version>/<region>.mwm, <version>/NOTICE-<pack>.txt, and release-<pack>.txt
holding the data version, so the app can tell a newer release’s pack from an older
one. Each archive is unpacked again and compared with the release file by file.upload_guard.py must pass before any archive is uploaded. It refuses an archive
whose release isn’t registered, whose files differ from the release in any way,
whose release marker is wrong, that sits next to a DO-NOT-UPLOAD.txt (test
packs), or whose notice carries the USGS 3DEP / Open Government Licence – Canada
credit while no app build shows that credit in the app yet.Each release has a folder under releases/ with what is needed to
rebuild it and to check a rebuild:
inputs.json: the Geofabrik extracts (URL, size, MD5, SHA-256, replication
timestamp), the CoMaps revision, the generator patch’s SHA-256, our generator
binary’s SHA-256 (for reference; a rebuild elsewhere gives a different binary), the
build command, and the elevation declaration.elevation-tiles.json and elevation-tiles.sha256: every elevation tile with its
product and checksum, and for 3DEP its source URL and source checksum.bubs-packs.json: the pack manifest the app ships with.notices/: each pack’s notice as shipped, named as inside the Apple-hosted pack
(NOTICE-<pack id>.txt).260929: OSM data as of 2026-09-29 20:22:51 UTC; World and 155 US regions in 46
packs; generator binary generator_tool SHA-256
233fbb77038ead75570fbc8553beed06dc8a237547089d0c4f9cfc3e44dd16ba.
Geofabrik may not keep dated extracts indefinitely. The same data can also be cut from the OpenStreetMap full-history planet at the replication timestamp.
us-latest extract does not contain Puerto Rico, the US
Virgin Islands, or Guam and the Northern Mariana Islands. They are separate
Geofabrik extracts (north-america/us/puerto-rico,
north-america/us/us-virgin-islands, australia-oceania/american-oceania)
and are merged into the planet. 260929 includes all of them.us-latest covers only Midway Atoll;
american-oceania adds Wake Island and Palmyra Atoll.Samoa
region, together with the independent state of Samoa, and the US packs don’t
include that region.