geopackage
A fast, robust, production-quality Rust implementation of the OGC GeoPackage 1.4 format, intended for use from Rust and, via a C ABI with the Arrow C Data Interface as the bulk data plane, from higher-level languages.
Status: pre-alpha (0.1.0). The read and write paths are complete and validated against external tooling (see Conformance), but the API will change without notice before 1.0.
Install
[]
= "0.1"
= "0.7" # any geo-traits implementation works; this is the common one
SQLite is bundled and built from source, so a C compiler is required; there is no system SQLite dependency. The minimum supported Rust version is 1.95.
Example
Create a file, declare a point layer, write features, index it, and query by bounding box:
use Point;
use ;
use ;
let gpkg = create?;
gpkg.create_layer?;
let layer = gpkg.layer?;
layer.create_spatial_index?;
layer.write_all?;
// Uses the RTree index when one is present, a full scan otherwise.
for feature in layer.features_in?
More examples
Runnable programs in geopackage/examples:
| Example | What it shows |
|---|---|
quickstart |
The snippet above, kept compiling. |
inspect |
Layers, schemas, SRS, feature counts and spatial-index health for a file, in the manner of ogrinfo -al -so. Uses open_lenient, so it reports problems rather than refusing to open. |
bulk_load |
Loading a large point layer with write_all, creating the index first so the bulk shadow-table build is used. |
bbox_query |
features_in bounding-box queries (RTree-accelerated or full-scan) and the select WHERE passthrough, with lazy geometry parsing. |
repair_index |
Detecting Legacy and Stale spatial indexes and repairing them. |
Workspace
| Crate | Purpose |
|---|---|
geopackage-core |
No-IO spec layer: GeoPackage Binary (GPB) header codec, normative table DDL, version-aware RTree trigger SQL, identifier quoting, application_id/user_version handling. Dependency-light by design so other implementations can share it. |
geopackage |
The library: container create/open over rusqlite (bundled + functions), the feature/attribute read and write paths, the RTree spatial-index lifecycle, and registration of the ST_IsEmpty/ST_MinX/… SQL functions required by the spatial index triggers. |
geopackage-core/fuzz |
cargo-fuzz targets (GPB parser). |
Design notes
- Sync core on rusqlite. The RTree extension's triggers call
ST_*functions that must be registered on every writing connection; sqlx-sqlite cannot register custom functions, and SQLite is synchronous anyway. Async wrappers can sit on top. - GeoPackage 1.4 trigger set (
update5/update6/update7) is emitted for new indexes; older generations are detected and repairable (repair_spatial_index) rather than silently mixed - mixed-generation triggers are a known source of file corruption (e.g. UPSERT against pre-1.4 triggers). - Escape hatches everywhere:
GeoPackage::connection()/from_connection()expose the underlying rusqlite connection. SQLite is the query engine; we do not wrap what we do not need to. - Interchange-first close. WAL is opt-in, and a handle that opted into it
checkpoints and resets the file to
DELETEon close, so a handed-over.gpkgis a single file with no sidecars.
Conformance
Files written by this crate are checked against OGC
ets-gpkg12 (40 passed, 1
failure whose regex hard-codes the GeoPackage 1.2 trigger set and rejects a
correct 1.4 one; no 1.3/1.4 ETS exists), the
PDOK validator (clean but for
two advisory findings on deliberate choices), ogrinfo, and a GDAL round-trip
that byte-compares geometry WKB and attribute values. The test corpus includes
GDAL-written, QGIS-written and raw-SQLite files. See
roadmap/04-m2-write-rtree.md
for the detailed results.
Known limitations
- Untrusted files can trigger a large allocation. The
wkb0.9.2 reader pre-allocates from element counts read out of the geometry blob without bounding them against the buffer, so a malformed 17-byte GPB blob declaring a 0xFFFFFFFF-member collection drives a multi-gigabyte allocation. Found by thegpb_geometryfuzz target. The fix belongs upstream in georust/wkb; do not parse untrusted GeoPackage files with 0.1.0. Tracked in #3. - Non-linear curve types (
CIRCULARSTRING,COMPOUNDCURVE, …) cannot have their envelopes computed and so cannot be inserted into an indexed table. Tracked in #5. - Feature iteration materialises the result set rather than streaming. Tracked in #4.
Roadmap
M1 (feature and attribute read: scan, bbox via rtree, WHERE passthrough, full
WKB envelopes) and M2 (write path, layer creation, bulk rtree build, trigger
repair) are complete and released as v0.1. Next: M3 GeoArrow RecordBatch
I/O, C ABI (geopackage-ffi), CLI, for v0.2. M4: tiles. M5: extensions (CRS
WKT2, metadata, schema, related tables).
The full roadmap, including the decision record, lives in roadmap/.
License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.