diff -Nru python-rtree-0.9.7/ci/install_libspatialindex.bash python-rtree-1.0.0/ci/install_libspatialindex.bash --- python-rtree-0.9.7/ci/install_libspatialindex.bash 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/ci/install_libspatialindex.bash 2022-04-04 21:43:52.000000000 +0000 @@ -34,7 +34,7 @@ curl -L -O https://github.com/libspatialindex/libspatialindex/archive/$VERSION.zip # check the file hash -echo "${SHA256} ${VERSION}.zip" | sha256sum --check +echo "${SHA256} ${VERSION}.zip" | sha256sum -c - rm -rf "libspatialindex-${VERSION}" || true unzip $VERSION @@ -45,7 +45,7 @@ cp "${SL}/CMakeLists.txt" .. -cmake -DCMAKE_BUILD_TYPE=Release .. +cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="${CIBW_ARCHS_MACOS/ /;}" .. make -j 4 # copy built libraries relative to path of this script diff -Nru python-rtree-0.9.7/CREDITS.txt python-rtree-1.0.0/CREDITS.txt --- python-rtree-0.9.7/CREDITS.txt 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/CREDITS.txt 2022-04-04 21:43:52.000000000 +0000 @@ -13,7 +13,7 @@ * .count() and .intersection() methods * Windows support * Node fetching -* Index property access +* Index property access Brent Pedersen @@ -26,3 +26,8 @@ * Custom storage API (both Rtree and libspatialindex) + +Adam Stewart + +* intersection/union support +* __len__ method diff -Nru python-rtree-0.9.7/debian/changelog python-rtree-1.0.0/debian/changelog --- python-rtree-0.9.7/debian/changelog 2020-12-25 05:49:44.000000000 +0000 +++ python-rtree-1.0.0/debian/changelog 2022-04-05 03:32:40.000000000 +0000 @@ -1,3 +1,13 @@ +python-rtree (1.0.0-1) unstable; urgency=medium + + * New upstream release. + * Update watch file for GitHub URL changes. + * Bump Standards-Version to 4.6.0, no changes. + * Bump debhelper compat to 12, no changes. + * Require at least libspatialindex-dev 1.8.5. + + -- Bas Couwenberg Tue, 05 Apr 2022 05:32:40 +0200 + python-rtree (0.9.7-1) unstable; urgency=medium * New upstream release. diff -Nru python-rtree-0.9.7/debian/compat python-rtree-1.0.0/debian/compat --- python-rtree-0.9.7/debian/compat 2020-03-19 19:33:54.000000000 +0000 +++ python-rtree-1.0.0/debian/compat 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -10 diff -Nru python-rtree-0.9.7/debian/control python-rtree-1.0.0/debian/control --- python-rtree-0.9.7/debian/control 2020-12-18 15:16:11.000000000 +0000 +++ python-rtree-1.0.0/debian/control 2022-04-05 03:31:00.000000000 +0000 @@ -3,16 +3,16 @@ Uploaders: Bas Couwenberg Section: python Priority: optional -Build-Depends: debhelper (>= 10~), +Build-Depends: debhelper-compat (= 12), dh-python, - libspatialindex-dev (>= 1.7.0), + libspatialindex-dev (>= 1.8.5), python3-all, python3-numpy, python3-pytest, python3-setuptools, python3-sphinx, python3-wheel -Standards-Version: 4.5.1 +Standards-Version: 4.6.0 Vcs-Browser: https://salsa.debian.org/debian-gis-team/python-rtree Vcs-Git: https://salsa.debian.org/debian-gis-team/python-rtree.git Homepage: https://github.com/Toblerity/rtree diff -Nru python-rtree-0.9.7/debian/watch python-rtree-1.0.0/debian/watch --- python-rtree-0.9.7/debian/watch 2020-11-06 18:53:55.000000000 +0000 +++ python-rtree-1.0.0/debian/watch 2021-10-23 17:27:42.000000000 +0000 @@ -4,5 +4,5 @@ uversionmangle=s/(\d)[_\.\-\+]?((RC|rc|pre|dev|beta|alpha|b|a)[\-\.]?\d*)$/$1~$2/,\ filenamemangle=s/(?:.*?)?(?:rel|v|rtree)?[\-\_]?(\d[\d\.]*)\.(tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz)))/rtree-$1.$2/,\ repacksuffix=+ds \ -https://github.com/Toblerity/rtree/releases \ -(?:.*?/archive/)*(?:rel|v|rtree|)[\-\_]?(\d[\d\-\.]+)\.(?:tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz))) +https://github.com/Toblerity/rtree/tags \ +(?:.*?/archive/(?:.*?/)?)?(?:rel|v|rtree|)[\-\_]?(\d[\d\-\.]+)\.(?:tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz))) diff -Nru python-rtree-0.9.7/DEPENDENCIES.txt python-rtree-1.0.0/DEPENDENCIES.txt --- python-rtree-0.9.7/DEPENDENCIES.txt 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/DEPENDENCIES.txt 2022-04-04 21:43:52.000000000 +0000 @@ -1,4 +1,5 @@ +- python 3.7+ - setuptools -- libspatialindex C library 1.7.0+: - http://libspatialindex.github.com +- libspatialindex C library 1.8.5+: + https://libspatialindex.org/ diff -Nru python-rtree-0.9.7/docs/requirements.txt python-rtree-1.0.0/docs/requirements.txt --- python-rtree-0.9.7/docs/requirements.txt 1970-01-01 00:00:00.000000000 +0000 +++ python-rtree-1.0.0/docs/requirements.txt 2022-04-04 21:43:52.000000000 +0000 @@ -0,0 +1 @@ +sphinx>=4 diff -Nru python-rtree-0.9.7/docs/source/changes.txt python-rtree-1.0.0/docs/source/changes.txt --- python-rtree-0.9.7/docs/source/changes.txt 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/docs/source/changes.txt 2022-04-04 21:43:52.000000000 +0000 @@ -4,19 +4,19 @@ .............................................................................. 0.9.3: 2019-12-10 -=============== +================= - find_library and libspatialindex library loading https://github.com/Toblerity/rtree/pull/131 0.9.2: 2019-12-09 -=============== +================= - Refactored tests to be based on unittest https://github.com/Toblerity/rtree/pull/129 - Update libspatialindex library loading code to adapt previous behavior https://github.com/Toblerity/rtree/pull/128 - Empty data streams throw exceptions and do not partially construct indexes https://github.com/Toblerity/rtree/pull/127 0.9.0: 2019-11-24 -=============== +================= - Add Index.GetResultSetOffset() - Add Index.contains() method for object and id (requires libspatialindex 1.9.3+) #116 diff -Nru python-rtree-0.9.7/docs/source/class.txt python-rtree-1.0.0/docs/source/class.txt --- python-rtree-0.9.7/docs/source/class.txt 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/docs/source/class.txt 2022-04-04 21:43:52.000000000 +0000 @@ -4,10 +4,10 @@ ------------------------------------------------------------------------------ .. autoclass:: rtree.index.Index - :members: __init__, insert, intersection, nearest, delete, bounds, count, close, dumps, loads, interleaved + :members: __init__, insert, intersection, nearest, delete, bounds, count, close, dumps, loads .. autoclass:: rtree.index.Property :members: .. autoclass:: rtree.index.Item - :members: __init__, bbox, object \ No newline at end of file + :members: __init__, bbox, object diff -Nru python-rtree-0.9.7/docs/source/conf.py python-rtree-1.0.0/docs/source/conf.py --- python-rtree-0.9.7/docs/source/conf.py 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/docs/source/conf.py 2022-04-04 21:43:52.000000000 +0000 @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Rtree documentation build configuration file, created by # sphinx-quickstart on Tue Aug 18 13:21:07 2009. @@ -11,11 +10,12 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import rtree import sys -import os -sys.path.append('../../') +from typing import List +sys.path.append("../../") + +import rtree # noqa: E402 # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -24,31 +24,35 @@ # -- General configuration ----------------------------------------------- +# If your documentation needs a minimal Sphinx version, state it here. +needs_sphinx = "4.0" + # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.intersphinx', - 'sphinx.ext.todo', - 'sphinx.ext.coverage', - 'sphinx.ext.ifconfig'] + "sphinx.ext.autodoc", + "sphinx.ext.doctest", + "sphinx.ext.intersphinx", + "sphinx.ext.todo", + "sphinx.ext.coverage", + "sphinx.ext.ifconfig", +] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.txt' +source_suffix = ".txt" # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'Rtree' -copyright = u'2019, Sean Gilles, Howard Butler, and contributors.' +project = "Rtree" +copyright = "2019, Sean Gilles, Howard Butler, and contributors." # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -61,164 +65,165 @@ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. -#unused_docs = [] +# unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. -exclude_trees = [] +exclude_trees: List[str] = [] # The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. -html_theme = 'nature' +html_theme = "nature" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path: List[str] = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_use_modindex = True +# html_use_modindex = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' +# html_file_suffix = '' # Output file base name for HTML help builder. -htmlhelp_basename = 'Rtreedoc' +htmlhelp_basename = "Rtreedoc" # -- Options for LaTeX output -------------------------------------------- # The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' +# latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' +# latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'Rtree.tex', u'Rtree Documentation', - u'Sean Gilles', 'manual'), + ("index", "Rtree.tex", "Rtree Documentation", "Sean Gilles", "manual") ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # Additional stuff for the LaTeX preamble. -#latex_preamble = '' +# latex_preamble = '' # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_use_modindex = True +# latex_use_modindex = True -pdf_documents = [ - ('index', u'Rtree', u'Rtree Documentation', u'The Rtree Team'), -] +pdf_documents = [("index", "Rtree", "Rtree Documentation", "The Rtree Team")] # A comma-separated list of custom stylesheets. Example: pdf_language = "en_US" pdf_fit_mode = "overflow" # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'http://docs.python.org/': None} +intersphinx_mapping = {"http://docs.python.org/": None} -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'http://docs.python.org/': None} + +# -- Extension configuration ------------------------------------------------- + +# sphinx.ext.autodoc +autodoc_typehints = "description" +autodoc_typehints_description_target = "documented" diff -Nru python-rtree-0.9.7/docs/source/examples.txt python-rtree-1.0.0/docs/source/examples.txt --- python-rtree-0.9.7/docs/source/examples.txt 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/docs/source/examples.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -.. _examples: - - -.. include:: ../../tests/index.txt - \ No newline at end of file diff -Nru python-rtree-0.9.7/docs/source/history.txt python-rtree-1.0.0/docs/source/history.txt --- python-rtree-0.9.7/docs/source/history.txt 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/docs/source/history.txt 2022-04-04 21:43:52.000000000 +0000 @@ -43,5 +43,5 @@ .. _`ZODB`: http://www.zodb.org/ .. _`R-trees`: http://en.wikipedia.org/wiki/R-tree .. _`ctypes`: http://docs.python.org/library/ctypes.html -.. _`libspatialindex`: http://libspatialindex.github.com +.. _`libspatialindex`: https://libspatialindex.org/ .. _`Rtree`: http://rtree.github.com \ No newline at end of file diff -Nru python-rtree-0.9.7/docs/source/index.txt python-rtree-1.0.0/docs/source/index.txt --- python-rtree-0.9.7/docs/source/index.txt 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/docs/source/index.txt 2022-04-04 21:43:52.000000000 +0000 @@ -27,7 +27,6 @@ class changes performance - examples history * :ref:`genindex` @@ -36,5 +35,5 @@ .. _`R-trees`: http://en.wikipedia.org/wiki/R-tree .. _`ctypes`: http://docs.python.org/library/ctypes.html -.. _`libspatialindex`: http://libspatialindex.github.com +.. _`libspatialindex`: https://libspatialindex.org/ .. _`Rtree`: http://toblerity.github.com/rtree/ diff -Nru python-rtree-0.9.7/docs/source/install.txt python-rtree-1.0.0/docs/source/install.txt --- python-rtree-0.9.7/docs/source/install.txt 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/docs/source/install.txt 2022-04-04 21:43:52.000000000 +0000 @@ -6,9 +6,9 @@ \*nix .............................................................................. -First, download and install version 1.7.0 of the `libspatialindex`_ library from: +First, download and install version 1.8.5+ of the `libspatialindex`_ library from: -http://libspatialindex.github.com +https://libspatialindex.org/ The library is a GNU-style build, so it is a matter of:: @@ -42,5 +42,5 @@ .. _`PyPI`: http://pypi.python.org/pypi/Rtree/ .. _`Rtree`: http://pypi.python.org/pypi/Rtree/ -.. _`libspatialindex`: http://libspatialindex.github.com +.. _`libspatialindex`: https://libspatialindex.org/ diff -Nru python-rtree-0.9.7/docs/source/README.txt python-rtree-1.0.0/docs/source/README.txt --- python-rtree-0.9.7/docs/source/README.txt 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/docs/source/README.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -Rtree: Spatial indexing for Python ------------------------------------------------------------------------------- - -`Rtree`_ is a `ctypes`_ Python wrapper of `libspatialindex`_ that provides a -number of advanced spatial indexing features for the spatially curious Python -user. These features include: - -* Nearest neighbor search -* Intersection search -* Multi-dimensional indexes -* Clustered indexes (store Python pickles directly with index entries) -* Bulk loading -* Deletion -* Disk serialization -* Custom storage implementation (to implement spatial indexing in ZODB, for example) - -Documentation and Website -.............................................................................. - -https://rtree.readthedocs.io/en/latest/ - -Requirements -.............................................................................. - -* `libspatialindex`_ 1.8.5+. - -Download -.............................................................................. - -* PyPI http://pypi.python.org/pypi/Rtree/ -* Windows binaries http://www.lfd.uci.edu/~gohlke/pythonlibs/#rtree - -Development -.............................................................................. - -* https://github.com/Toblerity/Rtree - -.. _`R-trees`: http://en.wikipedia.org/wiki/R-tree -.. _`ctypes`: http://docs.python.org/library/ctypes.html -.. _`libspatialindex`: http://libspatialindex.github.com -.. _`Rtree`: http://toblerity.github.com/rtree/ diff -Nru python-rtree-0.9.7/docs/source/tutorial.txt python-rtree-1.0.0/docs/source/tutorial.txt --- python-rtree-0.9.7/docs/source/tutorial.txt 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/docs/source/tutorial.txt 2022-04-04 21:43:52.000000000 +0000 @@ -121,7 +121,7 @@ a clustered index in `libspatialindex`_ parlance). The following inserts the picklable object ``42`` into the index with the given id:: - >>> idx.insert(id=id, bounds=(left, bottom, right, top), obj=42) + >>> idx.insert(id=id, coordinates=(left, bottom, right, top), obj=42) You can then return a list of objects by giving the ``objects=True`` flag to intersection:: @@ -193,8 +193,8 @@ >>> p.dat_extension = 'data' >>> p.idx_extension = 'index' >>> idx3d = index.Index('3d_index',properties=p) - >>> idx3d.insert(1, (0, 0, 60, 60, 23.0, 42.0)) - >>> idx3d.intersection( (-1, -1, 62, 62, 22, 43)) + >>> idx3d.insert(1, (0, 60, 23.0, 0, 60, 42.0)) + >>> idx3d.intersection( (-1, 62, 22, -1, 62, 43)) [1L] ZODB and Custom Storages @@ -205,4 +205,4 @@ .. _ZODB: http://www.zodb.org/ -.. _`libspatialindex`: http://libspatialindex.github.com +.. _`libspatialindex`: https://libspatialindex.org/ diff -Nru python-rtree-0.9.7/environment.yml python-rtree-1.0.0/environment.yml --- python-rtree-0.9.7/environment.yml 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/environment.yml 2022-04-04 21:43:52.000000000 +0000 @@ -3,5 +3,5 @@ - defaults - conda-forge dependencies: -- python>=3.5 -- libspatialindex +- python>=3.7 +- libspatialindex>=1.8.5 diff -Nru python-rtree-0.9.7/.github/workflows/build.yml python-rtree-1.0.0/.github/workflows/build.yml --- python-rtree-0.9.7/.github/workflows/build.yml 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/.github/workflows/build.yml 2022-04-04 21:43:52.000000000 +0000 @@ -7,6 +7,58 @@ types: - published jobs: + black: + name: black + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: '3.10' + - name: Setup + run: pip install black + - name: Lint with black + run: black --check --diff . + + flake8: + name: flake8 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: '3.10' + - name: Setup + run: pip install flake8 + - name: Lint with flake8 + run: flake8 + + isort: + name: isort + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: '3.10' + - name: Setup + run: pip install isort[colors] + - name: Lint with isort + run: isort --check --diff . + + mypy: + name: mypy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: '3.10' + - name: Setup + run: pip install mypy pytest + - name: Lint with mypy + run: mypy . + conda: name: Conda ${{ matrix.python-version }} - ${{ matrix.os }} @@ -15,7 +67,7 @@ fail-fast: true matrix: os: ['ubuntu-latest', 'macos-latest', 'windows-latest'] - python-version: ['3.8'] + python-version: ['3.7', '3.8', '3.9', '3.10'] sidx-version: ['1.8.5','1.9.3'] steps: @@ -34,11 +86,6 @@ shell: bash -l {0} run: | pip install -e . - - name: Lint with flake8 - shell: bash -l {0} - run: | - pip install flake8 - flake8 rtree/ - name: Test with pytest shell: bash -l {0} run: | @@ -48,69 +95,34 @@ ubuntu: name: Ubuntu ${{ matrix.os }} - runs-on: ubuntu-16.04 + runs-on: ubuntu-18.04 strategy: fail-fast: true matrix: - os: ['ubuntu-16.04', 'ubuntu-18.04'] + os: ['ubuntu-18.04', 'ubuntu-20.04'] steps: - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + name: Install Python + with: + python-version: '3.10' - name: Setup shell: bash -l {0} run: | sudo apt install libspatialindex-c4v5 python3-pip python3 -m pip install --upgrade pip - python3 -m pip install setuptools numpy flake8 pytest + python3 -m pip install setuptools numpy pytest - name: Build shell: bash -l {0} run: | python3 -m pip install --user . - - name: Lint with flake8 - shell: bash -l {0} - run: | - export PATH=$PATH:/home/runner/.local/bin - flake8 rtree/ - name: Test with pytest shell: bash -l {0} run: | python3 -m pytest --doctest-modules rtree tests - docs: - name: Docs - runs-on: ubuntu-latest - strategy: - fail-fast: true - container: osgeo/proj-docs - steps: - - uses: actions/checkout@v2 - - name: Run libspatialindex build - run: | - apt-get update -y - apt-get install -y -qq libspatialindex-dev - pip3 install --user . - - name: Print versions - shell: bash -l {0} - run: | - python3 --version - sphinx-build --version - - name: Lint .rst files - shell: bash -l {0} - run: | - if find . -name '*.rst' | xargs grep -P '\t'; then echo 'Tabs are bad, please use four spaces in .rst files.'; false; fi - working-directory: ./docs - - name: HTML - shell: bash -l {0} - run: | - make html - working-directory: ./docs - - name: PDF - shell: bash -l {0} - run: | - make latexpdf - working-directory: ./docs - wheels: name: Build wheel on ${{ matrix.os }} runs-on: ${{ matrix.os }} @@ -120,6 +132,10 @@ CIBW_TEST_COMMAND: "pytest -v {project}/tests" # we are copying the shared libraries ourselves so skip magical copy CIBW_REPAIR_WHEEL_COMMAND_MACOS: "" + MACOSX_DEPLOYMENT_TARGET: 10.9 + CIBW_BUILD_VERBOSITY_MACOS: 3 + CIBW_TEST_SKIP: "*-macosx_arm64 *-macosx_universal2:arm64" + CIBW_ARCHS_MACOS: "x86_64 arm64" CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "" CIBW_BEFORE_BUILD_LINUX: "pip install cmake; bash {project}/ci/install_libspatialindex.bash" strategy: @@ -127,13 +143,13 @@ os: [windows-latest, ubuntu-latest, macos-latest] steps: - uses: actions/checkout@v1 - - uses: actions/setup-python@v1 + - uses: actions/setup-python@v2 name: Install Python with: - python-version: '3.7' + python-version: '3.10' - name: Install cibuildwheel run: | - python -m pip install cibuildwheel==1.6.4 + python -m pip install cibuildwheel==2.3.1 - name: Run MacOS Preinstall Build if: startsWith(matrix.os, 'macos') run: | @@ -150,12 +166,44 @@ ci\install_libspatialindex.bat - name: Build wheels run: | - python -m cibuildwheel --output-dir wheelhouse + python3 -m cibuildwheel --output-dir wheelhouse - uses: actions/upload-artifact@v2 with: name: ${{ matrix.os }}-whl path: wheelhouse/*.whl + wheels_aarch64: + name: Build wheel on aarch64 for ${{ matrix.python_tag }} + runs-on: ubuntu-latest + strategy: + matrix: + python_tag: [ "cp37-*", "cp38-*", "cp39-*", "cp310-*"] + env: + CIBW_ARCHS_LINUX: aarch64 + CIBW_BUILD: ${{matrix.python_tag}} + CIBW_TEST_REQUIRES: pytest numpy + CIBW_TEST_COMMAND: "pytest -v {project}/tests" + CIBW_BEFORE_BUILD_LINUX: "pip install cmake; bash {project}/ci/install_libspatialindex.bash" + + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-python@v2 + name: Install Python + with: + python-version: '3.10' + - name: Install cibuildwheel + run: | + python -m pip install cibuildwheel==2.3.1 + - uses: docker/setup-qemu-action@v1 + name: Set up QEMU + - name: Build wheels + run: | + python -m cibuildwheel --output-dir wheelhouse + - uses: actions/upload-artifact@v2 + with: + name: aarch64-whl + path: wheelhouse/*.whl + collect-artifacts: name: Package and push release @@ -168,12 +216,16 @@ steps: - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + name: Install Python + with: + python-version: '3.10' - name: Source shell: bash -l {0} run: | sudo apt install libspatialindex-c4v5 python3-pip python3 -m pip install --upgrade pip - python3 -m pip install setuptools numpy flake8 pytest wheel + python3 -m pip install setuptools numpy pytest wheel export PATH=$PATH:/home/runner/.local/bin python3 setup.py sdist diff -Nru python-rtree-0.9.7/.gitignore python-rtree-1.0.0/.gitignore --- python-rtree-0.9.7/.gitignore 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/.gitignore 2022-04-04 21:43:52.000000000 +0000 @@ -7,3 +7,4 @@ *.dat include lib +.coverage diff -Nru python-rtree-0.9.7/pyproject.toml python-rtree-1.0.0/pyproject.toml --- python-rtree-0.9.7/pyproject.toml 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/pyproject.toml 2022-04-04 21:43:52.000000000 +0000 @@ -1,3 +1,26 @@ [build-system] -requires = [ "wheel", "setuptools" ] +requires = ["wheel", "setuptools>=39.2"] build-backend = "setuptools.build_meta" + +[tool.black] +target-version = ['py37', 'py38', 'py39'] +color = true +skip_magic_trailing_comma = true + +[tool.coverage.report] +# Ignore warnings for overloads +# https://github.com/nedbat/coveragepy/issues/970#issuecomment-612602180 +exclude_lines = [ + "pragma: no cover", + "@overload", +] + +[tool.isort] +profile = "black" +known_first_party = ["rtree"] +skip_gitignore = true +color_output = true + +[tool.mypy] +ignore_missing_imports = true +show_error_codes = true diff -Nru python-rtree-0.9.7/README.md python-rtree-1.0.0/README.md --- python-rtree-0.9.7/README.md 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/README.md 2022-04-04 21:43:52.000000000 +0000 @@ -1,11 +1,42 @@ -Rtree -===== +# Rtree: Spatial indexing for Python ![Build](https://github.com/Toblerity/rtree/workflows/Build/badge.svg) [![PyPI version](https://badge.fury.io/py/Rtree.svg)](https://badge.fury.io/py/Rtree) -RTree is a Python package with bindings for [libspatialindex](https://github.com/libspatialindex/libspatialindex). Wheels are available for most major platforms, and `rtree` with bundled `libspatialindex` can be installed via pip: + +Rtree`_ is a [ctypes](http://docs.python.org/library/ctypes.html) Python wrapper of [libspatialindex](https://libspatialindex.org/) that provides a +number of advanced spatial indexing features for the spatially curious Python +user. These features include: + +* Nearest neighbor search +* Intersection search +* Multi-dimensional indexes +* Clustered indexes (store Python pickles directly with index entries) +* Bulk loading +* Deletion +* Disk serialization +* Custom storage implementation (to implement spatial indexing in ZODB, for example) + + +Wheels are available for most major platforms, and `rtree` with bundled `libspatialindex` can be installed via pip: + ``` pip install rtree ``` +# Changes + +## 1.0.0 + + +* Python 3.7+ is now required (#212) (thanks @adamjstewart!) +* Type hints (#215 and others) (thanks @adamjstewart!) +* Python 3.10 wheels, including osx-arm64 #224 +* Clean up libspatialindex C API mismatches #222 (thanks @musicinmybrain!) +* Many doc updates, fixes, and type hints (thanks @adamjstewart!) #212 #221 #217 #215 +* __len__ method for index #194 +* Prevent get_coordinate_pointers from mutating inputs #205 (thanks @sjones94549!) +* linux-aarch64 wheels #183 (thanks @odidev!) +* black (#218) and flake8 (#145) linting + +https://github.com/Toblerity/rtree/releases/1.0.0 diff -Nru python-rtree-0.9.7/.readthedocs.yaml python-rtree-1.0.0/.readthedocs.yaml --- python-rtree-0.9.7/.readthedocs.yaml 1970-01-01 00:00:00.000000000 +0000 +++ python-rtree-1.0.0/.readthedocs.yaml 2022-04-04 21:43:52.000000000 +0000 @@ -0,0 +1,28 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the version of Python and other tools you might need +build: + apt_packages: + - libspatialindex-dev + os: ubuntu-20.04 + tools: + python: "3.10" + +# Build documentation in the docs/source directory with Sphinx +sphinx: + configuration: docs/source/conf.py + fail_on_warning: true + +# Optionally build your docs in additional formats such as PDF +formats: + - pdf + +# Declare the Python requirements required to build your docs +python: + install: + - requirements: docs/requirements.txt diff -Nru python-rtree-0.9.7/readthedocs.yml python-rtree-1.0.0/readthedocs.yml --- python-rtree-0.9.7/readthedocs.yml 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/readthedocs.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -python: - version: 3 - pip_install: true -conda: - file: environment.yml diff -Nru python-rtree-0.9.7/rtree/core.py python-rtree-1.0.0/rtree/core.py --- python-rtree-0.9.7/rtree/core.py 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/rtree/core.py 2022-04-04 21:43:52.000000000 +0000 @@ -8,8 +8,7 @@ "Error checking for Error calls" if result != 0: s = rt.Error_GetLastErrorMsg().decode() - msg = 'LASError in "%s": %s' % \ - (func.__name__, s) + msg = f'Error in "{func.__name__}": {s}' rt.Error_Reset() raise RTreeError(msg) return True @@ -19,7 +18,7 @@ "Error checking for void* returns" if not bool(result): s = rt.Error_GetLastErrorMsg().decode() - msg = 'Error in "%s": %s' % (func.__name__, s) + msg = f'Error in "{func.__name__}": {s}' rt.Error_Reset() raise RTreeError(msg) return result @@ -29,7 +28,7 @@ "Error checking for void* returns that might be empty with no error" if rt.Error_GetErrorCount(): s = rt.Error_GetLastErrorMsg().decode() - msg = 'Error in "%s": %s' % (func.__name__, s) + msg = f'Error in "{func.__name__}": {s}' rt.Error_Reset() raise RTreeError(msg) return result @@ -40,7 +39,7 @@ count = rt.Error_GetErrorCount() if count != 0: s = rt.Error_GetLastErrorMsg().decode() - msg = 'Error in "%s": %s' % (func.__name__, s) + msg = f'Error in "{func.__name__}": {s}' rt.Error_Reset() raise RTreeError(msg) return result @@ -51,7 +50,7 @@ count = rt.Error_GetErrorCount() if count != 0: s = rt.Error_GetLastErrorMsg().decode() - msg = 'Error in "%s": %s' % (func.__name__, s) + msg = f'Error in "{func.__name__}": {s}' rt.Error_Reset() raise RTreeError(msg) return result @@ -74,14 +73,20 @@ # load the shared library by looking in likely places rt = finder.load() +rt.SIDX_Version.argtypes = [] +rt.SIDX_Version.restype = ctypes.POINTER(ctypes.c_char) +rt.SIDX_Version.errcheck = free_returned_char_p # type: ignore + +rt.Error_GetLastErrorNum.argtypes = [] rt.Error_GetLastErrorNum.restype = ctypes.c_int rt.Error_GetLastErrorMsg.argtypes = [] rt.Error_GetLastErrorMsg.restype = ctypes.POINTER(ctypes.c_char) -rt.Error_GetLastErrorMsg.errcheck = free_error_msg_ptr +rt.Error_GetLastErrorMsg.errcheck = free_error_msg_ptr # type: ignore +rt.Error_GetLastErrorMethod.argtypes = [] rt.Error_GetLastErrorMethod.restype = ctypes.POINTER(ctypes.c_char) -rt.Error_GetLastErrorMethod.errcheck = free_returned_char_p +rt.Error_GetLastErrorMethod.errcheck = free_returned_char_p # type: ignore rt.Error_GetErrorCount.argtypes = [] rt.Error_GetErrorCount.restype = ctypes.c_int @@ -91,174 +96,201 @@ rt.Index_Create.argtypes = [ctypes.c_void_p] rt.Index_Create.restype = ctypes.c_void_p -rt.Index_Create.errcheck = check_void +rt.Index_Create.errcheck = check_void # type: ignore -NEXTFUNC = ctypes.CFUNCTYPE(ctypes.c_int, - ctypes.POINTER(ctypes.c_int64), - ctypes.POINTER(ctypes.POINTER(ctypes.c_double)), - ctypes.POINTER(ctypes.POINTER(ctypes.c_double)), - ctypes.POINTER(ctypes.c_uint32), - ctypes.POINTER(ctypes.POINTER(ctypes.c_ubyte)), - ctypes.POINTER(ctypes.c_uint32)) +_nDataLength_size_t = True +try: + _major, _minor, _patch = ( + int(part) for part in rt.SIDX_Version().decode("ascii").split(".") + ) +except (ValueError, UnicodeDecodeError): + pass # weird version; assume latest ABI +else: + if (_major, _minor, _patch) < (1, 9, 0): + # Headers had size_t*, but implementation had uint32_t* + _nDataLength_size_t = False +NEXTFUNC = ctypes.CFUNCTYPE( + ctypes.c_int, + ctypes.POINTER(ctypes.c_int64), + ctypes.POINTER(ctypes.POINTER(ctypes.c_double)), + ctypes.POINTER(ctypes.POINTER(ctypes.c_double)), + ctypes.POINTER(ctypes.c_uint32), + ctypes.POINTER(ctypes.POINTER(ctypes.c_ubyte)), + ctypes.POINTER(ctypes.c_size_t if _nDataLength_size_t else ctypes.c_uint32), +) rt.Index_CreateWithStream.argtypes = [ctypes.c_void_p, NEXTFUNC] rt.Index_CreateWithStream.restype = ctypes.c_void_p -rt.Index_CreateWithStream.errcheck = check_void +rt.Index_CreateWithStream.errcheck = check_void # type: ignore rt.Index_Destroy.argtypes = [ctypes.c_void_p] rt.Index_Destroy.restype = None -rt.Index_Destroy.errcheck = check_void_done +rt.Index_Destroy.errcheck = check_void_done # type: ignore rt.Index_GetProperties.argtypes = [ctypes.c_void_p] rt.Index_GetProperties.restype = ctypes.c_void_p -rt.Index_GetProperties.errcheck = check_void +rt.Index_GetProperties.errcheck = check_void # type: ignore -rt.Index_DeleteData.argtypes = [ctypes.c_void_p, - ctypes.c_int64, - ctypes.POINTER(ctypes.c_double), - ctypes.POINTER(ctypes.c_double), - ctypes.c_uint32] +rt.Index_DeleteData.argtypes = [ + ctypes.c_void_p, + ctypes.c_int64, + ctypes.POINTER(ctypes.c_double), + ctypes.POINTER(ctypes.c_double), + ctypes.c_uint32, +] rt.Index_DeleteData.restype = ctypes.c_int -rt.Index_DeleteData.errcheck = check_return +rt.Index_DeleteData.errcheck = check_return # type: ignore -rt.Index_InsertData.argtypes = [ctypes.c_void_p, - ctypes.c_int64, - ctypes.POINTER(ctypes.c_double), - ctypes.POINTER(ctypes.c_double), - ctypes.c_uint32, - ctypes.POINTER(ctypes.c_ubyte), - ctypes.c_uint32] +rt.Index_InsertData.argtypes = [ + ctypes.c_void_p, + ctypes.c_int64, + ctypes.POINTER(ctypes.c_double), + ctypes.POINTER(ctypes.c_double), + ctypes.c_uint32, + ctypes.POINTER(ctypes.c_ubyte), + ctypes.c_uint32, +] rt.Index_InsertData.restype = ctypes.c_int -rt.Index_InsertData.errcheck = check_return +rt.Index_InsertData.errcheck = check_return # type: ignore -rt.Index_GetBounds.argtypes = [ctypes.c_void_p, - ctypes.POINTER(ctypes.POINTER(ctypes.c_double)), - ctypes.POINTER(ctypes.POINTER(ctypes.c_double)), - ctypes.POINTER(ctypes.c_uint32)] +rt.Index_GetBounds.argtypes = [ + ctypes.c_void_p, + ctypes.POINTER(ctypes.POINTER(ctypes.c_double)), + ctypes.POINTER(ctypes.POINTER(ctypes.c_double)), + ctypes.POINTER(ctypes.c_uint32), +] rt.Index_GetBounds.restype = ctypes.c_int -rt.Index_GetBounds.errcheck = check_value +rt.Index_GetBounds.errcheck = check_value # type: ignore rt.Index_IsValid.argtypes = [ctypes.c_void_p] rt.Index_IsValid.restype = ctypes.c_int -rt.Index_IsValid.errcheck = check_value +rt.Index_IsValid.errcheck = check_value # type: ignore -rt.Index_Intersects_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_Intersects_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_Intersects_obj.restype = ctypes.c_int -rt.Index_Intersects_obj.errcheck = check_return +rt.Index_Intersects_obj.errcheck = check_return # type: ignore -rt.Index_Intersects_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_Intersects_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_Intersects_id.restype = ctypes.c_int -rt.Index_Intersects_id.errcheck = check_return +rt.Index_Intersects_id.errcheck = check_return # type: ignore -rt.Index_Intersects_count.argtypes = [ctypes.c_void_p, - ctypes.POINTER(ctypes.c_double), - ctypes.POINTER(ctypes.c_double), - ctypes.c_uint32, - ctypes.POINTER(ctypes.c_uint64)] - -rt.Index_NearestNeighbors_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_Intersects_count.argtypes = [ + ctypes.c_void_p, + ctypes.POINTER(ctypes.c_double), + ctypes.POINTER(ctypes.c_double), + ctypes.c_uint32, + ctypes.POINTER(ctypes.c_uint64), +] + +rt.Index_NearestNeighbors_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_NearestNeighbors_obj.restype = ctypes.c_int -rt.Index_NearestNeighbors_obj.errcheck = check_return +rt.Index_NearestNeighbors_obj.errcheck = check_return # type: ignore -rt.Index_NearestNeighbors_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_NearestNeighbors_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_NearestNeighbors_id.restype = ctypes.c_int -rt.Index_NearestNeighbors_id.errcheck = check_return +rt.Index_NearestNeighbors_id.errcheck = check_return # type: ignore -rt.Index_GetLeaves.argtypes = [ctypes.c_void_p, - ctypes.POINTER(ctypes.c_uint32), - ctypes.POINTER(ctypes.POINTER(ctypes.c_uint32)), - ctypes.POINTER(ctypes.POINTER(ctypes.c_int64)), - ctypes.POINTER(ctypes.POINTER( - ctypes.POINTER(ctypes.c_int64))), - ctypes.POINTER(ctypes.POINTER( - ctypes.POINTER(ctypes.c_double))), - ctypes.POINTER(ctypes.POINTER( - ctypes.POINTER(ctypes.c_double))), - ctypes.POINTER(ctypes.c_uint32)] +rt.Index_GetLeaves.argtypes = [ + ctypes.c_void_p, + ctypes.POINTER(ctypes.c_uint32), + ctypes.POINTER(ctypes.POINTER(ctypes.c_uint32)), + ctypes.POINTER(ctypes.POINTER(ctypes.c_int64)), + ctypes.POINTER(ctypes.POINTER(ctypes.POINTER(ctypes.c_int64))), + ctypes.POINTER(ctypes.POINTER(ctypes.POINTER(ctypes.c_double))), + ctypes.POINTER(ctypes.POINTER(ctypes.POINTER(ctypes.c_double))), + ctypes.POINTER(ctypes.c_uint32), +] rt.Index_GetLeaves.restype = ctypes.c_int -rt.Index_GetLeaves.errcheck = check_return +rt.Index_GetLeaves.errcheck = check_return # type: ignore -rt.Index_DestroyObjResults.argtypes = \ - [ctypes.POINTER(ctypes.POINTER(ctypes.c_void_p)), ctypes.c_uint32] +rt.Index_DestroyObjResults.argtypes = [ + ctypes.POINTER(ctypes.POINTER(ctypes.c_void_p)), + ctypes.c_uint32, +] rt.Index_DestroyObjResults.restype = None -rt.Index_DestroyObjResults.errcheck = check_void_done +rt.Index_DestroyObjResults.errcheck = check_void_done # type: ignore rt.Index_ClearBuffer.argtypes = [ctypes.c_void_p] rt.Index_ClearBuffer.restype = None -rt.Index_ClearBuffer.errcheck = check_void_done +rt.Index_ClearBuffer.errcheck = check_void_done # type: ignore rt.Index_Free.argtypes = [ctypes.POINTER(ctypes.c_void_p)] rt.Index_Free.restype = None rt.IndexItem_Destroy.argtypes = [ctypes.c_void_p] rt.IndexItem_Destroy.restype = None -rt.IndexItem_Destroy.errcheck = check_void_done +rt.IndexItem_Destroy.errcheck = check_void_done # type: ignore -rt.IndexItem_GetData.argtypes = [ctypes.c_void_p, - ctypes.POINTER( - ctypes.POINTER(ctypes.c_ubyte)), - ctypes.POINTER(ctypes.c_uint64)] +rt.IndexItem_GetData.argtypes = [ + ctypes.c_void_p, + ctypes.POINTER(ctypes.POINTER(ctypes.c_ubyte)), + ctypes.POINTER(ctypes.c_uint64), +] rt.IndexItem_GetData.restype = ctypes.c_int -rt.IndexItem_GetData.errcheck = check_value +rt.IndexItem_GetData.errcheck = check_value # type: ignore -rt.IndexItem_GetBounds.argtypes = [ctypes.c_void_p, - ctypes.POINTER( - ctypes.POINTER(ctypes.c_double)), - ctypes.POINTER( - ctypes.POINTER(ctypes.c_double)), - ctypes.POINTER(ctypes.c_uint32)] +rt.IndexItem_GetBounds.argtypes = [ + ctypes.c_void_p, + ctypes.POINTER(ctypes.POINTER(ctypes.c_double)), + ctypes.POINTER(ctypes.POINTER(ctypes.c_double)), + ctypes.POINTER(ctypes.c_uint32), +] rt.IndexItem_GetBounds.restype = ctypes.c_int -rt.IndexItem_GetBounds.errcheck = check_value +rt.IndexItem_GetBounds.errcheck = check_value # type: ignore rt.IndexItem_GetID.argtypes = [ctypes.c_void_p] rt.IndexItem_GetID.restype = ctypes.c_int64 -rt.IndexItem_GetID.errcheck = check_value +rt.IndexItem_GetID.errcheck = check_value # type: ignore try: rt.Index_GetResultSetOffset.argtypes = [ctypes.c_void_p] rt.Index_GetResultSetOffset.restype = ctypes.c_int64 - rt.Index_GetResultSetOffset.errcheck = check_value + rt.Index_GetResultSetOffset.errcheck = check_value # type: ignore rt.Index_SetResultSetOffset.argtypes = [ctypes.c_void_p, ctypes.c_int64] rt.Index_SetResultSetOffset.restype = ctypes.c_int - rt.Index_SetResultSetOffset.errcheck = check_return + rt.Index_SetResultSetOffset.errcheck = check_return # type: ignore rt.Index_GetResultSetLimit.argtypes = [ctypes.c_void_p] rt.Index_GetResultSetLimit.restype = ctypes.c_int64 - rt.Index_GetResultSetLimit.errcheck = check_value + rt.Index_GetResultSetLimit.errcheck = check_value # type: ignore rt.Index_SetResultSetLimit.argtypes = [ctypes.c_void_p, ctypes.c_int64] rt.Index_SetResultSetLimit.restype = ctypes.c_int - rt.Index_SetResultSetLimit.errcheck = check_return + rt.Index_SetResultSetLimit.errcheck = check_return # type: ignore rt.Index_Flush.argtypes = [ctypes.c_void_p] rt.Index_Flush.restype = None - rt.Index_Flush.errcheck = check_void_done + rt.Index_Flush.errcheck = check_void_done # type: ignore rt.Index_Contains_obj.argtypes = [ ctypes.c_void_p, @@ -266,10 +298,10 @@ ctypes.POINTER(ctypes.c_double), ctypes.c_uint32, ctypes.POINTER(ctypes.POINTER(ctypes.c_void_p)), - ctypes.POINTER(ctypes.c_uint64) + ctypes.POINTER(ctypes.c_uint64), ] rt.Index_Contains_obj.restype = ctypes.c_int - rt.Index_Contains_obj.errcheck = check_return + rt.Index_Contains_obj.errcheck = check_return # type: ignore rt.Index_Contains_id.argtypes = [ ctypes.c_void_p, @@ -277,321 +309,321 @@ ctypes.POINTER(ctypes.c_double), ctypes.c_uint32, ctypes.POINTER(ctypes.POINTER(ctypes.c_int64)), - ctypes.POINTER(ctypes.c_uint64) + ctypes.POINTER(ctypes.c_uint64), ] rt.Index_Contains_id.restype = ctypes.c_int - rt.Index_Contains_id.errcheck = check_return + rt.Index_Contains_id.errcheck = check_return # type: ignore except AttributeError: pass rt.IndexProperty_Create.argtypes = [] rt.IndexProperty_Create.restype = ctypes.c_void_p -rt.IndexProperty_Create.errcheck = check_void +rt.IndexProperty_Create.errcheck = check_void # type: ignore rt.IndexProperty_Destroy.argtypes = [ctypes.c_void_p] rt.IndexProperty_Destroy.restype = None -rt.IndexProperty_Destroy.errcheck = check_void_done +rt.IndexProperty_Destroy.errcheck = check_void_done # type: ignore -rt.IndexProperty_SetIndexType.argtypes = [ctypes.c_void_p, ctypes.c_int32] +rt.IndexProperty_SetIndexType.argtypes = [ctypes.c_void_p, ctypes.c_int] rt.IndexProperty_SetIndexType.restype = ctypes.c_int -rt.IndexProperty_SetIndexType.errcheck = check_return +rt.IndexProperty_SetIndexType.errcheck = check_return # type: ignore rt.IndexProperty_GetIndexType.argtypes = [ctypes.c_void_p] rt.IndexProperty_GetIndexType.restype = ctypes.c_int -rt.IndexProperty_GetIndexType.errcheck = check_value +rt.IndexProperty_GetIndexType.errcheck = check_value # type: ignore rt.IndexProperty_SetDimension.argtypes = [ctypes.c_void_p, ctypes.c_uint32] rt.IndexProperty_SetDimension.restype = ctypes.c_int -rt.IndexProperty_SetDimension.errcheck = check_return +rt.IndexProperty_SetDimension.errcheck = check_return # type: ignore rt.IndexProperty_GetDimension.argtypes = [ctypes.c_void_p] -rt.IndexProperty_GetDimension.restype = ctypes.c_int -rt.IndexProperty_GetDimension.errcheck = check_value +rt.IndexProperty_GetDimension.restype = ctypes.c_uint32 +rt.IndexProperty_GetDimension.errcheck = check_value # type: ignore -rt.IndexProperty_SetIndexVariant.argtypes = [ctypes.c_void_p, ctypes.c_uint32] +rt.IndexProperty_SetIndexVariant.argtypes = [ctypes.c_void_p, ctypes.c_int] rt.IndexProperty_SetIndexVariant.restype = ctypes.c_int -rt.IndexProperty_SetIndexVariant.errcheck = check_return +rt.IndexProperty_SetIndexVariant.errcheck = check_return # type: ignore rt.IndexProperty_GetIndexVariant.argtypes = [ctypes.c_void_p] rt.IndexProperty_GetIndexVariant.restype = ctypes.c_int -rt.IndexProperty_GetIndexVariant.errcheck = check_value +rt.IndexProperty_GetIndexVariant.errcheck = check_value # type: ignore -rt.IndexProperty_SetIndexStorage.argtypes = [ctypes.c_void_p, ctypes.c_uint32] +rt.IndexProperty_SetIndexStorage.argtypes = [ctypes.c_void_p, ctypes.c_int] rt.IndexProperty_SetIndexStorage.restype = ctypes.c_int -rt.IndexProperty_SetIndexStorage.errcheck = check_return +rt.IndexProperty_SetIndexStorage.errcheck = check_return # type: ignore rt.IndexProperty_GetIndexStorage.argtypes = [ctypes.c_void_p] rt.IndexProperty_GetIndexStorage.restype = ctypes.c_int -rt.IndexProperty_GetIndexStorage.errcheck = check_value +rt.IndexProperty_GetIndexStorage.errcheck = check_value # type: ignore rt.IndexProperty_SetIndexCapacity.argtypes = [ctypes.c_void_p, ctypes.c_uint32] rt.IndexProperty_SetIndexCapacity.restype = ctypes.c_int -rt.IndexProperty_SetIndexCapacity.errcheck = check_return +rt.IndexProperty_SetIndexCapacity.errcheck = check_return # type: ignore rt.IndexProperty_GetIndexCapacity.argtypes = [ctypes.c_void_p] -rt.IndexProperty_GetIndexCapacity.restype = ctypes.c_int -rt.IndexProperty_GetIndexCapacity.errcheck = check_value +rt.IndexProperty_GetIndexCapacity.restype = ctypes.c_uint32 +rt.IndexProperty_GetIndexCapacity.errcheck = check_value # type: ignore rt.IndexProperty_SetLeafCapacity.argtypes = [ctypes.c_void_p, ctypes.c_uint32] rt.IndexProperty_SetLeafCapacity.restype = ctypes.c_int -rt.IndexProperty_SetLeafCapacity.errcheck = check_return +rt.IndexProperty_SetLeafCapacity.errcheck = check_return # type: ignore rt.IndexProperty_GetLeafCapacity.argtypes = [ctypes.c_void_p] -rt.IndexProperty_GetLeafCapacity.restype = ctypes.c_int -rt.IndexProperty_GetLeafCapacity.errcheck = check_value +rt.IndexProperty_GetLeafCapacity.restype = ctypes.c_uint32 +rt.IndexProperty_GetLeafCapacity.errcheck = check_value # type: ignore rt.IndexProperty_SetPagesize.argtypes = [ctypes.c_void_p, ctypes.c_uint32] rt.IndexProperty_SetPagesize.restype = ctypes.c_int -rt.IndexProperty_SetPagesize.errcheck = check_return +rt.IndexProperty_SetPagesize.errcheck = check_return # type: ignore rt.IndexProperty_GetPagesize.argtypes = [ctypes.c_void_p] -rt.IndexProperty_GetPagesize.restype = ctypes.c_int -rt.IndexProperty_GetPagesize.errcheck = check_value +rt.IndexProperty_GetPagesize.restype = ctypes.c_uint32 +rt.IndexProperty_GetPagesize.errcheck = check_value # type: ignore -rt.IndexProperty_SetLeafPoolCapacity.argtypes = \ - [ctypes.c_void_p, ctypes.c_uint32] +rt.IndexProperty_SetLeafPoolCapacity.argtypes = [ctypes.c_void_p, ctypes.c_uint32] rt.IndexProperty_SetLeafPoolCapacity.restype = ctypes.c_int -rt.IndexProperty_SetLeafPoolCapacity.errcheck = check_return +rt.IndexProperty_SetLeafPoolCapacity.errcheck = check_return # type: ignore rt.IndexProperty_GetLeafPoolCapacity.argtypes = [ctypes.c_void_p] -rt.IndexProperty_GetLeafPoolCapacity.restype = ctypes.c_int -rt.IndexProperty_GetLeafPoolCapacity.errcheck = check_value +rt.IndexProperty_GetLeafPoolCapacity.restype = ctypes.c_uint32 +rt.IndexProperty_GetLeafPoolCapacity.errcheck = check_value # type: ignore -rt.IndexProperty_SetIndexPoolCapacity.argtypes = \ - [ctypes.c_void_p, ctypes.c_uint32] +rt.IndexProperty_SetIndexPoolCapacity.argtypes = [ctypes.c_void_p, ctypes.c_uint32] rt.IndexProperty_SetIndexPoolCapacity.restype = ctypes.c_int -rt.IndexProperty_SetIndexPoolCapacity.errcheck = check_return +rt.IndexProperty_SetIndexPoolCapacity.errcheck = check_return # type: ignore rt.IndexProperty_GetIndexPoolCapacity.argtypes = [ctypes.c_void_p] -rt.IndexProperty_GetIndexPoolCapacity.restype = ctypes.c_int -rt.IndexProperty_GetIndexPoolCapacity.errcheck = check_value +rt.IndexProperty_GetIndexPoolCapacity.restype = ctypes.c_uint32 +rt.IndexProperty_GetIndexPoolCapacity.errcheck = check_value # type: ignore -rt.IndexProperty_SetRegionPoolCapacity.argtypes = \ - [ctypes.c_void_p, ctypes.c_uint32] +rt.IndexProperty_SetRegionPoolCapacity.argtypes = [ctypes.c_void_p, ctypes.c_uint32] rt.IndexProperty_SetRegionPoolCapacity.restype = ctypes.c_int -rt.IndexProperty_SetRegionPoolCapacity.errcheck = check_return +rt.IndexProperty_SetRegionPoolCapacity.errcheck = check_return # type: ignore rt.IndexProperty_GetRegionPoolCapacity.argtypes = [ctypes.c_void_p] -rt.IndexProperty_GetRegionPoolCapacity.restype = ctypes.c_int -rt.IndexProperty_GetRegionPoolCapacity.errcheck = check_value +rt.IndexProperty_GetRegionPoolCapacity.restype = ctypes.c_uint32 +rt.IndexProperty_GetRegionPoolCapacity.errcheck = check_value # type: ignore -rt.IndexProperty_SetPointPoolCapacity.argtypes = \ - [ctypes.c_void_p, ctypes.c_uint32] +rt.IndexProperty_SetPointPoolCapacity.argtypes = [ctypes.c_void_p, ctypes.c_uint32] rt.IndexProperty_SetPointPoolCapacity.restype = ctypes.c_int -rt.IndexProperty_SetPointPoolCapacity.errcheck = check_return +rt.IndexProperty_SetPointPoolCapacity.errcheck = check_return # type: ignore rt.IndexProperty_GetPointPoolCapacity.argtypes = [ctypes.c_void_p] -rt.IndexProperty_GetPointPoolCapacity.restype = ctypes.c_int -rt.IndexProperty_GetPointPoolCapacity.errcheck = check_value +rt.IndexProperty_GetPointPoolCapacity.restype = ctypes.c_uint32 +rt.IndexProperty_GetPointPoolCapacity.errcheck = check_value # type: ignore -rt.IndexProperty_SetBufferingCapacity.argtypes = \ - [ctypes.c_void_p, ctypes.c_uint32] +rt.IndexProperty_SetBufferingCapacity.argtypes = [ctypes.c_void_p, ctypes.c_uint32] rt.IndexProperty_SetBufferingCapacity.restype = ctypes.c_int -rt.IndexProperty_SetBufferingCapacity.errcheck = check_return +rt.IndexProperty_SetBufferingCapacity.errcheck = check_return # type: ignore rt.IndexProperty_GetBufferingCapacity.argtypes = [ctypes.c_void_p] -rt.IndexProperty_GetBufferingCapacity.restype = ctypes.c_int -rt.IndexProperty_GetBufferingCapacity.errcheck = check_value +rt.IndexProperty_GetBufferingCapacity.restype = ctypes.c_uint32 +rt.IndexProperty_GetBufferingCapacity.errcheck = check_value # type: ignore -rt.IndexProperty_SetEnsureTightMBRs.argtypes = \ - [ctypes.c_void_p, ctypes.c_uint32] +rt.IndexProperty_SetEnsureTightMBRs.argtypes = [ctypes.c_void_p, ctypes.c_uint32] rt.IndexProperty_SetEnsureTightMBRs.restype = ctypes.c_int -rt.IndexProperty_SetEnsureTightMBRs.errcheck = check_return +rt.IndexProperty_SetEnsureTightMBRs.errcheck = check_return # type: ignore rt.IndexProperty_GetEnsureTightMBRs.argtypes = [ctypes.c_void_p] -rt.IndexProperty_GetEnsureTightMBRs.restype = ctypes.c_int -rt.IndexProperty_GetEnsureTightMBRs.errcheck = check_value +rt.IndexProperty_GetEnsureTightMBRs.restype = ctypes.c_uint32 +rt.IndexProperty_GetEnsureTightMBRs.errcheck = check_value # type: ignore rt.IndexProperty_SetOverwrite.argtypes = [ctypes.c_void_p, ctypes.c_uint32] rt.IndexProperty_SetOverwrite.restype = ctypes.c_int -rt.IndexProperty_SetOverwrite.errcheck = check_return +rt.IndexProperty_SetOverwrite.errcheck = check_return # type: ignore rt.IndexProperty_GetOverwrite.argtypes = [ctypes.c_void_p] -rt.IndexProperty_GetOverwrite.restype = ctypes.c_int -rt.IndexProperty_GetOverwrite.errcheck = check_value +rt.IndexProperty_GetOverwrite.restype = ctypes.c_uint32 +rt.IndexProperty_GetOverwrite.errcheck = check_value # type: ignore -rt.IndexProperty_SetNearMinimumOverlapFactor.argtypes = \ - [ctypes.c_void_p, ctypes.c_uint32] +rt.IndexProperty_SetNearMinimumOverlapFactor.argtypes = [ + ctypes.c_void_p, + ctypes.c_uint32, +] rt.IndexProperty_SetNearMinimumOverlapFactor.restype = ctypes.c_int -rt.IndexProperty_SetNearMinimumOverlapFactor.errcheck = check_return +rt.IndexProperty_SetNearMinimumOverlapFactor.errcheck = check_return # type: ignore rt.IndexProperty_GetNearMinimumOverlapFactor.argtypes = [ctypes.c_void_p] -rt.IndexProperty_GetNearMinimumOverlapFactor.restype = ctypes.c_int -rt.IndexProperty_GetNearMinimumOverlapFactor.errcheck = check_value +rt.IndexProperty_GetNearMinimumOverlapFactor.restype = ctypes.c_uint32 +rt.IndexProperty_GetNearMinimumOverlapFactor.errcheck = check_value # type: ignore rt.IndexProperty_SetWriteThrough.argtypes = [ctypes.c_void_p, ctypes.c_uint32] rt.IndexProperty_SetWriteThrough.restype = ctypes.c_int -rt.IndexProperty_SetWriteThrough.errcheck = check_return +rt.IndexProperty_SetWriteThrough.errcheck = check_return # type: ignore rt.IndexProperty_GetWriteThrough.argtypes = [ctypes.c_void_p] -rt.IndexProperty_GetWriteThrough.restype = ctypes.c_int -rt.IndexProperty_GetWriteThrough.errcheck = check_value +rt.IndexProperty_GetWriteThrough.restype = ctypes.c_uint32 +rt.IndexProperty_GetWriteThrough.errcheck = check_value # type: ignore rt.IndexProperty_SetFillFactor.argtypes = [ctypes.c_void_p, ctypes.c_double] rt.IndexProperty_SetFillFactor.restype = ctypes.c_int -rt.IndexProperty_SetFillFactor.errcheck = check_return +rt.IndexProperty_SetFillFactor.errcheck = check_return # type: ignore rt.IndexProperty_GetFillFactor.argtypes = [ctypes.c_void_p] rt.IndexProperty_GetFillFactor.restype = ctypes.c_double -rt.IndexProperty_GetFillFactor.errcheck = check_value +rt.IndexProperty_GetFillFactor.errcheck = check_value # type: ignore -rt.IndexProperty_SetSplitDistributionFactor.argtypes = \ - [ctypes.c_void_p, ctypes.c_double] +rt.IndexProperty_SetSplitDistributionFactor.argtypes = [ + ctypes.c_void_p, + ctypes.c_double, +] rt.IndexProperty_SetSplitDistributionFactor.restype = ctypes.c_int -rt.IndexProperty_SetSplitDistributionFactor.errcheck = check_return +rt.IndexProperty_SetSplitDistributionFactor.errcheck = check_return # type: ignore rt.IndexProperty_GetSplitDistributionFactor.argtypes = [ctypes.c_void_p] rt.IndexProperty_GetSplitDistributionFactor.restype = ctypes.c_double -rt.IndexProperty_GetSplitDistributionFactor.errcheck = check_value +rt.IndexProperty_GetSplitDistributionFactor.errcheck = check_value # type: ignore rt.IndexProperty_SetTPRHorizon.argtypes = [ctypes.c_void_p, ctypes.c_double] rt.IndexProperty_SetTPRHorizon.restype = ctypes.c_int -rt.IndexProperty_SetTPRHorizon.errcheck = check_return +rt.IndexProperty_SetTPRHorizon.errcheck = check_return # type: ignore rt.IndexProperty_GetTPRHorizon.argtypes = [ctypes.c_void_p] rt.IndexProperty_GetTPRHorizon.restype = ctypes.c_double -rt.IndexProperty_GetTPRHorizon.errcheck = check_value +rt.IndexProperty_GetTPRHorizon.errcheck = check_value # type: ignore -rt.IndexProperty_SetReinsertFactor.argtypes = \ - [ctypes.c_void_p, ctypes.c_double] +rt.IndexProperty_SetReinsertFactor.argtypes = [ctypes.c_void_p, ctypes.c_double] rt.IndexProperty_SetReinsertFactor.restype = ctypes.c_int -rt.IndexProperty_SetReinsertFactor.errcheck = check_return +rt.IndexProperty_SetReinsertFactor.errcheck = check_return # type: ignore rt.IndexProperty_GetReinsertFactor.argtypes = [ctypes.c_void_p] rt.IndexProperty_GetReinsertFactor.restype = ctypes.c_double -rt.IndexProperty_GetReinsertFactor.errcheck = check_value +rt.IndexProperty_GetReinsertFactor.errcheck = check_value # type: ignore rt.IndexProperty_SetFileName.argtypes = [ctypes.c_void_p, ctypes.c_char_p] rt.IndexProperty_SetFileName.restype = ctypes.c_int -rt.IndexProperty_SetFileName.errcheck = check_return +rt.IndexProperty_SetFileName.errcheck = check_return # type: ignore rt.IndexProperty_GetFileName.argtypes = [ctypes.c_void_p] -rt.IndexProperty_GetFileName.errcheck = free_returned_char_p +rt.IndexProperty_GetFileName.errcheck = free_returned_char_p # type: ignore rt.IndexProperty_GetFileName.restype = ctypes.POINTER(ctypes.c_char) -rt.IndexProperty_SetFileNameExtensionDat.argtypes = \ - [ctypes.c_void_p, ctypes.c_char_p] +rt.IndexProperty_SetFileNameExtensionDat.argtypes = [ctypes.c_void_p, ctypes.c_char_p] rt.IndexProperty_SetFileNameExtensionDat.restype = ctypes.c_int -rt.IndexProperty_SetFileNameExtensionDat.errcheck = check_return +rt.IndexProperty_SetFileNameExtensionDat.errcheck = check_return # type: ignore rt.IndexProperty_GetFileNameExtensionDat.argtypes = [ctypes.c_void_p] -rt.IndexProperty_GetFileNameExtensionDat.errcheck = free_returned_char_p -rt.IndexProperty_GetFileNameExtensionDat.restype = \ - ctypes.POINTER(ctypes.c_char) +rt.IndexProperty_GetFileNameExtensionDat.errcheck = free_returned_char_p # type: ignore +rt.IndexProperty_GetFileNameExtensionDat.restype = ctypes.POINTER(ctypes.c_char) -rt.IndexProperty_SetFileNameExtensionIdx.argtypes = \ - [ctypes.c_void_p, ctypes.c_char_p] +rt.IndexProperty_SetFileNameExtensionIdx.argtypes = [ctypes.c_void_p, ctypes.c_char_p] rt.IndexProperty_SetFileNameExtensionIdx.restype = ctypes.c_int -rt.IndexProperty_SetFileNameExtensionIdx.errcheck = check_return +rt.IndexProperty_SetFileNameExtensionIdx.errcheck = check_return # type: ignore rt.IndexProperty_GetFileNameExtensionIdx.argtypes = [ctypes.c_void_p] -rt.IndexProperty_GetFileNameExtensionIdx.errcheck = free_returned_char_p -rt.IndexProperty_GetFileNameExtensionIdx.restype = \ - ctypes.POINTER(ctypes.c_char) +rt.IndexProperty_GetFileNameExtensionIdx.errcheck = free_returned_char_p # type: ignore +rt.IndexProperty_GetFileNameExtensionIdx.restype = ctypes.POINTER(ctypes.c_char) -rt.IndexProperty_SetCustomStorageCallbacksSize.argtypes = \ - [ctypes.c_void_p, ctypes.c_uint32] +rt.IndexProperty_SetCustomStorageCallbacksSize.argtypes = [ + ctypes.c_void_p, + ctypes.c_uint32, +] rt.IndexProperty_SetCustomStorageCallbacksSize.restype = ctypes.c_int -rt.IndexProperty_SetCustomStorageCallbacksSize.errcheck = check_return +rt.IndexProperty_SetCustomStorageCallbacksSize.errcheck = check_return # type: ignore rt.IndexProperty_GetCustomStorageCallbacksSize.argtypes = [ctypes.c_void_p] rt.IndexProperty_GetCustomStorageCallbacksSize.restype = ctypes.c_uint32 -rt.IndexProperty_GetCustomStorageCallbacksSize.errcheck = check_value +rt.IndexProperty_GetCustomStorageCallbacksSize.errcheck = check_value # type: ignore -rt.IndexProperty_SetCustomStorageCallbacks.argtypes = \ - [ctypes.c_void_p, ctypes.c_void_p] +rt.IndexProperty_SetCustomStorageCallbacks.argtypes = [ctypes.c_void_p, ctypes.c_void_p] rt.IndexProperty_SetCustomStorageCallbacks.restype = ctypes.c_int -rt.IndexProperty_SetCustomStorageCallbacks.errcheck = check_return +rt.IndexProperty_SetCustomStorageCallbacks.errcheck = check_return # type: ignore rt.IndexProperty_GetCustomStorageCallbacks.argtypes = [ctypes.c_void_p] rt.IndexProperty_GetCustomStorageCallbacks.restype = ctypes.c_void_p -rt.IndexProperty_GetCustomStorageCallbacks.errcheck = check_value +rt.IndexProperty_GetCustomStorageCallbacks.errcheck = check_value # type: ignore rt.IndexProperty_SetIndexID.argtypes = [ctypes.c_void_p, ctypes.c_int64] rt.IndexProperty_SetIndexID.restype = ctypes.c_int -rt.IndexProperty_SetIndexID.errcheck = check_return +rt.IndexProperty_SetIndexID.errcheck = check_return # type: ignore rt.IndexProperty_GetIndexID.argtypes = [ctypes.c_void_p] rt.IndexProperty_GetIndexID.restype = ctypes.c_int64 -rt.IndexProperty_GetIndexID.errcheck = check_value +rt.IndexProperty_GetIndexID.errcheck = check_value # type: ignore -rt.SIDX_NewBuffer.argtypes = [ctypes.c_uint] +rt.SIDX_NewBuffer.argtypes = [ctypes.c_size_t] rt.SIDX_NewBuffer.restype = ctypes.c_void_p -rt.SIDX_NewBuffer.errcheck = check_void +rt.SIDX_NewBuffer.errcheck = check_void # type: ignore rt.SIDX_DeleteBuffer.argtypes = [ctypes.c_void_p] rt.SIDX_DeleteBuffer.restype = None -rt.SIDX_Version.argtypes = [] -rt.SIDX_Version.restype = ctypes.POINTER(ctypes.c_char) -rt.SIDX_Version.errcheck = free_returned_char_p - # TPR-Tree API try: - rt.Index_InsertTPData.argtypes = [ctypes.c_void_p, - ctypes.c_int64, - ctypes.POINTER(ctypes.c_double), - ctypes.POINTER(ctypes.c_double), - ctypes.POINTER(ctypes.c_double), - ctypes.POINTER(ctypes.c_double), - ctypes.c_double, - ctypes.c_double, - ctypes.c_uint32, - ctypes.POINTER(ctypes.c_ubyte), - ctypes.c_uint32] + rt.Index_InsertTPData.argtypes = [ + ctypes.c_void_p, + ctypes.c_int64, + ctypes.POINTER(ctypes.c_double), + ctypes.POINTER(ctypes.c_double), + ctypes.POINTER(ctypes.c_double), + ctypes.POINTER(ctypes.c_double), + ctypes.c_double, + ctypes.c_double, + ctypes.c_uint32, + ctypes.POINTER(ctypes.c_ubyte), + ctypes.c_size_t, + ] rt.Index_InsertTPData.restype = ctypes.c_int - rt.Index_InsertTPData.errcheck = check_return + rt.Index_InsertTPData.errcheck = check_return # type: ignore - rt.Index_DeleteTPData.argtypes = [ctypes.c_void_p, - ctypes.c_int64, - ctypes.POINTER(ctypes.c_double), - ctypes.POINTER(ctypes.c_double), - ctypes.POINTER(ctypes.c_double), - ctypes.POINTER(ctypes.c_double), - ctypes.c_double, - ctypes.c_double, - ctypes.c_uint32] + rt.Index_DeleteTPData.argtypes = [ + ctypes.c_void_p, + ctypes.c_int64, + ctypes.POINTER(ctypes.c_double), + ctypes.POINTER(ctypes.c_double), + ctypes.POINTER(ctypes.c_double), + ctypes.POINTER(ctypes.c_double), + ctypes.c_double, + ctypes.c_double, + ctypes.c_uint32, + ] rt.Index_DeleteTPData.restype = ctypes.c_int - rt.Index_DeleteTPData.errcheck = check_return + rt.Index_DeleteTPData.errcheck = check_return # type: ignore - rt.Index_TPIntersects_id.argtypes = [ctypes.c_void_p, - ctypes.POINTER(ctypes.c_double), - ctypes.POINTER(ctypes.c_double), - ctypes.POINTER(ctypes.c_double), - ctypes.POINTER(ctypes.c_double), - ctypes.c_double, - ctypes.c_double, - ctypes.c_uint32, - ctypes.POINTER( - ctypes.POINTER(ctypes.c_int64)), - ctypes.POINTER(ctypes.c_uint64)] + rt.Index_TPIntersects_id.argtypes = [ + ctypes.c_void_p, + ctypes.POINTER(ctypes.c_double), + ctypes.POINTER(ctypes.c_double), + ctypes.POINTER(ctypes.c_double), + ctypes.POINTER(ctypes.c_double), + ctypes.c_double, + ctypes.c_double, + ctypes.c_uint32, + ctypes.POINTER(ctypes.POINTER(ctypes.c_int64)), + ctypes.POINTER(ctypes.c_uint64), + ] rt.Index_TPIntersects_id.restype = ctypes.c_int - rt.Index_TPIntersects_id.errcheck = check_return + rt.Index_TPIntersects_id.errcheck = check_return # type: ignore - rt.Index_TPIntersects_obj.argtypes = [ctypes.c_void_p, - ctypes.POINTER(ctypes.c_double), - ctypes.POINTER(ctypes.c_double), - ctypes.POINTER(ctypes.c_double), - ctypes.POINTER(ctypes.c_double), - ctypes.c_double, - ctypes.c_double, - ctypes.c_uint32, - ctypes.POINTER( - ctypes.POINTER(ctypes.c_void_p)), - ctypes.POINTER(ctypes.c_uint64)] + rt.Index_TPIntersects_obj.argtypes = [ + ctypes.c_void_p, + ctypes.POINTER(ctypes.c_double), + ctypes.POINTER(ctypes.c_double), + ctypes.POINTER(ctypes.c_double), + ctypes.POINTER(ctypes.c_double), + ctypes.c_double, + ctypes.c_double, + ctypes.c_uint32, + ctypes.POINTER(ctypes.POINTER(ctypes.c_void_p)), + ctypes.POINTER(ctypes.c_uint64), + ] rt.Index_TPIntersects_obj.restype = ctypes.c_int - rt.Index_TPIntersects_obj.errcheck = check_return + rt.Index_TPIntersects_obj.errcheck = check_return # type: ignore - rt.Index_TPIntersects_count.argtypes = [ctypes.c_void_p, - ctypes.POINTER(ctypes.c_double), - ctypes.POINTER(ctypes.c_double), - ctypes.POINTER(ctypes.c_double), - ctypes.POINTER(ctypes.c_double), - ctypes.c_double, - ctypes.c_double, - ctypes.c_uint32, - ctypes.POINTER(ctypes.c_uint64)] + rt.Index_TPIntersects_count.argtypes = [ + ctypes.c_void_p, + ctypes.POINTER(ctypes.c_double), + ctypes.POINTER(ctypes.c_double), + ctypes.POINTER(ctypes.c_double), + ctypes.POINTER(ctypes.c_double), + ctypes.c_double, + ctypes.c_double, + ctypes.c_uint32, + ctypes.POINTER(ctypes.c_uint64), + ] + rt.Index_TPIntersects_count.restype = ctypes.c_int + rt.Index_TPIntersects_count.errcheck = check_return # type: ignore rt.Index_TPNearestNeighbors_id.argtypes = [ ctypes.c_void_p, @@ -602,11 +634,11 @@ ctypes.c_double, ctypes.c_double, ctypes.c_uint32, - ctypes.POINTER( - ctypes.POINTER(ctypes.c_int64)), - ctypes.POINTER(ctypes.c_uint64)] + ctypes.POINTER(ctypes.POINTER(ctypes.c_int64)), + ctypes.POINTER(ctypes.c_uint64), + ] rt.Index_TPNearestNeighbors_id.restype = ctypes.c_int - rt.Index_TPNearestNeighbors_id.errcheck = check_return + rt.Index_TPNearestNeighbors_id.errcheck = check_return # type: ignore rt.Index_TPNearestNeighbors_obj.argtypes = [ ctypes.c_void_p, @@ -617,10 +649,10 @@ ctypes.c_double, ctypes.c_double, ctypes.c_uint32, - ctypes.POINTER( - ctypes.POINTER(ctypes.c_void_p)), - ctypes.POINTER(ctypes.c_uint64)] + ctypes.POINTER(ctypes.POINTER(ctypes.c_void_p)), + ctypes.POINTER(ctypes.c_uint64), + ] rt.Index_TPNearestNeighbors_obj.restype = ctypes.c_int - rt.Index_TPNearestNeighbors_obj.errcheck = check_return + rt.Index_TPNearestNeighbors_obj.errcheck = check_return # type: ignore except AttributeError: pass diff -Nru python-rtree-0.9.7/rtree/exceptions.py python-rtree-1.0.0/rtree/exceptions.py --- python-rtree-0.9.7/rtree/exceptions.py 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/rtree/exceptions.py 2022-04-04 21:43:52.000000000 +0000 @@ -1,4 +1,3 @@ - class RTreeError(Exception): "RTree exception, indicates a RTree-related error." pass diff -Nru python-rtree-0.9.7/rtree/finder.py python-rtree-1.0.0/rtree/finder.py --- python-rtree-0.9.7/rtree/finder.py 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/rtree/finder.py 2022-04-04 21:43:52.000000000 +0000 @@ -4,78 +4,76 @@ Locate `libspatialindex` shared library by any means necessary. """ -import os -import sys import ctypes +import os import platform +import sys from ctypes.util import find_library # the current working directory of this file -_cwd = os.path.abspath(os.path.expanduser( - os.path.dirname(__file__))) +_cwd = os.path.abspath(os.path.expanduser(os.path.dirname(__file__))) # generate a bunch of candidate locations where the # libspatialindex shared library *might* be hanging out _candidates = [ - os.environ.get('SPATIALINDEX_C_LIBRARY', None), - os.path.join(_cwd, 'lib'), + os.environ.get("SPATIALINDEX_C_LIBRARY", None), + os.path.join(_cwd, "lib"), _cwd, - ''] + "", +] -def load(): - """ - Load the `libspatialindex` shared library. +def load() -> ctypes.CDLL: + """Load the `libspatialindex` shared library. - Returns - ----------- - rt : ctypes object - Loaded shared library + :returns: Loaded shared library """ - if os.name == 'nt': + if os.name == "nt": # check the platform architecture - if '64' in platform.architecture()[0]: - arch = '64' + if "64" in platform.architecture()[0]: + arch = "64" else: - arch = '32' - lib_name = 'spatialindex_c-{}.dll'.format(arch) + arch = "32" + lib_name = f"spatialindex_c-{arch}.dll" # add search paths for conda installs - if 'conda' in sys.version: - _candidates.append( - os.path.join(sys.prefix, "Library", "bin")) + if ( + os.path.exists(os.path.join(sys.prefix, "conda-meta")) + or "conda" in sys.version + ): + _candidates.append(os.path.join(sys.prefix, "Library", "bin")) # get the current PATH - oldenv = os.environ.get('PATH', '').strip().rstrip(';') + oldenv = os.environ.get("PATH", "").strip().rstrip(";") # run through our list of candidate locations for path in _candidates: if not path or not os.path.exists(path): continue # temporarily add the path to the PATH environment variable # so Windows can find additional DLL dependencies. - os.environ['PATH'] = ';'.join([path, oldenv]) + os.environ["PATH"] = ";".join([path, oldenv]) try: rt = ctypes.cdll.LoadLibrary(os.path.join(path, lib_name)) if rt is not None: return rt - except (WindowsError, OSError): + except OSError: pass except BaseException as E: - print('rtree.finder unexpected error: {}'.format(str(E))) + print(f"rtree.finder unexpected error: {E!s}") finally: - os.environ['PATH'] = oldenv - raise OSError("could not find or load {}".format(lib_name)) + os.environ["PATH"] = oldenv + raise OSError(f"could not find or load {lib_name}") - elif os.name == 'posix': + elif os.name == "posix": # posix includes both mac and linux # use the extension for the specific platform - if platform.system() == 'Darwin': + if platform.system() == "Darwin": # macos shared libraries are `.dylib` lib_name = "libspatialindex_c.dylib" else: # linux shared libraries are `.so` - lib_name = 'libspatialindex_c.so' + lib_name = "libspatialindex_c.so" # get the starting working directory cwd = os.getcwd() @@ -104,17 +102,16 @@ if rt is not None: return rt except BaseException as E: - print('rtree.finder ({}) unexpected error: {}'.format( - target, str(E))) + print(f"rtree.finder ({target}) unexpected error: {E!s}") finally: os.chdir(cwd) try: # try loading library using LD path search - rt = ctypes.cdll.LoadLibrary( - find_library('spatialindex_c')) - if rt is not None: - return rt + path = find_library("spatialindex_c") + if path is not None: + return ctypes.cdll.LoadLibrary(path) + except BaseException: pass diff -Nru python-rtree-0.9.7/rtree/index.py python-rtree-1.0.0/rtree/index.py --- python-rtree-0.9.7/rtree/index.py 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/rtree/index.py 2022-04-04 21:43:52.000000000 +0000 @@ -1,12 +1,21 @@ +from __future__ import annotations + import ctypes import os import os.path +import pickle import pprint +import sys +import warnings +from typing import Any, Dict, Iterator, List, Optional, Sequence, Tuple, Union, overload + +if sys.version_info >= (3, 8): + from typing import Literal +else: + from typing_extensions import Literal from . import core - -import pickle - +from .exceptions import RTreeError RT_Memory = 0 RT_Disk = 1 @@ -22,14 +31,14 @@ __c_api_version__ = core.rt.SIDX_Version() -major_version, minor_version, patch_version = [ - int(t) for t in __c_api_version__.decode('utf-8').split('.')] +major_version, minor_version, patch_version = ( + int(t) for t in __c_api_version__.decode("utf-8").split(".") +) -if (major_version < 2 and minor_version < 7): - raise Exception( - "This version of Rtree requires libspatialindex 1.7.0 or greater") +if (major_version, minor_version, patch_version) < (1, 8, 5): + raise Exception("Rtree requires libspatialindex 1.8.5 or greater") -__all__ = ['Rtree', 'Index', 'Property'] +__all__ = ["Rtree", "Index", "Property"] def _get_bounds(handle, bounds_fn, interleaved): @@ -38,19 +47,13 @@ dimension = ctypes.c_uint32(0) bounds_fn( - handle, - ctypes.byref(pp_mins), - ctypes.byref(pp_maxs), - ctypes.byref(dimension)) - if (dimension.value == 0): + handle, ctypes.byref(pp_mins), ctypes.byref(pp_maxs), ctypes.byref(dimension) + ) + 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)] @@ -77,10 +80,10 @@ return s -class Index(object): +class Index: """An R-Tree, MVR-Tree, or TPR-Tree indexing object""" - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any) -> None: """Creates a new index :param filename: @@ -122,7 +125,7 @@ This parameter determines the coordinate order for all methods that take in coordinates. - :param properties: An :class:`index.Property` object + :param properties: An :class:`index.Property` object. This object sets both the creation and instantiation properties for the object and they are passed down into libspatialindex. A few properties are curried from instantiation parameters @@ -196,15 +199,17 @@ True """ - self.properties = kwargs.get('properties', Property()) + self.properties = kwargs.get("properties", Property()) - if self.properties.type == RT_TPRTree \ - and not hasattr(core.rt, 'Index_InsertTPData'): + if self.properties.type == RT_TPRTree and not hasattr( + core.rt, "Index_InsertTPData" + ): raise RuntimeError( - "TPR-Tree type not supported with version of libspatialindex") + "TPR-Tree type not supported with version of libspatialindex" + ) # interleaved True gives 'bbox' order. - self.interleaved = bool(kwargs.get('interleaved', True)) + self.interleaved = bool(kwargs.get("interleaved", True)) stream = None basename = None @@ -229,22 +234,21 @@ self.properties.filename = basename # check we can read the file - f = basename + "." + self.properties.idx_extension + f = str(basename) + "." + self.properties.idx_extension p = os.path.abspath(f) # assume if the file exists, we're not going to overwrite it # unless the user explicitly set the property to do so if os.path.exists(p): - self.properties.overwrite = \ - bool(kwargs.get('overwrite', False)) + self.properties.overwrite = bool(kwargs.get("overwrite", False)) # assume we're fetching the first index_id. If the user # set it, we'll fetch that one. if not self.properties.overwrite: try: self.properties.index_id - except core.RTreeError: + except RTreeError: self.properties.index_id = 1 d = os.path.dirname(p) @@ -252,19 +256,13 @@ message = "Unable to open file '%s' for index storage" % f raise OSError(message) elif storage: - if (major_version < 2 and minor_version < 8): - raise core.RTreeError( - "libspatialindex {0} does not support custom storage" - .format(__c_api_version__)) - self.properties.storage = RT_Custom if storage.hasData: - self.properties.overwrite = \ - bool(kwargs.get('overwrite', False)) + self.properties.overwrite = bool(kwargs.get("overwrite", False)) if not self.properties.overwrite: try: self.properties.index_id - except core.RTreeError: + except RTreeError: self.properties.index_id = 1 else: storage.clear() @@ -273,7 +271,7 @@ else: self.properties.storage = RT_Memory - ps = kwargs.get('pagesize', None) + ps = kwargs.get("pagesize", None) if ps: self.properties.pagesize = int(ps) @@ -288,51 +286,64 @@ for item in stream: self.insert(*item) - def get_size(self): + def get_size(self) -> int: + warnings.warn( + "index.get_size() is deprecated, use len(index) instead", DeprecationWarning + ) + return len(self) + + def __len__(self) -> int: + """The number of entries in the index. + + :return: number of entries + """ try: return self.count(self.bounds) - except core.RTreeError: + except RTreeError: return 0 - def __repr__(self): - return 'rtree.index.Index(bounds={}, size={})'.format(self.bounds, - self.get_size()) + def __repr__(self) -> str: + return "rtree.index.Index(bounds={}, size={})".format( + self.bounds, self.get_size() + ) - def __getstate__(self): + def __getstate__(self) -> Dict[str, Any]: state = self.__dict__.copy() del state["handle"] return state - def __setstate__(self, state): + def __setstate__(self, state: Dict[str, Any]) -> None: self.__dict__.update(state) self.handle = IndexHandle(self.properties.handle) - def dumps(self, obj): + def dumps(self, obj: object) -> bytes: return pickle.dumps(obj) - def loads(self, string): + def loads(self, string: bytes) -> object: return pickle.loads(string) - def close(self): + def close(self) -> None: """Force a flush of the index to storage. Renders index inaccessible.""" if self.handle: self.handle.destroy() self.handle = None else: - raise IOError("Unclosable index") + raise OSError("Unclosable index") - def flush(self): + def flush(self) -> None: """Force a flush of the index to storage.""" if self.handle: self.handle.flush() - def get_coordinate_pointers(self, coordinates): + def get_coordinate_pointers( + self, coordinates: Sequence[float] + ) -> Tuple[float, float]: try: iter(coordinates) except TypeError: - raise TypeError('Bounds must be a sequence') + raise TypeError("Bounds must be a sequence") dimension = self.properties.dimension mins = ctypes.c_double * dimension @@ -343,33 +354,33 @@ # it's a point make it into a bbox. [x, y] => [x, y, x, y] if len(coordinates) == dimension: - coordinates += coordinates + coordinates = *coordinates, *coordinates if len(coordinates) != dimension * 2: - raise core.RTreeError( + raise RTreeError( "Coordinates must be in the form " - "(minx, miny, maxx, maxy) or (x, y) for 2D indexes") + "(minx, miny, maxx, maxy) or (x, y) for 2D indexes" + ) # so here all coords are in the form: # [xmin, ymin, zmin, xmax, ymax, zmax] for i in range(dimension): if not coordinates[i] <= coordinates[i + dimension]: - raise core.RTreeError( - "Coordinates must not have minimums more than maximums") + raise RTreeError( + "Coordinates must not have minimums more than maximums" + ) - p_mins = mins( - *[ctypes.c_double(coordinates[i]) for i in range(dimension)]) + p_mins = mins(*[ctypes.c_double(coordinates[i]) for i in range(dimension)]) p_maxs = maxs( - *[ctypes.c_double(coordinates[i + dimension]) - for i in range(dimension)]) + *[ctypes.c_double(coordinates[i + dimension]) for i in range(dimension)] + ) return (p_mins, p_maxs) @staticmethod def _get_time_doubles(times): if times[0] > times[1]: - raise core.RTreeError( - "Start time must be less than end time") + raise RTreeError("Start time must be less than end time") t_start = ctypes.c_double(times[0]) t_end = ctypes.c_double(times[1]) return t_start, t_end @@ -390,6 +401,7 @@ def get_result_limit(self): return core.rt.Index_GetResultSetOffset(self.handle) + result_limit = property(get_result_limit, set_result_limit) def set_result_offset(self, value): @@ -397,18 +409,17 @@ def get_result_offset(self): return core.rt.Index_GetResultSetLimit(self.handle) + result_offset = property(get_result_offset, set_result_offset) - def insert(self, id, coordinates, obj=None): + def insert(self, id: int, coordinates: Any, obj: object = None) -> None: """Inserts an item into the index with the given coordinates. - :param id: long integer - A long integer that is the identifier for this index entry. IDs + :param id: 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. - :param coordinates: sequence or array - This may be an object that satisfies the numpy array + :param coordinates: This may be an object that satisfies the numpy array protocol, providing the index's dimension * 2 coordinate pairs representing the `mink` and `maxk` coordinates in each dimension defining the bounds of the query window. @@ -445,7 +456,8 @@ """ if self.properties.type == RT_TPRTree: - return self._insertTP(id, *coordinates, obj=obj) + # https://github.com/python/mypy/issues/6799 + return self._insertTP(id, *coordinates, obj=obj) # type: ignore[misc] p_mins, p_maxs = self.get_coordinate_pointers(coordinates) data = ctypes.c_ubyte(0) @@ -453,11 +465,20 @@ pyserialized = None if obj is not None: size, data, pyserialized = self._serialize(obj) - core.rt.Index_InsertData(self.handle, id, p_mins, p_maxs, - self.properties.dimension, data, size) + core.rt.Index_InsertData( + self.handle, id, p_mins, p_maxs, self.properties.dimension, data, size + ) + add = insert - def _insertTP(self, id, coordinates, velocities, time, obj=None): + def _insertTP( + self, + id: int, + coordinates: Sequence[float], + velocities: Sequence[float], + time: float, + obj: object = None, + ) -> None: p_mins, p_maxs = self.get_coordinate_pointers(coordinates) pv_mins, pv_maxs = self.get_coordinate_pointers(velocities) # End time isn't used @@ -466,15 +487,24 @@ size = 0 if obj is not None: size, data, _ = self._serialize(obj) - core.rt.Index_InsertTPData(self.handle, id, p_mins, p_maxs, - pv_mins, pv_maxs, t_start, t_end, - self.properties.dimension, data, size) + core.rt.Index_InsertTPData( + self.handle, + id, + p_mins, + p_maxs, + pv_mins, + pv_maxs, + t_start, + t_end, + self.properties.dimension, + data, + size, + ) - def count(self, coordinates): + def count(self, coordinates: Any) -> int: """Return number of objects that intersect the given coordinates. - :param coordinates: sequence or array - This may be an object that satisfies the numpy array + :param coordinates: This may be an object that satisfies the numpy array protocol, providing the index's dimension * 2 coordinate pairs representing the `mink` and `maxk` coordinates in each dimension defining the bounds of the query window. @@ -519,45 +549,65 @@ p_num_results = ctypes.c_uint64(0) - core.rt.Index_Intersects_count(self.handle, - p_mins, - p_maxs, - self.properties.dimension, - ctypes.byref(p_num_results)) + core.rt.Index_Intersects_count( + self.handle, + p_mins, + p_maxs, + self.properties.dimension, + ctypes.byref(p_num_results), + ) return p_num_results.value - def _countTP(self, coordinates, velocities, times): + def _countTP( + self, coordinates: Sequence[float], velocities: Sequence[float], times: float + ) -> int: 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) p_num_results = ctypes.c_uint64(0) - core.rt.Index_TPIntersects_count(self.handle, - p_mins, - p_maxs, - pv_mins, - pv_maxs, - t_start, - t_end, - self.properties.dimension, - ctypes.byref(p_num_results)) + core.rt.Index_TPIntersects_count( + self.handle, + p_mins, + p_maxs, + pv_mins, + pv_maxs, + t_start, + t_end, + self.properties.dimension, + ctypes.byref(p_num_results), + ) return p_num_results.value - def contains(self, coordinates, objects=False): + @overload + def contains(self, coordinates: Any, objects: Literal[True]) -> Iterator[Item]: + ... + + @overload + def contains( + self, coordinates: Any, objects: Literal[False] = False + ) -> Optional[Iterator[int]]: + ... + + @overload + def contains(self, coordinates: Any, objects: Literal["raw"]) -> Iterator[object]: + ... + + def contains( + self, coordinates: Any, objects: Union[bool, Literal["raw"]] = False + ) -> Optional[Iterator[Union[Item, int, object]]]: """Return ids or objects in the index that contains within the given coordinates. - :param coordinates: sequence or array - This may be an object that satisfies the numpy array + :param coordinates: This may be an object that satisfies the numpy array protocol, providing the index's dimension * 2 coordinate pairs representing the `mink` and `maxk` coordinates in each dimension defining the bounds of the query window. - :param objects: True or False or 'raw' - If True, the intersection method will return index objects that + :param objects: If True, the intersection method will return index objects that were pickled when they were stored with each index entry, as well as the id and bounds of the index entries. If 'raw', the objects will be returned without the :class:`rtree.index.Item` wrapper. @@ -603,20 +653,107 @@ except AttributeError: return None - core.rt.Index_Contains_id(self.handle, - p_mins, - p_maxs, - self.properties.dimension, - ctypes.byref(it), - ctypes.byref(p_num_results)) + core.rt.Index_Contains_id( + self.handle, + p_mins, + p_maxs, + self.properties.dimension, + ctypes.byref(it), + ctypes.byref(p_num_results), + ) return self._get_ids(it, p_num_results.value) - def intersection(self, coordinates, objects=False): + def __and__(self, other: Index) -> Index: + """Take the intersection of two Index objects. + + :param other: another index + :return: a new index + :raises AssertionError: if self and other have different interleave or dimension + """ + assert self.interleaved == other.interleaved + assert self.properties.dimension == other.properties.dimension + + i = 0 + new_idx = Index(interleaved=self.interleaved, properties=self.properties) + + # For each Item in self... + for item1 in self.intersection(self.bounds, objects=True): + if self.interleaved: + # For each Item in other that intersects... + for item2 in other.intersection(item1.bbox, objects=True): + # Compute the intersection bounding box + bbox = [] + for j in range(len(item1.bbox)): + if j < len(item1.bbox) // 2: + bbox.append(max(item1.bbox[j], item2.bbox[j])) + else: + bbox.append(min(item1.bbox[j], item2.bbox[j])) + + new_idx.insert(i, bbox, (item1.object, item2.object)) + i += 1 + + else: + # For each Item in other that intersects... + for item2 in other.intersection(item1.bounds, objects=True): + # Compute the intersection bounding box + bounds = [] + for j in range(len(item1.bounds)): + if j % 2 == 0: + bounds.append(max(item1.bounds[j], item2.bounds[j])) + else: + bounds.append(min(item1.bounds[j], item2.bounds[j])) + + new_idx.insert(i, bounds, (item1.object, item2.object)) + i += 1 + + return new_idx + + def __or__(self, other: Index) -> Index: + """Take the union of two Index objects. + + :param other: another index + :return: a new index + :raises AssertionError: if self and other have different interleave or dimension + """ + assert self.interleaved == other.interleaved + assert self.properties.dimension == other.properties.dimension + + new_idx = Index(interleaved=self.interleaved, properties=self.properties) + + # For each index... + for old_idx in [self, other]: + # For each item... + for item in old_idx.intersection(old_idx.bounds, objects=True): + if self.interleaved: + new_idx.insert(item.id, item.bbox, item.object) + else: + new_idx.insert(item.id, item.bounds, item.object) + + return new_idx + + @overload + def intersection(self, coordinates: Any, objects: Literal[True]) -> Iterator[Item]: + ... + + @overload + def intersection( + self, coordinates: Any, objects: Literal[False] = False + ) -> Iterator[int]: + ... + + @overload + def intersection( + self, coordinates: Any, objects: Literal["raw"] + ) -> Iterator[object]: + ... + + def intersection( + self, coordinates: Any, objects: Union[bool, Literal["raw"]] = False + ) -> Iterator[Union[Item, int, object]]: """Return ids or objects in the index that intersect the given coordinates. - :param coordinates: sequence or array - This may be an object that satisfies the numpy array + :param coordinates: This may be an object that satisfies the numpy array protocol, providing the index's dimension * 2 coordinate pairs representing the `mink` and `maxk` coordinates in each dimension defining the bounds of the query window. @@ -625,8 +762,7 @@ velocity pairs `minvk` and `maxvk` and a time pair for the time range as a float. - :param objects: True or False or 'raw' - If True, the intersection method will return index objects that + :param objects: If True, the intersection method will return index objects that were pickled when they were stored with each index entry, as well as the id and bounds of the index entries. If 'raw', the objects will be returned without the :class:`rtree.index.Item` wrapper. @@ -675,7 +811,10 @@ """ if self.properties.type == RT_TPRTree: - return self._intersectionTP(*coordinates, objects=objects) + # https://github.com/python/mypy/issues/6799 + return self._intersectionTP( # type: ignore[misc] + *coordinates, objects=objects + ) if objects: return self._intersection_obj(coordinates, objects) @@ -685,12 +824,14 @@ it = ctypes.pointer(ctypes.c_int64()) - core.rt.Index_Intersects_id(self.handle, - p_mins, - p_maxs, - self.properties.dimension, - ctypes.byref(it), - ctypes.byref(p_num_results)) + core.rt.Index_Intersects_id( + self.handle, + p_mins, + p_maxs, + self.properties.dimension, + ctypes.byref(it), + ctypes.byref(p_num_results), + ) return self._get_ids(it, p_num_results.value) def _intersectionTP(self, coordinates, velocities, times, objects=False): @@ -708,9 +849,18 @@ call = core.rt.Index_TPIntersects_id it = ctypes.pointer(ctypes.c_int64()) - call(self.handle, p_mins, p_maxs, pv_mins, pv_maxs, t_start, t_end, - self.properties.dimension, ctypes.byref(it), - ctypes.byref(p_num_results)) + call( + self.handle, + p_mins, + p_maxs, + pv_mins, + pv_maxs, + t_start, + t_end, + self.properties.dimension, + ctypes.byref(it), + ctypes.byref(p_num_results), + ) if objects: return self._get_objects(it, p_num_results.value, objects) @@ -725,15 +875,17 @@ it = ctypes.pointer(ctypes.c_void_p()) - core.rt.Index_Intersects_obj(self.handle, - p_mins, - p_maxs, - self.properties.dimension, - ctypes.byref(it), - ctypes.byref(p_num_results)) + core.rt.Index_Intersects_obj( + self.handle, + p_mins, + p_maxs, + self.properties.dimension, + ctypes.byref(it), + ctypes.byref(p_num_results), + ) return self._get_objects(it, p_num_results.value, objects) - def _contains_obj(self, coordinates, objects): + def _contains_obj(self, coordinates: Any, objects): p_mins, p_maxs = self.get_coordinate_pointers(coordinates) @@ -746,23 +898,25 @@ except AttributeError: return None - core.rt.Index_Contains_obj(self.handle, - p_mins, - p_maxs, - self.properties.dimension, - ctypes.byref(it), - ctypes.byref(p_num_results)) + core.rt.Index_Contains_obj( + self.handle, + p_mins, + p_maxs, + self.properties.dimension, + ctypes.byref(it), + ctypes.byref(p_num_results), + ) return self._get_objects(it, p_num_results.value, objects) def _get_objects(self, it, num_results, objects): # take the pointer, yield the result objects and free items = ctypes.cast( - it, ctypes.POINTER(ctypes.POINTER(ctypes.c_void_p * num_results))) - its = ctypes.cast( - items, ctypes.POINTER(ctypes.POINTER(ctypes.c_void_p))) + it, ctypes.POINTER(ctypes.POINTER(ctypes.c_void_p * num_results)) + ) + its = ctypes.cast(items, ctypes.POINTER(ctypes.POINTER(ctypes.c_void_p))) try: - if objects != 'raw': + if objects != "raw": for i in range(num_results): yield Item(self.loads, items[i]) else: @@ -799,20 +953,44 @@ it = ctypes.pointer(ctypes.c_void_p()) - core.rt.Index_NearestNeighbors_obj(self.handle, - p_mins, - p_maxs, - self.properties.dimension, - ctypes.byref(it), - p_num_results) + core.rt.Index_NearestNeighbors_obj( + self.handle, + p_mins, + p_maxs, + self.properties.dimension, + ctypes.byref(it), + p_num_results, + ) return self._get_objects(it, p_num_results.contents.value, objects) - def nearest(self, coordinates, num_results=1, objects=False): + @overload + def nearest( + self, coordinates: Any, num_results: int, objects: Literal[True] + ) -> Iterator[Item]: + ... + + @overload + def nearest( + self, coordinates: Any, num_results: int, objects: Literal[False] = False + ) -> Iterator[int]: + ... + + @overload + def nearest( + self, coordinates: Any, num_results: int, objects: Literal["raw"] + ) -> Iterator[object]: + ... + + def nearest( + self, + coordinates: Any, + num_results: int = 1, + objects: Union[bool, Literal["raw"]] = False, + ) -> Iterator[Union[Item, int, object]]: """Returns the ``k``-nearest objects to the given coordinates. - :param coordinates: sequence or array - This may be an object that satisfies the numpy array + :param coordinates: This may be an object that satisfies the numpy array protocol, providing the index's dimension * 2 coordinate pairs representing the `mink` and `maxk` coordinates in each dimension defining the bounds of the query window. @@ -821,14 +999,12 @@ velocity pairs `minvk` and `maxvk` and a time pair for the time range as a float. - :param num_results: integer - The number of results to return nearest to the given coordinates. - If two index entries are equidistant, *both* are returned. + :param num_results: The number of results to return nearest to the given + coordinates. If two index entries are equidistant, *both* are returned. This property means that :attr:`num_results` may return more items than specified - :param objects: True / False / 'raw' - If True, the nearest method will return index objects that + :param objects: If True, the nearest method will return index objects that were pickled when they were stored with each index entry, as well as the id and bounds of the index entries. If 'raw', it will return the object as entered into the database @@ -845,7 +1021,8 @@ >>> hits = idx.nearest((0, 0, 10, 10), 3, objects=True) """ if self.properties.type == RT_TPRTree: - return self._nearestTP(*coordinates, objects=objects) + # https://github.com/python/mypy/issues/6799 + return self._nearestTP(*coordinates, objects=objects) # type: ignore[misc] if objects: return self._nearest_obj(coordinates, num_results, objects) @@ -863,21 +1040,18 @@ it = ctypes.pointer(ctypes.c_int64()) - core.rt.Index_NearestNeighbors_id(self.handle, - p_mins, - p_maxs, - self.properties.dimension, - ctypes.byref(it), - p_num_results) + core.rt.Index_NearestNeighbors_id( + self.handle, + p_mins, + p_maxs, + self.properties.dimension, + ctypes.byref(it), + p_num_results, + ) 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) @@ -891,8 +1065,18 @@ it = ctypes.pointer(ctypes.c_int64()) call = core.rt.Index_TPNearestNeighbors_id - call(self.handle, p_mins, p_maxs, pv_mins, pv_maxs, t_start, t_end, - self.properties.dimension, ctypes.byref(it), p_num_results) + call( + self.handle, + p_mins, + p_maxs, + pv_mins, + pv_maxs, + t_start, + t_end, + self.properties.dimension, + ctypes.byref(it), + p_num_results, + ) if objects: return self._get_objects(it, p_num_results.contents.value, objects) @@ -911,25 +1095,23 @@ """ if coordinate_interleaved is None: coordinate_interleaved = self.interleaved - return _get_bounds( - self.handle, core.rt.Index_GetBounds, coordinate_interleaved) + return _get_bounds(self.handle, core.rt.Index_GetBounds, coordinate_interleaved) + bounds = property(get_bounds) - def delete(self, id, coordinates): + def delete(self, id: int, coordinates: Any) -> None: """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 ID for the entry, which need not be unique. The + :param id: 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 + :param coordinates: Dimension * 2 coordinate pairs, representing the min and max coordinates in each dimension of the item to be deleted from the index. Their ordering will depend on the index's :attr:`interleaved` data member. @@ -966,24 +1148,39 @@ return self._deleteTP(id, *coordinates) p_mins, p_maxs = self.get_coordinate_pointers(coordinates) core.rt.Index_DeleteData( - self.handle, id, p_mins, p_maxs, self.properties.dimension) + self.handle, id, p_mins, p_maxs, self.properties.dimension + ) - def _deleteTP(self, id, coordinates, velocities, times): + def _deleteTP( + self, + id: int, + coordinates: Sequence[float], + velocities: Sequence[float], + times: float, + ) -> None: 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) core.rt.Index_DeleteTPData( - self.handle, id, p_mins, p_maxs, pv_mins, pv_maxs, t_start, t_end, - self.properties.dimension) + self.handle, + id, + p_mins, + p_maxs, + pv_mins, + pv_maxs, + t_start, + t_end, + self.properties.dimension, + ) - def valid(self): + def valid(self) -> bool: return bool(core.rt.Index_IsValid(self.handle)) def clearBuffer(self): return core.rt.Index_ClearBuffer(self.handle) @classmethod - def deinterleave(self, interleaved): + def deinterleave(self, interleaved: Sequence[object]) -> List[object]: """ [xmin, ymin, xmax, ymax] => [xmin, xmax, ymin, ymax] @@ -994,7 +1191,7 @@ [0, 10, 1, 11, 2, 12] """ - assert len(interleaved) % 2 == 0, ("must be a pairwise list") + assert len(interleaved) % 2 == 0, "must be a pairwise list" dimension = len(interleaved) // 2 di = [] for i in range(dimension): @@ -1002,7 +1199,7 @@ return di @classmethod - def interleave(self, deinterleaved): + def interleave(self, deinterleaved: Sequence[float]) -> List[float]: """ [xmin, xmax, ymin, ymax, zmin, zmax] => [xmin, ymin, zmin, xmax, ymax, zmax] @@ -1017,12 +1214,13 @@ [-1, 58, 22, 1, 62, 24] """ - assert len(deinterleaved) % 2 == 0, ("must be a pairwise list") + assert len(deinterleaved) % 2 == 0, "must be a pairwise list" # dimension = len(deinterleaved) / 2 interleaved = [] for i in range(2): - interleaved.extend([deinterleaved[i + j] - for j in range(0, len(deinterleaved), 2)]) + interleaved.extend( + [deinterleaved[i + j] for j in range(0, len(deinterleaved), 2)] + ) return interleaved def _create_idx_from_stream(self, stream): @@ -1034,8 +1232,9 @@ darray = ctypes.c_double * dimension mins = darray() maxs = darray() - no_data = ctypes.cast(ctypes.pointer(ctypes.c_ubyte(0)), - ctypes.POINTER(ctypes.c_ubyte)) + no_data = ctypes.cast( + ctypes.pointer(ctypes.c_ubyte(0)), ctypes.POINTER(ctypes.c_ubyte) + ) def py_next_item(p_id, p_mins, p_maxs, p_dimension, p_data, p_length): """This function must fill pointers to individual entries that will @@ -1088,38 +1287,39 @@ pp_maxs = ctypes.pointer(ctypes.pointer(ctypes.c_double())) dimension = ctypes.c_uint32(0) - core.rt.Index_GetLeaves(self.handle, - ctypes.byref(leaf_node_count), - ctypes.byref(p_leafsizes), - ctypes.byref(p_leafids), - ctypes.byref(pp_childids), - ctypes.byref(pp_mins), - ctypes.byref(pp_maxs), - ctypes.byref(dimension) - ) + core.rt.Index_GetLeaves( + self.handle, + ctypes.byref(leaf_node_count), + ctypes.byref(p_leafsizes), + ctypes.byref(p_leafids), + ctypes.byref(pp_childids), + ctypes.byref(pp_mins), + ctypes.byref(pp_maxs), + ctypes.byref(dimension), + ) output = [] count = leaf_node_count.value - sizes = ctypes.cast( - p_leafsizes, ctypes.POINTER(ctypes.c_uint32 * count)) + sizes = ctypes.cast(p_leafsizes, ctypes.POINTER(ctypes.c_uint32 * count)) ids = ctypes.cast(p_leafids, ctypes.POINTER(ctypes.c_int64 * count)) child = ctypes.cast( - pp_childids, - ctypes.POINTER(ctypes.POINTER(ctypes.c_int64) * count)) + pp_childids, ctypes.POINTER(ctypes.POINTER(ctypes.c_int64) * count) + ) mins = ctypes.cast( - pp_mins, - ctypes.POINTER(ctypes.POINTER(ctypes.c_double) * count)) + pp_mins, ctypes.POINTER(ctypes.POINTER(ctypes.c_double) * count) + ) maxs = ctypes.cast( - pp_maxs, - ctypes.POINTER(ctypes.POINTER(ctypes.c_double) * count)) + pp_maxs, ctypes.POINTER(ctypes.POINTER(ctypes.c_double) * count) + ) for i in range(count): p_child_ids = child.contents[i] id = ids.contents[i] size = sizes.contents[i] child_ids_array = ctypes.cast( - p_child_ids, ctypes.POINTER(ctypes.c_int64 * size)) + p_child_ids, ctypes.POINTER(ctypes.c_int64 * size) + ) child_ids = [] for j in range(size): @@ -1127,15 +1327,18 @@ # free the child ids list core.rt.Index_Free( - ctypes.cast(p_child_ids, ctypes.POINTER(ctypes.c_void_p))) + ctypes.cast(p_child_ids, ctypes.POINTER(ctypes.c_void_p)) + ) p_mins = mins.contents[i] p_maxs = maxs.contents[i] p_mins = ctypes.cast( - p_mins, ctypes.POINTER(ctypes.c_double * dimension.value)) + p_mins, ctypes.POINTER(ctypes.c_double * dimension.value) + ) p_maxs = ctypes.cast( - p_maxs, ctypes.POINTER(ctypes.c_double * dimension.value)) + p_maxs, ctypes.POINTER(ctypes.c_double * dimension.value) + ) bounds = [] bounds = [p_mins.contents[i] for i in range(dimension.value)] @@ -1144,10 +1347,8 @@ # free the bounds p_mins = ctypes.cast(p_mins, ctypes.POINTER(ctypes.c_double)) p_maxs = ctypes.cast(p_maxs, ctypes.POINTER(ctypes.c_double)) - core.rt.Index_Free( - ctypes.cast(p_mins, ctypes.POINTER(ctypes.c_void_p))) - core.rt.Index_Free( - ctypes.cast(p_maxs, ctypes.POINTER(ctypes.c_void_p))) + core.rt.Index_Free(ctypes.cast(p_mins, ctypes.POINTER(ctypes.c_void_p))) + core.rt.Index_Free(ctypes.cast(p_maxs, ctypes.POINTER(ctypes.c_void_p))) output.append((id, child_ids, bounds)) @@ -1158,12 +1359,12 @@ Rtree = Index -class Item(object): +class Item: """A container for index entries""" - __slots__ = ('handle', 'owned', 'id', 'object', 'bounds') + __slots__ = ("handle", "owned", "id", "object", "bounds") - def __init__(self, loads, handle, owned=False): + def __init__(self, loads, handle, owned=False) -> None: """There should be no reason to instantiate these yourself. Items are created automatically when you call :meth:`rtree.index.Index.intersection` (or other index querying @@ -1178,14 +1379,16 @@ self.object = None self.object = self.get_object(loads) - self.bounds = _get_bounds( - self.handle, core.rt.IndexItem_GetBounds, False) + self.bounds = _get_bounds(self.handle, core.rt.IndexItem_GetBounds, False) + + def __lt__(self, other: Item) -> bool: + return self.id < other.id - def __gt__(self, other): + def __gt__(self, other: Item) -> bool: return self.id > other.id @property - def bbox(self): + def bbox(self) -> List[float]: """Returns the bounding box of the index entry""" return Index.interleave(self.bounds) @@ -1203,18 +1406,17 @@ """Handle has been destroyed and can no longer be used""" -class Handle(object): - - def __init__(self, *args, **kwargs): +class Handle: + def __init__(self, *args: Any, **kwargs: Any) -> None: self._ptr = self._create(*args, **kwargs) - def _create(self, *args, **kwargs): + def _create(self, *args: Any, **kwargs: Any): raise NotImplementedError def _destroy(self, ptr): raise NotImplementedError - def destroy(self): + def destroy(self) -> None: try: if self._ptr is not None: @@ -1229,7 +1431,7 @@ raise InvalidHandleException return self._ptr - def __del__(self): + def __del__(self) -> None: try: self.destroy() except NameError: @@ -1245,7 +1447,7 @@ _create = core.rt.Index_Create _destroy = core.rt.Index_Destroy - def flush(self): + def flush(self) -> None: try: core.rt.Index_Flush if self._ptr is not None: @@ -1265,60 +1467,77 @@ _destroy = core.rt.IndexProperty_Destroy -class Property(object): +class Property: """An index property object is a container that contains a number of settable index properties. Many of these properties must be set at index creation times, while others can be used to adjust performance or behavior.""" pkeys = ( - 'buffering_capacity', 'custom_storage_callbacks', - 'custom_storage_callbacks_size', 'dat_extension', 'dimension', - 'filename', 'fill_factor', 'idx_extension', 'index_capacity', - 'index_id', 'leaf_capacity', 'near_minimum_overlap_factor', - 'overwrite', 'pagesize', 'point_pool_capacity', - 'region_pool_capacity', 'reinsert_factor', - 'split_distribution_factor', 'storage', 'tight_mbr', 'tpr_horizon', - 'type', 'variant', 'writethrough') + "buffering_capacity", + "custom_storage_callbacks", + "custom_storage_callbacks_size", + "dat_extension", + "dimension", + "filename", + "fill_factor", + "idx_extension", + "index_capacity", + "index_id", + "leaf_capacity", + "near_minimum_overlap_factor", + "overwrite", + "pagesize", + "point_pool_capacity", + "region_pool_capacity", + "reinsert_factor", + "split_distribution_factor", + "storage", + "tight_mbr", + "tpr_horizon", + "type", + "variant", + "writethrough", + ) - def __init__(self, handle=None, owned=True, **kwargs): + def __init__(self, handle=None, owned: bool = True, **kwargs: Any) -> None: if handle is None: handle = PropertyHandle() self.handle = handle self.initialize_from_dict(kwargs) - def initialize_from_dict(self, state): + def initialize_from_dict(self, state: Dict[str, Any]) -> None: for k, v in state.items(): if v is not None: setattr(self, k, v) - def __getstate__(self): + def __getstate__(self) -> Dict[Any, Any]: return self.as_dict() def __setstate__(self, state): self.handle = PropertyHandle() self.initialize_from_dict(state) - def as_dict(self): + def as_dict(self) -> Dict[str, Any]: d = {} for k in self.pkeys: try: v = getattr(self, k) - except core.RTreeError: + except RTreeError: v = None d[k] = v return d - def __repr__(self): + def __repr__(self) -> str: return repr(self.as_dict()) - def __str__(self): + def __str__(self) -> str: return pprint.pformat(self.as_dict()) - def get_index_type(self): + def get_index_type(self) -> int: return core.rt.IndexProperty_GetIndexType(self.handle) - def set_index_type(self, value): + def set_index_type(self, value: int) -> None: return core.rt.IndexProperty_SetIndexType(self.handle, value) type = property(get_index_type, set_index_type) @@ -1326,33 +1545,32 @@ :data:`RT_RTree`, :data:`RT_MVTree`, or :data:`RT_TPRTree`. Only RT_RTree (the default) is practically supported at this time.""" - def get_variant(self): + def get_variant(self) -> int: return core.rt.IndexProperty_GetIndexVariant(self.handle) - def set_variant(self, value): + def set_variant(self, value: int) -> None: return core.rt.IndexProperty_SetIndexVariant(self.handle, value) variant = property(get_variant, set_variant) """Index variant. Valid index variant values are :data:`RT_Linear`, :data:`RT_Quadratic`, and :data:`RT_Star`""" - def get_dimension(self): + def get_dimension(self) -> int: return core.rt.IndexProperty_GetDimension(self.handle) - def set_dimension(self, value): - if (value <= 0): - raise core.RTreeError( - "Negative or 0 dimensional indexes are not allowed") + def set_dimension(self, value: int) -> None: + if value <= 0: + raise RTreeError("Negative or 0 dimensional indexes are not allowed") return core.rt.IndexProperty_SetDimension(self.handle, value) dimension = property(get_dimension, set_dimension) """Index dimension. Must be greater than 0, though a dimension of 1 might have undefined behavior.""" - def get_storage(self): + def get_storage(self) -> int: return core.rt.IndexProperty_GetIndexStorage(self.handle) - def set_storage(self, value): + def set_storage(self, value: int) -> None: return core.rt.IndexProperty_SetIndexStorage(self.handle, value) storage = property(get_storage, set_storage) @@ -1365,86 +1583,82 @@ :data:`RT_Custom` is assumed. Otherwise, :data:`RT_Memory` is the default. """ - def get_pagesize(self): + def get_pagesize(self) -> int: return core.rt.IndexProperty_GetPagesize(self.handle) - def set_pagesize(self, value): - if (value <= 0): - raise core.RTreeError("Pagesize must be > 0") + def set_pagesize(self, value: int) -> None: + if value <= 0: + raise RTreeError("Pagesize must be > 0") return core.rt.IndexProperty_SetPagesize(self.handle, value) pagesize = property(get_pagesize, set_pagesize) """The pagesize when disk storage is used. It is ideal to ensure that your index entries fit within a single page for best performance.""" - def get_index_capacity(self): + def get_index_capacity(self) -> int: return core.rt.IndexProperty_GetIndexCapacity(self.handle) - def set_index_capacity(self, value): - if (value <= 0): - raise core.RTreeError("index_capacity must be > 0") + def set_index_capacity(self, value: int) -> None: + if value <= 0: + raise RTreeError("index_capacity must be > 0") return core.rt.IndexProperty_SetIndexCapacity(self.handle, value) index_capacity = property(get_index_capacity, set_index_capacity) """Index capacity""" - def get_leaf_capacity(self): + def get_leaf_capacity(self) -> int: return core.rt.IndexProperty_GetLeafCapacity(self.handle) - def set_leaf_capacity(self, value): - if (value <= 0): - raise core.RTreeError("leaf_capacity must be > 0") + def set_leaf_capacity(self, value: int) -> None: + if value <= 0: + raise RTreeError("leaf_capacity must be > 0") return core.rt.IndexProperty_SetLeafCapacity(self.handle, value) leaf_capacity = property(get_leaf_capacity, set_leaf_capacity) """Leaf capacity""" - def get_index_pool_capacity(self): + def get_index_pool_capacity(self) -> int: return core.rt.IndexProperty_GetIndexPoolCapacity(self.handle) - def set_index_pool_capacity(self, value): - if (value <= 0): - raise core.RTreeError("index_pool_capacity must be > 0") + def set_index_pool_capacity(self, value: int) -> None: + if value <= 0: + raise RTreeError("index_pool_capacity must be > 0") return core.rt.IndexProperty_SetIndexPoolCapacity(self.handle, value) - index_pool_capacity = property( - get_index_pool_capacity, set_index_pool_capacity) + index_pool_capacity = property(get_index_pool_capacity, set_index_pool_capacity) """Index pool capacity""" - def get_point_pool_capacity(self): + def get_point_pool_capacity(self) -> int: return core.rt.IndexProperty_GetPointPoolCapacity(self.handle) - def set_point_pool_capacity(self, value): - if (value <= 0): - raise core.RTreeError("point_pool_capacity must be > 0") + def set_point_pool_capacity(self, value: int) -> None: + if value <= 0: + raise RTreeError("point_pool_capacity must be > 0") return core.rt.IndexProperty_SetPointPoolCapacity(self.handle, value) - point_pool_capacity = property( - get_point_pool_capacity, set_point_pool_capacity) + point_pool_capacity = property(get_point_pool_capacity, set_point_pool_capacity) """Point pool capacity""" - def get_region_pool_capacity(self): + def get_region_pool_capacity(self) -> int: return core.rt.IndexProperty_GetRegionPoolCapacity(self.handle) - def set_region_pool_capacity(self, value): - if (value <= 0): - raise core.RTreeError("region_pool_capacity must be > 0") + def set_region_pool_capacity(self, value: int) -> None: + if value <= 0: + raise RTreeError("region_pool_capacity must be > 0") return core.rt.IndexProperty_SetRegionPoolCapacity(self.handle, value) - region_pool_capacity = property( - get_region_pool_capacity, set_region_pool_capacity) + region_pool_capacity = property(get_region_pool_capacity, set_region_pool_capacity) """Region pool capacity""" - def get_buffering_capacity(self): + def get_buffering_capacity(self) -> int: return core.rt.IndexProperty_GetBufferingCapacity(self.handle) - def set_buffering_capacity(self, value): - if (value <= 0): - raise core.RTreeError("buffering_capacity must be > 0") + def set_buffering_capacity(self, value: int) -> None: + if value <= 0: + raise RTreeError("buffering_capacity must be > 0") return core.rt.IndexProperty_SetBufferingCapacity(self.handle, value) - buffering_capacity = property( - get_buffering_capacity, set_buffering_capacity) + buffering_capacity = property(get_buffering_capacity, set_buffering_capacity) """Buffering capacity""" def get_tight_mbr(self): @@ -1452,8 +1666,7 @@ def set_tight_mbr(self, value): value = bool(value) - return bool( - core.rt.IndexProperty_SetEnsureTightMBRs(self.handle, value)) + return bool(core.rt.IndexProperty_SetEnsureTightMBRs(self.handle, value)) tight_mbr = property(get_tight_mbr, set_tight_mbr) """Uses tight bounding rectangles""" @@ -1468,17 +1681,17 @@ overwrite = property(get_overwrite, set_overwrite) """Overwrite existing index files""" - def get_near_minimum_overlap_factor(self): + def get_near_minimum_overlap_factor(self) -> int: return core.rt.IndexProperty_GetNearMinimumOverlapFactor(self.handle) - def set_near_minimum_overlap_factor(self, value): - if (value <= 0): - raise core.RTreeError("near_minimum_overlap_factor must be > 0") - return core.rt.IndexProperty_SetNearMinimumOverlapFactor( - self.handle, value) + def set_near_minimum_overlap_factor(self, value: int) -> None: + if value <= 0: + raise RTreeError("near_minimum_overlap_factor must be > 0") + return core.rt.IndexProperty_SetNearMinimumOverlapFactor(self.handle, value) near_minimum_overlap_factor = property( - get_near_minimum_overlap_factor, set_near_minimum_overlap_factor) + get_near_minimum_overlap_factor, set_near_minimum_overlap_factor + ) """Overlap factor for MVRTrees""" def get_writethrough(self): @@ -1491,24 +1704,24 @@ writethrough = property(get_writethrough, set_writethrough) """Write through caching""" - def get_fill_factor(self): + def get_fill_factor(self) -> int: return core.rt.IndexProperty_GetFillFactor(self.handle) - def set_fill_factor(self, value): + def set_fill_factor(self, value: int) -> None: return core.rt.IndexProperty_SetFillFactor(self.handle, value) fill_factor = property(get_fill_factor, set_fill_factor) """Index node fill factor before branching""" - def get_split_distribution_factor(self): + def get_split_distribution_factor(self) -> int: return core.rt.IndexProperty_GetSplitDistributionFactor(self.handle) - def set_split_distribution_factor(self, value): - return core.rt.IndexProperty_SetSplitDistributionFactor( - self.handle, value) + def set_split_distribution_factor(self, value: int) -> None: + return core.rt.IndexProperty_SetSplitDistributionFactor(self.handle, value) split_distribution_factor = property( - get_split_distribution_factor, set_split_distribution_factor) + get_split_distribution_factor, set_split_distribution_factor + ) """Split distribution factor""" def get_tpr_horizon(self): @@ -1534,7 +1747,7 @@ def set_filename(self, value): if isinstance(value, str): - value = value.encode('utf-8') + value = value.encode("utf-8") return core.rt.IndexProperty_SetFileName(self.handle, value) filename = property(get_filename, set_filename) @@ -1546,9 +1759,8 @@ def set_dat_extension(self, value): if isinstance(value, str): - value = value.encode('utf-8') - return core.rt.IndexProperty_SetFileNameExtensionDat( - self.handle, value) + value = value.encode("utf-8") + return core.rt.IndexProperty_SetFileNameExtensionDat(self.handle, value) dat_extension = property(get_dat_extension, set_dat_extension) """Extension for .dat file""" @@ -1559,33 +1771,32 @@ def set_idx_extension(self, value): if isinstance(value, str): - value = value.encode('utf-8') - return core.rt.IndexProperty_SetFileNameExtensionIdx( - self.handle, value) + value = value.encode("utf-8") + return core.rt.IndexProperty_SetFileNameExtensionIdx(self.handle, value) idx_extension = property(get_idx_extension, set_idx_extension) """Extension for .idx file""" - def get_custom_storage_callbacks_size(self): + def get_custom_storage_callbacks_size(self) -> int: return core.rt.IndexProperty_GetCustomStorageCallbacksSize(self.handle) - def set_custom_storage_callbacks_size(self, value): - return core.rt.IndexProperty_SetCustomStorageCallbacksSize( - self.handle, value) + def set_custom_storage_callbacks_size(self, value: int) -> None: + return core.rt.IndexProperty_SetCustomStorageCallbacksSize(self.handle, value) custom_storage_callbacks_size = property( - get_custom_storage_callbacks_size, set_custom_storage_callbacks_size) + get_custom_storage_callbacks_size, set_custom_storage_callbacks_size + ) """Size of callbacks for custom storage""" def get_custom_storage_callbacks(self): return core.rt.IndexProperty_GetCustomStorageCallbacks(self.handle) def set_custom_storage_callbacks(self, value): - return core.rt.IndexProperty_SetCustomStorageCallbacks( - self.handle, value) + return core.rt.IndexProperty_SetCustomStorageCallbacks(self.handle, value) custom_storage_callbacks = property( - get_custom_storage_callbacks, set_custom_storage_callbacks) + get_custom_storage_callbacks, set_custom_storage_callbacks + ) """Callbacks for custom storage""" def get_index_id(self): @@ -1606,45 +1817,68 @@ class CustomStorageCallbacks(ctypes.Structure): # callback types createCallbackType = ctypes.CFUNCTYPE( - None, ctypes.c_void_p, ctypes.POINTER(ctypes.c_int)) + None, ctypes.c_void_p, ctypes.POINTER(ctypes.c_int) + ) destroyCallbackType = ctypes.CFUNCTYPE( - None, ctypes.c_void_p, ctypes.POINTER(ctypes.c_int)) + None, ctypes.c_void_p, ctypes.POINTER(ctypes.c_int) + ) flushCallbackType = ctypes.CFUNCTYPE( - None, ctypes.c_void_p, ctypes.POINTER(ctypes.c_int)) + None, ctypes.c_void_p, ctypes.POINTER(ctypes.c_int) + ) loadCallbackType = ctypes.CFUNCTYPE( - None, ctypes.c_void_p, id_type, ctypes.POINTER(ctypes.c_uint32), + None, + ctypes.c_void_p, + id_type, + ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.POINTER(ctypes.c_uint8)), - ctypes.POINTER(ctypes.c_int)) + ctypes.POINTER(ctypes.c_int), + ) storeCallbackType = ctypes.CFUNCTYPE( - None, ctypes.c_void_p, ctypes.POINTER(id_type), ctypes.c_uint32, - ctypes.POINTER(ctypes.c_uint8), ctypes.POINTER(ctypes.c_int)) + None, + ctypes.c_void_p, + ctypes.POINTER(id_type), + ctypes.c_uint32, + ctypes.POINTER(ctypes.c_uint8), + ctypes.POINTER(ctypes.c_int), + ) deleteCallbackType = ctypes.CFUNCTYPE( - None, ctypes.c_void_p, id_type, ctypes.POINTER(ctypes.c_int)) + None, ctypes.c_void_p, id_type, ctypes.POINTER(ctypes.c_int) + ) - _fields_ = [('context', ctypes.c_void_p), - ('createCallback', createCallbackType), - ('destroyCallback', destroyCallbackType), - ('flushCallback', flushCallbackType), - ('loadCallback', loadCallbackType), - ('storeCallback', storeCallbackType), - ('deleteCallback', deleteCallbackType), - ] - - def __init__(self, context, createCallback, destroyCallback, - flushCallback, loadCallback, storeCallback, deleteCallback): - ctypes.Structure.__init__(self, - ctypes.c_void_p(context), - self.createCallbackType(createCallback), - self.destroyCallbackType(destroyCallback), - self.flushCallbackType(flushCallback), - self.loadCallbackType(loadCallback), - self.storeCallbackType(storeCallback), - self.deleteCallbackType(deleteCallback), - ) + _fields_ = [ + ("context", ctypes.c_void_p), + ("createCallback", createCallbackType), + ("destroyCallback", destroyCallbackType), + ("flushCallback", flushCallbackType), + ("loadCallback", loadCallbackType), + ("storeCallback", storeCallbackType), + ("deleteCallback", deleteCallbackType), + ] + + def __init__( + self, + context, + createCallback, + destroyCallback, + flushCallback, + loadCallback, + storeCallback, + deleteCallback, + ) -> None: + ctypes.Structure.__init__( + self, + ctypes.c_void_p(context), + self.createCallbackType(createCallback), + self.destroyCallbackType(destroyCallback), + self.flushCallbackType(flushCallback), + self.loadCallbackType(loadCallback), + self.storeCallbackType(storeCallback), + self.deleteCallbackType(deleteCallback), + ) -class ICustomStorage(object): +class ICustomStorage: # error codes NoError = 0 InvalidPageError = 1 @@ -1664,7 +1898,7 @@ raise NotImplementedError() hasData = property(lambda self: False) - '''Override this property to allow for reloadable storages''' + """Override this property to allow for reloadable storages""" class CustomStorageBase(ICustomStorage): @@ -1673,12 +1907,19 @@ def registerCallbacks(self, properties): callbacks = CustomStorageCallbacks( - ctypes.c_void_p(), self.create, self.destroy, self.flush, - self.loadByteArray, self.storeByteArray, self.deleteByteArray) + ctypes.c_void_p(), + self.create, + self.destroy, + self.flush, + self.loadByteArray, + self.storeByteArray, + self.deleteByteArray, + ) properties.custom_storage_callbacks_size = ctypes.sizeof(callbacks) self.callbacks = callbacks - properties.custom_storage_callbacks = \ - ctypes.cast(ctypes.pointer(callbacks), ctypes.c_void_p) + properties.custom_storage_callbacks = ctypes.cast( + ctypes.pointer(callbacks), ctypes.c_void_p + ) # the user must override these callback functions def create(self, context, returnError): @@ -1719,12 +1960,19 @@ def registerCallbacks(self, properties): callbacks = CustomStorageCallbacks( - 0, self._create, self._destroy, self._flush, self._loadByteArray, - self._storeByteArray, self._deleteByteArray) + 0, + self._create, + self._destroy, + self._flush, + self._loadByteArray, + self._storeByteArray, + self._deleteByteArray, + ) properties.custom_storage_callbacks_size = ctypes.sizeof(callbacks) self.callbacks = callbacks - properties.custom_storage_callbacks = \ - ctypes.cast(ctypes.pointer(callbacks), ctypes.c_void_p) + properties.custom_storage_callbacks = ctypes.cast( + ctypes.pointer(callbacks), ctypes.c_void_p + ) # these functions handle the C callbacks and massage the data, then # delegate to the function without underscore below @@ -1737,8 +1985,7 @@ def _flush(self, context, returnError): self.flush(returnError) - def _loadByteArray(self, context, page, resultLen, - resultData, returnError): + def _loadByteArray(self, context, page, resultLen, resultData, returnError): resultString = self.loadByteArray(page, returnError) if returnError.contents.value != self.NoError: return @@ -1782,7 +2029,7 @@ """Must be overridden. Must return a string with the loaded data.""" returnError.contents.value = self.IllegalStateError raise NotImplementedError("You must override this method.") - return '' + return "" def storeByteArray(self, page, data, returnError): """Must be overridden. Must return the new 64-bit page ID of the stored @@ -1800,7 +2047,7 @@ class RtreeContainer(Rtree): """An R-Tree, MVR-Tree, or TPR-Tree indexed container for python objects""" - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any) -> None: """Creates a new index :param stream: @@ -1825,9 +2072,8 @@ This parameter determines the coordinate order for all methods that take in coordinates. - :param properties: An :class:`index.Property` object - This object sets both the creation and instantiation properties - for the object and they are passed down into libspatialindex. + :param properties: This object sets both the creation and instantiation + properties for the object and they are passed down into libspatialindex. A few properties are curried from instantiation parameters for you like ``pagesize`` to ensure compatibility with previous versions of the library. All other properties must be set on the @@ -1872,41 +2118,40 @@ [34.37768294..., 26.73758537..., 49.37768294..., 41.73758537...] """ if args: - if isinstance(args[0], str) \ - or isinstance(args[0], bytes) \ - or isinstance(args[0], ICustomStorage): - raise ValueError('%s supports only in-memory indexes' - % self.__class__) - self._objects = {} - return super(RtreeContainer, self).__init__(*args, **kwargs) + if ( + isinstance(args[0], str) + or isinstance(args[0], bytes) + or isinstance(args[0], ICustomStorage) + ): + raise ValueError("%s supports only in-memory indexes" % self.__class__) + self._objects: Dict[int, Tuple[int, object]] = {} + return super().__init__(*args, **kwargs) - def get_size(self): + def get_size(self) -> int: try: return self.count(self.bounds) - except core.RTreeError: + except RTreeError: return 0 - def __repr__(self): - m = 'rtree.index.RtreeContainer(bounds={}, size={})' + def __repr__(self) -> str: + m = "rtree.index.RtreeContainer(bounds={}, size={})" return m.format(self.bounds, self.get_size()) - def __contains__(self, obj): + def __contains__(self, obj: object) -> bool: return id(obj) in self._objects - def __len__(self): + def __len__(self) -> int: return sum(count for count, obj in self._objects.values()) - def __iter__(self): + def __iter__(self) -> Iterator[object]: return iter(obj for count, obj in self._objects.values()) - def insert(self, obj, coordinates): + def insert(self, obj: object, coordinates: Any) -> None: # type: ignore[override] """Inserts an item into the index with the given coordinates. - :param obj: object - Any object. + :param obj: Any object. - :param coordinates: sequence or array - This may be an object that satisfies the numpy array + :param coordinates: This may be an object that satisfies the numpy array protocol, providing the index's dimension * 2 coordinate pairs representing the `mink` and `maxk` coordinates in each dimension defining the bounds of the query window. @@ -1940,16 +2185,27 @@ except KeyError: count = 1 self._objects[id(obj)] = (count, obj) - return super(RtreeContainer, self).insert(id(obj), coordinates, None) + return super().insert(id(obj), coordinates, None) - add = insert + add = insert # type: ignore[assignment] - def intersection(self, coordinates, bbox=False): + @overload # type: ignore[override] + def intersection(self, coordinates: Any, bbox: Literal[True]) -> Iterator[Item]: + ... + + @overload + def intersection( + self, coordinates: Any, bbox: Literal[False] = False + ) -> Iterator[object]: + ... + + def intersection( + self, coordinates: Any, bbox: bool = False + ) -> Iterator[Union[Item, object]]: """Return ids or objects in the index that intersect the given coordinates. - :param coordinates: sequence or array - This may be an object that satisfies the numpy array + :param coordinates: This may be an object that satisfies the numpy array protocol, providing the index's dimension * 2 coordinate pairs representing the `mink` and `maxk` coordinates in each dimension defining the bounds of the query window. @@ -1958,8 +2214,7 @@ velocity pairs `minvk` and `maxvk` and a time pair for the time range as a float. - :param bbox: True or False - If True, the intersection method will return the stored objects, + :param bbox: If True, the intersection method will return the stored objects, as well as the bounds of the entry. The following example queries the container for any stored objects that @@ -2003,25 +2258,35 @@ """ if bbox is False: - for id in super(RtreeContainer, - self).intersection(coordinates, bbox): + for id in super().intersection(coordinates, bbox): yield self._objects[id][1] elif bbox is True: - for value in super(RtreeContainer, - self).intersection(coordinates, bbox): + for value in super().intersection(coordinates, bbox): value.object = self._objects[value.id][1] value.id = None yield value else: - raise ValueError( - "valid values for the bbox argument are True and False") + raise ValueError("valid values for the bbox argument are True and False") - def nearest(self, coordinates, num_results=1, bbox=False): + @overload # type: ignore[override] + def nearest( + self, coordinates: Any, num_results: int = 1, bbox: Literal[True] = True + ) -> Iterator[Item]: + ... + + @overload + def nearest( + self, coordinates: Any, num_results: int = 1, bbox: Literal[False] = False + ) -> Iterator[object]: + ... + + def nearest( + self, coordinates: Any, num_results: int = 1, bbox: bool = False + ) -> Iterator[Union[Item, object]]: """Returns the ``k``-nearest objects to the given coordinates in increasing distance order. - :param coordinates: sequence or array - This may be an object that satisfies the numpy array + :param coordinates: This may be an object that satisfies the numpy array protocol, providing the index's dimension * 2 coordinate pairs representing the `mink` and `maxk` coordinates in each dimension defining the bounds of the query window. @@ -2030,14 +2295,12 @@ velocity pairs `minvk` and `maxvk` and a time pair for the time range as a float. - :param num_results: integer - The number of results to return nearest to the given coordinates. - If two entries are equidistant, *both* are returned. + :param num_results: The number of results to return nearest to the given + coordinates. If two entries are equidistant, *both* are returned. This property means that :attr:`num_results` may return more items than specified. - :param bbox: True or False - If True, the nearest method will return the stored objects, as + :param bbox: If True, the nearest method will return the stored objects, as well as the bounds of the entry. .. warning:: @@ -2051,28 +2314,23 @@ >>> hits = idx.nearest((0, 0, 10, 10), 3, bbox=True) """ if bbox is False: - for id in super(RtreeContainer, - self).nearest(coordinates, num_results, bbox): + for id in super().nearest(coordinates, num_results, bbox): yield self._objects[id][1] elif bbox is True: - for value in super(RtreeContainer, - self).nearest(coordinates, num_results, bbox): + for value in super().nearest(coordinates, num_results, bbox): value.object = self._objects[value.id][1] value.id = None yield value else: - raise ValueError( - "valid values for the bbox argument are True and False") + raise ValueError("valid values for the bbox argument are True and False") - def delete(self, obj, coordinates): + def delete(self, obj: object, coordinates: Any) -> None: """Deletes the item from the container within the specified coordinates. - :param obj: object - Any object. + :param obj: Any object. - :param coordinates: sequence or array - Dimension * 2 coordinate pairs, representing the min + :param coordinates: Dimension * 2 coordinate pairs, representing the min and max coordinates in each dimension of the item to be deleted from the index. Their ordering will depend on the index's :attr:`interleaved` data member. @@ -2114,15 +2372,19 @@ try: count = self._objects[id(obj)][0] - 1 except KeyError: - raise IndexError('object is not in the index') + raise IndexError("object is not in the index") if count == 0: del self._objects[id(obj)] else: self._objects[id(obj)] = (count, obj) - return super(RtreeContainer, self).delete(id(obj), coordinates) + return super().delete(id(obj), coordinates) def leaves(self): - return [(self._objects[id][1], [self._objects[child_id][1] - for child_id in child_ids], bounds) - for id, child_ids, bounds - in super(RtreeContainer, self).leaves()] + return [ + ( + self._objects[id][1], + [self._objects[child_id][1] for child_id in child_ids], + bounds, + ) + for id, child_ids, bounds in super(RtreeContainer, self).leaves() + ] diff -Nru python-rtree-0.9.7/rtree/__init__.py python-rtree-1.0.0/rtree/__init__.py --- python-rtree-0.9.7/rtree/__init__.py 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/rtree/__init__.py 2022-04-04 21:43:52.000000000 +0000 @@ -4,6 +4,6 @@ Rtree provides Python bindings to libspatialindex for quick hyperrectangular intersection queries. """ -__version__ = '0.9.7' +__version__ = "1.0.0" -from .index import Rtree, Index # noqa +from .index import Index, Rtree # noqa diff -Nru python-rtree-0.9.7/scripts/visualize.py python-rtree-1.0.0/scripts/visualize.py --- python-rtree-0.9.7/scripts/visualize.py 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/scripts/visualize.py 2022-04-04 21:43:52.000000000 +0000 @@ -1,10 +1,11 @@ #!/usr/bin/env python -from liblas import file import sys -from rtree import index -import ogr +from liblas import file +from osgeo import ogr + +from rtree import index def quick_create_layer_def(lyr, field_list): @@ -31,34 +32,23 @@ field_defn.Destroy() -shape_drv = ogr.GetDriverByName('ESRI Shapefile') +shape_drv = ogr.GetDriverByName("ESRI Shapefile") -shapefile_name = sys.argv[1].split('.')[0] +shapefile_name = sys.argv[1].split(".")[0] shape_ds = shape_drv.CreateDataSource(shapefile_name) -leaf_block_lyr = shape_ds.CreateLayer('leaf', geom_type=ogr.wkbPolygon) -point_block_lyr = shape_ds.CreateLayer('point', geom_type=ogr.wkbPolygon) -point_lyr = shape_ds.CreateLayer('points', geom_type=ogr.wkbPoint) +leaf_block_lyr = shape_ds.CreateLayer("leaf", geom_type=ogr.wkbPolygon) +point_block_lyr = shape_ds.CreateLayer("point", geom_type=ogr.wkbPolygon) +point_lyr = shape_ds.CreateLayer("points", geom_type=ogr.wkbPoint) quick_create_layer_def( - leaf_block_lyr, - [ - ('BLK_ID', ogr.OFTInteger), - ('COUNT', ogr.OFTInteger), - ]) + leaf_block_lyr, [("BLK_ID", ogr.OFTInteger), ("COUNT", ogr.OFTInteger)] +) quick_create_layer_def( - point_block_lyr, - [ - ('BLK_ID', ogr.OFTInteger), - ('COUNT', ogr.OFTInteger), - ]) + point_block_lyr, [("BLK_ID", ogr.OFTInteger), ("COUNT", ogr.OFTInteger)] +) -quick_create_layer_def( - point_lyr, - [ - ('ID', ogr.OFTInteger), - ('BLK_ID', ogr.OFTInteger), - ]) +quick_create_layer_def(point_lyr, [("ID", ogr.OFTInteger), ("BLK_ID", ogr.OFTInteger)]) p = index.Property() p.filename = sys.argv[1] @@ -98,10 +88,10 @@ miny = min(miny, p.y) maxy = max(maxy, p.y) feature = ogr.Feature(feature_def=point_lyr.GetLayerDefn()) - g = ogr.CreateGeometryFromWkt('POINT (%.8f %.8f)' % (p.x, p.y)) + g = ogr.CreateGeometryFromWkt(f"POINT ({p.x:.8f} {p.y:.8f})") feature.SetGeometry(g) - feature.SetField('ID', p_id) - feature.SetField('BLK_ID', block_id) + feature.SetField("ID", p_id) + feature.SetField("BLK_ID", block_id) result = point_lyr.CreateFeature(feature) del result @@ -109,8 +99,10 @@ def make_poly(minx, miny, maxx, maxy): - wkt = 'POLYGON ((%.8f %.8f, %.8f %.8f, %.8f %.8f, %.8f %.8f, %.8f %.8f))'\ - % (minx, miny, maxx, miny, maxx, maxy, minx, maxy, minx, miny) + wkt = ( + f"POLYGON (({minx:.8f} {miny:.8f}, {maxx:.8f} {miny:.8f}, {maxx:.8f} " + f"{maxy:.8f}, {minx:.8f} {maxy:.8f}, {minx:.8f} {miny:.8f}))" + ) shp = ogr.CreateGeometryFromWkt(wkt) return shp @@ -118,8 +110,8 @@ def make_feature(lyr, geom, id, count): feature = ogr.Feature(feature_def=lyr.GetLayerDefn()) feature.SetGeometry(geom) - feature.SetField('BLK_ID', id) - feature.SetField('COUNT', count) + feature.SetField("BLK_ID", id) + feature.SetField("COUNT", count) result = lyr.CreateFeature(feature) del result @@ -141,17 +133,15 @@ print(leaf[2]) leaf = make_poly(minx, miny, maxx, maxy) - print('leaf: ' + str([minx, miny, maxx, maxy])) + print("leaf: " + str([minx, miny, maxx, maxy])) pminx, pminy, pmaxx, pmaxy = get_bounds(ids, f, id) point = make_poly(pminx, pminy, pmaxx, pmaxy) - print('point: ' + str([pminx, pminy, pmaxx, pmaxy])) - print('point bounds: ' + - str([point.GetArea(), area(pminx, pminy, pmaxx, pmaxy)])) - print('leaf bounds: ' + - str([leaf.GetArea(), area(minx, miny, maxx, maxy)])) - print('leaf - point: ' + str([abs(point.GetArea() - leaf.GetArea())])) + print("point: " + str([pminx, pminy, pmaxx, pmaxy])) + print("point bounds: " + str([point.GetArea(), area(pminx, pminy, pmaxx, pmaxy)])) + print("leaf bounds: " + str([leaf.GetArea(), area(minx, miny, maxx, maxy)])) + print("leaf - point: " + str([abs(point.GetArea() - leaf.GetArea())])) print([minx, miny, maxx, maxy]) # if shp2.GetArea() != shp.GetArea(): # import pdb;pdb.set_trace() diff -Nru python-rtree-0.9.7/setup.cfg python-rtree-1.0.0/setup.cfg --- python-rtree-0.9.7/setup.cfg 1970-01-01 00:00:00.000000000 +0000 +++ python-rtree-1.0.0/setup.cfg 2022-04-04 21:43:52.000000000 +0000 @@ -0,0 +1,42 @@ +[metadata] +name = Rtree +version = attr: rtree.__version__ +url = https://github.com/Toblerity/rtree +author = Sean Gillies +author_email = sean.gillies@gmail.com +maintainer = Howard Butler +maintainer_email = howard@hobu.co +classifiers = + Development Status :: 5 - Production/Stable + Intended Audience :: Developers + Intended Audience :: Science/Research + License :: OSI Approved :: MIT License + Operating System :: OS Independent + Programming Language :: C + Programming Language :: C++ + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 + Topic :: Scientific/Engineering :: GIS + Topic :: Database +license = MIT +description = R-Tree spatial index for Python GIS +long_description = file: README.md +long_description_content_type = text/markdown +keywords = gis, spatial, index, r-tree + +[options] +zip_safe = False +python_requires = >=3.7 +install_requires = + typing_extensions>=3.7;python_version<'3.8' +include_package_date = True +packages = rtree + +[options.package_data] +rtree = lib + +[flake8] +max-line-length = 88 diff -Nru python-rtree-0.9.7/setup.py python-rtree-1.0.0/setup.py --- python-rtree-0.9.7/setup.py 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/setup.py 2022-04-04 21:43:52.000000000 +0000 @@ -1,41 +1,30 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import os -import sys from setuptools import setup -from setuptools.dist import Distribution from setuptools.command.install import install - +from setuptools.dist import Distribution from wheel.bdist_wheel import bdist_wheel as _bdist_wheel - -# Get text from README.txt -with open('docs/source/README.txt', 'r') as fp: - readme_text = fp.read() - -# Get __version without importing -with open('rtree/__init__.py', 'r') as fp: - # get and exec just the line which looks like "__version__ = '0.9.4'" - exec(next(line for line in fp if '__version__' in line)) - # current working directory of this setup.py file _cwd = os.path.abspath(os.path.split(__file__)[0]) -class bdist_wheel(_bdist_wheel): - def finalize_options(self): +class bdist_wheel(_bdist_wheel): # type: ignore[misc] + def finalize_options(self) -> None: _bdist_wheel.finalize_options(self) self.root_is_pure = False -class BinaryDistribution(Distribution): +class BinaryDistribution(Distribution): # type: ignore[misc] """Distribution which always forces a binary package with platform name""" - def has_ext_modules(foo): + + def has_ext_modules(foo) -> bool: return True -class InstallPlatlib(install): - def finalize_options(self): +class InstallPlatlib(install): # type: ignore[misc] + def finalize_options(self) -> None: """ Copy the shared libraries into the wheel. Note that this will *only* check in `rtree/lib` rather than anywhere on @@ -52,14 +41,12 @@ # get the location of the shared library on the filesystem # where we're putting the shared library in the build directory - target_dir = os.path.join(self.build_lib, 'rtree', 'lib') + target_dir = os.path.join(self.build_lib, "rtree", "lib") # where are we checking for shared libraries - source_dir = os.path.join(_cwd, 'rtree', 'lib') + source_dir = os.path.join(_cwd, "rtree", "lib") # what patterns represent shared libraries - patterns = {'*.so', - 'libspatialindex*dylib', - '*.dll'} + patterns = {"*.so", "libspatialindex*dylib", "*.dll"} if not os.path.isdir(source_dir): # no copying of binary parts to library @@ -85,38 +72,11 @@ # copy the source file to the target directory self.copy_file( - os.path.join(source_dir, file_name), - os.path.join(target_dir, file_name)) + os.path.join(source_dir, file_name), os.path.join(target_dir, file_name) + ) setup( - name='Rtree', - version=__version__, - description='R-Tree spatial index for Python GIS', - license='MIT', - keywords='gis spatial index r-tree', - author='Sean Gillies', - author_email='sean.gillies@gmail.com', - maintainer='Howard Butler', - maintainer_email='howard@hobu.co', - url='https://github.com/Toblerity/rtree', - long_description=readme_text, - packages=['rtree'], - package_data={"rtree": ['lib']}, - zip_safe=False, - include_package_data=True, distclass=BinaryDistribution, - cmdclass={'bdist_wheel': bdist_wheel, 'install': InstallPlatlib}, - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: MIT License', - 'Operating System :: OS Independent', - 'Programming Language :: C', - 'Programming Language :: C++', - 'Programming Language :: Python', - 'Topic :: Scientific/Engineering :: GIS', - 'Topic :: Database', - ], + cmdclass={"bdist_wheel": bdist_wheel, "install": InstallPlatlib}, ) diff -Nru python-rtree-0.9.7/tests/conftest.py python-rtree-1.0.0/tests/conftest.py --- python-rtree-0.9.7/tests/conftest.py 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/tests/conftest.py 2022-04-04 21:43:52.000000000 +0000 @@ -1,16 +1,15 @@ import os import shutil +from typing import Iterator +import py import pytest +data_files = ["boxes_15x15.data"] -data_files = [ - 'boxes_15x15.data', -] - -@pytest.yield_fixture(autouse=True) -def temporary_working_directory(tmpdir): +@pytest.fixture(autouse=True) +def temporary_working_directory(tmpdir: py.path.local) -> Iterator[None]: for filename in data_files: filename = os.path.join(os.path.dirname(__file__), filename) shutil.copy(filename, str(tmpdir)) diff -Nru python-rtree-0.9.7/tests/test_index.py python-rtree-1.0.0/tests/test_index.py --- python-rtree-0.9.7/tests/test_index.py 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/tests/test_index.py 2022-04-04 21:43:52.000000000 +0000 @@ -1,27 +1,29 @@ +import ctypes +import pickle import sys +import tempfile import unittest -import ctypes -import rtree +from typing import Dict, Iterator, Tuple + import numpy as np import pytest -import tempfile -import pickle - -from rtree import index, core -# is this running on Python 3 -PY3 = sys.version_info.major >= 3 +import rtree +from rtree import core, index +from rtree.exceptions import RTreeError class IndexTestCase(unittest.TestCase): - def setUp(self): - self.boxes15 = np.genfromtxt('boxes_15x15.data') + def setUp(self) -> None: + self.boxes15 = np.genfromtxt("boxes_15x15.data") self.idx = index.Index() for i, coords in enumerate(self.boxes15): self.idx.add(i, coords) - def boxes15_stream(self, interleaved=True): - boxes15 = np.genfromtxt('boxes_15x15.data') + def boxes15_stream( + self, interleaved: bool = True + ) -> Iterator[Tuple[int, Tuple[float, float, float, float], int]]: + boxes15 = np.genfromtxt("boxes_15x15.data") for i, (minx, miny, maxx, maxy) in enumerate(boxes15): if interleaved: @@ -29,70 +31,83 @@ else: yield (i, (minx, maxx, miny, maxy), 42) - def stream_basic(self): + def stream_basic(self) -> None: # some versions of libspatialindex screw up indexes on stream loading # so do a very simple index check rtree_test = rtree.index.Index( [(1564, [0, 0, 0, 10, 10, 10], None)], - properties=rtree.index.Property(dimension=3)) + properties=rtree.index.Property(dimension=3), + ) assert next(rtree_test.intersection([1, 1, 1, 2, 2, 2])) == 1564 class IndexVersion(unittest.TestCase): - - def test_libsidx_version(self): + def test_libsidx_version(self) -> None: self.assertTrue(index.major_version == 1) self.assertTrue(index.minor_version >= 7) -class IndexBounds(unittest.TestCase): +class IndexCount(unittest.TestCase): + def setUp(self) -> None: + self.boxes15 = np.genfromtxt("boxes_15x15.data") + self.idx = index.Index() + for i, coords in enumerate(self.boxes15): + self.idx.add(i, coords) + + def test_len(self) -> None: + self.assertEqual(len(self.idx), len(self.boxes15)) - def test_invalid_specifications(self): + def test_get_size(self) -> None: + with pytest.deprecated_call(): + self.assertEqual(self.idx.get_size(), len(self.boxes15)) + + +class IndexBounds(unittest.TestCase): + def test_invalid_specifications(self) -> None: """Invalid specifications of bounds properly throw""" idx = index.Index() - self.assertRaises(core.RTreeError, idx.add, - None, (0.0, 0.0, -1.0, 1.0)) - self.assertRaises(core.RTreeError, idx.intersection, - (0.0, 0.0, -1.0, 1.0)) - self.assertRaises(ctypes.ArgumentError, idx.add, None, (1, 1,)) + self.assertRaises(RTreeError, idx.add, None, (0.0, 0.0, -1.0, 1.0)) + self.assertRaises(RTreeError, idx.intersection, (0.0, 0.0, -1.0, 1.0)) + self.assertRaises(ctypes.ArgumentError, idx.add, None, (1, 1)) class IndexProperties(IndexTestCase): - @pytest.mark.skipif( - not hasattr(core.rt, 'Index_GetResultSetOffset'), - reason="Index_GetResultsSetOffset required in libspatialindex") - def test_result_offset(self): + not hasattr(core.rt, "Index_GetResultSetOffset"), + reason="Index_GetResultsSetOffset required in libspatialindex", + ) + def test_result_offset(self) -> None: idx = index.Rtree() idx.set_result_offset(3) self.assertEqual(idx.result_offset, 3) @pytest.mark.skipif( - not hasattr(core.rt, 'Index_GetResultSetLimit'), - reason="Index_GetResultsSetOffset required in libspatialindex") - def test_result_limit(self): + not hasattr(core.rt, "Index_GetResultSetLimit"), + reason="Index_GetResultsSetOffset required in libspatialindex", + ) + def test_result_limit(self) -> None: idx = index.Rtree() idx.set_result_limit(44) self.assertEqual(idx.result_limit, 44) - def test_invalid_properties(self): + def test_invalid_properties(self) -> None: """Invalid values are guarded""" p = index.Property() - self.assertRaises(core.RTreeError, p.set_buffering_capacity, -4321) - self.assertRaises(core.RTreeError, p.set_region_pool_capacity, -4321) - self.assertRaises(core.RTreeError, p.set_point_pool_capacity, -4321) - self.assertRaises(core.RTreeError, p.set_index_pool_capacity, -4321) - self.assertRaises(core.RTreeError, p.set_pagesize, -4321) - self.assertRaises(core.RTreeError, p.set_index_capacity, -4321) - self.assertRaises(core.RTreeError, p.set_storage, -4321) - self.assertRaises(core.RTreeError, p.set_variant, -4321) - self.assertRaises(core.RTreeError, p.set_dimension, -2) - self.assertRaises(core.RTreeError, p.set_index_type, 6) - self.assertRaises(core.RTreeError, p.get_index_id) + self.assertRaises(RTreeError, p.set_buffering_capacity, -4321) + self.assertRaises(RTreeError, p.set_region_pool_capacity, -4321) + self.assertRaises(RTreeError, p.set_point_pool_capacity, -4321) + self.assertRaises(RTreeError, p.set_index_pool_capacity, -4321) + self.assertRaises(RTreeError, p.set_pagesize, -4321) + self.assertRaises(RTreeError, p.set_index_capacity, -4321) + self.assertRaises(RTreeError, p.set_storage, -4321) + self.assertRaises(RTreeError, p.set_variant, -4321) + self.assertRaises(RTreeError, p.set_dimension, -2) + self.assertRaises(RTreeError, p.set_index_type, 6) + self.assertRaises(RTreeError, p.get_index_id) - def test_index_properties(self): + def test_index_properties(self) -> None: """Setting index properties returns expected values""" idx = index.Rtree() p = index.Property() @@ -114,8 +129,8 @@ p.writethrough = True p.tpr_horizon = 20.0 p.reinsert_factor = 0.3 - p.idx_extension = 'index' - p.dat_extension = 'data' + p.idx_extension = "index" + p.dat_extension = "data" idx = index.Index(properties=p) @@ -137,21 +152,29 @@ self.assertEqual(props.writethrough, True) self.assertEqual(props.tpr_horizon, 20.0) self.assertEqual(props.reinsert_factor, 0.3) - self.assertEqual(props.idx_extension, 'index') - self.assertEqual(props.dat_extension, 'data') + self.assertEqual(props.idx_extension, "index") + self.assertEqual(props.dat_extension, "data") class TestPickling(unittest.TestCase): - - def test_index(self): + # https://github.com/Toblerity/rtree/issues/87 + @pytest.mark.xfail + def test_index(self) -> None: idx = rtree.index.Index() + idx.insert(0, [0, 1, 2, 3], 4) unpickled = pickle.loads(pickle.dumps(idx)) self.assertNotEqual(idx.handle, unpickled.handle) - self.assertEqual(idx.properties.as_dict(), - unpickled.properties.as_dict()) + self.assertEqual(idx.properties.as_dict(), unpickled.properties.as_dict()) self.assertEqual(idx.interleaved, unpickled.interleaved) + self.assertEqual(len(idx), len(unpickled)) + self.assertEqual(idx.bounds, unpickled.bounds) + a = next(idx.intersection(idx.bounds, objects=True)) + b = next(unpickled.intersection(unpickled.bounds, objects=True)) + self.assertEqual(a.id, b.id) + self.assertEqual(a.bounds, b.bounds) + self.assertEqual(a.object, b.object) - def test_property(self): + def test_property(self) -> None: p = rtree.index.Property() unpickled = pickle.loads(pickle.dumps(p)) self.assertNotEqual(p.handle, unpickled.handle) @@ -159,8 +182,7 @@ class IndexContainer(IndexTestCase): - - def test_container(self): + def test_container(self) -> None: """rtree.index.RtreeContainer works as expected""" container = rtree.index.RtreeContainer() @@ -217,8 +239,7 @@ class IndexIntersection(IndexTestCase): - - def test_intersection(self): + def test_intersection(self) -> None: """Test basic insertion and retrieval""" self.assertTrue(0 in self.idx.intersection((0, 0, 60, 60))) @@ -227,32 +248,24 @@ self.assertTrue(len(hits), 10) self.assertEqual(hits, [0, 4, 16, 27, 35, 40, 47, 50, 76, 80]) - def test_objects(self): + def test_objects(self) -> None: """Test insertion of objects""" idx = index.Index() for i, coords in enumerate(self.boxes15): idx.add(i, coords) idx.insert( - 4321, - (34.3776829412, - 26.7375853734, - 49.3776829412, - 41.7375853734), - obj=42) + 4321, (34.3776829412, 26.7375853734, 49.3776829412, 41.7375853734), obj=42 + ) hits = idx.intersection((0, 0, 60, 60), objects=True) hit = [h for h in hits if h.id == 4321][0] self.assertEqual(hit.id, 4321) self.assertEqual(hit.object, 42) - box = ['%.10f' % t for t in hit.bbox] - expected = [ - '34.3776829412', - '26.7375853734', - '49.3776829412', - '41.7375853734'] + box = ["%.10f" % t for t in hit.bbox] + expected = ["34.3776829412", "26.7375853734", "49.3776829412", "41.7375853734"] self.assertEqual(box, expected) - def test_double_insertion(self): + def test_double_insertion(self) -> None: """Inserting the same id twice does not overwrite data""" idx = index.Index() idx.add(1, (2, 2)) @@ -261,34 +274,143 @@ self.assertEqual([1, 1], list(idx.intersection((0, 0, 5, 5)))) -class IndexSerialization(unittest.TestCase): +class TestIndexIntersectionUnion: + @pytest.fixture(scope="class") + def index_a_interleaved(self) -> index.Index: + idx = index.Index(interleaved=True) + idx.insert(1, (3, 3, 5, 5), "a_1") + idx.insert(2, (4, 2, 6, 4), "a_2") + return idx + + @pytest.fixture(scope="class") + def index_a_uninterleaved(self) -> index.Index: + idx = index.Index(interleaved=False) + idx.insert(1, (3, 5, 3, 5), "a_1") + idx.insert(2, (4, 6, 2, 4), "a_2") + return idx + + @pytest.fixture(scope="class") + def index_b_interleaved(self) -> index.Index: + idx = index.Index(interleaved=True) + idx.insert(3, (2, 1, 7, 6), "b_3") + idx.insert(4, (8, 7, 9, 8), "b_4") + return idx + + @pytest.fixture(scope="class") + def index_b_uninterleaved(self) -> index.Index: + idx = index.Index(interleaved=False) + idx.insert(3, (2, 7, 1, 6), "b_3") + idx.insert(4, (8, 9, 7, 8), "b_4") + return idx + + def test_intersection_interleaved( + self, index_a_interleaved: index.Index, index_b_interleaved: index.Index + ) -> None: + index_c_interleaved = index_a_interleaved & index_b_interleaved + assert index_c_interleaved.interleaved + assert len(index_c_interleaved) == 2 + for hit in index_c_interleaved.intersection( + index_c_interleaved.bounds, objects=True + ): + if hit.bbox == [3.0, 3.0, 5.0, 5.0]: + assert hit.object == ("a_1", "b_3") + elif hit.bbox == [4.0, 2.0, 6.0, 4.0]: + assert hit.object == ("a_2", "b_3") + else: + assert False + + def test_intersection_uninterleaved( + self, index_a_uninterleaved: index.Index, index_b_uninterleaved: index.Index + ) -> None: + index_c_uninterleaved = index_a_uninterleaved & index_b_uninterleaved + assert not index_c_uninterleaved.interleaved + assert len(index_c_uninterleaved) == 2 + for hit in index_c_uninterleaved.intersection( + index_c_uninterleaved.bounds, objects=True + ): + if hit.bounds == [3.0, 5.0, 3.0, 5.0]: + assert hit.object == ("a_1", "b_3") + elif hit.bounds == [4.0, 6.0, 2.0, 4.0]: + assert hit.object == ("a_2", "b_3") + else: + assert False + + def test_intersection_mismatch( + self, index_a_interleaved: index.Index, index_b_uninterleaved: index.Index + ) -> None: + with pytest.raises(AssertionError): + index_a_interleaved & index_b_uninterleaved + + def test_union_interleaved( + self, index_a_interleaved: index.Index, index_b_interleaved: index.Index + ) -> None: + index_c_interleaved = index_a_interleaved | index_b_interleaved + assert index_c_interleaved.interleaved + assert len(index_c_interleaved) == 4 + for hit in index_c_interleaved.intersection( + index_c_interleaved.bounds, objects=True + ): + if hit.bbox == [3.0, 3.0, 5.0, 5.0]: + assert hit.object == "a_1" + elif hit.bbox == [4.0, 2.0, 6.0, 4.0]: + assert hit.object == "a_2" + elif hit.bbox == [2.0, 1.0, 7.0, 6.0]: + assert hit.object == "b_3" + elif hit.bbox == [8.0, 7.0, 9.0, 8.0]: + assert hit.object == "b_4" + else: + assert False + + def test_union_uninterleaved( + self, index_a_uninterleaved: index.Index, index_b_uninterleaved: index.Index + ) -> None: + index_c_uninterleaved = index_a_uninterleaved | index_b_uninterleaved + assert not index_c_uninterleaved.interleaved + assert len(index_c_uninterleaved) == 4 + for hit in index_c_uninterleaved.intersection( + index_c_uninterleaved.bounds, objects=True + ): + if hit.bounds == [3.0, 5.0, 3.0, 5.0]: + assert hit.object == "a_1" + elif hit.bounds == [4.0, 6.0, 2.0, 4.0]: + assert hit.object == "a_2" + elif hit.bounds == [2.0, 7.0, 1.0, 6.0]: + assert hit.object == "b_3" + elif hit.bounds == [8.0, 9.0, 7.0, 8.0]: + assert hit.object == "b_4" + else: + assert False - def setUp(self): - self.boxes15 = np.genfromtxt('boxes_15x15.data') + def test_union_mismatch( + self, index_a_interleaved: index.Index, index_b_uninterleaved: index.Index + ) -> None: + with pytest.raises(AssertionError): + index_a_interleaved | index_b_uninterleaved - def boxes15_stream(self, interleaved=True): + +class IndexSerialization(unittest.TestCase): + def setUp(self) -> None: + self.boxes15 = np.genfromtxt("boxes_15x15.data") + + def boxes15_stream( + self, interleaved: bool = True + ) -> Iterator[Tuple[int, Tuple[float, float, float, float], int]]: for i, (minx, miny, maxx, maxy) in enumerate(self.boxes15): if interleaved: yield (i, (minx, miny, maxx, maxy), 42) else: yield (i, (minx, maxx, miny, maxy), 42) - def test_unicode_filenames(self): + def test_unicode_filenames(self) -> None: """Unicode filenames work as expected""" - if sys.version_info.major < 3: - return tname = tempfile.mktemp() - filename = tname + u'gilename\u4500abc' + filename = tname + "gilename\u4500abc" idx = index.Index(filename) idx.insert( - 4321, - (34.3776829412, - 26.7375853734, - 49.3776829412, - 41.7375853734), - obj=42) + 4321, (34.3776829412, 26.7375853734, 49.3776829412, 41.7375853734), obj=42 + ) - def test_pickling(self): + def test_pickling(self) -> None: """Pickling works as expected""" idx = index.Index() @@ -296,20 +418,23 @@ some_data = {"a": 22, "b": [1, "ccc"]} - idx.dumps = lambda obj: json.dumps(obj).encode('utf-8') - idx.loads = lambda string: json.loads(string.decode('utf-8')) + # https://github.com/python/mypy/issues/2427 + idx.dumps = lambda obj: json.dumps(obj).encode( # type: ignore[assignment] + "utf-8" + ) + idx.loads = lambda string: json.loads( # type: ignore[assignment] + string.decode("utf-8") + ) + idx.add(0, (0, 0, 1, 1), some_data) - self.assertEqual( - list( - idx.nearest( - (0, 0), 1, objects="raw"))[0], some_data) + self.assertEqual(list(idx.nearest((0, 0), 1, objects="raw"))[0], some_data) - def test_custom_filenames(self): + def test_custom_filenames(self) -> None: """Test using custom filenames for index serialization""" p = index.Property() - p.dat_extension = 'data' - p.idx_extension = 'index' + p.dat_extension = "data" + p.idx_extension = "index" tname = tempfile.mktemp() idx = index.Index(tname, properties=p) for i, coords in enumerate(self.boxes15): @@ -326,47 +451,156 @@ self.assertTrue(len(hits), 10) self.assertEqual(hits, [0, 4, 16, 27, 35, 40, 47, 50, 76, 80]) - def test_interleaving(self): + def test_interleaving(self) -> None: """Streaming against a persisted index without interleaving""" - def data_gen(interleaved=True): + + def data_gen( + interleaved: bool = True, + ) -> Iterator[Tuple[int, Tuple[float, float, float, float], int]]: for i, (minx, miny, maxx, maxy) in enumerate(self.boxes15): if interleaved: yield (i, (minx, miny, maxx, maxy), 42) else: yield (i, (minx, maxx, miny, maxy), 42) + p = index.Property() tname = tempfile.mktemp() - idx = index.Index(tname, - data_gen(interleaved=False), - properties=p, - interleaved=False) - hits = sorted(list(idx.intersection((0, 60, 0, 60)))) - self.assertTrue(len(hits), 10) - self.assertEqual(hits, [0, 4, 16, 27, 35, 40, 47, 50, 76, 80]) + idx = index.Index( + tname, data_gen(interleaved=False), properties=p, interleaved=False + ) + hits1 = sorted(list(idx.intersection((0, 60, 0, 60)))) + self.assertTrue(len(hits1), 10) + self.assertEqual(hits1, [0, 4, 16, 27, 35, 40, 47, 50, 76, 80]) leaves = idx.leaves() expected = [ - (0, [2, 92, 51, 55, 26, 95, 7, 81, 38, 22, 58, 89, 91, 83, 98, 37, - 70, 31, 49, 34, 11, 6, 13, 3, 23, 57, 9, 96, 84, 36, 5, 45, - 77, 78, 44, 12, 42, 73, 93, 41, 71, 17, 39, 54, 88, 72, 97, - 60, 62, 48, 19, 25, 76, 59, 66, 64, 79, 94, 40, 32, 46, 47, - 15, 68, 10, 0, 80, 56, 50, 30], - [-186.673789279, -96.7177218184, 172.392784956, 45.4856075292]), - (2, [61, 74, 29, 99, 16, 43, 35, 33, 27, 63, 18, 90, 8, 53, 82, - 21, 65, 24, 4, 1, 75, 67, 86, 52, 28, 85, 87, 14, 69, 20], - [-174.739939684, 32.6596016791, 184.761387556, 96.6043699778])] + ( + 0, + [ + 2, + 92, + 51, + 55, + 26, + 95, + 7, + 81, + 38, + 22, + 58, + 89, + 91, + 83, + 98, + 37, + 70, + 31, + 49, + 34, + 11, + 6, + 13, + 3, + 23, + 57, + 9, + 96, + 84, + 36, + 5, + 45, + 77, + 78, + 44, + 12, + 42, + 73, + 93, + 41, + 71, + 17, + 39, + 54, + 88, + 72, + 97, + 60, + 62, + 48, + 19, + 25, + 76, + 59, + 66, + 64, + 79, + 94, + 40, + 32, + 46, + 47, + 15, + 68, + 10, + 0, + 80, + 56, + 50, + 30, + ], + [-186.673789279, -96.7177218184, 172.392784956, 45.4856075292], + ), + ( + 2, + [ + 61, + 74, + 29, + 99, + 16, + 43, + 35, + 33, + 27, + 63, + 18, + 90, + 8, + 53, + 82, + 21, + 65, + 24, + 4, + 1, + 75, + 67, + 86, + 52, + 28, + 85, + 87, + 14, + 69, + 20, + ], + [-174.739939684, 32.6596016791, 184.761387556, 96.6043699778], + ), + ] - if PY3 and False: - # TODO : this reliably fails on Python 2.7 and 3.5 + if sys.maxsize > 2**32: + # TODO: this fails with CPython 3.7 manylinux i686 i.e. 32-bit # go through the traversal and see if everything is close - assert all(all(np.allclose(a, b) for a, b in zip(L, E)) - for L, E in zip(leaves, expected)) - - hits = sorted(list(idx.intersection((0, 60, 0, 60), objects=True))) - self.assertTrue(len(hits), 10) - self.assertEqual(hits[0].object, 42) + assert all( + all(np.allclose(a, b) for a, b in zip(L, E)) + for L, E in zip(leaves, expected) + ) + + hits2 = sorted(list(idx.intersection((0, 60, 0, 60), objects=True))) + self.assertTrue(len(hits2), 10) + self.assertEqual(hits2[0].object, 42) - def test_overwrite(self): + def test_overwrite(self) -> None: """Index overwrite works as expected""" tname = tempfile.mktemp() @@ -377,8 +611,7 @@ class IndexNearest(IndexTestCase): - - def test_nearest_basic(self): + def test_nearest_basic(self) -> None: """Test nearest basic selection of records""" hits = list(self.idx.nearest((0, 0, 10, 10), 3)) self.assertEqual(hits, [76, 48, 19]) @@ -390,7 +623,7 @@ hits = sorted(idx.nearest((13, 0, 20, 2), 3)) self.assertEqual(hits, [3, 4, 5]) - def test_nearest_equidistant(self): + def test_nearest_equidistant(self) -> None: """Test that if records are equidistant, both are returned.""" point = (0, 0) small_box = (-10, -10, 10, 10) @@ -423,22 +656,21 @@ self.assertEqual(list(idx.nearest(point, 2)), [2, 1]) self.assertEqual(list(idx.nearest(point, 1)), [2]) - def test_nearest_object(self): + def test_nearest_object(self) -> None: """Test nearest object selection of records""" idx = index.Index() locs = [(14, 10, 14, 10), (16, 10, 16, 10)] for i, (minx, miny, maxx, maxy) in enumerate(locs): - idx.add(i, (minx, miny, maxx, maxy), obj={'a': 42}) + idx.add(i, (minx, miny, maxx, maxy), obj={"a": 42}) hits = sorted( - [(i.id, i.object) - for i in idx.nearest((15, 10, 15, 10), 1, objects=True)]) - self.assertEqual(hits, [(0, {'a': 42}), (1, {'a': 42})]) + (i.id, i.object) for i in idx.nearest((15, 10, 15, 10), 1, objects=True) + ) + self.assertEqual(hits, [(0, {"a": 42}), (1, {"a": 42})]) class IndexDelete(IndexTestCase): - - def test_deletion(self): + def test_deletion(self) -> None: """Test we can delete data from the index""" idx = index.Index() for i, coords in enumerate(self.boxes15): @@ -452,7 +684,7 @@ class IndexMoreDimensions(IndexTestCase): - def test_3d(self): + def test_3d(self) -> None: """Test we make and query a 3D index""" p = index.Property() p.dimension = 3 @@ -461,7 +693,7 @@ hits = idx.intersection((-1, 1, 58, 62, 22, 24)) self.assertEqual(list(hits), [1]) - def test_4d(self): + def test_4d(self) -> None: """Test we make and query a 4D index""" p = index.Property() p.dimension = 4 @@ -472,8 +704,7 @@ class IndexStream(IndexTestCase): - - def test_stream_input(self): + def test_stream_input(self) -> None: p = index.Property() sindex = index.Index(self.boxes15_stream(), properties=p) bounds = (0, 0, 60, 60) @@ -484,68 +715,72 @@ self.assertEqual(len(objects), 10) self.assertEqual(objects[0].object, 42) - def test_empty_stream(self): + def test_empty_stream(self) -> None: """Assert empty stream raises exception""" - self.assertRaises(core.RTreeError, index.Index, ((x for x in []))) + self.assertRaises(RTreeError, index.Index, iter(())) - def test_exception_in_generator(self): + def test_exception_in_generator(self) -> None: """Assert exceptions raised in callbacks are raised in main thread""" + class TestException(Exception): pass - def create_index(): - def gen(): + def create_index() -> index.Index: + def gen() -> Iterator[Tuple[int, Tuple[int, int, int, int], None]]: # insert at least 6 or so before the exception for i in range(10): yield (i, (1, 2, 3, 4), None) raise TestException("raising here") + return index.Index(gen()) self.assertRaises(TestException, create_index) - def test_exception_at_beginning_of_generator(self): + def test_exception_at_beginning_of_generator(self) -> None: """ Assert exceptions raised in callbacks before generator function are raised in main thread. """ + class TestException(Exception): pass - def create_index(): - def gen(): + def create_index() -> index.Index: + def gen() -> None: raise TestException("raising here") - return index.Index(gen()) + + return index.Index(gen()) # type: ignore[func-returns-value] self.assertRaises(TestException, create_index) class DictStorage(index.CustomStorage): - """ A simple storage which saves the pages in a python dictionary """ + """A simple storage which saves the pages in a python dictionary""" - def __init__(self): + def __init__(self) -> None: index.CustomStorage.__init__(self) self.clear() def create(self, returnError): - """ Called when the storage is created on the C side """ + """Called when the storage is created on the C side""" def destroy(self, returnError): - """ Called when the storage is destroyed on the C side """ + """Called when the storage is destroyed on the C side""" - def clear(self): - """ Clear all our data """ - self.dict = {} + def clear(self) -> None: + """Clear all our data""" + self.dict: Dict = {} def loadByteArray(self, page, returnError): - """ Returns the data for page or returns an error """ + """Returns the data for page or returns an error""" try: return self.dict[page] except KeyError: returnError.contents.value = self.InvalidPageError def storeByteArray(self, page, data, returnError): - """ Stores the data for page """ + """Stores the data for page""" if page == self.NewPage: newPageId = len(self.dict) self.dict[newPageId] = data @@ -558,7 +793,7 @@ return page def deleteByteArray(self, page, returnError): - """ Deletes a page """ + """Deletes a page""" try: del self.dict[page] except KeyError: @@ -569,7 +804,7 @@ class IndexCustomStorage(unittest.TestCase): - def test_custom_storage(self): + def test_custom_storage(self) -> None: """Custom index storage works as expected""" settings = index.Property() settings.writethrough = True @@ -614,7 +849,7 @@ del storage - def test_custom_storage_reopening(self): + def test_custom_storage_reopening(self) -> None: """Reopening custom index storage works as expected""" storage = DictStorage() diff -Nru python-rtree-0.9.7/tests/test_tpr.py python-rtree-1.0.0/tests/test_tpr.py --- python-rtree-0.9.7/tests/test_tpr.py 2020-12-23 18:35:10.000000000 +0000 +++ python-rtree-1.0.0/tests/test_tpr.py 2022-04-04 21:43:52.000000000 +0000 @@ -1,52 +1,85 @@ -from collections import namedtuple, defaultdict +import os +import unittest +from collections import defaultdict, namedtuple from math import ceil +from typing import Any, Iterator, Optional, Tuple, Union -import unittest import numpy as np -import os from rtree.index import Index, Property, RT_TPRTree -class Cartesian(namedtuple("Cartesian", ( - "id", "time", "x", "y", "x_vel", "y_vel", "update_time", - "out_of_bounds"))): +class Cartesian( + namedtuple( + "Cartesian", + ("id", "time", "x", "y", "x_vel", "y_vel", "update_time", "out_of_bounds"), + ) +): __slots__ = () - def getX(self, t): + def getX(self, t: float) -> float: return self.x + self.x_vel * (t - self.time) - def getY(self, t): + def getY(self, t: float) -> float: return self.y + self.y_vel * (t - self.time) - def getXY(self, t): + def getXY(self, t: float) -> Tuple[float, float]: return self.getX(t), self.getY(t) - def get_coordinates(self, t_now=None): - return ((self.x, self.y, self.x, self.y), - (self.x_vel, self.y_vel, self.x_vel, self.y_vel), - self.time if t_now is None else (self.time, t_now)) + def get_coordinates( + self, t_now: Optional[float] = None + ) -> Tuple[ + Tuple[float, float, float, float], + Tuple[float, float, float, float], + Union[float, Tuple[float, float]], + ]: + return ( + (self.x, self.y, self.x, self.y), + (self.x_vel, self.y_vel, self.x_vel, self.y_vel), + self.time if t_now is None else (self.time, t_now), + ) -class QueryCartesian(namedtuple("QueryCartesian", ( - "start_time", "end_time", "x", "y", "dx", "dy"))): +class QueryCartesian( + namedtuple("QueryCartesian", ("start_time", "end_time", "x", "y", "dx", "dy")) +): __slots__ = () - def get_coordinates(self): - return ((self.x - self.dx, self.y - self.dy, - self.x + self.dx, self.y + self.dy), - (0, 0, 0, 0), - (self.start_time, self.end_time)) + def get_coordinates( + self, + ) -> Tuple[ + Tuple[float, float, float, float], + Tuple[float, float, float, float], + Tuple[float, float], + ]: + return ( + (self.x - self.dx, self.y - self.dy, self.x + self.dx, self.y + self.dy), + (0, 0, 0, 0), + (self.start_time, self.end_time), + ) def data_generator( - dataset_size=100, simulation_length=10, max_update_interval=20, - queries_per_time_step=5, min_query_extent=0.05, max_query_extent=0.1, - horizon=20, min_query_interval=2, max_query_interval=10, agility=0.01, - min_speed=0.0025, max_speed=0.0166, min_x=0, min_y=0, max_x=1, max_y=1, -): - - def create_object(id_, time, x=None, y=None): + dataset_size: int = 100, + simulation_length: int = 10, + max_update_interval: int = 20, + queries_per_time_step: int = 5, + min_query_extent: float = 0.05, + max_query_extent: float = 0.1, + horizon: int = 20, + min_query_interval: int = 2, + max_query_interval: int = 10, + agility: float = 0.01, + min_speed: float = 0.0025, + max_speed: float = 0.0166, + min_x: int = 0, + min_y: int = 0, + max_x: int = 1, + max_y: int = 1, +) -> Iterator[Tuple[str, int, Any]]: + def create_object( + id_: float, time: float, x: Optional[float] = None, y: Optional[float] = None + ) -> Cartesian: # Create object with random or defined x, y and random velocity if x is None: x = np.random.uniform(min_x, max_x) @@ -66,8 +99,7 @@ out_of_bounds = False update_time = time + max_update_interval - return Cartesian(id_, time, x, y, x_vel, y_vel, update_time, - out_of_bounds) + return Cartesian(id_, time, x, y, x_vel, y_vel, update_time, out_of_bounds) objects = list() objects_to_update = defaultdict(set) @@ -120,32 +152,41 @@ yield "QUERY", t_now, QueryCartesian(t, t + dt, x, y, dx, dy) -def intersects(x1, y1, x2, y2, x, y, dx, dy): +def intersects( + x1: float, y1: float, x2: float, y2: float, x: float, y: float, dx: float, dy: float +) -> bool: # Checks if line from x1, y1 to x2, y2 intersects with rectangle with # bottom left at x-dx, y-dy and top right at x+dx, y+dy. # Implementation of https://stackoverflow.com/a/293052 # Check if line points not both more/less than max/min for each axis - if (x1 > x + dx and x2 > x + dx) or (x1 < x - dx and x2 < x - dx) \ - or (y1 > y + dy and y2 > y + dy) or (y1 < y - dy and y2 < y - dy): + if ( + (x1 > x + dx and x2 > x + dx) + or (x1 < x - dx and x2 < x - dx) + or (y1 > y + dy and y2 > y + dy) + or (y1 < y - dy and y2 < y - dy) + ): return False # Check on which side (+ve, -ve) of the line the rectangle corners are, # returning True if any corner is on a different side. - calcs = ((y2 - y1) * rect_x + (x1 - x2) * rect_y + (x2 * y1 - x1 * y2) - for rect_x, rect_y in ((x - dx, y - dy), - (x + dx, y - dy), - (x - dx, y + dy), - (x + dx, y + dy))) + calcs = ( + (y2 - y1) * rect_x + (x1 - x2) * rect_y + (x2 * y1 - x1 * y2) + for rect_x, rect_y in ( + (x - dx, y - dy), + (x + dx, y - dy), + (x - dx, y + dy), + (x + dx, y + dy), + ) + ) sign = np.sign(next(calcs)) # First corner (bottom left) return any(np.sign(calc) != sign for calc in calcs) # Check remaining 3 class TPRTests(unittest.TestCase): - - def test_tpr(self): + def test_tpr(self) -> None: # TODO : this freezes forever on some windows cloud builds - if os.name == 'nt': + if os.name == "nt": return # Cartesians list for brute force @@ -160,8 +201,7 @@ tpr_tree.delete(object_.id, object_.get_coordinates(t_now)) del objects[object_.id] elif operation == "QUERY": - tree_intersect = set( - tpr_tree.intersection(object_.get_coordinates())) + tree_intersect = set(tpr_tree.intersection(object_.get_coordinates())) # Brute intersect brute_intersect = set() @@ -170,8 +210,15 @@ x_high, y_high = tree_object.getXY(object_.end_time) if intersects( - x_low, y_low, x_high, y_high, # Line - object_.x, object_.y, object_.dx, object_.dy): # Rect + x_low, + y_low, + x_high, + y_high, # Line + object_.x, + object_.y, + object_.dx, + object_.dy, + ): # Rect brute_intersect.add(tree_object.id) # Tree should match brute force approach