├── .coveragerc ├── .github └── workflows │ ├── ci-production.yml │ └── ci-tests.yml ├── .gitignore ├── .pre-commit-config.yaml ├── AUTHORS ├── CHANGELOG ├── CONTRIBUTORS ├── ISSUE_TEMPLATE.md ├── LICENSE ├── MANIFEST.in ├── README.rst ├── codecov.yml ├── docs ├── Makefile ├── make.bat └── source │ ├── conf.py │ ├── index.rst │ ├── modules.rst │ ├── openrouteservice.rst │ └── readme_link.rst ├── environment.yml ├── examples └── basic_example.ipynb ├── openrouteservice ├── __init__.py ├── client.py ├── convert.py ├── deprecation.py ├── directions.py ├── distance_matrix.py ├── elevation.py ├── exceptions.py ├── geocode.py ├── isochrones.py ├── optimization.py └── places.py ├── poetry.lock ├── pyproject.toml └── test ├── __init__.py ├── test_client.py ├── test_convert.py ├── test_deprecation_warning.py ├── test_directions.py ├── test_distance_matrix.py ├── test_elevation.py ├── test_exceptions.py ├── test_geocode.py ├── test_helper.py ├── test_isochrones.py ├── test_optimization.py └── test_places.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = False 3 | source = codecov 4 | omit = 5 | */venv/* 6 | */.venv/* 7 | */.env/* 8 | */.tox/* 9 | */docs/* 10 | tests/* 11 | setup.py 12 | 13 | [report] 14 | omit = 15 | */venv/* 16 | */.venv/* 17 | */.env/* 18 | */.tox/* 19 | */docs/* 20 | tests/* 21 | setup.py 22 | exclude_lines = 23 | pragma: no cover 24 | -------------------------------------------------------------------------------- /.github/workflows/ci-production.yml: -------------------------------------------------------------------------------- 1 | name: Continous Integration 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - master 8 | release: 9 | types: 10 | - created 11 | 12 | jobs: 13 | pytest: 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11", "pypy3.8", "pypy3.9" ] 18 | poetry-version: [ "latest" ] 19 | os: [ ubuntu-20.04, macos-latest, windows-latest ] 20 | runs-on: ${{ matrix.os }} 21 | name: Python ${{ matrix.python-version }} / Poetry ${{ matrix.poetry-version }} / ${{ matrix.os }} 22 | defaults: 23 | run: 24 | shell: bash 25 | steps: 26 | - uses: actions/checkout@v3 27 | with: 28 | fetch-depth: 0 29 | - name: Set up Python ${{ matrix.python-version }} 30 | uses: actions/setup-python@v4 31 | with: 32 | python-version: ${{ matrix.python-version }} 33 | cache: 'pip' 34 | - name: Update pip 35 | run: | 36 | python -m pip install --upgrade pip 37 | - name: Run Poetry action 38 | uses: abatilo/actions-poetry@v2 39 | with: 40 | poetry-version: ${{ matrix.poetry-version }} 41 | - name: View poetry --version 42 | run: poetry --version 43 | - name: Install dependencies 44 | run: poetry install 45 | - name: Run tests 46 | run: poetry run pytest 47 | - name: Upload coverage to Codecov 48 | uses: codecov/codecov-action@v1 49 | with: 50 | token: ${{ secrets.CODECOV_TOKEN }} 51 | flags: unittests 52 | env_vars: OS,PYTHON 53 | name: codecov-umbrella 54 | fail_ci_if_error: true 55 | verbose: true 56 | build-and-publish: 57 | name: Build and publish Python distributions 📦 to PyPI and TestPyPI 58 | runs-on: ubuntu-20.04 59 | needs: 60 | - pytest 61 | steps: 62 | - uses: actions/checkout@v2 63 | - name: Set up base Python 3.9 64 | uses: actions/setup-python@v2 65 | with: 66 | python-version: 3.9 67 | - name: Python Poetry Action 68 | uses: abatilo/actions-poetry@v2 69 | with: 70 | poetry-version: latest 71 | - name: Publish distribution 📦 with test.pypi.org 72 | if: startsWith(github.ref, 'refs/tags/v') 73 | run: | 74 | poetry config repositories.testpypi https://test.pypi.org/legacy/ 75 | poetry config pypi-token.testpypi ${{ secrets.TEST_PYPI_API_TOKEN }} 76 | poetry build 77 | poetry publish -r testpypi 78 | - name: Publish distribution 📦 to PyPI 79 | if: startsWith(github.ref, 'refs/tags') 80 | run: | 81 | poetry config pypi-token.pypi ${{ secrets.PYPI_API_TOKEN }} 82 | poetry build 83 | poetry publish 84 | -------------------------------------------------------------------------------- /.github/workflows/ci-tests.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: 4 | pull_request: 5 | branches: '**' 6 | 7 | jobs: 8 | lint: 9 | runs-on: ubuntu-20.04 10 | steps: 11 | - name: checkout 12 | uses: actions/checkout@v2 13 | - name: Install Python 14 | uses: actions/setup-python@v2 15 | with: 16 | python-version: 3.9 17 | - uses: pre-commit/action@v3.0.0 18 | with: 19 | extra_args: --all-files --show-diff-on-failure 20 | pytest: 21 | needs: 22 | - lint 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11", "pypy3.8", "pypy3.9" ] 27 | poetry-version: [ "1.5.1" ] 28 | os: [ ubuntu-20.04, macos-latest, windows-latest ] 29 | runs-on: ${{ matrix.os }} 30 | name: Python ${{ matrix.python-version }} / Poetry ${{ matrix.poetry-version }} / ${{ matrix.os }} 31 | defaults: 32 | run: 33 | shell: bash 34 | steps: 35 | - uses: actions/checkout@v3 36 | with: 37 | fetch-depth: 0 38 | - name: Set up Python ${{ matrix.python-version }} 39 | uses: actions/setup-python@v4 40 | with: 41 | python-version: ${{ matrix.python-version }} 42 | cache: 'pip' 43 | - name: Select the cache folder 44 | id: cache-folder 45 | run: | 46 | if [ ${{ matrix.os }} == 'ubuntu-20.04' ]; then 47 | CACHE_FOLDER="/home/runner/.cache/pypoetry" 48 | elif [ ${{ matrix.os }} == 'macos-latest' ]; then 49 | CACHE_FOLDER="/Users/runner/Library/Caches/pypoetry" 50 | elif [ ${{ matrix.os }} == 'windows-latest' ]; then 51 | CACHE_FOLDER="C:\Users\runneradmin\AppData\Local\pypoetry\Cache" 52 | fi 53 | echo "Cache folder is $CACHE_FOLDER" 54 | echo "folder=$CACHE_FOLDER" >> "$GITHUB_OUTPUT" 55 | - name: Cache Poetry cache 56 | uses: actions/cache@v3 57 | with: 58 | path: ${{ steps.cache-folder.outputs.folder }} 59 | key: poetry-cache-${{ matrix.os }}-${{ matrix.python-version }}-${{ matrix.poetry-version }} 60 | - name: Run Poetry action 61 | uses: abatilo/actions-poetry@v2 62 | with: 63 | poetry-version: ${{ matrix.poetry-version }} 64 | - name: View poetry --version 65 | run: poetry --version 66 | - name: Install dependencies 67 | run: poetry install 68 | - name: Run tests 69 | run: poetry run pytest 70 | - name: Upload coverage to Codecov 71 | uses: codecov/codecov-action@v3 72 | with: 73 | token: ${{ secrets.CODECOV_TOKEN }} 74 | flags: unittests 75 | env_vars: OS,PYTHON 76 | name: codecov-umbrella 77 | fail_ci_if_error: true 78 | verbose: true 79 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .tox/ 2 | .venv* 3 | .env* 4 | .coverage 5 | **/.ipynb_checkpoints/ 6 | *.geojson 7 | *.pyc 8 | *.in 9 | *egg-info/ 10 | dist/ 11 | build/ 12 | /docs/build 13 | test.py 14 | 15 | #IDE 16 | .spyproject/ 17 | .idea/ 18 | 19 | cover/ 20 | conda/ 21 | *coverage.xml 22 | /setup.py 23 | /requirements.txt 24 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.4.0 4 | hooks: 5 | - id: trailing-whitespace 6 | - id: end-of-file-fixer 7 | - id: check-yaml 8 | exclude: | 9 | (?x)( 10 | ^conda.recipe/meta.yaml 11 | ) 12 | - id: check-json 13 | - id: forbid-new-submodules 14 | - id: mixed-line-ending 15 | args: [ '--fix=lf' ] 16 | description: Forces to replace line ending by the UNIX 'lf' character. 17 | - id: pretty-format-json 18 | args: [ '--no-sort-keys' ] 19 | - id: no-commit-to-branch 20 | args: [ --branch, master ] 21 | - id: no-commit-to-branch 22 | args: [ --branch, main ] 23 | 24 | - repo: https://github.com/ambv/black 25 | rev: 23.3.0 26 | hooks: 27 | - id: black 28 | args: # arguments to configure black 29 | - --line-length=80 30 | - repo: https://github.com/PyCQA/flake8 31 | rev: 6.0.0 32 | hooks: 33 | - id: flake8 34 | additional_dependencies: 35 | [ 36 | "flake8-bugbear", 37 | "flake8-coding", 38 | "flake8-comprehensions", 39 | "flake8-debugger", 40 | "flake8-deprecated", 41 | "flake8-pep3101", 42 | "flake8-polyfill", 43 | "flake8-print", 44 | "flake8-docstrings", 45 | ] 46 | exclude: | 47 | (?x)( 48 | ^test/* | 49 | ^docs/* 50 | ) 51 | args: 52 | - "--max-line-length=120" 53 | - "--ignore=P101,D202,D401" 54 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of openrouteservice-py authors 2 | # for copyright purposes. This file is distinct from the CONTRIBUTORS files. 3 | # See the latter for an explanation. 4 | 5 | # Names should be added to this file as 6 | # Name or Organization 7 | # The email address is not required for organizations. 8 | 9 | # Please keep the list sorted. 10 | 11 | # The work is based on Python Client for Google Maps Services: 12 | # https://github.com/googlemaps/google-maps-services-python 13 | # Following authors were registered: 14 | 15 | Google Inc. 16 | 17 | # Modified to fit ORS needs by 18 | 19 | Julian Psotta 20 | Nils Nolde 21 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | # Unreleased 2 | 3 | - add 'intersections'-parameter to isochrones 4 | 5 | ## 2.2.0 6 | 7 | - restrict optimize_waypoints parameter to not trigger when options or 'shortest' is used 8 | - Add optimize_waypoints option to directions for simple TSP 9 | - get rid of validators, too maintenance-heavy 10 | 11 | ## 2.1.0 12 | 13 | - fix minor problems, apply PEP lint, add some tests 14 | - Add optimization endpoint 15 | 16 | # 2.0.0 17 | 18 | - implement all backend changes from moving to openrouteservice v5 19 | - now all parameters are named like their backend equivalents, while keeping the old ors-py parameter names for backwards compatibility, but with deprecation warnings 20 | - validator validates ALL parameters 21 | - added a Client.req property, returning the actual `requests` request 22 | 23 | ### v1.1.8 24 | 25 | - make dependencies more sensible (#32) 26 | 27 | ### v1.1.7 28 | 29 | - fix boundary.country for pelias_search (#30) 30 | - change isochrone defaults (#31) 31 | 32 | ### v1.1.6 33 | 34 | - fix boolean parameters (#28) 35 | 36 | ### v1.1.0 37 | 38 | - fix decoding of 2D polylines 39 | - add support for true booleans 40 | 41 | # v1.0 42 | 43 | - add Pelias Autocomplete 44 | - add elevation endpoint 45 | - proper parameter validation via Cerberus 46 | 47 | ## v0.4 48 | 49 | - add Pelias geocoding endpoints 50 | 51 | ## v0.3 52 | 53 | - add options object to directions API 54 | 55 | ## v0.2 56 | 57 | - Integrate openpoiservice (#1) 58 | - add dry_run 59 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # This is the official list of people who have contributed to the project. The 2 | # copyright is held by those individuals or organizations in the AUTHORS file. 3 | # 4 | # Names should be added to this file like so: 5 | # Name 6 | 7 | # Please keep the list sorted by first name. 8 | 9 | Julian Psotta 10 | Nils Nolde 11 | 12 | # The following people contributed to the original Python Client for Google Maps Services: 13 | 14 | Brett Morgan 15 | Chris Broadfoot 16 | Dave Holmes 17 | Luke Mahe 18 | Mark McDonald 19 | Sam Thorogood 20 | Sean Wohltman 21 | Stephen McDonald 22 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | #### Here's what I did 6 | 7 | 8 | --- 9 | #### Here's what I got 10 | 11 | 12 | --- 13 | #### Here's what I was expecting 14 | 15 | 16 | --- 17 | #### Here's what I think could be improved 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://github.com/GIScience/openrouteservice-py/workflows/tests/badge.svg 2 | :target: https://github.com/GIScience/openrouteservice-py/actions 3 | :alt: Build status 4 | 5 | .. image:: https://codecov.io/gh/GIScience/openrouteservice-py/branch/master/graph/badge.svg?token=QqGC8XfCiI 6 | :target: https://codecov.io/gh/GIScience/openrouteservice-py 7 | :alt: Codecov coverage 8 | 9 | .. image:: https://readthedocs.org/projects/openrouteservice-py/badge/?version=latest 10 | :target: http://openrouteservice-py.readthedocs.io/en/latest/?badge=latest 11 | :alt: Documentation Status 12 | 13 | .. image:: https://badge.fury.io/py/openrouteservice.svg 14 | :target: https://badge.fury.io/py/openrouteservice 15 | :alt: PyPI version 16 | 17 | .. image:: https://mybinder.org/badge_logo.svg 18 | :target: https://mybinder.org/v2/gh/GIScience/openrouteservice-py/master?filepath=examples%2Fbasic_example.ipynb 19 | :alt: MyBinder 20 | 21 | Quickstart 22 | ================================================== 23 | 24 | Description 25 | -------------------------------------------------- 26 | The openrouteservice library gives you painless access to the openrouteservice_ (ORS) routing API's. 27 | It performs requests against our API's for 28 | 29 | - directions_ 30 | - isochrones_ 31 | - `matrix routing calculations`_ 32 | - places_ 33 | - elevation_ 34 | - `Pelias geocoding`_ 35 | - `Pelias reverse geocoding`_ 36 | - `Pelias structured geocoding`_ 37 | - `Pelias autocomplete`_ 38 | - Optimization_ 39 | 40 | For further details, please visit: 41 | 42 | - homepage_ 43 | - `ORS API documentation`_ 44 | - `openrouteservice-py documentation`_ 45 | 46 | We also have a repo with a few useful examples here_. 47 | 48 | For support, please ask our forum_. 49 | 50 | By using this library, you agree to the ORS `terms and conditions`_. 51 | 52 | .. _openrouteservice: https://openrouteservice.org 53 | .. _homepage: https://openrouteservice.org 54 | .. _`ORS API documentation`: https://openrouteservice.org/documentation/ 55 | .. _`openrouteservice-py documentation`: http://openrouteservice-py.readthedocs.io/en/latest/ 56 | .. _directions: https://openrouteservice.org/documentation/#/reference/directions/directions/directions-service 57 | .. _`Pelias geocoding`: https://github.com/pelias/documentation/blob/master/search.md#available-search-parameters 58 | .. _`Pelias reverse geocoding`: https://github.com/pelias/documentation/blob/master/reverse.md#reverse-geocoding-parameters 59 | .. _`Pelias structured geocoding`: https://github.com/pelias/documentation/blob/master/structured-geocoding.md 60 | .. _`Pelias autocomplete`: https://github.com/pelias/documentation/blob/master/autocomplete.md 61 | .. _isochrones: https://openrouteservice.org/documentation/#/reference/isochrones/isochrones/isochrones-service 62 | .. _elevation: https://github.com/GIScience/openelevationservice/ 63 | .. _`reverse geocoding`: https://openrouteservice.org/documentation/#/reference/geocoding/geocoding/geocoding-service 64 | .. _`matrix routing calculations`: https://openrouteservice.org/documentation/#/reference/matrix/matrix/matrix-service-(post) 65 | .. _places: https://github.com/GIScience/openpoiservice 66 | .. _Optimization: https://github.com/VROOM-Project/vroom/blob/master/docs/API.md 67 | .. _here: https://github.com/GIScience/openrouteservice-examples/tree/master/python 68 | .. _`terms and conditions`: https://openrouteservice.org/terms-of-service/ 69 | .. _forum: https://ask.openrouteservice.org/c/sdks 70 | 71 | Requirements 72 | ----------------------------- 73 | openrouteservice-py is tested against Python 3.6, 3.7, 3.8 and 3.9, and PyPy3.6 and PyPy3.7. 74 | 75 | For setting up a testing environment, install **poetry** first. 76 | 77 | For Linux and osx:: 78 | 79 | curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python - 80 | 81 | For windows:: 82 | 83 | (Invoke-WebRequest -Uri https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py -UseBasicParsing).Content | python - 84 | 85 | Then create a venv and install the dependencies with poetry:: 86 | 87 | python -m venv .venv && source .venv/bin/activate 88 | poetry install -vv 89 | 90 | Installation 91 | ------------------------------ 92 | To install from PyPI, simply use pip:: 93 | 94 | pip install openrouteservice 95 | 96 | To install the latest and greatest from source:: 97 | 98 | pip install git+git://github.com/GIScience/openrouteservice-py@development 99 | 100 | 101 | 102 | Testing 103 | --------------------------------- 104 | If you want to run the unit tests, see Requirements_. ``cd`` to the library directory and run:: 105 | 106 | pytest -v 107 | 108 | ``-v`` flag for verbose output (recommended). 109 | 110 | 111 | Usage 112 | --------------------------------- 113 | 114 | For an interactive Jupyter notebook have a look on `mybinder.org `_. 115 | 116 | Basic example 117 | ^^^^^^^^^^^^^^^^^^^^ 118 | .. code:: python 119 | 120 | import openrouteservice 121 | 122 | coords = ((8.34234,48.23424),(8.34423,48.26424)) 123 | 124 | client = openrouteservice.Client(key='') # Specify your personal API key 125 | routes = client.directions(coords) 126 | 127 | print(routes) 128 | 129 | For convenience, all request performing module methods are wrapped inside the ``client`` class. This has the 130 | disadvantage, that your IDE can't auto-show all positional and optional arguments for the 131 | different methods. And there are a lot! 132 | 133 | The slightly more verbose alternative, preserving your IDE's smart functions, is 134 | 135 | .. code:: python 136 | 137 | import openrouteservice 138 | from openrouteservice.directions import directions 139 | 140 | coords = ((8.34234,48.23424),(8.34423,48.26424)) 141 | 142 | client = openrouteservice.Client(key='') # Specify your personal API key 143 | routes = directions(client, coords) # Now it shows you all arguments for .directions 144 | 145 | Optimize route 146 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ 147 | If you want to optimize the order of multiple waypoints in a simple `Traveling Salesman Problem `_, 148 | you can pass a ``optimize_waypoints`` parameter: 149 | 150 | .. code:: python 151 | 152 | import openrouteservice 153 | 154 | coords = ((8.34234,48.23424),(8.34423,48.26424), (8.34523,48.24424), (8.41423,48.21424)) 155 | 156 | client = openrouteservice.Client(key='') # Specify your personal API key 157 | routes = client.directions(coords, profile='cycling-regular', optimize_waypoints=True) 158 | 159 | print(routes) 160 | 161 | Decode Polyline 162 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ 163 | By default, the directions API returns `encoded polylines `_. 164 | To decode to a ``dict``, which is a GeoJSON geometry object, simply do 165 | 166 | .. code:: python 167 | 168 | import openrouteservice 169 | from openrouteservice import convert 170 | 171 | coords = ((8.34234,48.23424),(8.34423,48.26424)) 172 | 173 | client = openrouteservice.Client(key='') # Specify your personal API key 174 | 175 | # decode_polyline needs the geometry only 176 | geometry = client.directions(coords)['routes'][0]['geometry'] 177 | 178 | decoded = convert.decode_polyline(geometry) 179 | 180 | print(decoded) 181 | 182 | Dry run 183 | ^^^^^^^^^^^^^^^^^^^^ 184 | Although errors in query creation should be handled quite decently, you can do a dry run to print the request and its parameters: 185 | 186 | .. code:: python 187 | 188 | import openrouteservice 189 | 190 | coords = ((8.34234,48.23424),(8.34423,48.26424)) 191 | 192 | client = openrouteservice.Client() 193 | client.directions(coords, dry_run='true') 194 | 195 | Local ORS instance 196 | ^^^^^^^^^^^^^^^^^^^^ 197 | If you're hosting your own ORS instance, you can alter the ``base_url`` parameter to fit your own: 198 | 199 | .. code:: python 200 | 201 | import openrouteservice 202 | 203 | coords = ((8.34234,48.23424),(8.34423,48.26424)) 204 | 205 | # key can be omitted for local host 206 | client = openrouteservice.Client(base_url='http://localhost/ors') 207 | 208 | # Only works if you didn't change the ORS endpoints manually 209 | routes = client.directions(coords) 210 | 211 | # If you did change the ORS endpoints for some reason 212 | # you'll have to pass url and required parameters explicitly: 213 | routes = client.request( 214 | url='/new_url', 215 | post_json={ 216 | 'coordinates': coords, 217 | 'profile': 'driving-car', 218 | 'format': 'geojson' 219 | }) 220 | 221 | Support 222 | -------- 223 | 224 | For general support and questions, contact our forum_. 225 | 226 | For issues/bugs/enhancement suggestions, please use https://github.com/GIScience/openrouteservice-py/issues. 227 | 228 | 229 | .. _forum: https://ask.openrouteservice.org/c/sdks 230 | 231 | 232 | Acknowledgements 233 | ----------------- 234 | 235 | This library is based on the very elegant codebase from googlemaps_. 236 | 237 | 238 | .. _googlemaps: https://github.com/googlemaps/google-maps-services-python 239 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | parsers: 2 | gcov: 3 | branch_detection: 4 | conditional: yes 5 | loop: yes 6 | method: no 7 | macro: no 8 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python -msphinx 7 | SPHINXPROJ = openrouteservice-py 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=python -msphinx 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | set SPHINXPROJ=openrouteservice-py 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The Sphinx module was not found. Make sure you have Sphinx installed, 20 | echo.then set the SPHINXBUILD environment variable to point to the full 21 | echo.path of the 'sphinx-build' executable. Alternatively you may add the 22 | echo.Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # openrouteservice-py documentation build configuration file, created by 3 | # sphinx-quickstart on Wed Jan 31 20:43:55 2018. 4 | # 5 | # This file is execfile()d with the current directory set to its 6 | # containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | # If extensions (or modules to document with autodoc) are in another directory, 15 | # add these directories to sys.path here. If the directory is relative to the 16 | # documentation root, use os.path.abspath to make it absolute, like shown here. 17 | # 18 | import os 19 | import sys 20 | 21 | # sys.path.insert(0, 'C:\\Users\\gisadmin\\Documents\\Dev\\Git\\Uni\\ORS\\infrastructure\\SDK\\openrouteservice-python-api\\openrouteservice') 22 | sys.path.insert(0, os.path.abspath("../..")) 23 | 24 | # -- General configuration ------------------------------------------------ 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | # 28 | # needs_sphinx = '1.0' 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = ["sphinx.ext.autodoc", "sphinx.ext.todo", "sphinx.ext.coverage"] 34 | 35 | # Add any paths that contain templates here, relative to this directory. 36 | templates_path = [".templates"] 37 | 38 | # The suffix(es) of source filenames. 39 | # You can specify multiple suffix as a list of string: 40 | # 41 | # source_suffix = ['.rst', '.md'] 42 | source_suffix = ".rst" 43 | 44 | # The master toctree document. 45 | master_doc = "index" 46 | 47 | # General information about the project. 48 | project = "openrouteservice-py" 49 | copyright = "2023, HeiGIT gGmbH" 50 | author = "HeiGIT gGmbH" 51 | 52 | # The version info for the project you're documenting, acts as replacement for 53 | # |version| and |release|, also used in various other places throughout the 54 | # built documents. 55 | # 56 | # The short X.Y version. 57 | version = "0.4" 58 | # The full version, including alpha/beta/rc tags. 59 | release = "0.4" 60 | 61 | # The language for content autogenerated by Sphinx. Refer to documentation 62 | # for a list of supported languages. 63 | # 64 | # This is also used if you do content translation via gettext catalogs. 65 | # Usually you set "language" from the command line for these cases. 66 | language = None 67 | 68 | # List of patterns, relative to source directory, that match files and 69 | # directories to ignore when looking for source files. 70 | # This patterns also effect to html_static_path and html_extra_path 71 | exclude_patterns = [] 72 | 73 | # The name of the Pygments (syntax highlighting) style to use. 74 | pygments_style = "sphinx" 75 | 76 | # If true, `todo` and `todoList` produce output, else they produce nothing. 77 | todo_include_todos = True 78 | 79 | # -- Options for HTML output ---------------------------------------------- 80 | 81 | # The theme to use for HTML and HTML Help pages. See the documentation for 82 | # a list of builtin themes. 83 | # 84 | html_theme = "alabaster" 85 | 86 | # Theme options are theme-specific and customize the look and feel of a theme 87 | # further. For a list of options available for each theme, see the 88 | # documentation. 89 | # 90 | # html_theme_options = {} 91 | 92 | # Add any paths that contain custom static files (such as style sheets) here, 93 | # relative to this directory. They are copied after the builtin static files, 94 | # so a file named "default.css" will overwrite the builtin "default.css". 95 | html_static_path = [".static"] 96 | 97 | # Custom sidebar templates, must be a dictionary that maps document names 98 | # to template names. 99 | # 100 | # This is required for the alabaster theme 101 | # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars 102 | html_sidebars = { 103 | "**": [ 104 | "about.html", 105 | "navigation.html", 106 | "relations.html", # needs 'show_related': True theme option to display 107 | "searchbox.html", 108 | "donate.html", 109 | ] 110 | } 111 | 112 | # -- Options for HTMLHelp output ------------------------------------------ 113 | 114 | # Output file base name for HTML help builder. 115 | htmlhelp_basename = "openrouteservice-pydoc" 116 | 117 | # -- Options for LaTeX output --------------------------------------------- 118 | 119 | latex_elements = { 120 | # The paper size ('letterpaper' or 'a4paper'). 121 | # 122 | # 'papersize': 'letterpaper', 123 | # The font size ('10pt', '11pt' or '12pt'). 124 | # 125 | # 'pointsize': '10pt', 126 | # Additional stuff for the LaTeX preamble. 127 | # 128 | # 'preamble': '', 129 | # Latex figure (float) alignment 130 | # 131 | # 'figure_align': 'htbp', 132 | } 133 | 134 | # Grouping the document tree into LaTeX files. List of tuples 135 | # (source start file, target name, title, 136 | # author, documentclass [howto, manual, or own class]). 137 | latex_documents = [ 138 | ( 139 | master_doc, 140 | "openrouteservice-py.tex", 141 | "openrouteservice-py Documentation", 142 | "HeiGIT gGmbH", 143 | "manual", 144 | ), 145 | ] 146 | 147 | # -- Options for manual page output --------------------------------------- 148 | 149 | # One entry per manual page. List of tuples 150 | # (source start file, name, description, authors, manual section). 151 | man_pages = [ 152 | ( 153 | master_doc, 154 | "openrouteservice-py", 155 | "openrouteservice-py Documentation", 156 | [author], 157 | 1, 158 | ) 159 | ] 160 | 161 | # -- Options for Texinfo output ------------------------------------------- 162 | 163 | # Grouping the document tree into Texinfo files. List of tuples 164 | # (source start file, target name, title, author, 165 | # dir menu entry, description, category) 166 | texinfo_documents = [ 167 | ( 168 | master_doc, 169 | "openrouteservice-py", 170 | "openrouteservice-py Documentation", 171 | author, 172 | "openrouteservice-py", 173 | "One line description of project.", 174 | "Miscellaneous", 175 | ), 176 | ] 177 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. openrouteservice-py documentation master file, created by 2 | sphinx-quickstart on Wed Jan 31 20:43:55 2018. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | 7 | .. include:: ../../README.rst 8 | .. include:: openrouteservice.rst 9 | 10 | * :ref:`genindex` 11 | * :ref:`modindex` 12 | * :ref:`search` 13 | 14 | 15 | Indices and tables 16 | ================== 17 | 18 | .. toctree:: 19 | :maxdepth: 4 20 | 21 | readme_link 22 | openrouteservice 23 | -------------------------------------------------------------------------------- /docs/source/modules.rst: -------------------------------------------------------------------------------- 1 | docs 2 | ==== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | -------------------------------------------------------------------------------- /docs/source/openrouteservice.rst: -------------------------------------------------------------------------------- 1 | Library reference 2 | ======================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | openrouteservice\.client module 8 | ------------------------------- 9 | 10 | .. automodule:: openrouteservice.client 11 | :members: 12 | :exclude-members: directions, isochrones, distance_matrix, places, pelias_reverse, pelias_search, pelias_structured, pelias_autocomplete, elevation_line, elevation_point, optimization 13 | :show-inheritance: 14 | 15 | openrouteservice\.convert module 16 | -------------------------------- 17 | 18 | .. automodule:: openrouteservice.convert 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | openrouteservice\.directions module 24 | ----------------------------------- 25 | 26 | .. automodule:: openrouteservice.directions 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | openrouteservice\.isochrones module 32 | ----------------------------------- 33 | 34 | .. automodule:: openrouteservice.isochrones 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | openrouteservice\.distance\_matrix module 40 | ----------------------------------------- 41 | 42 | .. automodule:: openrouteservice.distance_matrix 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | openrouteservice\.geocode module 48 | ---------------------------------- 49 | 50 | .. automodule:: openrouteservice.geocode 51 | :members: 52 | :undoc-members: 53 | :show-inheritance: 54 | 55 | openrouteservice\.elevation module 56 | ---------------------------------- 57 | 58 | .. automodule:: openrouteservice.elevation 59 | :members: 60 | :undoc-members: 61 | :show-inheritance: 62 | 63 | openrouteservice\.places module 64 | -------------------------------- 65 | 66 | .. automodule:: openrouteservice.places 67 | :members: 68 | :undoc-members: 69 | :show-inheritance: 70 | 71 | openrouteservice\.optimization module 72 | ------------------------------------ 73 | 74 | .. automodule:: openrouteservice.optimization 75 | :members: 76 | :undoc-members: 77 | :show-inheritance: 78 | 79 | openrouteservice\.exceptions module 80 | ----------------------------------- 81 | 82 | .. automodule:: openrouteservice.exceptions 83 | :members: 84 | :undoc-members: 85 | :show-inheritance: 86 | 87 | 88 | Module contents 89 | --------------- 90 | 91 | .. automodule:: openrouteservice 92 | :members: 93 | :undoc-members: 94 | :show-inheritance: 95 | -------------------------------------------------------------------------------- /docs/source/readme_link.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../../README.rst 2 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | # for https://mybinder.org 2 | # from https://github.com/binder-examples/python-conda_pip/blob/master/environment.yml 3 | name: orspy-examples 4 | channels: 5 | - conda-forge 6 | dependencies: 7 | - python 8 | - folium 9 | - pip: 10 | - openrouteservice 11 | -------------------------------------------------------------------------------- /openrouteservice/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2014 Google Inc. All rights reserved. 3 | # 4 | # Modifications Copyright (C) 2018 HeiGIT, University of Heidelberg. 5 | # 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 8 | # use this file except in compliance with the License. You may obtain a copy of 9 | # the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16 | # License for the specific language governing permissions and limitations under 17 | # the License. 18 | # 19 | 20 | """Initialize openrouteservice.""" 21 | import pkg_resources 22 | 23 | __version__ = pkg_resources.get_distribution("openrouteservice").version 24 | 25 | 26 | def get_ordinal(number): 27 | """Produces an ordinal (1st, 2nd, 3rd, 4th) from a number.""" 28 | 29 | if number == 1: 30 | return "st" 31 | elif number == 2: 32 | return "nd" 33 | elif number == 3: 34 | return "rd" 35 | else: 36 | return "th" 37 | 38 | 39 | from openrouteservice.client import Client # noqa 40 | -------------------------------------------------------------------------------- /openrouteservice/client.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2014 Google Inc. All rights reserved. 3 | # 4 | # Modifications Copyright (C) 2018 HeiGIT, University of Heidelberg. 5 | # 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 8 | # use this file except in compliance with the License. You may obtain a copy of 9 | # the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16 | # License for the specific language governing permissions and limitations under 17 | # the License. 18 | # 19 | """Core client functionality, common across all API requests.""" 20 | 21 | from datetime import datetime 22 | from datetime import timedelta 23 | from urllib.parse import urlencode 24 | import cgi 25 | import functools 26 | import requests 27 | import json 28 | import random 29 | import time 30 | import warnings 31 | 32 | from openrouteservice import exceptions, __version__, get_ordinal 33 | 34 | _USER_AGENT = "ORSClientPython.v{}".format(__version__) 35 | _DEFAULT_BASE_URL = "https://api.openrouteservice.org" 36 | 37 | _RETRIABLE_STATUSES = set([503]) # noqa 38 | 39 | 40 | class Client: 41 | """Performs requests to the ORS API services.""" 42 | 43 | def __init__( 44 | self, 45 | key=None, 46 | base_url=_DEFAULT_BASE_URL, 47 | timeout=60, 48 | retry_timeout=60, 49 | requests_kwargs=None, 50 | retry_over_query_limit=True, 51 | ): 52 | """ 53 | Initialize the openrouteservice client. 54 | 55 | :param key: ORS API key. 56 | :type key: string 57 | 58 | :param base_url: The base URL for the request. Defaults to the ORS API 59 | server. Should not have a trailing slash. 60 | :type base_url: string 61 | 62 | :param timeout: Combined connect and read timeout for HTTP requests, in 63 | seconds. Specify "None" for no timeout. 64 | :type timeout: int 65 | 66 | :param retry_timeout: Timeout across multiple retriable requests, in 67 | seconds. 68 | :type retry_timeout: int 69 | 70 | :param requests_kwargs: Extra keyword arguments for the requests 71 | library, which among other things allow for proxy auth to be 72 | implemented. See the official requests docs for more info: 73 | http://docs.python-requests.org/en/latest/api/#main-interface 74 | :type requests_kwargs: dict 75 | 76 | :param retry_over_query_limit: If True, the client will retry when query 77 | limit is reached (HTTP 429). Default False. 78 | :type retry_over_query_limit: bool 79 | """ 80 | 81 | self._session = requests.Session() 82 | self._key = key 83 | self._base_url = base_url 84 | 85 | if self._base_url == _DEFAULT_BASE_URL and key is None: 86 | raise ValueError( 87 | "No API key was specified. Please visit https://openrouteservice.org/sign-up to create one." 88 | ) 89 | 90 | self._timeout = timeout 91 | self._retry_over_query_limit = retry_over_query_limit 92 | self._retry_timeout = timedelta(seconds=retry_timeout) 93 | self._requests_kwargs = requests_kwargs or {} 94 | self._requests_kwargs.update( 95 | { 96 | "headers": { 97 | "User-Agent": _USER_AGENT, 98 | "Content-type": "application/json", 99 | "Authorization": self._key, 100 | }, 101 | "timeout": self._timeout, 102 | } 103 | ) 104 | 105 | self._req = None 106 | 107 | def request( 108 | self, 109 | url, 110 | get_params=None, 111 | first_request_time=None, 112 | retry_counter=0, 113 | requests_kwargs=None, 114 | post_json=None, 115 | dry_run=None, 116 | ): 117 | """ 118 | Performs HTTP GET/POST with credentials, returning the body as JSON. 119 | 120 | :param url: URL path for the request. Should begin with a slash. 121 | :type url: string 122 | 123 | :param get_params: HTTP GET parameters. 124 | :type get_params: dict or list of key/value tuples 125 | 126 | :param first_request_time: The time of the first request (None if no 127 | retries have occurred). 128 | :type first_request_time: datetime.datetime 129 | 130 | :param retry_counter: The number of this retry, or zero for first attempt. 131 | :type retry_counter: int 132 | 133 | :param requests_kwargs: Same extra keywords arg for requests as per 134 | __init__, but provided here to allow overriding internally on a 135 | per-request basis. 136 | :type requests_kwargs: dict 137 | 138 | :param post_json: HTTP POST parameters. Only specified by calling method. 139 | :type post_json: dict 140 | 141 | :param dry_run: If 'true', only prints URL and parameters. 'true' or 'false'. 142 | :type dry_run: string 143 | 144 | :raises ApiError: when the API returns an error. 145 | :raises Timeout: if the request timed out. 146 | 147 | :rtype: dict from JSON response. 148 | """ 149 | 150 | if not first_request_time: 151 | first_request_time = datetime.now() 152 | 153 | elapsed = datetime.now() - first_request_time 154 | if elapsed > self._retry_timeout: 155 | raise exceptions.Timeout() 156 | 157 | if retry_counter > 0: 158 | # 0.5 * (1.5 ^ i) is an increased sleep time of 1.5x per iteration, 159 | # starting at 0.5s when retry_counter=1. The first retry will occur 160 | # at 1, so subtract that first. 161 | delay_seconds = 1.5 ** (retry_counter - 1) 162 | 163 | # Jitter this value by 50% and pause. 164 | time.sleep(delay_seconds * (random.random() + 0.5)) 165 | 166 | authed_url = self._generate_auth_url( 167 | url, 168 | get_params, 169 | ) 170 | 171 | # Default to the client-level self.requests_kwargs, with method-level 172 | # requests_kwargs arg overriding. 173 | requests_kwargs = requests_kwargs or {} 174 | final_requests_kwargs = dict(self._requests_kwargs, **requests_kwargs) 175 | 176 | # Determine GET/POST. 177 | requests_method = self._session.get 178 | 179 | if post_json is not None: 180 | requests_method = self._session.post 181 | final_requests_kwargs["json"] = post_json 182 | 183 | # Only print URL and parameters for dry_run 184 | if dry_run: 185 | print( # noqa 186 | "url:\n{}\nHeaders:\n{}".format( 187 | self._base_url + authed_url, 188 | json.dumps(final_requests_kwargs, indent=2), 189 | ) 190 | ) 191 | return 192 | 193 | try: 194 | response = requests_method( 195 | self._base_url + authed_url, **final_requests_kwargs 196 | ) 197 | self._req = response.request 198 | 199 | except requests.exceptions.Timeout: # pragma: no cover 200 | raise exceptions.Timeout() 201 | 202 | if response.status_code in _RETRIABLE_STATUSES: 203 | # Retry request. 204 | warnings.warn( 205 | "Server down.\nRetrying for the {0}{1} time.".format( 206 | retry_counter + 1, get_ordinal(retry_counter + 1) 207 | ), 208 | UserWarning, 209 | stacklevel=1, 210 | ) 211 | 212 | return self.request( 213 | url, 214 | get_params, 215 | first_request_time, 216 | retry_counter + 1, 217 | requests_kwargs, 218 | post_json, 219 | ) 220 | 221 | try: 222 | result = self._get_body(response) 223 | 224 | return result 225 | except exceptions._RetriableRequest as e: 226 | if ( 227 | isinstance(e, exceptions._OverQueryLimit) 228 | and not self._retry_over_query_limit # noqa 229 | ): 230 | raise 231 | 232 | warnings.warn( 233 | "Rate limit exceeded. Retrying for the {0}{1} time.".format( 234 | retry_counter + 1, get_ordinal(retry_counter + 1) 235 | ), 236 | UserWarning, 237 | stacklevel=1, 238 | ) 239 | # Retry request. 240 | return self.request( 241 | url, 242 | get_params, 243 | first_request_time, 244 | retry_counter + 1, 245 | requests_kwargs, 246 | post_json, 247 | ) 248 | 249 | @property 250 | def req(self): 251 | """Returns request object. Can be used in case of request failure.""" 252 | return self._req 253 | 254 | @staticmethod 255 | def _get_body(response): 256 | """Returns the body of a response object, raises status code exceptions if necessary.""" 257 | content_type = response.headers["Content-Type"] 258 | mime_type, _ = cgi.parse_header(content_type) 259 | if mime_type == "application/gpx+xml": 260 | body = response.text 261 | else: 262 | try: 263 | body = response.json() 264 | except json.JSONDecodeError: # pragma: no cover 265 | raise exceptions.HTTPError(response.status_code) 266 | 267 | # error = body.get('error') 268 | status_code = response.status_code 269 | 270 | if status_code == 429: 271 | raise exceptions._OverQueryLimit(status_code, body) 272 | if status_code != 200: 273 | raise exceptions.ApiError(status_code, body) 274 | 275 | return body 276 | 277 | @staticmethod 278 | def _generate_auth_url(path, params): 279 | """ 280 | Returns the path and query string portion of the request URL, first adding any necessary parameters. 281 | 282 | :param path: The path portion of the URL. 283 | :type path: string 284 | 285 | :param params: URL parameters. 286 | :type params: dict or list of key/value tuples 287 | 288 | :rtype: string 289 | 290 | """ 291 | 292 | if type(params) is dict: 293 | params = sorted(dict(**params).items()) 294 | elif params is None: 295 | return path 296 | 297 | return path + "?" + _urlencode_params(params) 298 | 299 | 300 | from openrouteservice.directions import directions # noqa 301 | from openrouteservice.distance_matrix import distance_matrix # noqa 302 | from openrouteservice.elevation import elevation_point # noqa 303 | from openrouteservice.elevation import elevation_line # noqa 304 | from openrouteservice.isochrones import isochrones # noqa 305 | from openrouteservice.geocode import pelias_search # noqa 306 | from openrouteservice.geocode import pelias_autocomplete # noqa 307 | from openrouteservice.geocode import pelias_structured # noqa 308 | from openrouteservice.geocode import pelias_reverse # noqa 309 | from openrouteservice.places import places # noqa 310 | from openrouteservice.optimization import optimization # noqa 311 | 312 | 313 | def _make_api_method(func): 314 | """ 315 | Provides a single entry point for modifying all API methods. 316 | 317 | For now this is limited to allowing the client object to be modified 318 | with an `extra_params` keyword arg to each method, that is then used 319 | as the params for each web service request. 320 | 321 | Please note that this is an unsupported feature for advanced use only. 322 | It's also currently incompatibile with multiple threads, see GH #160. 323 | """ 324 | 325 | @functools.wraps(func) 326 | def wrapper(*args, **kwargs): 327 | args[0]._extra_params = kwargs.pop("extra_params", None) 328 | result = func(*args, **kwargs) 329 | try: 330 | del args[0]._extra_params 331 | except AttributeError: # pragma: no cover 332 | pass 333 | return result 334 | 335 | return wrapper 336 | 337 | 338 | Client.directions = _make_api_method(directions) 339 | Client.distance_matrix = _make_api_method(distance_matrix) 340 | Client.elevation_point = _make_api_method(elevation_point) 341 | Client.elevation_line = _make_api_method(elevation_line) 342 | Client.isochrones = _make_api_method(isochrones) 343 | Client.pelias_search = _make_api_method(pelias_search) 344 | Client.pelias_autocomplete = _make_api_method(pelias_autocomplete) 345 | Client.pelias_structured = _make_api_method(pelias_structured) 346 | Client.pelias_reverse = _make_api_method(pelias_reverse) 347 | Client.places = _make_api_method(places) 348 | Client.optimization = _make_api_method(optimization) 349 | 350 | 351 | def _urlencode_params(params): 352 | """URL encodes the parameters. 353 | 354 | :param params: The parameters 355 | :type params: list of key/value tuples. 356 | 357 | :rtype: string 358 | """ 359 | params = [(key, val) for key, val in params] 360 | # Unquote unreserved chars which are incorrectly quoted 361 | # by urllib.urlencode, causing invalid auth signatures. See GH #72 362 | # for more info. 363 | return requests.utils.unquote_unreserved(urlencode(params)) 364 | -------------------------------------------------------------------------------- /openrouteservice/convert.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2014 Google Inc. All rights reserved. 3 | # 4 | # Modifications Copyright (C) 2018 HeiGIT, University of Heidelberg. 5 | # 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 8 | # use this file except in compliance with the License. You may obtain a copy of 9 | # the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16 | # License for the specific language governing permissions and limitations under 17 | # the License. 18 | # 19 | """Converts Python types to string representations suitable for ORS API server.""" 20 | 21 | 22 | def _pipe_list(arg): 23 | """Convert list of values to pipe-delimited string.""" 24 | if not _is_list(arg): 25 | raise TypeError( 26 | "Expected a list or tuple, " "but got {}".format(type(arg).__name__) 27 | ) 28 | return "|".join(map(str, arg)) 29 | 30 | 31 | def _comma_list(arg): 32 | """Convert list to comma-separated string.""" 33 | if not _is_list(arg): 34 | raise TypeError( 35 | "Expected a list or tuple, " "but got {}".format(type(arg).__name__) 36 | ) 37 | return ",".join(map(str, arg)) 38 | 39 | 40 | def _convert_bool(boolean): 41 | """Convert to stringified boolean.""" 42 | 43 | return str(boolean).lower() 44 | 45 | 46 | def _format_float(arg): 47 | """Formats a float value to be as short as possible. 48 | 49 | Trims extraneous trailing zeros and period to give API 50 | args the best possible chance of fitting within 2000 char 51 | URL length restrictions. 52 | 53 | For example: 54 | 55 | format_float(40) -> "40" 56 | format_float(40.0) -> "40" 57 | format_float(40.1) -> "40.1" 58 | format_float(40.001) -> "40.001" 59 | format_float(40.0010) -> "40.001" 60 | 61 | :param arg: The lat or lng float. 62 | :type arg: float 63 | 64 | :rtype: string 65 | """ 66 | return "{}".format(round(float(arg), 6)).rstrip("0").rstrip(".") 67 | 68 | 69 | def _build_coords(arg): 70 | """ 71 | Converts one or many lng/lat pair(s) to a comma-separated, pipe delimited string. 72 | 73 | Coordinates will be rounded to 5 digits. 74 | 75 | For example: 76 | 77 | convert.build_coords([(151.2069902,-33.8674869),(2.352315,48.513158)]) 78 | # '151.20699,-33.86749|2.35232,48.51316' 79 | 80 | :param arg: The lat/lon pair(s). 81 | :type arg: list or tuple 82 | 83 | :rtype: str 84 | """ 85 | if _is_list(arg): 86 | return _pipe_list(_concat_coords(arg)) 87 | else: 88 | raise TypeError( 89 | "Expected a list or tuple of lng/lat tuples or lists, " 90 | "but got {}".format(type(arg).__name__) 91 | ) 92 | 93 | 94 | def _concat_coords(arg): 95 | """Turn the passed coordinate tuple(s) in comma separated coordinate tuple(s). 96 | 97 | :param arg: coordinate pair(s) 98 | :type arg: list or tuple 99 | 100 | :rtype: list of strings 101 | """ 102 | if all(_is_list(tup) for tup in arg): 103 | # Check if arg is a list/tuple of lists/tuples 104 | return [_comma_list(map(_format_float, tup)) for tup in arg] 105 | else: 106 | return [_comma_list(_format_float(coord) for coord in arg)] 107 | 108 | 109 | def _is_list(arg): 110 | """Checks if arg is list-like.""" 111 | if isinstance(arg, dict): 112 | return False 113 | if isinstance(arg, str): # Python 3-only, as str has __iter__ 114 | return False 115 | return ( 116 | not _has_method(arg, "strip") 117 | and _has_method(arg, "__getitem__") # noqa 118 | or _has_method(arg, "__iter__") # noqa 119 | ) 120 | 121 | 122 | def _has_method(arg, method): 123 | """Returns true if the given object has a method with the given name. 124 | 125 | :param arg: the object 126 | 127 | :param method: the method name 128 | :type method: string 129 | 130 | :rtype: bool 131 | """ 132 | return hasattr(arg, method) and callable(getattr(arg, method)) 133 | 134 | 135 | def decode_polyline(polyline, is3d=False): 136 | """Decodes a Polyline string into a GeoJSON geometry. 137 | 138 | :param polyline: An encoded polyline, only the geometry. 139 | :type polyline: string 140 | 141 | :param is3d: Specifies if geometry contains Z component. 142 | :type is3d: boolean 143 | 144 | :returns: GeoJSON Linestring geometry 145 | :rtype: dict 146 | """ 147 | points = [] 148 | index = lat = lng = z = 0 149 | 150 | while index < len(polyline): 151 | result = 1 152 | shift = 0 153 | while True: 154 | b = ord(polyline[index]) - 63 - 1 155 | index += 1 156 | result += b << shift 157 | shift += 5 158 | if b < 0x1F: 159 | break 160 | lat += (~result >> 1) if (result & 1) != 0 else (result >> 1) 161 | 162 | result = 1 163 | shift = 0 164 | while True: 165 | b = ord(polyline[index]) - 63 - 1 166 | index += 1 167 | result += b << shift 168 | shift += 5 169 | if b < 0x1F: 170 | break 171 | lng += ~(result >> 1) if (result & 1) != 0 else (result >> 1) 172 | 173 | if is3d: 174 | result = 1 175 | shift = 0 176 | while True: 177 | b = ord(polyline[index]) - 63 - 1 178 | index += 1 179 | result += b << shift 180 | shift += 5 181 | if b < 0x1F: 182 | break 183 | if (result & 1) != 0: 184 | z += ~(result >> 1) 185 | else: 186 | z += result >> 1 187 | 188 | points.append( 189 | [ 190 | round(lng * 1e-5, 6), 191 | round(lat * 1e-5, 6), 192 | round(z * 1e-2, 1), 193 | ] 194 | ) 195 | 196 | else: 197 | points.append([round(lng * 1e-5, 6), round(lat * 1e-5, 6)]) 198 | 199 | geojson = {"type": "LineString", "coordinates": points} 200 | 201 | return geojson 202 | -------------------------------------------------------------------------------- /openrouteservice/deprecation.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright (C) 2018 HeiGIT, University of Heidelberg. 3 | # 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 6 | # use this file except in compliance with the License. You may obtain a copy of 7 | # the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations under 15 | # the License. 16 | # 17 | 18 | """Prints a deprecation warning.""" 19 | 20 | import warnings 21 | 22 | 23 | def warning(old_name, new_name): 24 | """Deprecation warning.""" 25 | 26 | warnings.warn( 27 | "{} will be deprecated in v2.0. Please use {} instead".format( 28 | old_name, new_name 29 | ), 30 | DeprecationWarning, 31 | stacklevel=2, 32 | ) 33 | -------------------------------------------------------------------------------- /openrouteservice/directions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2014 Google Inc. All rights reserved. 3 | # 4 | # Modifications Copyright (C) 2018 HeiGIT, University of Heidelberg. 5 | # 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 8 | # use this file except in compliance with the License. You may obtain a copy of 9 | # the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16 | # License for the specific language governing permissions and limitations under 17 | # the License. 18 | # 19 | """Performs requests to the ORS directions API.""" 20 | 21 | from openrouteservice import deprecation 22 | from openrouteservice.optimization import optimization, Job, Vehicle 23 | 24 | import warnings 25 | 26 | 27 | def directions( 28 | client, 29 | coordinates, 30 | profile="driving-car", 31 | format_out=None, 32 | format="json", 33 | preference=None, 34 | units=None, 35 | language=None, 36 | geometry=None, 37 | geometry_simplify=None, 38 | instructions=None, 39 | instructions_format=None, 40 | alternative_routes=None, 41 | roundabout_exits=None, 42 | attributes=None, 43 | maneuvers=None, 44 | radiuses=None, 45 | bearings=None, 46 | skip_segments=None, 47 | continue_straight=None, 48 | elevation=None, 49 | extra_info=None, 50 | maximum_speed=None, 51 | suppress_warnings=None, 52 | optimized=None, 53 | optimize_waypoints=None, 54 | options=None, 55 | validate=True, 56 | dry_run=None, 57 | ): 58 | """Get directions between an origin point and a destination point. 59 | 60 | For more information, visit https://go.openrouteservice.org/documentation/. 61 | 62 | :param coordinates: The coordinates tuple the route should be calculated 63 | from. In order of visit. 64 | :type origin: list or tuple of coordinate lists or tuples 65 | 66 | :param profile: Specifies the mode of transport to use when calculating 67 | directions. One of ["driving-car", "driving-hgv", "foot-walking", 68 | "foot-hiking", "cycling-regular", "cycling-road","cycling-mountain", 69 | "cycling-electric",]. Default "driving-car". 70 | :type profile: string 71 | 72 | :param format: Specifies the response format. One of ['json', 'geojson', 'gpx']. Default "json". 73 | Geometry format for "json" is Google's encodedpolyline. The GPX schema the response is validated 74 | against can be found here: 75 | https://raw.githubusercontent.com/GIScience/openrouteservice-schema/master/gpx/v2/ors-gpx.xsd. 76 | :type format: str 77 | 78 | :param format_out: DEPRECATED. 79 | :type format: str 80 | 81 | :param preference: Specifies the routing preference. One of ["fastest, "shortest", 82 | "recommended"]. Default "fastest". 83 | :type preference: string 84 | 85 | :param units: Specifies the distance unit. One of ["m", "km", "mi"]. Default "m". 86 | :type units: string 87 | 88 | :param language: Language for routing instructions. One of ["en", "de", "cn", 89 | "es", "ru", "dk", "fr", "it", "nl", "br", "se", "tr", "gr"]. Default "en". 90 | :type language: string 91 | 92 | :param language: The language in which to return results. 93 | :type language: string 94 | 95 | :param geometry: Specifies whether geometry should be returned. Default True. 96 | :type geometry: boolean 97 | 98 | :param geometry_simplify: Specifies whether to simplify the geometry. 99 | Default False. 100 | :type geometry_simplify: boolean 101 | 102 | :param instructions: Specifies whether to return turn-by-turn instructions. 103 | Default True. 104 | :type instructions: boolean 105 | 106 | :param instructions_format: Specifies the the output format for instructions. 107 | One of ["text", "html"]. Default "text". 108 | :type instructions_format: string 109 | 110 | :param alternative_routes: Specifies whether alternative routes are computed, 111 | and parameters for the algorithm determining suitable alternatives. Expects 112 | 3 keys: share_factor (float), target_count (int), weight_factor (float). 113 | More on https://openrouteservice.org/dev/#/api-docs/v2/directions/{profile}/geojson/post. 114 | :type alternative_routes: dict[int|float] 115 | 116 | :param roundabout_exits: Provides bearings of the entrance and all passed 117 | roundabout exits. Adds the 'exit_bearings' array to the 'step' object 118 | in the response. Default False. 119 | :type roundabout_exits: boolean 120 | 121 | :param attributes: Returns route attributes on ["detourfactor", "percentage"]. 122 | Must be a list of strings. Default None. 123 | :type attributes: list or tuple of strings 124 | 125 | :param maneuvers: Specifies whether the maneuver object is included into the step object or not. Default: false. 126 | :type maneuvers: bool 127 | 128 | :param radiuses: A list of maximum distances (measured in 129 | meters) that limit the search of nearby road segments to every given waypoint. 130 | The values must be greater than 0, the value of -1 specifies no limit in 131 | the search. The number of radiuses must correspond to the number of waypoints. 132 | Default None. 133 | :type radiuses: list or tuple 134 | 135 | :param bearings: Specifies a list of pairs (bearings and 136 | deviations) to filter the segments of the road network a waypoint can 137 | snap to. For example bearings=[[45,10],[120,20]]. Each pair is a 138 | comma-separated list that can consist of one or two float values, where 139 | the first value is the bearing and the second one is the allowed deviation 140 | from the bearing. The bearing can take values between 0 and 360 clockwise 141 | from true north. If the deviation is not set, then the default value of 142 | 100 degrees is used. The number of pairs must correspond to the number 143 | of waypoints. Setting optimized=false is mandatory for this feature to 144 | work for all profiles. The number of bearings corresponds to the length 145 | of waypoints-1 or waypoints. If the bearing information for the last waypoint 146 | is given, then this will control the sector from which the destination 147 | waypoint may be reached. 148 | :type bearings: list or tuple or lists or tuples 149 | 150 | :param skip_segments: Specifies the segments that should be skipped in the route calculation. 151 | A segment is the connection between two given coordinates and the counting starts with 1 152 | for the connection between the first and second coordinate. 153 | :type skip_segments: list[int] 154 | 155 | :param continue_straight: Forces the route to keep going straight at waypoints 156 | restricting u-turns even if u-turns would be faster. This setting 157 | will work for all profiles except for driving-*. In this case you will 158 | have to set optimized=false for it to work. True or False. Default False. 159 | :type continue_straight: boolean 160 | 161 | :param elevation: Specifies whether to return elevation values for points. 162 | Default False. 163 | :type elevation: boolean 164 | 165 | :param extra_info: Returns additional information on ["steepness", "suitability", 166 | "surface", "waycategory", "waytype", "tollways", "traildifficulty", "roadaccessrestrictions"]. 167 | Must be a list of strings. Default None. 168 | :type extra_info: list or tuple of strings 169 | 170 | :param maximum_speed: The maximum speed to drive (or walk). Default None. 171 | :type maximum_speed: int 172 | 173 | :param suppress_warnings: Tells the system to not return any warning messages and corresponding extra_info. 174 | For false the extra information can still be explicitly requested by adding it with the extra_info parameter. 175 | :type suppress_warnings: bool 176 | 177 | :param optimized: If set False, forces to not use Contraction Hierarchies. 178 | :type optimized: bool 179 | 180 | :param options: Refer to https://openrouteservice.org/dev/#/api-docs/v2/directions/{profile}/geojson/post for 181 | detailed documentation. Construct your own dict() following the example 182 | of the minified options object. Will be converted to json automatically. 183 | :type options: dict 184 | 185 | :param optimize_waypoints: If True, a `Vroom `_ instance (ORS optimization 186 | endpoint) will optimize the `via` waypoints, i.e. all coordinates between the first and the last. It assumes 187 | the first coordinate to be the start location and the last coordinate to be the end location. Only requests with 188 | a minimum of 4 coordinates, no routing options and fastest weighting. Default False. 189 | :type optimize_waypoints: bool 190 | 191 | :param validate: Specifies whether parameters should be validated before sending the request. Default True. 192 | :type validate: bool 193 | 194 | :param dry_run: Print URL and parameters without sending the request. 195 | :param dry_run: boolean 196 | 197 | :raises ValueError: When parameter has wrong value. 198 | :raises TypeError: When parameter is of wrong type. 199 | 200 | :returns: sanitized set of parameters 201 | :rtype: call to Client.request() 202 | """ 203 | 204 | # call optimization endpoint and get new order of waypoints 205 | if optimize_waypoints is not None and not dry_run: 206 | if len(coordinates) <= 3: 207 | warnings.warn( # noqa: B028 208 | "Less than 4 coordinates, nothing to optimize!", UserWarning 209 | ) 210 | elif options: 211 | warnings.warn( # noqa: B028 212 | "Options are not compatible with optimization.", UserWarning 213 | ) 214 | elif preference == "shortest": 215 | warnings.warn( # noqa: B028 216 | "Shortest is not compatible with optimization.", UserWarning 217 | ) 218 | else: 219 | coordinates = _optimize_waypoint_order(client, coordinates, profile) 220 | 221 | params = {"coordinates": coordinates} 222 | 223 | if format_out: 224 | deprecation.warning("format_out", "format") 225 | 226 | format = format_out or format 227 | 228 | if preference: 229 | params["preference"] = preference 230 | 231 | if units: 232 | params["units"] = units 233 | 234 | if language: 235 | params["language"] = language 236 | 237 | if geometry is not None: 238 | params["geometry"] = geometry 239 | 240 | if geometry_simplify is not None: 241 | params["geometry_simplify"] = geometry_simplify 242 | 243 | if instructions is not None: 244 | params["instructions"] = instructions 245 | 246 | if instructions_format: 247 | params["instructions_format"] = instructions_format 248 | 249 | if alternative_routes: 250 | params["alternative_routes"] = alternative_routes 251 | 252 | if roundabout_exits is not None: 253 | params["roundabout_exits"] = roundabout_exits 254 | 255 | if attributes: 256 | params["attributes"] = attributes 257 | 258 | if radiuses: 259 | params["radiuses"] = radiuses 260 | 261 | if maneuvers is not None: 262 | params["maneuvers"] = maneuvers 263 | 264 | if bearings: 265 | params["bearings"] = bearings 266 | 267 | if skip_segments: 268 | params["skip_segments"] = skip_segments 269 | 270 | if continue_straight is not None: 271 | params["continue_straight"] = continue_straight 272 | 273 | if elevation is not None: 274 | params["elevation"] = elevation 275 | 276 | if extra_info: 277 | params["extra_info"] = extra_info 278 | 279 | if maximum_speed is not None: 280 | params["maximum_speed"] = maximum_speed 281 | 282 | if suppress_warnings is not None: 283 | params["suppress_warnings"] = suppress_warnings 284 | 285 | if optimized is not None: 286 | if (bearings or continue_straight) and optimized in (True, "true"): 287 | params["optimized"] = "false" 288 | print( # noqa 289 | "Set optimized='false' due to incompatible parameter settings." 290 | ) 291 | else: 292 | params["optimized"] = optimized 293 | 294 | if options: 295 | params["options"] = options 296 | 297 | return client.request( 298 | "/v2/directions/" + profile + "/" + format, 299 | {}, 300 | post_json=params, 301 | dry_run=dry_run, 302 | ) 303 | 304 | 305 | def _optimize_waypoint_order(client, coordinates, profile): 306 | start = coordinates[0] 307 | end = coordinates[-1] 308 | veh = [Vehicle(id=0, profile=profile, start=start, end=end)] 309 | 310 | jobs = [] 311 | for idx, coord in enumerate(coordinates[1:-1]): 312 | jobs.append(Job(id=idx, location=coord)) 313 | 314 | params = {"jobs": jobs, "vehicles": veh} 315 | 316 | optimization_res = optimization(client, **params) # pragma: no cover 317 | coordinates = [] # pragma: no cover 318 | for step in optimization_res["routes"][0]["steps"]: # pragma: no cover 319 | coordinates.append(step["location"]) # pragma: no cover 320 | return coordinates # pragma: no cover 321 | -------------------------------------------------------------------------------- /openrouteservice/distance_matrix.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2014 Google Inc. All rights reserved. 3 | # 4 | # Modifications Copyright (C) 2018 HeiGIT, University of Heidelberg. 5 | # 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 8 | # use this file except in compliance with the License. You may obtain a copy of 9 | # the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16 | # License for the specific language governing permissions and limitations under 17 | # the License. 18 | # 19 | """Performs requests to the ORS Matrix API.""" 20 | 21 | 22 | def distance_matrix( 23 | client, 24 | locations, 25 | profile="driving-car", 26 | sources=None, 27 | destinations=None, 28 | metrics=None, 29 | resolve_locations=None, 30 | units=None, 31 | optimized=None, 32 | validate=True, 33 | dry_run=None, 34 | ): 35 | """Gets travel distance and time for a matrix of origins and destinations. 36 | 37 | :param locations: One or more pairs of lng/lat values. 38 | :type locations: a single location, or a list of locations, where a 39 | location is a list or tuple of lng,lat values 40 | 41 | :param profile: Specifies the mode of transport to use when calculating 42 | directions. One of ["driving-car", "driving-hgv", "foot-walking", 43 | "foot-hiking", "cycling-regular", "cycling-road", 44 | "cycling-safe", "cycling-mountain", "cycling-tour", 45 | "cycling-electric",]. Default "driving-car". 46 | :type profile: string 47 | 48 | :param sources: A list of indices that refer to the list of locations 49 | (starting with 0). If not passed, all indices are considered. 50 | :type sources: list or tuple 51 | 52 | :param destinations: A list of indices that refer to the list of locations 53 | (starting with 0). If not passed, all indices are considered. 54 | :type destinations: list or tuple 55 | 56 | :param metrics: Specifies a list of returned metrics. One or more of ["distance", 57 | "duration"]. Default ['duration']. 58 | :type metrics: list of strings 59 | 60 | :param resolve_locations: Specifies whether given locations are resolved or 61 | not. If set 'true', every element in destinations and sources will 62 | contain the name element that identifies the name of the closest street. 63 | Default False. 64 | :type resolve_locations: boolean 65 | 66 | :param units: Specifies the unit system to use when displaying results. 67 | One of ["m", "km", "m"]. Default "m". 68 | :type units: string 69 | 70 | :param optimized: Specifies whether Dijkstra algorithm ('false') or any 71 | available technique to speed up shortest-path routing ('true') is used. 72 | For normal Dijkstra the number of visited nodes is limited to 100000. 73 | Default True 74 | :type optimized: boolean 75 | 76 | :param validate: Specifies whether parameters should be validated before sending the request. Default True. 77 | :type validate: bool 78 | 79 | :param dry_run: Print URL and parameters without sending the request. 80 | :param dry_run: boolean 81 | 82 | :raises ValueError: When profile parameter has wrong value. 83 | 84 | :rtype: call to Client.request() 85 | """ 86 | 87 | params = { 88 | "locations": locations, 89 | } 90 | 91 | if sources: 92 | params["sources"] = sources 93 | 94 | if destinations: 95 | params["destinations"] = destinations 96 | 97 | if profile: 98 | params["profile"] = profile 99 | 100 | if sources: 101 | params["sources"] = sources 102 | 103 | if destinations: 104 | params["destinations"] = destinations 105 | 106 | if metrics: 107 | params["metrics"] = metrics 108 | 109 | if resolve_locations is not None: 110 | params["resolve_locations"] = resolve_locations 111 | 112 | if units: 113 | params["units"] = units 114 | 115 | if optimized is not None: 116 | params["optimized"] = optimized 117 | 118 | return client.request( 119 | "/v2/matrix/" + profile + "/json", 120 | {}, 121 | post_json=params, 122 | dry_run=dry_run, 123 | ) 124 | -------------------------------------------------------------------------------- /openrouteservice/elevation.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright (C) 2018 HeiGIT, University of Heidelberg. 3 | # 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 6 | # use this file except in compliance with the License. You may obtain a copy of 7 | # the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations under 15 | # the License. 16 | """Performs requests to the ORS elevation API.""" 17 | 18 | 19 | def elevation_point( 20 | client, 21 | format_in, 22 | geometry, 23 | format_out="geojson", 24 | dataset="srtm", 25 | validate=True, 26 | dry_run=None, 27 | ): 28 | """ 29 | POSTs 2D point to be enriched with elevation. # noqa 30 | 31 | :param format_in: Format of input geometry. One of ['geojson', 32 | 'point'] 33 | :type format_in: string 34 | 35 | :param geometry: Point geometry 36 | :type geometry: depending on format_in, either list of coordinates or Point geojson 37 | 38 | :param format_out: Format of output geometry, one of ['geojson', 'point'] 39 | :type format_out: string 40 | 41 | :param dataset: Elevation dataset to be used. Currently only SRTM v4.1 available. 42 | :type dataset: string 43 | 44 | :param dry_run: Print URL and parameters without sending the request. 45 | :param dry_run: boolean 46 | 47 | :returns: correctly formatted parameters 48 | :rtype: Client.request() 49 | """ 50 | 51 | params = { 52 | "format_in": format_in, 53 | "geometry": geometry, 54 | "format_out": format_out, 55 | "dataset": dataset, 56 | } 57 | 58 | return client.request( 59 | "/elevation/point", {}, post_json=params, dry_run=dry_run 60 | ) 61 | 62 | 63 | def elevation_line( 64 | client, 65 | format_in, 66 | geometry, 67 | format_out="geojson", 68 | dataset="srtm", 69 | validate=True, 70 | dry_run=None, 71 | ): 72 | """ 73 | POSTs 2D point to be enriched with elevation. # noqa 74 | 75 | :param format_in: Format of input geometry. One of ['geojson', 76 | 'polyline', 'encodedpolyline'] 77 | :type format_in: string 78 | 79 | :param geometry: Point geometry 80 | :type geometry: depending on format_in, either list of coordinates, LineString 81 | geojson or string 82 | 83 | :param format_out: Format of output geometry, one of ['geojson', 84 | 'polyline', 'encodedpolyline'] 85 | :type format_out: string 86 | 87 | :param dataset: Elevation dataset to be used. Currently only SRTM v4.1 available. 88 | :type dataset: string 89 | 90 | :param dry_run: Print URL and parameters without sending the request. 91 | :param dry_run: boolean 92 | 93 | :returns: correctly formatted parameters 94 | :rtype: Client.request() 95 | """ 96 | 97 | params = { 98 | "format_in": format_in, 99 | "geometry": geometry, 100 | "format_out": format_out, 101 | "dataset": dataset, 102 | } 103 | 104 | return client.request( 105 | "/elevation/line", {}, post_json=params, dry_run=dry_run 106 | ) 107 | -------------------------------------------------------------------------------- /openrouteservice/exceptions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2014 Google Inc. All rights reserved. 3 | # 4 | # Modifications Copyright (C) 2018 HeiGIT, University of Heidelberg. 5 | # 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 8 | # use this file except in compliance with the License. You may obtain a copy of 9 | # the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16 | # License for the specific language governing permissions and limitations under 17 | # the License. 18 | # 19 | """Defines exceptions that are thrown by the ORS client.""" 20 | 21 | 22 | class ValidationError(Exception): 23 | """Something went wrong during cerberus validation.""" 24 | 25 | def __init__(self, errors): # noqa 26 | msg = "\n".join(["{}".format(str(errors))]) 27 | Exception.__init__(self, msg) 28 | 29 | 30 | class ApiError(Exception): 31 | """Represents an exception returned by the remote API.""" 32 | 33 | def __init__(self, status, message=None): # noqa 34 | self.status = status 35 | self.message = message 36 | 37 | def __str__(self): # noqa 38 | if self.message is None: 39 | return str(self.status) 40 | else: 41 | return "%s (%s)" % (self.status, self.message) # noqa 42 | 43 | 44 | class HTTPError(Exception): 45 | """An unexpected HTTP error occurred.""" 46 | 47 | def __init__(self, status_code): # noqa 48 | self.status_code = status_code 49 | 50 | def __str__(self): # noqa 51 | return "HTTP Error: %d" % self.status_code # noqa 52 | 53 | 54 | class Timeout(Exception): 55 | """The request timed out.""" 56 | 57 | pass 58 | 59 | 60 | class _RetriableRequest(Exception): 61 | """Signifies that the request can be retried.""" 62 | 63 | pass 64 | 65 | 66 | class _OverQueryLimit(ApiError, _RetriableRequest): 67 | """Signifies that the request failed because the client exceeded its query rate limit. 68 | 69 | Normally we treat this as a retriable condition, but we allow the calling code to specify that these requests should 70 | not be retried. 71 | """ 72 | 73 | pass 74 | -------------------------------------------------------------------------------- /openrouteservice/geocode.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright (C) 2018 HeiGIT, University of Heidelberg. 3 | # 4 | # Modifications Copyright (C) 2018 HeiGIT, University of Heidelberg. 5 | # 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 8 | # use this file except in compliance with the License. You may obtain a copy of 9 | # the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16 | # License for the specific language governing permissions and limitations under 17 | # the License. 18 | # 19 | """Performs requests to the ORS geocode API (direct Pelias clone).""" 20 | from openrouteservice import convert 21 | 22 | 23 | def pelias_search( 24 | client, 25 | text, 26 | focus_point=None, 27 | rect_min_x=None, 28 | rect_min_y=None, 29 | rect_max_x=None, 30 | rect_max_y=None, 31 | circle_point=None, 32 | circle_radius=None, 33 | sources=None, 34 | layers=None, 35 | country=None, 36 | size=None, 37 | validate=True, 38 | dry_run=None, 39 | ): 40 | """ 41 | Geocoding is the process of converting addresses into geographic coordinates. 42 | 43 | This endpoint queries directly against a Pelias instance. 44 | 45 | :param text: Full-text query against search endpoint. Required. 46 | :type text: string 47 | 48 | :param focus_point: Focusses the search to be around this point and gives 49 | results within a 100 km radius higher scores. 50 | :type query: list or tuple of (Long, Lat) 51 | 52 | :param rect_min_x: Min longitude by which to constrain request geographically. 53 | :type rect_min_x: float 54 | 55 | :param rect_min_y: Min latitude by which to constrain request geographically. 56 | :type rect_min_y: float 57 | 58 | :param rect_max_x: Max longitude by which to constrain request geographically. 59 | :type rect_max_x: float 60 | 61 | :param rect_max_y: Max latitude by which to constrain request geographically. 62 | :type rect_max_y: float 63 | 64 | :param circle_point: Geographical constraint in form a circle. 65 | :type circle_point: list or tuple of (Long, Lat) 66 | 67 | :param circle_radius: Radius of circle constraint in km. Default 50. 68 | :type circle_radius: integer 69 | 70 | :param sources: The originating source of the data. One or more of 71 | ['osm', 'oa', 'wof', 'gn']. Currently only 'osm', 'wof' and 'gn' are 72 | supported. 73 | :type sources: list of strings 74 | 75 | :param layers: The administrative hierarchy level for the query. Refer to 76 | https://github.com/pelias/documentation/blob/master/search.md#filter-by-data-type 77 | for details. 78 | :type layers: list of strings 79 | 80 | :param country: Constrain query by country. Accepts a alpha-2 or alpha-3 81 | digit ISO-3166 country code. 82 | :type country: str 83 | 84 | :param size: The amount of results returned. Default 10. 85 | :type size: integer 86 | 87 | :param dry_run: Print URL and parameters without sending the request. 88 | :param dry_run: boolean 89 | 90 | :raises ValueError: When parameter has invalid value(s). 91 | :raises TypeError: When parameter is of the wrong type. 92 | 93 | :rtype: call to Client.request() 94 | """ 95 | 96 | params = {"text": text} 97 | 98 | if focus_point: 99 | params["focus.point.lon"] = convert._format_float(focus_point[0]) 100 | params["focus.point.lat"] = convert._format_float(focus_point[1]) 101 | 102 | if rect_min_x: 103 | params["boundary.rect.min_lon"] = convert._format_float(rect_min_x) # 104 | 105 | if rect_min_y: 106 | params["boundary.rect.min_lat"] = convert._format_float(rect_min_y) # 107 | 108 | if rect_max_x: 109 | params["boundary.rect.max_lon"] = convert._format_float(rect_max_x) # 110 | 111 | if rect_max_y: 112 | params["boundary.rect.max_lat"] = convert._format_float(rect_max_y) # 113 | 114 | if circle_point: 115 | params["boundary.circle.lon"] = convert._format_float( 116 | circle_point[0] 117 | ) # 118 | params["boundary.circle.lat"] = convert._format_float( 119 | circle_point[1] 120 | ) # 121 | 122 | if circle_radius: 123 | params["boundary.circle.radius"] = circle_radius 124 | 125 | if sources: 126 | params["sources"] = convert._comma_list(sources) 127 | 128 | if layers: 129 | params["layers"] = convert._comma_list(layers) 130 | 131 | if country: 132 | params["boundary.country"] = country 133 | 134 | if size: 135 | params["size"] = size 136 | 137 | return client.request("/geocode/search", params, dry_run=dry_run) 138 | 139 | 140 | def pelias_autocomplete( 141 | client, 142 | text, 143 | focus_point=None, 144 | rect_min_x=None, 145 | rect_min_y=None, 146 | rect_max_x=None, 147 | rect_max_y=None, 148 | country=None, 149 | sources=None, 150 | layers=None, 151 | validate=True, 152 | dry_run=None, 153 | ): 154 | """ 155 | Autocomplete geocoding can be used alongside /search to enable real-time feedback. 156 | 157 | It represents a type-ahead functionality, which helps to find the desired location, 158 | without to require a fully specified search term. 159 | 160 | This endpoint queries directly against a Pelias instance. 161 | For fully documentation, please see https://github.com/pelias/documentation/blob/master/autocomplete.md 162 | 163 | :param text: Full-text query against search endpoint. Required. 164 | :type text: string 165 | 166 | :param focus_point: Focusses the search to be around this point and gives 167 | results within a 100 km radius higher scores. 168 | :type query: list or tuple of (Long, Lat) 169 | 170 | :param rect_min_x: Min longitude by which to constrain request geographically. 171 | :type rect_min_x: float 172 | 173 | :param rect_min_y: Min latitude by which to constrain request geographically. 174 | :type rect_min_y: float 175 | 176 | :param rect_max_x: Max longitude by which to constrain request geographically. 177 | :type rect_max_x: float 178 | 179 | :param rect_max_y: Max latitude by which to constrain request geographically. 180 | :type rect_max_y: float 181 | 182 | :param country: Constrain query by country. Accepts a alpha-2 or alpha-3 183 | digit ISO-3166 country codes. 184 | :type country: str 185 | 186 | :param sources: The originating source of the data. One or more of 187 | ['osm', 'oa', 'wof', 'gn']. Currently only 'osm', 'wof' and 'gn' are 188 | supported. 189 | :type sources: list of strings 190 | 191 | :param layers: The administrative hierarchy level for the query. Refer to 192 | https://github.com/pelias/documentation/blob/master/search.md#filter-by-data-type 193 | for details. 194 | :type layers: list of strings 195 | 196 | :param dry_run: Print URL and parameters without sending the request. 197 | :param dry_run: boolean 198 | 199 | :raises ValueError: When parameter has invalid value(s). 200 | :raises TypeError: When parameter is of the wrong type. 201 | 202 | :rtype: dict from JSON response 203 | """ 204 | 205 | params = {"text": text} 206 | 207 | if focus_point: 208 | params["focus.point.lon"] = convert._format_float(focus_point[0]) 209 | params["focus.point.lat"] = convert._format_float(focus_point[1]) 210 | 211 | if rect_min_x: 212 | params["boundary.rect.min_lon "] = convert._format_float(rect_min_x) 213 | 214 | if rect_min_y: 215 | params["boundary.rect.min_lat "] = convert._format_float(rect_min_y) 216 | 217 | if rect_max_x: 218 | params["boundary.rect.max_lon "] = convert._format_float(rect_max_x) 219 | 220 | if rect_max_y: 221 | params["boundary.rect.max_lon "] = convert._format_float(rect_max_y) 222 | 223 | if country: 224 | params["boundary.country"] = country 225 | 226 | if sources: 227 | params["sources"] = convert._comma_list(sources) 228 | 229 | if layers: 230 | params["layers"] = convert._comma_list(layers) 231 | 232 | return client.request("/geocode/autocomplete", params, dry_run=dry_run) 233 | 234 | 235 | def pelias_structured( 236 | client, 237 | address=None, 238 | neighbourhood=None, 239 | borough=None, 240 | locality=None, 241 | county=None, 242 | region=None, 243 | postalcode=None, 244 | country=None, 245 | validate=True, 246 | dry_run=None, 247 | # size=None 248 | ): 249 | """ 250 | With structured geocoding, you can search for the individual parts of a location. 251 | 252 | Structured geocoding is an option on the search endpoint, 253 | which allows you to define a query that maintains the individual fields. 254 | 255 | This endpoint queries directly against a Pelias instance. 256 | For full documentation, please see https://github.com/pelias/documentation/blob/master/structured-geocoding.md 257 | 258 | :param address: Can contain a full address with house number or only a street name. 259 | :type address: string 260 | 261 | :param neighbourhood: Neighbourhoods are vernacular geographic entities that 262 | may not necessarily be official administrative divisions but are important nonetheless. 263 | :type neighbourhood: string 264 | 265 | # Check all passed arguments 266 | convert._is_valid_args(locals()) 267 | 268 | :param borough: Mostly known in the context of New York City, even though they may exist in other cities. 269 | :type borough: string 270 | 271 | :param locality: Localities are equivalent to what are commonly referred to as cities. 272 | :type locality: string 273 | 274 | :param county: Administrative divisions between localities and regions. 275 | Not as commonly used in geocoding as localities, but useful when attempting to 276 | disambiguate between localities. 277 | :type county: string 278 | 279 | :param region: Normally the first-level administrative divisions within countries, analogous to states 280 | and provinces in the United States and Canada. Can be a full name or abbreviation. 281 | :type region: string 282 | 283 | :param postalcode: Dictated by an administrative division, which is almost always countries. 284 | Postal codes are unique within a country. 285 | :type postalcode: integer 286 | 287 | :param country: Highest-level divisions supported in a search. Can be a full name or abbreviation. 288 | :type country: string 289 | 290 | :param dry_run: Print URL and parameters without sending the request. 291 | :param dry_run: boolean 292 | 293 | :raises TypeError: When parameter is of the wrong type. 294 | 295 | :rtype: dict from JSON response 296 | """ 297 | 298 | params = {} 299 | 300 | if address: 301 | params["address"] = address 302 | 303 | if neighbourhood: 304 | params["neighbourhood"] = neighbourhood 305 | 306 | if borough: 307 | params["borough"] = borough 308 | 309 | if locality: 310 | params["locality"] = locality 311 | 312 | if county: 313 | params["county"] = county 314 | 315 | if region: 316 | params["region"] = region 317 | 318 | if postalcode: 319 | params["postalcode"] = postalcode 320 | 321 | if country: 322 | params["country"] = country 323 | 324 | return client.request("/geocode/search/structured", params, dry_run=dry_run) 325 | 326 | 327 | def pelias_reverse( 328 | client, 329 | point, 330 | circle_radius=None, 331 | sources=None, 332 | layers=None, 333 | country=None, 334 | size=None, 335 | validate=True, 336 | dry_run=None, 337 | ): 338 | """ 339 | Reverse geocoding is the process of converting geographic coordinates into a human-readable address. 340 | 341 | This endpoint queries directly against a Pelias instance. 342 | 343 | :param point: Coordinate tuple. Required. 344 | :type point: list or tuple of [Lon, Lat] 345 | 346 | :param circle_radius: Radius around point to limit query in km. Default 1. 347 | :type circle_radius: integer 348 | 349 | :param sources: The originating source of the data. One or more of 350 | ['osm', 'oa', 'wof', 'gn']. Currently only 'osm', 'wof' and 'gn' are 351 | supported. 352 | :type sources: list of strings 353 | 354 | :param layers: The administrative hierarchy level for the query. Refer to 355 | https://github.com/pelias/documentation/blob/master/search.md#filter-by-data-type 356 | for details. 357 | :type layers: list of strings 358 | 359 | :param country: Constrain query by country. Accepts a alpha-2 or alpha-3 360 | digit ISO-3166 country codes. 361 | :type country: str 362 | 363 | :param size: The amount of results returned. Default 10. 364 | :type size: integer 365 | 366 | :param dry_run: Print URL and parameters without sending the request. 367 | :param dry_run: boolean 368 | 369 | :raises ValueError: When parameter has invalid value(s). 370 | 371 | :rtype: dict from JSON response 372 | """ 373 | 374 | params = { 375 | "point.lon": convert._format_float(point[0]), 376 | "point.lat": convert._format_float(point[1]), 377 | } 378 | 379 | if circle_radius: 380 | params["boundary.circle.radius"] = str(circle_radius) 381 | 382 | if sources: 383 | params["sources"] = convert._comma_list(sources) 384 | 385 | if layers: 386 | params["layers"] = convert._comma_list(layers) 387 | 388 | if country: 389 | params["boundary.country"] = country 390 | 391 | if size: 392 | params["size"] = size 393 | 394 | return client.request("/geocode/reverse", params, dry_run=dry_run) 395 | -------------------------------------------------------------------------------- /openrouteservice/isochrones.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright (C) 2018 HeiGIT, University of Heidelberg. 3 | # 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 6 | # use this file except in compliance with the License. You may obtain a copy of 7 | # the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations under 15 | # the License. 16 | # 17 | """Performs requests to the ORS isochrones API.""" 18 | 19 | from openrouteservice import deprecation 20 | 21 | 22 | def isochrones( 23 | client, 24 | locations, 25 | profile="driving-car", 26 | range_type="time", 27 | range=None, 28 | intervals=None, 29 | segments=None, 30 | interval=None, 31 | intersections=None, 32 | units=None, 33 | location_type=None, 34 | options=None, 35 | smoothing=None, 36 | attributes=None, 37 | validate=True, 38 | dry_run=None, 39 | ): 40 | """Gets travel distance and time for a matrix of origins and destinations. 41 | 42 | :param locations: One pair of lng/lat values. 43 | :type locations: list or tuple of lng,lat values 44 | 45 | :param profile: Specifies the mode of transport to use when calculating 46 | directions. One of ["driving-car", "driving-hgv", "foot-walking", 47 | "foot-hiking", "cycling-regular", "cycling-road", "cycling-mountain", 48 | "cycling-electric",]. Default "driving-car". 49 | :type profile: string 50 | 51 | :param range_type: Set 'time' for isochrones or 'distance' for equidistants. 52 | Default 'time'. 53 | :type sources: string 54 | 55 | :param intervals: [SOON DEPRECATED] replaced by `range`. 56 | :type intervals: list of integer(s) 57 | 58 | :param range: Ranges to calculate distances/durations for. This can be 59 | a list of multiple ranges, e.g. [600, 1200, 1400] or a single value list. 60 | In the latter case, you can also specify the 'interval' variable to break 61 | the single value into more isochrones. In meters or seconds. 62 | :type range: list of integer(s) 63 | 64 | :param segments: [SOON DEPRECATED] replaced by `interval`. 65 | :type segments: integer 66 | 67 | :param interval: Segments isochrones or equidistants for one 'range' value. 68 | Only has effect if used with a single value 'range' value. 69 | In meters or seconds. 70 | :type interval: integer 71 | 72 | :param intersections: Specifies whether to return intersecting polygons. 73 | :type intersections: boolean 74 | 75 | :param units: Specifies the unit system to use when displaying results. 76 | One of ["m", "km", "m"]. Default "m". 77 | :type units: string 78 | 79 | :param location_type: 'start' treats the location(s) as starting point, 80 | 'destination' as goal. Default 'start'. 81 | :type location_type: string 82 | 83 | :param options: Additional options for the isochrones request. Refer to 84 | https://openrouteservice.org/dev/#/api-docs/v2/isochrones/{profile}/post 85 | for detailed documentation. Expects a dict(). Will be converted to JSON 86 | automatically. 87 | :type options: dict 88 | 89 | :param smoothing: Applies a level of generalisation to the isochrone polygons generated. 90 | Value between 0 and 1, whereas a value closer to 1 will result in a more generalised shape. 91 | :type smoothing: float 92 | 93 | :param attributes: 'area' returns the area of each polygon in its feature 94 | properties. 'reachfactor' returns a reachability score between 0 and 1. 95 | 'total_pop' returns population statistics from https://ghsl.jrc.ec.europa.eu/about.php. 96 | One or more of ['area', 'reachfactor', 'total_pop']. Default 'area'. 97 | :type attributes: list of string(s) 98 | 99 | :param validate: Specifies whether parameters should be validated before sending the request. Default True. 100 | :type validate: bool 101 | 102 | :param dry_run: Print URL and parameters without sending the request. 103 | :param dry_run: boolean 104 | 105 | :raises ValueError: When parameter has invalid value(s). 106 | 107 | :rtype: call to Client.request() 108 | """ 109 | 110 | params = {"locations": locations} 111 | 112 | if profile: # pragma: no cover 113 | params["profile"] = profile 114 | 115 | if range_type: # pragma: no cover 116 | params["range_type"] = range_type 117 | 118 | if intervals: # pragma: no cover 119 | deprecation.warning("intervals", "range") 120 | 121 | range = range or intervals 122 | params["range"] = range 123 | 124 | if segments: # pragma: no cover 125 | deprecation.warning("segments", "interval") 126 | 127 | interval = interval or segments 128 | if interval: # pragma: no cover 129 | params["interval"] = interval 130 | 131 | if units: # pragma: no cover 132 | params["units"] = units 133 | 134 | if intersections: 135 | params["intersections"] = intersections 136 | 137 | if location_type: # pragma: no cover 138 | params["location_type"] = location_type 139 | 140 | if smoothing: # pragma: no cover 141 | params["smoothing"] = smoothing 142 | 143 | if attributes: # pragma: no cover 144 | params["attributes"] = attributes 145 | 146 | if options: # pragma: no cover 147 | params["options"] = options 148 | 149 | return client.request( 150 | "/v2/isochrones/" + profile + "/geojson", 151 | {}, 152 | post_json=params, 153 | dry_run=dry_run, 154 | ) 155 | -------------------------------------------------------------------------------- /openrouteservice/optimization.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright (C) 2018 HeiGIT, University of Heidelberg. 3 | # 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 6 | # use this file except in compliance with the License. You may obtain a copy of 7 | # the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations under 15 | # the License. 16 | # 17 | """Performs requests to the ORS optimization API.""" 18 | 19 | 20 | def optimization( 21 | client, 22 | jobs=None, 23 | vehicles=None, 24 | shipments=None, 25 | matrix=None, 26 | geometry=None, 27 | dry_run=None, 28 | ): 29 | """# noqa 30 | Optimize a fleet of vehicles on a number of jobs. 31 | 32 | For more information, visit https://github.com/VROOM-Project/vroom/blob/master/docs/API.md. 33 | 34 | Example: 35 | 36 | >>> from openrouteservice import Client, optimization 37 | >>> coordinates = [[8.688641, 49.420577], [8.680916, 49.415776]] 38 | >>> jobs, vehicles = list(), list() 39 | >>> for idx, coord in enumerate(coordinates): 40 | jobs.append(optimization.Job(id=idx, location=coord)) 41 | vehicles.append(optimization.Vehicle(id=idx, location=coord)) 42 | >>> api = Client(key='somekey') 43 | >>> result = api.optimization(jobs=jobs, vehicles=vehicles) 44 | 45 | :param jobs: The Job objects to fulfill. 46 | :type jobs: list of Job 47 | 48 | :param vehicles: The vehicles to fulfill the :class:`openrouteservice.optimization.Job`'s. 49 | :type vehicles: list of Vehicle 50 | 51 | :param shipments: The Shipment objects to fulfill. 52 | :type shipments: list of Shipment 53 | 54 | :param matrix: Specify a custom cost matrix. If not specified, it will be calculated with 55 | the :meth:`openrouteservice.matrix.matrix` endpoint. 56 | :type matrix: list of lists of int 57 | 58 | :param geometry: If the geometry of the resulting routes should be calculated. Default False. 59 | :type geometry: bool 60 | 61 | :param dry_run: Print URL and parameters without sending the request. 62 | :param dry_run: boolean 63 | 64 | :returns: Response of optimization endpoint. 65 | :rtype: dict 66 | """ 67 | 68 | assert all([isinstance(x, Vehicle) for x in vehicles]) # noqa 69 | 70 | params = {"vehicles": [vehicle.__dict__ for vehicle in vehicles]} 71 | 72 | if jobs: 73 | assert all([isinstance(x, Job) for x in jobs]) # noqa 74 | params["jobs"] = [job.__dict__ for job in jobs] 75 | if shipments: 76 | assert all([isinstance(x, Shipment) for x in shipments]) # noqa 77 | params["shipments"] = [] 78 | 79 | for shipment in shipments: 80 | shipment_dict = {} 81 | if hasattr(shipment, "pickup"): 82 | assert isinstance(shipment.pickup, ShipmentStep) 83 | shipment_dict["pickup"] = shipment.pickup.__dict__ 84 | if hasattr(shipment, "delivery"): 85 | assert isinstance(shipment.delivery, ShipmentStep) 86 | shipment_dict["delivery"] = shipment.delivery.__dict__ 87 | shipment_dict["amount"] = shipment.amount 88 | shipment_dict["skills"] = shipment.skills 89 | shipment_dict["priority"] = shipment.priority 90 | 91 | params["shipments"].append(shipment_dict) 92 | 93 | if geometry is not None: 94 | params.update({"options": {"g": geometry}}) 95 | 96 | if matrix: 97 | params["matrix"] = matrix 98 | 99 | return client.request( 100 | "/optimization", {}, post_json=params, dry_run=dry_run 101 | ) 102 | 103 | 104 | class Job: 105 | """ 106 | Class to create a Job object for optimization endpoint. 107 | 108 | Full documentation at https://github.com/VROOM-Project/vroom/blob/master/docs/API.md#jobs. 109 | """ 110 | 111 | def __init__( 112 | self, 113 | id, 114 | location=None, 115 | location_index=None, 116 | service=None, 117 | amount=None, 118 | skills=None, 119 | priority=None, 120 | time_windows=None, 121 | ): 122 | """ 123 | Create a job object for the optimization endpoint. 124 | 125 | :param id: Integer used as unique identifier. 126 | :type id: int 127 | 128 | :param location: Location of the job, as [lon, lat]. Optional if custom matrix is provided. 129 | :type location: tuple of float or list of float 130 | 131 | :param location_index: Index of relevant row and column in custom matrix. Mandatory if custom 132 | matrix is provided. Irrelevant when no custom matrix is provided. 133 | :type location_index: int 134 | 135 | :param service: Optional job service duration in seconds 136 | :type service: int 137 | 138 | :param amount: An array of integers describing multidimensional quantities. 139 | :type amount: list of int or tuple of int 140 | 141 | :param skills: An array of integers defining mandatory skills for this job. 142 | :type skills: list of int or tuple of int 143 | 144 | :param priority: An integer in the [0, 10] range describing priority level (defaults to 0). 145 | :type priority: int 146 | 147 | :param time_windows: An array of time_window objects describing valid slots for job service start. 148 | :type time_windows: list of lists of int 149 | """ 150 | 151 | self.id = id 152 | 153 | if location is not None: 154 | self.location = location 155 | 156 | if location_index is not None: 157 | self.location_index = location_index 158 | 159 | if service is not None: 160 | self.service = service 161 | 162 | if amount is not None: 163 | self.amount = amount 164 | 165 | if skills is not None: 166 | self.skills = skills 167 | 168 | if priority is not None: 169 | self.priority = priority 170 | 171 | if time_windows is not None: 172 | self.time_windows = time_windows 173 | 174 | 175 | class ShipmentStep: 176 | """ 177 | Class to create a Shipment object for optimization endpoint. 178 | 179 | Full documentation at https://github.com/VROOM-Project/vroom/blob/master/docs/API.md#shipments. 180 | """ 181 | 182 | def __init__( 183 | self, 184 | id=None, 185 | location=None, 186 | location_index=None, 187 | service=None, 188 | time_windows=None, 189 | ): 190 | """ 191 | Create a shipment step object for the optimization endpoint. 192 | 193 | :param id: Integer used as unique identifier. 194 | :type id: int 195 | 196 | :param location: Location of the job, as [lon, lat]. Optional if custom matrix is provided. 197 | :type location: tuple of float or list of float 198 | 199 | :param location_index: Index of relevant row and column in custom matrix. Mandatory if custom 200 | matrix is provided. Irrelevant when no custom matrix is provided. 201 | :type location_index: int 202 | 203 | :param service: Optional job service duration in seconds 204 | :type service: int 205 | 206 | :param time_windows: An array of time_window objects describing valid slots for job service start. 207 | :type time_windows: list of lists of int 208 | """ 209 | 210 | self.id = id 211 | 212 | if location is not None: 213 | self.location = location 214 | 215 | if location_index is not None: 216 | self.location_index = location_index 217 | 218 | if service is not None: 219 | self.service = service 220 | 221 | if time_windows is not None: 222 | self.time_windows = time_windows 223 | 224 | 225 | class Shipment: 226 | """ 227 | Class to create a Shipment object for optimization endpoint. 228 | 229 | Full documentation at https://github.com/VROOM-Project/vroom/blob/master/docs/API.md#shipments. 230 | """ 231 | 232 | def __init__( 233 | self, 234 | pickup=None, 235 | delivery=None, 236 | amount=None, 237 | skills=None, 238 | priority=None, 239 | ): 240 | """ 241 | Create a shipment object for the optimization endpoint. 242 | 243 | :param pickup: a ShipmentStep object describing pickup 244 | :type pickup: ShipmentStep 245 | 246 | :param delivery: a ShipmentStep object describing delivery 247 | :type delivery: ShipmentStep 248 | 249 | :param amount: An array of integers describing multidimensional quantities. 250 | :type amount: list of int or tuple of int 251 | 252 | :param skills: An array of integers defining mandatory skills. 253 | :type skills: list of int or tuple of int 254 | 255 | :param priority: An integer in the [0, 10] range describing priority level (defaults to 0). 256 | :type priority: int 257 | """ 258 | 259 | if pickup is not None: 260 | self.pickup = pickup 261 | 262 | if delivery is not None: 263 | self.delivery = delivery 264 | 265 | if amount is not None: 266 | self.amount = amount 267 | 268 | if skills is not None: 269 | self.skills = skills 270 | 271 | if priority is not None: 272 | self.priority = priority 273 | 274 | 275 | class Vehicle: 276 | """ 277 | Class to create a Vehicle object for optimization endpoint. 278 | 279 | Full documentation at https://github.com/VROOM-Project/vroom/blob/master/docs/API.md#vehicles. 280 | """ 281 | 282 | def __init__( 283 | self, 284 | id, 285 | profile="driving-car", 286 | start=None, 287 | start_index=None, 288 | end=None, 289 | end_index=None, 290 | capacity=None, 291 | skills=None, 292 | time_window=None, 293 | ): 294 | """ 295 | Create a Vehicle object for the optimization endpoint. 296 | 297 | :param id: Integer used as unique identifier. 298 | :param id: int 299 | 300 | :param profile: Specifies the mode of transport to use when calculating 301 | directions. One of ["driving-car", "driving-hgv", "foot-walking", 302 | "foot-hiking", "cycling-regular", "cycling-road","cycling-mountain", 303 | "cycling-electric",]. Default "driving-car". 304 | :type profile: str 305 | 306 | :param start: Coordinate for the vehicle to start from. If not specified, the start 307 | location will be the first visited job, determined by the optimization engine. 308 | :type start: list of float or tuple of float 309 | 310 | :param start_index: Index of relevant row and column in custom matrix. Irrelevant 311 | if no custom matrix is provided. 312 | :type start_index: int 313 | 314 | :param end: Coordinate for the vehicle to end at. If not specified, the end 315 | location will be the last visited job, determined by the optimization engine. 316 | :type end: list of float or tuple of float 317 | 318 | :param end_index: Index of relevant row and column in custom matrix. Irrelevant 319 | if no custom matrix is provided. 320 | :type end_index: int 321 | 322 | :param capacity: An array of integers describing multidimensional quantities. 323 | :type capacity: list of int or tuple of int 324 | 325 | :param skills: An array of integers defining skills for this vehicle. 326 | :param skills: list of int or tuple of int 327 | 328 | :param time_window: A time_window object describing working hours for this vehicle. 329 | :param time_window: list of int or tuple of int 330 | """ 331 | 332 | self.id = id 333 | self.profile = profile 334 | 335 | if start is not None: 336 | self.start = start 337 | 338 | if start_index is not None: 339 | self.start_index = start_index 340 | 341 | if end is not None: 342 | self.end = end 343 | 344 | if end_index is not None: 345 | self.end_index = end_index 346 | 347 | if capacity is not None: 348 | self.capacity = capacity 349 | 350 | if skills is not None: 351 | self.skills = skills 352 | 353 | if time_window is not None: 354 | self.time_window = time_window 355 | -------------------------------------------------------------------------------- /openrouteservice/places.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright (C) 2018 HeiGIT, University of Heidelberg. 3 | # 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 6 | # use this file except in compliance with the License. You may obtain a copy of 7 | # the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations under 15 | # the License. 16 | """Performs requests to the ORS Places API.""" 17 | 18 | from openrouteservice import convert 19 | 20 | 21 | def places( 22 | client, 23 | request, 24 | geojson=None, 25 | bbox=None, 26 | buffer=None, 27 | filter_category_ids=None, 28 | filter_category_group_ids=None, 29 | filters_custom=None, 30 | limit=None, 31 | sortby=None, 32 | validate=True, 33 | dry_run=None, 34 | ): 35 | """Gets POI's filtered by specified parameters. 36 | 37 | :param request: Type of request. One of ['pois', 'list', 'stats']. 38 | 'pois': returns geojson of pois; 39 | 'stats': returns statistics of passed categories; 40 | 'list': returns mapping of category ID's to textual representation. 41 | :type request: string 42 | 43 | :param geojson: GeoJSON dict used for the query. 44 | :type geojson: dict 45 | 46 | :param buffer: Buffers geometry of 'geojson' or 'bbox' with the specified 47 | value in meters. 48 | :type intervals: integer 49 | 50 | :param filter_category_ids: Filter by ORS custom category IDs. See 51 | https://github.com/GIScience/openrouteservice-docs#places-response 52 | for the mappings. 53 | :type units: list of integer 54 | 55 | :param filter_category_group_ids: Filter by ORS custom high-level category 56 | groups. See 57 | https://github.com/GIScience/openrouteservice-docs#places-response 58 | for the mappings. 59 | :type attributes: list of integer 60 | 61 | :param filters_custom: Specify additional filters by key/value. Default ORS 62 | filters are 63 | 'name': free text 64 | 'wheelchair': ['yes', 'limited', 'no', 'designated'] 65 | 'smoking': ['dedicated','yes','separated','isolated', 'no', 'outside'] 66 | 'fee': ['yes','no', 'str'] 67 | :type filters_custom: dict of lists/str 68 | 69 | :param limit: limit for POI queries. 70 | :type limit: integer 71 | base_url='http://localhost:5000' 72 | 73 | :param sortby: Sorts the returned features by 'distance' or 'category'. 74 | For request='pois' only. 75 | :type sortby: string 76 | 77 | :param dry_run: Print URL and parameters without sending the request. 78 | :param dry_run: boolean 79 | 80 | :rtype: call to Client.request() 81 | """ 82 | 83 | params = { 84 | "request": request, 85 | "filters": {}, 86 | "geometry": {}, 87 | } 88 | 89 | if request != "category_list": 90 | if geojson: 91 | params["geometry"]["geojson"] = geojson 92 | 93 | if bbox: 94 | params["geometry"]["bbox"] = bbox 95 | 96 | if buffer: 97 | params["geometry"]["buffer"] = buffer 98 | 99 | if filter_category_ids and convert._is_list(filter_category_ids): 100 | params["filters"]["category_ids"] = filter_category_ids 101 | 102 | if filter_category_group_ids and convert._is_list( 103 | filter_category_group_ids 104 | ): 105 | params["filters"]["category_group_ids"] = filter_category_group_ids 106 | 107 | if filters_custom: 108 | for f in filters_custom: 109 | params["filters"][f] = filters_custom[f] 110 | 111 | if limit: 112 | params["limit"] = limit 113 | 114 | if sortby: 115 | params["sortby"] = sortby 116 | 117 | return client.request("/pois", {}, post_json=params, dry_run=dry_run) 118 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.5.0 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "certifi" 5 | version = "2023.5.7" 6 | description = "Python package for providing Mozilla's CA Bundle." 7 | optional = false 8 | python-versions = ">=3.6" 9 | files = [ 10 | {file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"}, 11 | {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"}, 12 | ] 13 | 14 | [[package]] 15 | name = "cfgv" 16 | version = "3.3.1" 17 | description = "Validate configuration and produce human readable error messages." 18 | optional = false 19 | python-versions = ">=3.6.1" 20 | files = [ 21 | {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, 22 | {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, 23 | ] 24 | 25 | [[package]] 26 | name = "charset-normalizer" 27 | version = "3.1.0" 28 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 29 | optional = false 30 | python-versions = ">=3.7.0" 31 | files = [ 32 | {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, 33 | {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, 34 | {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, 35 | {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, 36 | {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, 37 | {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, 38 | {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, 39 | {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, 40 | {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, 41 | {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, 42 | {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, 43 | {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, 44 | {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, 45 | {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, 46 | {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, 47 | {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, 48 | {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, 49 | {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, 50 | {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, 51 | {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, 52 | {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, 53 | {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, 54 | {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, 55 | {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, 56 | {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, 57 | {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, 58 | {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, 59 | {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, 60 | {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, 61 | {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, 62 | {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, 63 | {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, 64 | {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, 65 | {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, 66 | {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, 67 | {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, 68 | {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, 69 | {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, 70 | {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, 71 | {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, 72 | {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, 73 | {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, 74 | {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, 75 | {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, 76 | {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, 77 | {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, 78 | {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, 79 | {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, 80 | {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, 81 | {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, 82 | {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, 83 | {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, 84 | {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, 85 | {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, 86 | {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, 87 | {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, 88 | {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, 89 | {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, 90 | {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, 91 | {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, 92 | {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, 93 | {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, 94 | {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, 95 | {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, 96 | {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, 97 | {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, 98 | {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, 99 | {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, 100 | {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, 101 | {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, 102 | {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, 103 | {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, 104 | {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, 105 | {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, 106 | {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, 107 | ] 108 | 109 | [[package]] 110 | name = "colorama" 111 | version = "0.4.6" 112 | description = "Cross-platform colored terminal text." 113 | optional = false 114 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 115 | files = [ 116 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 117 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 118 | ] 119 | 120 | [[package]] 121 | name = "coverage" 122 | version = "7.2.7" 123 | description = "Code coverage measurement for Python" 124 | optional = false 125 | python-versions = ">=3.7" 126 | files = [ 127 | {file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"}, 128 | {file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"}, 129 | {file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"}, 130 | {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"}, 131 | {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"}, 132 | {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"}, 133 | {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"}, 134 | {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"}, 135 | {file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"}, 136 | {file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"}, 137 | {file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"}, 138 | {file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"}, 139 | {file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"}, 140 | {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"}, 141 | {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"}, 142 | {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"}, 143 | {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"}, 144 | {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"}, 145 | {file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"}, 146 | {file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"}, 147 | {file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"}, 148 | {file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"}, 149 | {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"}, 150 | {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"}, 151 | {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"}, 152 | {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"}, 153 | {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"}, 154 | {file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"}, 155 | {file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"}, 156 | {file = "coverage-7.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f"}, 157 | {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb"}, 158 | {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9"}, 159 | {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd"}, 160 | {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a"}, 161 | {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959"}, 162 | {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02"}, 163 | {file = "coverage-7.2.7-cp37-cp37m-win32.whl", hash = "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f"}, 164 | {file = "coverage-7.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0"}, 165 | {file = "coverage-7.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5"}, 166 | {file = "coverage-7.2.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5"}, 167 | {file = "coverage-7.2.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9"}, 168 | {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6"}, 169 | {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e"}, 170 | {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050"}, 171 | {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5"}, 172 | {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f"}, 173 | {file = "coverage-7.2.7-cp38-cp38-win32.whl", hash = "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e"}, 174 | {file = "coverage-7.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c"}, 175 | {file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"}, 176 | {file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"}, 177 | {file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"}, 178 | {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"}, 179 | {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"}, 180 | {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"}, 181 | {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"}, 182 | {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"}, 183 | {file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"}, 184 | {file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"}, 185 | {file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"}, 186 | {file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"}, 187 | ] 188 | 189 | [package.dependencies] 190 | tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} 191 | 192 | [package.extras] 193 | toml = ["tomli"] 194 | 195 | [[package]] 196 | name = "distlib" 197 | version = "0.3.6" 198 | description = "Distribution utilities" 199 | optional = false 200 | python-versions = "*" 201 | files = [ 202 | {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, 203 | {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, 204 | ] 205 | 206 | [[package]] 207 | name = "exceptiongroup" 208 | version = "1.1.1" 209 | description = "Backport of PEP 654 (exception groups)" 210 | optional = false 211 | python-versions = ">=3.7" 212 | files = [ 213 | {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, 214 | {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, 215 | ] 216 | 217 | [package.extras] 218 | test = ["pytest (>=6)"] 219 | 220 | [[package]] 221 | name = "execnet" 222 | version = "1.9.0" 223 | description = "execnet: rapid multi-Python deployment" 224 | optional = false 225 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 226 | files = [ 227 | {file = "execnet-1.9.0-py2.py3-none-any.whl", hash = "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"}, 228 | {file = "execnet-1.9.0.tar.gz", hash = "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5"}, 229 | ] 230 | 231 | [package.extras] 232 | testing = ["pre-commit"] 233 | 234 | [[package]] 235 | name = "filelock" 236 | version = "3.12.0" 237 | description = "A platform independent file lock." 238 | optional = false 239 | python-versions = ">=3.7" 240 | files = [ 241 | {file = "filelock-3.12.0-py3-none-any.whl", hash = "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9"}, 242 | {file = "filelock-3.12.0.tar.gz", hash = "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718"}, 243 | ] 244 | 245 | [package.extras] 246 | docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] 247 | testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] 248 | 249 | [[package]] 250 | name = "identify" 251 | version = "2.5.24" 252 | description = "File identification library for Python" 253 | optional = false 254 | python-versions = ">=3.7" 255 | files = [ 256 | {file = "identify-2.5.24-py2.py3-none-any.whl", hash = "sha256:986dbfb38b1140e763e413e6feb44cd731faf72d1909543178aa79b0e258265d"}, 257 | {file = "identify-2.5.24.tar.gz", hash = "sha256:0aac67d5b4812498056d28a9a512a483f5085cc28640b02b258a59dac34301d4"}, 258 | ] 259 | 260 | [package.extras] 261 | license = ["ukkonen"] 262 | 263 | [[package]] 264 | name = "idna" 265 | version = "3.4" 266 | description = "Internationalized Domain Names in Applications (IDNA)" 267 | optional = false 268 | python-versions = ">=3.5" 269 | files = [ 270 | {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, 271 | {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, 272 | ] 273 | 274 | [[package]] 275 | name = "importlib-metadata" 276 | version = "6.6.0" 277 | description = "Read metadata from Python packages" 278 | optional = false 279 | python-versions = ">=3.7" 280 | files = [ 281 | {file = "importlib_metadata-6.6.0-py3-none-any.whl", hash = "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed"}, 282 | {file = "importlib_metadata-6.6.0.tar.gz", hash = "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"}, 283 | ] 284 | 285 | [package.dependencies] 286 | typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} 287 | zipp = ">=0.5" 288 | 289 | [package.extras] 290 | docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] 291 | perf = ["ipython"] 292 | testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] 293 | 294 | [[package]] 295 | name = "iniconfig" 296 | version = "2.0.0" 297 | description = "brain-dead simple config-ini parsing" 298 | optional = false 299 | python-versions = ">=3.7" 300 | files = [ 301 | {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, 302 | {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, 303 | ] 304 | 305 | [[package]] 306 | name = "nodeenv" 307 | version = "1.8.0" 308 | description = "Node.js virtual environment builder" 309 | optional = false 310 | python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" 311 | files = [ 312 | {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, 313 | {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, 314 | ] 315 | 316 | [package.dependencies] 317 | setuptools = "*" 318 | 319 | [[package]] 320 | name = "packaging" 321 | version = "23.1" 322 | description = "Core utilities for Python packages" 323 | optional = false 324 | python-versions = ">=3.7" 325 | files = [ 326 | {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, 327 | {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, 328 | ] 329 | 330 | [[package]] 331 | name = "platformdirs" 332 | version = "3.5.1" 333 | description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 334 | optional = false 335 | python-versions = ">=3.7" 336 | files = [ 337 | {file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"}, 338 | {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, 339 | ] 340 | 341 | [package.dependencies] 342 | typing-extensions = {version = ">=4.5", markers = "python_version < \"3.8\""} 343 | 344 | [package.extras] 345 | docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] 346 | test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] 347 | 348 | [[package]] 349 | name = "pluggy" 350 | version = "1.0.0" 351 | description = "plugin and hook calling mechanisms for python" 352 | optional = false 353 | python-versions = ">=3.6" 354 | files = [ 355 | {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, 356 | {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, 357 | ] 358 | 359 | [package.dependencies] 360 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 361 | 362 | [package.extras] 363 | dev = ["pre-commit", "tox"] 364 | testing = ["pytest", "pytest-benchmark"] 365 | 366 | [[package]] 367 | name = "poetry-semver" 368 | version = "0.1.0" 369 | description = "A semantic versioning library for Python" 370 | optional = false 371 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 372 | files = [ 373 | {file = "poetry-semver-0.1.0.tar.gz", hash = "sha256:d809b612aa27b39bf2d0fc9d31b4f4809b0e972646c5f19cfa46c725b7638810"}, 374 | {file = "poetry_semver-0.1.0-py2.py3-none-any.whl", hash = "sha256:4e6349bd7231cc657f0e1930f7b204e87e33dfd63eef5cac869363969515083a"}, 375 | ] 376 | 377 | [[package]] 378 | name = "poetry2conda" 379 | version = "0.3.0" 380 | description = "Convert pyproject.toml to environment.yaml" 381 | optional = false 382 | python-versions = ">=3.6,<4.0" 383 | files = [ 384 | {file = "poetry2conda-0.3.0-py3-none-any.whl", hash = "sha256:218618d37331bd6d3d3007edcf21d71802990a0ce9c27e8bb23f739cfa82ed03"}, 385 | {file = "poetry2conda-0.3.0.tar.gz", hash = "sha256:574b6295ff877ff8fb56fe2034160ff9ee9db55a3f3c2faec65458e69125491b"}, 386 | ] 387 | 388 | [package.dependencies] 389 | poetry-semver = ">=0.1.0,<0.2.0" 390 | toml = ">=0.10.0,<0.11.0" 391 | 392 | [[package]] 393 | name = "pre-commit" 394 | version = "2.21.0" 395 | description = "A framework for managing and maintaining multi-language pre-commit hooks." 396 | optional = false 397 | python-versions = ">=3.7" 398 | files = [ 399 | {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, 400 | {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, 401 | ] 402 | 403 | [package.dependencies] 404 | cfgv = ">=2.0.0" 405 | identify = ">=1.0.0" 406 | importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} 407 | nodeenv = ">=0.11.1" 408 | pyyaml = ">=5.1" 409 | virtualenv = ">=20.10.0" 410 | 411 | [[package]] 412 | name = "pytest" 413 | version = "7.3.1" 414 | description = "pytest: simple powerful testing with Python" 415 | optional = false 416 | python-versions = ">=3.7" 417 | files = [ 418 | {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"}, 419 | {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"}, 420 | ] 421 | 422 | [package.dependencies] 423 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 424 | exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} 425 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 426 | iniconfig = "*" 427 | packaging = "*" 428 | pluggy = ">=0.12,<2.0" 429 | tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} 430 | 431 | [package.extras] 432 | testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] 433 | 434 | [[package]] 435 | name = "pytest-cov" 436 | version = "4.1.0" 437 | description = "Pytest plugin for measuring coverage." 438 | optional = false 439 | python-versions = ">=3.7" 440 | files = [ 441 | {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, 442 | {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, 443 | ] 444 | 445 | [package.dependencies] 446 | coverage = {version = ">=5.2.1", extras = ["toml"]} 447 | pytest = ">=4.6" 448 | 449 | [package.extras] 450 | testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] 451 | 452 | [[package]] 453 | name = "pytest-xdist" 454 | version = "3.3.1" 455 | description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" 456 | optional = false 457 | python-versions = ">=3.7" 458 | files = [ 459 | {file = "pytest-xdist-3.3.1.tar.gz", hash = "sha256:d5ee0520eb1b7bcca50a60a518ab7a7707992812c578198f8b44fdfac78e8c93"}, 460 | {file = "pytest_xdist-3.3.1-py3-none-any.whl", hash = "sha256:ff9daa7793569e6a68544850fd3927cd257cc03a7ef76c95e86915355e82b5f2"}, 461 | ] 462 | 463 | [package.dependencies] 464 | execnet = ">=1.1" 465 | pytest = ">=6.2.0" 466 | 467 | [package.extras] 468 | psutil = ["psutil (>=3.0)"] 469 | setproctitle = ["setproctitle"] 470 | testing = ["filelock"] 471 | 472 | [[package]] 473 | name = "pyyaml" 474 | version = "6.0" 475 | description = "YAML parser and emitter for Python" 476 | optional = false 477 | python-versions = ">=3.6" 478 | files = [ 479 | {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, 480 | {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, 481 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, 482 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, 483 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, 484 | {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, 485 | {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, 486 | {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, 487 | {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, 488 | {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, 489 | {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, 490 | {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, 491 | {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, 492 | {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, 493 | {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, 494 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, 495 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, 496 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, 497 | {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, 498 | {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, 499 | {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, 500 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, 501 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, 502 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, 503 | {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, 504 | {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, 505 | {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, 506 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, 507 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, 508 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, 509 | {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, 510 | {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, 511 | {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, 512 | {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, 513 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, 514 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, 515 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, 516 | {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, 517 | {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, 518 | {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, 519 | ] 520 | 521 | [[package]] 522 | name = "requests" 523 | version = "2.31.0" 524 | description = "Python HTTP for Humans." 525 | optional = false 526 | python-versions = ">=3.7" 527 | files = [ 528 | {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, 529 | {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, 530 | ] 531 | 532 | [package.dependencies] 533 | certifi = ">=2017.4.17" 534 | charset-normalizer = ">=2,<4" 535 | idna = ">=2.5,<4" 536 | urllib3 = ">=1.21.1,<3" 537 | 538 | [package.extras] 539 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] 540 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] 541 | 542 | [[package]] 543 | name = "responses" 544 | version = "0.23.1" 545 | description = "A utility library for mocking out the `requests` Python library." 546 | optional = false 547 | python-versions = ">=3.7" 548 | files = [ 549 | {file = "responses-0.23.1-py3-none-any.whl", hash = "sha256:8a3a5915713483bf353b6f4079ba8b2a29029d1d1090a503c70b0dc5d9d0c7bd"}, 550 | {file = "responses-0.23.1.tar.gz", hash = "sha256:c4d9aa9fc888188f0c673eff79a8dadbe2e75b7fe879dc80a221a06e0a68138f"}, 551 | ] 552 | 553 | [package.dependencies] 554 | pyyaml = "*" 555 | requests = ">=2.22.0,<3.0" 556 | types-PyYAML = "*" 557 | typing-extensions = {version = "*", markers = "python_version < \"3.8\""} 558 | urllib3 = ">=1.25.10" 559 | 560 | [package.extras] 561 | tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asyncio", "pytest-cov", "pytest-httpserver", "tomli", "tomli-w", "types-requests"] 562 | 563 | [[package]] 564 | name = "setuptools" 565 | version = "67.8.0" 566 | description = "Easily download, build, install, upgrade, and uninstall Python packages" 567 | optional = false 568 | python-versions = ">=3.7" 569 | files = [ 570 | {file = "setuptools-67.8.0-py3-none-any.whl", hash = "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f"}, 571 | {file = "setuptools-67.8.0.tar.gz", hash = "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102"}, 572 | ] 573 | 574 | [package.extras] 575 | docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] 576 | testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] 577 | testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] 578 | 579 | [[package]] 580 | name = "toml" 581 | version = "0.10.2" 582 | description = "Python Library for Tom's Obvious, Minimal Language" 583 | optional = false 584 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 585 | files = [ 586 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, 587 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, 588 | ] 589 | 590 | [[package]] 591 | name = "tomli" 592 | version = "2.0.1" 593 | description = "A lil' TOML parser" 594 | optional = false 595 | python-versions = ">=3.7" 596 | files = [ 597 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 598 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 599 | ] 600 | 601 | [[package]] 602 | name = "types-pyyaml" 603 | version = "6.0.12.10" 604 | description = "Typing stubs for PyYAML" 605 | optional = false 606 | python-versions = "*" 607 | files = [ 608 | {file = "types-PyYAML-6.0.12.10.tar.gz", hash = "sha256:ebab3d0700b946553724ae6ca636ea932c1b0868701d4af121630e78d695fc97"}, 609 | {file = "types_PyYAML-6.0.12.10-py3-none-any.whl", hash = "sha256:662fa444963eff9b68120d70cda1af5a5f2aa57900003c2006d7626450eaae5f"}, 610 | ] 611 | 612 | [[package]] 613 | name = "typing-extensions" 614 | version = "4.6.2" 615 | description = "Backported and Experimental Type Hints for Python 3.7+" 616 | optional = false 617 | python-versions = ">=3.7" 618 | files = [ 619 | {file = "typing_extensions-4.6.2-py3-none-any.whl", hash = "sha256:3a8b36f13dd5fdc5d1b16fe317f5668545de77fa0b8e02006381fd49d731ab98"}, 620 | {file = "typing_extensions-4.6.2.tar.gz", hash = "sha256:06006244c70ac8ee83fa8282cb188f697b8db25bc8b4df07be1873c43897060c"}, 621 | ] 622 | 623 | [[package]] 624 | name = "urllib3" 625 | version = "2.0.2" 626 | description = "HTTP library with thread-safe connection pooling, file post, and more." 627 | optional = false 628 | python-versions = ">=3.7" 629 | files = [ 630 | {file = "urllib3-2.0.2-py3-none-any.whl", hash = "sha256:d055c2f9d38dc53c808f6fdc8eab7360b6fdbbde02340ed25cfbcd817c62469e"}, 631 | {file = "urllib3-2.0.2.tar.gz", hash = "sha256:61717a1095d7e155cdb737ac7bb2f4324a858a1e2e6466f6d03ff630ca68d3cc"}, 632 | ] 633 | 634 | [package.extras] 635 | brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] 636 | secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] 637 | socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] 638 | zstd = ["zstandard (>=0.18.0)"] 639 | 640 | [[package]] 641 | name = "virtualenv" 642 | version = "20.23.0" 643 | description = "Virtual Python Environment builder" 644 | optional = false 645 | python-versions = ">=3.7" 646 | files = [ 647 | {file = "virtualenv-20.23.0-py3-none-any.whl", hash = "sha256:6abec7670e5802a528357fdc75b26b9f57d5d92f29c5462ba0fbe45feacc685e"}, 648 | {file = "virtualenv-20.23.0.tar.gz", hash = "sha256:a85caa554ced0c0afbd0d638e7e2d7b5f92d23478d05d17a76daeac8f279f924"}, 649 | ] 650 | 651 | [package.dependencies] 652 | distlib = ">=0.3.6,<1" 653 | filelock = ">=3.11,<4" 654 | importlib-metadata = {version = ">=6.4.1", markers = "python_version < \"3.8\""} 655 | platformdirs = ">=3.2,<4" 656 | 657 | [package.extras] 658 | docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] 659 | test = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.7.1)", "time-machine (>=2.9)"] 660 | 661 | [[package]] 662 | name = "yapf" 663 | version = "0.33.0" 664 | description = "A formatter for Python code." 665 | optional = false 666 | python-versions = "*" 667 | files = [ 668 | {file = "yapf-0.33.0-py2.py3-none-any.whl", hash = "sha256:4c2b59bd5ffe46f3a7da48df87596877189148226ce267c16e8b44240e51578d"}, 669 | {file = "yapf-0.33.0.tar.gz", hash = "sha256:da62bdfea3df3673553351e6246abed26d9fe6780e548a5af9e70f6d2b4f5b9a"}, 670 | ] 671 | 672 | [package.dependencies] 673 | tomli = ">=2.0.1" 674 | 675 | [[package]] 676 | name = "zipp" 677 | version = "3.15.0" 678 | description = "Backport of pathlib-compatible object wrapper for zip files" 679 | optional = false 680 | python-versions = ">=3.7" 681 | files = [ 682 | {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, 683 | {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, 684 | ] 685 | 686 | [package.extras] 687 | docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] 688 | testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] 689 | 690 | [metadata] 691 | lock-version = "2.0" 692 | python-versions = ">=3.7, <4.0" 693 | content-hash = "b6ff1c016cffbcd76fb5a342e2beadce57e4a3ec3895cf33f2cad26348c25dfc" 694 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "openrouteservice" 3 | version = "2.3.3" 4 | description = "Python client for requests to openrouteservice API services." 5 | authors = ["Julian Psotta "] 6 | readme = 'README.rst' 7 | license = "Apache2" 8 | 9 | [tool.poetry.dependencies] 10 | python = ">=3.7, <4.0" 11 | requests = ">=2.31.0" 12 | responses = ">=0.23.1" 13 | poetry2conda = "^0.3.0" 14 | 15 | 16 | [tool.poetry.dev-dependencies] 17 | pytest = ">=7.3.1" 18 | pytest-cov = ">=4.1.0" 19 | yapf = ">=0.33.0" 20 | importlib-metadata = ">=6.6.0" 21 | virtualenv = { version = ">=20.23.0", python = ">=3.6, <4.0" } 22 | pre-commit = { version = ">=2.21.0", python = ">=3.6, <4.0" } 23 | coverage = "^7.2.7" 24 | pytest-xdist = "^3.3.1" 25 | 26 | [tool.pytest.ini_options] 27 | addopts = "--cov=openrouteservice --cov-report xml --cov-report term-missing --cov-fail-under 96 -n auto" 28 | 29 | [build-system] 30 | requires = ["poetry-core>=1.0.0"] 31 | build-backend = "poetry.core.masonry.api" 32 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Google Inc. All rights reserved. 2 | # 3 | # Modifications Copyright (C) 2018 HeiGIT, University of Heidelberg. 4 | # 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 7 | # use this file except in compliance with the License. You may obtain a copy of 8 | # the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | # License for the specific language governing permissions and limitations under 16 | # the License. 17 | # 18 | 19 | import unittest 20 | import openrouteservice 21 | 22 | from urllib.parse import urlparse, parse_qsl 23 | 24 | # For relative imports to work in Python 3.6 25 | import os 26 | import sys 27 | 28 | sys.path.append(os.path.dirname(os.path.realpath(__file__))) 29 | 30 | 31 | class TestCase(unittest.TestCase): 32 | def setUp(self): 33 | self.key = "sample_key" 34 | self.client = openrouteservice.Client(self.key) 35 | 36 | def assertURLEqual(self, first, second, msg=None): 37 | """Check that two arguments are equivalent URLs. Ignores the order of 38 | query arguments. 39 | """ 40 | first_parsed = urlparse(first) 41 | second_parsed = urlparse(second) 42 | self.assertEqual(first_parsed[:3], second_parsed[:3], msg) 43 | 44 | first_qsl = sorted(parse_qsl(first_parsed.query)) 45 | second_qsl = sorted(parse_qsl(second_parsed.query)) 46 | self.assertEqual(first_qsl, second_qsl, msg) 47 | 48 | def assertDictContainsSubset(self, a, b, **kwargs): 49 | """Replaces deprecated unittest.TestCase.assertDictContainsSubset""" 50 | c = dict([(k, b[k]) for k in a.keys() if k in b.keys()]) 51 | self.assertEqual(a, c) 52 | -------------------------------------------------------------------------------- /test/test_client.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2014 Google Inc. All rights reserved. 3 | # 4 | # Modifications Copyright (C) 2018 HeiGIT, University of Heidelberg. 5 | # 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 8 | # use this file except in compliance with the License. You may obtain a copy of 9 | # the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16 | # License for the specific language governing permissions and limitations under 17 | # the License. 18 | # 19 | """Tests for client module.""" 20 | 21 | import responses 22 | import time 23 | 24 | import openrouteservice 25 | from test.test_helper import * 26 | import test as _test 27 | 28 | 29 | class ClientTest(_test.TestCase): 30 | def test_no_api_key(self): 31 | with self.assertRaises(ValueError): 32 | client = openrouteservice.Client() 33 | client.directions(PARAM_LINE) 34 | 35 | def test_invalid_api_key(self): 36 | with self.assertRaises(openrouteservice.exceptions.ApiError): 37 | client = openrouteservice.Client(key="Invalid key.") 38 | client.directions(PARAM_LINE) 39 | 40 | def test_urlencode(self): 41 | encoded_params = openrouteservice.client._urlencode_params( 42 | [("address", "=Sydney ~")] 43 | ) 44 | self.assertEqual("address=%3DSydney+~", encoded_params) 45 | 46 | @responses.activate 47 | def test_raise_over_query_limit(self): 48 | valid_query = ENDPOINT_DICT["directions"] 49 | responses.add( 50 | responses.POST, 51 | "https://api.openrouteservice.org/v2/directions/{}/geojson".format( 52 | valid_query["profile"] 53 | ), 54 | json=valid_query, 55 | status=429, 56 | content_type="application/json", 57 | ) 58 | 59 | with self.assertRaises(openrouteservice.exceptions._OverQueryLimit): 60 | client = openrouteservice.Client( 61 | key=self.key, retry_over_query_limit=False 62 | ) 63 | client.directions(**valid_query) 64 | 65 | with self.assertRaises(openrouteservice.exceptions.Timeout): 66 | client = openrouteservice.Client( 67 | key=self.key, retry_over_query_limit=True, retry_timeout=3 68 | ) 69 | client.directions(**valid_query) 70 | 71 | @responses.activate 72 | def test_raise_timeout_retriable_requests(self): 73 | # Mock query gives 503 as HTTP status, code should try a few times to 74 | # request the same and then fail on Timout() error. 75 | retry_timeout = 3 76 | valid_query = ENDPOINT_DICT["directions"] 77 | responses.add( 78 | responses.POST, 79 | "https://api.openrouteservice.org/v2/directions/{}/geojson".format( 80 | valid_query["profile"] 81 | ), 82 | json=valid_query, 83 | status=503, 84 | content_type="application/json", 85 | ) 86 | 87 | client = openrouteservice.Client( 88 | key=self.key, retry_timeout=retry_timeout 89 | ) 90 | 91 | start = time.time() 92 | with self.assertRaises(openrouteservice.exceptions.Timeout): 93 | client.directions(**valid_query) 94 | end = time.time() 95 | self.assertTrue(retry_timeout < end - start < 3 * retry_timeout) 96 | 97 | @responses.activate 98 | def test_host_override_with_parameters(self): 99 | # Test if it's possible to override host for individual hosting. 100 | responses.add( 101 | responses.GET, 102 | "https://foo.com/bar", 103 | body='{"status":"OK","results":[]}', 104 | status=200, 105 | content_type="application/json", 106 | ) 107 | 108 | client = openrouteservice.Client(base_url="https://foo.com") 109 | client.request("/bar", {"bunny": "pretty", "fox": "prettier"}) 110 | 111 | request = client.req 112 | 113 | self.assertEqual( 114 | "https://foo.com/bar?bunny=pretty&fox=prettier", request.url 115 | ) 116 | self.assertEqual("GET", request.method) 117 | self.assertEqual({"bunny": "pretty", "fox": "prettier"}, request.params) 118 | 119 | self.assertURLEqual( 120 | "https://foo.com/bar?bunny=pretty&fox=prettier", 121 | responses.calls[0].request.url, 122 | ) 123 | self.assertEqual(1, len(responses.calls)) 124 | 125 | @responses.activate 126 | def test_dry_run(self): 127 | # Test that nothing is requested when dry_run is 'true' 128 | 129 | responses.add( 130 | responses.GET, 131 | "https://api.openrouteservice.org/directions", 132 | body='{"status":"OK","results":[]}', 133 | status=200, 134 | content_type="application/json", 135 | ) 136 | 137 | req = self.client.request( 138 | get_params={"format_out": "geojson"}, 139 | url="directions/", 140 | dry_run="true", 141 | ) 142 | 143 | self.assertEqual(0, len(responses.calls)) 144 | 145 | @responses.activate 146 | def test_no_get_parameter(self): 147 | responses.add( 148 | responses.POST, 149 | "https://api.openrouteservice.org/directions", 150 | body='{"status":"OK","results":[]}', 151 | status=200, 152 | content_type="application/json", 153 | ) 154 | 155 | req = self.client.request( 156 | post_json={}, url="v2/directions/driving-car/json", dry_run="true" 157 | ) 158 | 159 | self.assertEqual(0, len(responses.calls)) 160 | 161 | # Test if the client works with a post request without a get parameter 162 | 163 | @responses.activate 164 | def test_key_in_header(self): 165 | # Test that API key is being put in the Authorization header 166 | query = ENDPOINT_DICT["directions"] 167 | 168 | responses.add( 169 | responses.POST, 170 | "https://api.openrouteservice.org/v2/directions/{}/geojson".format( 171 | query["profile"] 172 | ), 173 | json=ENDPOINT_DICT["directions"], 174 | status=200, 175 | content_type="application/json", 176 | ) 177 | 178 | resp = self.client.directions(**query) 179 | 180 | self.assertDictContainsSubset( 181 | {"Authorization": self.key}, responses.calls[0].request.headers 182 | ) 183 | -------------------------------------------------------------------------------- /test/test_convert.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2014 Google Inc. All rights reserved. 3 | # 4 | # Modifications Copyright (C) 2018 HeiGIT, University of Heidelberg. 5 | # 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 8 | # use this file except in compliance with the License. You may obtain a copy of 9 | # the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16 | # License for the specific language governing permissions and limitations under 17 | # the License. 18 | # 19 | """Tests for the convert module.""" 20 | 21 | import unittest 22 | 23 | from openrouteservice import convert 24 | 25 | 26 | class ConvertTest(unittest.TestCase): 27 | def test_build_single_coord_tuple(self): 28 | expected = "1,2" 29 | ll = (1, 2) 30 | self.assertEqual(expected, convert._build_coords(ll)) 31 | 32 | ll = [1, 2] 33 | self.assertEqual(expected, convert._build_coords(ll)) 34 | 35 | with self.assertRaises(TypeError): 36 | convert._build_coords(1) 37 | 38 | with self.assertRaises(TypeError): 39 | convert._build_coords({"lat": 1, "lon": 2}) 40 | 41 | with self.assertRaises(TypeError): 42 | convert._build_coords("1,2") 43 | 44 | def test_build_multi_coord_tuple(self): 45 | expected = "1,2|3,4" 46 | 47 | ll = ((1, 2), (3, 4)) 48 | self.assertEqual(expected, convert._build_coords(ll)) 49 | 50 | ll = [(1, 2), (3, 4)] 51 | self.assertEqual(expected, convert._build_coords(ll)) 52 | 53 | ll = ([1, 2], [3, 4]) 54 | self.assertEqual(expected, convert._build_coords(ll)) 55 | 56 | ll = [[1, 2], [3, 4]] 57 | self.assertEqual(expected, convert._build_coords(ll)) 58 | 59 | with self.assertRaises(TypeError): 60 | convert._build_coords({{"lat": 1, "lon": 2}, {"lat": 3, "lon": 4}}) 61 | 62 | with self.assertRaises(TypeError): 63 | convert._build_coords("[1,2],[3,4]") 64 | 65 | def test_convert_bool(self): 66 | self.assertEqual("true", convert._convert_bool("True")) 67 | self.assertEqual("true", convert._convert_bool("true")) 68 | self.assertEqual("true", convert._convert_bool(True)) 69 | 70 | def test_polyline_decode_3d(self): 71 | syd_mel_route = ( 72 | r"mlqlHat`t@OiACMvAs@HCPGJ?JAJBRFTRLJPNHDNDJ" 73 | "@D?fACRAZCPAb@AF?HAfBQJEDAn@QFC@QD_@@QFe@Bg" 74 | "@@KBy@?M@a@@q@?iE?C?OGgAkEwUQ{@c@gBQeAYeCIe" 75 | "AWmDAIImACUOyBIeAC}@Ey@?QLC@_@@KBiAVmDF]Ni@" 76 | "Zu@RYBA^_@~A{A`Ai@JCPGf@Qf@]X_@BMAMIKuBTI?G" 77 | "E?A?ADOnCsB\c@DGDIl@sAJUFMBGJUP[DCD@DP@l@?R" 78 | "?h@Bx@PnAAl@?BAFc@rAAB?@BRHBFEN[FQFQRg@Rw@J" 79 | "g@Ny@DUDOJe@N_ADm@BkBGcC@s@Du@l@eEZgBP_AHe@" 80 | "He@Fc@RuATaA?SCWAGIOQS[Qu@Ym@C}@R{@`@m@p@Wj" 81 | "@]nAGBE?KGAE?E?KVcB`@eB^mAn@uALUJSj@y@fA}@f" 82 | "@k@BGHM^k@r@qAHSLU^i@bA_Af@q@PYFKHIHCJ?RLFN" 83 | "XjAj@tDj@rERzBLzCHp@xAdKLf@RXTDNEBCFGDEDE@G" 84 | "@GDKBGRc@Xi@N[JUf@u@l@o@f@c@h@]XMfQ}D|EcAlA" 85 | "ORIJQ?C?CAUKOSGwAMa@M_EsBcBqA_A{@k@q@sCcEi@" 86 | "gAWo@[gAYyAMy@y@aNMyAc@uDS_As@uBMc@Ig@SeBKc" 87 | "@Uy@AI@A]GGCMIiCmAGCWMqAk@" 88 | ) 89 | 90 | points = convert.decode_polyline(syd_mel_route, True)["coordinates"] 91 | self.assertEqual(len(points[0]), 3) 92 | self.assertAlmostEqual(8.69201, points[0][0], places=5) 93 | self.assertAlmostEqual(49.410151, points[0][1], places=5) 94 | self.assertAlmostEqual(0.1, points[0][2], places=2) 95 | self.assertAlmostEqual(8.69917, points[-1][0], places=5) 96 | self.assertAlmostEqual(49.41868, points[-1][1], places=5) 97 | self.assertAlmostEqual(12.5, points[-1][2], places=2) 98 | 99 | def test_polyline_decode_2d(self): 100 | syd_mel_route = r"u`rgFswjpAKD" 101 | 102 | points = convert.decode_polyline(syd_mel_route, False)["coordinates"] 103 | self.assertEqual(len(points[0]), 2) 104 | self.assertAlmostEqual([13.3313, 38.10843], points[0], places=5) 105 | self.assertAlmostEqual([13.33127, 38.10849], points[1], places=5) 106 | 107 | def test_pipe_list_bad_argument(self): 108 | with self.assertRaises(TypeError): 109 | convert._pipe_list(5) 110 | 111 | def test_comma_list_bad_argument(self): 112 | with self.assertRaises(TypeError): 113 | convert._comma_list(5) 114 | -------------------------------------------------------------------------------- /test/test_deprecation_warning.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | 3 | from openrouteservice import deprecation 4 | 5 | with warnings.catch_warnings(record=True) as w: 6 | # Cause all warnings to always be triggered. 7 | warnings.simplefilter("always") 8 | # Trigger a warning. 9 | deprecation.warning("foo", "bar") 10 | # Verify some things 11 | assert len(w) == 1 12 | assert issubclass(w[-1].category, DeprecationWarning) 13 | assert "deprecated" in str(w[-1].message) 14 | -------------------------------------------------------------------------------- /test/test_directions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2014 Google Inc. All rights reserved. 3 | # 4 | # Modifications Copyright (C) 2018 HeiGIT, University of Heidelberg. 5 | # 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 8 | # use this file except in compliance with the License. You may obtain a copy of 9 | # the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16 | # License for the specific language governing permissions and limitations under 17 | # the License. 18 | # 19 | """Tests for the directions module.""" 20 | 21 | import responses 22 | import warnings 23 | 24 | import test as _test 25 | from copy import deepcopy 26 | 27 | from openrouteservice import exceptions 28 | from test.test_helper import ENDPOINT_DICT, GPX_RESPONSE 29 | 30 | 31 | class DirectionsTest(_test.TestCase): 32 | valid_query = ENDPOINT_DICT["directions"] 33 | 34 | @responses.activate 35 | def test_directions(self): 36 | responses.add( 37 | responses.POST, 38 | "https://api.openrouteservice.org/v2/directions/{}/geojson".format( 39 | self.valid_query["profile"] 40 | ), 41 | json=self.valid_query, 42 | status=200, 43 | content_type="application/json", 44 | ) 45 | 46 | resp = self.client.directions(**self.valid_query) 47 | 48 | self.assertEqual(resp, self.valid_query) 49 | self.assertIn("sample_key", responses.calls[0].request.headers.values()) 50 | 51 | @responses.activate 52 | def test_directions_gpx(self): 53 | query = deepcopy(self.valid_query) 54 | query["format"] = "gpx" 55 | 56 | responses.add( 57 | responses.POST, 58 | "https://api.openrouteservice.org/v2/directions/{}/gpx".format( 59 | self.valid_query["profile"] 60 | ), 61 | body=GPX_RESPONSE, 62 | status=200, 63 | content_type="application/gpx+xml;charset=UTF-8", 64 | ) 65 | 66 | resp = self.client.directions(**query) 67 | 68 | self.assertEqual(resp, GPX_RESPONSE) 69 | self.assertIn("sample_key", responses.calls[0].request.headers.values()) 70 | 71 | @responses.activate 72 | def test_directions_incompatible_parameters(self): 73 | self.valid_query["optimized"] = True 74 | responses.add( 75 | responses.POST, 76 | "https://api.openrouteservice.org/v2/directions/{}/geojson".format( 77 | self.valid_query["profile"] 78 | ), 79 | json=self.valid_query, 80 | status=200, 81 | content_type="application/json", 82 | ) 83 | 84 | resp = self.client.directions(**self.valid_query) 85 | 86 | self.assertEqual(resp, self.valid_query) 87 | self.assertIn("sample_key", responses.calls[0].request.headers.values()) 88 | 89 | def test_format_out_deprecation(self): 90 | bad_query = deepcopy(self.valid_query) 91 | bad_query["format_out"] = "json" 92 | bad_query["dry_run"] = True 93 | 94 | with warnings.catch_warnings(record=True) as w: 95 | # Cause all warnings to always be triggered. 96 | warnings.simplefilter("always") 97 | # Trigger a warning. 98 | _ = self.client.directions(**bad_query) 99 | 100 | assert len(w) == 1 101 | assert issubclass(w[-1].category, DeprecationWarning) 102 | assert "deprecated" in str(w[-1].message) 103 | 104 | def test_optimized_waypoints(self): 105 | query = deepcopy(self.valid_query) 106 | query["coordinates"] = [ 107 | [8.688641, 49.420577], 108 | [8.680916, 49.415776], 109 | [8.688641, 49.420577], 110 | [8.680916, 49.415776], 111 | ] 112 | query["optimize_waypoints"] = True 113 | 114 | responses.add( 115 | responses.POST, 116 | "https://api.openrouteservice.org/v2/directions/{}/geojson".format( 117 | query["profile"] 118 | ), 119 | json=query, 120 | status=200, 121 | content_type="application/json", 122 | ) 123 | 124 | # Too exhausting to really test this 125 | with self.assertRaises(exceptions.ApiError): 126 | resp = self.client.directions(**query) 127 | 128 | def test_optimized_waypoints_shuffle(self): 129 | query = deepcopy(self.valid_query) 130 | query["coordinates"] = [ 131 | [8.688641, 49.420577], 132 | [8.680916, 49.415776], 133 | [8.688641, 49.420577], 134 | [8.680916, 49.415776], 135 | ] 136 | query["optimize_waypoints"] = True 137 | query.pop("options") 138 | 139 | responses.add( 140 | responses.POST, 141 | "https://api.openrouteservice.org/v2/directions/{}/geojson".format( 142 | query["profile"] 143 | ), 144 | json=query, 145 | status=200, 146 | content_type="application/json", 147 | ) 148 | with self.assertRaises(exceptions.ApiError): 149 | resp = self.client.directions(**query) 150 | 151 | @responses.activate 152 | def test_optimize_warnings(self): 153 | query = deepcopy(self.valid_query) 154 | query["optimize_waypoints"] = True 155 | 156 | # Test Coordinates 157 | 158 | responses.add( 159 | responses.POST, 160 | "https://api.openrouteservice.org/v2/directions/{}/geojson".format( 161 | query["profile"] 162 | ), 163 | json=query, 164 | status=200, 165 | content_type="application/json", 166 | ) 167 | 168 | with warnings.catch_warnings(record=True) as w: 169 | # Cause all warnings to always be triggered. 170 | warnings.simplefilter("always") 171 | 172 | resp = self.client.directions(**query) 173 | 174 | assert len(w) == 1 175 | assert issubclass(w[-1].category, UserWarning) 176 | assert "4 coordinates" in str(w[-1].message) 177 | 178 | # Test Options 179 | 180 | query["coordinates"] = [ 181 | [8.688641, 49.420577], 182 | [8.680916, 49.415776], 183 | [8.688641, 49.420577], 184 | [8.680916, 49.415776], 185 | ] 186 | 187 | responses.add( 188 | responses.POST, 189 | "https://api.openrouteservice.org/v2/directions/{}/geojson".format( 190 | query["profile"] 191 | ), 192 | json=query, 193 | status=200, 194 | content_type="application/json", 195 | ) 196 | 197 | with warnings.catch_warnings(record=True) as w: 198 | # Cause all warnings to always be triggered. 199 | warnings.simplefilter("always") 200 | 201 | resp = self.client.directions(**query) 202 | 203 | assert len(w) == 1 204 | assert issubclass(w[-1].category, UserWarning) 205 | assert "Options" in str(w[-1].message) 206 | 207 | # Test Preference 208 | 209 | query["options"] = None 210 | query["preference"] = "shortest" 211 | 212 | responses.add( 213 | responses.POST, 214 | "https://api.openrouteservice.org/v2/directions/{}/geojson".format( 215 | query["profile"] 216 | ), 217 | json=query, 218 | status=200, 219 | content_type="application/json", 220 | ) 221 | 222 | with warnings.catch_warnings(record=True) as w: 223 | # Cause all warnings to always be triggered. 224 | warnings.simplefilter("always") 225 | 226 | resp = self.client.directions(**query) 227 | 228 | assert len(w) == 1 229 | assert issubclass(w[-1].category, UserWarning) 230 | assert "Shortest" in str(w[-1].message) 231 | -------------------------------------------------------------------------------- /test/test_distance_matrix.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2014 Google Inc. All rights reserved. 3 | # 4 | # Modifications Copyright (C) 2018 HeiGIT, University of Heidelberg. 5 | # 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 8 | # use this file except in compliance with the License. You may obtain a copy of 9 | # the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16 | # License for the specific language governing permissions and limitations under 17 | # the License. 18 | # 19 | """Tests for the distance matrix module.""" 20 | import responses 21 | import test as _test 22 | 23 | from test.test_helper import ENDPOINT_DICT, PARAM_LINE 24 | 25 | 26 | class DistanceMatrixTest(_test.TestCase): 27 | valid_query = ENDPOINT_DICT["distance_matrix"] 28 | 29 | @responses.activate 30 | def test_matrix(self): 31 | query = self.valid_query.copy() 32 | query["locations"] = tuple([tuple(x) for x in PARAM_LINE]) 33 | 34 | responses.add( 35 | responses.POST, 36 | "https://api.openrouteservice.org/v2/matrix/{}/json".format( 37 | query["profile"] 38 | ), 39 | json=query, 40 | status=200, 41 | content_type="application/json", 42 | ) 43 | 44 | resp = self.client.distance_matrix(**query) 45 | self.assertEqual(resp, self.valid_query) 46 | -------------------------------------------------------------------------------- /test/test_elevation.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright (C) 2018 HeiGIT, University of Heidelberg. 3 | # 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 6 | # use this file except in compliance with the License. You may obtain a copy of 7 | # the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations under 15 | # the License. 16 | """Tests for the distance matrix module.""" 17 | import responses 18 | import test as _test 19 | 20 | from test.test_helper import ENDPOINT_DICT 21 | 22 | 23 | class ElevationTest(_test.TestCase): 24 | valid_query = ENDPOINT_DICT["elevation_line"] 25 | 26 | @responses.activate 27 | def test_elevation_line(self): 28 | responses.add( 29 | responses.POST, 30 | "https://api.openrouteservice.org/elevation/line", 31 | json=self.valid_query, 32 | status=200, 33 | content_type="application/json", 34 | ) 35 | 36 | resp = self.client.elevation_line(**self.valid_query) 37 | 38 | self.assertEqual(len(responses.calls), 1) 39 | self.assertEqual(resp, self.valid_query) 40 | 41 | @responses.activate 42 | def test_elevation_point(self): 43 | query = ENDPOINT_DICT["elevation_point"] 44 | responses.add( 45 | responses.POST, 46 | "https://api.openrouteservice.org/elevation/point", 47 | json=self.valid_query, 48 | status=200, 49 | content_type="application/json", 50 | ) 51 | 52 | resp = self.client.elevation_point(**query) 53 | 54 | self.assertEqual(len(responses.calls), 1) 55 | self.assertEqual(resp, self.valid_query) 56 | -------------------------------------------------------------------------------- /test/test_exceptions.py: -------------------------------------------------------------------------------- 1 | from openrouteservice.exceptions import ( 2 | ValidationError, 3 | ApiError, 4 | HTTPError, 5 | Timeout, 6 | _RetriableRequest, 7 | _OverQueryLimit, 8 | ) 9 | 10 | import test as _test 11 | 12 | from pprint import pprint 13 | 14 | 15 | class ExceptionTest(_test.TestCase): 16 | def test_ValidationError(self): 17 | exception = ValidationError("hamspam") 18 | 19 | pprint(exception.__dict__) 20 | self.assertIsInstance(exception, Exception) 21 | 22 | def test_ApIError(self): 23 | exception = ApiError(500, "hamspam") 24 | 25 | pprint(exception.__dict__) 26 | 27 | self.assertEqual(exception.status, 500) 28 | self.assertEqual(exception.message, "hamspam") 29 | 30 | self.assertEqual(str(exception), "500 (hamspam)") 31 | 32 | exception = ApiError(500) 33 | 34 | self.assertEqual(str(exception), "500") 35 | 36 | def test_HTTPError(self): 37 | exception = HTTPError(500) 38 | 39 | self.assertEqual(exception.status_code, 500) 40 | 41 | self.assertEqual(str(exception), "HTTP Error: 500") 42 | 43 | def test_Timeout(self): 44 | exception = Timeout() 45 | 46 | self.assertIsInstance(exception, Exception) 47 | 48 | def test_RetriableRequest(self): 49 | exception = _RetriableRequest() 50 | 51 | self.assertIsInstance(exception, Exception) 52 | 53 | def test_OverQueryLimit(self): 54 | exception = _OverQueryLimit(500, "hamspam") 55 | 56 | self.assertIsInstance(exception, Exception) 57 | self.assertIsInstance(exception, ApiError) 58 | self.assertIsInstance(exception, _RetriableRequest) 59 | 60 | self.assertEqual(str(exception), "500 (hamspam)") 61 | -------------------------------------------------------------------------------- /test/test_geocode.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2014 Google Inc. All rights reserved. 3 | # 4 | # Modifications Copyright (C) 2018 HeiGIT, University of Heidelberg. 5 | # 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 8 | # use this file except in compliance with the License. You may obtain a copy of 9 | # the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16 | # License for the specific language governing permissions and limitations under 17 | # the License. 18 | """Tests for the Pelias geocoding module.""" 19 | 20 | import responses 21 | 22 | import test as _test 23 | from test.test_helper import ENDPOINT_DICT 24 | 25 | 26 | class GeocodingPeliasTest(_test.TestCase): 27 | search = ENDPOINT_DICT["pelias_search"] 28 | autocomplete = ENDPOINT_DICT["pelias_autocomplete"] 29 | structured = ENDPOINT_DICT["pelias_structured"] 30 | reverse = ENDPOINT_DICT["pelias_reverse"] 31 | 32 | @responses.activate 33 | def test_pelias_search(self): 34 | responses.add( 35 | responses.GET, 36 | "https://api.openrouteservice.org/geocode/search", 37 | body='{"status":"OK","results":[]}', 38 | status=200, 39 | content_type="application/json", 40 | ) 41 | 42 | results = self.client.pelias_search(**self.search) 43 | 44 | self.assertEqual(1, len(responses.calls)) 45 | self.assertURLEqual( 46 | "https://api.openrouteservice.org/geocode/search?boundary.circle.lat=48.23424&boundary.circle.lon=8.34234&boundary.circle.radius=50&boundary.country=de&boundary.rect.max_lat=501&boundary.rect.max_lon=501&boundary.rect.min_lat=500&boundary.rect.min_lon=500&focus.point.lat=48.23424&focus.point.lon=8.34234&layers=locality%2Ccounty%2Cregion&size=50&sources=osm%2Cwof%2Cgn&text=Heidelberg", 47 | responses.calls[0].request.url, 48 | ) 49 | 50 | @responses.activate 51 | def test_pelias_autocomplete(self): 52 | responses.add( 53 | responses.GET, 54 | "https://api.openrouteservice.org/geocode/autocomplete", 55 | body='{"status":"OK","results":[]}', 56 | status=200, 57 | content_type="application/json", 58 | ) 59 | 60 | results = self.client.pelias_autocomplete(**self.autocomplete) 61 | 62 | self.assertEqual(1, len(responses.calls)) 63 | self.assertURLEqual( 64 | "https://api.openrouteservice.org/geocode/autocomplete?boundary.country=de&boundary.rect.max_lon%09=500&boundary.rect.min_lat%09=500&boundary.rect.min_lon%09=500&focus.point.lat=48.23424&focus.point.lon=8.34234&layers=locality%2Ccounty%2Cregion&sources=osm%2Cwof%2Cgn&text=Heidelberg", 65 | responses.calls[0].request.url, 66 | ) 67 | 68 | @responses.activate 69 | def test_pelias_structured(self): 70 | responses.add( 71 | responses.GET, 72 | "https://api.openrouteservice.org/geocode/search/structured", 73 | body='{"status":"OK","results":[]}', 74 | status=200, 75 | content_type="application/json", 76 | ) 77 | 78 | results = self.client.pelias_structured(**self.structured) 79 | 80 | self.assertEqual(1, len(responses.calls)) 81 | self.assertURLEqual( 82 | "https://api.openrouteservice.org/geocode/search/structured?address=Berliner+Stra%C3%9Fe+45&borough=Heidelberg&country=de&county=Rhein-Neckar-Kreis&locality=Heidelberg&neighbourhood=Neuenheimer+Feld&postalcode=69120®ion=Baden-W%C3%BCrttemberg", 83 | responses.calls[0].request.url, 84 | ) 85 | 86 | @responses.activate 87 | def test_pelias_reverse(self): 88 | responses.add( 89 | responses.GET, 90 | "https://api.openrouteservice.org/geocode/reverse", 91 | body='{"status":"OK","results":[]}', 92 | status=200, 93 | content_type="application/json", 94 | ) 95 | 96 | results = self.client.pelias_reverse(**self.reverse) 97 | 98 | self.assertEqual(1, len(responses.calls)) 99 | self.assertURLEqual( 100 | "https://api.openrouteservice.org/geocode/reverse?boundary.circle.radius=50&boundary.country=de&layers=locality%2Ccounty%2Cregion&point.lat=48.23424&point.lon=8.34234&size=50&sources=osm%2Cwof%2Cgn", 101 | responses.calls[0].request.url, 102 | ) 103 | -------------------------------------------------------------------------------- /test/test_helper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | PARAM_POINT = [8.34234, 48.23424] 4 | PARAM_LINE = [[8.688641, 49.420577], [8.680916, 49.415776]] 5 | PARAM_POLY = [[[8.688641, 49.420577], [8.680916, 49.415776]]] 6 | 7 | PARAM_INT_BIG = 500 8 | PARAM_INT_SMALL = 50 9 | PARAM_LIST_ONE = [PARAM_INT_SMALL, PARAM_INT_SMALL] 10 | PARAM_LIST_TWO = [PARAM_LIST_ONE, PARAM_LIST_ONE] 11 | 12 | PARAM_GEOJSON_POINT = {"type": "Point", "coordinates": PARAM_POINT} 13 | PARAM_GEOJSON_LINE = {"type": "LineString", "coordinates": PARAM_LINE} 14 | PARAM_GEOJSON_POLY = {"type": "Polygon", "coordinates": PARAM_POLY} 15 | 16 | ENDPOINT_DICT = { 17 | "directions": { 18 | "coordinates": PARAM_LINE, 19 | "profile": "driving-car", 20 | "preference": "fastest", 21 | "format": "geojson", 22 | "units": "mi", 23 | "language": "en", 24 | "geometry": "true", 25 | "geometry_simplify": "false", 26 | "maneuvers": True, 27 | "suppress_warnings": False, 28 | "instructions": "false", 29 | "instructions_format": "html", 30 | "alternative_routes": { 31 | "share_factor": 0.6, 32 | "target_count": 2, 33 | "weight_factor": 1.4, 34 | }, 35 | "roundabout_exits": "true", 36 | "attributes": ["avgspeed"], 37 | "radiuses": PARAM_LIST_ONE, 38 | "bearings": PARAM_LIST_TWO, 39 | "skip_segments": [0, 1], 40 | "elevation": "true", 41 | "maximum_speed": 95, 42 | "extra_info": ["roadaccessrestrictions"], 43 | "optimized": "false", 44 | "continue_straight": True, 45 | "options": {"avoid_features": ["highways", "tollways"]}, 46 | }, 47 | "isochrones": { 48 | "locations": PARAM_LINE, 49 | "profile": "cycling-regular", 50 | "range_type": "distance", 51 | "range": [PARAM_INT_BIG], 52 | "units": "m", 53 | "location_type": "destination", 54 | "attributes": ["area", "reachfactor"], 55 | "interval": [PARAM_INT_SMALL], 56 | "intersections": "true", 57 | "options": {"avoid_features": ["highways", "tollways"]}, 58 | }, 59 | "distance_matrix": { 60 | "locations": PARAM_LINE, 61 | "sources": [1], 62 | "destinations": [0], 63 | "profile": "driving-car", 64 | "metrics": ["duration", "distance"], 65 | "resolve_locations": "true", 66 | "units": "mi", 67 | "optimized": "false", 68 | }, 69 | "elevation_point": { 70 | "format_in": "geojson", 71 | "format_out": "point", 72 | "geometry": PARAM_GEOJSON_POINT, 73 | "dataset": "srtm", 74 | }, 75 | "elevation_line": { 76 | "format_in": "geojson", 77 | "format_out": "polyline", 78 | "geometry": PARAM_GEOJSON_LINE, 79 | "dataset": "srtm", 80 | }, 81 | "pelias_search": { 82 | "text": "Heidelberg", 83 | "focus_point": PARAM_POINT, 84 | "rect_min_x": PARAM_INT_BIG, 85 | "rect_min_y": PARAM_INT_BIG, 86 | "rect_max_x": PARAM_INT_BIG + 1, 87 | "rect_max_y": PARAM_INT_BIG + 1, 88 | "circle_point": PARAM_POINT, 89 | "circle_radius": PARAM_INT_SMALL, 90 | "sources": ["osm", "wof", "gn"], 91 | "layers": ["locality", "county", "region"], 92 | "country": "de", 93 | "size": PARAM_INT_SMALL, 94 | }, 95 | "pelias_autocomplete": { 96 | "text": "Heidelberg", 97 | "focus_point": PARAM_POINT, 98 | "rect_min_x": PARAM_INT_BIG, 99 | "rect_min_y": PARAM_INT_BIG, 100 | "rect_max_x": PARAM_INT_BIG, 101 | "rect_max_y": PARAM_INT_BIG, 102 | "sources": ["osm", "wof", "gn"], 103 | "layers": ["locality", "county", "region"], 104 | "country": "de", 105 | }, 106 | "pelias_structured": { 107 | "address": "Berliner Straße 45", 108 | "neighbourhood": "Neuenheimer Feld", 109 | "borough": "Heidelberg", 110 | "locality": "Heidelberg", 111 | "county": "Rhein-Neckar-Kreis", 112 | "region": "Baden-Württemberg", 113 | "postalcode": 69120, 114 | "country": "de", 115 | }, 116 | "pelias_reverse": { 117 | "point": PARAM_POINT, 118 | "circle_radius": PARAM_INT_SMALL, 119 | "sources": ["osm", "wof", "gn"], 120 | "layers": ["locality", "county", "region"], 121 | "country": "de", 122 | "size": PARAM_INT_SMALL, 123 | }, 124 | "pois": { 125 | "request": "pois", 126 | "geojson": PARAM_GEOJSON_POINT, 127 | "bbox": PARAM_LINE, 128 | "buffer": PARAM_INT_SMALL, 129 | "filter_category_ids": [PARAM_INT_SMALL], 130 | "filter_category_group_ids": [PARAM_INT_BIG], 131 | "filters_custom": { 132 | "name": "Deli", 133 | "wheelchair": ["yes", "limited"], 134 | "smoking": ["dedicated", "separated"], 135 | "fee": ["yes", "no"], 136 | }, 137 | "limit": PARAM_INT_SMALL, 138 | "sortby": "distance", 139 | }, 140 | "optimization": { 141 | "shipments": [ 142 | { 143 | "pickup": { 144 | "id": 0, 145 | "location": [8.688641, 49.420577], 146 | "location_index": 0, 147 | "service": 500, 148 | "time_windows": [[50, 50]], 149 | }, 150 | "delivery": { 151 | "id": 0, 152 | "location": [8.688641, 49.420577], 153 | "location_index": 0, 154 | "service": 500, 155 | "time_windows": [[50, 50]], 156 | }, 157 | "amount": [50], 158 | "skills": [50, 50], 159 | "priority": 50, 160 | }, 161 | { 162 | "pickup": { 163 | "id": 1, 164 | "location": [8.680916, 49.415776], 165 | "location_index": 1, 166 | "service": 500, 167 | "time_windows": [[50, 50]], 168 | }, 169 | "delivery": { 170 | "id": 1, 171 | "location": [8.680916, 49.415776], 172 | "location_index": 1, 173 | "service": 500, 174 | "time_windows": [[50, 50]], 175 | }, 176 | "amount": [50], 177 | "skills": [50, 50], 178 | "priority": 50, 179 | }, 180 | ], 181 | "jobs": [ 182 | { 183 | "id": 0, 184 | "location": PARAM_LINE[0], 185 | "location_index": 0, 186 | "service": PARAM_INT_BIG, 187 | "amount": [PARAM_INT_SMALL], 188 | "skills": PARAM_LIST_ONE, 189 | "priority": PARAM_INT_SMALL, 190 | "time_windows": [PARAM_LIST_ONE], 191 | }, 192 | { 193 | "id": 1, 194 | "location": PARAM_LINE[1], 195 | "location_index": 1, 196 | "service": PARAM_INT_BIG, 197 | "amount": [PARAM_INT_SMALL], 198 | "skills": PARAM_LIST_ONE, 199 | "priority": PARAM_INT_SMALL, 200 | "time_windows": [PARAM_LIST_ONE], 201 | }, 202 | ], 203 | "vehicles": [ 204 | { 205 | "id": 0, 206 | "profile": "driving-car", 207 | "start": PARAM_LINE[0], 208 | "start_index": 0, 209 | "end_index": 0, 210 | "end": PARAM_LINE[0], 211 | "capacity": [PARAM_INT_SMALL], 212 | "skills": PARAM_LIST_ONE, 213 | "time_window": PARAM_LIST_ONE, 214 | }, 215 | { 216 | "id": 1, 217 | "profile": "driving-car", 218 | "start": PARAM_LINE[1], 219 | "start_index": 1, 220 | "end_index": 1, 221 | "end": PARAM_LINE[1], 222 | "capacity": [PARAM_INT_SMALL], 223 | "skills": PARAM_LIST_ONE, 224 | "time_window": PARAM_LIST_ONE, 225 | }, 226 | ], 227 | "options": {"g": False}, 228 | "matrix": PARAM_LIST_TWO, 229 | }, 230 | } 231 | 232 | GPX_RESPONSE = """ 233 | 234 | 235 | 236 | openrouteservice directions 237 | This is a directions instructions file as GPX, generated from openrouteservice 238 | openrouteservice 239 | 240 | 241 | https://openrouteservice.org/ 242 | text/html 243 | 244 | 245 | 246 | 2021 247 | LGPL 3.0 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | """ 257 | -------------------------------------------------------------------------------- /test/test_isochrones.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright (C) 2018 HeiGIT, University of Heidelberg. 3 | # 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 6 | # use this file except in compliance with the License. You may obtain a copy of 7 | # the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations under 15 | # the License. 16 | # 17 | """Tests for the distance matrix module.""" 18 | import responses 19 | import test as _test 20 | from test.test_helper import ENDPOINT_DICT 21 | import pytest 22 | 23 | 24 | class IsochronesTest(_test.TestCase): 25 | @responses.activate 26 | def test_isochrones(self): 27 | query = ENDPOINT_DICT["isochrones"] 28 | 29 | responses.add( 30 | responses.POST, 31 | "https://api.openrouteservice.org/v2/isochrones/{}/geojson".format( 32 | query["profile"] 33 | ), 34 | json=query, 35 | status=200, 36 | content_type="application/json", 37 | ) 38 | 39 | resp = self.client.isochrones(**query) 40 | 41 | self.assertEqual(1, len(responses.calls)) 42 | self.assertEqual(resp, query) 43 | 44 | def test_isochrones_must_fail(self): 45 | query = ENDPOINT_DICT["isochrones"] 46 | query.update({"foo": {"bar": "baz"}}) 47 | self.assertRaises(TypeError, self.client.isochrones, **query) 48 | -------------------------------------------------------------------------------- /test/test_optimization.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2014 Google Inc. All rights reserved. 3 | # 4 | # Modifications Copyright (C) 2018 HeiGIT, University of Heidelberg. 5 | # 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 8 | # use this file except in compliance with the License. You may obtain a copy of 9 | # the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16 | # License for the specific language governing permissions and limitations under 17 | # the License. 18 | # 19 | """Tests for the distance matrix module.""" 20 | import responses 21 | import test as _test 22 | from copy import deepcopy 23 | import json 24 | 25 | from test.test_helper import * 26 | from openrouteservice.optimization import Job, Vehicle, ShipmentStep, Shipment 27 | 28 | 29 | class OptimizationTest(_test.TestCase): 30 | def _get_params(self): 31 | jobs, vehicles, shipments = list(), list(), list() 32 | 33 | for idx, coord in enumerate(PARAM_LINE): 34 | jobs.append( 35 | Job( 36 | idx, 37 | location=coord, 38 | service=PARAM_INT_BIG, 39 | location_index=idx, 40 | amount=[PARAM_INT_SMALL], 41 | skills=PARAM_LIST_ONE, 42 | priority=PARAM_INT_SMALL, 43 | time_windows=[PARAM_LIST_ONE], 44 | ) 45 | ) 46 | 47 | vehicles.append( 48 | Vehicle( 49 | idx, 50 | profile="driving-car", 51 | start=coord, 52 | start_index=idx, 53 | end=coord, 54 | end_index=idx, 55 | capacity=[PARAM_INT_SMALL], 56 | skills=PARAM_LIST_ONE, 57 | time_window=PARAM_LIST_ONE, 58 | ) 59 | ) 60 | 61 | shipments.append( 62 | Shipment( 63 | pickup=ShipmentStep( 64 | idx, 65 | location=coord, 66 | location_index=idx, 67 | service=PARAM_INT_BIG, 68 | time_windows=[PARAM_LIST_ONE], 69 | ), 70 | delivery=ShipmentStep( 71 | idx, 72 | location=coord, 73 | location_index=idx, 74 | service=PARAM_INT_BIG, 75 | time_windows=[PARAM_LIST_ONE], 76 | ), 77 | amount=[PARAM_INT_SMALL], 78 | skills=PARAM_LIST_ONE, 79 | priority=PARAM_INT_SMALL, 80 | ) 81 | ) 82 | 83 | return jobs, vehicles, shipments 84 | 85 | def test_jobs_vehicles_classes(self): 86 | jobs, vehicles, shipments = self._get_params() 87 | 88 | self.assertEqual( 89 | ENDPOINT_DICT["optimization"]["jobs"], [j.__dict__ for j in jobs] 90 | ) 91 | self.assertEqual( 92 | ENDPOINT_DICT["optimization"]["vehicles"], 93 | [v.__dict__ for v in vehicles], 94 | ) 95 | 96 | @responses.activate 97 | def test_full_optimization(self): 98 | query = deepcopy(ENDPOINT_DICT["optimization"]) 99 | 100 | jobs, vehicles, shipments = self._get_params() 101 | 102 | responses.add( 103 | responses.POST, 104 | "https://api.openrouteservice.org/optimization", 105 | json={}, 106 | status=200, 107 | content_type="application/json", 108 | ) 109 | 110 | self.client.optimization( 111 | jobs, vehicles, shipments, geometry=False, matrix=PARAM_LIST_TWO 112 | ) 113 | 114 | self.assertEqual( 115 | query, json.loads(responses.calls[0].request.body.decode("utf-8")) 116 | ) 117 | -------------------------------------------------------------------------------- /test/test_places.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2014 Google Inc. All rights reserved. 3 | # 4 | # Modifications Copyright (C) 2018 HeiGIT, University of Heidelberg. 5 | # 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 8 | # use this file except in compliance with the License. You may obtain a copy of 9 | # the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16 | # License for the specific language governing permissions and limitations under 17 | # the License. 18 | # 19 | """Tests for the distance matrix module.""" 20 | import responses 21 | import test as _test 22 | 23 | from test.test_helper import ENDPOINT_DICT 24 | 25 | 26 | class PlacesTest(_test.TestCase): 27 | @responses.activate 28 | def test_pois(self): 29 | query = ENDPOINT_DICT["pois"] 30 | responses.add( 31 | responses.POST, 32 | "https://api.openrouteservice.org/pois", 33 | json=query, 34 | status=200, 35 | content_type="application/json", 36 | ) 37 | 38 | resp = self.client.places(**query) 39 | 40 | self.assertEqual(len(responses.calls), 1) 41 | self.assertEqual(resp, query) 42 | --------------------------------------------------------------------------------