Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0563504
grammatical accommodations
teunbrand Apr 22, 2026
5790373
Add Spatial geom type for choropleth/geographic visualization
teunbrand Apr 22, 2026
0169edb
Add spatial feature: geometry auto-detection, geozero conversion, and…
teunbrand Apr 22, 2026
f896b23
Add spatial tests, fix geometry encoding, remove auto-detection
teunbrand Apr 22, 2026
a4dae74
Allow arbitrary SQL setup statements (INSTALL, LOAD, SET, etc.)
teunbrand Apr 23, 2026
b473542
Merge branch 'main' into spatial_start
teunbrand Apr 23, 2026
f5b2617
refactor(reader): replace ColumnBuilder with direct Arrow export via …
teunbrand Apr 23, 2026
2694724
refactor(execute): use DDL instead of Arrow round-trip for temp table…
teunbrand Apr 24, 2026
ca0b1c3
feat(spatial): conditional ST_AsWKB stat transform and native GEOMETR…
teunbrand Apr 24, 2026
8a3c622
Revert "refactor(execute): use DDL instead of Arrow round-trip for te…
teunbrand Apr 24, 2026
ab8c9eb
Merge branch 'main' into spatial_start
teunbrand Apr 24, 2026
e20af2f
refactor(spatial): remove string geometry paths, require native GEOMETRY
teunbrand Apr 24, 2026
143c05b
refactor(spatial): use dialect for geometry-to-WKB conversion
teunbrand Apr 24, 2026
1d4fefb
feat(data): add ggsql:world built-in dataset
teunbrand Apr 24, 2026
07001ee
feat(spatial): auto-load spatial extension via dialect
teunbrand Apr 24, 2026
80a31aa
cargo fmt
teunbrand Apr 24, 2026
c9442e7
feat(spatial): auto-detect geometry column by name and type
teunbrand Apr 24, 2026
f1bb971
fix(spatial): load spatial extension before registering ggsql:world
teunbrand Apr 24, 2026
85ddbf1
override defaults chosen by claude
teunbrand Apr 24, 2026
e59582c
add docs
teunbrand Apr 24, 2026
75e4c91
Merge branch 'main' into spatial_start
teunbrand Apr 24, 2026
ed840be
candles in pentagram shape for clippy
teunbrand Apr 24, 2026
4737f0a
beg for clippy's forgiveness
teunbrand Apr 24, 2026
dbca85a
try installing the spatial module during the test
teunbrand Apr 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ proptest = "1.4"
# Color interpolation
palette = "0.7"

# Spatial
geozero = { version = "0.14", default-features = false }
hex = "0.4"

# Utilities
regex = "1.10"
chrono = "0.4"
Expand Down
2 changes: 2 additions & 0 deletions doc/ggsql.xml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@
<item>arrow</item>
<item>rule</item>
<item>errorbar</item>
<item>spatial</item>
</list>

<!-- Aesthetics -->
Expand Down Expand Up @@ -188,6 +189,7 @@
<!-- Specialty aesthetics -->
<item>slope</item>
<item>intercept</item>
<item>geometry</item>
<!-- Facet aesthetics -->
<item>panel</item>
<item>row</item>
Expand Down
1 change: 1 addition & 0 deletions doc/syntax/index.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ There are many different layers to choose from when visualising your data. Some
- [`boxplot`](layer/type/boxplot.qmd) displays continuous variables as 5-number summaries.
- [`errorbar`](layer/type/errorbar.qmd) a line segment with hinges at the endpoints.
- [`smooth`](layer/type/smooth.qmd) a trendline that follows the data shape.
- [`spatial`](layer/type/spatial.qmd) simple features from geometry.

### Position adjustments
- [`stack`](layer/position/stack.qmd) places objects with a shared baseline on top of each other.
Expand Down
84 changes: 84 additions & 0 deletions doc/syntax/layer/type/spatial.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
title: "Spatial"
---

> Layers are declared with the [`DRAW` clause](../../clause/draw.qmd). Read the documentation for this clause for a thorough description of how to use it.

The spatial layer is used to render geographic geometries consisting of polygons, lines and points used to make maps like choropleths.
It differs from other layers in that uses a special [simple features](https://en.wikipedia.org/wiki/Simple_Features) geometry column that defines the shapes.

## Aesthetics
The following aesthetics are recognised by the spatial layer.

### Required
* `geometry`: a column of simple features.

Note that the `geometry` column is required, but an attempt is made to detect such a column automatically.
In practise, this mapping does not often need to be declared.

### Optional
* `stroke` The colour of the lines.
* `fill` The colour of the inner area.
* `colour` Shorthand for setting `stroke` and `fill` simultaneously.
* `opacity` The opacity of colours.
* `linewidth` The width of the lines.
* `linetype` The dash pattern of the line.

## Settings
The spatial layer has no additional settings.

## Data transformation
The spatial layer transforms the `geometry` column to [Well-Known Binary](https://libgeos.org/specifications/wkb/).

## Orientation
The spatial layer has no orientations.

## Examples

Note that depending on your reader, you may need to activate modules for spatial analysis.

```{ggsql}
-- For example, for DuckDB, one could use:
INSTALL spatial;
LOAD spatial;
```

A basic map of the world using built-in data.
Note that the geometry column is automatically detected.

```{ggsql}
VISUALISE FROM ggsql:world
DRAW spatial
```

If the geometry column isn't automatically detected —for example because it has a non-standard name— you may need to declare the mapping explicitly.

```{ggsql}
SELECT geom AS foo FROM ggsql:world
VISUALISE
DRAW spatial MAPPING foo AS geometry
```

Filtering on other columns.

```{ggsql}
VISUALISE FROM ggsql:world
DRAW spatial FILTER continent == 'Asia'
```

Filtering based on spatial operations.

```{ggsql}
VISUALISE FROM ggsql:world
DRAW spatial
FILTER ST_Intersects(geom, ST_MAkeEnvelope(-20.0, -35.0, 55.0, 38.0))
```

Make a choropleth map by mapping a variable to a fill aesthetic.

```{ggsql}
VISUALISE FROM ggsql:world
DRAW spatial
MAPPING population AS fill
SETTING opacity => 1
```
6 changes: 3 additions & 3 deletions ggsql-vscode/syntaxes/ggsql.tmLanguage.json
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@
{
"comment": "Specialty and computed aesthetics",
"name": "support.type.aesthetic.ggsql",
"match": "\\b(weight|coef|intercept|offset|density|count|intensity)\\b"
"match": "\\b(weight|coef|intercept|offset|density|count|intensity|geometry)\\b"
},
{
"comment": "Facet aesthetics",
Expand Down Expand Up @@ -320,7 +320,7 @@
{
"comment": "Geom types from grammar.js",
"name": "support.type.geom.ggsql",
"match": "\\b(point|line|path|bar|col|area|tile|polygon|ribbon|histogram|density|smooth|boxplot|violin|text|label|segment|arrow|rule|errorbar)\\b"
"match": "\\b(point|line|path|bar|col|area|tile|polygon|ribbon|histogram|density|smooth|boxplot|violin|text|label|segment|arrow|rule|errorbar|spatial)\\b"
},
{ "include": "#common-clause-patterns" }
]
Expand All @@ -334,7 +334,7 @@
"patterns": [
{
"name": "support.type.geom.ggsql",
"match": "\\b(point|line|path|bar|col|area|tile|polygon|ribbon|histogram|density|smooth|boxplot|violin|text|label|segment|arrow|rule|errorbar)\\b"
"match": "\\b(point|line|path|bar|col|area|tile|polygon|ribbon|histogram|density|smooth|boxplot|violin|text|label|segment|arrow|rule|errorbar|spatial)\\b"
},
{ "include": "#common-clause-patterns" }
]
Expand Down
6 changes: 5 additions & 1 deletion src/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ bytes = { workspace = true }
# Writers
plotters = { workspace = true, optional = true }

# Spatial
geozero = { workspace = true, optional = true, features = ["with-wkb", "with-geojson"] }

# Serialization
serde.workspace = true
serde_json.workspace = true
Expand All @@ -64,13 +67,14 @@ tempfile = "3.8"
ureq = "3"

[features]
default = ["duckdb", "sqlite", "vegalite", "ipc", "parquet", "builtin-data", "odbc"]
default = ["duckdb", "sqlite", "vegalite", "ipc", "parquet", "builtin-data", "odbc", "spatial"]
ipc = []
duckdb = ["dep:duckdb"]
parquet = ["dep:parquet"]
postgres = ["dep:postgres"]
sqlite = ["dep:rusqlite"]
odbc = ["dep:odbc-api", "dep:toml_edit"]
spatial = ["dep:geozero"]
vegalite = []
ggplot2 = []
builtin-data = []
Expand Down
Binary file added src/data/world.parquet
Binary file not shown.
Loading
Loading