geopackage
A Rust implementation of the OGC GeoPackage 1.4 format: vector features, attribute tables, spatial indexing, and columnar I/O through Apache Arrow. Pre-1.0: the API will change without notice.
Install
[]
= "0.1"
= "0.7" # any geo-traits implementation works; this is the common one
Columnar read and write through Apache Arrow is behind an off-by-default
feature, which adds the arrow-array and arrow-schema dependencies:
= { = "0.1", = ["arrow"] }
SQLite is bundled and built from source, so a C compiler is required and there is no system SQLite dependency. The minimum supported Rust version is 1.95.
Example
Create a file, declare a point layer, write features, and query by bounding box.
use Point;
use ;
use ;
let gpkg = create?;
gpkg.create_layer?;
// `create_layer` builds a spatial index; decline it with
// `.spatial_index(false)` on the builder.
let layer = gpkg.layer?;
layer.write_all?;
// Uses the RTree index when one is present, a full scan otherwise.
for feature in layer.features_in?
Columnar read and write
With the arrow feature, Layer::read_arrow reads a layer as Arrow record
batches, on min(4, available parallelism) threads by default, and
Layer::write_arrow writes batches back through the same path as write_all.
Geometry is a GeoArrow WKB column whose metadata stores the CRS as PROJJSON.
Layer::arrow_schema and TableSchemaBuilder::from_arrow_schema give the two
directions of the type mapping, so a layer can be copied without restating its
schema.
More examples
Runnable programs in geopackage/examples:
| Example | What it shows |
|---|---|
quickstart |
The example above. |
inspect |
Layers, schemas, SRS, feature counts and spatial-index health for a file, like 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, which engages the bulk index build. |
bbox_query |
features_in bounding-box queries and the select WHERE passthrough, with lazy geometry parsing. |
repair_index |
Detecting Legacy and Stale spatial indexes and repairing them. |
Configuration
The defaults produce a single-file GeoPackage with an indexed feature layer. What is settable:
| Setting | Where | Default |
|---|---|---|
Journal mode and synchronous level |
OpenOptions |
unset: an existing file keeps its mode, a new file is DELETE, synchronous is SQLite's default; WAL is opt-in |
| Whether a new layer is indexed | TableSchemaBuilder::spatial_index |
true |
| Primary-key and geometry column names | TableSchemaBuilder, GeometrySpec |
fid, geom |
| Rows sharing a write transaction | the batch_size argument of write_all / write_arrow |
caller-supplied; 0 writes all rows in one transaction |
| Bulk index build: row threshold, structural check, RTree node fill | BulkIndexOptions |
10,000 rows, RtreeOnly, 1.0 |
DATETIME parsing, and whether a value its declared type does not strictly permit is read or rejected |
ConversionOptions |
strict, lenient |
| Rows per Arrow batch, and threads the columnar read uses | ArrowReadOptions |
65,536 rows, min(4, available parallelism) |
| Geometry bytes per Arrow batch; a batch that would cross it is emitted with fewer rows | ArrowReadOptions::max_batch_bytes |
min(INT32_MAX, RAM / 4); the column's Arrow offsets are 32-bit, so 2 GB is a hard ceiling |
Each setting is documented in full on its type in the
crate documentation. Anything not covered is
reachable as SQL through GeoPackage::connection().
Workspace
| Crate | Purpose |
|---|---|
geopackage-core |
Format primitives, no IO or SQLite: GeoPackage Binary (GPB) header codec, normative table DDL, version-aware RTree trigger SQL, identifier quoting, application_id/user_version handling. |
geopackage |
The container: create/open over rusqlite with bundled SQLite, the feature and attribute read and write paths, columnar read and write through Apache Arrow (feature arrow), the RTree spatial-index lifecycle, and the ST_* SQL functions the index triggers require. |
geopackage-core/fuzz |
cargo-fuzz targets for the GPB parser. |
Design
- Synchronous API over rusqlite.
GeoPackage::connection()andfrom_connection()expose the underlying connection; SQLite is the query engine, so anything the API does not cover is a query away. - 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. - New indexes get the GeoPackage 1.4 trigger set. Older and mixed trigger
generations, a known source of file corruption, are detected and repaired
with
repair_spatial_indexrather than silently mixed. - CRS definitions are stored, never transformed: there is no PROJ dependency
and no coordinate transformation.
add_epsg_srswrites WKT1 where the code has one, and WKT2 through thegpkg_crs_wkt_1_1extension otherwise (for codes with no WKT1 form, such as the geographic 3D EPSG:4979), matching GDAL.
Conformance
Files written by this crate are checked against OGC
ets-gpkg12: 40 passed, 1
failed, where the failing test's 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 reports two
advisory findings, both on deliberate test-file choices. A GDAL round-trip
byte-compares geometry WKB and attribute values after an ogr2ogr copy, and
the test corpus includes GDAL-written, QGIS-written and raw-SQLite files.
Known limitations
- Untrusted files can trigger a large allocation. The
wkbreader 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. The fix belongs upstream in georust/wkb; do not parse untrusted GeoPackage files. 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. - Tile pyramids are not implemented. A file's tile tables are visible
through
contents()but there is no read or write path for them.
License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.