rusqlite-gpkg
GeoPackage reader/writer built on top of rusqlite.
Overview
rusqlite-gpkg provides a small API around the main GeoPackage concepts:
Gpkgrepresents the whole data of GeoPackage data.GpkgLayerrepresents a single layer in the data.GpkgFeaturerepresents a single feature in the layer.Valuerepresents a single property value related to the feature.
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 layers:
list_layers()returns the layer/table names.get_layer(name)loads aGpkgLayerby name.create_layer(...)creates a new feature layer and returns aGpkgLayer.
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::
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 geometry handling
use WkbArray;
use GeoArrowArrayAccessor;
use ;