Skip to content

Add hakunapi-tiles-source-pmtiles: tile sources reading PMTiles v3 archives - #242

Merged
jampukka merged 1 commit into
nlsfi:mainfrom
jampukka:tiles/source-pmtiles
Oct 18, 2026
Merged

jampukka merged 1 commit into
nlsfi:mainfrom
jampukka:tiles/source-pmtiles

Conversation

@jampukka

Copy link
Copy Markdown
Collaborator

Adds the community module that serves tiles from a PMTiles v3 archive, read in place from a local file or over HTTP range requests, through the MapTileSource and VectorTileSource SPIs. Not wired into the reference webapp. Reading only: the archive writer comes with the tile generator PR.

  • PMTilesArchive
    • The opened archive: header, root directory, leaf directories and tile lookup by tile id. Leaf chains are capped at four levels.
    • Leaf directories sit in a bounded LRU (64 entries), so a leaf is read once and then reused.
    • Lengths and counts read from the archive are checked before anything is allocated.
  • TileId
    • XYZ to the PMTiles Hilbert tile id and back. Directories are sorted by this id, which is what the lookup depends on.
  • Directory
    • One parsed directory with find returning the entry that covers a tile id. A run length of 0 marks a pointer to a leaf directory.
  • RangeReader
    • The byte access under the archive, by offset and length.
    • FileRangeReader: positional reads on one channel, so concurrent tile requests need no lock.
    • HttpRangeReader: one Range request per read.
  • TileIndex / BoxedTileIndex
    • Resident index from tile id to byte location, built at startup only when pmtiles.sendfile is set. On those layers a request does no directory I/O; otherwise a tile read walks the directories.
    • BoxedTileIndex is a dense array per zoom, used only when every zoom's bounding box is at least half filled. Otherwise the sorted TileIndex serves.
  • PMTilesTileSource / PMTilesVectorTileSource
    • The raster (pmtiles) and vector (pmtiles-vector) sources, sharing one config parser. Tile bytes are relayed as stored, never decoded.
  • No new third-party dependency (tiles-core, slf4j-api)
    • range readers are local until: hakunapi-bytes can not be used from a Java 21 module; migration is the intent.

Behaviour:

  • No change to existing modules; tiles are served only when a pmtiles or pmtiles-vector layer is configured
  • Only HTTP 206 is accepted from a remote archive; a server that ignores Range and answers 200 is an error
  • Range lengths and decompressed sizes are capped at 64 MiB, and a directory's entry count cannot exceed its byte length / 4
  • tile_compression brotli and zstd are served with Content-Encoding: br and zstd, gzip as gzip; an archive without tile compression is served without the header
  • Brotli and zstd internal compression are rejected at startup, since decoding them would need a dependency
  • A tile address outside the zoom's range is an empty tile (404), not an error
  • pmtiles.sendfile is accepted only for pmtiles-vector, and needs pmtiles.path; for pmtiles it fails at startup
  • The configured type must match the archive: an MVT archive needs pmtiles-vector, a raster archive needs pmtiles; a mismatch fails at startup

@jampukka
jampukka merged commit ba4cb56 into nlsfi:main Oct 18, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant