fastquadtree 0.5.0__cp38-abi3-win_amd64.whl → 0.6.0__cp38-abi3-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
fastquadtree/__init__.py CHANGED
@@ -1,13 +1,13 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Any, Iterable, List, Optional, Tuple, overload
4
- from typing import Literal
3
+ from typing import Any, Literal, Tuple, overload
5
4
 
6
- # Compiled Rust module is provided by maturin (tool.maturin.module-name)
7
- from ._native import QuadTree as _RustQuadTree
8
5
  from ._bimap import BiMap # type: ignore[attr-defined]
9
6
  from ._item import Item
10
7
 
8
+ # Compiled Rust module is provided by maturin (tool.maturin.module-name)
9
+ from ._native import QuadTree as _RustQuadTree
10
+
11
11
  Bounds = Tuple[float, float, float, float]
12
12
  """Axis-aligned rectangle as (min_x, min_y, max_x, max_y)."""
13
13
 
@@ -45,14 +45,14 @@ class QuadTree:
45
45
  ValueError: If parameters are invalid or inserts are out of bounds.
46
46
  """
47
47
 
48
- __slots__ = ("_native", "_items", "_next_id", "_count", "_bounds")
48
+ __slots__ = ("_bounds", "_count", "_items", "_native", "_next_id")
49
49
 
50
50
  def __init__(
51
51
  self,
52
52
  bounds: Bounds,
53
53
  capacity: int,
54
54
  *,
55
- max_depth: Optional[int] = None,
55
+ max_depth: int | None = None,
56
56
  track_objects: bool = False,
57
57
  start_id: int = 1,
58
58
  ):
@@ -60,14 +60,14 @@ class QuadTree:
60
60
  self._native = _RustQuadTree(bounds, capacity)
61
61
  else:
62
62
  self._native = _RustQuadTree(bounds, capacity, max_depth=max_depth)
63
- self._items: Optional[BiMap] = BiMap() if track_objects else None
63
+ self._items: BiMap | None = BiMap() if track_objects else None
64
64
  self._next_id: int = int(start_id)
65
65
  self._count: int = 0
66
66
  self._bounds = bounds
67
67
 
68
68
  # ---------- inserts ----------
69
69
 
70
- def insert(self, xy: Point, *, id: Optional[int] = None, obj: Any = None) -> int:
70
+ def insert(self, xy: Point, *, id_: int | None = None, obj: Any = None) -> int:
71
71
  """
72
72
  Insert a single point.
73
73
 
@@ -83,15 +83,14 @@ class QuadTree:
83
83
  Raises:
84
84
  ValueError: If the point is outside tree bounds.
85
85
  """
86
- if id is None:
87
- id = self._next_id
86
+ if id_ is None:
87
+ id_ = self._next_id
88
88
  self._next_id += 1
89
- else:
90
- # ensure future auto-ids do not collide
91
- if id >= self._next_id:
92
- self._next_id = id + 1
89
+ # ensure future auto-ids do not collide
90
+ elif id_ >= self._next_id:
91
+ self._next_id = id_ + 1
93
92
 
94
- if not self._native.insert(id, xy):
93
+ if not self._native.insert(id_, xy):
95
94
  x, y = xy
96
95
  bx0, by0, bx1, by1 = self._bounds
97
96
  raise ValueError(
@@ -99,76 +98,72 @@ class QuadTree:
99
98
  )
100
99
 
101
100
  if self._items is not None:
102
- self._items.add(Item(id, xy[0], xy[1], obj))
101
+ self._items.add(Item(id_, xy[0], xy[1], obj))
103
102
 
104
103
  self._count += 1
105
- return id
104
+ return id_
106
105
 
107
- def insert_many_points(self, points: Iterable[Point]) -> int:
106
+ def insert_many_points(self, points: list[Point]) -> int:
108
107
  """
109
108
  Bulk insert points with auto-assigned ids.
110
109
 
111
110
  Args:
112
- points: Iterable of (x, y) points.
111
+ points: List of (x, y) points.
113
112
 
114
113
  Returns:
115
- Number of points successfully inserted.
116
-
117
- Raises:
118
- ValueError: If any point is outside tree bounds.
114
+ The number of points inserted
119
115
  """
120
- ins = self._native.insert
121
- nid = self._next_id
122
- inserted = 0
123
- bx0, by0, bx1, by1 = self._bounds
124
- for xy in points:
125
- id_ = nid
126
- nid += 1
127
- if not ins(id_, xy):
128
- x, y = xy
129
- raise ValueError(
130
- f"Point ({x}, {y}) is outside bounds ({bx0}, {by0}, {bx1}, {by1})"
131
- )
132
- inserted += 1
133
- if self._items is not None:
134
- self._items.add(Item(id_, xy[0], xy[1], None))
135
- self._next_id = nid
136
- self._count += inserted
137
- return inserted
116
+ start_id = self._next_id
117
+ last_id = self._native.insert_many_points(start_id, points)
138
118
 
139
- def attach(self, id: int, obj: Any) -> None:
119
+ num_inserted = last_id - start_id + 1
120
+
121
+ if num_inserted < len(points):
122
+ raise ValueError("One or more points are outside tree bounds")
123
+
124
+ self._next_id = last_id + 1
125
+
126
+ # Update the item tracker if needed
127
+ if self._items is not None:
128
+ for i, id_ in enumerate(range(start_id, last_id + 1)):
129
+ x, y = points[i]
130
+ self._items.add(Item(id_, x, y, None))
131
+
132
+ return num_inserted
133
+
134
+ def attach(self, id_: int, obj: Any) -> None:
140
135
  """
141
136
  Attach or replace the Python object for an existing id.
142
137
  Tracking must be enabled.
143
138
 
144
139
  Args:
145
- id: Target id.
140
+ id_: Target id.
146
141
  obj: Object to associate with id.
147
142
  """
148
143
  if self._items is None:
149
144
  raise ValueError("Cannot attach objects when track_objects=False")
150
145
 
151
- item = self._items.by_id(id)
146
+ item = self._items.by_id(id_)
152
147
  if item is None:
153
- raise KeyError(f"Id {id} not found in quadtree")
154
- self._items.add(Item(id, item.x, item.y, obj))
148
+ raise KeyError(f"Id {id_} not found in quadtree")
149
+ self._items.add(Item(id_, item.x, item.y, obj))
155
150
 
156
- def delete(self, id: int, xy: Point) -> bool:
151
+ def delete(self, id_: int, xy: Point) -> bool:
157
152
  """
158
153
  Delete an item by id and exact coordinates.
159
154
 
160
155
  Args:
161
- id: Integer id to remove.
156
+ id_: Integer id to remove.
162
157
  xy: Coordinates (x, y) of the item.
163
158
 
164
159
  Returns:
165
160
  True if the item was found and deleted, else False.
166
161
  """
167
- deleted = self._native.delete(id, xy)
162
+ deleted = self._native.delete(id_, xy)
168
163
  if deleted:
169
164
  self._count -= 1
170
165
  if self._items is not None:
171
- self._items.pop_id(id) # ignore result
166
+ self._items.pop_id(id_) # ignore result
172
167
  return deleted
173
168
 
174
169
  def delete_by_object(self, obj: Any) -> bool:
@@ -196,21 +191,21 @@ class QuadTree:
196
191
  if item is None:
197
192
  return False
198
193
 
199
- return self.delete(item.id, (item.x, item.y))
194
+ return self.delete(item.id_, (item.x, item.y))
200
195
 
201
196
  # ---------- queries ----------
202
197
 
203
198
  @overload
204
199
  def query(
205
200
  self, rect: Bounds, *, as_items: Literal[False] = ...
206
- ) -> List[_IdCoord]: ...
201
+ ) -> list[_IdCoord]: ...
207
202
 
208
203
  @overload
209
- def query(self, rect: Bounds, *, as_items: Literal[True]) -> List[Item]: ...
204
+ def query(self, rect: Bounds, *, as_items: Literal[True]) -> list[Item]: ...
210
205
 
211
206
  def query(
212
207
  self, rect: Bounds, *, as_items: bool = False
213
- ) -> List[_IdCoord] | List[Item]:
208
+ ) -> list[_IdCoord] | list[Item]:
214
209
  """
215
210
  Return all points inside an axis-aligned rectangle.
216
211
 
@@ -228,8 +223,8 @@ class QuadTree:
228
223
 
229
224
  if self._items is None:
230
225
  raise ValueError("Cannot return results as items with track_objects=False")
231
- out: List[Item] = []
232
- for id_, x, y in raw:
226
+ out: list[Item] = []
227
+ for id_, _, _ in raw:
233
228
  item = self._items.by_id(id_)
234
229
  if item is None:
235
230
  raise RuntimeError(
@@ -242,12 +237,10 @@ class QuadTree:
242
237
  @overload
243
238
  def nearest_neighbor(
244
239
  self, xy: Point, *, as_item: Literal[False] = ...
245
- ) -> Optional[_IdCoord]: ...
240
+ ) -> _IdCoord | None: ...
246
241
 
247
242
  @overload
248
- def nearest_neighbor(
249
- self, xy: Point, *, as_item: Literal[True]
250
- ) -> Optional[Item]: ...
243
+ def nearest_neighbor(self, xy: Point, *, as_item: Literal[True]) -> Item | None: ...
251
244
 
252
245
  def nearest_neighbor(self, xy: Point, *, as_item: bool = False):
253
246
  """
@@ -266,7 +259,7 @@ class QuadTree:
266
259
 
267
260
  if self._items is None:
268
261
  raise ValueError("Cannot return result as item with track_objects=False")
269
- id_, x, y = t
262
+ id_, _x, _y = t
270
263
  item = self._items.by_id(id_)
271
264
  if item is None:
272
265
  raise RuntimeError(
@@ -278,12 +271,12 @@ class QuadTree:
278
271
  @overload
279
272
  def nearest_neighbors(
280
273
  self, xy: Point, k: int, *, as_items: Literal[False] = ...
281
- ) -> List[_IdCoord]: ...
274
+ ) -> list[_IdCoord]: ...
282
275
 
283
276
  @overload
284
277
  def nearest_neighbors(
285
278
  self, xy: Point, k: int, *, as_items: Literal[True]
286
- ) -> List[Item]: ...
279
+ ) -> list[Item]: ...
287
280
 
288
281
  def nearest_neighbors(self, xy: Point, k: int, *, as_items: bool = False):
289
282
  """
@@ -303,7 +296,7 @@ class QuadTree:
303
296
  if self._items is None:
304
297
  raise ValueError("Cannot return results as items with track_objects=False")
305
298
 
306
- out: List[Item] = []
299
+ out: list[Item] = []
307
300
  for id_, _, _ in raw:
308
301
  item = self._items.by_id(id_)
309
302
  if item is None:
@@ -316,7 +309,7 @@ class QuadTree:
316
309
 
317
310
  # ---------- misc ----------
318
311
 
319
- def get(self, id: int) -> Any | None:
312
+ def get(self, id_: int) -> Any | None:
320
313
  """
321
314
  Return the object associated with id.
322
315
 
@@ -325,12 +318,12 @@ class QuadTree:
325
318
  """
326
319
  if self._items is None:
327
320
  raise ValueError("Cannot get objects when track_objects=False")
328
- item = self._items.by_id(id)
321
+ item = self._items.by_id(id_)
329
322
  if item is None:
330
323
  return None
331
324
  return item.obj
332
325
 
333
- def get_all_rectangles(self) -> List[Bounds]:
326
+ def get_all_rectangles(self) -> list[Bounds]:
334
327
  """
335
328
  Return all node rectangles in the current quadtree.
336
329
 
@@ -339,7 +332,7 @@ class QuadTree:
339
332
  """
340
333
  return self._native.get_all_rectangles()
341
334
 
342
- def get_all_objects(self) -> List[Any]:
335
+ def get_all_objects(self) -> list[Any]:
343
336
  """
344
337
  Return all tracked objects.
345
338
 
@@ -350,7 +343,7 @@ class QuadTree:
350
343
  raise ValueError("Cannot get objects when track_objects=False")
351
344
  return [t.obj for t in self._items.items() if t.obj is not None]
352
345
 
353
- def get_all_items(self) -> List[Item]:
346
+ def get_all_items(self) -> list[Item]:
354
347
  """
355
348
  Return all tracked items.
356
349
 
@@ -385,4 +378,4 @@ class QuadTree:
385
378
  NativeQuadTree = _RustQuadTree
386
379
 
387
380
 
388
- __all__ = ["QuadTree", "Item", "Bounds", "Point"]
381
+ __all__ = ["Bounds", "Item", "Point", "QuadTree"]
fastquadtree/__init__.pyi CHANGED
@@ -1,67 +1,70 @@
1
- from typing import Any, Iterable, List, Optional, Tuple, Type, overload
2
- from typing import Literal as _Literal # avoid polluting public namespace
1
+ from typing import (
2
+ Any,
3
+ Iterable,
4
+ Literal as _Literal, # avoid polluting public namespace
5
+ overload,
6
+ )
3
7
 
4
- Bounds = Tuple[float, float, float, float]
5
- Point = Tuple[float, float]
8
+ Bounds = tuple[float, float, float, float]
9
+ Point = tuple[float, float]
6
10
 
7
11
  class Item:
8
- id: int
12
+ id_: int
9
13
  x: float
10
14
  y: float
11
15
  obj: Any | None
12
16
 
13
17
  class QuadTree:
14
18
  # Expose the raw native class for power users
15
- NativeQuadTree: Type
19
+ NativeQuadTree: type
16
20
 
17
21
  def __init__(
18
22
  self,
19
23
  bounds: Bounds,
20
24
  capacity: int,
21
25
  *,
22
- max_depth: Optional[int] = None,
26
+ max_depth: int | None = None,
23
27
  track_objects: bool = False,
24
28
  start_id: int = 1,
25
29
  ) -> None: ...
26
30
 
27
31
  # Inserts
28
- def insert(self, xy: Point, *, id: Optional[int] = ..., obj: Any = ...) -> int: ...
32
+ def insert(self, xy: Point, *, id_: int | None = ..., obj: Any = ...) -> int: ...
29
33
  def insert_many_points(self, points: Iterable[Point]) -> int: ...
30
- def insert_many(self, items: Iterable[Tuple[Point, Any]]) -> int: ...
31
- def attach(self, id: int, obj: Any) -> None: ...
34
+ def attach(self, id_: int, obj: Any) -> None: ...
32
35
 
33
36
  # Deletions
34
- def delete(self, id: int, xy: Point) -> bool: ...
37
+ def delete(self, id_: int, xy: Point) -> bool: ...
35
38
  def delete_by_object(self, obj: Any) -> bool: ...
36
39
 
37
40
  # Queries
38
41
  @overload
39
42
  def query(
40
43
  self, rect: Bounds, *, as_items: _Literal[False] = ...
41
- ) -> List[Tuple[int, float, float]]: ...
44
+ ) -> list[tuple[int, float, float]]: ...
42
45
  @overload
43
- def query(self, rect: Bounds, *, as_items: _Literal[True]) -> List[Item]: ...
46
+ def query(self, rect: Bounds, *, as_items: _Literal[True]) -> list[Item]: ...
44
47
  @overload
45
48
  def nearest_neighbor(
46
49
  self, xy: Point, *, as_item: _Literal[False] = ...
47
- ) -> Optional[Tuple[int, float, float]]: ...
50
+ ) -> tuple[int, float, float] | None: ...
48
51
  @overload
49
52
  def nearest_neighbor(
50
53
  self, xy: Point, *, as_item: _Literal[True]
51
- ) -> Optional[Item]: ...
54
+ ) -> Item | None: ...
52
55
  @overload
53
56
  def nearest_neighbors(
54
57
  self, xy: Point, k: int, *, as_items: _Literal[False] = ...
55
- ) -> List[Tuple[int, float, float]]: ...
58
+ ) -> list[tuple[int, float, float]]: ...
56
59
  @overload
57
60
  def nearest_neighbors(
58
61
  self, xy: Point, k: int, *, as_items: _Literal[True]
59
- ) -> List[Item]: ...
62
+ ) -> list[Item]: ...
60
63
 
61
64
  # Misc
62
- def get(self, id: int) -> Any | None: ...
63
- def get_all_rectangles(self) -> List[Bounds]: ...
64
- def get_all_objects(self) -> List[Any]: ...
65
- def get_all_items(self) -> List[Item]: ...
65
+ def get(self, id_: int) -> Any | None: ...
66
+ def get_all_rectangles(self) -> list[Bounds]: ...
67
+ def get_all_objects(self) -> list[Any]: ...
68
+ def get_all_items(self) -> list[Item]: ...
66
69
  def count_items(self) -> int: ...
67
70
  def __len__(self) -> int: ...
fastquadtree/_bimap.py CHANGED
@@ -1,6 +1,7 @@
1
1
  # _bimap.py
2
2
  from __future__ import annotations
3
- from typing import Any, Iterable, Iterator, Tuple, Optional
3
+
4
+ from typing import Any, Iterable, Iterator
4
5
 
5
6
  from ._item import Item
6
7
 
@@ -33,7 +34,7 @@ class BiMap:
33
34
  Insert or replace mapping for this Item.
34
35
  Handles conflicts so that both id and obj point to this exact Item.
35
36
  """
36
- id_ = item.id
37
+ id_ = item.id_
37
38
  obj = item.obj
38
39
 
39
40
  # Unlink any old item currently bound to this id
@@ -47,20 +48,20 @@ class BiMap:
47
48
  if obj is not None:
48
49
  prev = self._objid_to_item.get(id(obj))
49
50
  if prev is not None and prev is not item:
50
- self._id_to_item.pop(prev.id, None)
51
+ self._id_to_item.pop(prev.id_, None)
51
52
 
52
53
  # Link new
53
54
  self._id_to_item[id_] = item
54
55
  if obj is not None:
55
56
  self._objid_to_item[id(obj)] = item
56
57
 
57
- def by_id(self, id_: int) -> Optional[Item]:
58
+ def by_id(self, id_: int) -> Item | None:
58
59
  return self._id_to_item.get(id_)
59
60
 
60
- def by_obj(self, obj: Any) -> Optional[Item]:
61
+ def by_obj(self, obj: Any) -> Item | None:
61
62
  return self._objid_to_item.get(id(obj))
62
63
 
63
- def pop_id(self, id_: int) -> Optional[Item]:
64
+ def pop_id(self, id_: int) -> Item | None:
64
65
  it = self._id_to_item.pop(id_, None)
65
66
  if it is not None:
66
67
  obj = it.obj
@@ -68,19 +69,19 @@ class BiMap:
68
69
  self._objid_to_item.pop(id(obj), None)
69
70
  return it
70
71
 
71
- def pop_obj(self, obj: Any) -> Optional[Item]:
72
+ def pop_obj(self, obj: Any) -> Item | None:
72
73
  it = self._objid_to_item.pop(id(obj), None)
73
74
  if it is not None:
74
- self._id_to_item.pop(it.id, None)
75
+ self._id_to_item.pop(it.id_, None)
75
76
  return it
76
77
 
77
- def pop_item(self, item: Item) -> Optional[Item]:
78
+ def pop_item(self, item: Item) -> Item | None:
78
79
  """
79
80
  Remove this exact Item if present on either side.
80
81
  """
81
82
  removed = None
82
83
  # Remove by id first
83
- removed = self._id_to_item.pop(item.id)
84
+ removed = self._id_to_item.pop(item.id_)
84
85
 
85
86
  # Remove by obj side
86
87
  obj = item.obj
@@ -104,7 +105,7 @@ class BiMap:
104
105
  def contains_obj(self, obj: Any) -> bool:
105
106
  return id(obj) in self._objid_to_item
106
107
 
107
- def items_by_id(self) -> Iterator[Tuple[int, Item]]:
108
+ def items_by_id(self) -> Iterator[tuple[int, Item]]:
108
109
  return iter(self._id_to_item.items())
109
110
 
110
111
  def items(self) -> Iterator[Item]:
fastquadtree/_item.py CHANGED
@@ -1,5 +1,6 @@
1
1
  # item.py
2
2
  from __future__ import annotations
3
+
3
4
  from typing import Any
4
5
 
5
6
 
@@ -8,16 +9,16 @@ class Item:
8
9
  Lightweight view of an index entry.
9
10
 
10
11
  Attributes:
11
- id: Integer identifier.
12
+ id_: Integer identifier.
12
13
  x: X coordinate.
13
14
  y: Y coordinate.
14
15
  obj: The attached Python object if available, else None.
15
16
  """
16
17
 
17
- __slots__ = ("id", "x", "y", "obj")
18
+ __slots__ = ("id_", "obj", "x", "y")
18
19
 
19
- def __init__(self, id: int, x: float, y: float, obj: Any | None = None):
20
- self.id = id
20
+ def __init__(self, id_: int, x: float, y: float, obj: Any | None = None):
21
+ self.id_ = id_
21
22
  self.x = x
22
23
  self.y = y
23
24
  self.obj = obj
fastquadtree/_native.pyd CHANGED
Binary file
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastquadtree
3
- Version: 0.5.0
3
+ Version: 0.6.0
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3 :: Only
6
6
  Classifier: Programming Language :: Rust
@@ -11,6 +11,13 @@ Classifier: Topic :: Scientific/Engineering :: Information Analysis
11
11
  Classifier: Topic :: Software Development :: Libraries
12
12
  Classifier: Typing :: Typed
13
13
  Classifier: License :: OSI Approved :: MIT License
14
+ Requires-Dist: ruff>=0.6.0 ; extra == 'dev'
15
+ Requires-Dist: pytest>=7.0 ; extra == 'dev'
16
+ Requires-Dist: pytest-cov>=4.1 ; extra == 'dev'
17
+ Requires-Dist: coverage>=7.5 ; extra == 'dev'
18
+ Requires-Dist: mypy>=1.10 ; extra == 'dev'
19
+ Requires-Dist: build>=1.2.1 ; extra == 'dev'
20
+ Provides-Extra: dev
14
21
  License-File: LICENSE
15
22
  Summary: Rust-accelerated quadtree for Python with fast inserts, range queries, and k-NN search.
16
23
  Keywords: quadtree,spatial-index,geometry,rust,pyo3,nearest-neighbor,k-nn
@@ -28,15 +35,15 @@ Project-URL: Issues, https://github.com/Elan456/fastquadtree/issues
28
35
  [![Wheels](https://img.shields.io/pypi/wheel/fastquadtree.svg)](https://pypi.org/project/fastquadtree/#files)
29
36
  [![License: MIT](https://img.shields.io/pypi/l/fastquadtree.svg)](LICENSE)
30
37
 
31
- [![Downloads total](https://static.pepy.tech/badge/fastquadtree)](https://pepy.tech/projects/fastquadtree)
32
- [![Downloads month](https://static.pepy.tech/badge/fastquadtree/month)](https://pepy.tech/projects/fastquadtree)
38
+ [![PyPI Downloads](https://static.pepy.tech/personalized-badge/fastquadtree?period=total&units=INTERNATIONAL_SYSTEM&left_color=GRAY&right_color=BLUE&left_text=Total+Downloads)](https://pepy.tech/projects/fastquadtree)
33
39
 
34
40
  [![Build](https://github.com/Elan456/fastquadtree/actions/workflows/release.yml/badge.svg)](https://github.com/Elan456/fastquadtree/actions/workflows/ci.yml)
35
41
  [![Codecov](https://codecov.io/gh/Elan456/fastquadtree/branch/main/graph/badge.svg)](https://codecov.io/gh/Elan456/fastquadtree)
36
42
 
37
43
  [![Rust core via PyO3](https://img.shields.io/badge/Rust-core%20via%20PyO3-orange)](https://pyo3.rs/)
38
44
  [![Built with maturin](https://img.shields.io/badge/Built%20with-maturin-1f6feb)](https://www.maturin.rs/)
39
- [![Code style: Black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
45
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
46
+
40
47
 
41
48
 
42
49
  ![Interactive_V2_Screenshot](https://raw.githubusercontent.com/Elan456/fastquadtree/main/assets/interactive_v2_screenshot.png)
@@ -58,18 +65,27 @@ fastquadtree **outperforms** all other quadtree Python packages, including the R
58
65
  ![Throughput](https://raw.githubusercontent.com/Elan456/fastquadtree/main/assets/quadtree_bench_throughput.png)
59
66
 
60
67
  ### Summary (largest dataset, PyQtree baseline)
61
- - Points: **500,000**, Queries: **500**
68
+ - Points: **250,000**, Queries: **500**
62
69
  --------------------
63
- - Fastest total: **fastquadtree** at **1.591 s**
70
+ - Fastest total: **fastquadtree** at **0.120 s**
64
71
 
65
72
  | Library | Build (s) | Query (s) | Total (s) | Speed vs PyQtree |
66
73
  |---|---:|---:|---:|---:|
67
- | fastquadtree | 0.165 | 1.427 | 1.591 | 5.09× |
68
- | Rtree | 1.320 | 2.369 | 3.688 | 2.20× |
69
- | PyQtree | 2.687 | 5.415 | 8.102 | 1.00× |
70
- | nontree-QuadTree | 1.284 | 9.891 | 11.175 | 0.73× |
71
- | quads | 2.346 | 10.129 | 12.475 | 0.65× |
72
- | e-pyquadtree | 1.795 | 11.855 | 13.650 | 0.59× |
74
+ | fastquadtree | 0.031 | 0.089 | 0.120 | 14.64× |
75
+ | Shapely STRtree | 0.179 | 0.100 | 0.279 | 6.29× |
76
+ | nontree-QuadTree | 0.595 | 0.605 | 1.200 | 1.46× |
77
+ | Rtree | 0.961 | 0.300 | 1.261 | 1.39× |
78
+ | e-pyquadtree | 1.005 | 0.660 | 1.665 | 1.05× |
79
+ | PyQtree | 1.492 | 0.263 | 1.755 | 1.00× |
80
+ | quads | 1.407 | 0.484 | 1.890 | 0.93× |
81
+
82
+ #### Benchmark Configuration
83
+ | Parameter | Value |
84
+ |---|---:|
85
+ | Bounds | (0, 0, 1000, 1000) |
86
+ | Max points per node | 128 |
87
+ | Max depth | 16 |
88
+ | Queries per experiment | 500 |
73
89
 
74
90
  ## Install
75
91
 
@@ -273,7 +289,7 @@ MIT. See `LICENSE`.
273
289
 
274
290
  ## Acknowledgments
275
291
 
276
- * Python libraries compared: [PyQtree], [e-pyquadtree], [Rtree], [nontree], [quads]
292
+ * Python libraries compared: [PyQtree], [e-pyquadtree], [Rtree], [nontree], [quads], [Shapely]
277
293
  * Built with [PyO3] and [maturin]
278
294
 
279
295
  [PyQtree]: https://pypi.org/project/pyqtree/
@@ -282,4 +298,6 @@ MIT. See `LICENSE`.
282
298
  [maturin]: https://www.maturin.rs/
283
299
  [Rtree]: https://pypi.org/project/Rtree/
284
300
  [nontree]: https://pypi.org/project/nontree/
285
- [quads]: https://pypi.org/project/quads/
301
+ [quads]: https://pypi.org/project/quads/
302
+ [Shapely]: https://pypi.org/project/Shapely/
303
+
@@ -0,0 +1,10 @@
1
+ fastquadtree-0.6.0.dist-info/METADATA,sha256=NRMlg6YFAuT7fwSjEfV2F_r3Yw71JDwKibvRVPaQyHI,10746
2
+ fastquadtree-0.6.0.dist-info/WHEEL,sha256=7bfl5v0wbVhXZba613g0x-n2obNNfpQuN8I1cQ4oaU8,94
3
+ fastquadtree-0.6.0.dist-info/licenses/LICENSE,sha256=46IVFhoCIwMo-ocq4olyEB1eBvvtaKic5yGLeKXnDuc,1092
4
+ fastquadtree/__init__.py,sha256=o7zH6lOu2lTxMafl9FNVZiecM0KVxqhC3FNSRbpaaqY,12435
5
+ fastquadtree/__init__.pyi,sha256=_HGJ9lpIZ9-JTSfMKSn8aif_tu60m2vgOXR4d0uXnRY,2062
6
+ fastquadtree/_bimap.py,sha256=SPFFw9hWAVRKZVXyDGYFFNw4MLV8qUlOq8gDbLcTqkg,3402
7
+ fastquadtree/_item.py,sha256=p-ymnE9S-c8Lc00KhNKN9Pt4bd05nZ6g-DB8q8D9zYk,533
8
+ fastquadtree/_native.pyd,sha256=72nxQfcx-JZkSZM3Jm-r7qHbwrL6Rhd5lrvftuuioeQ,254464
9
+ fastquadtree/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ fastquadtree-0.6.0.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- fastquadtree-0.5.0.dist-info/METADATA,sha256=7TPGeXEmmg20TEZc2Wx3gAiGMzdTuklhapwtEbn8IrQ,10119
2
- fastquadtree-0.5.0.dist-info/WHEEL,sha256=7bfl5v0wbVhXZba613g0x-n2obNNfpQuN8I1cQ4oaU8,94
3
- fastquadtree-0.5.0.dist-info/licenses/LICENSE,sha256=46IVFhoCIwMo-ocq4olyEB1eBvvtaKic5yGLeKXnDuc,1092
4
- fastquadtree/__init__.py,sha256=rqttCrFBaqMjD7BDZlcwkU9eeOQh2gzRi81xKHHLwLk,12652
5
- fastquadtree/__init__.pyi,sha256=4lKaqtnvylRzhj322ue_y_DO_xxrkzOzjpYnH5SN1Fg,2166
6
- fastquadtree/_bimap.py,sha256=zMp20NJkvVuO3jn2OeVApf7j4siBI4r2kU6SHyb7EZE,3428
7
- fastquadtree/_item.py,sha256=imyiwnbmY21mHsOvNqB4j-TsOPpAmsvb38LKxEL8q4o,526
8
- fastquadtree/_native.pyd,sha256=84XbRhl0Lj2ofRahwDK4Pjw_JiXvOVHfEFGD8hHbbJ0,247808
9
- fastquadtree/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- fastquadtree-0.5.0.dist-info/RECORD,,