Embedded spatio-temporal database · Rust

Know where everything is, and where it’s been.

Spatio is an embedded spatio-temporal database that tracks moving objects inside your process. Current positions live in an in-memory R*-tree for fast spatial queries. Every move is appended to a durable log on disk, so you can replay any object’s path.

pip install spatio
namespace “fleet” · 0 objects WGS84 · Lower Manhattan
Simulated, at 5× speed. Point at the map to move the query.
Architecture

Hot state for now. Cold state for history.

Most location data is asked two questions: where is it now, and where has it been. Spatio keeps those apart so neither slows the other down.

Hot state · memory

The latest position of every object

  • DashMapSharded concurrent map from object ID to its current location. Lookups are O(1).
  • R*-treeSpatial index over current positions, for radius, bbox, cylinder, polygon and KNN queries.
  • Recent bufferA bounded per-object window of recent points, so short trajectory reads skip the disk.
Cold state · disk

Every movement, in order

  • Trajectory logAppend-only and CRC-checked. Each upsert becomes one record.
  • Sync policyChoose how often records are flushed and fsynced, trading latency for durability.
  • CheckpointsSnapshots of live objects bound recovery to the records written since the last one.

On restart, Spatio loads the latest checkpoint and replays the log tail to rebuild the hot state. There is no server to run and nothing to configure: open a path and start writing.

Queries

Ask around a coordinate, or around an object.

Every spatial query also has an object-relative form. query_near("drones", "drone_alpha", 500) finds what is close to a drone without you looking up its position first.

Radius

Everything within a distance in meters, sorted nearest first.

query_radius(ns, center, radius, limit) O(log N + K)

Bounding box

Objects inside a rectangle, in 2D, or a box in 3D when altitude matters.

query_bbox · query_within_bbox_3d O(log N + K)

Cylinder

A radius plus an altitude band. Built for drones and airspace.

query_within_cylinder(ns, c, min_z, max_z, r, n) O(log N + K)

K nearest

The k closest objects, with no radius to guess.

knn(ns, center, k) · knn_near_object O(k log N)

Polygon

Point-in-polygon over a geofence, with holes. Polygons round-trip as GeoJSON.

query_polygon(ns, polygon, limit) O(log N + K·V)

Trajectory

An object’s path between two timestamps, read from the recent buffer or the log.

query_trajectory(ns, id, start, end, limit) O(B log B) buffered
Quickstart

A database you import.

Spatio runs in your process, like SQLite. The Python, TypeScript and Go bindings call the same Rust core directly, with no network hop.

PythonNative bindings built with PyO3. pip install spatio.
TypeScriptNative Node.js addon built with napi-rs, types included. npm install spatio-db.
GoLoads the C ABI through purego, so your build needs no cgo or C toolchain.
Server modeWhen several processes need one database, spatio-server exposes the same API over TCP, with auth and TLS.
import spatio

db = spatio.Spatio.open("fleet.db")

# Longitude, latitude. Metadata is any JSON-able dict.
nyc = spatio.Point(-74.0060, 40.7128)
db.upsert("cities", "nyc", nyc, {"population": 8_000_000})

# Everything within 100 km, nearest first
nearby = db.query_radius("cities", nyc, 100_000, limit=10)
for object_id, point, meta, dist in nearby:
    print(object_id, round(dist))
Benchmarks

Core operations, in memory.

Spatio only, from the v0.4.0 release benchmark: 100,000 objects on a uniform grid, one thread, one namespace, on aarch64.

OperationOps/sLatency
upsert, new object185,3246.1 µs
upsert, moving object72,73013.8 µs
get6,788,2560.15 µs
knn, k = 10143,8107.0 µs
distance_between4,514,5270.22 µs

These are best-case figures. They use an in-memory database with no disk, fsync, network or concurrent writers, so durable writes and server calls are slower. There is no comparison with other databases here because we don’t yet have one that measures them the same way. Every release’s results are in the repository, including changes from the previous release.

Costs

What each call costs, written down.

N is objects in the namespace, K the objects a query’s envelope matches, L the records in the log. The full table, including the slow paths, is in COMPLEXITY.md.

OperationComplexityHow
getO(1)DashMap lookup.
upsertO(log N)R*-tree remove and reinsert. Skips the index when the object hasn’t moved.
query_radiusO(log N + K)Envelope prune, haversine per candidate, top-k heap.
knnO(k log N)R*-tree nearest-neighbor iterator.
query_polygonO(log N + K·V)Bounding-box broad phase, then point-in-polygon over V vertices.
query_trajectoryO(L)Falls back to a log scan once the recent buffer has filled. Size the buffer for your read window.
FAQ

Questions, answered plainly.

What is Spatio?

Spatio is an open-source embedded spatio-temporal database written in Rust. It runs inside your application, like SQLite, and stores the current position of moving objects along with the history of where they have been. It is MIT licensed.

What is Spatio used for?

Applications that track things that move: delivery and logistics fleets, drones and airspace monitoring, and geofencing. Typical questions are which vehicles are within 5 km of a warehouse, which drones are near this drone, and where a truck was during the last hour.

How does Spatio compare to PostGIS and SpatiaLite?

PostGIS and SpatiaLite are SQL databases with rich geometry types and hundreds of spatial functions. Spatio is narrower: no SQL, and objects are points with metadata. In return it keeps current positions in an in-memory R*-tree and records every move in a trajectory log, with no schema or server to set up. Choose PostGIS or SpatiaLite for general GIS work, and Spatio for high-rate position updates.

How does Spatio compare to Redis GEO and Tile38?

Redis GEO and Tile38 are servers you run and connect to over the network. Spatio is a library that runs in your process, with an optional server for when you need one. It also keeps each object’s movement history on disk and supports 3D queries, such as a radius plus an altitude band.

Does Spatio persist data across restarts?

Yes, when opened with a file path. Every update is appended to a CRC-checked log, with a configurable sync policy that trades latency for durability. On restart Spatio loads the latest checkpoint and replays the log tail. It also has a purely in-memory mode.

Which languages does Spatio support?

Rust natively, Python through PyO3, Node.js and TypeScript through napi-rs, and Go through the C ABI using purego, so Go builds need no cgo. All of them call the same Rust core in process.

Can several processes share one Spatio database?

Yes, through spatio-server, which exposes the same API over TCP with token auth and TLS. Rust programs connect with the spatio-client crate. Within a single process, the embedded database needs no server.

What does Spatio not do?

It has no SQL, joins or secondary indexes on metadata, and it stores points rather than arbitrary geometries. Polygons are query shapes, such as a geofence, not stored objects. If you need those features, use a general-purpose spatial database.