spatio 0.1.5-alpha.2

A high-performance, embedded spatio-temporal database for modern applications
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
<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.

---

## 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<RwLock> (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]#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

```bash
pip install spatio
```

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

### Rust

Add this to your `Cargo.toml`:

```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
```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:

```bash
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:

```c
#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
```rust
// Basic key-value operations
db.insert("key", b"value", None)?;
let value = db.get("key")?;
db.delete("key")?;
```

### Spatial Operations
```rust
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](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:

```rust
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](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:

```rust
// 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:

```bash
# 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
```rust
// 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
```rust
db.atomic(|batch| {
    batch.insert("key1", b"value1", None)?;
    batch.insert("key2", b"value2", None)?;
    batch.delete("old_key")?;
    Ok(())
})?;
```

### Time-to-Live (TTL)
```rust
// 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](https://pkvartsianyi.github.io/spatio/) for deeper architectural notes.

## Project Status

- Current version: **0.1.X**
- Active development: APIs may still change.
- Follow [releases]https://github.com/pkvartsianyi/spatio/releases for migration notes and roadmap updates.

## Contributing

Contributions are welcome! Please read our [Contributing Guidelines](CONTRIBUTING.md) before submitting pull requests.

### Development Setup
```bash
git clone https://github.com/pkvartsianyi/spatio
cd spatio
cargo test
cargo clippy
cargo fmt
```

## Links & Resources

### Package Repositories
- **PyPI**: https://pypi.org/project/spatio
- **Crates.io**: https://crates.io/crates/spatio

### Documentation & Source
- **GitHub Repository**: https://github.com/pkvartsianyi/spatio
- **Rust Documentation**: https://docs.rs/spatio
- **Python Documentation**: https://github.com/pkvartsianyi/spatio/tree/main/py-spatio

### Community
- **Issues & Bug Reports**: https://github.com/pkvartsianyi/spatio/issues
- **Releases & Changelog**: https://github.com/pkvartsianyi/spatio/releases

## License

MIT License ([LICENSE](LICENSE))

## Spatial Features

Spatio provides comprehensive geospatial operations powered by the [georust/geo](https://github.com/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:
- [SPATIAL_FEATURES.md]SPATIAL_FEATURES.md - Complete API reference
- [examples/advanced_spatial.rs]examples/advanced_spatial.rs - Comprehensive demo

## Acknowledgments

- Built with the Rust ecosystem's excellent geospatial libraries (especially [georust/geo]https://github.com/georust/geo)
- Inspired by modern embedded databases and spatial indexing research
- Thanks to the Rust community for feedback and contributions