Skip to main content

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

Project description

fastquadtree

PyPI version Python versions Wheels License: MIT

PyPI Downloads

Build Codecov

Rust core via PyO3 Built with maturin Ruff

Interactive_V2_Screenshot

Rust-optimized quadtree with a simple Python API.

  • Python package: fastquadtree
  • Python ≥ 3.8
  • Import path: from fastquadtree import QuadTree

Benchmarks

fastquadtree outperforms all other quadtree Python packages, including the Rtree spatial index.

Library comparison

Total time Throughput

Summary (largest dataset, PyQtree baseline)

  • Points: 250,000, Queries: 500

  • Fastest total: fastquadtree at 0.120 s
Library Build (s) Query (s) Total (s) Speed vs PyQtree
fastquadtree 0.031 0.089 0.120 14.64×
Shapely STRtree 0.179 0.100 0.279 6.29×
nontree-QuadTree 0.595 0.605 1.200 1.46×
Rtree 0.961 0.300 1.261 1.39×
e-pyquadtree 1.005 0.660 1.665 1.05×
PyQtree 1.492 0.263 1.755 1.00×
quads 1.407 0.484 1.890 0.93×

Benchmark Configuration

Parameter Value
Bounds (0, 0, 1000, 1000)
Max points per node 128
Max depth 16
Queries per experiment 500

Install

pip install fastquadtree

If you are developing locally:

# optimized dev install
maturin develop --release

Quickstart

from fastquadtree 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

# Delete items by ID and location
deleted = qt.delete(id2, (200, 300))  # True if found and deleted
print(f"Deleted: {deleted}")
print(f"Remaining items: {qt.count_items()}")

# For object tracking with track_objects=True
qt_tracked = QuadTree((0, 0, 1000, 1000), capacity=4, track_objects=True)
player1 = {"name": "Alice", "score": 100}
player2 = {"name": "Bob", "score": 200}

id1 = qt_tracked.insert((50, 50), obj=player1)
id2 = qt_tracked.insert((150, 150), obj=player2)

# Delete by object reference (O(1) lookup!)
deleted = qt_tracked.delete_by_object(player1)
print(f"Deleted player: {deleted}")  # True

Working with Python objects

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

Wrapper Managed Objects

from fastquadtree 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 within a bounding box
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) defines the 2D area covered by the quadtree
  • 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 for convenience.
  • start_id — starting value for auto-assigned ids

Core Methods

Full docs are in the docstrings of the Python Shim

  • insert(xy, *, id=None, obj=None) -> int

  • insert_many_points(points) -> int

  • query(rect, *, as_items=False) -> list

  • nearest_neighbor(xy, *, as_item=False) -> (id, x, y) | Item | None

  • nearest_neighbors(xy, k, *, as_items=False) -> list

  • delete(id, xy) -> bool

  • delete_by_object(obj) -> bool (requires track_objects=True)

  • attach(id, obj) -> None (requires track_objects=True)

  • count_items() -> int

  • get(id) -> object | None

  • get_all_rectangles() -> list[tuple] (for visualization)

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 closed on the min edge and open 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.

Native vs Shim Benchmark

Setup

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

Timing (seconds)

Variant Build Query Total
Native 0.483 4.380 4.863
Shim (no map) 0.668 4.167 4.835
Shim (track+objs) 1.153 4.458 5.610

Overhead vs Native

  • No map: build 1.38x, query 0.95x, total 0.99x
  • Track + objs: build 2.39x, query 1.02x, total 1.15x

Run benchmarks

To run the benchmarks yourself, first install the dependencies:

pip install -r benchmarks/requirements.txt

Then run:

python benchmarks/cross_library_bench.py
python benchmarks/benchmark_native_vs_shim.py 

Run Visualizer

A visualizer is included to help you understand how the quadtree subdivides space.

pip install -r interactive/requirements.txt
python interactive/interactive_v2.py

Check the CLI arguments for the cross-library benchmark in benchmarks/quadtree_bench/main.py.

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 delete items from the quadtree? Yes! Use delete(id, xy) to remove specific items. You must provide both the ID and exact location for precise deletion. This handles cases where multiple items exist at the same location. If you're using track_objects=True, you can also use delete_by_object(obj) for convenient object-based deletion with O(1) lookup. The tree automatically merges nodes when item counts drop below capacity.

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

fastquadtree-0.6.1.tar.gz (683.9 kB view details)

Uploaded Source

Built Distributions

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

fastquadtree-0.6.1-cp38-abi3-win_amd64.whl (135.6 kB view details)

Uploaded CPython 3.8+Windows x86-64

fastquadtree-0.6.1-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (230.6 kB view details)

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

fastquadtree-0.6.1-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl (240.8 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ ARMv7l

fastquadtree-0.6.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (230.6 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ ARM64

fastquadtree-0.6.1-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl (401.1 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 fastquadtree-0.6.1.tar.gz.

File metadata

  • Download URL: fastquadtree-0.6.1.tar.gz
  • Upload date:
  • Size: 683.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: maturin/1.9.5

File hashes

Hashes for fastquadtree-0.6.1.tar.gz
Algorithm Hash digest
SHA256 f0dcfa7970aa1d63a7a6155587458037aa3b4cf0eb2b229201083afee628d4d0
MD5 3e294c7a07fd23cb8c57da5437d9d7f3
BLAKE2b-256 7080729fcfbb54040a059f696cae581c4763c949659b18966e90082b09651baf

See more details on using hashes here.

File details

Details for the file fastquadtree-0.6.1-cp38-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for fastquadtree-0.6.1-cp38-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 d29459f7b80427f67ab2110de053e10c5d8fee1313297522ae3e74443a8d4fe5
MD5 ee564430bce7d09095fe48d85e7aa9f3
BLAKE2b-256 91a26ae3d5d1354346e6eac3594fb82c2312a2ddabd0e889d15c136a626e5689

See more details on using hashes here.

File details

Details for the file fastquadtree-0.6.1-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for fastquadtree-0.6.1-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 185c35c07411fb292a6d56f360526f22cd3b020d1789675d6cbc6fb250c38571
MD5 b78ea00fa2634d5477ea9631e6cc923b
BLAKE2b-256 14d6efffdbafda49296e315997bd71b879b18b46db2f113fe0ffba2270c32112

See more details on using hashes here.

File details

Details for the file fastquadtree-0.6.1-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl.

File metadata

File hashes

Hashes for fastquadtree-0.6.1-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl
Algorithm Hash digest
SHA256 e9e4494d7fdcc25a051a245ae2320b9aa42b486a54d0497cbce54c74b98ac37e
MD5 d14c68036c28ed76ffd398ee805f9b75
BLAKE2b-256 ef4db6a2684f6a4463be8ccf8d0da429e1938fdc67a1ce9bfb6dc9c02af68864

See more details on using hashes here.

File details

Details for the file fastquadtree-0.6.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for fastquadtree-0.6.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 e6cbe1b4405d74da3c2789436ddd5d8b3917fc02fd507ffbb0922d41865ede76
MD5 07d12d43a000156d611e51c035f4785b
BLAKE2b-256 a58681f31487ef942f887c24d3cc7fc902e7b0d1f592630e55529b84eaa06997

See more details on using hashes here.

File details

Details for the file fastquadtree-0.6.1-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl.

File metadata

File hashes

Hashes for fastquadtree-0.6.1-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Algorithm Hash digest
SHA256 55b5a80d0cc4edb21bfa58b4d4d011d3e4942ccdc1f47aaaf8d66ec11770cc38
MD5 05a1d1b3529fa7a151b779dd0eb84467
BLAKE2b-256 01af0531809a185bb988d5acdff0254f25d3a99f185010978326ce7d44c7a6db

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