kenro 0.4.0

SpatiaLite-style spatial SQL for SQLite in pure Rust — PostGIS-compatible ST_ functions, GeoPackage R-tree, CRS transform, H3, MVT. Use via rusqlite, loadable extension, or WASM
Documentation
[workspace]
members = [
    "crates/kenro-abi",
    "crates/kenro-ext",
    "crates/kenro-ext-test",
    "crates/kenro-wasm",
]
# WARNING: never build/test with `--workspace` (or multiple -p spanning kenro
# and kenro-ext). kenro's dev-deps need rusqlite/bundled while kenro-ext needs
# rusqlite/loadable_extension; unified in one cargo invocation,
# loadable_extension wins the libsqlite3-sys build, nothing links a real
# SQLite, and test binaries crash at runtime (it is NOT a build error).
# Use separate invocations: `cargo test` (root = kenro only),
# `cargo build -p kenro-ext`, `cargo test -p kenro-ext-test`.
# (kenro-wasm has no libsqlite3-sys in its graph and cannot participate in
# the collision, but keep the per-package discipline anyway.)

# Size-optimized release profile, shared by the wasm and loadable-extension
# artifacts (measured in docs/wasm.md; wasm32 always aborts on panic, no
# panic setting needed).
[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
strip = true

[package]
name = "kenro"
version = "0.4.0"
edition = "2024"
rust-version = "1.85"
description = "SpatiaLite-style spatial SQL for SQLite in pure Rust — PostGIS-compatible ST_ functions, GeoPackage R-tree, CRS transform, H3, MVT. Use via rusqlite, loadable extension, or WASM"
license = "MIT OR Apache-2.0"
repository = "https://github.com/reearth/kenro"
keywords = ["sqlite", "gis", "spatialite", "postgis", "geopackage"]
categories = ["database", "science::geo"]
readme = "README.md"
exclude = [".github/"]

[features]
# The default set covers most use cases (I/O, predicates, GeoPackage
# triggers, measures/processing, transform, H3, GeoJSON) while keeping the
# build small; `full` adds the overlay engine on top (MVT is already in the
# default set — see the `mvt` feature's note below).
default = ["transform", "h3", "geojson", "mvt"]
full = [
    "transform",
    "h3",
    "geojson",
    "mvt",
    "overlay",
    "spheroid",
    "concave-hull",
    "delaunay",
    "crs-full",
    "gml",
    "text-encodings",
    "voronoi",
    "routing",
]
rusqlite = ["dep:rusqlite"]
transform = ["dep:proj4rs"]
h3 = ["dep:h3o"]
geojson = ["dep:geojson"]
# Overlay engine: ST_Intersection/Difference/SymDifference/Union/Buffer and
# the ST_Union aggregate (pulls i_overlay's mesh — the largest single
# contributor to wasm size). ST_Split reaches i_overlay's slice directly
# because geo does not re-export it; the version must stay pinned to whatever
# geo depends on, or cargo links two copies of the engine.
overlay = ["dep:i_overlay"]
# ST_AsKML / ST_AsSVG. No XML library: the only non-numeric text is the
# caller's namespace prefix, which is validated rather than escaped. KML is
# defined in WGS84 and PostGIS reprojects to it rather than labelling the
# SRID, so this pulls `transform`.
text-encodings = ["transform"]
# Ellipsoidal measures: ST_DistanceSpheroid / ST_LengthSpheroid, which pull
# geographiclib (~17 KB of wasm). The spherical ST_DistanceSphere stays in the
# default set — it costs nothing extra and answers "how many metres apart" for
# most callers; the ellipsoid buys the last 0.1%.
spheroid = []
# The two most expensive functions in the catalog, by measured wasm size on
# the standard tier: ST_ConcaveHull (+41 KB) and ST_DelaunayTriangles
# (+81 KB, pulling spade). Both are in `full`; neither is worth carrying in a
# build that does not call them.
concave-hull = []
delaunay = []
# ST_VoronoiPolygons / ST_VoronoiLines. The cells are the dual of the Delaunay
# triangulation (`delaunay`, spade) and geo clips them with BooleanOps, which
# is i_overlay (`overlay`). Naming both keeps a `delaunay`-only build from
# quietly acquiring the overlay mesh — the single largest size contributor.
voronoi = ["delaunay", "overlay"]
# Vector tiles: ST_AsMVTGeom + the ST_AsMVT aggregate. Clipping is done by
# dedicated axis-aligned-rectangle algorithms, so this does NOT pull the
# overlay engine and is cheap enough to sit in the default set.
mvt = []
# GML 2/3 I/O. Reading pulls quick-xml (+13 KB of wasm, measured); writing is
# hand-rolled like the WKT and GeoJSON emitters. kenro validates nothing
# against a schema, which is what libxml2 buys SpatiaLite here.
gml = ["dep:quick-xml"]
# The full crs-definitions EPSG registry as a fallback for codes outside
# kenro's curated table — every national and local grid. In `full`, because a
# build that says "full" should transform to EPSG:6677 without a rebuild; the
# cost is measured in docs/wasm.md. Codes are u16, so EPSG > 65535 is still
# out of reach.
crs-full = ["transform", "proj4rs/crs-definitions"]
# pgRouting-style shortest paths: the kenro_dijkstra / kenro_dijkstra_cost /
# kenro_drivingdistance aggregates. Pure code — a hand-rolled binary-heap
# Dijkstra over the rows the aggregate collects, no new dependencies, and the
# JSON result rides serde_json, which is already unconditional. Off by default
# because a spatial build does not automatically want a graph engine.
routing = []

[dependencies]
geo = "0.33"
geo-types = "0.7"
geozero = { version = "0.15", default-features = false, features = [
    "with-wkb",
    "with-wkt",
    "with-geo",
] }
serde_json = "1"
thiserror = "2"
rusqlite = { version = "0.40", optional = true, default-features = false, features = [
    "functions",
] }
proj4rs = { version = "0.1.10", optional = true, default-features = false, features = [
    "geo-types",
] }
h3o = { version = "0.10", optional = true }
geojson = { version = "0.24", optional = true, default-features = false, features = [
    "geo-types",
] }
quick-xml = { version = "0.37", optional = true, default-features = false }
# Kept at the exact version geo 0.33 resolves to; see the `overlay` feature.
i_overlay = { version = "4.5", optional = true, default-features = false }

[dev-dependencies]
rusqlite = { version = "0.40", features = ["functions", "bundled"] }
proptest = "1"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
geo = "0.33"
geo-types = "0.7"

[[example]]
name = "accuracy_report"
required-features = ["transform"]

[[example]]
name = "routing_bench"
required-features = ["rusqlite", "routing"]

[[test]]
name = "manifest_consistency"
required-features = ["rusqlite"]

[[test]]
name = "postgis_compat"
required-features = ["rusqlite"]

[[test]]
name = "threed_derived"
required-features = ["rusqlite", "overlay", "transform"]

[[test]]
name = "golden_predicates"
required-features = ["rusqlite"]

[[test]]
name = "gpkg_rtree"
required-features = ["rusqlite"]

[[test]]
name = "register_smoke"
required-features = ["rusqlite", "overlay", "mvt", "transform", "h3", "geojson"]

[[test]]
name = "golden_transform"
required-features = ["rusqlite", "transform"]

[[test]]
name = "golden_h3"
required-features = ["rusqlite", "h3"]

[[test]]
name = "golden_geojson"
required-features = ["rusqlite", "geojson"]

[[test]]
name = "golden_accessors"
required-features = ["rusqlite"]

[[test]]
name = "golden_processing"
required-features = ["rusqlite"]

[[test]]
name = "golden_bool_ops"
required-features = ["rusqlite", "overlay"]

[[test]]
name = "golden_buffer"
required-features = ["rusqlite", "overlay"]

[[test]]
name = "golden_mvt"
required-features = ["rusqlite", "mvt"]

[[test]]
name = "golden_routing"
required-features = ["rusqlite", "routing"]

[[test]]
name = "golden_box_text"
required-features = ["rusqlite"]

[[test]]
name = "golden_quantize"
required-features = ["rusqlite"]

[[test]]
name = "gpkg_polyhedralsurface"
required-features = ["rusqlite"]

[[test]]
name = "proptests"
required-features = ["overlay", "mvt"]