Offline Use with PMTiles¶
WebATM supports fully offline operation using PMTiles — a single-file, cloud-optimized map archive format. Once set up, WebATM makes no CDN calls during page load and no remote tile requests, so it works in air-gapped environments, aircraft, ships, field deployments, and other networks without reliable internet.

Why PMTiles?¶
- Single file deployment — one
.pmtilesarchive replaces an entire tile server. No tile cache, no tile seeding scripts, no background jobs. - No server software required — PMTiles is read client-side via HTTP
Rangerequests, which Flask's default static handler already supports. - Portable — drop the file onto a USB stick, ship it with a Docker image, or host it on any static file server.
Setup¶
-
Build with internet available.
script/build_frontend.sh(ornpm run buildinfrontend/) triggers a prebuild step that copies MapLibre GL CSS, the MapLibre GL map worker, and Font Awesome assets fromnode_modulesintoWebATM/static/vendor/, and webpack bundles MapLibre GL JS andsocket.io-clientinto the app bundles. After this step the page loads every library locally. -
Drop in an offline basemap. Download a PMTiles archive (e.g. the Protomaps worldwide basemap) and save it to:
The offline style at
WebATM/static/map/offline-style.jsonexpects that exact path.Reverse proxies
Make sure
.pmtilesfiles are not gzip-encoded — PMTiles relies on HTTPRangerequests, which Flask's default static handler supports out of the box. -
Select the offline map. Open Settings → Map Display Configuration and choose Offline (Local PMTiles). The app also auto-falls-back to the offline style if it detects that the configured online style can't be reached (e.g. no DNS, captive portal, blocked outbound) — including when the request hangs without ever failing: a reachability probe with a short timeout swaps to the offline basemap within a few seconds of first load.
The offline styles (offline-style.json and its light variant
offline-style-light.json in WebATM/static/map/) are minimal
Protomaps-schema basemaps with no text labels of their own, so no sprite
sheets need to be bundled. WebATM's overlay labels (airport and waypoint
names, shape labels, aircraft tags) do need map glyphs to render offline:
they use the Open Sans Regular fontstack served from
WebATM/static/glyphs/. The prebuilt release assets bundle already includes
it; if you build from source, see WebATM/static/glyphs/README.md for how to
populate it.
Generating a custom regional archive¶
The worldwide Protomaps build is ~110 GB. For a specific region (e.g. a
single FIR or training area), the
pmtiles CLI can extract a
bounding-box subset from the global archive, bringing the file size down to a
few MB–GB depending on coverage and max zoom:
pmtiles extract https://build.protomaps.com/<build>.pmtiles region.pmtiles \
--bbox=<minLon>,<minLat>,<maxLon>,<maxLat> \
--maxzoom=12
Copy the resulting region.pmtiles to WebATM/static/tiles/world.pmtiles
(the filename the offline style expects) and you're done.
Navdata overlay (airports, runways, navaids)¶
The airport overlay (airport symbols, runway polygons, radio navaids, and the
map's "go to" search box) does not come from the basemap — it is a
separate archive that works at full detail regardless of the basemap's zoom
range. It is built from the public-domain
OurAirports open data
(airports.csv, runways.csv, navaids.csv, downloaded automatically) by
the offline build pipeline in script/navdata/:
This needs tippecanoe (for tippecanoe
and tile-join) plus python3 and curl, and produces two outputs:
WebATM/static/tiles/navdata.pmtiles— the vector-tile archive rendered by the map (airports, heliports, runways, waypoints).WebATM/static/navdata/navdata.sqlite— the search index behindGET /api/navdata/search, used by the map's "go to" box.
Note
Both outputs are gitignored (large/binary) and are already included in the prebuilt release assets bundle — you only need to run the pipeline when building your own data. See script/navdata/README.md for what each dataset provides and the zoom/declutter tuning flags.
Taxiways & aprons (OpenStreetMap)¶
Taxiways and aprons are the exception, since OurAirports has no geometry for them — they come from OpenStreetMap:
- Taxiway centrelines are included in Protomaps planet builds (in the
roadslayer) from roughly z11 upward. A common worldwide extract at--maxzoom=8therefore contains none of them; a regional extract with--maxzoom=13or higher does, and WebATM renders them automatically under the Taxiways & Aprons display toggle. - Apron polygons are not present in Protomaps builds at all (the schema has no apron kind). To get true aprons — and taxiways independent of the basemap's zoom range — bake them into the navdata archive from an OSM extract instead:
This additionally needs osmium on the
PATH; the navdata README covers a worldwide-coverage recipe built from
continent extracts. When the navdata archive contains these layers they
take precedence over the basemap's aeroway data, so nothing is ever
drawn twice.