fastquadtree 1.1.1__cp38-abi3-win_amd64.whl → 1.2.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.

Potentially problematic release.


This version of fastquadtree might be problematic. Click here for more details.

@@ -1,6 +1,7 @@
1
1
  # _abc_quadtree.py
2
2
  from __future__ import annotations
3
3
 
4
+ import pickle
4
5
  from abc import ABC, abstractmethod
5
6
  from typing import (
6
7
  TYPE_CHECKING,
@@ -58,8 +59,9 @@ class _BaseQuadTree(Generic[G, HitT, ItemType], ABC):
58
59
  def _new_native(self, bounds: Bounds, capacity: int, max_depth: int | None) -> Any:
59
60
  """Create the native engine instance."""
60
61
 
62
+ @staticmethod
61
63
  @abstractmethod
62
- def _make_item(self, id_: int, geom: G, obj: Any | None) -> ItemType:
64
+ def _make_item(id_: int, geom: G, obj: Any | None) -> ItemType:
63
65
  """Build an ItemType from id, geometry, and optional object."""
64
66
 
65
67
  # ---- ctor ----
@@ -84,6 +86,71 @@ class _BaseQuadTree(Generic[G, HitT, ItemType], ABC):
84
86
  self._next_id = 0
85
87
  self._count = 0
86
88
 
89
+ # ---- serialization ----
90
+
91
+ def to_dict(self) -> dict[str, Any]:
92
+ """
93
+ Serialize the quadtree to a dict suitable for JSON or other serialization.
94
+
95
+ Returns:
96
+ Includes a binary 'core' key for the native engine state, plus other metadata such as bounds and capacity and the obj store if tracking is enabled.
97
+ """
98
+
99
+ core_bytes = self._native.to_bytes()
100
+
101
+ return {
102
+ "core": core_bytes,
103
+ "store": self._store.to_dict() if self._store is not None else None,
104
+ "bounds": self._bounds,
105
+ "capacity": self._capacity,
106
+ "max_depth": self._max_depth,
107
+ "track_objects": self._track_objects,
108
+ "next_id": self._next_id,
109
+ "count": self._count,
110
+ }
111
+
112
+ def to_bytes(self) -> bytes:
113
+ """
114
+ Serialize the quadtree to bytes.
115
+
116
+ Returns:
117
+ Bytes representing the serialized quadtree. Can be saved as a file or loaded with `from_bytes()`.
118
+ """
119
+ return pickle.dumps(self.to_dict())
120
+
121
+ @classmethod
122
+ def from_bytes(cls, data: bytes) -> _BaseQuadTree[G, HitT, ItemType]:
123
+ """
124
+ Deserialize a quadtree from bytes.
125
+
126
+ Args:
127
+ data: Bytes representing the serialized quadtree from `to_bytes()`.
128
+
129
+ Returns:
130
+ A new quadtree instance with the same state as when serialized.
131
+ """
132
+ in_dict = pickle.loads(data)
133
+ core_bytes = in_dict["core"]
134
+ store_dict = in_dict["store"]
135
+
136
+ qt = cls.__new__(cls) # type: ignore[call-arg]
137
+ qt._native = cls._new_native_from_bytes(core_bytes)
138
+
139
+ if store_dict is not None:
140
+ qt._store = ObjStore.from_dict(store_dict, qt._make_item)
141
+ else:
142
+ qt._store = None
143
+
144
+ # Extract bounds, capacity, max_depth from native
145
+ qt._bounds = in_dict["bounds"]
146
+ qt._capacity = in_dict["capacity"]
147
+ qt._max_depth = in_dict["max_depth"]
148
+ qt._next_id = in_dict["next_id"]
149
+ qt._count = in_dict["count"]
150
+ qt._track_objects = in_dict["track_objects"]
151
+
152
+ return qt
153
+
87
154
  # ---- internal helper ----
88
155
 
89
156
  def _ids_to_objects(self, ids: Iterable[int]) -> list[Any]:
fastquadtree/_item.py CHANGED
@@ -27,6 +27,35 @@ class Item:
27
27
  self.geom: Point | Bounds = geom
28
28
  self.obj: Any | None = obj
29
29
 
30
+ def to_dict(self) -> dict[str, Any]:
31
+ """
32
+ Serialize the item to a dictionary.
33
+
34
+ Returns:
35
+ A dictionary with 'id', 'geom', and 'obj' keys.
36
+ """
37
+ return {
38
+ "id": self.id_,
39
+ "geom": self.geom,
40
+ "obj": self.obj,
41
+ }
42
+
43
+ @classmethod
44
+ def from_dict(cls, data: dict[str, Any]) -> Item:
45
+ """
46
+ Deserialize an item from a dictionary.
47
+
48
+ Args:
49
+ data: A dictionary with 'id', 'geom', and 'obj' keys.
50
+
51
+ Returns:
52
+ An Item instance populated with the deserialized data.
53
+ """
54
+ id_ = data["id"]
55
+ geom = data["geom"]
56
+ obj = data["obj"]
57
+ return cls(id_, geom, obj)
58
+
30
59
 
31
60
  class PointItem(Item):
32
61
  """
fastquadtree/_native.pyd CHANGED
Binary file
@@ -35,11 +35,40 @@ class ObjStore(Generic[TItem]):
35
35
 
36
36
  if items:
37
37
  for it in items:
38
- self.add(it)
38
+ self.add(it, handle_out_of_order=True)
39
+
40
+ # ---- Serialization ----
41
+ def to_dict(self) -> dict[str, Any]:
42
+ """
43
+ Serialize to a dict suitable for JSON or other serialization.
44
+
45
+ Returns:
46
+ A dict with 'items' key containing list of serialized items.
47
+ """
48
+ items = [it.to_dict() for it in self._arr if it is not None]
49
+ return {"items": items}
50
+
51
+ @classmethod
52
+ def from_dict(cls, data: dict[str, Any], item_factory: Any) -> ObjStore[TItem]:
53
+ """
54
+ Deserialize from a dict.
55
+
56
+ Args:
57
+ data: A dict with 'items' key containing list of serialized items.
58
+ item_factory: A callable that takes (id, obj) and returns an Item.
59
+
60
+ Returns:
61
+ An ObjStore instance populated with the deserialized items.
62
+ """
63
+ items = []
64
+ for item_data in data.get("items", []):
65
+ item = Item.from_dict(item_data)
66
+ items.append(item_factory(item.id_, item.geom, item.obj))
67
+ return cls(items)
39
68
 
40
69
  # -------- core --------
41
70
 
42
- def add(self, item: TItem) -> None:
71
+ def add(self, item: TItem, handle_out_of_order: bool = False) -> None:
43
72
  """
44
73
  Insert or replace the mapping at item.id_. Reverse map updated so obj points to id.
45
74
  """
@@ -48,9 +77,14 @@ class ObjStore(Generic[TItem]):
48
77
 
49
78
  # ids must be dense and assigned by the caller
50
79
  if id_ > len(self._arr):
51
- raise AssertionError(
52
- "ObjStore.add received an out-of-order id, use alloc_id() to get the next available id"
53
- )
80
+ if not handle_out_of_order:
81
+ raise AssertionError(
82
+ "ObjStore.add received an out-of-order id, use alloc_id() to get the next available id"
83
+ )
84
+ # fill holes with None
85
+ while len(self._arr) < id_:
86
+ self._arr.append(None)
87
+ self._objs.append(None)
54
88
 
55
89
  if id_ == len(self._arr):
56
90
  # append
@@ -49,6 +49,18 @@ class QuadTree(_BaseQuadTree[Point, _IdCoord, PointItem]):
49
49
  track_objects=track_objects,
50
50
  )
51
51
 
52
+ @classmethod
53
+ def from_bytes(cls, data: bytes) -> QuadTree:
54
+ """
55
+ Create a QuadTree instance from serialized bytes.
56
+
57
+ Args:
58
+ data: Serialized byte data from `to_bytes()`.
59
+ Returns:
60
+ A QuadTree instance.
61
+ """
62
+ return super().from_bytes(data)
63
+
52
64
  @overload
53
65
  def query(
54
66
  self, rect: Bounds, *, as_items: Literal[False] = ...
@@ -145,5 +157,11 @@ class QuadTree(_BaseQuadTree[Point, _IdCoord, PointItem]):
145
157
  return _RustQuadTree(bounds, capacity)
146
158
  return _RustQuadTree(bounds, capacity, max_depth=max_depth)
147
159
 
148
- def _make_item(self, id_: int, geom: Point, obj: Any | None) -> PointItem:
160
+ @classmethod
161
+ def _new_native_from_bytes(cls, data: bytes) -> Any:
162
+ """Create a new native engine instance from serialized bytes."""
163
+ return _RustQuadTree.from_bytes(data)
164
+
165
+ @staticmethod
166
+ def _make_item(id_: int, geom: Point, obj: Any | None) -> PointItem:
149
167
  return PointItem(id_, geom, obj)
fastquadtree/pyqtree.py CHANGED
@@ -122,6 +122,9 @@ class Index:
122
122
  - **item**: The item to insert into the index, which will be returned by the intersection method
123
123
  - **bbox**: The spatial bounding box tuple of the item, with four members (xmin,ymin,xmax,ymax)
124
124
  """
125
+ if type(bbox) is list: # Handle list input
126
+ bbox = tuple(bbox)
127
+
125
128
  if self._free:
126
129
  rid = self._free.pop()
127
130
  self._objects[rid] = item
@@ -141,6 +144,9 @@ class Index:
141
144
 
142
145
  Both parameters need to exactly match the parameters provided to the insert method.
143
146
  """
147
+ if type(bbox) is list: # Handle list input
148
+ bbox = tuple(bbox)
149
+
144
150
  rid = self._item_to_id.pop(id(item))
145
151
  self._qt.delete(rid, bbox)
146
152
  self._objects[rid] = None
@@ -50,6 +50,18 @@ class RectQuadTree(_BaseQuadTree[Bounds, _IdRect, RectItem]):
50
50
  track_objects=track_objects,
51
51
  )
52
52
 
53
+ @classmethod
54
+ def from_bytes(cls, data: bytes) -> RectQuadTree:
55
+ """
56
+ Create a RectQuadTree instance from serialized bytes.
57
+
58
+ Args:
59
+ data: Serialized byte data from `to_bytes()`.
60
+ Returns:
61
+ A RectQuadTree instance.
62
+ """
63
+ return super().from_bytes(data)
64
+
53
65
  @overload
54
66
  def query(
55
67
  self, rect: Bounds, *, as_items: Literal[False] = ...
@@ -81,5 +93,11 @@ class RectQuadTree(_BaseQuadTree[Bounds, _IdRect, RectItem]):
81
93
  return _RustRectQuadTree(bounds, capacity)
82
94
  return _RustRectQuadTree(bounds, capacity, max_depth=max_depth)
83
95
 
84
- def _make_item(self, id_: int, geom: Bounds, obj: Any | None) -> RectItem:
96
+ @classmethod
97
+ def _new_native_from_bytes(cls, data: bytes) -> Any:
98
+ """Create a new native engine instance from serialized bytes."""
99
+ return _RustRectQuadTree.from_bytes(data)
100
+
101
+ @staticmethod
102
+ def _make_item(id_: int, geom: Bounds, obj: Any | None) -> RectItem:
85
103
  return RectItem(id_, geom, obj)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastquadtree
3
- Version: 1.1.1
3
+ Version: 1.2.0
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3 :: Only
6
6
  Classifier: Programming Language :: Rust
@@ -70,6 +70,7 @@ Rust-optimized quadtree with a clean Python API
70
70
  - Support for [inserting bounding boxes](https://elan456.github.io/fastquadtree/api/rect_quadtree/) or points
71
71
  - Fast KNN and range queries
72
72
  - Optional object tracking for id ↔ object mapping
73
+ - Fast [serialization](https://elan456.github.io/fastquadtree/benchmark/#serialization-vs-rebuild) to/from bytes
73
74
  - [100% test coverage](https://codecov.io/gh/Elan456/fastquadtree) and CI on GitHub Actions
74
75
  - Offers a drop-in [pyqtree shim](https://elan456.github.io/fastquadtree/benchmark/#pyqtree-drop-in-shim-performance-gains) that is 6.567x faster while keeping the same API
75
76
 
@@ -0,0 +1,13 @@
1
+ fastquadtree-1.2.0.dist-info/METADATA,sha256=lLyIIqULosQRzzWzbaCn8icIa2dEeaCneAIV--od--k,9773
2
+ fastquadtree-1.2.0.dist-info/WHEEL,sha256=CG8OzNtm0LMpJ2zhrjswlO8N-965OeMLklsQAG-nMvQ,94
3
+ fastquadtree-1.2.0.dist-info/licenses/LICENSE,sha256=46IVFhoCIwMo-ocq4olyEB1eBvvtaKic5yGLeKXnDuc,1092
4
+ fastquadtree/__init__.py,sha256=jy7uimnd7QDllmNOgnRByKom1aONIKOWhCvCJxndsnM,200
5
+ fastquadtree/_base_quadtree.py,sha256=g0xpc_RfdCJscNlt04RmTIETYIMjmQtCR3_yg3di2pg,12416
6
+ fastquadtree/_item.py,sha256=hlA71tvmLUQey42VokaZ7AmorIA8yZoGlvZWW9G4l4k,2496
7
+ fastquadtree/_native.pyd,sha256=_VmaB34V0tbtcS7yi-VBKRkYlKZazbjdkcWeUWjyUlw,338944
8
+ fastquadtree/_obj_store.py,sha256=e164LT01nU5KqPX0udMzcJtpONAohDMEw-c9zEqh7uo,6723
9
+ fastquadtree/point_quadtree.py,sha256=nzYu48-XN0CVDE7zPOxfW4wVpVlxHDObXhjitkzqNzI,5864
10
+ fastquadtree/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ fastquadtree/pyqtree.py,sha256=Evu-MKHKrs_xNVWChukD4hJLyIc-u7iuQ4hA_zvJtu4,6246
12
+ fastquadtree/rect_quadtree.py,sha256=73lL6gz51j_WNTnvgRr38ZpMrPPiLM9UW977pD8vzy8,3662
13
+ fastquadtree-1.2.0.dist-info/RECORD,,
@@ -1,13 +0,0 @@
1
- fastquadtree-1.1.1.dist-info/METADATA,sha256=vT-1Ozgx_xPB0ixOsf0ZE97kjLT1bMSshXqhe9AFupA,9659
2
- fastquadtree-1.1.1.dist-info/WHEEL,sha256=CG8OzNtm0LMpJ2zhrjswlO8N-965OeMLklsQAG-nMvQ,94
3
- fastquadtree-1.1.1.dist-info/licenses/LICENSE,sha256=46IVFhoCIwMo-ocq4olyEB1eBvvtaKic5yGLeKXnDuc,1092
4
- fastquadtree/__init__.py,sha256=jy7uimnd7QDllmNOgnRByKom1aONIKOWhCvCJxndsnM,200
5
- fastquadtree/_base_quadtree.py,sha256=wICOB-CHpneZ00OvPZzQpoUGKVvTRhLScnFxMY4i9Do,10205
6
- fastquadtree/_item.py,sha256=42sGn--Bcq8NrgNBcoKzJClJjjAHK6ntesyf31qz5yM,1741
7
- fastquadtree/_native.pyd,sha256=H36CeF-_KGgdO5ZlAPM_3ao6mO4dTtWALZacxyMjtS0,303104
8
- fastquadtree/_obj_store.py,sha256=NVBGB8XsSiWFzoBgeVE2ScSuL7K9nXaggsp6nXqSGyc,5434
9
- fastquadtree/point_quadtree.py,sha256=v_iNQZfLOPDKwTwIPMMinSfRCn1im3P0gWEsOnK6qpY,5327
10
- fastquadtree/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- fastquadtree/pyqtree.py,sha256=5FgKVQidHnzPsS9KwWLiliJoOcv0VtMFvMP3jr2uHm8,6072
12
- fastquadtree/rect_quadtree.py,sha256=ZrOj5dNVg4B2lhuKZ9EMOoYeQNbD3Vbz8S3TxCVCj_c,3109
13
- fastquadtree-1.1.1.dist-info/RECORD,,