rusqlite-gpkg
GeoPackage reader/writer built on top of rusqlite.
Web Demo
A simple GitHub Pages demo is available with a button to generate
and download a .gpkg file in the browser using a Web Worker + OPFS.
https://yutannihilation.github.io/rusqlite-gpkg/
See web/README.md for implementation details and design notes.
Overview
rusqlite-gpkg provides a small API around the main GeoPackage concepts:
Feature layers (spatial data with geometry):
Gpkgrepresents the GeoPackage connection.GpkgLayerrepresents a single feature layer (table with geometry column).GpkgFeaturerepresents a single feature (row with geometry + properties).
Attribute tables (non-spatial tabular data, no geometry):
GpkgAttributeTablerepresents a single attribute table.GpkgAttributeRowrepresents a single row (properties only).
Value represents a single property value in both cases.
Apache Arrow support is available behind the arrow feature flag.
You can find some example codes in the bottom of this README.
The library focuses on simple, explicit flows. You control how layers are created and which property columns are present.
Browser usage (to_bytes / from_bytes)
Web environments often cannot access files directly (OPFS can be used by
rusqlite, but this crate does not currently expose a way to enable it). In
those cases, the recommended workflow is to serialize a GeoPackage to bytes.
use Gpkg;
let gpkg = open_in_memory?;
// ... write layers/features ...
let bytes = gpkg.to_bytes?; // store bytes in IndexedDB, send over network, etc.
let restored = from_bytes?;
# Ok::
Gpkg
Gpkg represents the GeoPackage connection and is the entry point for almost all
operations. There are multiple ways to open it:
Gpkg::open_read_only(path): open an existing file without write access.Gpkg::open(path): open a new or existing file for read/write.Gpkg::open_in_memory(): create a transient in-memory GeoPackage.
From a Gpkg, you can discover or create content:
Feature layers:
list_layers()returns the feature layer names.get_layer(name)loads aGpkgLayerby name.create_layer(...)creates a new feature layer and returns aGpkgLayer.
Attribute tables:
list_attribute_tables()returns the attribute table names.get_attribute_table(name)loads aGpkgAttributeTableby name.create_attribute_table(...)creates a new attribute table and returns aGpkgAttributeTable.
use Gpkg;
let gpkg = open_read_only?;
let layers = gpkg.list_layers?;
let layer = gpkg.get_layer?;
# Ok::
GpkgLayer
GpkgLayer represents a single feature table. You typically get it from
Gpkg::get_layer (for existing data) or Gpkg::create_layer (for new data).
It exposes the layer schema (geometry column name, property columns) and
methods to iterate, insert, or update features. Insertions and updates accept
any geometry that implements geo_traits::GeometryTrait<T = f64>, including
common types from geo_types and parsed wkt::Wkt.
GpkgLayer::features() always allocates a Vec<GpkgFeature> for the whole
layer. For large datasets, use features_batch(batch_size) to stream features
in chunks and limit peak memory.
use Point;
use ;
let gpkg = new?;
let columns = vec!;
let layer = gpkg.create_layer?;
layer.insert?;
let count = layer.features?.count;
# Ok::
Batch iteration example:
use Gpkg;
let gpkg = open_read_only?;
let layer = gpkg.get_layer?;
for batch in layer.features_batch?
# Ok::
You might notice the params! macro in the example above. It is useful when
you want to pass a fixed list of values.
params! accepts Option<T> and converts None to SQL NULL. Because None
has no inherent type, you may need to annotate it:
layer.insert?;
When programmatically constructing parameters, build an iterator of &Value
from owned values:
use Value;
let raw = vec!;
let values: = raw.iter.map.collect;
layer.insert?;
GpkgFeature
GpkgFeature represents one row in a layer. You usually obtain it by iterating
GpkgLayer::features(). It provides the primary key (id()), geometry (geometry()),
and property access via property(name) returning an owned Value. The geometry is returned as a
wkb::reader::Wkb, which you can inspect or convert to WKT for display.
use Gpkg;
use write_geometry;
let gpkg = open_read_only?;
let layer = gpkg.get_layer?;
let features = layer.features?;
let feature = features.first.expect;
let id = feature.id;
let geom = feature.geometry?;
let mut wkt = Stringnew;
write_geometry?;
let name: String = feature
.property
.ok_or?
.try_into?;
# Ok::
Value
Value is the crate's owned dynamic value used for feature properties. It
mirrors SQLite's dynamic typing (null, integer, real, text, blob) and is
returned by GpkgFeature::property as Option<Value>. Convert using
try_into() or match directly.
use Gpkg;
let gpkg = open_read_only?;
let layer = gpkg.get_layer?;
let features = layer.features?;
let feature = features.first.expect;
let name: String = feature.property.ok_or?.try_into?;
let active: bool = feature.property.ok_or?.try_into?;
# Ok::
The conversion above returns an error if the value is NULL. If you want to
handle NULL, convert to Option<T>; NULL becomes None and non-null values
become Some(T):
use Value;
let value = Null;
let maybe_i64: = value.try_into?;
assert_eq!;
# Ok::
GpkgAttributeTable
GpkgAttributeTable represents a non-spatial table (no geometry column).
It follows the GeoPackage spec Section 2.4 (data_type = 'attributes' in
gpkg_contents). The API mirrors GpkgLayer but without geometry arguments.
use ;
let gpkg = open_in_memory?;
let columns = vec!;
let table = gpkg.create_attribute_table?;
table.insert?;
table.insert?;
let rows = table.rows?;
let name: String = rows.property.unwrap.try_into?;
assert_eq!;
# Ok::
Disclaimer
Most of the implementation is coded by Codex, while the primary idea is based on my own work in https://github.com/yutannihilation/duckdb-ext-st-read-multi/pulls. This probably requires more testing against real data; feedback is welcome!
Prior Work
Example
Reader
use ;
use write_geometry;
Writer
use Point;
use ;
Arrow reader
use ;
Arrow writer
[!WARNING] When the writer auto-registers a non-default SRS (anything other than the built-in EPSG:4326), the
definitioncolumn ingpkg_spatial_ref_sysis set to"undefined"because we don't have a WKT1 source for arbitrary EPSG codes. Some GeoPackage consumers may expect a proper WKT1 definition there.
use ;
use ;
use ;
use Arc;
Arrow attribute table reader/writer
use ;
use ;
use ;
use Arc;
Arrow geometry handling
use WkbArray;
use GeoArrowArrayAccessor;
use ;