⚓ A high-performance, low-memory S-57/S-101 chart engine: vector tiles, S-52 styles, PNG and PDF.
tile57 turns IHO S-57 ENC cells into Mapbox Vector Tiles with a matching MapLibre S-52
style, or renders finished charts directly to PNG and PDF — one Zig library with a
C ABI, compiled natively or to WASM.
·
📚 Docs →
Warning
Not for navigation. This project is coded almost entirely with AI (Claude) and human-reviewed. It is an experiment in using AI to implement a large, complex specification from scratch — not a certified or tested navigation product. Do not rely on it for real-world navigation. See Known limitations.
tile57 is an experiment in building a real, spec-faithful nautical chart engine almost entirely with AI assistance. A few specific goals shape its design:
-
AI-written, human-reviewed. Every significant piece of this codebase was generated by Claude and reviewed by a human. The project tests how far AI can carry the heavy lifting of spec interpretation, test coverage, and implementation correctness on a non-trivial domain.
-
Spec adherence first. The goal is to implement S-57 decoding, S-101 portrayal, and S-52 display as faithfully as possible, using the actual IHO spec documents and the official Portrayal Catalogue — not approximations or shortcuts.
-
Cross-platform via Zig. Zig's build system and cross-compilation support let the same core compile to native (Linux, macOS, Windows) and WASM without code changes. Go and Zig were chosen specifically because both have excellent build systems and first-class WASM targets, making the engine usable in desktop apps, servers, and browsers from one codebase.
-
Coupled tile + style. The engine emits vector tiles (MLT or MVT) and a matching MapLibre GL style together. The same style works for MapLibre Native and MapLibre GL JS, so native and web renderers share one chart look without separate style maintenance.
-
Language-agnostic embedding. A thin C ABI (
libtile57.a) bridges the Zig core to any language with C FFI. Go bindings ship in the repo; others are straightforward additions. -
An engine anyone can build on. The end goal is an easy-to-use S-57/S-100 chart rendering engine that anyone can use to build their marine app ideas — without first becoming an IHO spec expert. Open a chart, get tiles, PNGs, or PDFs; the S-52 rules, portrayal catalogue, and mariner settings are the engine's problem. Ideas it should make easy:
- an anchor alarm that draws your swing circle over a real chart,
- a Windy plugin overlaying forecast weather on ENC charts,
- a native cross-platform Qt6 C++ chartplotter,
- a paper-style passage-plan PDF printer, a race-committee display, a tides kiosk…
tile57 decodes NOAA/IHO S-57 ENC cells and generates vector tiles by
(z, x, y) — MapLibre Tiles (MLT, the default bake format; MapLibre GL JS ≥ 5.12
decodes them natively) or Mapbox Vector Tiles (--format mvt) — running the
official IHO S-101 Portrayal Catalogue in embedded Lua to produce S-52
nautical portrayal. Alongside the tiles it emits a MapLibre GL style and the
portrayal assets it references — colour tables, line styles, and the sprite
- area-fill pattern atlases — so a renderer like MapLibre can draw a chart directly.
It is high-performance and low-memory by design:
- Lazy, per-cell work. A multi-cell ENC_ROOT is indexed cheaply (band + bbox); cells are parsed and portrayed only when a requested tile needs them, then held under an LRU bound. A streaming open reads a cell's bytes on demand (and frees them on eviction), so a host holds only the working set — not the whole catalogue.
- Band-streamed bakes. Baking an ENC_ROOT to one PMTiles archive streams band-by-band (finest → coarsest, best-band dedup), so peak memory tracks the largest single band.
- Pure-Zig core. The foundational format/encode packages have no libc; only the Lua portrayal + sprite rasterizer pull in C.
S-57 ENC cell (.000)
│ ISO 8211 decode src/s57/iso8211.zig
▼
S-57 feature + geometry model src/s57/
│ S-101 portrayal (embedded Lua) src/portray/ + src/s100/
▼
portrayal instruction stream
│ scene generation src/scene/ (project + clip + draw calls)
▼
render Surface ──► MVT / MLT tiles (src/tiles/) + MapLibre style.json + assets
└───► PNG raster / vector PDF / terminal text (src/render/)
The stages are separate Zig modules — s57 (including its ISO 8211 decoder),
s100, tiles, render, scene, assets — pure Zig with no libc; only the
Lua portrayal (portray) and the sprite rasterizer (sprite) pull in C. See
the architecture docs.
Add tile57 as a dependency, then @import("tile57"):
const tile57 = @import("tile57");
// Open an on-disk ENC_ROOT directory (or a single .000 file) as a streaming chart.
var chart = try tile57.Chart.openPath("ENC_ROOT/", null, true);
defer chart.deinit();
if (try chart.tile(z, x, y)) |mvt| { // decompressed MVT bytes, or null if empty
defer tile57.freeBytes(mvt);
// … hand to your renderer …
}Chart.openPath streams cells on demand (working-set only); Chart.openBytes opens a
single in-memory cell. See the Zig API docs.
The same engine behind a thin C ABI (include/tile57.h):
// Open an on-disk ENC_ROOT directory (or a single .000 file).
tile57_chart *chart = tile57_chart_open("ENC_ROOT/");
uint8_t *mvt; size_t n;
if (tile57_chart_tile(chart, z, x, y, &mvt, &n) == TILE57_TILE_OK) {
/* … render mvt … */
tile57_free(mvt, n);
}
tile57_chart_close(chart);libtile57.a also exposes the ENC_ROOT bake, the MapLibre style builder, and the
asset/atlas generators. See the C API docs.
The offline tool bakes charts and emits portrayal assets:
zig build # builds zig-out/bin/tile57
tile57 bake CELL.000 -o out/ # one cell -> bundle (tiles + style + assets + manifest)
tile57 bake ENC_ROOT -o out/ # whole catalogue, band-streamed -> same bundle
tile57 assets -o assets/ # colortables + linestyles + sprite + patterns
tile57 png ENC_ROOT --view -76.48,38.974,15 --size 1600x1200 -o chart.png
tile57 pdf ENC_ROOT --view -76.48,38.974,15 --size 1600x1200 -o chart.pdf
tile57 ascii CELL.000 --view -76.48,38.974,13 --ansi --tui # the chart in your terminalThe Zig engine + CLI need only Zig 0.16:
git submodule update --init --recursive # vendored S-101 catalogue
zig build && zig build testFull instructions: docs/installation.
Docs source lives in docs/: intro,
getting started, the
Zig API, the C API,
the architecture, and the
tile schema. See also CHANGELOG.md.
tile57's own code is MIT © Jeremy Collins. It embeds the IHO S-101 Portrayal Catalogue (© IHO) and vendors nanosvg (zlib) + stb_image_write (public domain). NOAA ENC charts are U.S. public domain and not for navigation.