⚓ An S-57/S-101 chart engine: vector tiles, S-52 styles, PNG and PDF.
tile57 turns IHO S-57 ENC cells into vector tiles (MLT or MVT) 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 to build on. The goal is an S-57/S-100 chart engine you can use to build a marine app 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. It aims to support:
- 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; MapLibre GL JS ≥ 5.12
decodes them natively) or Mapbox Vector Tiles — 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 holds only its working set:
- 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.
- Per-cell bakes. Each ENC cell bakes to its own PMTiles at its compilation
scale, so a bake holds one cell at a time; the runtime compositor stitches the
cells by
(z, x, y)on demand. - 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/iso8211/
▼
S-57 feature + geometry model src/s57/
│ adapt S-57 → S-101 features src/s101/ (adapter)
▼
S-101 feature records
│ S-101 portrayal (embedded Lua) src/portray/ + rules
▼
portrayal instruction stream src/s101/ (instructions)
│ 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 — iso8211, s57, s101, tiles,
render, scene, style — 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();
const bbox = chart.bounds(); // geographic extent [w, s, e, n], or null
// … render a view (chart.renderView), query features, or bake an archive …Chart renders views, queries features, and reads metadata. The runtime tile
path — bake each cell, then compose by (z, x, y) on demand — is exposed through
the C ABI. See the Zig API docs.
The same engine behind a thin C ABI (include/tile57.h).
Tiles are made one way — bake each cell to its own PMTiles, then compose on
demand — the structure tile57 bake ENC_ROOT -o out/ writes:
// out/ holds tiles/<CELL>.pmtiles (one per cell) + partition.tpart
const char *paths[] = { "out/tiles/US5MD1MC.pmtiles" };
tile57_compose_source *src = tile57_compose_open(paths, 1, "out/partition.tpart");
uint8_t *tile; size_t n;
if (tile57_compose_serve(src, z, x, y, &tile, &n) == 1) { /* decompressed MLT */
/* … hand the tile to your renderer … */
tile57_free(tile, n);
}
tile57_compose_close(src);A separate tile57_chart handle renders finished PNG/PDF views and answers
metadata + object queries; libtile57.a also exposes 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 -> out/tiles/<CELL>.pmtiles + partition.tpart
tile57 bake ENC_ROOT -o out/ # whole catalogue -> per-cell tiles/ + partition.tpart
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.
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.