Skip to content

Build PROJ into the static render; version 0.300.0 - #3

Merged
boydjohnson merged 3 commits into
mainfrom
feature/static-proj
Sep 18, 2026
Merged

boydjohnson merged 3 commits into
mainfrom
feature/static-proj

Conversation

@boydjohnson

@boydjohnson boydjohnson commented Sep 18, 2026 •

Copy link
Copy Markdown
Owner

Adds reprojection to the static render: a Map can now use any EPSG code or proj string — a Minnesota map in UTM 15N, a US map in Albers — instead of only plain lon/lat.

How

PROJ 9.9.0 is built from source on every platform with libtiff and libcurl off. Alpine's and Homebrew's PROJ both pull those in (and OpenSSL behind curl) purely to read and download datum-shift grids. Without grids, a transformation that wants one uses PROJ's ballpark fallback — about a metre between WGS84 and NAD83, invisible at map scale.

A static PROJ embeds proj.db in the library (EMBED_RESOURCE_FILES, default ON for static builds), and USE_ONLY_EMBEDDED_RESOURCE_FILES stops it reading one off disk. So there's no data directory to ship, and behavior doesn't depend on the host.

With PROJ linked, mapnik classifies +proj=longlat exactly as the dynamic build does — so the projection.cpp patch is gone, one fewer edit to mapnik.

Verified locally (aarch64 linux-musl in podman, arm64 macOS)

  • render_relations_projected (new) draws the fixture in EPSG:28992 and fails on a blank image, since a misprojected render still exits 0. Passes on both platforms.
  • On a bare alpine:3.22 with zero proj.db files on disk, the unpacked archive renders the projected style correctly — the EPSG lookup can only have come from the embedded database. macOS also renders it with an empty environment.
  • Linux and macOS projected output is byte-identical (md5 6889ae8e478fcbf382bcbca4068c2a14), and visually correct: the same Baarle boundaries in true proportions rather than lon/lat's east–west stretch.
  • Existing lon/lat styles are unchanged: scale_denominator=1.92857 and md5 d48d0556925e17e872d0ac25229319d2 on both platforms, identical to v0.1.0.
  • Still system-libraries-only on macOS; still fully static on Linux.

Costs

before after
linux-musl binary / tarball 16.5 / 10.4 MB 31.5 / 14.6 MB
macOS binary / tarball 44 / 21 MB 58 / 25 MB

Plus PROJ's ~250 TUs on a cold build; the CI cache covers repeat runs. The cache key includes cmake/**, so this PR starts a fresh (cold) cache lineage.

Licensing

PROJ (MIT), SQLite (public domain) and nlohmann/json (MIT, bundled by PROJ) — all compatible. NOTICE updated.

Build notes

  • PROJ has C sources, so the static build now enables C.
  • mapnik wraps PROJ_INCLUDE_DIRS in $<BUILD_INTERFACE:...>; a list splits the genex and leaks a source path into mapnik's install(EXPORT), so the config shim passes a single path.
  • The release smoke test now also runs the projected render on the bare image, so every published archive proves its embedded proj.db.

Version

Bumps the project to 0.300.0 (from 0.1.0), matching the osmflat-rs versioning. release.yml requires the tag to equal project(... VERSION), so this release is v0.300.0.

Regex needed no work: Boost.Regex with ICU was already in, and .match()/.replace() filters work in v0.1.0.

🤖 Generated with Claude Code

boydjohnson and others added 2 commits September 18, 2026 16:37
Adds reprojection: a Map can now use any EPSG code or proj string, e.g. a
Minnesota map in UTM 15N or a US map in Albers instead of plain lon/lat.

PROJ 9.9.0 is built from source on every platform, with libtiff and
libcurl off: Alpine's and Homebrew's builds pull both in (plus OpenSSL
behind curl) only to read and download datum-shift grids. Without grids a
transformation that wants one uses PROJ's ballpark fallback, about a
metre between WGS84 and NAD83. A static PROJ embeds proj.db in the
library, and USE_ONLY_EMBEDDED_RESOURCE_FILES keeps it from reading one
off disk, so there is no data directory to ship.

With PROJ linked, mapnik classifies '+proj=longlat' exactly as the
dynamic build does, so the projection.cpp patch that faked that is gone.
Existing lon/lat styles are unchanged: scale_denominator 1.92857 and
md5 d48d0556925e17e872d0ac25229319d2, same as v0.1.0, on both platforms.

Build notes: PROJ needs C enabled; mapnik wraps PROJ_INCLUDE_DIRS in
$<BUILD_INTERFACE:>, so the config shim passes a single path (a list
splits the genex and leaks a source path into mapnik's export).

New test render_relations_projected draws the fixture in EPSG:28992 and
fails on a blank image (a misprojection still exits 0). The release smoke
test runs it on a bare alpine image with no proj.db anywhere. Verified:
linux-musl and macOS produce byte-identical projected output (md5
6889ae8e478fcbf382bcbca4068c2a14), and with 0 proj.db files on disk.

Size: linux 16.5 -> 31.5 MB (tarball 10.4 -> 14.6), macOS 44 -> 58 MB
(21 -> 25). NOTICE now lists PROJ (MIT), SQLite (public domain) and
nlohmann/json (MIT).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Matches the osmflat-rs versioning. release.yml requires the tag to equal
project(... VERSION), so this release is tagged v0.300.0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@boydjohnson boydjohnson changed the title Build PROJ into the static render Build PROJ into the static render; version 0.300.0 Sep 18, 2026
A Docker Hub connection reset while pulling alpine:3.22 failed the
x86_64 static-render job before any build ran ('read: connection reset
by peer'). Pull up front with backoff (5 attempts, 10-50s) so a flaky
registry doesn't fail a job; the build and smoke-test docker runs then
use the local image. Same step in release.yml, where a flake would
otherwise fail a tagged release.

Also fixes release.yml's header, which still said linux/musl only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@boydjohnson
boydjohnson merged commit 450e3c3 into main Sep 18, 2026
4 checks passed
@boydjohnson
boydjohnson deleted the feature/static-proj branch September 18, 2026 21:55
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