DuckDB extension for converting GEOMETRY/WKB to and from GeoArrow native encodings, powered by geoarrow-c.
Installing and Loading
INSTALL duck_geoarrow FROM community;
LOAD duck_geoarrow;
Example
INSTALL duck_geoarrow FROM community;
LOAD duck_geoarrow;
-- Read a GeoArrow-encoded GeoParquet file written by geopandas. A geometry column
-- written with encoding="polygon" arrives as STRUCT(x DOUBLE, y DOUBLE)[][].
SELECT county_name, st_geomfromgeoarrow('polygon', geometry) AS geom
FROM read_parquet('county_regions.parquet');
-- Write GEOMETRY back out as a GeoArrow native encoding
COPY (SELECT county_name, st_asgeoarrowpolygon(geom) AS geometry FROM counties)
TO 'counties.parquet';
-- GEOMETRY -> GeoArrow native encoding, one function per geometry type
SELECT st_asgeoarrowpoint('POINT(30 10)'::GEOMETRY);
-- {'x': 30.0, 'y': 10.0}
SELECT st_asgeoarrowpolygon('POLYGON((0 0, 1 0, 1 1, 0 0))'::GEOMETRY);
-- [[{'x': 0.0, 'y': 0.0}, {'x': 1.0, 'y': 0.0}, {'x': 1.0, 'y': 1.0}, {'x': 0.0, 'y': 0.0}]]
-- GeoArrow native encoding -> GEOMETRY; the type name disambiguates colliding shapes
SELECT st_geomfromgeoarrow('linestring', [{'x': 0.0, 'y': 0.0}, {'x': 1.0, 'y': 1.0}]),
st_geomfromgeoarrow('multipoint', [{'x': 0.0, 'y': 0.0}, {'x': 1.0, 'y': 1.0}]);
-- LINESTRING (0 0, 1 1) | MULTIPOINT (0 0, 1 1)
-- Z and M: reading infers them from the value, writing takes them as an argument
SELECT st_geomfromgeoarrow('point', {'x': 1.0, 'y': 2.0, 'z': 3.0});
-- POINT Z (1 2 3)
SELECT st_asgeoarrowpoint('POINT Z (1 2 3)'::GEOMETRY, 'xyz');
-- {'x': 1.0, 'y': 2.0, 'z': 3.0}
-- Flat struct form: one struct per geometry with parallel coordinate arrays
SELECT st_asgeoarrow('POINT(30 10)'::GEOMETRY);
-- {'geometry_type': 1, 'xs': [30.0], 'ys': [10.0], 'ring_offsets': [], 'geom_offsets': []}
SELECT duck_geoarrow_version();
About duck_geoarrow
duck_geoarrow converts between DuckDB GEOMETRY (or WKB BLOB) and the
GeoArrow coordinate encodings, powered by the
geoarrow-c library.
Two GeoArrow representations are supported:
- Native encodings: nested lists of separated coordinates (
STRUCT(x, y)[][]for a Polygon, and so on). This is what GeoParquet, geoarrow-pyarrow and geopandas produce, so it is the one to reach for when reading or writing files. - A flat struct: one struct per geometry holding parallel coordinate and offset
arrays (
geometry_type,xs,ys, optionalzs/ms,ring_offsets,geom_offsets). Convenient for hand-building geometries in SQL.
Both directions support XY, XYZ, XYM and XYZM.
Functions
| Function | Direction | Notes |
|---|---|---|
st_geomfromgeoarrow(type, value) |
GeoArrow → GEOMETRY | Generic: any native encoding, type given as a string |
st_geomfromgeoarrow<type>(value) |
GeoArrow → GEOMETRY | One name per geometry type |
st_geomfromgeoarrow(struct) |
GeoArrow → GEOMETRY | Flat struct form |
st_asgeoarrow<type>(geom[, dims]) |
GEOMETRY → GeoArrow | Native encoding for one geometry type |
st_asgeoarrow(geom[, dims]) |
GEOMETRY → GeoArrow | Flat struct form |
duck_geoarrow_version() |
— | Extension and geoarrow-c versions |
<type> is one of point, linestring, polygon, multipoint, multilinestring or
multipolygon. All conversion functions accept either GEOMETRY or a BLOB holding WKB.
Native encodings
| Geometry type | DuckDB type | GEOMETRY → GeoArrow | GeoArrow → GEOMETRY |
|---|---|---|---|
| Point | STRUCT(x, y) |
st_asgeoarrowpoint |
st_geomfromgeoarrowpoint |
| LineString | STRUCT(x, y)[] |
st_asgeoarrowlinestring |
st_geomfromgeoarrowlinestring |
| Polygon | STRUCT(x, y)[][] |
st_asgeoarrowpolygon |
st_geomfromgeoarrowpolygon |
| MultiPoint | STRUCT(x, y)[] |
st_asgeoarrowmultipoint |
st_geomfromgeoarrowmultipoint |
| MultiLineString | STRUCT(x, y)[][] |
st_asgeoarrowmultilinestring |
st_geomfromgeoarrowmultilinestring |
| MultiPolygon | STRUCT(x, y)[][][] |
st_asgeoarrowmultipolygon |
st_geomfromgeoarrowmultipolygon |
These are the separated-coordinate layouts from the GeoArrow specification, so the values interoperate directly with other GeoArrow implementations.
The generic st_geomfromgeoarrow(type, value) exists because the DuckDB types collide:
LineString and MultiPoint are both STRUCT(x, y)[], and Polygon and MultiLineString are
both STRUCT(x, y)[][]. The type name is resolved at bind time, so an unknown type or a
mismatched shape is a binder error rather than a runtime one. Names are case- and
separator-insensitive and may carry a z, m or zm suffix.
Z and M dimensions
GeoArrow carries extra ordinates in the coordinate struct (STRUCT(x, y, z),
STRUCT(x, y, m), STRUCT(x, y, z, m)), and the flat struct gains matching zs / ms
lists. Reading needs no extra argument since the value's type says which ordinates are
present. Writing takes the dimensions as an argument (xy, xyz, xym, xyzm) because
a SQL function's return type must be fixed before any data is seen; omitting it means
xy. Dropping ordinates is allowed, inventing them is not: asking for xyz from a 2D
geometry raises an error rather than filling in NaNs.
Notes
NULLinput producesNULLoutput. ANULLinside a list (a null ring, say) is read as an empty ring, since the GeoArrow native encoding only carries validity at the top level.- GeometryCollection, and the
geoarrow.boxand interleaved-coordinate encodings, are not supported.
Added Functions
| function_name | function_type | description | comment | examples |
|---|---|---|---|---|
| duck_geoarrow_version | scalar | Returns the version of the duck_geoarrow extension together with the version of the bundled geoarrow-c library. | NULL | [duck_geoarrow_version()] |
| st_asgeoarrow | scalar | Converts a GEOMETRY or WKB BLOB to the flat GeoArrow encoding, writing the ordinates named by dims ('xy', 'xyz', 'xym' or 'xyzm'); dropping ordinates the geometry carries is allowed, asking for ones it does not have is an error. | NULL | [st_asgeoarrow('LINESTRING ZM (0 0 1 5, 1 1 2 6)'::GEOMETRY, 'xyzm')] |
| st_asgeoarrow | scalar | Converts a GEOMETRY or WKB BLOB to the flat GeoArrow encoding: one STRUCT per geometry holding a geometry_type code alongside parallel coordinate (xs, ys) and offset (ring_offsets, geom_offsets) arrays, with XY coordinates. | NULL | [st_asgeoarrow('POINT(30 10)'::GEOMETRY)] |
| st_asgeoarrowlinestring | scalar | Converts a LineString GEOMETRY or WKB BLOB into the GeoArrow native encoding for that geometry type (STRUCT(x DOUBLE, y DOUBLE)[]), erroring if the input is not a LineString. | NULL | [st_asgeoarrowlinestring('LINESTRING (0 0, 4 0)'::GEOMETRY)] |
| st_asgeoarrowlinestring | scalar | Converts a LineString GEOMETRY or WKB BLOB into the GeoArrow native encoding for that geometry type, writing the ordinates named by dims ('xy', 'xyz', 'xym' or 'xyzm'). | NULL | [st_asgeoarrowlinestring('LINESTRING Z (0 0 1, 4 0 2)'::GEOMETRY, 'xyz')] |
| st_asgeoarrowmultilinestring | scalar | Converts a MultiLineString GEOMETRY or WKB BLOB into the GeoArrow native encoding for that geometry type (STRUCT(x DOUBLE, y DOUBLE)[][]), erroring if the input is not a MultiLineString. | NULL | [st_asgeoarrowmultilinestring('MULTILINESTRING ((0 0, 4 0), (4 4, 8 8))'::GEOMETRY)] |
| st_asgeoarrowmultilinestring | scalar | Converts a MultiLineString GEOMETRY or WKB BLOB into the GeoArrow native encoding for that geometry type, writing the ordinates named by dims ('xy', 'xyz', 'xym' or 'xyzm'). | NULL | [st_asgeoarrowmultilinestring('MULTILINESTRING Z ((0 0 1, 4 0 2), (4 4 3, 8 8 4))'::GEOMETRY, 'xyz')] |
| st_asgeoarrowmultipoint | scalar | Converts a MultiPoint GEOMETRY or WKB BLOB into the GeoArrow native encoding for that geometry type (STRUCT(x DOUBLE, y DOUBLE)[]), erroring if the input is not a MultiPoint. | NULL | [st_asgeoarrowmultipoint('MULTIPOINT (0 0, 4 0)'::GEOMETRY)] |
| st_asgeoarrowmultipoint | scalar | Converts a MultiPoint GEOMETRY or WKB BLOB into the GeoArrow native encoding for that geometry type, writing the ordinates named by dims ('xy', 'xyz', 'xym' or 'xyzm'). | NULL | [st_asgeoarrowmultipoint('MULTIPOINT Z (0 0 1, 4 0 2)'::GEOMETRY, 'xyz')] |
| st_asgeoarrowmultipolygon | scalar | Converts a MultiPolygon GEOMETRY or WKB BLOB into the GeoArrow native encoding for that geometry type (STRUCT(x DOUBLE, y DOUBLE)[][][]), erroring if the input is not a MultiPolygon. | NULL | [st_asgeoarrowmultipolygon('MULTIPOLYGON (((0 0, 4 0, 4 4, 0 0)))'::GEOMETRY)] |
| st_asgeoarrowmultipolygon | scalar | Converts a MultiPolygon GEOMETRY or WKB BLOB into the GeoArrow native encoding for that geometry type, writing the ordinates named by dims ('xy', 'xyz', 'xym' or 'xyzm'). | NULL | [st_asgeoarrowmultipolygon('MULTIPOLYGON Z (((0 0 1, 4 0 2, 4 4 3, 0 0 1)))'::GEOMETRY, 'xyz')] |
| st_asgeoarrowpoint | scalar | Converts a Point GEOMETRY or WKB BLOB into the GeoArrow native encoding for that geometry type (STRUCT(x DOUBLE, y DOUBLE)), erroring if the input is not a Point. | NULL | [st_asgeoarrowpoint('POINT (30 10)'::GEOMETRY)] |
| st_asgeoarrowpoint | scalar | Converts a Point GEOMETRY or WKB BLOB into the GeoArrow native encoding for that geometry type, writing the ordinates named by dims ('xy', 'xyz', 'xym' or 'xyzm'). | NULL | [st_asgeoarrowpoint('POINT Z (0 0 1)'::GEOMETRY, 'xyz')] |
| st_asgeoarrowpolygon | scalar | Converts a Polygon GEOMETRY or WKB BLOB into the GeoArrow native encoding for that geometry type (STRUCT(x DOUBLE, y DOUBLE)[][]), erroring if the input is not a Polygon. | NULL | [st_asgeoarrowpolygon('POLYGON ((0 0, 4 0, 4 4, 0 0))'::GEOMETRY)] |
| st_asgeoarrowpolygon | scalar | Converts a Polygon GEOMETRY or WKB BLOB into the GeoArrow native encoding for that geometry type, writing the ordinates named by dims ('xy', 'xyz', 'xym' or 'xyzm'). | NULL | [st_asgeoarrowpolygon('POLYGON Z ((0 0 1, 4 0 2, 4 4 3, 0 0 1))'::GEOMETRY, 'xyz')] |
| st_geomfromgeoarrow | scalar | Converts a flat GeoArrow struct back into a GEOMETRY; fields are matched by name, so their order in the struct does not matter, and the zs / ms lists are read when present. | NULL | [st_geomfromgeoarrow({'geometry_type': 2::UTINYINT, 'xs': [0.0, 1.0, 2.0], 'ys': [0.0, 1.0, 2.0], 'ring_offsets': []::INTEGER[], 'geom_offsets': []::INTEGER[]})] |
| st_geomfromgeoarrow | scalar | Converts any GeoArrow native encoding into a GEOMETRY, with geometry_type naming the type as a constant string ('point', 'linestring', 'polygon', 'multipoint', 'multilinestring' or 'multipolygon', optionally with a z, m or zm suffix); the name is required because LineString and MultiPoint, and Polygon and MultiLineString, share one DuckDB type. | NULL | [st_geomfromgeoarrow('linestring', [{'x': 0.0, 'y': 0.0}, {'x': 1.0, 'y': 1.0}])] |
| st_geomfromgeoarrowlinestring | scalar | Converts a GeoArrow native LineString encoding (STRUCT(x DOUBLE, y DOUBLE)[], or its XYZ / XYM / XYZM equivalent) into a LineString GEOMETRY; the value's own type says which ordinates are present, so no dimensions argument is needed. | NULL | [st_geomfromgeoarrowlinestring([{'x': 0.0, 'y': 0.0}, {'x': 4.0, 'y': 0.0}])] |
| st_geomfromgeoarrowmultilinestring | scalar | Converts a GeoArrow native MultiLineString encoding (STRUCT(x DOUBLE, y DOUBLE)[][], or its XYZ / XYM / XYZM equivalent) into a MultiLineString GEOMETRY; the value's own type says which ordinates are present, so no dimensions argument is needed. | NULL | [st_geomfromgeoarrowmultilinestring([[{'x': 0.0, 'y': 0.0}, {'x': 4.0, 'y': 0.0}], [{'x': 4.0, 'y': 4.0}, {'x': 8.0, 'y': 8.0}]])] |
| st_geomfromgeoarrowmultipoint | scalar | Converts a GeoArrow native MultiPoint encoding (STRUCT(x DOUBLE, y DOUBLE)[], or its XYZ / XYM / XYZM equivalent) into a MultiPoint GEOMETRY; the value's own type says which ordinates are present, so no dimensions argument is needed. | NULL | [st_geomfromgeoarrowmultipoint([{'x': 0.0, 'y': 0.0}, {'x': 4.0, 'y': 0.0}])] |
| st_geomfromgeoarrowmultipolygon | scalar | Converts a GeoArrow native MultiPolygon encoding (STRUCT(x DOUBLE, y DOUBLE)[][][], or its XYZ / XYM / XYZM equivalent) into a MultiPolygon GEOMETRY; the value's own type says which ordinates are present, so no dimensions argument is needed. | NULL | [st_geomfromgeoarrowmultipolygon([[[{'x': 0.0, 'y': 0.0}, {'x': 4.0, 'y': 0.0}, {'x': 4.0, 'y': 4.0}, {'x': 0.0, 'y': 0.0}]]])] |
| st_geomfromgeoarrowpoint | scalar | Converts a GeoArrow native Point encoding (STRUCT(x DOUBLE, y DOUBLE), or its XYZ / XYM / XYZM equivalent) into a Point GEOMETRY; the value's own type says which ordinates are present, so no dimensions argument is needed. | NULL | [st_geomfromgeoarrowpoint({'x': 30.0, 'y': 10.0})] |
| st_geomfromgeoarrowpolygon | scalar | Converts a GeoArrow native Polygon encoding (STRUCT(x DOUBLE, y DOUBLE)[][], or its XYZ / XYM / XYZM equivalent) into a Polygon GEOMETRY; the value's own type says which ordinates are present, so no dimensions argument is needed. | NULL | [st_geomfromgeoarrowpolygon([[{'x': 0.0, 'y': 0.0}, {'x': 4.0, 'y': 0.0}, {'x': 4.0, 'y': 4.0}, {'x': 0.0, 'y': 0.0}]])] |
Overloaded Functions
This extension does not add any function overloads.
Added Types
This extension does not add any types.
Added Settings
This extension does not add any settings.