[Git][debian-gis-team/python-rtree][upstream] New upstream version 0.9.4

Bas Couwenberg gitlab at salsa.debian.org
Wed Feb 12 11:15:09 GMT 2020



Bas Couwenberg pushed to branch upstream at Debian GIS Project / python-rtree


Commits:
7b189e4d by Bas Couwenberg at 2020-02-12T05:51:20+01:00
New upstream version 0.9.4
- - - - -


12 changed files:

- .travis.yml
- README.md
- ci/azp/conda.yml
- ci/azp/docker.yml
- ci/azp/linux-1604-pip.yml
- ci/azp/linux-1804-pip.yml
- ci/azp/osx.yml
- ci/azp/win.yml
- rtree/__init__.py
- rtree/core.py
- rtree/index.py
- tests/test_index.py


Changes:

=====================================
.travis.yml
=====================================
@@ -19,7 +19,9 @@ addons:
       - libspatialindex-c3
 
 install:
+  - pip install flake8
   - pip install -e .
 
 script:
+  - flake8 --ignore=E501 --exclude=rtree/__init__.py rtree/
   - python -m pytest --doctest-modules rtree tests/test_*


=====================================
README.md
=====================================
@@ -2,6 +2,7 @@ Rtree
 =====
 
 [![Build Status](https://travis-ci.org/Toblerity/rtree.svg)](https://travis-ci.org/Toblerity/rtree)
+[![PyPI version](https://badge.fury.io/py/Rtree.svg)](https://badge.fury.io/py/Rtree)
 
 Python bindings for libspatialindex 1.8.3.
 


=====================================
ci/azp/conda.yml
=====================================
@@ -27,11 +27,12 @@ jobs:
 
   - bash: |
       source activate rtree
-      conda install --yes --quiet --name rtree python=$PYTHON_VERSION libspatialindex=$SIDX_VERSION
+      conda install --yes --quiet --name rtree -c conda-forge python=$PYTHON_VERSION libspatialindex=$SIDX_VERSION
     displayName: Install Anaconda packages
 
   - bash: |
       source activate rtree
-      pip install pytest numpy
+      pip install flake8 pytest numpy
+      flake8 --ignore=E501 --exclude=rtree/__init__.py rtree/
       python -m pytest --doctest-modules rtree tests/test_*
-    displayName: pytest
+    displayName: Lint with Flake8 and run unit tests


=====================================
ci/azp/docker.yml
=====================================
@@ -36,6 +36,7 @@ jobs:
       sudo update-locale LANG=en_US.UTF-8
       export LANG="en_US.UTF-8"
       export LC_ALL="en_US.UTF-8"
-      pip install pytest numpy
+      pip install flake8 pytest numpy
+      flake8 --ignore=E501 --exclude=rtree/__init__.py rtree/
       python -m pytest --doctest-modules rtree tests/test_*
-    displayName: 'Run pytest'
+    displayName: Lint with Flake8 and run unit tests


=====================================
ci/azp/linux-1604-pip.yml
=====================================
@@ -14,6 +14,7 @@ jobs:
     displayName: pip install
 
   - bash: |
-      pip3 install pytest numpy
+      pip3 install flake8 pytest numpy
+      flake8 --ignore=E501 --exclude=rtree/__init__.py rtree/
       python3 -m pytest --doctest-modules rtree tests/test_*
-    displayName: pytest
+    displayName: Lint with Flake8 and run unit tests


=====================================
ci/azp/linux-1804-pip.yml
=====================================
@@ -14,6 +14,7 @@ jobs:
     displayName: pip install
 
   - bash: |
-      pip3 install pytest numpy
+      pip3 install flake8 pytest numpy
+      flake8 --ignore=E501 --exclude=rtree/__init__.py rtree/
       python3 -m pytest --doctest-modules rtree tests/test_*
-    displayName: pytest
+    displayName: Lint with Flake8 and run unit tests


=====================================
ci/azp/osx.yml
=====================================
@@ -40,11 +40,12 @@ jobs:
 
   - bash: |
       source activate rtree
-      conda install --yes --quiet --name rtree python=$PYTHON_VERSION libspatialindex=$SIDX_VERSION
+      conda install --yes --quiet --name rtree -c conda-forge python=$PYTHON_VERSION libspatialindex=$SIDX_VERSION
     displayName: Install Anaconda packages
 
   - bash: |
       source activate rtree
-      pip install pytest numpy
+      pip install flake8 pytest numpy
+      flake8 --ignore=E501 --exclude=rtree/__init__.py rtree/
       python -m pytest --doctest-modules rtree tests/test_*
-    displayName: pytest
+    displayName: Lint with Flake8 and run unit tests


=====================================
ci/azp/win.yml
=====================================
@@ -29,11 +29,12 @@ jobs:
 
   - script: |
       call activate rtree
-      conda install --yes --quiet --name rtree python=%PYTHON_VERSION% libspatialindex=%SIDX_VERSION%
+      conda install --yes --quiet --name rtree -c conda-forge python=%PYTHON_VERSION% libspatialindex=%SIDX_VERSION%
     displayName: Install Anaconda packages
 
   - script: |
       call activate rtree
-      pip install pytest numpy
+      pip install flake8 pytest numpy
+      flake8 --ignore=E501 --exclude=rtree/__init__.py rtree/
       python -m pytest --doctest-modules rtree tests
-    displayName: pytest
+    displayName: Lint with Flake8 and run unit tests


=====================================
rtree/__init__.py
=====================================
@@ -2,4 +2,4 @@ from .index import Rtree
 
 from .core import rt
 
-__version__ = '0.9.3'
+__version__ = '0.9.4'


=====================================
rtree/core.py
=====================================
@@ -107,8 +107,6 @@ if os.name == 'nt':
                     os.environ['PATH'] = oldenv
         return None
 
-
-
     base_name = 'spatialindex_c'
     if '64' in platform.architecture()[0]:
         arch = '64'
@@ -328,23 +326,25 @@ try:
     rt.Index_Flush.restype = None
     rt.Index_Flush.errcheck = check_void_done
 
-    rt.Index_Contains_obj.argtypes = [ctypes.c_void_p,
-                                      ctypes.POINTER(ctypes.c_double),
-                                      ctypes.POINTER(ctypes.c_double),
-                                      ctypes.c_uint32,
-                                      ctypes.POINTER(
-                                          ctypes.POINTER(ctypes.c_void_p)),
-                                      ctypes.POINTER(ctypes.c_uint64)]
+    rt.Index_Contains_obj.argtypes = [
+        ctypes.c_void_p,
+        ctypes.POINTER(ctypes.c_double),
+        ctypes.POINTER(ctypes.c_double),
+        ctypes.c_uint32,
+        ctypes.POINTER(ctypes.POINTER(ctypes.c_void_p)),
+        ctypes.POINTER(ctypes.c_uint64)
+    ]
     rt.Index_Contains_obj.restype = ctypes.c_int
     rt.Index_Contains_obj.errcheck = check_return
 
-    rt.Index_Contains_id.argtypes = [ctypes.c_void_p,
-                                       ctypes.POINTER(ctypes.c_double),
-                                       ctypes.POINTER(ctypes.c_double),
-                                       ctypes.c_uint32,
-                                       ctypes.POINTER(
-                                           ctypes.POINTER(ctypes.c_int64)),
-                                       ctypes.POINTER(ctypes.c_uint64)]
+    rt.Index_Contains_id.argtypes = [
+        ctypes.c_void_p,
+        ctypes.POINTER(ctypes.c_double),
+        ctypes.POINTER(ctypes.c_double),
+        ctypes.c_uint32,
+        ctypes.POINTER(ctypes.POINTER(ctypes.c_int64)),
+        ctypes.POINTER(ctypes.c_uint64)
+    ]
     rt.Index_Contains_id.restype = ctypes.c_int
     rt.Index_Contains_id.errcheck = check_return
 


=====================================
rtree/index.py
=====================================
@@ -5,24 +5,8 @@ import pprint
 
 from . import core
 
-try:
-    import cPickle as pickle
-except ImportError:
-    import pickle
+import pickle
 
-import sys
-if sys.version_info[0] == 2:
-    range = xrange
-    string_types = basestring
-elif sys.version_info[0] == 3:
-    string_types = str
-
-
-def string_output(s):
-    if sys.version_info[0] == 2:
-        return s
-    elif sys.version_info[0] == 3:
-        return s.decode('UTF-8')
 
 RT_Memory = 0
 RT_Disk = 1
@@ -61,10 +45,12 @@ def _get_bounds(handle, bounds_fn, interleaved):
     if (dimension.value == 0):
         return None
 
-    mins = ctypes.cast(pp_mins, ctypes.POINTER(ctypes.c_double
-                                               * dimension.value))
-    maxs = ctypes.cast(pp_maxs, ctypes.POINTER(ctypes.c_double
-                                               * dimension.value))
+    mins = ctypes.cast(
+        pp_mins, ctypes.POINTER(ctypes.c_double * dimension.value)
+    )
+    maxs = ctypes.cast(
+        pp_maxs, ctypes.POINTER(ctypes.c_double * dimension.value)
+    )
 
     results = [mins.contents[i] for i in range(dimension.value)]
     results += [maxs.contents[i] for i in range(dimension.value)]
@@ -94,7 +80,7 @@ def _get_data(handle):
 class Index(object):
     """An R-Tree, MVR-Tree, or TPR-Tree indexing object"""
 
-    def __init__(self,  *args, **kwargs):
+    def __init__(self, *args, **kwargs):
         """Creates a new index
 
         :param filename:
@@ -161,7 +147,7 @@ class Index(object):
 
             >>> idx = index.Index(properties=p)
             >>> idx  # doctest: +ELLIPSIS
-            <rtree.index.Index object at 0x...>
+            rtree.index.Index(bounds=[1.7976931348623157e+308, 1.7976931348623157e+308, -1.7976931348623157e+308, -1.7976931348623157e+308], size=0)
 
         Insert an item into the index::
 
@@ -220,7 +206,7 @@ class Index(object):
         basename = None
         storage = None
         if args:
-            if isinstance(args[0], string_types) or isinstance(args[0], bytes):
+            if isinstance(args[0], str) or isinstance(args[0], bytes):
                 # they sent in a filename
                 basename = args[0]
                 # they sent in a filename, stream
@@ -283,7 +269,6 @@ class Index(object):
         else:
             self.properties.storage = RT_Memory
 
-
         ps = kwargs.get('pagesize', None)
         if ps:
             self.properties.pagesize = int(ps)
@@ -299,6 +284,15 @@ class Index(object):
                 for item in stream:
                     self.insert(*item)
 
+    def get_size(self):
+        try:
+            return self.count(self.bounds)
+        except core.RTreeError:
+            return 0
+
+    def __repr__(self):
+        return 'rtree.index.Index(bounds={}, size={})'.format(self.bounds, self.get_size())
+
     def __getstate__(self):
         state = self.__dict__.copy()
         del state["handle"]
@@ -386,7 +380,6 @@ class Index(object):
         # return serialized to keep it alive for the pointer.
         return size, ctypes.cast(p, ctypes.POINTER(ctypes.c_uint8)), serialized
 
-
     def set_result_limit(self, value):
         return core.rt.Index_SetResultSetOffset(self.handle, value)
 
@@ -463,7 +456,7 @@ class Index(object):
         p_mins, p_maxs = self.get_coordinate_pointers(coordinates)
         pv_mins, pv_maxs = self.get_coordinate_pointers(velocities)
         # End time isn't used
-        t_start, t_end = self._get_time_doubles((time, time+1))
+        t_start, t_end = self._get_time_doubles((time, time + 1))
         data = ctypes.c_ubyte(0)
         size = 0
         if obj is not None:
@@ -529,7 +522,6 @@ class Index(object):
 
         return p_num_results.value
 
-
     def _countTP(self, coordinates, velocities, times):
         p_mins, p_maxs = self.get_coordinate_pointers(coordinates)
         pv_mins, pv_maxs = self.get_coordinate_pointers(velocities)
@@ -777,7 +769,7 @@ class Index(object):
                         yield self.loads(data)
 
             core.rt.Index_DestroyObjResults(its, num_results)
-        except:  # need to catch all exceptions, not just rtree.
+        except Exception:  # need to catch all exceptions, not just rtree.
             core.rt.Index_DestroyObjResults(its, num_results)
             raise
 
@@ -790,7 +782,7 @@ class Index(object):
             for i in range(num_results):
                 yield items.contents[i]
             core.rt.Index_Free(its)
-        except:
+        except Exception:
             core.rt.Index_Free(its)
             raise
 
@@ -854,6 +846,12 @@ class Index(object):
             return self._nearest_obj(coordinates, num_results, objects)
         p_mins, p_maxs = self.get_coordinate_pointers(coordinates)
 
+        # p_num_results is an input and output for C++ lib
+        # as an input it says "get n closest neighbors"
+        # but if multiple neighbors are at the same distance, both will be returned
+        # so the number of returned neighbors may be > p_num_results
+        # thus p_num_results.contents.value gets set as an output by the C++ lib
+        #  to indicate the actual number of results for _get_ids to use
         p_num_results = ctypes.pointer(ctypes.c_uint64(num_results))
 
         it = ctypes.pointer(ctypes.c_int64())
@@ -865,10 +863,9 @@ class Index(object):
                                           ctypes.byref(it),
                                           p_num_results)
 
-        return self._get_ids(it, min(num_results,p_num_results.contents.value))
+        return self._get_ids(it, p_num_results.contents.value)
 
-    def _nearestTP(self, coordinates, velocities, times, num_results=1,
-                  objects=False):
+    def _nearestTP(self, coordinates, velocities, times, num_results=1, objects=False):
         p_mins, p_maxs = self.get_coordinate_pointers(coordinates)
         pv_mins, pv_maxs = self.get_coordinate_pointers(velocities)
         t_start, t_end = self._get_time_doubles(times)
@@ -907,13 +904,17 @@ class Index(object):
     bounds = property(get_bounds)
 
     def delete(self, id, coordinates):
-        """Deletes items from the index with the given ``'id'`` within the
-        specified coordinates.
+        """Deletes an item from the index with the given ``'id'`` and
+           coordinates given by the ``coordinates`` sequence. As the index can
+           contain multiple items with the same ID and coordinates, deletion
+           is not guaranteed to delete all items in the index with the given ID
+           and coordinates.
 
         :param id: long integer
-            A long integer that is the identifier for this index entry.  IDs
-            need not be unique to be inserted into the index, and it is up
-            to the user to ensure they are unique if this is a requirement.
+            A long integer ID for the entry, which need not be unique. The
+            index can contain multiple entries with identical IDs and
+            coordinates. Uniqueness of items should be enforced at the
+            application level by the user.
 
         :param coordinates: sequence or array
             Dimension * 2 coordinate pairs, representing the min
@@ -1045,8 +1046,8 @@ class Index(object):
             # this code assumes the coords are not interleaved.
             # xmin, xmax, ymin, ymax, zmin, zmax
             for i in range(dimension):
-                mins[i] = coordinates[i*2]
-                maxs[i] = coordinates[(i*2)+1]
+                mins[i] = coordinates[i * 2]
+                maxs[i] = coordinates[(i * 2) + 1]
 
             p_mins[0] = ctypes.cast(mins, ctypes.POINTER(ctypes.c_double))
             p_maxs[0] = ctypes.cast(maxs, ctypes.POINTER(ctypes.c_double))
@@ -1140,6 +1141,7 @@ class Index(object):
 
         return output
 
+
 # An alias to preserve backward compatibility
 Rtree = Index
 
@@ -1204,8 +1206,8 @@ class Handle(object):
         try:
 
             if self._ptr is not None:
-               self._destroy(self._ptr)
-               self._ptr = None
+                self._destroy(self._ptr)
+                self._ptr = None
         except AttributeError:
             pass
 
@@ -1235,10 +1237,11 @@ class IndexHandle(Handle):
         try:
             core.rt.Index_Flush
             if self._ptr is not None:
-               core.rt.Index_Flush(self._ptr)
+                core.rt.Index_Flush(self._ptr)
         except AttributeError:
             pass
 
+
 class IndexStreamHandle(IndexHandle):
 
     _create = core.rt.Index_CreateWithStream
@@ -1515,11 +1518,10 @@ class Property(object):
     """Reinsert factor"""
 
     def get_filename(self):
-        s = core.rt.IndexProperty_GetFileName(self.handle)
-        return string_output(s)
+        return core.rt.IndexProperty_GetFileName(self.handle).decode()
 
     def set_filename(self, value):
-        if isinstance(value, string_types):
+        if isinstance(value, str):
             value = value.encode('utf-8')
         return core.rt.IndexProperty_SetFileName(self.handle, value)
 
@@ -1527,11 +1529,10 @@ class Property(object):
     """Index filename for disk storage"""
 
     def get_dat_extension(self):
-        s = core.rt.IndexProperty_GetFileNameExtensionDat(self.handle)
-        return string_output(s)
+        return core.rt.IndexProperty_GetFileNameExtensionDat(self.handle).decode()
 
     def set_dat_extension(self, value):
-        if isinstance(value, string_types):
+        if isinstance(value, str):
             value = value.encode('utf-8')
         return core.rt.IndexProperty_SetFileNameExtensionDat(
             self.handle, value)
@@ -1540,11 +1541,10 @@ class Property(object):
     """Extension for .dat file"""
 
     def get_idx_extension(self):
-        s = core.rt.IndexProperty_GetFileNameExtensionIdx(self.handle)
-        return string_output(s)
+        return core.rt.IndexProperty_GetFileNameExtensionIdx(self.handle).decode()
 
     def set_idx_extension(self, value):
-        if isinstance(value, string_types):
+        if isinstance(value, str):
             value = value.encode('utf-8')
         return core.rt.IndexProperty_SetFileNameExtensionIdx(
             self.handle, value)
@@ -1836,7 +1836,7 @@ class RtreeContainer(Rtree):
 
             >>> idx = index.RtreeContainer(properties=p)
             >>> idx  # doctest: +ELLIPSIS
-            <rtree.index.RtreeContainer object at 0x...>
+            rtree.index.RtreeContainer(bounds=[1.7976931348623157e+308, 1.7976931348623157e+308, -1.7976931348623157e+308, -1.7976931348623157e+308], size=0)
 
         Insert an item into the index::
 
@@ -1854,7 +1854,7 @@ class RtreeContainer(Rtree):
             [34.37768294..., 26.73758537..., 49.37768294..., 41.73758537...]
         """
         if args:
-            if isinstance(args[0], string_types) \
+            if isinstance(args[0], str) \
                     or isinstance(args[0], bytes) \
                     or isinstance(args[0], ICustomStorage):
                 raise ValueError('%s supports only in-memory indexes'
@@ -1862,6 +1862,15 @@ class RtreeContainer(Rtree):
         self._objects = {}
         return super(RtreeContainer, self).__init__(*args, **kwargs)
 
+    def get_size(self):
+        try:
+            return self.count(self.bounds)
+        except core.RTreeError:
+            return 0
+
+    def __repr__(self):
+        return 'rtree.index.RtreeContainer(bounds={}, size={})'.format(self.bounds, self.get_size())
+
     def __contains__(self, obj):
         return id(obj) in self._objects
 
@@ -1974,11 +1983,11 @@ class RtreeContainer(Rtree):
             49.3776829412, 41.7375853734])]
 
         """
-        if bbox == False:
+        if bbox is False:
             for id in super(RtreeContainer,
                             self).intersection(coordinates, bbox):
                 yield self._objects[id][1]
-        elif bbox == True:
+        elif bbox is True:
             for value in super(RtreeContainer,
                                self).intersection(coordinates, bbox):
                 value.object = self._objects[value.id][1]
@@ -1988,7 +1997,7 @@ class RtreeContainer(Rtree):
             raise ValueError(
                 "valid values for the bbox argument are True and False")
 
-    def nearest(self, coordinates, num_results = 1, bbox=False):
+    def nearest(self, coordinates, num_results=1, bbox=False):
         """Returns the ``k``-nearest objects to the given coordinates
         in increasing distance order.
 
@@ -2022,11 +2031,11 @@ class RtreeContainer(Rtree):
             >>> idx.insert(object(), (34.37, 26.73, 49.37, 41.73))
             >>> hits = idx.nearest((0, 0, 10, 10), 3, bbox=True)
         """
-        if bbox == False:
+        if bbox is False:
             for id in super(RtreeContainer,
                             self).nearest(coordinates, num_results, bbox):
                 yield self._objects[id][1]
-        elif bbox == True:
+        elif bbox is True:
             for value in super(RtreeContainer,
                                self).nearest(coordinates, num_results, bbox):
                 value.object = self._objects[value.id][1]


=====================================
tests/test_index.py
=====================================
@@ -340,6 +340,38 @@ class IndexNearest(IndexTestCase):
             idx.add(i, (start, 1, stop, 1))
         hits = sorted(idx.nearest((13, 0, 20, 2), 3))
         self.assertEqual(hits, [3, 4, 5])
+    
+    def test_nearest_equidistant(self):
+        """Test that if records are equidistant, both are returned."""
+        point = (0, 0)
+        small_box = (-10, -10, 10, 10)
+        large_box = (-50, -50, 50, 50)
+
+        idx = index.Index()
+        idx.insert(0, small_box)
+        idx.insert(1, large_box)
+        self.assertEqual(list(idx.nearest(point, 2)), [0, 1])
+        self.assertEqual(list(idx.nearest(point, 1)), [0, 1])
+
+        idx.insert(2, (0, 0))
+        self.assertEqual(list(idx.nearest(point, 2)), [0, 1, 2])
+        self.assertEqual(list(idx.nearest(point, 1)), [0, 1, 2])
+
+        idx = index.Index()
+        idx.insert(0, small_box)
+        idx.insert(1, large_box)
+        idx.insert(2, (50, 50)) # point on top right vertex of large_box
+        point = (51, 51) # right outside of large_box
+        self.assertEqual(list(idx.nearest(point, 2)), [1, 2])
+        self.assertEqual(list(idx.nearest(point, 1)), [1, 2])
+
+        idx = index.Index()
+        idx.insert(0, small_box)
+        idx.insert(1, large_box)
+        idx.insert(2, (51, 51)) # point right outside on top right vertex of large_box
+        point = (51, 52) # shifted 1 unit up from the point above
+        self.assertEqual(list(idx.nearest(point, 2)), [2, 1])
+        self.assertEqual(list(idx.nearest(point, 1)), [2])
 
 
     def test_nearest_object(self):



View it on GitLab: https://salsa.debian.org/debian-gis-team/python-rtree/commit/7b189e4d9ec405d470a00defad5ab2b8ba7f8941

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/python-rtree/commit/7b189e4d9ec405d470a00defad5ab2b8ba7f8941
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20200212/7258db2c/attachment-0001.html>


More information about the Pkg-grass-devel mailing list