Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-Rtree for openSUSE:Factory checked in at 2025-09-10 17:30:32 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-Rtree (Old) and /work/SRC/openSUSE:Factory/.python-Rtree.new.1977 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-Rtree" Wed Sep 10 17:30:32 2025 rev:6 rq:1303574 version:1.4.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-Rtree/python-Rtree.changes 2025-06-03 19:10:47.158034417 +0200 +++ /work/SRC/openSUSE:Factory/.python-Rtree.new.1977/python-Rtree.changes 2025-09-10 17:30:43.558754546 +0200 @@ -1,0 +2,16 @@ +Wed Sep 10 05:53:17 UTC 2025 - John Paul Adrian Glaubitz <[email protected]> + +- Update to 1.4.1 + * Rename main branch references by :user:`mwtoews` in (#356) + * Fixing an incorrect reassignment in nearest_v and intersection_v + by @Atilleusz in (#358) + * Add spatialindex version to tests, add common pytest configuration + by @mwtoews in (#360) + * Refactor array-loading methods, add tests by @mwtoews in (#361) + * Minor refactor of code blocks in docs by @mwtoews in (#362) + * Resolve some issues in the batch API by @FreddieWitherden in (#367) + * Fix #369 (load libspatialindex without changing cwd) + by @remicres in (#370) + * arm64 wheels on Windows by @w8sl in (#378) and (#371) + +------------------------------------------------------------------- Old: ---- rtree-1.4.0.tar.gz New: ---- rtree-1.4.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-Rtree.spec ++++++ --- /var/tmp/diff_new_pack.ptcrHF/_old 2025-09-10 17:30:44.422790752 +0200 +++ /var/tmp/diff_new_pack.ptcrHF/_new 2025-09-10 17:30:44.422790752 +0200 @@ -18,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-Rtree -Version: 1.4.0 +Version: 1.4.1 Release: 0 Summary: R-Tree spatial index for Python GIS License: MIT ++++++ rtree-1.4.0.tar.gz -> rtree-1.4.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtree-1.4.0/CHANGES.rst new/rtree-1.4.1/CHANGES.rst --- old/rtree-1.4.0/CHANGES.rst 2025-03-06 00:23:54.000000000 +0100 +++ new/rtree-1.4.1/CHANGES.rst 2025-08-13 21:26:31.000000000 +0200 @@ -1,3 +1,17 @@ +1.4.1: 2025-08-13 +================= + +- Rename main branch references by :user:`mwtoews` in :PR:`356` +- Fixing an incorrect reassignment in nearest_v and intersection_v by :user:`Atilleusz` in :PR:`358` +- Add spatialindex version to tests, add common pytest configuration by :user:`mwtoews` in :PR:`360` +- Refactor array-loading methods, add tests by :user:`mwtoews` in :PR:`361` +- Minor refactor of code blocks in docs by :user:`mwtoews` in :PR:`362` +- Resolve some issues in the batch API by :user:`FreddieWitherden` in :PR:`367` +- fix #369 (load libspatialindex without changing cwd) by :user:`remicres` in :PR:`370` +- arm64 wheels on windows by @w8sl in :PR:`378` and :PR:`371` + +`Full Changelog <https://github.com/Toblerity/rtree/compare/1.4.0...1.4.1>`__ + 1.4.0: 2025-03-06 ================= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtree-1.4.0/PKG-INFO new/rtree-1.4.1/PKG-INFO --- old/rtree-1.4.0/PKG-INFO 2025-03-06 00:23:56.896201800 +0100 +++ new/rtree-1.4.1/PKG-INFO 2025-08-13 21:26:35.375225800 +0200 @@ -1,17 +1,16 @@ -Metadata-Version: 2.2 +Metadata-Version: 2.4 Name: rtree -Version: 1.4.0 +Version: 1.4.1 Summary: R-Tree spatial index for Python GIS Author-email: Sean Gillies <[email protected]> Maintainer-email: Howard Butler <[email protected]>, Mike Taves <[email protected]> -License: MIT +License-Expression: MIT Project-URL: Documentation, https://rtree.readthedocs.io Project-URL: Repository, https://github.com/Toblerity/rtree Keywords: gis,spatial,index,r-tree Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Science/Research -Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.9 @@ -24,10 +23,11 @@ Requires-Python: >=3.9 Description-Content-Type: text/markdown License-File: LICENSE.txt +Dynamic: license-file # Rtree: Spatial indexing for Python - +[](https://github.com/Toblerity/rtree/actions/workflows/test.yml) [](https://badge.fury.io/py/rtree) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtree-1.4.0/README.md new/rtree-1.4.1/README.md --- old/rtree-1.4.0/README.md 2025-03-06 00:23:54.000000000 +0100 +++ new/rtree-1.4.1/README.md 2025-08-13 21:26:31.000000000 +0200 @@ -1,6 +1,6 @@ # Rtree: Spatial indexing for Python - +[](https://github.com/Toblerity/rtree/actions/workflows/test.yml) [](https://badge.fury.io/py/rtree) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtree-1.4.0/docs/source/install.rst new/rtree-1.4.1/docs/source/install.rst --- old/rtree-1.4.0/docs/source/install.rst 2025-03-06 00:23:54.000000000 +0100 +++ new/rtree-1.4.1/docs/source/install.rst 2025-08-13 21:26:31.000000000 +0200 @@ -10,7 +10,9 @@ https://libspatialindex.org -The library supports CMake builds, so it is a matter of:: +The library supports CMake builds, so it is a matter of: + +.. code-block:: console $ mkdir build && cd build $ cmake .. @@ -20,15 +22,21 @@ You may need to run the ``ldconfig`` command after installing the library to ensure that applications can find it at startup time. -Rtree can be easily installed via pip:: +Rtree can be easily installed via pip: + +.. code-block:: console $ pip install rtree -or by running in a local source directory:: +or by running in a local source directory: + +.. code-block:: console $ pip install -e . -You can build and test in place like:: +You can build and test in place like: + +.. code-block:: console $ pytest @@ -37,9 +45,11 @@ The Windows DLLs of `libspatialindex`_ are pre-compiled in windows installers that are available from `PyPI`_. Installation on Windows -is as easy as:: +is as easy as: + +.. code-block:: console - pip install rtree + $ pip install rtree .. _`PyPI`: https://pypi.org/project/rtree/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtree-1.4.0/docs/source/performance.rst new/rtree-1.4.1/docs/source/performance.rst --- old/rtree-1.4.0/docs/source/performance.rst 2025-03-06 00:23:54.000000000 +0100 +++ new/rtree-1.4.1/docs/source/performance.rst 2025-08-13 21:26:31.000000000 +0200 @@ -6,7 +6,7 @@ See the `benchmarks.py`_ file for a comparison of various query methods and how much acceleration can be obtained from using Rtree. -.. _benchmarks.py: https://github.com/Toblerity/rtree/blob/master/benchmarks/benchmarks.py +.. _benchmarks.py: https://github.com/Toblerity/rtree/blob/main/benchmarks/benchmarks.py There are a few simple things that will improve performance. @@ -17,12 +17,14 @@ performance over :py:meth:`~rtree.index.Index.insert` by allowing the data to be pre-sorted -:: +.. code-block:: pycon + >>> from rtree import index >>> def generator_function(somedata): - ... for i, obj in enumerate(somedata): - ... yield (i, (obj.xmin, obj.ymin, obj.xmax, obj.ymax), obj) - >>> r = index.Index(generator_function(somedata)) + ... for i, obj in enumerate(somedata): + ... yield (i, (obj.xmin, obj.ymin, obj.xmax, obj.ymax), obj) + ... + >>> r = index.Index(generator_function(somedata)) # doctest: +SKIP After bulk loading the index, you can then insert additional records into the index using :py:meth:`~rtree.index.Index.insert` @@ -30,12 +32,14 @@ Override :py:data:`~rtree.index.Index.dumps` to use the highest pickle protocol ............................................................................... -:: +.. code-block:: pycon - >>> import cPickle, rtree + >>> import pickle + >>> import rtree >>> class FastRtree(rtree.Rtree): ... def dumps(self, obj): - ... return cPickle.dumps(obj, -1) + ... return pickle.dumps(obj, -1) + ... >>> r = FastRtree() .. topic:: Update from January 2024 @@ -45,13 +49,16 @@ .. _pull request on GitHub: https://github.com/Toblerity/rtree/pull/197 -Use objects='raw' +Use objects="raw" ............................................................................... In any :py:meth:`~rtree.index.Index.intersection` or -:py:meth:`~rtree.index.Index.nearest` or query, use objects='raw' keyword -argument :: +:py:meth:`~rtree.index.Index.nearest` or query, use ``objects="raw"`` keyword +argument: +.. code-block:: pycon + + >>> xmin, ymin, xmax, ymax = 0.0, 0.0, 1.0, 1.0 >>> objs = r.intersection((xmin, ymin, xmax, ymax), objects="raw") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtree-1.4.0/docs/source/tutorial.rst new/rtree-1.4.1/docs/source/tutorial.rst --- old/rtree-1.4.0/docs/source/tutorial.rst 2025-03-06 00:23:54.000000000 +0100 +++ new/rtree-1.4.1/docs/source/tutorial.rst 2025-08-13 21:26:31.000000000 +0200 @@ -18,7 +18,9 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ After :ref:`installing <installation>` :ref:`Rtree <home>`, you should be able to -open up a Python prompt and issue the following:: +open up a Python prompt and issue the following: + +.. code-block:: pycon >>> from rtree import index @@ -31,7 +33,9 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ After importing the index module, construct an index with the default -construction:: +construction: + +.. code-block:: pycon >>> idx = index.Index() @@ -45,7 +49,9 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ After instantiating the index, create a bounding box that we can -insert into the index:: +insert into the index: + +.. code-block:: pycon >>> left, bottom, right, top = (0.0, 0.0, 1.0, 1.0) @@ -61,7 +67,9 @@ Insert records into the index ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Insert an entry into the index:: +Insert an entry into the index: + +.. code-block:: pycon >>> idx.insert(0, (left, bottom, right, top)) @@ -90,13 +98,17 @@ Intersection ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -Given a query window, return ids that are contained within the window:: +Given a query window, return ids that are contained within the window: + +.. code-block:: pycon >>> list(idx.intersection((1.0, 1.0, 2.0, 2.0))) [0] Given a query window that is beyond the bounds of data we have in the -index:: +index: + +.. code-block:: pycon >>> list(idx.intersection((1.0000001, 1.0000001, 2.0, 2.0))) [] @@ -105,7 +117,9 @@ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ The following finds the 1 nearest item to the given bounds. If multiple items -are of equal distance to the bounds, both are returned:: +are of equal distance to the bounds, both are returned: + +.. code-block:: pycon >>> idx.insert(1, (left, bottom, right, top)) >>> list(idx.nearest((1.0000001, 1.0000001, 2.0, 2.0), 1)) @@ -119,12 +133,16 @@ Rtree also supports inserting any object you can pickle into the index (called a clustered index in `libspatialindex`_ parlance). The following inserts the -picklable object ``42`` into the index with the given id:: +picklable object ``42`` into the index with the given id ``2``: + +.. code-block:: pycon - >>> idx.insert(id=id, coordinates=(left, bottom, right, top), obj=42) + >>> idx.insert(id=2, coordinates=(left, bottom, right, top), obj=42) You can then return a list of objects by giving the ``objects=True`` flag -to intersection:: +to intersection: + +.. code-block:: pycon >>> [n.object for n in idx.intersection((left, bottom, right, top), objects=True)] [None, None, 42] @@ -140,17 +158,28 @@ One of :ref:`Rtree <home>`'s most useful properties is the ability to serialize Rtree indexes to disk. These include the clustered indexes -described :ref:`here <clustered>`:: +described :ref:`here <clustered>`: - >>> file_idx = index.Rtree('rtree') +.. code-block:: pycon + + >>> import os + >>> from tempfile import TemporaryDirectory + >>> prev_dir = os.getcwd() + >>> temp_dir = TemporaryDirectory() + >>> os.chdir(temp_dir.name) + >>> file_idx = index.Rtree("myidx") >>> file_idx.insert(1, (left, bottom, right, top)) >>> file_idx.insert(2, (left - 1.0, bottom - 1.0, right + 1.0, top + 1.0)) >>> [n for n in file_idx.intersection((left, bottom, right, top))] [1, 2] + >>> sorted(os.listdir()) + ['myidx.dat', 'myidx.idx'] + >>> os.chdir(prev_dir) + >>> temp_dir.cleanup() .. note:: - By default, if an index file with the given name `rtree` in the example + By default, if an index file with the given name ``myidx`` in the example above already exists on the file system, it will be opened in append mode and not be re-created. You can control this behavior with the :py:attr:`rtree.index.Property.overwrite` property of the index property @@ -170,12 +199,12 @@ are controllable using the :py:attr:`rtree.index.Property.dat_extension` and :py:attr:`rtree.index.Property.idx_extension` index properties. -:: +.. code-block:: pycon - >>> p = rtree.index.Property() - >>> p.dat_extension = 'data' - >>> p.idx_extension = 'index' - >>> file_idx = index.Index('rtree', properties = p) + >>> p = index.Property() + >>> p.dat_extension = "data" + >>> p.idx_extension = "index" + >>> file_idx = index.Index("rtree", properties=p) # doctest: +SKIP 3D indexes .............................................................................. @@ -185,17 +214,23 @@ stored on disk using two files -- an index file (.idx) and a data (.dat) file. You can modify the extensions these files use by altering the properties of the index at instantiation time. The following creates a 3D index that is -stored on disk as the files ``3d_index.data`` and ``3d_index.index``:: +stored on disk as the files ``3d_index.data`` and ``3d_index.index``: + +.. code-block:: pycon >>> from rtree import index + >>> temp_dir = TemporaryDirectory() + >>> os.chdir(temp_dir.name) >>> p = index.Property() >>> p.dimension = 3 - >>> p.dat_extension = 'data' - >>> p.idx_extension = 'index' - >>> idx3d = index.Index('3d_index',properties=p) + >>> p.dat_extension = "data" + >>> p.idx_extension = "index" + >>> idx3d = index.Index("3d_index", properties=p) >>> idx3d.insert(1, (0, 60, 23.0, 0, 60, 42.0)) - >>> idx3d.intersection( (-1, 62, 22, -1, 62, 43)) - [1L] + >>> list(idx3d.intersection((-1, 60, 22, 1, 62, 43))) + [1] + >>> os.chdir(prev_dir) + >>> temp_dir.cleanup() ZODB and Custom Storages .............................................................................. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtree-1.4.0/pyproject.toml new/rtree-1.4.1/pyproject.toml --- old/rtree-1.4.0/pyproject.toml 2025-03-06 00:23:54.000000000 +0100 +++ new/rtree-1.4.1/pyproject.toml 2025-08-13 21:26:31.000000000 +0200 @@ -15,12 +15,11 @@ readme = "README.md" requires-python = ">=3.9" keywords = ["gis", "spatial", "index", "r-tree"] -license = {text = "MIT"} +license = "MIT" classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Intended Audience :: Science/Research", - "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.9", @@ -49,7 +48,6 @@ rtree = ["py.typed"] [tool.cibuildwheel] -build = "cp39-*" build-verbosity = 3 before-all = "pip install wheel" repair-wheel-command = "python scripts/repair_wheel.py -w {dest_dir} {wheel}" @@ -82,7 +80,7 @@ ] [tool.cibuildwheel.windows] -archs = ["AMD64"] +archs = ["auto64"] before-build = [ "call {project}\\scripts\\install_libspatialindex.bat", ] @@ -95,6 +93,11 @@ "@overload", ] +[tool.pytest.ini_options] +minversion = "6.0" +addopts = "--import-mode=importlib" +testpaths = ["tests"] + [tool.ruff.lint] select = [ "E", "W", # pycodestyle diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtree-1.4.0/rtree/__init__.py new/rtree-1.4.1/rtree/__init__.py --- old/rtree-1.4.0/rtree/__init__.py 2025-03-06 00:23:54.000000000 +0100 +++ new/rtree-1.4.1/rtree/__init__.py 2025-08-13 21:26:31.000000000 +0200 @@ -7,6 +7,6 @@ from __future__ import annotations -__version__ = "1.4.0" +__version__ = "1.4.1" from .index import Index, Rtree # noqa diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtree-1.4.0/rtree/finder.py new/rtree-1.4.1/rtree/finder.py --- old/rtree-1.4.0/rtree/finder.py 2025-03-06 00:23:54.000000000 +0100 +++ new/rtree-1.4.1/rtree/finder.py 2025-08-13 21:26:31.000000000 +0200 @@ -86,8 +86,6 @@ except importlib.metadata.PackageNotFoundError: pass - # get the starting working directory - cwd = os.getcwd() for cand in _candidates: if cand.is_dir(): # if our candidate is a directory use best guess @@ -104,9 +102,9 @@ continue try: - # move to the location we're checking - os.chdir(path) # try loading the target file candidate + # These should be fully specified paths to + # files rt = ctypes.cdll.LoadLibrary(str(target)) if rt is not None: return rt @@ -115,8 +113,6 @@ f"rtree.finder ({target}) unexpected error: {err!s}", file=sys.stderr, ) - finally: - os.chdir(cwd) try: # try loading library using LD path search diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtree-1.4.0/rtree/index.py new/rtree-1.4.1/rtree/index.py --- old/rtree-1.4.0/rtree/index.py 2025-03-06 00:23:54.000000000 +0100 +++ new/rtree-1.4.1/rtree/index.py 2025-08-13 21:26:31.000000000 +0200 @@ -1061,12 +1061,7 @@ """ import numpy as np - assert mins.shape == maxs.shape - assert mins.strides == maxs.strides - - # Cast - mins = mins.astype(np.float64) - maxs = maxs.astype(np.float64) + mins, maxs = self._prepare_v_arrays(mins, maxs) # Extract counts n, d = mins.shape @@ -1085,7 +1080,7 @@ self.handle, n - offn, d, - len(ids), + len(ids) - offi, d_i_stri, d_j_stri, mins[offn:].ctypes.data, @@ -1103,12 +1098,13 @@ offi += counts[offn : offn + nr.value].sum() offn += nr.value - ids = ids.resize(2 * len(ids), refcheck=False) + ids.resize(2 * len(ids) + counts[offn], refcheck=False) def nearest_v( self, mins, maxs, + *, num_results=1, max_dists=None, strict=False, @@ -1144,12 +1140,7 @@ """ import numpy as np - assert mins.shape == maxs.shape - assert mins.strides == maxs.strides - - # Cast - mins = mins.astype(np.float64) - maxs = maxs.astype(np.float64) + mins, maxs = self._prepare_v_arrays(mins, maxs) # Extract counts n, d = mins.shape @@ -1164,9 +1155,11 @@ offn, offi = 0, 0 if max_dists is not None: - assert len(max_dists) == n - - dists = max_dists.astype(np.float64).copy() + dists = np.ascontiguousarray(np.atleast_1d(max_dists), dtype=np.float64) + if dists.ndim != 1: + raise ValueError("max_dists must have 1 dimension") + if len(dists) != n: + raise ValueError(f"max_dists must have length {n}") elif return_max_dists: dists = np.zeros(n) else: @@ -1178,7 +1171,7 @@ num_results if not strict else -num_results, n - offn, d, - len(ids), + len(ids) - offi, d_i_stri, d_j_stri, mins[offn:].ctypes.data, @@ -1189,7 +1182,7 @@ ctypes.byref(nr), ) - # If we got the expected nuber of results then return + # If we got the expected number of results then return if nr.value == n - offn: if return_max_dists: return ids[: counts.sum()], counts, dists @@ -1200,7 +1193,34 @@ offi += counts[offn : offn + nr.value].sum() offn += nr.value - ids = ids.resize(2 * len(ids), refcheck=False) + ids.resize(2 * len(ids) + counts[offn], refcheck=False) + + def _prepare_v_arrays(self, mins, maxs): + import numpy as np + + # Ensure inputs are 2D float64 arrays + if mins is maxs: + mins = maxs = np.atleast_2d(mins).astype(np.float64) + else: + mins = np.atleast_2d(mins).astype(np.float64) + maxs = np.atleast_2d(maxs).astype(np.float64) + + if mins.ndim != 2 or maxs.ndim != 2: + raise ValueError("mins/maxs must have 2 dimensions: (n, d)") + if mins.shape != maxs.shape: + raise ValueError("mins and maxs shapes not equal") + if mins.strides != maxs.strides: + raise ValueError("mins and maxs strides not equal") + + # Handle invalid strides + if any(s % mins.itemsize for s in mins.strides): + if mins is maxs: + mins = maxs = mins.copy() + else: + mins = mins.copy() + maxs = maxs.copy() + + return mins, maxs def _nearestTP(self, coordinates, velocities, times, num_results=1, objects=False): p_mins, p_maxs = self.get_coordinate_pointers(coordinates) @@ -1427,22 +1447,26 @@ return IndexStreamHandle(self.properties.handle, stream) def _create_idx_from_array(self, ibuf, minbuf, maxbuf): - assert len(ibuf) == len(minbuf) - assert len(ibuf) == len(maxbuf) - assert minbuf.strides == maxbuf.strides - - # Cast - ibuf = ibuf.astype(int) - minbuf = minbuf.astype(float) - maxbuf = maxbuf.astype(float) + import numpy as np + + # Prepare the arrays + ibuf = ibuf.astype(np.int64) + minbuf, maxbuf = self._prepare_v_arrays(minbuf, maxbuf) + + if len(ibuf) != len(minbuf): + raise ValueError("index and point counts different") + + # Handle misaligned data + if ibuf.strides[0] % ibuf.itemsize: + ibuf = ibuf.copy() # Extract counts n, d = minbuf.shape # Compute strides - i_stri = ibuf.strides[0] // 8 - d_i_stri = minbuf.strides[0] // 8 - d_j_stri = minbuf.strides[1] // 8 + i_stri = ibuf.strides[0] // ibuf.itemsize + d_i_stri = minbuf.strides[0] // minbuf.itemsize + d_j_stri = minbuf.strides[1] // minbuf.itemsize return IndexArrayHandle( self.properties.handle, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtree-1.4.0/rtree.egg-info/PKG-INFO new/rtree-1.4.1/rtree.egg-info/PKG-INFO --- old/rtree-1.4.0/rtree.egg-info/PKG-INFO 2025-03-06 00:23:56.000000000 +0100 +++ new/rtree-1.4.1/rtree.egg-info/PKG-INFO 2025-08-13 21:26:35.000000000 +0200 @@ -1,17 +1,16 @@ -Metadata-Version: 2.2 +Metadata-Version: 2.4 Name: rtree -Version: 1.4.0 +Version: 1.4.1 Summary: R-Tree spatial index for Python GIS Author-email: Sean Gillies <[email protected]> Maintainer-email: Howard Butler <[email protected]>, Mike Taves <[email protected]> -License: MIT +License-Expression: MIT Project-URL: Documentation, https://rtree.readthedocs.io Project-URL: Repository, https://github.com/Toblerity/rtree Keywords: gis,spatial,index,r-tree Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Science/Research -Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.9 @@ -24,10 +23,11 @@ Requires-Python: >=3.9 Description-Content-Type: text/markdown License-File: LICENSE.txt +Dynamic: license-file # Rtree: Spatial indexing for Python - +[](https://github.com/Toblerity/rtree/actions/workflows/test.yml) [](https://badge.fury.io/py/rtree) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtree-1.4.0/rtree.egg-info/SOURCES.txt new/rtree-1.4.1/rtree.egg-info/SOURCES.txt --- old/rtree-1.4.0/rtree.egg-info/SOURCES.txt 2025-03-06 00:23:56.000000000 +0100 +++ new/rtree-1.4.1/rtree.egg-info/SOURCES.txt 2025-08-13 21:26:35.000000000 +0200 @@ -32,6 +32,7 @@ rtree.egg-info/top_level.txt tests/__init__.py tests/boxes_15x15.data +tests/common.py tests/conftest.py tests/rungrind.dist tests/test_finder.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtree-1.4.0/tests/common.py new/rtree-1.4.1/tests/common.py --- old/rtree-1.4.0/tests/common.py 1970-01-01 01:00:00.000000000 +0100 +++ new/rtree-1.4.1/tests/common.py 2025-08-13 21:26:31.000000000 +0200 @@ -0,0 +1,10 @@ +"""Common test functions.""" + +import pytest + +from rtree.core import rt + +sidx_version_string = rt.SIDX_Version().decode() +sidx_version = tuple(map(int, sidx_version_string.split(".", maxsplit=3)[:3])) + +skip_sidx_lt_210 = pytest.mark.skipif(sidx_version < (2, 1, 0), reason="SIDX < 2.1.0") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtree-1.4.0/tests/conftest.py new/rtree-1.4.1/tests/conftest.py --- old/rtree-1.4.0/tests/conftest.py 2025-03-06 00:23:54.000000000 +0100 +++ new/rtree-1.4.1/tests/conftest.py 2025-08-13 21:26:31.000000000 +0200 @@ -8,7 +8,7 @@ import py import pytest -import rtree +from .common import sidx_version_string data_files = ["boxes_15x15.data"] @@ -25,7 +25,7 @@ def pytest_report_header(config): """Header for pytest.""" vers = [ - f"SIDX version: {rtree.core.rt.SIDX_Version().decode()}", + f"SIDX version: {sidx_version_string}", f"NumPy version: {numpy.__version__}", ] return "\n".join(vers) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtree-1.4.0/tests/test_index.py new/rtree-1.4.1/tests/test_index.py --- old/rtree-1.4.0/tests/test_index.py 2025-03-06 00:23:54.000000000 +0100 +++ new/rtree-1.4.1/tests/test_index.py 2025-08-13 21:26:31.000000000 +0200 @@ -14,6 +14,8 @@ from rtree import core, index from rtree.exceptions import RTreeError +from .common import skip_sidx_lt_210 + class IndexTestCase(unittest.TestCase): def setUp(self) -> None: @@ -268,6 +270,26 @@ self.assertEqual([1, 1], list(idx.intersection((0, 0, 5, 5)))) + @skip_sidx_lt_210 + def test_intersection_v(self) -> None: + mins = np.array([[0, 1]] * 2).T + maxs = np.array([[60, 50]] * 2).T + ret = self.idx.intersection_v(mins, maxs) + assert type(ret) is tuple + ids, counts = ret + assert ids.dtype == np.int64 + ids0 = [0, 4, 16, 27, 35, 40, 47, 50, 76, 80] + ids1 = [0, 16, 27, 35, 47, 76] + assert ids.tolist() == ids0 + ids1 + assert counts.dtype == np.uint64 + assert counts.tolist() == [len(ids0), len(ids1)] + + # errors + with pytest.raises(ValueError, match="must have 2 dimensions"): + self.idx.intersection_v(np.ones((2, 3, 4)), 4) + with pytest.raises(ValueError, match="shapes not equal"): + self.idx.intersection_v([0], [10, 12]) + class TestIndexIntersectionUnion: @pytest.fixture(scope="class") @@ -314,6 +336,17 @@ else: assert False + @skip_sidx_lt_210 + def test_intersection_v_interleaved( + self, index_a_interleaved: index.Index, index_b_interleaved: index.Index + ) -> None: + index_c_interleaved = index_a_interleaved & index_b_interleaved + mins = index_c_interleaved.bounds[0:2] + maxs = index_c_interleaved.bounds[2:4] + idxs, counts = index_c_interleaved.intersection_v(mins, maxs) + assert idxs.tolist() == [0, 1] + assert counts.tolist() == [2] + def test_intersection_uninterleaved( self, index_a_uninterleaved: index.Index, index_b_uninterleaved: index.Index ) -> None: @@ -330,6 +363,17 @@ else: assert False + @skip_sidx_lt_210 + def test_intersection_v_uninterleaved( + self, index_a_uninterleaved: index.Index, index_b_uninterleaved: index.Index + ) -> None: + index_c_uninterleaved = index_a_uninterleaved & index_b_uninterleaved + mins = index_c_uninterleaved.bounds[0::2] + maxs = index_c_uninterleaved.bounds[1::2] + idxs, counts = index_c_uninterleaved.intersection_v(mins, maxs) + assert idxs.tolist() == [0, 1] + assert counts.tolist() == [2] + def test_intersection_mismatch( self, index_a_interleaved: index.Index, index_b_uninterleaved: index.Index ) -> None: @@ -617,6 +661,46 @@ hits = sorted(idx.nearest((13, 0, 20, 2), 3)) self.assertEqual(hits, [3, 4, 5]) + @skip_sidx_lt_210 + def test_nearest_v_basic(self) -> None: + mins = np.array([[0, 5]] * 2).T + maxs = np.array([[10, 15]] * 2).T + ret = self.idx.nearest_v(mins, maxs, num_results=3) + assert type(ret) is tuple + ids, counts = ret + assert ids.dtype == np.int64 + ids0 = [76, 48, 19] + ids1 = [76, 47, 48] + assert ids.tolist() == ids0 + ids1 + assert counts.dtype == np.uint64 + assert counts.tolist() == [3, 3] + + ret = self.idx.nearest_v(mins, maxs, num_results=3, return_max_dists=True) + assert type(ret) is tuple + ids, counts, max_dists = ret + assert ids.tolist() == ids0 + ids1 + assert counts.tolist() == [3, 3] + assert max_dists.dtype == np.float64 + np.testing.assert_allclose(max_dists, [7.54938045, 11.05686397]) + + ret = self.idx.nearest_v( + mins, maxs, num_results=3, max_dists=[10, 10], return_max_dists=True + ) + ids, counts, max_dists = ret + assert ids.tolist() == ids0 + ids1[:2] + assert counts.tolist() == [3, 2] + np.testing.assert_allclose(max_dists, [7.54938045, 3.92672575]) + + # errors + with pytest.raises(ValueError, match="must have 2 dimensions"): + self.idx.nearest_v(np.ones((2, 3, 4)), 4) + with pytest.raises(ValueError, match="shapes not equal"): + self.idx.nearest_v([0], [10, 12]) + with pytest.raises(ValueError, match="max_dists must have 1 dimension"): + self.idx.nearest_v(maxs, mins, max_dists=[[10]]) + with pytest.raises(ValueError, match="max_dists must have length 2"): + self.idx.nearest_v(maxs, mins, max_dists=[10]) + def test_nearest_equidistant(self) -> None: """Test that if records are equidistant, both are returned.""" point = (0, 0) @@ -677,25 +761,47 @@ self.assertEqual(hits, []) -class IndexMoreDimensions(IndexTestCase): - def test_3d(self) -> None: - """Test we make and query a 3D index""" +class Index3d(IndexTestCase): + """Test we make and query a 3D index""" + + def setUp(self) -> None: p = index.Property() p.dimension = 3 - idx = index.Index(properties=p, interleaved=False) - idx.insert(1, (0, 0, 60, 60, 22, 22.0)) - hits = idx.intersection((-1, 1, 58, 62, 22, 24)) + self.idx = index.Index(properties=p, interleaved=False) + self.idx.insert(1, (0, 0, 60, 60, 22, 22.0)) + self.coords = (-1, 1, 58, 62, 22, 24) + + def test_intersection(self) -> None: + hits = self.idx.intersection(self.coords) self.assertEqual(list(hits), [1]) - def test_4d(self) -> None: - """Test we make and query a 4D index""" + @skip_sidx_lt_210 + def test_intersection_v(self) -> None: + idxs, counts = self.idx.intersection_v(self.coords[0::2], self.coords[1::2]) + assert idxs.tolist() == [1] + assert counts.tolist() == [1] + + +class Index4d(IndexTestCase): + """Test we make and query a 4D index""" + + def setUp(self) -> None: p = index.Property() p.dimension = 4 - idx = index.Index(properties=p, interleaved=False) - idx.insert(1, (0, 0, 60, 60, 22, 22.0, 128, 142)) - hits = idx.intersection((-1, 1, 58, 62, 22, 24, 120, 150)) + self.idx = index.Index(properties=p, interleaved=False) + self.idx.insert(1, (0, 0, 60, 60, 22, 22.0, 128, 142)) + self.coords = (-1, 1, 58, 62, 22, 24, 120, 150) + + def test_intersection(self) -> None: + hits = self.idx.intersection(self.coords) self.assertEqual(list(hits), [1]) + @skip_sidx_lt_210 + def test_intersection_v(self) -> None: + idxs, counts = self.idx.intersection_v(self.coords[0::2], self.coords[1::2]) + assert idxs.tolist() == [1] + assert counts.tolist() == [1] + class IndexStream(IndexTestCase): def test_stream_input(self) -> None:
