Skip to content

feat: enable spatial (map) support in the Vega-Lite writer - #12

Merged
cpsievert merged 4 commits into
mainfrom
feat/spatial-support
Oct 13, 2026
Merged

cpsievert merged 4 commits into
mainfrom
feat/spatial-support

Conversation

@cpsievert

@cpsievert cpsievert commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Enables the ggsql crate's spatial cargo feature so DRAW spatial map layers render through the Vega-Lite writer. Today the wheel parses and validates spatial queries, but rendering fails with Output generation error: Spatial visualization requires the 'spatial' feature to be enabled — after this change, maps render end to end, including through the render_chart() → Altair → browser-widget path.

Runtime requirement: DuckDB spatial extension

All DRAW spatial queries need DuckDB's spatial extension at runtime — the reader issues LOAD spatial automatically (extent computation uses ST_ functions) but never INSTALL it. So users must run INSTALL spatial once per machine (e.g. via DuckDBReader.execute_sql()); the ~59 MB download is cached per DuckDB version/platform under ~/.duckdb/extensions. Without it, spatial queries fail with a clear SQL error pointing at INSTALL spatial.

With the extension present, verified working:

  • Polygon maps and choropleths from WKB geometry columns or the built-in ggsql:world dataset (identity projection, with graticule)
  • Named map projections (PROJECT TO mercator, equal_earth, …) via server-side ST_Transform
  • Point layers and mixed geometry types in one spatial layer
  • Faceted small-multiple maps (each panel fits its own extent)

Binary size impact: negligible

Same crate, same release profile, only the feature toggled: 60,737,888 → 60,871,744 bytes (+134 KB, +0.22%). The feature pulls in only the pure-Rust geozero crate (with-wkb/with-wkt/with-geojson) — no native dependencies (no PROJ/GEOS).

Note: ggsql-r has the same feature list (plus odbc); enabling spatial there is an identical one-line change if parity is wanted, but is out of scope here.

Verification

import ggsql

reader = ggsql.DuckDBReader("duckdb://memory")
reader.execute_sql("INSTALL spatial")   # one-time per machine; ggsql LOADs automatically after that

spec = reader.execute("VISUALISE FROM ggsql:world DRAW spatial")
vl_json = ggsql.VegaLiteWriter().render(spec)        # geoshape layers + fitted projection
chart = ggsql.VegaLiteWriter().render_chart(spec)    # Altair LayerChart, to_dict() round-trips

Tests: uv run pytest tests/ — new TestSpatial class covers Vega-Lite rendering of DRAW spatial (basic and choropleth) plus the render_chart() Altair round-trip. The class fixture INSTALLs the extension (skips when offline); a 10-minute timeout on the CI test step bounds download hangs. Rust checks: cargo fmt -- --check and cargo clippy -- -D warnings are clean.

Add the crate's spatial feature so DRAW spatial map layers render
through VegaLiteWriter instead of raising "Spatial visualization
requires the 'spatial' feature to be enabled". Geometry columns (WKB)
and the built-in ggsql:world dataset work out of the box with the
default projection; named map projections (e.g. PROJECT TO mercator)
and ST_* SQL functions additionally require the DuckDB spatial
extension.

Binary size impact is negligible (+~134 KB, 0.22%): the feature only
pulls in the pure-Rust geozero crate (wkb/wkt/geojson), no native
dependencies.
The ggsql crate only issues LOAD spatial (never INSTALL), which fails on
machines without the extension binary cached under ~/.duckdb/extensions.
CI runners start clean, so all TestSpatial tests failed on Linux/macOS
and the download attempt hung the Windows test step indefinitely.

Also add a 10-minute timeout to the test step to bound future hangs.
Spatial layers always need DuckDB's spatial extension at runtime (the
reader issues LOAD spatial and computes extents via ST_ functions), not
just named projections and ST_* calls. The previous wording claimed
WKB columns and ggsql:world worked with no extension present, which
only appeared true on machines where the extension was already cached.
@cpsievert
cpsievert marked this pull request as ready for review October 13, 2026 17:31
@cpsievert
cpsievert merged commit c14eb16 into main Oct 13, 2026
14 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