spatio 0.1.7

A high-performance, embedded spatio-temporal database for modern applications
Documentation
<p align="center">
    <a href="https://github.com/pkvartsianyi/spatio">
        <img src="assets/images/logo-min.png" height="60" alt="Spatio Logo">
    </a>
</p>

<h1 align="center">Spatio</h1>

<p align="center">
  <a href="https://opensource.org/licenses/MIT">
    <img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT">
  </a>
  <a href="https://crates.io/crates/spatio">
    <img src="https://img.shields.io/crates/v/spatio.svg" alt="Crates.io">
  </a>
  <a href="https://pypi.org/project/spatio">
    <img src="https://img.shields.io/pypi/v/spatio.svg" alt="PyPI">
  </a>
  <a href="https://pkvartsianyi.github.io/spatio/">
    <img src="https://img.shields.io/badge/Docs-Available-blue.svg" alt="Documentation">
  </a>
  <a href="https://docs.rs/spatio">
    <img src="https://img.shields.io/badge/docs.rs-spatio-66c2a5" alt="Rust Docs">
  </a>
</p>

**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.

## Quick Start

### Python
```bash
pip install spatio
```

```python
import spatio

db = spatio.Spatio.memory()

# Store a point (longitude, latitude)
nyc = spatio.Point(-74.0060, 40.7128)
db.insert_point("cities", nyc, b"New York")

# Find nearby points
nearby = db.query_within_radius("cities", nyc, 100000, 10)
```

### Rust
```toml
[dependencies]
spatio = "0.1"
```

```rust
use spatio::prelude::*;

fn main() -> Result<()> {
    let mut db = Spatio::memory()?;

    let nyc = Point::new(-74.0060, 40.7128);
    db.insert_point("cities", &nyc, b"New York", None)?;

    let nearby = db.query_within_radius("cities", &nyc, 100_000.0, 10)?;
    println!("Found {} cities", nearby.len());

    Ok(())
}
```

## Features

**Spatial queries:** Radius search, bounding box, K-nearest-neighbors, polygon containment
**3D support:** Full altitude-aware indexing and queries
**Trajectories:** Track movement over time
**Distance metrics:** Haversine, Geodesic, Rhumb, Euclidean
**TTL:** Auto-expire old data
**Persistence:** Snapshots (default) or append-only file (AOF)
**Namespaces:** Logical data separation in one database

## Coordinate Order

**Important:** Spatio uses `(longitude, latitude)` order everywhere - same as GeoJSON and most GIS tools.

```rust
// Correct: lon, lat
let point = Point::new(-74.0060, 40.7128);  // NYC
```

This matches the mathematical (x, y) convention and makes GeoJSON interop trivial.

## Examples

### 2D Spatial
```rust
// Insert points
db.insert_point("pois", &Point::new(-74.0, 40.7), b"Restaurant", None)?;

// Find nearby (within 1km)
let nearby = db.query_within_radius("pois", &center, 1000.0, 10)?;

// Bounding box query
let in_box = db.find_within_bounds("pois", 40.0, -75.0, 41.0, -73.0, 100)?;

// K-nearest neighbors
let nearest = db.knn("pois", &center, 5, 10_000.0, DistanceMetric::Haversine)?;
```

### 3D Spatial
```rust
use spatio::Point3d;

// Track drones with altitude
let drone = Point3d::new(-74.0060, 40.7128, 100.0);
db.insert_point_3d("drones", &drone, b"Alpha", None)?;

// 3D sphere query
let nearby = db.query_within_sphere_3d("drones", &drone, 200.0, 10)?;

// Cylindrical query (altitude range + radius)
let in_cylinder = db.query_within_cylinder_3d(
    "drones", &drone, 50.0, 150.0, 1000.0, 10
)?;
```

### Trajectories
```rust
use spatio::TemporalPoint;

let path = vec![
    TemporalPoint {
        point: Point::new(-74.00, 40.71),
        timestamp: UNIX_EPOCH + Duration::from_secs(100)
    },
    TemporalPoint {
        point: Point::new(-74.01, 40.72),
        timestamp: UNIX_EPOCH + Duration::from_secs(200)
    },
];

db.insert_trajectory("truck:001", &path, None)?;

// Query movement history
let history = db.query_trajectory("truck:001", 100, 300)?;
```

### TTL (Time-To-Live)
```rust
use spatio::SetOptions;

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

// Expired items return None
let value = db.get("session:123")?;  // None if expired

// Clean up expired items manually
let removed = db.cleanup_expired()?;
```

**Important:** TTL is lazy - expired items stick around in memory until you call `cleanup_expired()` or they get overwritten. For long-running apps, clean up periodically or you'll leak memory.

### Persistence

**Snapshots (default):** Point-in-time saves, good for edge devices
```rust
let config = Config::default().with_snapshot_auto_ops(1000);
let db = DBBuilder::new()
    .snapshot_path("data.snapshot")
    .config(config)
    .build()?;
// Auto-saves every 1000 operations
```

**AOF (optional):** Write-ahead log, good for zero data loss
```rust
let db = DBBuilder::new()
    .aof_path("data.aof")
    .build()?;
// Requires --features aof
```

## Platforms

**Supported:**
- Linux (x86_64, aarch64)
- macOS (x86_64, arm64)

**Not supported:**
- Windows (use WSL2 or Docker)

See [PLATFORMS.md](PLATFORMS.md) for details.

## API Overview

**Key-Value:**
```rust
db.insert(key, value, options)?;
db.get(key)?;
db.delete(key)?;
```

**Spatial:**
```rust
db.insert_point(namespace, &point, data, options)?;
db.query_within_radius(namespace, &center, radius, limit)?;
db.count_within_radius(namespace, &center, radius)?;
db.contains_point(namespace, &center, radius)?;
db.find_within_bounds(namespace, min_lat, min_lon, max_lat, max_lon, limit)?;
db.knn(namespace, &center, k, max_radius, metric)?;
db.query_within_polygon(namespace, &polygon, limit)?;
db.distance_between(&p1, &p2, metric)?;
```

**3D Spatial:**
```rust
db.insert_point_3d(namespace, &point3d, data, options)?;
db.query_within_sphere_3d(namespace, &center, radius, limit)?;
db.query_within_cylinder_3d(namespace, &center, min_z, max_z, radius, limit)?;
db.query_within_bbox_3d(namespace, &bbox, limit)?;
db.knn_3d(namespace, &center, k)?;
```

**Trajectories:**
```rust
db.insert_trajectory(object_id, &points, options)?;
db.query_trajectory(object_id, start_time, end_time)?;
```

**Utility:**
```rust
db.atomic(|batch| { /* multiple ops */ })?;
db.cleanup_expired()?;
db.count_expired();
db.stats();
```

## Documentation

- **Examples:** [examples/](examples/) directory
- **Spatial features:** [SPATIAL_FEATURES.md](SPATIAL_FEATURES.md)
- **Platform support:** [PLATFORMS.md](PLATFORMS.md)
- **API docs:** [docs.rs/spatio](https://docs.rs/spatio)
- **Python docs:** [py-spatio/README.md](py-spatio/README.md)

## Status

Current version: **0.1.x** (active development)

APIs may change. Check [CHANGELOG.md](CHANGELOG.md) before upgrading.

## Contributing

Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md).

## License

MIT - see [LICENSE](LICENSE)

## Links

- **GitHub:** https://github.com/pkvartsianyi/spatio
- **Crates.io:** https://crates.io/crates/spatio
- **PyPI:** https://pypi.org/project/spatio
- **Issues:** https://github.com/pkvartsianyi/spatio/issues