Skip to content

feat(drawer): embed tiled base maps in SVG output on every platform - #45

Merged
nighthawk merged 5 commits into
mainfrom
feat/svg-tiled-base-map
Oct 27, 2026
Merged

nighthawk merged 5 commits into
mainfrom
feat/svg-tiled-base-map

Conversation

@nighthawk

@nighthawk nighthawk commented Oct 27, 2026 •

Copy link
Copy Markdown
Member

Why

Maparoni's MCP tools now return map renders as PNGs (maparoni/lambdaroni#285): the Lambda produces SVG through GeoDrawer.drawSVG on Amazon Linux and Phoenix rasterises it with rsvg-convert. drawSVG skipped raster base maps entirely, and all tile compositing lived behind canImport(CoreGraphics), so the chat renders were vector-only.

The way

The per-pixel tile compositor is now pure Swift (renderTiledBaseMapBitmap), shared by the existing CoreGraphics path and by drawSVG, which composites the cached tiles into one bitmap, encodes it as PNG with swift-png and emits an <image> with a base64 data URI beneath the vector content. Embedding rather than linking matters because librsvg refuses remote fetches but happily decodes data URIs.

Along the way the .baseMap / .tiledBaseMap content cases lose their CoreGraphics gate, prefetchTiles(for:) becomes public (with a [Content] overload for callers that just want everything fetched before a sync draw), TileImage.pngDecoder is available on every platform, and the URLTemplateTileSource convenience initialiser falls back to it off Apple platforms. .baseMap is still omitted from SVG.

Swift 5 compilers are dropped. swift-png's dependencies need Swift 6.0 tools, and no older swift-png resolves on 5.10 either, so the Linux CI matrix is now 6.2 and 6.0.

Testing

  • swift test on macOS (64 tests) and in swift:6.3-amazonlinux2023 via Docker (77 tests) both pass. The new TiledBaseMapSVGTests render a 2×2 StaticTileSource grid to SVG, check the <image> precedes the <path>, and decode the embedded PNG to confirm the west/east tiles landed where expected.
  • Not verified here: an actual rsvg-convert run of the output, and a real slippy-map source end to end. Follow-ups on the Lambda side: /formaggio/run.svg needs to opt into a tiled base map, and GeoLogicKit's GeoJSON+SVG.swift continent-scale minimum bounding box makes small maps render as dots.

🤖 Generated with Claude Code

nighthawk and others added 2 commits October 27, 2026 18:09
Move the tiled base map compositor off CoreGraphics into a pure-Swift
`renderTiledBaseMapBitmap` that both the CGImage path and `drawSVG`
share. `drawSVG` now composites the cached tiles at the drawer's size,
encodes them as PNG via swift-png and emits an `<image>` with a base64
data URI beneath the vector content, so librsvg and other offline
rasterisers show the base map without fetching tiles.

The `.baseMap` and `.tiledBaseMap` content cases are no longer gated on
CoreGraphics, `prefetchTiles(for:)` is public (plus a `[Content]`
overload), `TileImage.pngDecoder` is available everywhere and the
`URLTemplateTileSource` convenience initialiser picks it on non-Apple
platforms. swift-png stays below 4.5 because its newer dependencies
need Swift 6.0 tools while CI still builds with 5.10.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
swift-png 4.5 depends on packages whose manifests need Swift 6.0 tools,
and no 4.4.x release resolves on a Swift 5.10 toolchain either (its
swift-hash dependency has the same requirement). Swift 5 compilers are no
longer supported, so the Linux matrix is 6.2 and 6.0 and swift-png
follows its latest line.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@nighthawk

nighthawk commented Oct 27, 2026 •

Copy link
Copy Markdown
Member Author

Suggested squash-merge commit message:

feat(drawer): embed tiled base maps in SVG output on every platform

`drawSVG` now composites the cached tiles of a `.tiledBaseMap`, encodes
them as PNG and emits an `<image>` with a base64 data URI (once, via
`xlink:href`) beneath the vector content, so librsvg and other offline
rasterisers show the base map.

- Move the tile compositor off CoreGraphics into a pure-Swift
  `renderTiledBaseMapBitmap` shared by the CGImage path and `drawSVG`.
- Snapshot only the fetched tiles before compositing instead of probing
  the whole 2^z × 2^z grid, which took minutes at street-level zooms.
- Add swift-png for PNG encode/decode on every platform, with
  `TileImage.pngDecoder` as the `URLTemplateTileSource` default off
  Apple platforms.
- Let `URLTemplateTileSource` send extra request headers, for services
  that check the Origin of key-restricted requests.
- Ungate the `.baseMap` and `.tiledBaseMap` content cases from
  CoreGraphics.
- Make `prefetchTiles(for:)` public and add a `[Content]` overload.
- Drop Swift 5 compilers: swift-png's dependencies need Swift 6.0
  tools, so the Linux CI matrix is 6.2 and 6.0.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

nighthawk and others added 3 commits October 27, 2026 18:41
Key-restricted tile services such as MapTiler check the Origin of every
request, so callers need a way to set headers beyond User-Agent.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The compositor probed the whole 2^z × 2^z tile grid of the shared cache
before each render, one lock acquisition per cell. At world zooms that is
nothing; at zoom 14 it is 268 million lookups and a four-gigabyte array,
so a street-level render took minutes. It now asks the cache for the
tiles it holds for that source and zoom and indexes a compact grid over
their extent.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The <image> carried the same base64 data URI in both `href` and
`xlink:href`, doubling every SVG with a base map (2.3 MB for an 860 KB
PNG). `xlink:href` alone is read by every renderer, librsvg included;
plain `href` is SVG 2 only, so it was the redundant one.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@nighthawk nighthawk self-assigned this Oct 27, 2026
@nighthawk nighthawk added the enhancement New feature or request label Oct 27, 2026
@nighthawk
nighthawk merged commit 9c02bc7 into main Oct 27, 2026
7 checks passed
@nighthawk
nighthawk deleted the feat/svg-tiled-base-map branch October 27, 2026 11:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant