spatio 0.1.6

A high-performance, embedded spatio-temporal database for modern applications
Documentation

Spatio is a compact and efficient embedded spatio-temporal database written in Rust. It's designed for real-time 2D and 3D location data, with low memory usage, optional persistence, and native Python bindings.

No SQL parser, no external dependencies, and requires no setup.


Features

Embedded and Lightweight

  • Self-contained — Runs without external services or dependencies
  • Minimal API surface — Open, insert, and query
  • Low memory footprint — Suitable for IoT, edge, and embedded environments
  • Single-Writer Thread Safety — Uses a shared Arc (without lock upgrades) to allow concurrent readers and a single writer

Performance Scope

  • Concurrent read access — Multiple readers operate without blocking; writes are serialized under a global lock
  • Spatio-temporal queries — Use a geohash + R-tree hybrid to balance lookup precision and performance for moderate datasets
  • Configurable persistence — Append-Only File (AOF) with sync policies
  • Startup and Shutdown — AOF logs are replayed automatically on startup

Spatio-Temporal Indexing and Querying

  • Spatio-Temporal Indexing — R-Tree + geohash hybrid indexing with optional history tracking
  • Advanced Spatial Operations — Distance calculations (Haversine, Geodesic, Rhumb, Euclidean), K-nearest-neighbors, polygon queries, convex hull, bounding box operations
  • Spatio-Temporal Queries — Nearby search, bounding box, distance, containment, and time slicing
  • 3D Spatial Support — Full 3D point indexing with altitude-aware queries (spherical, cylindrical, bounding box)
  • Trajectory Support — Store and query movement over time (2D and 3D)
  • GeoJSON I/O — Supports import and export of geometries in GeoJSON format

Data Management

  • Namespaces — Isolate data logically within the same instance
  • TTL Support — Automatically removes expired data
  • Temporal Queries — Filter keys by recent activity with optional history tracking
  • Atomic Batches — Supports grouped write operations with atomic semantics.
  • Custom Configs — JSON/TOML serializable configuration

Language Support

  • Rust — Native
  • Python — Provides bindings implemented via PyO3 (pip install spatio)
  • C / C++ — Provides an extern "C" ABI for interoperability (see C ABI)

Compile-Time Feature Flags

  • time-index (default) — enables creation-time indexing and per-key history APIs. Disable it for the lightest build: cargo add spatio --no-default-features --features="aof,geojson".

Sync Strategy Configuration

  • SyncMode::All (default) — call fsync/File::sync_all after each batch of writes (durable but slower).
  • SyncMode::Data — use fdatasync/File::sync_data when your platform supports it (data-only durability).
  • sync_batch_size — number of write operations to batch before a sync when SyncPolicy::Always is selected (default: 1). Tune via Config::with_sync_batch_size to reduce syscall frequency.

Installation

Python

pip install spatio

📦 PyPI: https://pypi.org/project/spatio

Rust

Add this to your Cargo.toml:

[dependencies]
spatio = "0.1"

📦 Crates.io: https://crates.io/crates/spatio

Quick Start

Python usage lives in the dedicated bindings package—see py-spatio/README.md for up-to-date installation notes and examples.

Rust

use spatio::prelude::*;
use spatio::Point3d;
use std::time::Duration;

fn main() -> Result<()> {
    // Configure the database
    let config = Config::with_geohash_precision(9)
        .with_default_ttl(Duration::from_secs(3600));

    // Create an in-memory database with configuration
    let db = Spatio::memory_with_config(config)?;

    // Create a namespace for logical separation
    let ns = db.namespace("vehicles");

    // Insert a point (automatically indexed)
    let truck = Point::new(-74.0060, 40.7128);
    ns.insert_point("truck:001", &truck, b"Truck A", None)?;

    // Query for nearby points
    let results = ns.query_within_radius(&truck, 1000.0, 10)?;
    println!("Found {} nearby objects", results.len());

    // Check if a key exists
    if let Some(data) = ns.get("truck:001")? {
        println!("Data: {:?}", data);
    }

    // 3D spatial tracking (altitude-aware)
    let drone = Point3d::new(-74.0060, 40.7128, 100.0); // lon, lat, altitude
    db.insert_point_3d("drones", &drone, b"Drone Alpha", None)?;

    // Query within 3D sphere
    let nearby_3d = db.query_within_sphere_3d("drones", &drone, 200.0, 10)?;
    println!("Found {} drones within 200m (3D)", nearby_3d.len());

    Ok(())
}

C ABI

Note: The C ABI is experimental and is not actively developed.

The crate ships with a C-compatible ABI for embedding. Build the shared library once:

cargo build --release --lib
# target/release/libspatio.so    (Linux)
# target/release/libspatio.dylib (macOS)
# target/release/spatio.dll      (Windows)

Link the resulting library and declare the externs you need (or generate them with bindgen). Minimal usage example:

#include <stdint.h>
#include <stdio.h>
#include <string.h>

typedef struct SpatioHandle SpatioHandle;
typedef struct {
    unsigned char *data;
    size_t len;
} SpatioBuffer;

extern SpatioHandle *spatio_open(const char *path);
extern void spatio_close(SpatioHandle *handle);
extern int spatio_insert(SpatioHandle *handle, const char *key,
                         const unsigned char *value, size_t value_len);
extern int spatio_get(SpatioHandle *handle, const char *key, SpatioBuffer *out);
extern void spatio_free_buffer(SpatioBuffer buffer);

int main(void) {
    SpatioHandle *db = spatio_open("example.aof");
    if (!db) {
        fprintf(stderr, "failed to open database\n");
        return 1;
    }

    const char *key = "greeting";
    const char *value = "hello";
    spatio_insert(db, key, (const unsigned char *)value, strlen(value));

    SpatioBuffer buf = {0};
    if (spatio_get(db, key, &buf) == 0) {
        printf("value = %.*s\n", (int)buf.len, buf.data);
        spatio_free_buffer(buf);
    }

    spatio_close(db);
    return 0;
}

Safety note: Callers must pass valid, null-terminated strings and free any buffers produced by spatio_get using spatio_free_buffer. Structured error reporting is under development; spatio_last_error_message currently returns NULL.

For runnable demos and extended use-case walkthroughs, check examples/README.md.

API Overview

Core Operations

// Basic key-value operations
db.insert("key", b"value", None)?;
let value = db.get("key")?;
db.delete("key")?;

Spatial Operations

use spatio::{distance_between, DistanceMetric};
use geo::polygon;

let point = Point::new(-74.0060, 40.7128);

// Insert point with automatic spatial indexing
db.insert_point("namespace", &point, b"data", None)?;

// Distance calculations with multiple metrics
let nyc = Point::new(-74.0060, 40.7128);
let la = Point::new(-118.2437, 34.0522);
let dist = db.distance_between(&nyc, &la, DistanceMetric::Haversine)?;
println!("Distance: {:.2} km", dist / 1000.0); // ~3,944 km

// K-nearest-neighbors search
let nearest = db.knn("namespace", &point, 5, 500_000.0, DistanceMetric::Haversine)?;
for (pt, data, distance) in nearest {
    println!("Found point at {:.2} km", distance / 1000.0);
}

// Polygon queries (using geo crate)
let area = polygon![
    (x: -74.0, y: 40.7),
    (x: -73.9, y: 40.7),
    (x: -73.9, y: 40.8),
    (x: -74.0, y: 40.8),
];
let in_polygon = db.query_within_polygon("namespace", &area, 100)?;

// Find nearby points
let nearby = db.query_within_radius("namespace", &point, 1000.0, 10)?;

// Check if points exist in region
let exists = db.contains_point("namespace", &point, 1000.0)?;

// Count points within distance
let count = db.count_within_radius("namespace", &point, 1000.0)?;

// Query bounding box
let in_bounds = db.find_within_bounds("namespace", 40.0, -75.0, 41.0, -73.0, 10)?;
let intersects = db.intersects_bounds("namespace", 40.0, -75.0, 41.0, -73.0)?;

Note: See SPATIAL_FEATURES.md for complete documentation on all spatial operations, including convex hull, polygon area calculations, and more.

3D Spatial Operations

Spatio provides comprehensive 3D spatial indexing for altitude-aware applications like drone tracking, aviation, and multi-floor building navigation:

use spatio::{Point3d, BoundingBox3D, Spatio};

let db = Spatio::memory()?;

// Insert 3D points (lon, lat, altitude)
let drone = Point3d::new(-74.0060, 40.7128, 100.0); // 100m altitude
db.insert_point_3d("drones", &drone, b"Drone Alpha", None)?;

// Spherical 3D query (true 3D distance)
let control = Point3d::new(-74.0065, 40.7133, 100.0);
let nearby = db.query_within_sphere_3d("drones", &control, 200.0, 10)?;

// Cylindrical query (altitude range + horizontal radius)
let results = db.query_within_cylinder_3d(
    "drones",
    &control,
    50.0,   // min altitude
    150.0,  // max altitude
    1000.0, // horizontal radius
    10
)?;

// 3D bounding box query
let bbox = BoundingBox3D::new(
    -74.0080, 40.7120, 40.0,  // min x, y, z
    -74.0050, 40.7150, 110.0, // max x, y, z
);
let in_box = db.query_within_bbox_3d("drones", &bbox, 100)?;

// K-nearest neighbors in 3D
let nearest = db.knn_3d("drones", &control, 5)?;

// 3D distance calculations
let dist_3d = db.distance_between_3d(&point_a, &point_b)?;

See examples/3d_spatial_tracking.rs for a complete demonstration.

Logging

Spatio uses the log crate for diagnostics and warnings. To see log output in your application:

// Add to your Cargo.toml:
[dependencies]
spatio = "0.1"
env_logger = "0.11"  // or any other log implementation

// Initialize logging in your main():
fn main() {
    env_logger::init();
    // ... your code
}

Control log verbosity with the RUST_LOG environment variable:

# See all debug logs
RUST_LOG=debug cargo run

# Only warnings and errors
RUST_LOG=warn cargo run

# Only Spatio logs
RUST_LOG=spatio=debug cargo run

Spatio logs important events such as:

  • Database lock errors (poisoned locks)
  • AOF replay warnings (invalid coordinates, corrupted entries)
  • Performance warnings (large expiration clusters)
  • Spatial index warnings (invalid query parameters)

Trajectory Tracking

// Store movement over time
let trajectory = vec![
    TemporalPoint { point: Point::new(-74.0060, 40.7128), timestamp: UNIX_EPOCH + Duration::from_secs(1640995200) },
    TemporalPoint { point: Point::new(-74.0040, 40.7150), timestamp: UNIX_EPOCH + Duration::from_secs(1640995260) },
    TemporalPoint { point: Point::new(-74.0020, 40.7172), timestamp: UNIX_EPOCH + Duration::from_secs(1640995320) },
];
db.insert_trajectory("vehicle:truck001", &trajectory, None)?;

// Query trajectory for time range
let path = db.query_trajectory("vehicle:truck001", 1640995200, 1640995320)?;

Atomic Operations

db.atomic(|batch| {
    batch.insert("key1", b"value1", None)?;
    batch.insert("key2", b"value2", None)?;
    batch.delete("old_key")?;
    Ok(())
})?;

Time-to-Live (TTL)

// Data expires in 1 hour
let opts = SetOptions::with_ttl(Duration::from_secs(3600));
db.insert("temp_key", b"temp_value", Some(opts))?;

Architecture Overview

Spatio is organized in layered modules:

  • Storage – Pluggable backends (in-memory by default, AOF for durability) with a common trait surface.
  • Indexing – Geohash-based point index with configurable precision and smart fallback during searches.
  • Query – Radius, bounding-box, and trajectory primitives that reuse the shared index and TTL cleanup workers.
  • API – Ergonomic Rust API plus PyO3 bindings that expose the same core capabilities.

See the docs site for deeper architectural notes.

Project Status

  • Current version: 0.1.X
  • Active development: APIs may still change.
  • Follow releases for migration notes and roadmap updates.

Contributing

Contributions are welcome! Please read our Contributing Guidelines before submitting pull requests.

Development Setup

git clone https://github.com/pkvartsianyi/spatio
cd spatio
cargo test
cargo clippy
cargo fmt

Links & Resources

Package Repositories

Documentation & Source

Community

License

MIT License (LICENSE)

Spatial Features

Spatio provides comprehensive geospatial operations powered by the georust/geo crate:

  • 4 Distance Metrics: Haversine (fast spherical), Geodesic (accurate ellipsoidal), Rhumb (constant bearing), Euclidean (planar)
  • K-Nearest-Neighbors: Find the K closest points efficiently using spatial index
  • Polygon Queries: Point-in-polygon tests, polygon area calculations
  • Geometric Operations: Convex hull, bounding boxes, bbox expansion and intersection
  • Full geo Integration: All operations leverage battle-tested geo crate implementations

Coordinate Convention:

  • Rust: Point::new(longitude, latitude) - follows geo crate (x, y) convention
  • Python: Point(latitude, longitude) - user-friendly order, converted internally

For complete documentation and examples, see:

Acknowledgments

  • Built with the Rust ecosystem's excellent geospatial libraries (especially georust/geo)
  • Inspired by modern embedded databases and spatial indexing research
  • Thanks to the Rust community for feedback and contributions