Skip to main content

Rust-accelerated quadtree for Python with fast inserts, range queries, and k-NN search.

Project description

Here’s a drop-in README that matches your new shim interface.

# quadtree-rs

Rust-accelerated quadtree with a simple Python API.

- Python package: **`quadtree_rs`**
- Python ≥ 3.8
- Import path: `from quadtree_rs import QuadTree`

## Install

```bash
pip install quadtree_rs

If you are developing locally:

# optimized dev install
maturin develop --release

Quickstart

from quadtree_rs import QuadTree

# Bounds are (min_x, min_y, max_x, max_y)
qt = QuadTree(bounds=(0, 0, 1000, 1000), capacity=20)  # max_depth is optional

# Insert points with auto ids
id1 = qt.insert((10, 10))
id2 = qt.insert((200, 300))
id3 = qt.insert((999, 500), id=42)  # you can supply your own id

# Axis-aligned rectangle query
hits = qt.query((0, 0, 250, 350))  # returns [(id, x, y), ...] by default
print(hits)  # e.g. [(1, 10.0, 10.0), (2, 200.0, 300.0)]

# Nearest neighbor
best = qt.nearest_neighbor((210, 310))  # -> (id, x, y) or None
print(best)

# k-nearest neighbors
top3 = qt.nearest_neighbors((210, 310), 3)
print(top3)  # list of up to 3 (id, x, y) tuples

Working with Python objects

You can keep the tree pure and manage your own id → object map, or let the wrapper manage it.

Option A: Manage your own map

from quadtree_rs import QuadTree

qt = QuadTree((0, 0, 1000, 1000), capacity=16)
objects: dict[int, object] = {}

def add(obj) -> int:
    obj_id = qt.insert(obj.position)  # auto id
    objects[obj_id] = obj
    return obj_id

# Later, resolve ids back to objects
ids = [obj_id for (obj_id, x, y) in qt.query((100, 100, 300, 300))]
selected = [objects[i] for i in ids]

Option B: Ask the wrapper to track objects

from quadtree_rs import QuadTree

qt = QuadTree((0, 0, 1000, 1000), capacity=16, track_objects=True)

# Store the object alongside the point
qt.insert((25, 40), obj={"name": "apple"})

# Ask for Item objects so you can access .obj lazily
items = qt.query((0, 0, 100, 100), as_items=True)
for it in items:
    print(it.id, it.x, it.y, it.obj)

You can also attach or replace an object later:

qt.attach(123, my_object)  # binds object to id 123

API

QuadTree(bounds, capacity, *, max_depth=None, track_objects=False, start_id=1)

  • bounds — tuple (min_x, min_y, max_x, max_y) covering all points you will insert
  • capacity — max number of points kept in a leaf before splitting
  • max_depth — optional depth cap. If omitted, the tree can keep splitting as needed
  • track_objects — if True, the wrapper maintains an id → object map
  • start_id — starting value for auto-assigned ids

Methods

  • insert(xy: tuple[float, float], *, id: int | None = None, obj: object | None = None) -> int Insert a point. Returns the id used. Raises ValueError if the point is outside bounds. If track_objects=True and obj is provided, the object is stored under that id.

  • insert_many_points(points: Iterable[tuple[float, float]]) -> int Bulk insert points with auto ids. Returns count inserted.

  • attach(id: int, obj: object) -> None Attach or replace an object for an existing id. If track_objects was false, a map is created on first use.

  • query(rect: tuple[float, float, float, float], *, as_items: bool = False) -> list[(id, x, y)] | list[Item] Return all points whose coordinates lie inside the rectangle. Use as_items=True to get Item wrappers with lazy .obj.

  • nearest_neighbor(xy: tuple[float, float], *, as_item: bool = False) -> (id, x, y) | Item | None Return the closest point to xy, or None if empty.

  • nearest_neighbors(xy: tuple[float, float], k: int, *, as_items: bool = False) -> list[(id, x, y)] | list[Item] Return up to k nearest points.

  • get(id: int) -> object | None Get the object associated with id if tracking is enabled.

  • __len__() -> int Number of successful inserts made through this wrapper.

  • NativeQuadTree Reference to the underlying Rust class quadtree_rs._native.QuadTree for power users.

Item (returned when as_items=True)

  • Attributes: id, x, y, and a lazy obj property
  • Accessing obj performs a dictionary lookup only if tracking is enabled

Geometric conventions

  • Rectangles are (min_x, min_y, max_x, max_y).
  • Containment rule is open on the min edge and closed on the max edge (x > min_x and x <= max_x and y > min_y and y <= max_y). This only matters for points exactly on edges.

Performance tips

  • Choose capacity so that leaves keep a small batch of points. Typical values are 8 to 64.
  • If your data is very skewed, set a max_depth to prevent long chains.
  • For fastest local runs, use maturin develop --release.
  • The wrapper keeps Python overhead low: raw tuple results by default, Item wrappers only when requested.

Benchmarks

Library comparison

Generated with benchmarks/bench_plotly.py in this repo.

  • 100k points, 500 queries, capacity 20, max depth 10
  • Median over 3 runs per size

Total time Throughput

Example summary (PyQtree baseline). Numbers vary by machine.

Library Build (s) Query (s) Total (s)
Brute force - 4.068 4.068
e-pyquadtree 0.447 1.951 2.398
PyQtree 0.686 0.685 1.371
quadtree-rs 0.038 0.285 0.323

Native vs shim

Setup

  • Points: 100,000
  • Queries: 500
  • Repeats: 5

Timing (seconds)

Variant Build Query Total
Native 0.038 0.317 0.354
Shim (no map) 0.051 0.309 0.360
Shim (track+objs) 0.057 0.321 0.379

Overhead vs native

  • No map: build 1.35x, query 0.98x, total 1.01x
  • Track + objs: build 1.53x, query 1.01x, total 1.07x

FAQ

What happens if I insert the same id more than once? Allowed. For k-nearest, duplicates are de-duplicated by id. For range queries you will see every inserted point.

Can I store rectangles or circles? The core stores points. To index objects with extent, insert whatever representative point you choose. For rectangles you can insert centers or build an AABB tree separately.

Threading Use one tree per thread if you need heavy parallel inserts from Python.

License

MIT. See LICENSE.

Acknowledgments

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

quadtree_rs-0.2.0.tar.gz (491.9 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

quadtree_rs-0.2.0-cp38-abi3-win_amd64.whl (143.2 kB view details)

Uploaded CPython 3.8+Windows x86-64

quadtree_rs-0.2.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (238.9 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ x86-64

quadtree_rs-0.2.0-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl (416.2 kB view details)

Uploaded CPython 3.8+macOS 10.12+ universal2 (ARM64, x86-64)macOS 10.12+ x86-64macOS 11.0+ ARM64

File details

Details for the file quadtree_rs-0.2.0.tar.gz.

File metadata

  • Download URL: quadtree_rs-0.2.0.tar.gz
  • Upload date:
  • Size: 491.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: maturin/1.9.4

File hashes

Hashes for quadtree_rs-0.2.0.tar.gz
Algorithm Hash digest
SHA256 4f86c7909a97536e5be5519fd7ad0230ffd2f4b3340cb8628938755049b757cf
MD5 1711104d2a251b88f4966ce792110f25
BLAKE2b-256 aa1217dcf2db3ab488317d637b7523cdd278ff3399f5e951976889852e8b4b28

See more details on using hashes here.

File details

Details for the file quadtree_rs-0.2.0-cp38-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for quadtree_rs-0.2.0-cp38-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 bae979191697c51c7c01b04499eda16d096673e440b889de54abda4fbc655071
MD5 634be5f5e753cc47e132d40e550e4d83
BLAKE2b-256 a8479ef6c682dc90118a01f33c2af092a72aaaa864e6f87292bd39d9cbc549b8

See more details on using hashes here.

File details

Details for the file quadtree_rs-0.2.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for quadtree_rs-0.2.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 2937d8bae1a45f11f105315ccd93f167b72360a3b2753edf41f9110568becd87
MD5 f605aba2638c9c0a4d51a0d76c4c9fae
BLAKE2b-256 97a213b88eb4803dc07eb3e24ee58e98165c33240259dfc2d2850f644e90717d

See more details on using hashes here.

File details

Details for the file quadtree_rs-0.2.0-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl.

File metadata

File hashes

Hashes for quadtree_rs-0.2.0-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Algorithm Hash digest
SHA256 1b039ad0847d6df2896102c714a571151bc3ccfa0033fadd648deea66cb7cd4a
MD5 980a40b8faff33bc3534c90155aeafd9
BLAKE2b-256 f28088de319da40ebda6d22810cc4cdba553a0328171f66685022e4d419aa5ae

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page