Skip to content

Commit cb0aeef

Browse files
committed
feat: enable spatial support in the Vega-Lite writer
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.
1 parent e6bf1ef commit cb0aeef

4 files changed

Lines changed: 170 additions & 2 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## UNRELEASED
4+
5+
### Added
6+
7+
- Enabled the crate's `spatial` feature, so `DRAW spatial` map layers now render through the Vega-Lite writer 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 (`INSTALL spatial; LOAD spatial;`).
8+
39
## 0.5.2
410

511
### Added

‎Cargo.lock‎

Lines changed: 125 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ crate-type = ["cdylib"]
1616
[dependencies]
1717
pyo3 = { version = "0.26", features = ["extension-module", "abi3-py310"] }
1818
arrow = { version = "58", default-features = false, features = ["ipc"] }
19-
ggsql = { version = "0.5.2", default-features = false, features = ["duckdb", "vegalite", "builtin-data", "svg", "pdf", "hep"] }
19+
ggsql = { version = "0.5.2", default-features = false, features = ["duckdb", "vegalite", "builtin-data", "svg", "pdf", "hep", "spatial"] }
2020

2121
[features]
2222
default = []

‎tests/test_ggsql.py‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,44 @@ def test_render_multi_layer(self):
217217
assert "layer" in spec_dict
218218

219219

220+
class TestSpatial:
221+
"""Tests for spatial (map) layers via the VegaLiteWriter."""
222+
223+
def test_render_spatial_builtin_world(self):
224+
reader = ggsql.DuckDBReader("duckdb://memory")
225+
spec = reader.execute("VISUALISE FROM ggsql:world DRAW spatial")
226+
writer = ggsql.VegaLiteWriter()
227+
228+
result = writer.render(spec)
229+
spec_dict = json.loads(result)
230+
marks = [
231+
layer["mark"]["type"] if isinstance(layer["mark"], dict) else layer["mark"]
232+
for layer in spec_dict["layer"]
233+
]
234+
assert "geoshape" in marks
235+
236+
def test_render_spatial_choropleth(self):
237+
reader = ggsql.DuckDBReader("duckdb://memory")
238+
spec = reader.execute(
239+
"VISUALISE FROM ggsql:world DRAW spatial MAPPING population AS fill"
240+
)
241+
writer = ggsql.VegaLiteWriter()
242+
243+
result = writer.render(spec)
244+
assert "geoshape" in result
245+
246+
def test_render_chart_spatial(self):
247+
reader = ggsql.DuckDBReader("duckdb://memory")
248+
spec = reader.execute("VISUALISE FROM ggsql:world DRAW spatial")
249+
writer = ggsql.VegaLiteWriter()
250+
251+
chart = writer.render_chart(spec)
252+
assert isinstance(chart, altair.TopLevelMixin)
253+
# The chart survives Altair-level round-tripping (this is how the
254+
# spec reaches the browser widget)
255+
assert "projection" in chart.to_dict()
256+
257+
220258
class TestRenderAltairDataFrameConversion:
221259
"""Tests for DataFrame handling in render_altair()."""
222260

0 commit comments

Comments
 (0)