├── .gitattributes ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml ├── release.yml └── workflows │ ├── build_docs.yml │ ├── release_and_publish.yml │ └── testing.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── ci ├── 310-latest.yaml ├── 310-oldest.yaml ├── 311-latest.yaml ├── 312-dev.yaml └── 312-latest.yaml ├── codecov.yml ├── docs ├── .buildinfo ├── .nojekyll ├── Makefile ├── _static │ ├── images │ │ ├── ardc_logo.png │ │ ├── crime_counts.png │ │ ├── facility_location.png │ │ ├── mst_logo_pasta.png │ │ ├── net_rep.png │ │ ├── network_k.png │ │ ├── nsf_logo.png │ │ ├── pysal_favicon.ico │ │ ├── pysal_logo.png │ │ ├── pysal_logo.svg │ │ ├── pysal_nav_logo_2line.svg │ │ └── spaghetti_nav_logo.svg │ ├── pysal-styles.css │ ├── pysal_favicon.ico │ └── references.bib ├── api.rst ├── conf.py ├── index.rst ├── installation.rst ├── references.rst └── tutorials.rst ├── environment.yml ├── notebooks ├── caveats.ipynb ├── connected-components.ipynb ├── facility-location.ipynb ├── network-segmentation.ipynb ├── network-spatial-autocorrelation.ipynb ├── network-spatial-dependence.ipynb ├── network-spatial-weights.ipynb ├── pointpattern-attributes.ipynb ├── quickstart.ipynb ├── shortest-path-visualization.ipynb ├── spanning-trees.ipynb ├── transportation-problem.ipynb └── tsp.ipynb ├── paper ├── figs │ ├── spaghetti_network.png │ └── spaghetti_pointpattern_moran.png ├── paper.bib └── paper.md ├── pyproject.toml └── spaghetti ├── __init__.py ├── analysis.py ├── network.py ├── tests ├── __init__.py └── test_network.py └── util.py /.gitattributes: -------------------------------------------------------------------------------- 1 | *.ipynb linguist-language=Python 2 | spaghetti/_version.py export-subst -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines for `spaghetti` 2 | 3 | Thank you for your interest in contributing! We work primarily on Github. Please review the contributing procedures [here](http://pysal.org/getting_started#for-developers) and [here](https://github.com/pysal/pysal/wiki/GitHub-Standard-Operating-Procedures) so that we can accept your contributions! Alternatively, contact someone in the [development chat channel](https://gitter.im//pysal/Spaghetti). 4 | 5 | 6 | ## Contact 7 | 8 | 1. All questions, comments, & discussions should happen in a public forum, where possible. Please start a [discussion](https://github.com/pysal/spaghetti/discussions) for questions or open an [issue](https://github.com/pysal/spaghetti/issues) if there appears to be a bug. Private messages and emails will not be answered in a substantive manner. 9 | 10 | 11 | ## Style and format 12 | 13 | 1. At the time of this writing, Python 3.10, 3.11, and 3.12 are the officially supported versions. 14 | 2. This project implements the linting and formatting conventions of [`ruff`](https://docs.astral.sh/ruff/) on all incoming Pull Requests. To ensure a PR is properly linted and formatted prior to creating a Pull Request, [install `pre-commit`](https://pre-commit.com/#installation) in your development environment and then [set up the configuration of pre-commit hooks](https://pre-commit.com/#3-install-the-git-hook-scripts). 15 | 3. Import packages, classes, and functions with their full name where possible. 16 | * For example: 17 | 18 | :white_check_mark: 19 | ```python 20 | import spaghetti 21 | from shapely.geometry import Point 22 | ``` 23 | :x: 24 | ```python 25 | import spaghetti as spgh 26 | from shapely.geometry import Point as pt 27 | ``` 28 | 29 | 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thank you for filing this issue! To help troubleshoot this issue, please follow 2 | the following directions to the best of your ability before submitting an issue. 3 | Feel free to delete this text once you've filled out the relevant requests. 4 | 5 | Please include the output of the following in your issue submission. If you don't know how to provide the information, commands to get the relevant information from the Python interpreter will follow each bullet point. 6 | 7 | Feel free to delete the commands after you've filled out each bullet. 8 | 9 | 10 | - Platform information: 11 | ```python 12 | >>> import os; print(os.name, os.sys.platform);print(os.uname()) 13 | ``` 14 | - Python version: 15 | ```python 16 | >>> import sys; print(sys.version) 17 | ``` 18 | - spaghetti version: 19 | ```python 20 | >>> import spaghetti; print(spaghetti.__version__) 21 | ``` 22 | - libpysal version: 23 | ```python 24 | >>> import libpysal; print(libpysal.__version__) 25 | ``` 26 | - SciPy version: 27 | ```python 28 | >>> import scipy; print(scipy.__version__) 29 | ``` 30 | - NumPy version: 31 | ```python 32 | >>> import numpy; print(numpy.__version__) 33 | ``` 34 | - GeoPandas verion (if applicable): 35 | ```python 36 | >>> import geopandas; print(geopandas.__version__) 37 | ``` 38 | 39 | Also, please upload any relevant data as [a file 40 | attachment](https://help.github.com/articles/file-attachments-on-issues-and-pull-requests/). Please **do not** upload pickled objects, since it's nearly impossible to troubleshoot them without replicating your exact namespace. Instead, provide the minimal subset of the data required to replicate the problem. If it makes you more comfortable submitting the issue, feel free to: 41 | 42 | 1. remove personally identifying information from data or code 43 | 2. provide only the required subset of the full data or code 44 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: jGaboardi 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Screenshots** 20 | If applicable, add screenshots to help explain your problem. 21 | 22 | **Additional context** 23 | Add any other context about the problem here. 24 | 25 | **System & Versions** 26 | - Platform information: 27 | ```python 28 | >>> import os; print(os.name, os.sys.platform);print(os.uname()) 29 | ``` 30 | - Python version: 31 | ```python 32 | >>> import sys; print(sys.version) 33 | ``` 34 | - spaghetti version: 35 | ```python 36 | >>> import spaghetti; print(spaghetti.__version__) 37 | ``` 38 | - libpysal version: 39 | ```python 40 | >>> import libpysal; print(libpysal.__version__) 41 | ``` 42 | - SciPy version: 43 | ```python 44 | >>> import scipy; print(scipy.__version__) 45 | ``` 46 | - NumPy version: 47 | ```python 48 | >>> import numpy; print(numpy.__version__) 49 | ``` 50 | - GeoPandas verion (if applicable): 51 | ```python 52 | >>> import geopandas; print(geopandas.__version__) 53 | ``` 54 | 55 | Also, please upload any relevant data as [a file 56 | attachment](https://help.github.com/articles/file-attachments-on-issues-and-pull-requests/). Please **do not** upload pickled objects, since it's nearly impossible to troubleshoot them without replicating your exact namespace. Instead, provide the minimal subset of the data required to replicate the problem. If it makes you more comfortable submitting the issue, feel free to: 57 | 58 | 1. remove personally identifying information from data or code 59 | 2. provide only the required subset of the full data or code 60 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: jGaboardi 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Hello! Please make sure to check all these boxes before submitting a Pull Request 2 | (PR). Once you have checked the boxes, feel free to remove all text except the 3 | justification in point 4. 4 | 5 | 1. [ ] You have run tests on this submission, either by using [Travis Continuous Integration testing](https://github.com/pysal/pysal/wiki/GitHub-Standard-Operating-Procedures#automated-testing-w-travis-ci) testing or running `nosetests` on your changes? 6 | 2. [ ] This pull introduces new functionality covered by 7 | [docstrings](https://en.wikipedia.org/wiki/Docstring#Python) and 8 | [unittests](https://docs.python.org/3.6/library/unittest.html)? 9 | 3. [ ] You have [assigned a 10 | reviewer](https://help.github.com/articles/assigning-issues-and-pull-requests-to-other-github-users/) and added relevant [labels](https://help.github.com/articles/applying-labels-to-issues-and-pull-requests/) 11 | 4. [ ] The justification for this PR is: 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | reviewers: 13 | - "jGaboardi" 14 | 15 | - package-ecosystem: "pip" 16 | directory: "/" 17 | schedule: 18 | interval: "daily" 19 | reviewers: 20 | - "jGaboardi" 21 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | labels: 4 | - ignore-for-release 5 | authors: 6 | - octocat 7 | - dependabot 8 | categories: 9 | - title: Breaking Changes 🛠 10 | labels: 11 | - Semver-Major 12 | - breaking-change 13 | - title: Exciting New Features 🎉 14 | labels: 15 | - Semver-Minor 16 | - enhancement 17 | - title: Other Changes 18 | labels: 19 | - "*" 20 | -------------------------------------------------------------------------------- /.github/workflows/build_docs.yml: -------------------------------------------------------------------------------- 1 | name: Build Docs 2 | 3 | on: 4 | push: 5 | # Sequence of patterns matched against refs/tags 6 | tags: 7 | - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 8 | workflow_dispatch: 9 | inputs: 10 | version: 11 | description: Manual Doc Build 12 | default: test 13 | required: false 14 | jobs: 15 | docs: 16 | name: build & push docs 17 | runs-on: ${{ matrix.os }} 18 | timeout-minutes: 90 19 | strategy: 20 | matrix: 21 | os: ['ubuntu-latest'] 22 | environment-file: [ci/312-latest.yaml] 23 | experimental: [false] 24 | defaults: 25 | run: 26 | shell: bash -l {0} 27 | 28 | steps: 29 | - name: checkout repo 30 | uses: actions/checkout@v4 31 | with: 32 | fetch-depth: 0 # Fetch all history for all branches and tags. 33 | 34 | - name: setup micromamba 35 | uses: mamba-org/setup-micromamba@v2 36 | with: 37 | environment-file: ${{ matrix.environment-file }} 38 | micromamba-version: 'latest' 39 | 40 | - name: reinstall for version 41 | run: | 42 | pip install -e . --no-deps --force-reinstall 43 | 44 | - name: make docs 45 | run: | 46 | cd docs 47 | make html 48 | 49 | - name: commit docs 50 | run: | 51 | git clone https://github.com/ammaraskar/sphinx-action-test.git --branch gh-pages --single-branch gh-pages 52 | cp -r docs/_build/html/* gh-pages/ 53 | cd gh-pages 54 | git config --local user.email "action@github.com" 55 | git config --local user.name "GitHub Action" 56 | git add . 57 | git commit -m "Update documentation" -a || true 58 | # The above command will fail if no changes were present, 59 | # so we ignore the return code. 60 | 61 | - name: push to gh-pages 62 | uses: ad-m/github-push-action@master 63 | with: 64 | branch: gh-pages 65 | directory: gh-pages 66 | github_token: ${{ secrets.GITHUB_TOKEN }} 67 | force: true 68 | -------------------------------------------------------------------------------- /.github/workflows/release_and_publish.yml: -------------------------------------------------------------------------------- 1 | # Release package on GitHub and publish to PyPI 2 | # IMPORTANT -- 1 MANUAL STEP 3 | # * FOLLOWING TAGGED RELEASE 4 | # - update CHANGELOG.md 5 | #-------------------------------------------------- 6 | name: Release & Publish 7 | 8 | on: 9 | push: 10 | # Sequence of patterns matched against refs/tags 11 | tags: 12 | - "v*" # Push events to matching v*, i.e. v1.0, v20.15.10 13 | workflow_dispatch: 14 | inputs: 15 | version: 16 | description: Manual Release 17 | default: test 18 | required: false 19 | 20 | 21 | jobs: 22 | build: 23 | name: Create release & publish to PyPI 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: Checkout repo 27 | uses: actions/checkout@v4 28 | with: 29 | fetch-depth: 0 # Fetch all history for all branches and tags. 30 | 31 | - name: Set up python 32 | uses: actions/setup-python@v5 33 | with: 34 | python-version: "3.x" 35 | 36 | - name: Install Dependencies 37 | run: | 38 | python -m pip install --upgrade pip 39 | python -m pip install --upgrade build twine 40 | python -m build 41 | twine check --strict dist/* 42 | 43 | - name: Create Release Notes 44 | uses: actions/github-script@v7 45 | with: 46 | github-token: ${{secrets.GITHUB_TOKEN}} 47 | script: | 48 | await github.request(`POST /repos/${{ github.repository }}/releases`, { 49 | tag_name: "${{ github.ref }}", 50 | generate_release_notes: true 51 | }); 52 | 53 | - name: Publish distribution 📦 to PyPI 54 | uses: pypa/gh-action-pypi-publish@release/v1 55 | with: 56 | user: __token__ 57 | password: ${{ secrets.PYPI_PASSWORD }} 58 | -------------------------------------------------------------------------------- /.github/workflows/testing.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | pull_request: 8 | branches: 9 | - '*' 10 | schedule: 11 | - cron: '59 23 * * *' 12 | workflow_dispatch: 13 | inputs: 14 | version: 15 | description: Manual Unittest Run 16 | default: test 17 | required: false 18 | 19 | jobs: 20 | testing: 21 | name: ${{ matrix.os }}, ${{ matrix.environment-file }} 22 | runs-on: ${{ matrix.os }} 23 | timeout-minutes: 30 24 | strategy: 25 | matrix: 26 | os: [ubuntu-latest] 27 | environment-file: 28 | - ci/310-oldest.yaml 29 | - ci/310-latest.yaml 30 | - ci/311-latest.yaml 31 | - ci/312-latest.yaml 32 | - ci/312-dev.yaml 33 | include: 34 | - environment-file: ci/312-latest.yaml 35 | os: macos-13 # Intel 36 | - environment-file: ci/312-latest.yaml 37 | os: macos-14 # Apple Silicon 38 | - environment-file: ci/312-latest.yaml 39 | os: windows-latest 40 | fail-fast: false 41 | 42 | defaults: 43 | run: 44 | shell: bash -l {0} 45 | 46 | steps: 47 | - name: checkout repo 48 | uses: actions/checkout@v4 49 | with: 50 | fetch-depth: 0 # Fetch all history for all branches and tags. 51 | 52 | - name: setup micromamba 53 | uses: mamba-org/setup-micromamba@v2 54 | with: 55 | environment-file: ${{ matrix.environment-file }} 56 | micromamba-version: 'latest' 57 | 58 | - name: environment info 59 | run: | 60 | micromamba info 61 | micromamba list 62 | 63 | - name: spatial versions 64 | run: 'python -c "import geopandas; geopandas.show_versions();"' 65 | 66 | - name: run tests 67 | run: | 68 | pytest \ 69 | spaghetti \ 70 | -v \ 71 | -r a \ 72 | -n auto \ 73 | --color yes \ 74 | --cov spaghetti \ 75 | --cov-append \ 76 | --cov-report term-missing \ 77 | --cov-report xml \ 78 | --timeout 60 79 | 80 | - name: run docstring tests 81 | if: contains(matrix.environment-file, 'latest') || contains(matrix.environment-file, 'dev') 82 | run: | 83 | pytest \ 84 | -v \ 85 | -r a \ 86 | -n auto \ 87 | --color yes \ 88 | --doctest-only spaghetti 89 | 90 | - name: codecov 91 | uses: codecov/codecov-action@v5 92 | with: 93 | token: ${{ secrets.CODECOV_TOKEN }} 94 | file: ./coverage.xml 95 | name: spaghetti-codecov 96 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.pyc 3 | .rope* 4 | .idea/ 5 | *.ipynb 6 | notebooks/.ipynb_checkpoints/ 7 | .ipynb_checkpoints/ 8 | .DS_Store 9 | 10 | *.egg 11 | *.egg-info 12 | dist 13 | build 14 | eggs 15 | parts 16 | bin 17 | var 18 | sdist 19 | develop-eggs 20 | .installed.cfg 21 | lib 22 | lib64 23 | __pycache__ 24 | 25 | .log 26 | 27 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | files: "spaghetti\/" 2 | repos: 3 | - repo: https://github.com/astral-sh/ruff-pre-commit 4 | rev: "v0.8.6" 5 | hooks: 6 | - id: ruff 7 | - id: ruff-format 8 | 9 | ci: 10 | autofix_prs: false 11 | autoupdate_schedule: quarterly 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # Changes 3 | 4 | ## Version 1.5.0 (2020-05-04) 5 | 6 | We closed a total of 9 issues (enhancements and bug fixes) through 3 pull requests, since our last release on 2020-05-03. 7 | 8 | ## Highlights 9 | * New logo! 10 | * Minimum/maximum spanning trees 11 | * Refactored *K* Function 12 | * More notebooks and tutorials 13 | * Further improved and refactored testing 14 | 15 | ## Issues Closed 16 | - GHA for release and publish (#487) 17 | - Attempting GHA release workflow #2 (#489) 18 | - Attempting GHA release workflow (#488) 19 | - Version bump for release v1.5.0.rc0 (#486) 20 | - [WIP] Update Network K Function (#469) 21 | - Break spatial analysis tutorial into several notebooks (#478) 22 | 23 | ## Pull Requests 24 | - Attempting GHA release workflow #2 (#489) 25 | - Attempting GHA release workflow (#488) 26 | - Version bump for release v1.5.0.rc0 (#486) 27 | 28 | The following individuals contributed to this release: 29 | 30 | - James Gaboardi 31 | 32 | ------------------------------------ 33 | 34 | ## Version 1.5.0.rc0 (2020-05-02) 35 | 36 | We closed a total of 100 issues (enhancements and bug fixes) through 38 pull requests, since our last release on 2020-02-24. 37 | 38 | ## Issues Closed 39 | - [WIP] Update Network K Function (#469) 40 | - Break spatial analysis tutorial into several notebooks (#478) 41 | - Update, review, and rename K statistic (#477) 42 | - toy PR to test codecov.yml (#485) 43 | - toy PR to test tcodecov.yml (#484) 44 | - treebeard CI (#481) 45 | - Docs funding (#483) 46 | - [WIP] Dropping "change" param in codecov.yml (#479) 47 | - Add funding source? (#480) 48 | - add funding sources to README (#482) 49 | - [BUG] Correction to NetworkK formulation (#470) 50 | - Revert "[Bug] Addressing bug in K-Function formation" (#476) 51 | - [Bug] Addressing bug in K-Function formation (#471) 52 | - [TST] add codecov.yml (#472) 53 | - no post until 24 codecov reports (#475) 54 | - attempting custom codecov reports (#474) 55 | - update install docs (#473) 56 | - Network L Function (#467) 57 | - Remove G and F functions (#466) 58 | - Removing G and F functions (#468) 59 | - [TEST] regular 3x3 lattice for network analysis testing (#442) 60 | - DOC: correct comments for K-function related code (#465) 61 | - Updating docs with new sphinx release (#464) 62 | - Remedy for non-transparent favicon (#463) 63 | - stylize the minimum spanning tree plot in README.md (#462) 64 | - Updating pinned requirements versions (#461) 65 | - Streamling GHA (#460) 66 | - [ENH] Functionality for spanning trees (#449) 67 | - [ENH][WIP] Min/Max Spanning Trees functionality (#459) 68 | - Update Python version support in CONTRIBUTING.md (#458) 69 | - Adding network arc spatial weights notebook (#457) 70 | - Appveyor still being triggered (#454) 71 | - Corrections for Caveat notebook (#456) 72 | - CHANGELOG.txt --> CHANGELOG.md (#455) 73 | - [TST] GitHub Actions only; Migrate away from Travis/Appveyor (#453) 74 | - Customizing/improving GitHub Actions (#452) 75 | - Trying out GitHub Actions for additional testing (#451) 76 | - [ENH] caveats notebook (#426) 77 | - [WIP][ENH] New notebook for caveats (#445) 78 | - release v1.4.3 (#404) 79 | - logo update, rearrange (#448) 80 | - updating README logo (#447) 81 | - package import warning? (#427) 82 | - Improved docstrings for NetworkF and NetworkG (#444) 83 | - analysis.ffunction() may not be correct (#225) 84 | - [MAINT] re-evaluate testing structure (#438) 85 | - [TST] Updating tests and testing structure (#443) 86 | - moving watermark to notebook reqs (#441) 87 | - appveyor — rebuild the master 3.8 PYPI (#436) 88 | - [MAINT] DRYing off unittests (#439) 89 | - Docs for v1.4.2.post1 were not rebuilt (#434) 90 | - rebuilding docs for post release (#435) 91 | - release v1.4.2 (#392) 92 | - pushing changelog for 1.4.2post1 (#433) 93 | - version bump v1.4.2 --> 1.4.2post1 (#432) 94 | - fixing conda-forge failure (#431) 95 | - final PR before v1.4.2 release — updating CHANGELOG.md (#430) 96 | - updating README (#429) 97 | - [WIP][ENH] longest and largest network components (#424) 98 | - Add a fully connected attribute (#425) 99 | - [ENH] longest/largest connected component (#414) 100 | - add logos to website (#345) 101 | 102 | ## Pull Requests 103 | - Update, review, and rename K statistic (#477) 104 | - toy PR to test codecov.yml (#485) 105 | - Docs funding (#483) 106 | - [WIP] Dropping "change" param in codecov.yml (#479) 107 | - add funding sources to README (#482) 108 | - [Bug] Addressing bug in K-Function formation (#471) 109 | - no post until 24 codecov reports (#475) 110 | - attempting custom codecov reports (#474) 111 | - update install docs (#473) 112 | - Removing G and F functions (#468) 113 | - DOC: correct comments for K-function related code (#465) 114 | - Updating docs with new sphinx release (#464) 115 | - Remedy for non-transparent favicon (#463) 116 | - stylize the minimum spanning tree plot in README.md (#462) 117 | - Updating pinned requirements versions (#461) 118 | - Streamling GHA (#460) 119 | - [ENH][WIP] Min/Max Spanning Trees functionality (#459) 120 | - Update Python version support in CONTRIBUTING.md (#458) 121 | - Adding network arc spatial weights notebook (#457) 122 | - Corrections for Caveat notebook (#456) 123 | - CHANGELOG.txt --> CHANGELOG.md (#455) 124 | - [TST] GitHub Actions only; Migrate away from Travis/Appveyor (#453) 125 | - Customizing/improving GitHub Actions (#452) 126 | - Trying out GitHub Actions for additional testing (#451) 127 | - [WIP][ENH] New notebook for caveats (#445) 128 | - logo update, rearrange (#448) 129 | - updating README logo (#447) 130 | - Improved docstrings for NetworkF and NetworkG (#444) 131 | - [TST] Updating tests and testing structure (#443) 132 | - moving watermark to notebook reqs (#441) 133 | - [MAINT] DRYing off unittests (#439) 134 | - rebuilding docs for post release (#435) 135 | - pushing changelog for 1.4.2post1 (#433) 136 | - version bump v1.4.2 --> 1.4.2post1 (#432) 137 | - fixing conda-forge failure (#431) 138 | - final PR before v1.4.2 release — updating CHANGELOG.md (#430) 139 | - updating README (#429) 140 | - [WIP][ENH] longest and largest network components (#424) 141 | 142 | The following individuals contributed to this release: 143 | 144 | - James Gaboardi 145 | - Serge Rey 146 | 147 | ------------------------------------ 148 | 149 | 150 | ## Version 1.4.2.post2 (2020-02-24) 151 | 152 | We closed a total of 1 issues (enhancements and bug fixes) through 1 pull requests, since our last release on 2020-02-24. 153 | 154 | ## Issues Closed 155 | - rebuilding docs for post release (#435) 156 | 157 | ## Pull Requests 158 | - Docs for v1.4.2.post1 were not rebuilt (#434) 159 | 160 | The following individuals contributed to this release: 161 | 162 | - James Gaboardi 163 | 164 | ------------------------------------ 165 | 166 | 167 | ## Version 1.4.2post1 (2020-02-24) 168 | 169 | We closed a total of 0 issues (enhancements and bug fixes) through 2 pull requests, since our last release on 2020-02-24. 170 | 171 | ## Pull Requests 172 | - version bump v1.4.2 --> 1.4.2post1 (#432) 173 | - fixing conda-forge failure (#431) 174 | 175 | The following individuals contributed to this release: 176 | 177 | - James Gaboardi 178 | 179 | ------------------------------------ 180 | 181 | 182 | ## Version 1.4.2 (2020-02-24) 183 | 184 | We closed a total of 57 issues (enhancements and bug fixes) through 18 pull requests, since our last release on 2020-01-25. 185 | 186 | ## Highlights 187 | * Python 3.8 support and Windows OS testing 188 | * Increased robustness of `regular_lattices()`, which now can create a lattice from any bounds, including bounding box of a shapefile or GeoDataFrame 189 | * Point type A to point type B shortest path extraction 190 | * Longest and largest network component identification 191 | * Network component extraction 192 | * New "Integrations and Applications" notebook 193 | * General cleanup, including bug and typo fixes 194 | 195 | 196 | ## Issues Closed 197 | - updating README (#429) 198 | - [WIP][ENH] longest and largest network components (#424) 199 | - Add a fully connected attribute (#425) 200 | - [ENH] longest/largest connected component (#414) 201 | - add logos to website (#345) 202 | - attempting fast finish again (#428) 203 | - pushing fix for environment.yml (#423) 204 | - Add 3.8 as a supported Python version in docs (#422) 205 | - [ENH] matplotlib-scalebar for notebooks/binders (#416) 206 | - using a true scalebar in notebook plots (#421) 207 | - [REQ] requirements for notebooks/binders? (#413) 208 | - adding notebook reqs files (#420) 209 | - support multiplatform testing (#407) 210 | - make sure data set is downloaded for appveyor.yml (#419) 211 | - Migrate testing functionality from nose to pytest (#417) 212 | - [ENH] moving towards multi-platform testing (#418) 213 | - [On Hold] too many emails from Travis CI (#322) 214 | - origin-destination point patterns for shortest_paths() (#412) 215 | - The Transportation Problem notebook (#103) 216 | - transportation problem ipynb and type-a to type-b shortest paths (#415) 217 | - [WIP][ENH] origin for lattice (#405) 218 | - add testing for Python 3.8 (#408) 219 | - [BUG] typo in Travis CI badge URL (#410) 220 | - silently hands RuntimeWarning in NetworkK (#411) 221 | - adding py3.8 to travis (#409) 222 | - [DOC] notebook doc pngs (#406) 223 | - release v1.4.1 (#365) 224 | - rebuilding docs for version bump (#402) 225 | - Version bump to v1.4.1 (#401) 226 | - Binder is broken for network-analysis (#399) 227 | - Some tutorial links giving 404 (#400) 228 | - Fix links 2 (#398) 229 | - Fix links for notebooks (#397) 230 | - [DOC] Reorg for tutorials page (#396) 231 | - Traveling Salesperson Notebook (#110) 232 | - create new TSP notebook with pulp (#100) 233 | - [WIP] TSP notebook (#386) 234 | - [DOC] paths docstring missing (#394) 235 | - correcting routes doc (#395) 236 | 237 | ## Pull Requests 238 | - final PR before v1.4.2 release — updating CHANGELOG.md (#430) 239 | - updating README (#429) 240 | - [WIP][ENH] longest and largest network components (#424) 241 | - pushing fix for environment.yml (#423) 242 | - Add 3.8 as a supported Python version in docs (#422) 243 | - using a true scalebar in notebook plots (#421) 244 | - adding notebook reqs files (#420) 245 | - make sure data set is downloaded for appveyor.yml (#419) 246 | - [ENH] moving towards multi-platform testing (#418) 247 | - transportation problem ipynb and type-a to type-b shortest paths (#415) 248 | - [WIP][ENH] origin for lattice (#405) 249 | - adding py3.8 to travis (#409) 250 | - [DOC] notebook doc pngs (#406) 251 | - rebuilding docs for version bump (#402) 252 | - Version bump to v1.4.1 (#401) 253 | - Fix links 2 (#398) 254 | - Fix links for notebooks (#397) 255 | - [WIP] TSP notebook (#386) 256 | - correcting routes doc (#395) 257 | 258 | The following individuals contributed to this release: 259 | 260 | - James Gaboardi 261 | - Serge Rey 262 | 263 | 264 | 265 | --------------------------------------------- 266 | 267 | ## Version 1.4.1 (2020-01-25) 268 | 269 | We closed a total of 93 issues (enhancements and bug fixes) through 35 pull requests, since our last release on 2019-12-31. 270 | 271 | ## Issues Closed 272 | - Binder is broken for network-analysis (#399) 273 | - Some tutorial links giving 404 (#400) 274 | - Fix links 2 (#398) 275 | - Fix links for notebooks (#397) 276 | - [DOC] Reorg for tutorials page (#396) 277 | - Traveling Salesperson Notebook (#110) 278 | - create new TSP notebook with pulp (#100) 279 | - [WIP] TSP notebook (#386) 280 | - [DOC] paths docstring missing (#394) 281 | - correcting routes doc (#395) 282 | - [ENH] add back `build` badge (#393) 283 | - [BUG] Stale links on pysal.org/spaghetti (#387) 284 | - Rebuild docs (#391) 285 | - force read-add conn-comp notebook (#390) 286 | - repushing missing notebooks (#389) 287 | - Stale links (#388) 288 | - [ENH] shortest-path extraction (#380) 289 | - [ENH] regular lattice generator (#385) 290 | - [WIP][ENH] addressing shortest path extract (#382) 291 | - mention the pysal/notebook project (#384) 292 | - point pattern --> network error message (#383) 293 | - [ENH] connected components demo notebook (#381) 294 | - updating README.md (#379) 295 | - data type testing (#378) 296 | - spaghetti should handle native cg.Point/Chain objects (#217) 297 | - Native geometries (#377) 298 | - adding CoC link to README.md (#376) 299 | - update CHANGELOG.md format (#375) 300 | - all reqs found in requirements*.txt necessary? (#346) 301 | - [ENH] explore overlapping "nearest point" calculation — DRY (#320) 302 | - altering reference format in docs (#372) 303 | - Clear instructions for black/pre-commit for contributing (#369) 304 | - Contrib precommit (#371) 305 | - alldistances vs. distancematrix? (#351) 306 | - distance matrix and tree storage (#370) 307 | - update readme (#367) 308 | - Adding citations for facility location notebook (#366) 309 | - v1.4 release checklist (#340) 310 | - updating README.md (#364) 311 | - add pre-commit black for PRs? (#333) 312 | - trying out the black pre-commit hook (#363) 313 | - adding descartes to environment.yml (#362) 314 | - Attempt binder3 (#361) 315 | - need readthedocs.yml? (#360) 316 | - need all tarball tests? (#359) 317 | - Attempt binder2 (#358) 318 | - using environment.yml for binder (#357) 319 | - Add blob/master/ to notebooks link (#354) 320 | - addressing #354 (#356) 321 | - Citation review (#355) 322 | - launch binder (#78) 323 | - version 1.3.1 --> 1.4 bump (#353) 324 | - updating version and docs (#352) 325 | - improve spaghetti homepage and notebooks? (#332) 326 | - Review docs for links, etc. (#338) 327 | - Spelling and default value of `n_processes` (#341) 328 | - [WIP] TSP notebook (#330) 329 | - [ENH][WIP]extension of #339: Rahul799 docs/bugfix (#342) 330 | 331 | ## Pull Requests 332 | - Fix links 2 (#398) 333 | - Fix links for notebooks (#397) 334 | - [WIP] TSP notebook (#386) 335 | - correcting routes doc (#395) 336 | - [ENH] add back `build` badge (#393) 337 | - Rebuild docs (#391) 338 | - force read-add conn-comp notebook (#390) 339 | - repushing missing notebooks (#389) 340 | - Stale links (#388) 341 | - [WIP][ENH] addressing shortest path extract (#382) 342 | - mention the pysal/notebook project (#384) 343 | - point pattern --> network error message (#383) 344 | - [ENH] connected components demo notebook (#381) 345 | - updating README.md (#379) 346 | - data type testing (#378) 347 | - Native geometries (#377) 348 | - adding CoC link to README.md (#376) 349 | - update CHANGELOG.md format (#375) 350 | - altering reference format in docs (#372) 351 | - Contrib precommit (#371) 352 | - distance matrix and tree storage (#370) 353 | - update readme (#367) 354 | - Adding citations for facility location notebook (#366) 355 | - updating README.md (#364) 356 | - trying out the black pre-commit hook (#363) 357 | - adding descartes to environment.yml (#362) 358 | - Attempt binder3 (#361) 359 | - Attempt binder2 (#358) 360 | - using environment.yml for binder (#357) 361 | - addressing #354 (#356) 362 | - Citation review (#355) 363 | - version 1.3.1 --> 1.4 bump (#353) 364 | - updating version and docs (#352) 365 | - [WIP] TSP notebook (#330) 366 | - [ENH][WIP]extension of #339: Rahul799 docs/bugfix (#342) 367 | 368 | The following individuals contributed to this release: 369 | 370 | - James Gaboardi 371 | 372 | 373 | 374 | ----------------------------------------------------- 375 | 376 | 377 | ## Version 1.4.0 (2019-12-31) 378 | 379 | We closed a total of 84 issues (enhancements and bug fixes) through 31 pull requests, since our last release on 2019-07-29. 380 | 381 | ### Issues Closed 382 | - updating version and docs (#352) 383 | - improve spaghetti homepage and notebooks? (#332) 384 | - Review docs for links, etc. (#338) 385 | - Spelling and default value of `n_processes` (#341) 386 | - [WIP] TSP notebook (#330) 387 | - [ENH][WIP]extension of #339: Rahul799 docs/bugfix (#342) 388 | - Revert "order analysis.py classes/functions alphabetically" (#350) 389 | - order analysis.py classes/functions alphabetically (#349) 390 | - updating requirement_dev.txt (#347) 391 | - update README.md (#348) 392 | - tarball jobs failing on Travis (#343) 393 | - updating README.md (#344) 394 | - links in the docs,reviewed (#339) 395 | - automatically generate docstrings for class members (#336) 396 | - Update docs (#337) 397 | - Updating .travis.yml (#335) 398 | - fix indentation in `.travis.yml` (#334) 399 | - update README.md badges (#331) 400 | - Citation stipulation for notebooks (#327) 401 | - Notebooks update part 2 (#329) 402 | - Notebooks update (#328) 403 | - update email address in notebooks (#324) 404 | - Plotting error in Facility_Location.ipynb (#325) 405 | - Updating Notebooks (#326) 406 | - module 'pysal.explore.spaghetti' has no attribute 'element_as_gdf' (#323) 407 | - [ENH] util.compute_length() — DRY (#318) 408 | - swapping out distance calculation function (#319) 409 | - adjusting snapping image in README.md (#317) 410 | - inaccurate documentation — Network.split_arcs() (#315) 411 | - Split arcs update (#316) 412 | - streamline badges in README.md (#313) 413 | - streaming badges on README.md (#314) 414 | - adding +travis to notification recipients (#312) 415 | - broken "development guidelines" link (#309) 416 | - updating broken dev link (#311) 417 | - Update Travis CI schema (#299) 418 | - Update travis CI for new testing schema (#308) 419 | - adding static docs badge (#310) 420 | - mock c modules for doc dependencies (#288) 421 | - doi missing from citation (#306) 422 | - adding Zenodo DOI to README.md and website (#307) 423 | - blackify setup.py (#305) 424 | - Change Read the Docs to GitHub? (#301) 425 | - updating README to reflect GitHub docs (#304) 426 | - switching to github docs as per #301 (#303) 427 | - get_versions() function (#302) 428 | - code formatting with black (#293) 429 | - blackifying code (#293) (#300) 430 | - rtree missing libspatialindex_c library file (#294) 431 | - updating install instructions (addressing #294) (#296) 432 | - updating install instructions + version bump (#298) 433 | - updating font for "Warning" (#297) 434 | - update of install instructions in README.md (#295) 435 | 436 | ### Pull Requests 437 | - updating version and docs (#352) 438 | - [WIP] TSP notebook (#330) 439 | - [ENH][WIP]extension of #339: Rahul799 docs/bugfix (#342) 440 | - Revert "order analysis.py classes/functions alphabetically" (#350) 441 | - order analysis.py classes/functions alphabetically (#349) 442 | - updating requirement_dev.txt (#347) 443 | - update README.md (#348) 444 | - updating README.md (#344) 445 | - Update docs (#337) 446 | - Updating .travis.yml (#335) 447 | - fix indentation in `.travis.yml` (#334) 448 | - update README.md badges (#331) 449 | - Notebooks update part 2 (#329) 450 | - Notebooks update (#328) 451 | - Updating Notebooks (#326) 452 | - swapping out distance calculation function (#319) 453 | - adjusting snapping image in README.md (#317) 454 | - Split arcs update (#316) 455 | - streaming badges on README.md (#314) 456 | - adding +travis to notification recipients (#312) 457 | - updating broken dev link (#311) 458 | - Update travis CI for new testing schema (#308) 459 | - adding static docs badge (#310) 460 | - adding Zenodo DOI to README.md and website (#307) 461 | - blackify setup.py (#305) 462 | - updating README to reflect GitHub docs (#304) 463 | - switching to github docs as per #301 (#303) 464 | - blackifying code (#293) (#300) 465 | - updating install instructions + version bump (#298) 466 | - updating font for "Warning" (#297) 467 | - update of install instructions in README.md (#295) 468 | 469 | The following individuals contributed to this release: 470 | 471 | - James Gaboardi 472 | - Rahul799 473 | 474 | 475 | 476 | ----------------------------------------------------- 477 | 478 | 479 | ## Version 1.3.1 (2019-07-29) 480 | 481 | We closed a total of 21 issues (enhancements and bug fixes) through 7 pull requests, since our last release on 2019-05-21. 482 | 483 | ### Issues Closed 484 | - updating font for "Warning" (#297) 485 | - update of install instructions in README.md (#295) 486 | - remove geopandas version stipulation (#291) 487 | - updating citation langauge (#290) 488 | - build error beginning (07/01/2019) (#289) 489 | - update css (#287) 490 | - update readthedocs.yml? (#284) 491 | - spaghetti api docs rendering incorrectly (#285) 492 | - docs table left aligned (#286) 493 | - remove extra line in .travis.yml (#283) 494 | - update Changelog (#282) 495 | - Manually updated CHANGELOG following stable release. (#281) 496 | - v1.3 release (#264) 497 | - Bump version 1.3rc2 --> 1.3 (#280) 498 | 499 | ### Pull Requests 500 | - updating font for "Warning" (#297) 501 | - update of install instructions in README.md (#295) 502 | - updating citation langauge (#290) 503 | - docs table left aligned (#286) 504 | - remove extra line in .travis.yml (#283) 505 | - update Changelog (#282) 506 | - Bump version 1.3rc2 --> 1.3 (#280) 507 | 508 | The following individuals contributed to this release: 509 | 510 | - James Gaboardi 511 | - Wei Kang 512 | 513 | ----------------------------------------------------- 514 | 515 | 516 | ## Version 1.3 (2019-05-21) 517 | 518 | We closed a total of 14 issues (enhancements and bug fixes) through 5 pull requests, since our last release on 2019-05-14. 519 | 520 | ### Issues Closed 521 | - Manually updated CHANGELOG following stable release (#281) 522 | - v1.3 release (#264) 523 | - add `requirements_dev.txt` to `MANIFEST.in` (#277) 524 | - Update changelog (#279) 525 | - version bump to include req_dev in MANIFEST.in (#278) 526 | - v1.2 --> 1.3rc1 (for release candidate) (#276) 527 | - Updating notebooks prior to v1.3 release (#275) 528 | - updating README (#274) 529 | - update notebooks following prior to v1.3 release (#158) 530 | 531 | ### Pull Requests 532 | - Update changelog (#279) 533 | - version bump to include req_dev in MANIFEST.in (#278) 534 | - v1.2 --> 1.3rc1 (for release candidate) (#276) 535 | - Updating notebooks prior to v1.3 release (#275) 536 | - updating README (#274) 537 | 538 | The following individuals contributed to this release: 539 | 540 | - James Gaboardi 541 | 542 | 543 | ----------------------------------------------------- 544 | 545 | 546 | ## Version 1.3rc2 (2019-05-14) 547 | 548 | We closed a total of 9 issues (enhancements and bug fixes) through 4 pull requests, since our last release on 2019-05-14. 549 | 550 | ### Issues Closed 551 | - version bump to include req_dev in MANIFEST.in (#278) 552 | - v1.2 --> 1.3rc1 (for release candidate) (#276) 553 | - Updating notebooks prior to v1.3 release (#275) 554 | - updating README (#274) 555 | - update notebooks following prior to v1.3 release (#158) 556 | 557 | ### Pull Requests 558 | - version bump to include req_dev in MANIFEST.in (#278) 559 | - v1.2 --> 1.3rc1 (for release candidate) (#276) 560 | - Updating notebooks prior to v1.3 release (#275) 561 | - updating README (#274) 562 | 563 | The following individuals contributed to this release: 564 | 565 | - James Gaboardi 566 | 567 | 568 | ----------------------------------------------------- 569 | 570 | 571 | ## Version 1.3rc1 (2019-05-14) 572 | 573 | We closed a total of 54 issues (enhancements and bug fixes) through 18 pull requests, since our last release on 2019-02-27. 574 | 575 | ### Issues Closed 576 | - Updating notebooks prior to v1.3 release (#275) 577 | - updating README (#274) 578 | - update notebooks following prior to v1.3 release (#158) 579 | - TypeError: '>' not supported between instances of '_NodeCursor' and '_NodeCursor' (#196) 580 | - Update docsite (#273) 581 | - fix docs building (#272) 582 | - docs failing with rtree (#268) 583 | - 3.5 --> 3.7 readthedocs (#269) 584 | - updating README.md (#271) 585 | - conda-forge recipe (#265) 586 | - Solo owner/maintainer (#270) 587 | - update requirements in README (#266) 588 | - updating requirements in README (#267) 589 | - Switch snapping to Rtree package? (#259) 590 | - update python versions in setup.py (#262) 591 | - updating supported python versions in setup.py (#263) 592 | - Drop 3.5, Add 3.7 testing (#260) 593 | - adding support for python3.7 (#261) 594 | - Update Facility_Location.ipynb (#257) 595 | - Further failures in TravisCI builds. (`rake`) (#256) 596 | - bumping down geopandas requirement to 0.3.0 (#255) 597 | - bumping geopandas req version to 0.4.1 (#254) 598 | - TravisCI build failing with `geopandas` (#253) 599 | - Streamline setup travis (#252) 600 | - Remove redundancies in `requirements_xxx.txt` files (#250) 601 | - Removing redundant requirements (#251) 602 | - pandas.core error leading to Travis CI failure (#240) 603 | - uniform .travis.yml (#248) 604 | - switching from pip to conda for reqs install resolves .travis error (#249) 605 | - v1.2.rc1? (#246) 606 | - updating GitHub version badge (#247) 607 | - update version badges (#245) 608 | - AttributeError: module 'spaghetti' has no attribute 'element_as_gdf' (#242) 609 | - bump from rc to stable (#244) 610 | - v1.2rc1 bump (#243) 611 | - adding pandas as a requirement (#241) 612 | 613 | ### Pull Requests 614 | - Updating notebooks prior to v1.3 release (#275) 615 | - updating README (#274) 616 | - Update docsite (#273) 617 | - fix docs building (#272) 618 | - 3.5 --> 3.7 readthedocs (#269) 619 | - updating README.md (#271) 620 | - updating requirements in README (#267) 621 | - Switch snapping to Rtree package? (#259) 622 | - updating supported python versions in setup.py (#263) 623 | - adding support for python3.7 (#261) 624 | - Update Facility_Location.ipynb (#257) 625 | - bumping down geopandas requirement to 0.3.0 (#255) 626 | - bumping geopandas req version to 0.4.1 (#254) 627 | - Streamline setup travis (#252) 628 | - switching from pip to conda for reqs install resolves .travis error (#249) 629 | - updating GitHub version badge (#247) 630 | - bump from rc to stable (#244) 631 | - v1.2rc1 bump (#243) 632 | 633 | The following individuals contributed to this release: 634 | 635 | - James Gaboardi 636 | - Wei Kang 637 | - Levi John Wolf 638 | - @Tomgertin 639 | 640 | 641 | ----------------------------------------------------- 642 | 643 | 644 | ## Version 1.2 (2019-02) 645 | 646 | This is a stable release of v1.2rc1 with no changes to code. 647 | 648 | 649 | 650 | ----------------------------------------------------- 651 | 652 | ## Version 1.2rc1 (2019-02-27) 653 | 654 | We closed a total of 160 issues (enhancements and bug fixes) through 60 pull requests, since our last release on 2018-11-01. 655 | 656 | ### Issues Closed 657 | - Coverage testing (#239) 658 | - update .travis.yml and introduce .coveragerc similar to pysal/giddy#81 (#238) 659 | - Update `bug` template (#237) 660 | - Update issue templates (#236) 661 | - Add .github directory based on libpysal (#234) 662 | - Adding community info (#235) 663 | - remove README.rst (#223) 664 | - Removing README.rst and updating setup.py (#233) 665 | - pysal/giddy#77 (reference labels) (#231) 666 | - Ref docs (#232) 667 | - Api site doc (#230) 668 | - correction in notebooks/Network_Usage.ipynb (#229) 669 | - edges_to_arcs naming convention (#227) 670 | - altering edges_to_arcs crosswalk name (#228) 671 | - Improve in-line docs -- 24PullRequests for Christmas (#202) 672 | - updating utils.py docs (#226) 673 | - updating all analysis.py docs (#224) 674 | - Docs split arcs (#222) 675 | - Docs allneighbordistances (#221) 676 | - updating docs for full_distance_matrix (#220) 677 | - notes for simulate_observations (#219) 678 | - Docs net funcs (#218) 679 | - bug in snapping points? (#125) 680 | - Deprecation: Container-Based Linux Build Environments (#188) 681 | - Remove sorted edges (#216) 682 | - purpose of class SortedEdges(OrderedDict)? (#90) 683 | - geometry notes in _newpoint_coords (#215) 684 | - count_per_link -- updating docs (#214) 685 | - TravisCI error raise ReadTimeoutError(self._pool, None, 'Read timed out.') (#213) 686 | - variable declaration correction in _snap_to_link (#212) 687 | - compute_distance_to_vertices -- in-line doc improvement (#211) 688 | - improved in-line comments in network.Network.distancebandweights (#210) 689 | - improved in-line comments in network.Network.contiguityweights (#209) 690 | - improved in-line comments in network.Network._docs_evaluate_napts (#208) 691 | - improved in-line comments in network.Network._yield_napts (#207) 692 | - improved in-line comments in network.Network.extractgraph (#206) 693 | - improved in-line comments in network.Network.extract_components (#205) 694 | - improved in-line comments in network.Network._round_sig (#204) 695 | - improved in-line comments in network.Network.__init__ (#203) 696 | - improved in-line comments in _extractnetwork (#201) 697 | - minor spaghetti/util.py doc clean (#200) 698 | - update notebooks following #185 (#187) 699 | - README graphic legend update (#197) 700 | - Update notebooks (#199) 701 | - correcting arc -- arcs attribute (#193) 702 | - attribute declaration error (network.Network.arc) (#192) 703 | - (from pysal.network) Edge ID data structure #933 (#176) 704 | - (from pysal.network) network ring bug #655 (#184) 705 | - Debug of data structure/algo that led to ring/graph errors (#185) 706 | - Creating Network instance from road shapefile fails; ValueError: list.remove(x): x not in list (#9) 707 | - (from pysal.network) Revisit graph weights in network module #496 (#186) 708 | - Updating spaghetti ReadTheDocs (#183) 709 | - tree not recorded in same network arc (#180) 710 | - Nearest tree update (#182) 711 | - clean/create more coherent and uniform variable names (#49) 712 | - Updating/standardizing network/graph element naming (#181) 713 | - Dijkstra maintenance (#179) 714 | - [MAINT] dijkstra (#178) 715 | - Adding .png of snapped point to README.md (#177) 716 | - fixing bug in distancebandweights (#175) 717 | - bug in network.Network.distancebandweights (#174) 718 | - determine necessity of test_distance_band_weights (#62) 719 | - updating ReadTheDocs following #172 (#173) 720 | - Pep8 docs review (#172) 721 | - record connected components (#168) 722 | - [WIP] contiguityweights and connected_components (#171) 723 | - add break condition in contiguityweights() (#170) 724 | - Edge Weighting pysal/pysal #609 (#66) 725 | - Merge unittest scripts (#169) 726 | - Further streamline unittests (#157) 727 | - api docs (#162) 728 | - network element as geodataframe (#143) 729 | - Refactor element as gdf (#167) 730 | - Restructure docs (#166) 731 | - Non responsive docs (#165) 732 | - typo in dijkstra reference (#163) 733 | - Update api docs (#164) 734 | - Update api (#161) 735 | - cleaning up notebooks/Snapping_Demonstration.ipynb (#160) 736 | - Update notebooks (#159) 737 | - add minor test for _round_sig (#152) 738 | - Round sig test (#156) 739 | - updating element_as_gdf.rst (#155) 740 | - Update doc element to gdf (#154) 741 | - Streamline unittests (#147) 742 | - Streamline unittests (#153) 743 | - ENH: element_to_gdf() (#145) 744 | - correcting list comp syntax in _round_sig (#151) 745 | - error in _round_sig() when a coordinate is 0.0 (#150) 746 | - [ENH] streets/network and points need to be in the same CRS/EPSG (#71) 747 | - update markdown docs fromSpaghetti_Pointpatterns_Empirical.ipynb (#146) 748 | - updating typo in notebooks/Spaghetti_Pointpatterns_Empirical.ipynb (#149) 749 | - Minor notebook update (#148) 750 | - update spaghetti info on pysal.site (#144) 751 | - fix RuntimeWarning in analysis.py (#140) 752 | - usage of networkx (#141) 753 | - updating notebooks (#142) 754 | - update spaghetti landing site notebook links (#139) 755 | - rerunning notebooks (#138) 756 | - travis build still failing (#127) 757 | 758 | ### Pull Requests 759 | - Coverage testing (#239) 760 | - Update `bug` template (#237) 761 | - Update issue templates (#236) 762 | - Adding community info (#235) 763 | - Removing README.rst and updating setup.py (#233) 764 | - Ref docs (#232) 765 | - Api site doc (#230) 766 | - correction in notebooks/Network_Usage.ipynb (#229) 767 | - altering edges_to_arcs crosswalk name (#228) 768 | - updating utils.py docs (#226) 769 | - updating all analysis.py docs (#224) 770 | - Docs split arcs (#222) 771 | - Docs allneighbordistances (#221) 772 | - updating docs for full_distance_matrix (#220) 773 | - notes for simulate_observations (#219) 774 | - Docs net funcs (#218) 775 | - Remove sorted edges (#216) 776 | - geometry notes in _newpoint_coords (#215) 777 | - count_per_link -- updating docs (#214) 778 | - variable declaration correction in _snap_to_link (#212) 779 | - compute_distance_to_vertices -- in-line doc improvement (#211) 780 | - improved in-line comments in network.Network.distancebandweights (#210) 781 | - improved in-line comments in network.Network.contiguityweights (#209) 782 | - improved in-line comments in network.Network._docs_evaluate_napts (#208) 783 | - improved in-line comments in network.Network._yield_napts (#207) 784 | - improved in-line comments in network.Network.extractgraph (#206) 785 | - improved in-line comments in network.Network.extract_components (#205) 786 | - improved in-line comments in network.Network._round_sig (#204) 787 | - improved in-line comments in network.Network.__init__ (#203) 788 | - improved in-line comments in _extractnetwork (#201) 789 | - minor spaghetti/util.py doc clean (#200) 790 | - Update notebooks (#199) 791 | - correcting arc -- arcs attribute (#193) 792 | - Debug of data structure/algo that led to ring/graph errors (#185) 793 | - Updating spaghetti ReadTheDocs (#183) 794 | - Nearest tree update (#182) 795 | - Updating/standardizing network/graph element naming (#181) 796 | - Dijkstra maintenance (#179) 797 | - Adding .png of snapped point to README.md (#177) 798 | - fixing bug in distancebandweights (#175) 799 | - updating ReadTheDocs following #172 (#173) 800 | - Pep8 docs review (#172) 801 | - [WIP] contiguityweights and connected_components (#171) 802 | - Merge unittest scripts (#169) 803 | - Refactor element as gdf (#167) 804 | - Restructure docs (#166) 805 | - Non responsive docs (#165) 806 | - Update api docs (#164) 807 | - Update api (#161) 808 | - cleaning up notebooks/Snapping_Demonstration.ipynb (#160) 809 | - Update notebooks (#159) 810 | - Round sig test (#156) 811 | - updating element_as_gdf.rst (#155) 812 | - Update doc element to gdf (#154) 813 | - Streamline unittests (#153) 814 | - ENH: element_to_gdf() (#145) 815 | - correcting list comp syntax in _round_sig (#151) 816 | - updating typo in notebooks/Spaghetti_Pointpatterns_Empirical.ipynb (#149) 817 | - updating notebooks (#142) 818 | - rerunning notebooks (#138) 819 | 820 | The following individuals contributed to this release: 821 | 822 | - James Gaboardi 823 | 824 | ----------------------------------------------------- 825 | 826 | 827 | ## Version 1.1.1 (2018-10-31) 828 | 829 | We closed a total of 5 issues (enhancements and bug fixes) through 2 pull requests, since our last release on 2018-10-30. 830 | 831 | ### Issues Closed 832 | - removed .txt format CHANGELOG (#129) 833 | - rel: v1.1.0 -- Adjusting CHANGELOG.md prior to tagged release (#128) 834 | - prepare new release for pypi -- v1.1.0 (#57) 835 | 836 | ### Pull Requests 837 | - removed .txt format CHANGELOG (#129) 838 | - rel: v1.1.0 -- Adjusting CHANGELOG.md prior to tagged release (#128) 839 | 840 | The following individuals contributed to this release: 841 | 842 | - James Gaboardi 843 | 844 | ----------------------------------------------------- 845 | 846 | 847 | ## Version 1.1.0 (2018-10-30) 848 | 849 | We closed a total of 82 issues (enhancements and bug fixes) through 25 pull requests, since our last release on 2018-08-10. 850 | 851 | ### Issues Closed 852 | - prepare new release for pypi -- v1.1.0 (#57) 853 | - refreshing documentation (#124) 854 | - option to add distance from point to snapped location (#75) 855 | - attempting pyproj_fix (#122) 856 | - [WIP] Add snap dist (#123) 857 | - travis CI build failing with `KeyError: 'PROJ_LIB'` (#121) 858 | - resolving obs_to_node question (#120) 859 | - why convert obs_to_node from defaultdict to list? (#93) 860 | - network.PointPatterns condense code chunk (#74) 861 | - condensing idvariable code chunk (#119) 862 | - Network Cross Nearest Neighbor (#102) 863 | - refreshing docs (#117) 864 | - shortest path look up from allneighborsdistances? (#115) 865 | - adding shortest path traceback for point patterns (#116) 866 | - ImportError: No module named 'boto3' (#113) 867 | - adding boto3 test req for current fiona bug (#114) 868 | - [WIP] cleanup_nearest_neighbor (#112) 869 | - duplicate neighbor distance functions? (#91) 870 | - network.allneighbordistances documentation not accurate (#111) 871 | - [WIP] General package maintenance (#109) 872 | - new badges (#96) 873 | - tools/ (#99) 874 | - updating thumbnails in docs (#108) 875 | - [WIP] updating docs, badges, tools, etc. (#107) 876 | - initializing new sphinx docs based on submodule_template (#98) 877 | - new labels for issues (#105) 878 | - populating sphinx docs (#37) 879 | - tests for analysis and util (#44) 880 | - NetworkF (#94) 881 | - rename functions to be more pythonic (#104) 882 | - add poisson distribution to tests (#106) 883 | - initial sphix docs attempt (#67) 884 | - bumping version to 1.1.0 (#97) 885 | - adding in new tests for utils.py (#95) 886 | - add flag for util.generatetree() (#92) 887 | - [completed atm] - docstrings cleanup (#89) 888 | - clean docstrings (#77) 889 | - adding MANIFEST.in (#88) 890 | - clearing Facility_Location.ipynb (#87) 891 | - removing typo in Facility_Location (#86) 892 | - clearing Facility_Location.ipynb (#85) 893 | - updating Facility_Location.ipynb for typos (#84) 894 | - adding Facility_Location.ipynb (#83) 895 | - new notebook ideas (#48) 896 | - adding windows functionality for 'last updated' (#82) 897 | - ensure nearest nodes are returned as np.array() (#73) 898 | - snapping trouble when the initial node in KDtree is the nearest (#72) 899 | - add Github version badge (#80) 900 | - add open issues badge (#79) 901 | - update notebooks as per pysal/pysal#1057 (#81) 902 | - [Complete/Needs Review] updating `in_shp` parameter in spaghetti.Network (#69) 903 | - [ENH] geopandas.GeoDataFrame for PointPattern (#28) 904 | - update in_shp kwarg in spaghetti.Network (#68) 905 | - removing undeclared edge_time attribute (#65) 906 | - update README.txt (#33) 907 | - [ENH] Add badges (#31) 908 | - Publish on Zenodo (#36) 909 | 910 | ### Pull Requests 911 | - refreshing documentation (#124) 912 | - attempting pyproj_fix (#122) 913 | - [WIP] Add snap dist (#123) 914 | - resolving obs_to_node question (#120) 915 | - condensing idvariable code chunk (#119) 916 | - refreshing docs (#117) 917 | - adding shortest path traceback for point patterns (#116) 918 | - [WIP] cleanup_nearest_neighbor (#112) 919 | - [WIP] General package maintenance (#109) 920 | - updating thumbnails in docs (#108) 921 | - [WIP] updating docs, badges, tools, etc. (#107) 922 | - initializing new sphinx docs based on submodule_template (#98) 923 | - bumping version to 1.1.0 (#97) 924 | - adding in new tests for utils.py (#95) 925 | - [completed atm] - docstrings cleanup (#89) 926 | - adding MANIFEST.in (#88) 927 | - clearing Facility_Location.ipynb (#87) 928 | - removing typo in Facility_Location (#86) 929 | - clearing Facility_Location.ipynb (#85) 930 | - updating Facility_Location.ipynb for typos (#84) 931 | - adding Facility_Location.ipynb (#83) 932 | - adding windows functionality for 'last updated' (#82) 933 | - ensure nearest nodes are returned as np.array() (#73) 934 | - [Complete/Needs Review] updating `in_shp` parameter in spaghetti.Network (#69) 935 | - removing undeclared edge_time attribute (#65) 936 | 937 | The following individuals contributed to this release: 938 | 939 | - James Gaboardi 940 | 941 | 942 | -------------------------------------------------------------- 943 | 944 | 945 | ## GitHub Stats for 2017/03/15 - 2018/07/28 (tag: None) 946 | 947 | These lists are automatically generated,and may be incomplete or contain duplicates. 948 | 949 | 950 | We closed a total of 39 issues, 12 pull requests and 27 regular issues; 951 | this is the full list (generated with the script 952 | :file:`tools/github_stats.py`): 953 | 954 | ### Pull Requests (12): 955 | 956 | * `#25`: preparing for pypi release 957 | * `#23`: Py2topy3 958 | * `#24`: Spaghetti/update travis 959 | * `#16`: adding an api.py file to spaghetti 960 | * `#17`: Spag/analysis cosmetics 961 | * `#14`: removing `math` in favor of `numpy` for calcs 962 | * `#13`: minor doc commit to practice git workflow 963 | * `#11`: adding a license 964 | * `#10`: setting up travis ci 965 | * `#8`: the first of the new network tutorial notebooks for pysal/spaghetti 966 | * `#7`: rename makes geonet redundant 967 | * `#6`: updated package name and short description in __init__ 968 | 969 | ### Issues (27): 970 | 971 | * `#5`: update import scheme for new package name 972 | * `#26`: Prepare a release of spaghetti for pypi 973 | * `#32`: pip 974 | * `#25`: preparing for pypi release 975 | * `#19`: api.py tests 976 | * `#29`: trailing comma not allowed with surrounding parenthesis 977 | * `#27`: Necessity of __future__? 978 | * `#21`: `spaghetti` currently only python 2.7.x compatible 979 | * `#22`: Geopandas read 980 | * `#23`: Py2topy3 981 | * `#24`: Spaghetti/update travis 982 | * `#20`: Generalize the Network input API for libpysal/#59 983 | * `#18`: adding gitter badge to README.md 984 | * `#15`: spaghetti needs an api.py module 985 | * `#17`: Spag/analysis cosmetics 986 | * `#16`: adding an api.py file to spaghetti 987 | * `#4`: test release and setup.py 988 | * `#3`: add a readme 989 | * `#14`: removing `math` in favor of `numpy` for calcs 990 | * `#13`: minor doc commit to practice git workflow 991 | * `#12`: Pp notebook 992 | * `#11`: adding a license 993 | * `#2`: set up testing for travis 994 | * `#10`: setting up travis ci 995 | * `#8`: the first of the new network tutorial notebooks for pysal/spaghetti 996 | * `#7`: rename makes geonet redundant 997 | * `#6`: updated package name and short description in __init__ 998 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2017 PySAL-spaghetti Developers 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 |

5 | 6 | # [pysal/spaghetti](http://pysal.org/spaghetti/) 7 | 8 | # *SPA*tial *G*rap*H*s: n*ET*works, *T*opology, & *I*nference 9 | 10 | Spaghetti is an open-source Python library for the analysis of network-based spatial data. Originating from the `network` module in [PySAL (Python Spatial Analysis Library)](http://pysal.org), it is under active development for the inclusion of newly proposed methods for building graph-theoretic networks and the analysis of network events. 11 | 12 | *An example of a network's [minimum spanning tree](https://pysal.org/spaghetti/generated/spaghetti.spanning_tree.html#spaghetti.spanning_tree):*

13 | 14 |

15 | 16 | |[![PyPI version](https://badge.fury.io/py/spaghetti.svg)](https://badge.fury.io/py/spaghetti)| [![Conda Version](https://img.shields.io/conda/vn/conda-forge/spaghetti.svg)](https://anaconda.org/conda-forge/spaghetti) | ![tag](https://img.shields.io/github/v/release/pysal/spaghetti?include_prereleases&sort=semver) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/pysal/spaghetti/main) 17 | |:---:|:---:|:---:|:---:| 18 | |[![Downloads](https://pepy.tech/badge/spaghetti)](https://pepy.tech/project/spaghetti) | [![Conda Downloads](https://img.shields.io/conda/dn/conda-forge/spaghetti.svg)](https://anaconda.org/conda-forge/spaghetti) | [![Documentation](https://img.shields.io/static/v1.svg?label=docs&message=current&color=9cf)](http://pysal.org/spaghetti/) | [![Discord](https://img.shields.io/badge/Discord-join%20chat-7289da?style=flat&logo=discord&logoColor=cccccc&link=https://discord.gg/BxFTEPFFZn)](https://discord.gg/BxFTEPFFZn) 19 | | ![Pypi python versions](https://img.shields.io/pypi/pyversions/spaghetti.svg) | [![Conda Recipe](https://img.shields.io/badge/recipe-spaghetti-red.svg)](https://github.com/conda-forge/spaghetti-feedstock) | [![codecov](https://codecov.io/gh/pysal/spaghetti/branch/main/graph/badge.svg)](https://codecov.io/gh/pysal/spaghetti) | [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) 20 | | [![Continuous Integration](https://github.com/pysal/spaghetti/actions/workflows/testing.yml/badge.svg)](https://github.com/pysal/spaghetti/actions/workflows/testing.yml) | [![status](https://joss.theoj.org/papers/52b8d0c81bbf311465b45bfc26379e74/status.svg)](https://joss.theoj.org/papers/52b8d0c81bbf311465b45bfc26379e74) | [![DOI](https://zenodo.org/badge/88305306.svg)](https://zenodo.org/badge/latestdoi/88305306) | [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) 21 | 22 | ## Examples 23 | 24 | The following are a selection of some examples that can be launched individually as interactive binders from the links on their respective pages. Additional examples can be found in the [Tutorials](https://pysal.org/spaghetti/tutorials.html) section of the documentation. See the [`pysal/notebooks`](http://pysal.org/notebooks) project for a [`jupyter-book`](https://github.com/choldgraf/jupyter-book) version of this repository. 25 | * [Quickstart](https://pysal.org/spaghetti/notebooks/quickstart.html) 26 | * [Shortest Path Visualization](https://pysal.org/spaghetti/notebooks/shortest-path-visualization.html) 27 | * [Caveats](https://pysal.org/spaghetti/notebooks/caveats.html) 28 | 29 | ## Installation 30 | 31 | Python >= [3.10](https://docs.python.org/3.10/) is tested for support by `spaghetti`. Please make sure that you are operating in a Python >= 3.10 environment. 32 | 33 | **Installing with `conda` via [`conda-forge`](https://github.com/conda-forge/spaghetti-feedstock) (highly recommended)** 34 | 35 | To install `spaghetti` and all its dependencies, we recommend using the [`conda`](https://docs.conda.io/en/latest/) 36 | manager, specifically with the [`conda-forge`](https://conda-forge.org) channel. This can be obtained by installing the [`Anaconda Distribution`](https://docs.continuum.io/anaconda/) (a free Python distribution for data science), or through [`miniconda`](https://docs.conda.io/en/latest/miniconda.html) (minimal distribution only containing Python and the conda package manager). 37 | 38 | Using `conda`, `spaghetti` can be installed as follows: 39 | ``` 40 | $ conda config --set channel_priority strict 41 | $ conda install --channel conda-forge spaghetti 42 | ``` 43 | Also, `geopandas` provides [a nice example](https://geopandas.readthedocs.io/en/latest/getting_started/install.html#creating-a-new-environment) to create a fresh environment for working with spatial data. 44 | 45 | **Installing with [`PyPI`](https://pypi.org/project/spaghetti/)** 46 | ``` 47 | $ pip install spaghetti 48 | ``` 49 | *or* download the source distribution (`.tar.gz`) and decompress it to your selected destination. Open a command shell and navigate to the decompressed folder. 50 | ``` 51 | $ pip install . 52 | ``` 53 | 54 | ***Warning*** 55 | 56 | When installing via `pip`, you have to ensure that the required dependencies for `spaghetti` are installed on your operating system. Details on how to install these packages are linked below. Using `conda` (above) avoids having to install the dependencies separately. 57 | 58 | Install the most current development version of `spaghetti` by running: 59 | 60 | ``` 61 | $ pip install git+https://github.com/pysal/spaghetti 62 | ``` 63 | 64 | ## Requirements 65 | 66 | - [`esda`](https://pysal.org/esda/) 67 | - [`geopandas`](https://geopandas.org/en/stable/) 68 | - [`libpysal`](https://pysal.org/libpysal/) 69 | - [`libspatialindex`](https://libspatialindex.org/en/stable/) 70 | - [`numpy`](https://numpy.org/devdocs/) 71 | - [`rtree`](https://rtree.readthedocs.io/en/stable/) 72 | - [`scipy`](http://scipy.github.io/devdocs/) 73 | - [`shapely`](https://shapely.readthedocs.io/en/stable/) 74 | 75 | ## History 76 | 77 | `spaghetti` was 78 | created and has evolved in line with the Python Spatial Analysis Library ecosystem for 79 | the specific purpose of utilizing the functionality of spatial weights in 80 | [`libpysal`](https://pysal.org/libpysal/) for generating network segment contiguity objects. 81 | The PySAL project was started in the mid-2000s when installation was difficult to maintain. 82 | Due to the non-triviality of relying on dependencies to secondary packages, a conscious 83 | decision was made to limit dependencies and build native PySAL data structures in cases 84 | where at all possible. Therefore, the original `pysal.network` submodule was developed to 85 | address the need for integrating support for network data structures with PySAL weights 86 | data structures, with the target audience being spatial data scientists and anyone 87 | interested in investigating network-centric phenomena within PySAL. Owing to the 88 | co-development of network functionality found within `spaghetti` and the evolution of 89 | the wider PySAL ecosystem, today, the package provides specialized network functionality 90 | that easily integrates with the rest of PySAL. This allows users of `spaghetti`’s network 91 | functionality to access spatial analysis functionality that complements network analysis, 92 | such as spatial statistical tools with `esda` and integration with core components of 93 | `libpysal`: `libpysal.weights` (mentioned above), 94 | `libpysal.cg` (computational geometry and data structures), 95 | `libpysal.io` (input-output), and `libpysal.examples` (built-in example data). 96 | 97 | ## Contribute 98 | 99 | PySAL-spaghetti is under active development and contributors are welcome. 100 | 101 | If you have any suggestions, feature requests, or bug reports, please open new [issues](https://github.com/pysal/spaghetti/issues) on GitHub. To submit patches, please review [PySAL's documentation for developers](https://pysal.org/docs/devs/), the PySAL [development guidelines](https://github.com/pysal/pysal/wiki), the `spaghetti` [contributing guidelines](https://github.com/pysal/spaghetti/blob/main/.github/CONTRIBUTING.md) before opening a [pull request](https://github.com/pysal/spaghetti/pulls). Once your changes get merged, you’ll automatically be added to the [Contributors List](https://github.com/pysal/spaghetti/graphs/contributors). 102 | 103 | ## Support 104 | 105 | If you are having issues, please [create an issue](https://github.com/pysal/spaghetti/issues), start a [discussion](https://github.com/pysal/spaghetti/discussions), or talk to us in [PySAL's Discord channel](https://discord.gg/BxFTEPFFZn). All questions, comments, & discussions should happen in a public forum, where possible. Private messages and emails will not be answered in a substantive manner. 106 | 107 | ## Code of Conduct 108 | 109 | As a PySAL-federated project, `spaghetti` follows the [Code of Conduct](https://github.com/pysal/governance/blob/main/conduct/code_of_conduct.rst) under the [PySAL governance model](https://github.com/pysal/governance). 110 | 111 | ## License 112 | 113 | The project is licensed under the [BSD 3-Clause license](https://github.com/pysal/spaghetti/blob/main/LICENSE.txt). 114 | 115 | ## BibTeX Citation 116 | 117 | If you use PySAL-spaghetti in a scientific publication, we would appreciate using the following citations: 118 | 119 | ``` 120 | @article{Gaboardi2021, 121 | doi = {10.21105/joss.02826}, 122 | url = {https://doi.org/10.21105/joss.02826}, 123 | year = {2021}, 124 | publisher = {The Open Journal}, 125 | volume = {6}, 126 | number = {62}, 127 | pages = {2826}, 128 | author = {James D. Gaboardi and Sergio Rey and Stefanie Lumnitz}, 129 | title = {spaghetti: spatial network analysis in PySAL}, 130 | journal = {Journal of Open Source Software} 131 | } 132 | 133 | @misc{Gaboardi2018, 134 | author = {Gaboardi, James D. and Laura, Jay and Rey, Sergio and 135 | Wolf, Levi John and Folch, David C. and Kang, Wei and 136 | Stephens, Philip and Schmidt, Charles}, 137 | month = {oct}, 138 | year = {2018}, 139 | title = {pysal/spaghetti}, 140 | url = {https://github.com/pysal/spaghetti}, 141 | doi = {10.5281/zenodo.1343650}, 142 | keywords = {graph-theory,network-analysis,python,spatial-networks,topology} 143 | } 144 | ``` 145 | 146 | ## Funding 147 | This project is/was partially funded through: 148 | 149 | [](https://atlantardc.wordpress.com) Atlanta Research Data Center: [A Polygon-Based Approach to Spatial Network Allocation](https://atlantardc.files.wordpress.com/2018/05/ardc-newsletter_2018_2.pdf) 150 | 151 | [](https://www.nsf.gov/index.jsp) National Science Foundation Award #1825768: [National Historical Geographic Information System](https://www.nsf.gov/awardsearch/showAward?AWD_ID=1825768&HistoricalAwards=false) 152 | -------------------------------------------------------------------------------- /ci/310-latest.yaml: -------------------------------------------------------------------------------- 1 | name: test 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - python=3.10 6 | # required 7 | - esda 8 | - geopandas 9 | - libpysal 10 | - numpy 11 | - pandas 12 | - pip 13 | - rtree 14 | - scipy 15 | - shapely 16 | - watermark 17 | # testing 18 | - codecov 19 | - pytest 20 | - pytest-cov 21 | - pytest-doctestplus 22 | - pytest-timeout 23 | - pytest-xdist 24 | -------------------------------------------------------------------------------- /ci/310-oldest.yaml: -------------------------------------------------------------------------------- 1 | name: test 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - python=3.10 6 | # required 7 | - esda=2.1 8 | - fiona<1.10 9 | - geopandas=0.12 10 | - libpysal=4.6 11 | - numpy=1.22 12 | - pandas=1.4 13 | - pip 14 | - rtree=1.0 15 | - scipy=1.8 16 | - shapely=2.0 17 | # testing 18 | - codecov 19 | - pytest 20 | - pytest-cov 21 | - pytest-timeout 22 | - pytest-xdist 23 | -------------------------------------------------------------------------------- /ci/311-latest.yaml: -------------------------------------------------------------------------------- 1 | name: test 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - python=3.11 6 | # required 7 | - esda 8 | - geopandas 9 | - libpysal 10 | - numpy 11 | - pandas 12 | - pip 13 | - rtree 14 | - scipy 15 | - shapely 16 | - watermark 17 | # testing 18 | - codecov 19 | - pytest 20 | - pytest-cov 21 | - pytest-doctestplus 22 | - pytest-timeout 23 | - pytest-xdist 24 | -------------------------------------------------------------------------------- /ci/312-dev.yaml: -------------------------------------------------------------------------------- 1 | name: test 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - python=3.12 6 | # required 7 | - geos # for shapely 8 | - matplotlib # for esda 9 | - numpy 10 | - pip 11 | - rtree 12 | - watermark 13 | # testing 14 | - codecov 15 | - pytest 16 | - pytest-cov 17 | - pytest-doctestplus 18 | - pytest-timeout 19 | - pytest-xdist 20 | - pip: 21 | # dev versions of packages 22 | - --pre --index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple --extra-index-url https://pypi.org/simple 23 | - pandas 24 | - scipy 25 | - git+https://github.com/pysal/esda.git@main 26 | - git+https://github.com/geopandas/geopandas.git@main 27 | - git+https://github.com/pysal/libpysal.git@main 28 | - git+https://github.com/shapely/shapely.git@main 29 | -------------------------------------------------------------------------------- /ci/312-latest.yaml: -------------------------------------------------------------------------------- 1 | name: test 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - python=3.12 6 | # required 7 | - esda 8 | - geopandas 9 | - libpysal 10 | - numpy 11 | - pandas 12 | - pip 13 | - rtree 14 | - scipy 15 | - shapely 16 | - watermark 17 | # testing 18 | - codecov 19 | - pytest 20 | - pytest-cov 21 | - pytest-doctestplus 22 | - pytest-timeout 23 | - pytest-xdist 24 | # for docs build action (this env only) 25 | - nbsphinx 26 | - numpydoc 27 | - sphinx 28 | - sphinxcontrib-bibtex 29 | - sphinx_bootstrap_theme 30 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | notify: 3 | after_n_builds: 6 4 | coverage: 5 | range: 50..95 6 | round: nearest 7 | precision: 1 8 | status: 9 | project: 10 | default: 11 | threshold: 2% 12 | patch: 13 | default: 14 | threshold: 2% 15 | target: 80% 16 | ignore: 17 | - "tests/*" 18 | comment: 19 | layout: "reach, diff, files" 20 | behavior: once 21 | after_n_builds: 6 22 | require_changes: true 23 | -------------------------------------------------------------------------------- /docs/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: 23a3f8d9c1559fbe5ac06f8019bbbead 4 | tags: 645f666f9bcd5a90fca523b33c5a78b7 5 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = spaghetti 8 | SOURCEDIR = . 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 | @rsync -r --exclude '.ipynb_checkpoints/' ../notebooks/ ./notebooks/ 21 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 22 | 23 | github: 24 | @make html 25 | 26 | sync: 27 | @rsync -avh _build/html/ ../docs/ --delete 28 | @make clean 29 | touch .nojekyll 30 | 31 | clean: 32 | rm -rf $(BUILDDIR)/* 33 | rm -rf auto_examples/ 34 | rm -rf generated/ 35 | -------------------------------------------------------------------------------- /docs/_static/images/ardc_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pysal/spaghetti/72ae8892a9ae62d0d18aecc310709b0e00d5f090/docs/_static/images/ardc_logo.png -------------------------------------------------------------------------------- /docs/_static/images/crime_counts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pysal/spaghetti/72ae8892a9ae62d0d18aecc310709b0e00d5f090/docs/_static/images/crime_counts.png -------------------------------------------------------------------------------- /docs/_static/images/facility_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pysal/spaghetti/72ae8892a9ae62d0d18aecc310709b0e00d5f090/docs/_static/images/facility_location.png -------------------------------------------------------------------------------- /docs/_static/images/mst_logo_pasta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pysal/spaghetti/72ae8892a9ae62d0d18aecc310709b0e00d5f090/docs/_static/images/mst_logo_pasta.png -------------------------------------------------------------------------------- /docs/_static/images/net_rep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pysal/spaghetti/72ae8892a9ae62d0d18aecc310709b0e00d5f090/docs/_static/images/net_rep.png -------------------------------------------------------------------------------- /docs/_static/images/network_k.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pysal/spaghetti/72ae8892a9ae62d0d18aecc310709b0e00d5f090/docs/_static/images/network_k.png -------------------------------------------------------------------------------- /docs/_static/images/nsf_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pysal/spaghetti/72ae8892a9ae62d0d18aecc310709b0e00d5f090/docs/_static/images/nsf_logo.png -------------------------------------------------------------------------------- /docs/_static/images/pysal_favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pysal/spaghetti/72ae8892a9ae62d0d18aecc310709b0e00d5f090/docs/_static/images/pysal_favicon.ico -------------------------------------------------------------------------------- /docs/_static/images/pysal_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pysal/spaghetti/72ae8892a9ae62d0d18aecc310709b0e00d5f090/docs/_static/images/pysal_logo.png -------------------------------------------------------------------------------- /docs/_static/pysal-styles.css: -------------------------------------------------------------------------------- 1 | /* Make thumbnails with equal heights */ 2 | @media only screen and (min-width : 500px) { 3 | .row.equal-height { 4 | display: flex; 5 | flex-wrap: wrap; 6 | } 7 | .row.equal-height > [class*='col-'] { 8 | display: flex; 9 | flex-direction: row, column; 10 | } 11 | .row.equal-height.row:after, 12 | .row.equal-height.row:before { 13 | display: flex; 14 | } 15 | 16 | .row.equal-height > [class*='col-'] > .thumbnail, 17 | .row.equal-height > [class*='col-'] > .thumbnail > .caption { 18 | display: flex; 19 | flex: .9 .1 auto; 20 | flex-direction: column; 21 | } 22 | .row.equal-height > [class*='col-'] > .thumbnail > .caption > .flex-text { 23 | flex-grow: 1; 24 | } 25 | .row.equal-height > [class*='col-'] > .thumbnail > img { 26 | width: 400px; 27 | height: 100%; /* force image's height */ 28 | 29 | /* force image fit inside it's "box" */ 30 | -webkit-object-fit: cover; 31 | -moz-object-fit: cover; 32 | -ms-object-fit: cover; 33 | -o-object-fit: cover; 34 | object-fit: cover; 35 | } 36 | } 37 | 38 | .row.extra-bottom-padding{ 39 | margin-bottom: 20px; 40 | } 41 | 42 | 43 | .topnavicons { 44 | margin-left: 10% !important; 45 | } 46 | 47 | .topnavicons li { 48 | margin-left: 0px !important; 49 | min-width: 100px; 50 | text-align: center; 51 | } 52 | 53 | .topnavicons .thumbnail { 54 | margin-right: 10px; 55 | border: none; 56 | box-shadow: none; 57 | text-align: center; 58 | font-size: 85%; 59 | font-weight: bold; 60 | line-height: 10px; 61 | height: 100px; 62 | } 63 | 64 | .topnavicons .thumbnail img { 65 | display: block; 66 | margin-left: auto; 67 | margin-right: auto; 68 | } 69 | 70 | 71 | /* Table with a scrollbar */ 72 | .bodycontainer { max-height: 800px; width: 100%; margin: 0; padding: 0; overflow-y: auto; } 73 | .table-scrollable { margin: 0; padding: 0; } 74 | 75 | .label { 76 | color: #E74C3C; 77 | font-size: 100%; 78 | font-weight: bold; 79 | width: 100%; 80 | height: 100%; 81 | text-align: left; 82 | vertical-align: middle; 83 | padding: 6px; 84 | margin: 2px; 85 | overflow-x: auto; 86 | overflow-y: auto; 87 | } 88 | 89 | div.body { 90 | max-width: 1080px; 91 | } 92 | 93 | table.longtable.align-default{ 94 | text-align: left; 95 | } 96 | -------------------------------------------------------------------------------- /docs/_static/pysal_favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pysal/spaghetti/72ae8892a9ae62d0d18aecc310709b0e00d5f090/docs/_static/pysal_favicon.ico -------------------------------------------------------------------------------- /docs/_static/references.bib: -------------------------------------------------------------------------------- 1 | %% Saved with string encoding Unicode (UTF-8) 2 | 3 | @article{Dijkstra1959a, 4 | abstract = {We consider a graph with n vertices, all pairs of which are connected by an edge; each edge is of given positive length. The following two basic problems are solved. Problem 1: construct the tree of minimal total length between the n vertices. (A tree is a graph with one and only one path between any two vertices.) Problem 2: find the path of minimal total length between two given vertices.}, 5 | author = {Dijkstra, E. W.}, 6 | doi = {10.1007/BF01386390}, 7 | isbn = {0029-599X (Print) 0945-3245 (Online)}, 8 | issn = {0029-599X}, 9 | journal = {Numerische Mathematik}, 10 | number = {1}, 11 | pages = {269--271}, 12 | pmid = {18215627}, 13 | title = {{A Note on Two Problems in Connexion with Graphs}}, 14 | volume = {1}, 15 | year = {1959} 16 | } 17 | 18 | @article{Baddeley2020, 19 | author = {Baddeley, Adrian and Nair, Gopalan and Rakshit, Suman and McSwiggan, Greg and Davies, Tilman M.}, 20 | doi = {10.1016/j.spasta.2020.100435}, 21 | journal = {Spatial Statistics}, 22 | keywords = {Distance metric,K-function,Kernel density estimation,Nonparametric estimation,Pair correlation function,Stationary process}, 23 | number = {xxxx}, 24 | pages = {100435}, 25 | publisher = {Elsevier B.V.}, 26 | title = {{Analysing point patterns on networks - A review}}, 27 | year = {2020} 28 | } 29 | 30 | @article{doi:10.1111/j.1538-4632.2001.tb00448.x, 31 | author = {Okabe, Atsuyuki and Yamada, Ikuho}, 32 | title = {{The K-Function Method on a Network and Its Computational Implementation}}, 33 | journal = {Geographical Analysis}, 34 | volume = {33}, 35 | number = {3}, 36 | pages = {271-290}, 37 | doi = {10.1111/j.1538-4632.2001.tb00448.x}, 38 | abstract = {This paper proposes two statistical methods, called the network K-function method and the network cross K-function method, for analyzing the distribution of points on a network. First, by extending the ordinary K-function method defined on a homogeneous infinite plane with the Euclidean distance, the paper formulates the K-function method and the cross K-function method on a finite irregular network with the shortest-path distance. Second, the paper shows advantages of the network K-function methods, such as that the network K-function methods can deal with spatial point processes on a street network in a small district, and that they can exactly take the boundary effect into account. Third, the paper develops the computational implementation of the network K-functions, and shows that the computational order of the K-function method is O(n2Q log nQ) and that of the network cross K-function is O(nQ log U3Q), where nQ is the number of nodes of a network.}, 39 | year = {2001} 40 | } 41 | 42 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Chapter 3 43 | @inbook{doi:10.1002/9781119967101.ch3, 44 | author = {Okabe, Atsuyki and Sugihara, Kokichi}, 45 | publisher = {John Wiley \& Sons, Ltd}, 46 | isbn = {9781119967101}, 47 | title = {Basic Computational Methods for Network Spatial Analysis}, 48 | booktitle = {Spatial Analysis along Networks}, 49 | chapter = {3}, 50 | pages = {45-80}, 51 | doi = {10.1002/9781119967101.ch3}, 52 | year = {2012}, 53 | keywords = {basic computational methods in network spatial analysis, practical computational methods, data structures for one-layer networks, winged-edge data, retrieving local information, attribute data representation, winged-edge, new nodes inserted and existing nodes deleted, nonplanar network data structure, multiple-layered networks, basic geometric computations, time complexity of algorithm, and measuring efficiency, three basic computational methods, and minimum spanning tree}, 54 | abstract = {Summary This chapter contains sections titled: Data structures for one-layer networks Data structures for nonplanar networks Basic geometric computations Basic computational methods on networks} 55 | } 56 | 57 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Chapter 5 58 | @inbook{doi:10.1002/9781119967101.ch5, 59 | author = {Okabe, Atsuyki and Sugihara, Kokichi}, 60 | publisher = {John Wiley \& Sons, Ltd}, 61 | isbn = {9781119967101}, 62 | title = {Network Nearest-Neighbor Distance Methods}, 63 | booktitle = {Spatial Analysis along Networks}, 64 | chapter = {5}, 65 | pages = {101-118}, 66 | doi = {10.1002/9781119967101.ch5}, 67 | year = {2012}, 68 | keywords = {network nearest-neighbor distance methods, neighborhood, an underlying concept common to spatial methods, network-constrained NND, neighborhood and shortest-path distance, event to next nearest event, network NND method, NND extension on plane or planar NND, network auto NND and network cross NND method, network cross NND methods, straightforward extension, local cross NND to global cross NND, network NND for lines, computational methods, for auto NND and cross NND}, 69 | abstract = {Summary This chapter contains sections titled: Network auto nearest-neighbor distance methods Network cross nearest-neighbor distance methods Network nearest-neighbor distance method for lines Computational methods for the network nearest-neighbor distance methods} 70 | } 71 | 72 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Chapter 6 73 | @inbook{doi:10.1002/9781119967101.ch6, 74 | author = {Okabe, Atsuyki and Sugihara, Kokichi}, 75 | publisher = {John Wiley \& Sons, Ltd}, 76 | isbn = {9781119967101}, 77 | title = {Network K Function Methods}, 78 | booktitle = {Spatial Analysis along Networks}, 79 | chapter = {6}, 80 | pages = {119-136}, 81 | doi = {10.1002/9781119967101.ch6}, 82 | year = {2012}, 83 | keywords = {network K function methods, distance-based methods, point pattern analysis in spatial, NND and network K function, network K function, extension of K as planar K function, K function in spatial statistics, or Ripley's K function, the planar K function, most used in spatial analysis, network K function, network auto K and the network cross K function, auto K function with one type of point, and cross K with two, global Voronoi cross K, point of type A to collection of type B points, shortest-path distance and network geometric characteristics, shortest-path distances and corresponding Euclidean distances}, 84 | abstract = {Summary This chapter contains sections titled: Network auto K function methods Network cross K function methods Network K function methods in relation to geometric characteristics of a network Computational methods for the network K function methods} 85 | } 86 | 87 | @inbook{doi:10.1002/9780470549094.ch5, 88 | author = {ÓSullivan, D. and Unwin, D. J.}, 89 | publisher = {John Wiley \& Sons, Ltd}, 90 | isbn = {9780470549094}, 91 | title = {Point Pattern Analysis}, 92 | booktitle = {Geographic Information Analysis}, 93 | chapter = {5}, 94 | pages = {121-156}, 95 | doi = {10.1002/9780470549094.ch5}, 96 | year = {2010}, 97 | keywords = {point separation, standard deviation, observed VMR, estimation methods, computationally intensive}, 98 | abstract = {Summary This chapter contains sections titled: Introduction Describing a Point Pattern Assessing Point Patterns Statistically Monte Carlo Testing Conclusions} 99 | } 100 | 101 | @article{Ripley1976, 102 | author = {Ripley, Brian David}, 103 | doi = {10.2307/3212829}, 104 | journal = {Journal of Applied Probability}, 105 | number = {2}, 106 | pages = {255--266}, 107 | title = {{The Second-Order Analysis of Stationary Point Processes}}, 108 | volume = {13}, 109 | year = {1976} 110 | } 111 | 112 | @article{Ripley1977, 113 | author = {Ripley, Brian David}, 114 | journal = {Journal of the Royal Statistical Society}, 115 | number = {2}, 116 | pages = {172--212}, 117 | title = {{Modelling Spatial Patterns}}, 118 | volume = {39}, 119 | year = {1977}, 120 | doi = {10.1111/j.2517-6161.1977.tb01615.x} 121 | } 122 | 123 | @Article{pysal2007, 124 | author={Rey, Sergio J. and Anselin, Luc}, 125 | title={{PySAL: A Python Library of Spatial Analytical Methods}}, 126 | journal={The Review of Regional Studies}, 127 | year=2007, 128 | volume={37}, 129 | number={1}, 130 | pages={5-27}, 131 | keywords={Open Source; Software; Spatial}, 132 | url = {https://rrs.scholasticahq.com/article/8285.pdf} 133 | } 134 | 135 | @book{AnselinRey2014, 136 | address = {Chicago}, 137 | author = {Anselin, Luc and Rey, Sergio J.}, 138 | publisher = {{GeoDa Press}}, 139 | title = {{Modern Spatial Econometrics in Practice: A Guide to {GeoDa}, {GeoDaSpace} and {PySAL}}}, 140 | year = {2014} 141 | } 142 | 143 | @Article{rey_open_2015, 144 | Title = {Open {Geospatial} {Analytics} with {PySAL}}, 145 | Author = {Rey, Sergio J. and Anselin, Luc and Li, Xun and Pahle, Robert and Laura, Jason and Li, Wenwen and Koschinsky, Julia}, 146 | Journal = {{ISPRS International Journal of Geo-Information}}, 147 | Year = {2015}, 148 | Number = {2}, 149 | Pages = {815--836}, 150 | Volume = {4}, 151 | Keywords = {open science}, 152 | doi = {doi:10.3390/ijgi4020815} 153 | } 154 | 155 | @misc{esda:_2019, 156 | author={S.J. Rey and L.J. Wolf and W. Kang and P. Stephens and J. Laura and C. Schmidt and D. Arribas-Bel and S. Lumnitz and J.C. Duque and D.C. Folch and L. Anselin and N. Malizia and J.D. Gaboardi and F. Fernandes and M. Seth and mhwang4 and mlyons-tcc}, 157 | title={{pysal/esda}}, 158 | month=jul, 159 | year={2019}, 160 | publisher={Zenodo}, 161 | version={v2.1.1}, 162 | doi={10.5281/zenodo.3265190} 163 | } 164 | 165 | @article{doi:10.1111/gean.12211, 166 | author = {Gaboardi, James D. and Folch, David C. and Horner, Mark W.}, 167 | title = {{Connecting Points to Spatial Networks: Effects on Discrete Optimization Models}}, 168 | journal = {Geographical Analysis}, 169 | year = {2020}, 170 | volume = {52}, 171 | issue = {2}, 172 | pages = {299--322}, 173 | doi = {10.1111/gean.12211}, 174 | abstract = {To accommodate network allocation, population polygons are frequently transformed into singular, weighted centroids which are then integrated into the network either by snapping each centroid to the nearest network segment or by generating an artificial connector that becomes part of the network. In this article, an investigation of the connection method of network allocation is undertaken with two primary foci: (1) how the density of centroid connectors effects travel cost along the network; and (2) how the algorithms utilized to determine the placement of connectors are affected by the density of connectors. We hypothesize that both issues have an effect on network travel cost and, therefore, on network-based modeling. These hypotheses are tested on three nested spatial networks in the New England region of the United States. Two fundamental facility location models, the p-median problem, and p-center problem, are solved at each spatial extent while varying the density of connectors from one to four. When generating more than one connector thought must be given to the method of connection, the angle of dispersion, the acceptable tolerance of connector length, segment crossing, and saturated connectivity. A novel and thorough framework is proposed to address these concerns.} 175 | } 176 | 177 | @incollection{Cliff1981, 178 | address = {London}, 179 | author = {Cliff, A.D. and Haggett, P.}, 180 | booktitle = {Quantitative Geography: A British View}, 181 | chapter = {22}, 182 | editor = {Wrigley, N. and Bennett, R.J.}, 183 | pages = {225--234}, 184 | publisher = {Routledge {\&} Kegan Paul}, 185 | title = {{Graph Theory}}, 186 | year = {1981} 187 | } 188 | 189 | @article{Tansel1983a, 190 | author = {Tansel, Barbaros C. and Francis, Richard L .and Lowe, Timothy J.}, 191 | journal = {Management Science}, 192 | number = {4}, 193 | pages = {482--497}, 194 | title = {{State of the Art---Location on Networks: A survey. Part I: The p-center and p-median Problems}}, 195 | volume = {29}, 196 | year = {1983}, 197 | doi = {https://doi.org/10.1287/mnsc.29.4.482}, 198 | } 199 | 200 | @book{AhujaRavindraK, 201 | publisher = {Prentice Hall}, 202 | isbn = {013617549X}, 203 | year = {1993}, 204 | title = {{Network Flows: Theory, Algorithms, and Applications}}, 205 | language = {eng}, 206 | address = {Englewood Cliffs, N.J.}, 207 | author = {Ahuja, Ravindra K. and Magnanti, Thomas L. and Orlin, James B.}, 208 | keywords = {Network analysis (Planning); Mathematical optimization}, 209 | lccn = {92026702}, 210 | } 211 | 212 | @incollection{Labbe1995, 213 | author = {Labbé, Martine and Peeters, Dominique and Thisse, Jacques-Fran{\c{c}}ois}, 214 | booktitle = {Network Routing}, 215 | chapter = {7}, 216 | doi = {10.1016/S0927-0507(05)80111-2}, 217 | isbn = {9780444821416}, 218 | issn = {09270507}, 219 | pages = {551--624}, 220 | publisher = {Elsevier}, 221 | series = {Handbooks in Operations Research and Management Science}, 222 | title = {{Location on Networks}}, 223 | volume = {8}, 224 | year = {1995} 225 | } 226 | 227 | @incollection{Kuby2009, 228 | title = {{Network Analysis}}, 229 | editor = {Rob Kitchin and Nigel Thrift}, 230 | booktitle = {{International Encyclopedia of Human Geography}}, 231 | publisher = {Elsevier}, 232 | address = {Oxford}, 233 | pages = {391--398}, 234 | year = {2009}, 235 | isbn = {978-0-08-044910-4}, 236 | doi = {https://doi.org/10.1016/B978-008044910-4.00481-8}, 237 | author = {Kuby, M.J. and Roberts, T.D. and Upchurch, C.D. and S. Tierney} 238 | } 239 | 240 | @article{Barthelemy2011, 241 | author = {Barthélemy, Marc}, 242 | isbn = {0370-1573}, 243 | issn = {03701573}, 244 | journal = {Physics Reports}, 245 | number = {1--3}, 246 | pages = {1--101}, 247 | pmid = {24906003}, 248 | publisher = {Elsevier B.V.}, 249 | title = {{Spatial networks}}, 250 | volume = {499}, 251 | year = {2011}, 252 | doi = {https://doi.org/10.1016/j.physrep.2010.11.002}, 253 | } 254 | 255 | @book{Okabe2012, 256 | address = {West Sussex, UK}, 257 | author = {Okabe, Atsuyki and Sugihara, Kokichi}, 258 | publisher = {John Wiley {\&} Sons, Inc.}, 259 | title = {{Spatial Analysis Along Networks}}, 260 | year = {2012}, 261 | doi = {10.1002/9781119967101} 262 | } 263 | 264 | @book{daskin2013, 265 | author = {Mark S. Daskin}, 266 | publisher = {John Wiley \& Sons, Ltd}, 267 | isbn = {9781118537015}, 268 | title = {{Network and Discrete Location: Models, Algorithms, and Applications, Second Edition}}, 269 | doi = {10.1002/9781118537015}, 270 | year = {2013}, 271 | keywords = {location taxonomy, analytic models, continuous models, discrete location models, network models, maximum covering model, ambulance location, landfill location}, 272 | } 273 | 274 | @article{Ducruet2014, 275 | author = {Ducruet, César and Beauguitte, Laurent}, 276 | doi = {10.1007/s11067-013-9222-6}, 277 | issn = {15729427}, 278 | journal = {Networks and Spatial Economics}, 279 | number = {3--4}, 280 | pages = {297--316}, 281 | title = {{Spatial Science and Network Science: Review and Outcomes of a Complex Relationship}}, 282 | volume = {14}, 283 | year = {2014} 284 | } 285 | 286 | @article{Weber2016, 287 | author = {Weber, Joe}, 288 | doi = {10.1080/00330124.2015.1106324}, 289 | issn = {0033-0124}, 290 | journal = {The Professional Geographer}, 291 | number = {January}, 292 | pages = {1--11}, 293 | title = {{The Properties of Topological Network Connectivity Measures and Their Application to U.S. Urban Freeway Networks}}, 294 | volume = {0124}, 295 | year = {2016} 296 | } 297 | 298 | @article{Okabe2006a, 299 | author = {Okabe, Atsuyuki and Okunuki, Keiichi and Shiode, Shino}, 300 | doi = {10.1111/j.0016-7363.2005.00674.x}, 301 | isbn = {1538-4632}, 302 | issn = {0016-7363}, 303 | journal = {Geographical Analysis}, 304 | pages = {57--66}, 305 | title = {{SANET: A Toolbox for Spatial Analysis on a Network}}, 306 | volume = {38}, 307 | year = {2006} 308 | } 309 | 310 | @inproceedings{Hagberg2008, 311 | address = {Pasadena, CA USA}, 312 | author = {Hagberg, A.A. and Schult, D.A. and Swart, P.J.}, 313 | booktitle = {Proceedings of the 7th Python in Science Conference (SciPy 2008)}, 314 | editor = {Varoquaux, G. and Vaught, T. and Millman, J.}, 315 | isbn = {3333333333}, 316 | issn = {1540-9295}, 317 | pages = {11--15}, 318 | title = {{Exploring Network Structure, Dynamics, and Function using NetworkX}}, 319 | year = {2008} 320 | } 321 | 322 | @article{Foti2012, 323 | author = {Foti, Fletcher and Waddell, Paul and Luxen, Dennis}, 324 | journal = {{4th Transportation Research Board Conference on Innovations in Travel Modeling (ITM)}}, 325 | pages = {1--14}, 326 | title = {{A Generalized Computational Framework for Accessibility: From the Pedestrian to the Metropolitan Scale}}, 327 | year = {2012} 328 | } 329 | 330 | @article{Boeing2017, 331 | author = {Boeing, Geoff}, 332 | doi = {10.1016/j.compenvurbsys.2017.05.004}, 333 | issn = {01989715}, 334 | journal = {Computers, Environment and Urban Systems}, 335 | pages = {126--139}, 336 | publisher = {Elsevier Ltd}, 337 | title = {{OSMnx: New Methods for Acquiring, Constructing, Analyzing, and Visualizing Complex Street Networks}}, 338 | volume = {65}, 339 | year = {2017} 340 | } 341 | 342 | @article{Hakimi1964, 343 | author = {Hakimi, S. L.}, 344 | doi = {10.1287/opre.12.3.450}, 345 | isbn = {0030364X}, 346 | issn = {0030-364X}, 347 | journal = {Operations Research}, 348 | number = {3}, 349 | pages = {450--459}, 350 | pmid = {8598587}, 351 | title = {{Optimum Locations of Switching Centers and the Absolute Centers and Medians of a Graph}}, 352 | volume = {12}, 353 | year = {1964} 354 | } 355 | 356 | @article{ReVelle1970, 357 | author = {ReVelle, C. S. and Swain, R.W.}, 358 | journal = {Geographical Analysis}, 359 | number = {1}, 360 | pages = {30--42}, 361 | title = {{Central Facilities Location}}, 362 | volume = {2}, 363 | year = {1970} 364 | } 365 | 366 | @article{Toregas1971, 367 | author = {Toregas, Constantine and Swain, R. and ReVelle, C. S. and Bergman, L.}, 368 | doi = {10.1287/opre.19.6.1363}, 369 | isbn = {0030364X}, 370 | issn = {0030-364X}, 371 | journal = {Operations Research}, 372 | number = {6}, 373 | pages = {1363--1373}, 374 | title = {{The Location of Emergency Service Facilities}}, 375 | volume = {19}, 376 | year = {1971} 377 | } 378 | 379 | @article{Toregas1972, 380 | author = {Toregas, Constantine and ReVelle, Charles S.}, 381 | doi = {10.1017/CBO9781107415324.004}, 382 | isbn = {9788578110796}, 383 | issn = {1098-6596}, 384 | journal = {Papers of the Regional Science Association}, 385 | number = {1}, 386 | pages = {133 -- 144}, 387 | pmid = {25246403}, 388 | title = {{Optimal Location Under Time or Distance Constraints}}, 389 | volume = {28}, 390 | year = {1972} 391 | } 392 | 393 | @article{Church1974, 394 | author = {Church, Richard L. and ReVelle, C.S.}, 395 | doi = {doi.org/10.1111/j.1435-5597.1974.tb00902.x}, 396 | isbn = {10568190 (ISSN)}, 397 | issn = {10568190}, 398 | journal = {Papers in Regional Science Association}, 399 | pages = {101--118}, 400 | title = {{The Maximal Covering Location Problem}}, 401 | volume = {32}, 402 | year = {1974} 403 | } 404 | 405 | @article{ReVelle2005, 406 | author = {ReVelle, C. S. and Eiselt, H. A.}, 407 | doi = {10.1016/j.ejor.2003.11.032}, 408 | isbn = {1410516709}, 409 | issn = {01679295}, 410 | journal = {European Journal of Operational Research}, 411 | pages = {1--19}, 412 | title = {{Location analysis: A synthesis and survey}}, 413 | volume = {165}, 414 | year = {2005} 415 | } 416 | 417 | @misc{tom_russell_2019_3379659, 418 | author = {Tom Russell and Elco Koks}, 419 | title = {{tomalrussell/snkit: v1.6.0}}, 420 | month = aug, 421 | year = {2019}, 422 | publisher = {Zenodo}, 423 | version = {v1.6.0}, 424 | doi = {10.5281/zenodo.3379659} 425 | } 426 | 427 | 428 | @article{Bektas2014, 429 | author = {Bektaş, Tolga and Gouveia, Luis}, 430 | doi = {10.1016/j.ejor.2013.07.038}, 431 | issn = {03772217}, 432 | journal = {European Journal of Operational Research}, 433 | number = {3}, 434 | pages = {820--832}, 435 | title = {{Requiem for the Miller-Tucker-Zemlin subtour elimination constraints?}}, 436 | volume = {236}, 437 | year = {2014} 438 | } 439 | @article{Miller1960, 440 | author = {Miller, C. E. and Tucker, A. W. and Zemlin, R. A.}, 441 | doi = {10.1145/321043.321046}, 442 | issn = {1557735X}, 443 | journal = {Journal of the ACM (JACM)}, 444 | number = {4}, 445 | pages = {326--329}, 446 | title = {{Integer Programming Formulation of Traveling Salesman Problems}}, 447 | volume = {7}, 448 | year = {1960} 449 | } 450 | 451 | @article{Flood1956, 452 | author = {Flood, Merrill M.}, 453 | journal = {Operations Research}, 454 | number = {1}, 455 | pages = {61--75}, 456 | title = {{The Traveling-Salesman Problem}}, 457 | volume = {4}, 458 | year = {1956} 459 | } 460 | 461 | @article{Dantzig1954, 462 | author = {Dantzig, G. and Fulkerson, R. and Johnson, S.}, 463 | journal = {Journal of the Operational Research Society of America}, 464 | number = {4}, 465 | pages = {393--410}, 466 | title = {{Solution of a Large-Scale Traveling-Salesman Problem}}, 467 | volume = {2}, 468 | year = {1954} 469 | } 470 | 471 | @book{Miller2001, 472 | address = {New York}, 473 | author = {Miller, Harvey J. and Shaw, Shih-Lung}, 474 | publisher = {Oxford University Press}, 475 | title = {{Geographic Information Systems for Transportation}}, 476 | year = {2001} 477 | } 478 | 479 | @book{Gass2005, 480 | address = {New York}, 481 | author = {Gass, Saul I. and Assad, Arjang A.}, 482 | publisher = {Springer}, 483 | title = {{An Annotated Timeline of Operations Research: An Informal History}}, 484 | year = {2005} 485 | } 486 | 487 | @misc{Cummings2000, 488 | author = {Cummings, Nigel}, 489 | title = {A brief History of the Travelling Salesman Problem}, 490 | editor = {The Operational Research Society}, 491 | month = {jun}, 492 | year = {2000}, 493 | url = {https://www.theorsociety.com/about-or/or-methods/heuristics/a-brief-history-of-the-travelling-salesman-problem/}, 494 | note = {Accessed: January, 2020}, 495 | } 496 | 497 | @book{Church2009, 498 | address = {Hoboken}, 499 | author = {Church, Richard L. and Murray, Alan T.}, 500 | publisher = {John Wiley {\&} Sons, Inc.}, 501 | title = {{Business Site Selection, Locational Analysis, and GIS}}, 502 | year = {2009} 503 | } 504 | 505 | @article{Koopmans1949, 506 | author = {Koopmans, Tjalling}, 507 | journal = {Econometrica}, 508 | pages = {136--146}, 509 | title = {{Optimum Utilization of the Transportation System}}, 510 | volume = {17}, 511 | year = {1949} 512 | } 513 | 514 | @book{Phillips1981a, 515 | address = {Englewood Cliffs, NJ}, 516 | author = {Phillips, Don T. and Garcia-Diaz, Alberto}, 517 | publisher = {Prentice Hall}, 518 | title = {{Fundamentals of Network Analysis}}, 519 | year = {1981} 520 | } 521 | 522 | @article{Hitchcock1941, 523 | author = {Hitchcock, Frank L.}, 524 | doi = {10.1017/CBO9781107415324.004}, 525 | isbn = {9788578110796}, 526 | issn = {1098-6596}, 527 | journal = {Journal of Math and Physics}, 528 | number = {1}, 529 | pages = {224--230}, 530 | pmid = {25246403}, 531 | title = {{The Distribution of a Product from Several Sources to Numerous Localities}}, 532 | volume = {20}, 533 | year = {1941} 534 | } 535 | 536 | @article{GrahamHell_1985, 537 | author = {Graham, R. L. and Hell, Pavol}, 538 | title = {On the History of the Minimum Spanning Tree Problem}, 539 | year = {1985}, 540 | volume = {7}, 541 | number = {1}, 542 | doi = {10.1109/MAHC.1985.10011}, 543 | journal = {IEEE Annals of the History of Computing}, 544 | pages = {43-57} 545 | } 546 | 547 | @article{Moran1950, 548 | author = {Moran, P. A. P.}, 549 | doi = {10.2307/2332142}, 550 | journal = {Biometrika}, 551 | number = {1/2}, 552 | pages = {17-23}, 553 | title = {{Notes on Continuous Stochastic Phenomena}}, 554 | volume = {37}, 555 | year = {1950} 556 | } 557 | 558 | @article{Getis1992, 559 | author = {Getis, Arthur and Ord, J. K.}, 560 | doi = {10.1111/j.1538-4632.1992.tb00261.x}, 561 | journal = {Geographical Analysis}, 562 | number = {3}, 563 | pages = {189--206}, 564 | title = {{The Analysis of Spatial Association by Use of Distance Statistics}}, 565 | volume = {24}, 566 | year = {1992} 567 | } 568 | 569 | @article{Lumnitz2020, 570 | author = {Lumnitz, Stefanie and Arribas-Bell, Dani and Cortes, Renan X. and Gaboardi, James D. and Griess, Verena and Kang, Wei and Oshan, Taylor M. and Wolf, Levi and Rey, Sergio}, 571 | doi = {10.21105/joss.01882}, 572 | journal = {Journal of Open Source Software}, 573 | number = {47}, 574 | pages = {1--4}, 575 | title = {splot - visual analytics for spatial statistics}, 576 | volume = {5}, 577 | year = {2020} 578 | } 579 | 580 | @misc{splot:package:_2020, 581 | author = {Stefanie Lumnitz and 582 | Dani Arribas-Bel and 583 | Renan Xavier Cortes and 584 | James Gaboardi and 585 | Verena Griess and 586 | Wei Kang and 587 | Taylor Oshan and 588 | Levi John Wolf and 589 | Sergio Rey}, 590 | title = {splot - visual analytics for spatial statistics}, 591 | month = mar, 592 | year = 2020, 593 | publisher = {Zenodo}, 594 | version = {v.1.1.3}, 595 | doi = {10.5281/zenodo.3724199}, 596 | } 597 | 598 | @article{Anselin1995, 599 | author = {Anselin, Luc}, 600 | doi = {10.1111/j.1538-4632.1995.tb00338.x}, 601 | journal = {Geographical Analysis}, 602 | number = {2}, 603 | pages = {93--115}, 604 | title = {{Local indicators of spatial association --- LISA}}, 605 | volume = {27}, 606 | year = {1995} 607 | } 608 | 609 | @book{moran:_cliff81, 610 | Address = {London}, 611 | Author = {Cliff, A.D. and Ord, J.K.}, 612 | Publisher = {Pion}, 613 | Title = {Spatial Processes: Models and Applications}, 614 | Year = {1981} 615 | } 616 | 617 | @article{Gaboardi2021, 618 | doi = {10.21105/joss.02826}, 619 | year = {2021}, 620 | publisher = {{The Open Journal}}, 621 | volume = {6}, 622 | number = {62}, 623 | pages = {2826}, 624 | author = {James D. Gaboardi and Sergio Rey and Stefanie Lumnitz}, 625 | title = {{spaghetti: spatial network analysis in PySAL}}, 626 | journal = {{Journal of Open Source Software}} 627 | } 628 | 629 | @article{Rey2021, 630 | author = {Rey, S.J. and Anselin, L. and Amaral, P. and Arribas-Bel, D. and Cortes, R.X. and Gaboardi, J.D. and Kang, W. and Knaap, E. and Li, Z. and Lumnitz, S. and Oshan, T.M. and Shao, H. and Wolf, L.J.}, 631 | title = {{The PySAL Ecosystem: Philosophy and Implementation}}, 632 | journal = {Geographical Analysis}, 633 | year = {2021}, 634 | doi = {10.1111/gean.12276}, 635 | abstract = {PySAL is a library for geocomputation and spatial data science. Written in Python, the library has a long history of supporting novel scholarship and broadening methodological impacts far afield of academic work. Recently, many new techniques, methods of analyses, and development modes have been implemented, making the library much larger and more encompassing than that previously discussed in the literature. As such, we provide an introduction to the library as it stands now, as well as the scientific and conceptual underpinnings of its core set of components. Finally, we provide a prospective look at the library's future evolution.} 636 | } 637 | -------------------------------------------------------------------------------- /docs/api.rst: -------------------------------------------------------------------------------- 1 | .. _api_ref: 2 | 3 | .. currentmodule:: spaghetti 4 | 5 | API reference 6 | ============= 7 | 8 | .. _network_api: 9 | 10 | 11 | Network feature instantiation 12 | ----------------------------- 13 | 14 | .. autosummary:: 15 | :toctree: generated/ 16 | 17 | spaghetti.Network 18 | spaghetti.PointPattern 19 | 20 | 21 | Network feature extraction and creation 22 | --------------------------------------- 23 | 24 | .. autosummary:: 25 | :toctree: generated/ 26 | 27 | spaghetti.extract_component 28 | spaghetti.spanning_tree 29 | spaghetti.element_as_gdf 30 | spaghetti.regular_lattice 31 | 32 | 33 | Save and load a network 34 | ----------------------- 35 | 36 | .. autosummary:: 37 | :toctree: generated/ 38 | 39 | spaghetti.network.Network.savenetwork 40 | spaghetti.network.Network.loadnetwork 41 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # SPAGHETTI documentation build configuration file, based on `giddy`. This file is 4 | # execfile()d with the current directory set to its containing dir. Note that not all 5 | # possible configuration values are present in this autogenerated file. All configuration 6 | # values have a default; values that are commented out serve to show the default. If 7 | # extensions (or modules to document with autodoc) are in another directory, add these 8 | # directories to sys.path here. If the directory is relative to the documentation root, 9 | # use os.path.abspath to make it absolute, like shown here. 10 | 11 | 12 | import sphinx_bootstrap_theme 13 | import spaghetti 14 | 15 | # -- General configuration ------------------------------------------------ 16 | 17 | # If your documentation needs a minimal Sphinx version, state it here. 18 | # 19 | # needs_sphinx = '1.0' 20 | # Add any Sphinx extension module names here, as strings. They can be 21 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 22 | # ones. 23 | extensions = [ #'sphinx_gallery.gen_gallery', 24 | "sphinx.ext.autodoc", 25 | "sphinx.ext.autosummary", 26 | "sphinx.ext.viewcode", 27 | "sphinxcontrib.bibtex", 28 | "sphinx.ext.mathjax", 29 | "sphinx.ext.doctest", 30 | "sphinx.ext.intersphinx", 31 | "numpydoc", 32 | "nbsphinx", 33 | ] 34 | bibtex_bibfiles = ["_static/references.bib"] 35 | 36 | # Add any paths that contain templates here, relative to this directory. 37 | templates_path = ["_templates"] 38 | 39 | # The suffix(es) of source filenames. 40 | # You can specify multiple suffix as a list of string: 41 | # 42 | # source_suffix = ['.rst', '.md'] 43 | source_suffix = ".rst" 44 | 45 | # The master toctree document. 46 | master_doc = "index" 47 | 48 | # General information about the project. 49 | project = "spaghetti" 50 | copyright = "2017-, pysal developers" 51 | author = "pysal developers" 52 | 53 | # The version info for the project you're documenting, acts as replacement for 54 | # |version| and |release|, also used in various other places throughout the 55 | # built documents. 56 | # 57 | # The full version. 58 | version = spaghetti.__version__ 59 | release = spaghetti.__version__ 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 = "en" 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 = ["_build", "Thumbs.db", ".DS_Store", "tests/*"] 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 = False 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 | html_theme = "bootstrap" 86 | html_theme_path = sphinx_bootstrap_theme.get_html_theme_path() 87 | html_title = "%s v%s Manual" % (project, version) 88 | 89 | # (Optional) Logo of your package. Should be small enough to fit the navbar (ideally 24x24). 90 | # Path should be relative to the ``_static`` files directory. 91 | # html_logo = "_static/images/package_logo.jpg" 92 | 93 | # (Optional) PySAL favicon 94 | html_favicon = "_static/images/pysal_favicon.ico" 95 | 96 | 97 | # Theme options are theme-specific and customize the look and feel of a theme 98 | # further. For a list of options available for each theme, see the 99 | # documentation. 100 | # 101 | html_theme_options = { 102 | # Navigation bar title. (Default: ``project`` value) 103 | "navbar_title": project, 104 | # Render the next and previous page links in navbar. (Default: true) 105 | "navbar_sidebarrel": False, 106 | # Render the current pages TOC in the navbar. (Default: true) 107 | #'navbar_pagenav': True, 108 | #'navbar_pagenav': False, 109 | # No sidebar 110 | "nosidebar": True, 111 | # Tab name for the current pages TOC. (Default: "Page") 112 | #'navbar_pagenav_name': "Page", 113 | # Global TOC depth for "site" navbar tab. (Default: 1) 114 | # Switching to -1 shows all levels. 115 | "globaltoc_depth": 2, 116 | # Include hidden TOCs in Site navbar? 117 | # 118 | # Note: If this is "false", you cannot have mixed ``:hidden:`` and 119 | # non-hidden ``toctree`` directives in the same page, or else the build 120 | # will break. 121 | # 122 | # Values: "true" (default) or "false" 123 | "globaltoc_includehidden": "true", 124 | # HTML navbar class (Default: "navbar") to attach to
element. 125 | # For black navbar, do "navbar navbar-inverse" 126 | #'navbar_class': "navbar navbar-inverse", 127 | # Fix navigation bar to top of page? 128 | # Values: "true" (default) or "false" 129 | "navbar_fixed_top": "true", 130 | # Location of link to source. 131 | # Options are "nav" (default), "footer" or anything else to exclude. 132 | "source_link_position": "footer", 133 | # Bootswatch (http://bootswatch.com/) theme. 134 | # 135 | # Options are nothing (default) or the name of a valid theme 136 | # such as "amelia" or "cosmo", "yeti", "flatly". 137 | "bootswatch_theme": "yeti", 138 | # Choose Bootstrap version. 139 | # Values: "3" (default) or "2" (in quotes) 140 | "bootstrap_version": "3", 141 | # Navigation bar menu 142 | "navbar_links": [ 143 | ("Installation", "installation"), 144 | ("Tutorials", "tutorials"), 145 | ("API", "api"), 146 | ("References", "references"), 147 | ], 148 | } 149 | 150 | # Add any paths that contain custom static files (such as style sheets) here, 151 | # relative to this directory. They are copied after the builtin static files, 152 | # so a file named "default.css" will overwrite the builtin "default.css". 153 | html_static_path = ["_static"] 154 | 155 | # Custom sidebar templates, maps document names to template names. 156 | # html_sidebars = {} 157 | # html_sidebars = {'sidebar': ['localtoc.html', 'sourcelink.html', 'searchbox.html']} 158 | 159 | # -- Options for HTMLHelp output ------------------------------------------ 160 | 161 | # Output file base name for HTML help builder. 162 | htmlhelp_basename = project + "doc" 163 | 164 | 165 | # -- Options for LaTeX output --------------------------------------------- 166 | 167 | latex_elements = { 168 | # The paper size ('letterpaper' or 'a4paper'). 169 | # 170 | # 'papersize': 'letterpaper', 171 | # The font size ('10pt', '11pt' or '12pt'). 172 | # 173 | # 'pointsize': '10pt', 174 | # Additional stuff for the LaTeX preamble. 175 | # 176 | # 'preamble': '', 177 | # Latex figure (float) alignment 178 | # 179 | # 'figure_align': 'htbp', 180 | } 181 | 182 | # Grouping the document tree into LaTeX files. List of tuples 183 | # (source start file, target name, title, 184 | # author, documentclass [howto, manual, or own class]). 185 | latex_documents = [ 186 | ( 187 | master_doc, 188 | "%s.tex" % project, 189 | "%s Documentation" % project, 190 | "pysal developers", 191 | "manual", 192 | ) 193 | ] 194 | 195 | 196 | # -- Options for manual page output --------------------------------------- 197 | 198 | # One entry per manual page. List of tuples 199 | # (source start file, name, description, authors, manual section). 200 | man_pages = [(master_doc, "%s" % project, "%s Documentation" % project, [author], 1)] 201 | 202 | 203 | # -- Options for Texinfo output ------------------------------------------- 204 | 205 | # Grouping the document tree into Texinfo files. List of tuples 206 | # (source start file, target name, title, author, 207 | # dir menu entry, description, category) 208 | texinfo_documents = [ 209 | ( 210 | master_doc, 211 | "%s" % project, 212 | "%s Documentation" % project, 213 | author, 214 | "%s" % project, 215 | "SPAtial GrapHs: nETworks, Topology, & Inference", 216 | "Miscellaneous", 217 | ) 218 | ] 219 | 220 | 221 | # ----------------------------------------------------------------------------- 222 | # Autosummary 223 | # ----------------------------------------------------------------------------- 224 | 225 | # Generate the API documentation when building 226 | autosummary_generate = True 227 | 228 | # avoid showing members twice 229 | numpydoc_show_class_members = False 230 | numpydoc_use_plots = True 231 | class_members_toctree = True 232 | numpydoc_show_inherited_class_members = True 233 | numpydoc_xref_param_type = True 234 | 235 | # automatically document class members 236 | autodoc_default_options = {"members": True, "undoc-members": True} 237 | 238 | # display the source code for Plot directive 239 | plot_include_source = True 240 | 241 | 242 | def setup(app): 243 | app.add_css_file("pysal-styles.css") 244 | 245 | 246 | # Example configuration for intersphinx: refer to the Python standard library. 247 | intersphinx_mapping = { 248 | "esda": ("https://pysal.org/esda/", None), 249 | "geopandas": ("https://geopandas.org/en/latest/", None), 250 | "libpysal": ("https://pysal.org/libpysal/", None), 251 | "matplotlib": ("https://matplotlib.org/stable/", None), 252 | "networkx": ("https://networkx.org/documentation/stable/", None), 253 | "numpy": ("https://numpy.org/doc/stable/", None), 254 | "pandas": ("https://pandas.pydata.org/pandas-docs/stable/", None), 255 | "pointpats": ("https://pysal.org/pointpats/", None), 256 | "python": ("https://docs.python.org/3.12/", None), 257 | "scipy": ("https://docs.scipy.org/doc/scipy/", None), 258 | } 259 | 260 | # This is processed by Jinja2 and inserted before each notebook 261 | nbsphinx_prolog = r""" 262 | {% set docname = env.doc2path(env.docname, base=None) %} 263 | 264 | .. only:: html 265 | 266 | .. role:: raw-html(raw) 267 | :format: html 268 | 269 | .. nbinfo:: 270 | 271 | This page was generated from `{{ docname }}`__. 272 | Interactive online version: 273 | :raw-html:`Binder badge` 274 | 275 | __ https://github.com/pysal/spaghetti/blob/main/{{ docname }} 276 | 277 | .. raw:: latex 278 | 279 | \nbsphinxstartnotebook{\scriptsize\noindent\strut 280 | \textcolor{gray}{The following section was generated from 281 | \sphinxcode{\sphinxupquote{\strut {{ docname | escape_latex }}}} \dotfill}} 282 | """ 283 | 284 | # This is processed by Jinja2 and inserted after each notebook 285 | nbsphinx_epilog = r""" 286 | .. raw:: latex 287 | 288 | \nbsphinxstopnotebook{\scriptsize\noindent\strut 289 | \textcolor{gray}{\dotfill\ \sphinxcode{\sphinxupquote{\strut 290 | {{ env.doc2path(env.docname, base='doc') | escape_latex }}}} ends here.}} 291 | """ 292 | 293 | # List of arguments to be passed to the kernel that executes the notebooks: 294 | nbsphinx_execute_arguments = [ 295 | "--InlineBackend.figure_formats={'svg', 'pdf'}", 296 | "--InlineBackend.rc={'figure.dpi': 96}", 297 | ] 298 | 299 | 300 | mathjax3_config = { 301 | "TeX": {"equationNumbers": {"autoNumber": "AMS", "useLabelIds": True}}, 302 | } 303 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. documentation master file 2 | 3 | .. raw:: html 4 | 5 | spaghetti logo 12 | 13 | `spaghetti` 14 | =========== 15 | 16 | **SPA**\ tial **G**\ rap\ **H**\ s: n\ **ET**\ works, **T**\ opology, & **I**\ nference 17 | --------------------------------------------------------------------------------------- 18 | 19 | `Spaghetti` is an open-source Python library for the analysis of network-based 20 | spatial data. Originating from the `network` module in `PySAL (Python Spatial 21 | Analysis Library) `_, it is under active development for the 22 | inclusion of newly proposed methods for building graph-theoretic networks and 23 | the analysis of network events. An installation guide, API reference, 24 | and usage tutorials are provided here through the links above. 25 | 26 | .. raw:: html 27 | 28 | 64 |
65 | 66 | History 67 | ------- 68 | 69 | `Spaghetti` was 70 | created and has evolved in line with the Python Spatial Analysis Library ecosystem for 71 | the specific purpose of utilizing the functionality of spatial weights in 72 | `libpysal `_ for generating network segment contiguity objects. 73 | The PySAL project was started in the mid-2000s when installation was difficult to maintain. 74 | Due to the non-triviality of relying on dependencies to secondary packages, a conscious 75 | decision was made to limit dependencies and build native PySAL data structures in cases 76 | where at all possible. Therefore, the original `pysal.network` submodule was developed to 77 | address the need for integrating support for network data structures with PySAL weights 78 | data structures, with the target audience being spatial data scientists and anyone 79 | interested in investigating network-centric phenomena within PySAL. Owing to the 80 | co-development of network functionality found within `spaghetti` and the evolution of 81 | the wider PySAL ecosystem, today, the package provides specialized network functionality 82 | that easily integrates with the rest of PySAL. This allows users of `spaghetti`’s network 83 | functionality to access spatial analysis functionality that complements network analysis, 84 | such as spatial statistical tools with `esda` and integration with core components of 85 | `libpysal`: `libpysal.weights` (mentioned above), 86 | `libpysal.cg` (computational geometry and data structures), 87 | `libpysal.io` (input-output), and `libpysal.examples` (built-in example data). 88 | 89 | Development 90 | ----------- 91 | 92 | Development of `spaghetti` is hosted on GitHub_. 93 | 94 | Support 95 | ------- 96 | 97 | All questions, comments, & discussions should happen in a public forum, where possible. 98 | Please start a `discussion `_ for questions, talk to us in `PySAL's Discord channel `_, or open an `issue `_ if there appears to be a 99 | bug. Private messages and emails will not be answered in a substantive manner. 100 | 101 | Citing `spaghetti` 102 | ------------------ 103 | 104 | If you use PySAL-spaghetti in a scientific publication, we would appreciate using the following BibTeX citations:: 105 | 106 | @article{Gaboardi2021, 107 | doi = {10.21105/joss.02826}, 108 | url = {https://doi.org/10.21105/joss.02826}, 109 | year = {2021}, 110 | publisher = {The Open Journal}, 111 | volume = {6}, 112 | number = {62}, 113 | pages = {2826}, 114 | author = {James D. Gaboardi and Sergio Rey and Stefanie Lumnitz}, 115 | title = {spaghetti: spatial network analysis in PySAL}, 116 | journal = {Journal of Open Source Software} 117 | } 118 | 119 | @misc{Gaboardi2018, 120 | author = {Gaboardi, James D. and Laura, Jay and Rey, Sergio and 121 | Wolf, Levi John and Folch, David C. and Kang, Wei and 122 | Stephens, Philip and Schmidt, Charles}, 123 | month = {oct}, 124 | year = {2018}, 125 | title = {pysal/spaghetti}, 126 | url = {https://github.com/pysal/spaghetti}, 127 | doi = {10.5281/zenodo.1343650}, 128 | keywords = {graph-theory,network-analysis,python,spatial-networks,topology} 129 | } 130 | 131 | Citing Work 132 | ----------- 133 | 134 | * **Lovelace, R**. `Open source tools for geographic analysis in transport planning`. Journal of Geographical Systems (2021): 1-32. https://doi.org/10.1007/s10109-020-00342-2. 135 | * **Rey, Sergio J., et al**. `The PySAL Ecosystem: Philosophy and Implementation`. Geographical Analysis (2022): 467-487. https://doi.org/10.1111/gean.12276. 136 | * **Barboza-Salerno, Gia E., and Jacquelyn CA Meshelemiah**. `Gun Violence on Walkable Routes to and from School: Recommendations for Policy and Practice`. Journal of Urban Health (2023): 1-16. https://doi.org/10.1007/s11524-023-00802-2 137 | * **Barboza, Gia, and Jacquelyn Meshelemiah**. `Danger, Students Beware, School Ahead! Gun Violence Exposure Near Schools in Compton, California`. (2023). https://doi.org/10.21203/rs.3.rs-2976516/v1 138 | 139 | Funding 140 | ------- 141 | 142 | This project is/was partially funded through: 143 | 144 | .. figure:: _static/images/ardc_logo.png 145 | :target: https://atlantardc.wordpress.com 146 | :width: 150 147 | :align: left 148 | 149 | The Atlanta Research Data Center: `A Polygon-Based Approach to Spatial Network Allocation `_ 150 | 151 | .. figure:: _static/images/nsf_logo.png 152 | :target: https://www.nsf.gov/index.jsp 153 | :width: 100 154 | :align: left 155 | 156 | National Science Foundation Award #1825768: `National Historical Geographic Information System `_ 157 | 158 | .. raw:: html 159 | 160 | PySAL Logo 167 | 168 | .. toctree:: 169 | :hidden: 170 | :maxdepth: 3 171 | :caption: Contents: 172 | 173 | Installation 174 | Tutorials 175 | API 176 | References 177 | 178 | .. _PySAL: https://github.com/pysal/pysal 179 | .. _GitHub: https://github.com/pysal/spaghetti 180 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | .. Installation 2 | 3 | Python 3.10_ is tested for support by `spaghetti`. Please make sure that you are operating in a Python >= 3.10 environment. 4 | 5 | Installation 6 | ============ 7 | 8 | Installing with ``conda`` via `spaghetti-feedstock`_ (highly recommended) 9 | ------------------------------------------------------------------------- 10 | 11 | To install `spaghetti` and all its dependencies, we recommend using the conda_ manager, specifically with the conda-forge_ channel. This can be obtained by installing the `Anaconda Distribution`_ (a free Python distribution for data science), or through miniconda_ (minimal distribution only containing Python and the ``conda`` package manager). 12 | 13 | Using ``conda``, `spaghetti` can be installed as follows:: 14 | 15 | $ conda config --set channel_priority strict 16 | $ conda install --channel conda-forge spaghetti 17 | 18 | Also, ``geopandas`` provides `a nice example`_ to create a fresh environment for working with spatial data. 19 | 20 | Installing with `Python Package Index`_ 21 | --------------------------------------- 22 | :: 23 | 24 | $ pip install spaghetti 25 | 26 | *or* download the source distribution (``.tar.gz``) and decompress it to your selected destination. Open a command shell and navigate to the decompressed folder. :: 27 | 28 | $ pip install . 29 | 30 | .. role:: rubric 31 | 32 | **Warning** 33 | 34 | When installing via ``pip``, you have to ensure that the required dependencies for `spaghetti` are installed on your operating system. Details on how to install these packages are linked here_. Using ``conda`` (above) avoids having to install the dependencies separately. 35 | 36 | Development Version 37 | ------------------- 38 | 39 | Install the most current development version of `spaghetti` by running:: 40 | 41 | $ pip install git+https://github.com/pysal/spaghetti 42 | 43 | You can also fork_ the `pysal/spaghetti`_ repo and create a local clone of your fork. By making changes to your local clone and submitting a pull request to `pysal/spaghetti`_, you can contribute to the spaghetti development. 44 | 45 | | 46 | 47 | .. _3.10: https://docs.python.org/3.10/ 48 | .. _spaghetti-feedstock: https://github.com/conda-forge/spaghetti-feedstock 49 | .. _a nice example: https://geopandas.readthedocs.io/en/latest/getting_started/install.html#creating-a-new-environment 50 | .. _conda: https://docs.conda.io/en/latest/ 51 | .. _conda-forge: https://conda-forge.org 52 | .. _Anaconda Distribution: https://docs.continuum.io/anaconda/ 53 | .. _miniconda: https://docs.conda.io/en/latest/miniconda.html 54 | .. _Python Package Index: https://pypi.org/project/spaghetti/ 55 | .. _pysal/spaghetti: https://github.com/pysal/spaghetti 56 | .. _fork: https://help.github.com/articles/fork-a-repo/ 57 | .. _here: https://github.com/pysal/spaghetti#requirements 58 | -------------------------------------------------------------------------------- /docs/references.rst: -------------------------------------------------------------------------------- 1 | .. reference for the docs 2 | 3 | References 4 | ========== 5 | 6 | .. bibliography:: _static/references.bib 7 | :all: 8 | -------------------------------------------------------------------------------- /docs/tutorials.rst: -------------------------------------------------------------------------------- 1 | .. tutorials 2 | 3 | 4 | Tutorials 5 | ========= 6 | 7 | .. toctree:: 8 | :maxdepth: 1 9 | :caption: Basic functionality: 10 | 11 | notebooks/quickstart.ipynb 12 | notebooks/pointpattern-attributes.ipynb 13 | notebooks/connected-components.ipynb 14 | notebooks/shortest-path-visualization.ipynb 15 | 16 | .. toctree:: 17 | :maxdepth: 1 18 | :caption: Advanced functionality and known limitations: 19 | 20 | notebooks/network-segmentation.ipynb 21 | notebooks/network-spatial-dependence.ipynb 22 | notebooks/network-spatial-weights.ipynb 23 | notebooks/spanning-trees.ipynb 24 | notebooks/caveats.ipynb 25 | 26 | .. toctree:: 27 | :maxdepth: 1 28 | :caption: Integrations & applications: 29 | 30 | notebooks/network-spatial-autocorrelation.ipynb 31 | notebooks/facility-location.ipynb 32 | notebooks/transportation-problem.ipynb 33 | notebooks/tsp.ipynb 34 | 35 | Citations: 36 | * Advanced functionality: 37 | 38 | * Spatial Dependence: 39 | :cite:`Ripley1976`, :cite:`Ripley1977`, :cite:`doi:10.1002/9780470549094.ch5`, :cite:`doi:10.1111/j.1538-4632.2001.tb00448.x`, :cite:`doi:10.1002/9781119967101.ch6` 40 | 41 | * Spatial Weights: 42 | :cite:`pysal2007`, :cite:`AnselinRey2014`, :cite:`rey_open_2015` 43 | 44 | * Spanning Trees: 45 | :cite:`GrahamHell_1985`, :cite:`AhujaRavindraK`, :cite:`doi:10.1002/9781119967101.ch3` 46 | 47 | * Integrations & applications: 48 | 49 | * Spatial Autocorrelation: 50 | :cite:`Moran1950`, :cite:`moran:_cliff81`, :cite:`Anselin1995`, :cite:`Getis1992`, :cite:`esda:_2019`, :cite:`splot:package:_2020`, :cite:`Lumnitz2020` 51 | 52 | * Facility Location: 53 | :cite:`Hakimi1964`, :cite:`ReVelle1970`, :cite:`Toregas1971`, :cite:`Toregas1972`, :cite:`Church1974`, :cite:`ReVelle2005` 54 | 55 | * The Transportation Problem: 56 | :cite:`Hitchcock1941`, :cite:`Koopmans1949`, :cite:`Phillips1981a`, :cite:`Gass2005`, :cite:`Church2009`, :cite:`daskin2013` 57 | 58 | * The Traveling Salesperson Problem: 59 | :cite:`Dantzig1954`, :cite:`Flood1956`, :cite:`Miller1960`, :cite:`Cummings2000`, :cite:`Miller2001`, :cite:`Bektas2014` 60 | 61 | 62 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: notebooks-environment 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - python=3.12 6 | - esda 7 | - geopandas 8 | - libspatialindex 9 | - matplotlib 10 | - matplotlib-scalebar 11 | - numpy 12 | - pandas 13 | - pulp 14 | - rtree 15 | - scipy 16 | - seaborn 17 | - shapely 18 | - splot 19 | - watermark 20 | - pip 21 | - pip: 22 | - mip 23 | - ortools 24 | - git+https://github.com/pysal/spaghetti.git@main 25 | -------------------------------------------------------------------------------- /paper/figs/spaghetti_network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pysal/spaghetti/72ae8892a9ae62d0d18aecc310709b0e00d5f090/paper/figs/spaghetti_network.png -------------------------------------------------------------------------------- /paper/figs/spaghetti_pointpattern_moran.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pysal/spaghetti/72ae8892a9ae62d0d18aecc310709b0e00d5f090/paper/figs/spaghetti_pointpattern_moran.png -------------------------------------------------------------------------------- /paper/paper.bib: -------------------------------------------------------------------------------- 1 | @article{pysal2007, 2 | author={Sergio Rey and Luc Anselin}, 3 | title={{PySAL: A Python Library of Spatial Analytical Methods}}, 4 | journal={The Review of Regional Studies}, 5 | year=2007, 6 | volume={37}, 7 | number={1}, 8 | pages={5-27}, 9 | keywords={Open Source; Software; Spatial}, 10 | url={https://rrs.scholasticahq.com/article/8285.pdf} 11 | } 12 | 13 | @article{pysal2015, 14 | title={Open {Geospatial} {Analytics} with {PySAL}}, 15 | author={Sergio Rey and Luc Anselin and Xun Li and Robert Pahle and Jason Laura and Wenwen Li and Julia Koschinsky}, 16 | journal={{ISPRS International Journal of Geo-Information}}, 17 | year={2015}, 18 | number={2}, 19 | pages={815--836}, 20 | volume={4}, 21 | keywords={open science}, 22 | doi={10.3390/ijgi4020815} 23 | } 24 | 25 | @article{gaboardi2020a, 26 | author={James D. Gaboardi and David C. Folch and Mark W. Horner}, 27 | title={{Connecting Points to Spatial Networks: Effects on Discrete Optimization Models}}, 28 | journal={Geographical Analysis}, 29 | year={2020}, 30 | volume={52}, 31 | issue={2}, 32 | pages={299--322}, 33 | doi={10.1111/gean.12211}, 34 | } 35 | 36 | @misc{gaboardi2018, 37 | author={James D. Gaboardi and Jay Laura and Sergio Rey and Levi John Wolf and David C. Folch and Wei Kang and Philip Stephens and Charles Schmidt}, 38 | month={oct}, 39 | year={2018}, 40 | title={pysal/spaghetti}, 41 | url= {https://github.com/pysal/spaghetti}, 42 | doi={10.5281/zenodo.1343650}, 43 | keywords={graph-theory,network-analysis,python,spatial-networks,topology} 44 | } 45 | 46 | @article{okabe2006a, 47 | author={Atsuyuki Okabe and Keiichi Okunuki and Shino Shiode}, 48 | doi={10.1111/j.0016-7363.2005.00674.x}, 49 | isbn={1538-4632}, 50 | issn={0016-7363}, 51 | journal={Geographical Analysis}, 52 | pages={57--66}, 53 | title={{SANET: A Toolbox for Spatial Analysis on a Network}}, 54 | volume={38}, 55 | year={2006} 56 | } 57 | 58 | @book{okabe2012, 59 | address={West Sussex, UK}, 60 | author={Atsuyuki Okabe and Kokichi Sugihara}, 61 | publisher={John Wiley {\&} Sons, Inc.}, 62 | title={{Spatial Analysis Along Networks}}, 63 | year={2012}, 64 | doi={10.1002/9781119967101} 65 | } 66 | 67 | @inproceedings{hagberg2008, 68 | address={Pasadena, CA USA}, 69 | author={Aric A. Hagberg and Daniel A. Schult and Pieter J. Swart}, 70 | booktitle={Proceedings of the 7th Python in Science Conference (SciPy 2008)}, 71 | editor={G{\"{a}}el Varoquaux and Travis Vaught and Jarrod Millman}, 72 | issn ={1540-9295}, 73 | pages={11--15}, 74 | title={{Exploring Network Structure, Dynamics, and Function using NetworkX}}, 75 | year={2008} 76 | } 77 | 78 | @article{boeing2017, 79 | author={Geoff Boeing}, 80 | doi={10.1016/j.compenvurbsys.2017.05.004}, 81 | issn={01989715}, 82 | journal={Computers, Environment and Urban Systems}, 83 | pages={126--139}, 84 | publisher={Elsevier Ltd}, 85 | title={{OSMnx: New Methods for Acquiring, Constructing, Analyzing, and Visualizing Complex Street Networks}}, 86 | volume={65}, 87 | year={2017} 88 | } 89 | 90 | @misc{russell2019, 91 | author={Tom Russell and Elco Koks}, 92 | title={{tomalrussell/snkit: v1.6.0}}, 93 | month=aug, 94 | year={2019}, 95 | publisher={Zenodo}, 96 | version={v1.6.0}, 97 | doi={10.5281/zenodo.3379659} 98 | } 99 | 100 | @misc{geopandas2021, 101 | author={Kelsey Jordahl and Joris Van den Bossche and Martin Fleischmann and James McBride and Jacob Wasserman and Jeffrey Gerard and Adrian Garcia Badaracco and Alan D. Snow and Jeff Tratner and Matthew Perry and Carson Farmer and Geir Arne Hjelle and Micah Cochran and Sean Gillies and Lucas Culbertson and Matt Bartos and Giacomo Caria and Nick Eubank and sangarshanan and Sergio Rey and maxalbert and Aleksey Bilogur and Brendan Ward and Christopher Ren and Dani Arribas-Bel and Flavin and Leah Wasser and Levi John Wolf and Martin Journois and abonte}, 102 | title={geopandas/geopandas: v0.9.0}, 103 | month=feb, 104 | year=2021, 105 | publisher={Zenodo}, 106 | version={v0.9.0}, 107 | doi={10.5281/zenodo.4569086}, 108 | url={https://doi.org/10.5281/zenodo.4569086} 109 | } 110 | 111 | @misc{reback2021pandas, 112 | author={Jeff Reback and Wes McKinney and jbrockmendel and Joris Van den Bossche and Tom Augspurger and Phillip Cloud and gfyoung and Simon Hawkins and Sinhrks and Matthew Roeschke and Adam Klein and Terji Petersen and Jeff Tratner and Chang She and William Ayd and Shahar Naveh and Marc Garcia and patrick and Jeremy Schendel and Andy Hayden and Daniel Saxton and Vytautas Jancauskas and Richard Shadrach and Marco Gorelli and Ali McMaster and Pietro Battiston and Skipper Seabold and Kaiqi Dong and chris-b1 and h-vetinari}, 113 | title={pandas-dev/pandas: Pandas 1.2.3}, 114 | month=mar, 115 | year=2021, 116 | publisher={Zenodo}, 117 | version={v1.2.3}, 118 | doi={10.5281/zenodo.4572994}, 119 | url={https://doi.org/10.5281/zenodo.4572994} 120 | } 121 | 122 | @inproceedings{ mckinney-proc-scipy-2010, 123 | author={{W}es {M}c{K}inney}, 124 | title={{D}ata {S}tructures for {S}tatistical {C}omputing in {P}ython}, 125 | booktitle={{P}roceedings of the 9th {P}ython in {S}cience {C}onference}, 126 | pages={56--61}, 127 | year={2010}, 128 | editor={{S}t\'efan van der {W}alt and {J}arrod {M}illman}, 129 | doi={10.25080/Majora-92bf1922-00a} 130 | } 131 | 132 | @misc{libpysal2020, 133 | author={Sergio Rey and Philip Stephens and Levi John Wolf and Charles Schmidt and jlaura and Taylor Oshan and Dani Arribas-Bel and James Gaboardi and David Folch and mhwang4 and Wei Kang and Nicholas Malizia and Pedro Amaral and Luc Anselin and Eli Knaap and Hu Shao and Marynia and Andrew Winslow and Conceptron and Jotham Apaloo and Martin Fleischmann and Andy Eschbacher and Stefanie Lumnitz and Siddharth S and Forest Gregg and Elliott Sales de Andrade and Bas Couwenberg and Josh Kalderimis and Hannes and Andy Reagan}, 134 | title={pysal/libpysal: v4.2.2}, 135 | month=feb, 136 | year=2020, 137 | publisher={Zenodo}, 138 | version={v4.2.2}, 139 | doi={10.5281/zenodo.1472807}, 140 | url={https://doi.org/10.5281/zenodo.1472807} 141 | } 142 | 143 | @article{Marshall2018, 144 | author={Stephen Marshall and Jorge Gil and Karl Kropf and Martin Tomko and Lucas Figueiredo}, 145 | doi={10.1007/s11067-018-9427-9}, 146 | issn={15729427}, 147 | journal={Networks and Spatial Economics}, 148 | keywords={Graph representation,Modelling,Street networks}, 149 | number={3}, 150 | pages={735--749}, 151 | title={{Street Network Studies: from Networks to Models and their Representations}}, 152 | volume={18}, 153 | year={2018} 154 | } 155 | 156 | @inproceedings{foti2012generalized, 157 | title={A generalized computational framework for accessibility: from the pedestrian to the metropolitan scale}, 158 | author={Fletcher Foti and Paul Waddell and Dennis Luxen}, 159 | booktitle={Proceedings of the 4th TRB Conference on Innovations in Travel Modeling. Transportation Research Board}, 160 | year={2012} 161 | } 162 | 163 | @inbook{osullivan_unwin2010.ch5, 164 | author={David O'Sullivan and David J. Unwin}, 165 | publisher={John Wiley \& Sons, Ltd}, 166 | isbn={9780470549094}, 167 | title={Point Pattern Analysis}, 168 | booktitle={Geographic Information Analysis}, 169 | chapter={5}, 170 | pages={121-156}, 171 | doi={10.1002/9780470549094.ch5}, 172 | year={2010}, 173 | keywords={point separation, standard deviation, observed VMR, estimation methods, computationally intensive}, 174 | abstract={Summary This chapter contains sections titled: Introduction Describing a Point Pattern Assessing Point Patterns Statistically Monte Carlo Testing Conclusions} 175 | } 176 | 177 | @article{splot:_Lumnitz2020, 178 | author={Stefanie Lumnitz and Dani Arribas-Bell and Renan X. Cortes and James D. Gaboardi and Verena Griess and Wei Kang and Taylor M. Oshan and Levi Wolf and Sergio Rey}, 179 | doi={10.21105/joss.01882}, 180 | journal={Journal of Open Source Software}, 181 | number={47}, 182 | pages={1--4}, 183 | title={splot - visual analytics for spatial statistics}, 184 | volume={5}, 185 | year={2020} 186 | } 187 | 188 | @misc{esda:_2019, 189 | author={Sergio Rey and Levi Wolf and Wei Kang and Phil Stephens and Jason Laura and Charles Schmidt and Dani Arribas-Bel and Stefanie Lumnitz and Juan Carlos Duque and David Folch and Luc Anselin and Nicholas Malizia and James Gaboardi and Filipe Fernandes and Mridul Seth and mhwang4 and mlyons-tcc}, 190 | title={{pysal/esda}}, 191 | month=jul, 192 | year={2019}, 193 | publisher={Zenodo}, 194 | version={v2.1.1}, 195 | doi={10.5281/zenodo.3265190} 196 | } 197 | 198 | @book{moran:_cliff81, 199 | address={London}, 200 | author={Andrew David Cliff and J. Keith Ord}, 201 | publisher={Pion}, 202 | title={Spatial Processes: Models and Applications}, 203 | year={1981} 204 | } 205 | -------------------------------------------------------------------------------- /paper/paper.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'spaghetti: spatial network analysis in PySAL' 3 | tags: 4 | - Python 5 | - PySAL 6 | - spatial networks 7 | - network analysis 8 | authors: 9 | - name: James D. Gaboardi 10 | orcid: 0000-0002-4776-6826 11 | affiliation: 1 12 | - name: Sergio Rey 13 | orcid: 0000-0001-5857-9762 14 | affiliation: 2 15 | - name: Stefanie Lumnitz 16 | orcid: 0000-0002-7007-5812 17 | affiliation: 3 18 | affiliations: 19 | - name: Pennsylvania State University 20 | index: 1 21 | - name: Center for Geospatial Sciences, University of California Riverside 22 | index: 2 23 | - name: Directorate of Earth Observation Programs, ESRIN, European Space Agency 24 | index: 3 25 | date: 2 April 2021 26 | bibliography: paper.bib 27 | --- 28 | 29 | 30 | # Summary 31 | 32 | The role spatial networks, such as streets, play on the human experience cannot be overstated. All of our daily activities fall along, or in close proximity to, roads, bike paths, and subway systems to name a few. Therefore, when performing spatial analysis in many cases considering network space, as opposed to Euclidean space, allows for a more precise representation of daily human action and movement patterns. For example, people generally cannot get to work by driving in a straight line directly from their home, but move along paths within networks. To this end, `spaghetti` (**spa**tial **g**rap**h**s: n**et**works, **t**opology, & **i**nference), a sub-module embedded in the wider [PySAL](https://pysal.org) ecosystem, was developed to address network-centric research questions with a strong focus on spatial analysis [@pysal2007;@pysal2015;@gaboardi2018]. 33 | 34 | Through `spaghetti`, first, network objects can be created and analysed from collections of line data by various means including reading in a shapefile or passing in a `geopandas.GeoDataFrame` at which time the line data are assigned network topology. Second, `spaghetti` provides computational tools to support statistical analysis of so-called network-based events along many different types of previously loaded networks. Network based-events or near-network observations are events that happen along spatial networks in our daily lives, i.e., locations of trees along footpaths, biking accidents along roads or locations of coffee shops along streets. As with `spaghetti.Network` objects, `spaghetti.PointPattern` objects can be [created from](https://pysal.org/spaghetti/generated/spaghetti.PointPattern.html#spaghetti.PointPattern) shapefiles, `geopandas.GeoDataFrame` objects or single `libpysal.cg.Point `objects. Near-network observations can then be snapped to nearest network segments enabling the calculation of observation distance matrices. Third, these observation distance matrices can be used both within `spaghetti` to perform clustering analysis or serve as input for other network-centric problems (e.g., optimal routing), or within the wider PySAL ecosystem to perform exploratory spatial analysis with [`esda`](https://pysal.org/esda/). Finally, `spaghetti`’s network elements (vertices and arcs) can also be extracted as `geopandas.GeoDataFrame` objects for visualization and integrated into further spatial statistical analysis within PySAL (e.g., `esda`). 35 | 36 | 37 | # Related Work & Statement of Need 38 | 39 | The most well-known network analysis package within the Python scientific stack is [NetworkX](https://networkx.github.io) [@hagberg2008], which can be used for modelling any type of complex network (e.g., social, spatial, etc.). [OSMnx](https://osmnx.readthedocs.io/en/stable/) [@boeing2017] is built on top of NetworkX and queries [OpenStreetMap](https://openstreetmap.org) for modelling street networks with resultant network objects returned within a `geopandas.GeoDataFrame` [@geopandas2021]. Another package, [`pandana`](https://github.com/UDST/pandana) [@foti2012generalized], is built on top of `pandas` [@mckinney-proc-scipy-2010;@reback2021pandas] with a focus on shortest path calculation and accessibility measures. Within the realm of Python, the functionality provided by [`snkit`](https://github.com/tomalrussell/snkit) [@russell2019] is most comparable to `spaghetti`, though it's main purpose is the processing of raw line data into clean network objects. Outside of Python, [SANET](http://sanet.csis.u-tokyo.ac.jp) [@okabe2006a] is the most closely related project to `spaghetti`, however, it is not written in Python and provides a GUI plugin for GIS software such as QGIS. Moreover, SANET is not fully open source. While all the libraries above are important for network-based research, `spaghetti` was created and has evolved in line with the Python Spatial Analysis Library ecosystem for the specific purpose of utilizing the functionality of spatial weights in [`libpysal`](https://pysal.org/libpysal/) for generating network segment contiguity objects. 40 | 41 | 42 | # Current Functionality 43 | 44 | Considering the related projects in the [Related Work & Statement of Need section](# Related Work & Statement of Need) detailed above, `spaghetti` fills a niche for not only the processing of spatial network objects, but also post-processing analysis. In other words, this package can be used to study the network *itself* or provide the foundation for studying network-based phenomena, such as crimes along city streets, all within a fully open-source environment. Considering this, the primary purpose of `spaghetti` is creating network objects: collections of vertices and arcs, and their topological relationships. The creation of a network object is realized through the following general steps: 45 | 46 | 1. read in line data or create features (regular lattices) 47 | 1. generate the network representation 48 | 1. extract contiguity weights (if desired) as show in \autoref{fig:gridweights} 49 | 1. identify connected components (if desired) 50 | 1. extract graph representation of the network (if desired) 51 | 52 | After the creation of a base network object it can be manipulated, analyzed, and utilized as the input for subsequent modelling scenarios. The following are several such examples: 53 | 54 | * allocating (snapping) observation point patterns to the network (see \autoref{fig:pointsnapmoran}) 55 | * calculating all neighbor distance matrices 56 | * point type A to point type A (auto) 57 | * point type A to point type B (cross) 58 | * utilizing observation counts on network segments and network spatial weights within the [Moran’s *I*](https://pysal.org/spaghetti/generated/spaghetti.Network.html#spaghetti.Network.Moran) attribute to analyze global spatial autocorrelation [@moran:_cliff81;@esda:_2019] as seen in \autoref{fig:pointsnapmoran} 59 | * simulating point patterns that can be used within the [*K* function](https://pysal.org/spaghetti/generated/spaghetti.Network.html#spaghetti.Network.GlobalAutoK) attribute for cluster analysis [@osullivan_unwin2010.ch5;@okabe2012] 60 | * splitting the network into (nearly) uniform segments 61 | * extracting features as `geopandas.GeoDataFrame` objects: 62 | * network arcs, vertices and point patterns 63 | * largest/longest components 64 | * shortest paths 65 | * minimum/maximum spanning trees 66 | 67 | The following two demonstrations show several functionalities mentioned above, including feature creation, network instantiation, network allocation, and feature extraction, along with supplementary plots in \autoref{fig:gridweights} and \autoref{fig:pointsnapmoran}. 68 | 69 | ```python 70 | import spaghetti 71 | %matplotlib inline 72 | # generate network 73 | lattice = spaghetti.regular_lattice((0,0,3,3), 2, exterior=True) 74 | ntw = spaghetti.Network(in_data=lattice) 75 | # extract network elements 76 | vertices_df, arcs_df = spaghetti.element_as_gdf(ntw, vertices=True, arcs=True) 77 | # plot 78 | base_kws = {"figsize":(12, 12), "lw":5, "color":"k", "zorder":0} 79 | base = arcs_df.plot(**base_kws, alpha=.35) 80 | node_kws, edge_kws = {"s":100, "zorder":2}, {"zorder":1} 81 | w_kws = {"edge_kws":edge_kws, "node_kws":node_kws} 82 | ntw.w_network.plot(arcs_df, indexed_on="id", ax=base, **w_kws) 83 | vertices_df.plot(ax=base, fc="r", ec="k", markersize=50, zorder=2) 84 | ``` 85 | 86 | ![A 4x4 regular lattice with network arcs in gray and vertices in red. Connectivity is demonstrated with `libpysal` spatial weights, which are plotted over the network in black [@libpysal2020]. \label{fig:gridweights}](figs/spaghetti_network.png){ width=50% } 87 | 88 | ```python 89 | import spaghetti, libpysal, matplotlib 90 | # create a network from a line shapefile 91 | ntw = spaghetti.Network(in_data=libpysal.examples.get_path("streets.shp")) 92 | # associate point observations with the network 93 | pp_name = "schools" 94 | pp_shp = libpysal.examples.get_path("%s.shp" % pp_name) 95 | ntw.snapobservations(pp_shp, pp_name, attribute=True) 96 | # calculation global spatial autocorrelation (Moran's I) 97 | moran, yaxis = ntw.Moran(pp_name) 98 | # extract network elements & observations 99 | arcs_df = spaghetti.element_as_gdf(ntw, arcs=True) 100 | schools = spaghetti.element_as_gdf(ntw, pp_name=pp_name) 101 | schools_snapped = spaghetti.element_as_gdf(ntw, pp_name=pp_name, snapped=True) 102 | # plot 103 | base_kws = {"figsize":(7, 7), "lw":3, "color":"k", "zorder":0} 104 | base = arcs_df.plot(**base_kws, alpha=.35) 105 | schools.plot(ax=base, fc="b", ec="k", markersize=100, zorder=1, alpha=.5) 106 | schools_snapped.plot(ax=base, fc="g", ec="k", markersize=50, zorder=2) 107 | matplotlib.pyplot.title(f"Moran's $I$: {round(moran.I, 3)}", size="xx-large") 108 | ``` 109 | 110 | ![Demonstrating the creation of a network and point pattern from shapefiles, followed by spatial autocorrelation analysis. A shapefile of school locations (blue) is read in and the points are snapped to the nearest network segments (green). A Moran's *I* statistic of -0.026 indicates near complete spatial randomness, though slightly dispersed. \label{fig:pointsnapmoran}](figs/spaghetti_pointpattern_moran.png){ width=50% } 111 | 112 | The overview presented here provides a high-level summary of functionality. More detailed examples and applications can be found in the *Tutorials* section of the `spaghetti` [documentation](https://pysal.org/spaghetti/tutorials.html). 113 | 114 | 115 | # Planned Enhancements 116 | 117 | As with any software project, there are always plans for further improvements and additional functionality. Four such major enhancements are described here. The first addition will likely be network partitioning through use of voronoi diagrams generated in network space. Network-constrained voronoi diagrams can be utilized as tools for analysis in and of themselves and can also be input for further analysis, such as the voronoi extension of the Network *K* function [@okabe2012]. Second, the current algorithm for allocating observations to a network within `spaghetti` allows for points to be snapped to a single location along the nearest network segment. While this is ideal for concrete observations, such as individual crime incidents, multiple network connections for abstract network events, such as census tract centroids, may be more appropriate [@gaboardi2020a]. Third, the core functionality of `spaghetti` is nearly entirely written with pure Python data structures, which are excellent for code readability and initial development but generally suffer in terms of performance. There are currently several functions that can be utilized with an optional `geopandas` installation, however, further integration with the `pandas` stack has the potential to greatly improve performance. Finally, `spaghetti` developers will assess together with PySAL developers how to best support visualization and visual analysis targeted towards `spaghetti` network objects, implemented within visualization packages like [`splot`](https://splot.readthedocs.io/en/latest/?badge=latest) or [`mapclassify`](https://pysal.org/mapclassify/) and exposed as high level plotting functionality in `spaghetti` [@splot:_Lumnitz2020]. 118 | 119 | 120 | # Concluding Remarks 121 | 122 | Network-constrained spatial analysis is an important facet of scientific inquiry, especially within the social and geographic sciences [@Marshall2018]. Being able to perform this type of spatial analysis with a well-documented and tested open-source software package further facilitates fully reproducible and open science. With these motivations and core values, the `spaghetti` developers and wider PySAL team look forward to creating and supporting research into the future. 123 | 124 | 125 | # Acknowledgements 126 | 127 | Firstly, we would like to thank all the contributors to, and users of, this package. We would also like to acknowledge Jay Laura, who was the original lead developer of this package (`pysal.network`) prior to the introduction of the PySAL 2.0 ecosystem. The development of this package was partially supported by the [Atlanta Research Data Center](https://atlantardc.wordpress.com) and National Science Foundation Award [#1825768](https://www.nsf.gov/awardsearch/showAward?AWD_ID=1825768). 128 | 129 | 130 | # References 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=61.0", "setuptools_scm[toml]>=6.2"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [tool.setuptools_scm] 6 | 7 | [project] 8 | name = "spaghetti" 9 | dynamic = ["version"] 10 | maintainers = [ 11 | {name = "James D. Gaboardi", email = "jgaboardi@gmail.com"}, 12 | ] 13 | license = {text = "BSD 3-Clause"} 14 | description = "Analysis of Network-constrained Spatial Data" 15 | keywords = ["spatial statistics", "networks", "graphs"] 16 | readme = "README.md" 17 | classifiers = [ 18 | "Programming Language :: Python :: 3", 19 | "License :: OSI Approved :: BSD License", 20 | "Operating System :: OS Independent", 21 | "Intended Audience :: Science/Research", 22 | "Topic :: Scientific/Engineering :: GIS", 23 | ] 24 | requires-python = ">=3.10" 25 | dependencies = [ 26 | "esda>=2.1", 27 | "geopandas>=0.12", 28 | "libpysal>=4.6", 29 | "numpy>=1.22", 30 | "pandas>=1.4,!=1.5.0", 31 | "rtree>=1.0", 32 | "scipy>=1.8", 33 | "shapely>=2.0.1", 34 | ] 35 | 36 | [project.urls] 37 | Home = "https://pysal.org/spaghetti/" 38 | Repository = "https://github.com/pysal/spaghetti" 39 | 40 | [project.optional-dependencies] 41 | dev = [ 42 | "ruff", 43 | "pre-commit", 44 | ] 45 | docs = [ 46 | "nbsphinx", 47 | "numpydoc", 48 | "sphinx", 49 | "sphinxcontrib-bibtex", 50 | "sphinx_bootstrap_theme", 51 | ] 52 | tests = [ 53 | "pytest", 54 | "pytest-cov", 55 | "pytest-doctestplus", 56 | "pytest-timeout", 57 | "pytest-xdist", 58 | "codecov", 59 | "coverage", 60 | "twine", 61 | "wheel", 62 | ] 63 | nb_pypi = [ 64 | "mip", 65 | "ortools", 66 | ] 67 | nb_conda = [ 68 | "matplotlib", 69 | "matplotlib-scalebar", 70 | "pulp", 71 | "seaborn", 72 | "splot", 73 | "watermark", 74 | ] 75 | 76 | [tool.setuptools.packages.find] 77 | include = [ 78 | "spaghetti", 79 | "spaghetti.*", 80 | ] 81 | 82 | [tool.ruff] 83 | line-length = 88 84 | lint.select = ["E", "F", "W", "I", "UP", "N", "B", "A", "C4", "SIM", "ARG"] 85 | exclude = ["spaghetti/tests/*", "docs/*"] 86 | 87 | [tool.ruff.lint.per-file-ignores] 88 | "*__init__.py" = [ 89 | "F401", # imported but unused 90 | "F403", # star import; unable to detect undefined names 91 | ] 92 | 93 | [tool.coverage.run] 94 | source = ["./spaghetti"] 95 | 96 | [tool.coverage.report] 97 | exclude_lines = [ 98 | "if self.debug:", 99 | "pragma: no cover", 100 | "raise NotImplementedError", 101 | "except ModuleNotFoundError:", 102 | "except ImportError", 103 | ] 104 | ignore_errors = true 105 | omit = ["spaghetti/tests/*", "docs/conf.py"] 106 | -------------------------------------------------------------------------------- /spaghetti/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | # `spaghetti` --- Spatial Graphs: Networks, Topology, & Inference 3 | """ 4 | 5 | import contextlib 6 | from importlib.metadata import PackageNotFoundError, version 7 | 8 | from .network import ( 9 | Network, 10 | PointPattern, 11 | SimulatedPointPattern, 12 | element_as_gdf, 13 | extract_component, 14 | regular_lattice, 15 | spanning_tree, 16 | ) 17 | 18 | with contextlib.suppress(PackageNotFoundError): 19 | __version__ = version("spaghetti") 20 | -------------------------------------------------------------------------------- /spaghetti/analysis.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | 3 | 4 | class FuncBase: 5 | """Base object for performing network analysis on a 6 | ``spaghetti.Network`` object. 7 | 8 | Parameters 9 | ---------- 10 | ntw : spaghetti.Network 11 | A spaghetti network object. 12 | pointpattern : spaghetti.network.PointPattern 13 | A spaghetti point pattern object. 14 | nsteps : int 15 | The number of steps at which the count of the nearest 16 | neighbors is computed. Default is 10. 17 | permutations : int 18 | The number of permutations to perform. Default is 99. 19 | threshold : float 20 | The level at which significance is computed. 21 | (0.5 would be 97.5% and 2.5%). Default is 0.5. 22 | distribution : str 23 | The distribution from which random points are sampled. 24 | Currently, the only supported distribution is uniform. 25 | upperbound : float 26 | The upper bound at which the `K`-function is computed. 27 | Defaults to the maximum observed nearest neighbor distance. 28 | 29 | Attributes 30 | ---------- 31 | sim : numpy.ndarray 32 | A simulated distance matrix. 33 | npts : int 34 | The number of points (``pointpattern.npoints``). 35 | xaxis : numpy.ndarray 36 | The observed x-axis of values. 37 | observed : numpy.ndarray 38 | The observed y-axis of values. 39 | 40 | """ 41 | 42 | def __init__( 43 | self, 44 | ntw, 45 | pointpattern, 46 | nsteps=10, 47 | permutations=99, 48 | threshold=0.5, 49 | distribution="uniform", 50 | upperbound=None, 51 | ): 52 | # set initial class attributes 53 | self.ntw = ntw 54 | self.pointpattern = pointpattern 55 | self.nsteps = nsteps 56 | self.permutations = permutations 57 | self.threshold = threshold 58 | 59 | # set and validate the distribution 60 | self.distribution = distribution.lower() 61 | self.validatedistribution() 62 | 63 | # create an empty array to store the simulated points 64 | self.sim = numpy.empty((permutations, nsteps)) 65 | self.pts = self.pointpattern 66 | self.npts = self.pts.npoints 67 | 68 | # set the upper bounds 69 | self.upperbound = upperbound 70 | 71 | # compute the statistic K 72 | self.computeobserved() 73 | self.computepermutations() 74 | 75 | # compute the envelope vectors 76 | self.computeenvelope() 77 | 78 | def validatedistribution(self): 79 | """Ensure the statistical distribution is supported.""" 80 | 81 | valid_distributions = ["uniform"] 82 | if self.distribution not in valid_distributions: 83 | msg = f"{self.distribution} distribution not currently supported." 84 | raise RuntimeError(msg) 85 | 86 | def computeenvelope(self): 87 | """Compute upper and lower bounds of envelope.""" 88 | 89 | upper = 1.0 - self.threshold / 2.0 90 | lower = self.threshold / 2.0 91 | 92 | self.upperenvelope = numpy.nanmax(self.sim, axis=0) * upper 93 | self.lowerenvelope = numpy.nanmin(self.sim, axis=0) * lower 94 | 95 | def setbounds(self, distances): 96 | """Set the upper bound.""" 97 | if self.upperbound is None: 98 | self.upperbound = numpy.nanmax(distances) 99 | 100 | 101 | class GlobalAutoK(FuncBase): 102 | """See full description in ``network.Network.GlobalAutoK()``. 103 | 104 | Attributes 105 | ---------- 106 | lam : float 107 | The ``lambda`` value; representing intensity. 108 | 109 | """ 110 | 111 | def computeobserved(self): 112 | """Compute the K function of observed points.""" 113 | 114 | # pairwise distances 115 | distances = self.ntw.allneighbordistances(self.pointpattern) 116 | distances = upper_triangle_as_vector(distances) 117 | 118 | self.setbounds(distances) 119 | 120 | # set the intensity (lambda) 121 | self.lam = self.npts / sum(self.ntw.arc_lengths.values()) 122 | 123 | # compute a Global Auto K-Function 124 | observedx, observedy = global_auto_k( 125 | self.npts, distances, self.upperbound, self.lam, self.nsteps 126 | ) 127 | 128 | # set observed values 129 | self.observed = observedy 130 | self.xaxis = observedx 131 | 132 | def computepermutations(self): 133 | """Compute the K function on permutations (Monte Carlo simulation).""" 134 | 135 | # for each round of permutations 136 | for p in range(self.permutations): 137 | # simulate a point pattern 138 | sim = self.ntw.simulate_observations( 139 | self.npts, distribution=self.distribution 140 | ) 141 | 142 | # distances 143 | distances = self.ntw.allneighbordistances(sim) 144 | distances = upper_triangle_as_vector(distances) 145 | 146 | # compute a Global Auto K-Function 147 | simx, simy = global_auto_k( 148 | self.npts, distances, self.upperbound, self.lam, self.nsteps 149 | ) 150 | 151 | # label the permutation 152 | self.sim[p] = simy 153 | 154 | 155 | def upper_triangle_as_vector(matrix): 156 | """Return the upper triangle of a symmetric matrix without the diagonal.""" 157 | return matrix[numpy.triu_indices_from(matrix, k=1)] 158 | 159 | 160 | def global_auto_k(n_obs, dists, upperbound, intensity, nsteps): 161 | """Compute a `K`-function. 162 | 163 | Parameters 164 | ---------- 165 | n_obs : int 166 | The number of observations. See ``self.npts``. 167 | dists : numpy.ndarray 168 | A vector (the upper triangle of a symmetric matrix) 169 | of pairwise distances. 170 | upperbound : int or float 171 | The end value of the sequence. 172 | intensity : float 173 | lambda value 174 | nsteps : int 175 | The number of distance bands. Must be non-negative. 176 | 177 | Returns 178 | ------- 179 | x : numpy.ndarray 180 | The x-axis of values. 181 | y : numpy.ndarray 182 | The y-axis of values. 183 | 184 | """ 185 | 186 | # create interval for x-axis 187 | x = numpy.linspace(0, upperbound, num=nsteps).reshape(-1, 1) 188 | 189 | # "iterate" over the x-axis interval, slice out and count neighbors within 190 | # each step radius, and multiply x2 to account for the lower triangle 191 | y = (dists < x).sum(axis=1) * 2.0 192 | 193 | # finalize the K computation for the denominator if the y-axis vector 194 | y /= n_obs * intensity 195 | 196 | # reset the shape of the x-axis 197 | x = x.squeeze() 198 | 199 | return x, y 200 | -------------------------------------------------------------------------------- /spaghetti/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pysal/spaghetti/72ae8892a9ae62d0d18aecc310709b0e00d5f090/spaghetti/tests/__init__.py -------------------------------------------------------------------------------- /spaghetti/util.py: -------------------------------------------------------------------------------- 1 | import geopandas 2 | import numpy 3 | import pandas 4 | import shapely 5 | from libpysal import cg 6 | from rtree import Rtree 7 | from shapely.geometry import LineString 8 | 9 | 10 | def compute_length(v0, v1): 11 | """Compute the euclidean distance between two points. 12 | 13 | Parameters 14 | ---------- 15 | v0 : tuple 16 | Coordinate sequence in the form x,y. 17 | vq : tuple 18 | Coordinate sequence in the form x,y. 19 | 20 | Returns 21 | ------- 22 | euc_dist : float 23 | Euclidean distance. 24 | 25 | Examples 26 | -------- 27 | 28 | >>> import spaghetti 29 | >>> point1, point2 = (0,0), (1,1) 30 | >>> spaghetti.util.compute_length(point1, point2) 31 | 1.4142135623730951 32 | 33 | """ 34 | 35 | euc_dist = cg.standalone.get_points_dist(v0, v1) 36 | 37 | return euc_dist 38 | 39 | 40 | def get_neighbor_distances(ntw, v0, link): 41 | """Get distances to the nearest vertex neighbors along 42 | connecting arcs. 43 | 44 | Parameters 45 | ---------- 46 | ntw : spaghetti.Network 47 | A spaghetti network object. 48 | v0 : int 49 | The vertex ID. 50 | link : dict 51 | The key is a tuple (start vertex, end vertex); value is ``float``. 52 | Cost per arc to travel, e.g. distance. 53 | 54 | Returns 55 | ------- 56 | neighbors : dict 57 | The key is an integer (vertex ID); value is ``float`` (distance). 58 | 59 | Examples 60 | -------- 61 | 62 | >>> import spaghetti 63 | >>> from libpysal import examples 64 | >>> ntw = spaghetti.Network(examples.get_path("streets.shp")) 65 | >>> neighs = spaghetti.util.get_neighbor_distances(ntw, 0, ntw.arc_lengths) 66 | >>> numpy.round(neighs[1], 10) 67 | np.float64(102.6235345344) 68 | 69 | """ 70 | 71 | # fetch links associated with vertices 72 | arcs = ntw.enum_links_vertex(v0) 73 | 74 | # create neighbor distance lookup 75 | neighbors = {} 76 | 77 | # iterate over each associated link 78 | for arc in arcs: 79 | # set distance from vertex1 to vertex2 (link length) 80 | if arc[0] != v0: 81 | neighbors[arc[0]] = link[arc] 82 | else: 83 | neighbors[arc[1]] = link[arc] 84 | 85 | return neighbors 86 | 87 | 88 | def generatetree(pred): 89 | """Rebuild the shortest path from root origin to destination. 90 | 91 | Parameters 92 | ---------- 93 | pred : list 94 | List of preceding vertices for route traversal. 95 | 96 | Returns 97 | ------- 98 | tree : dict 99 | The key is the root origin; value is the root origin to destination. 100 | 101 | Examples 102 | -------- 103 | 104 | >>> import spaghetti 105 | >>> from libpysal import examples 106 | >>> ntw = spaghetti.Network(examples.get_path("streets.shp")) 107 | >>> distance, pred = spaghetti.util.dijkstra(ntw, 0) 108 | >>> tree = spaghetti.util.generatetree(pred) 109 | >>> tree[3] 110 | [np.int64(23), np.int64(22), np.int64(20), np.int64(19), np.int64(170), np.int64(2), np.int64(0)] 111 | 112 | """ # noqa: E501 113 | 114 | # instantiate tree lookup 115 | tree = {} 116 | 117 | # iterate over the list of predecessor vertices 118 | for i, p in enumerate(pred): 119 | # if the route begins/ends with itself set the 120 | # root vertex and continue to next iteration 121 | if p == -1: 122 | # tree keyed by root vertex with root vertex as path 123 | tree[i] = [i] 124 | continue 125 | 126 | # set the initial vertex `p` as `idx` 127 | idx = p 128 | # and add it as the first vertex in the path 129 | path = [idx] 130 | 131 | # iterate through the path until back to home vertex 132 | while idx >= 0: 133 | # set the next vertex on the path 134 | next_vertex = pred[idx] 135 | # and redeclare the current `idx` 136 | idx = next_vertex 137 | 138 | # add the vertex to path while not at home vertex 139 | if idx >= 0: 140 | path.append(next_vertex) 141 | 142 | # tree keyed by root vertex with network vertices as path 143 | tree[i] = path 144 | 145 | return tree 146 | 147 | 148 | def dijkstra(ntw, v0, initial_dist=numpy.inf): 149 | """Compute the shortest path between a start vertex and 150 | all other vertices in an origin-destination matrix. 151 | 152 | Parameters 153 | ---------- 154 | ntw : spaghetti.Network 155 | A spaghetti network object. 156 | v0 : int 157 | Start vertex ID. 158 | initial_dist : float 159 | Integer break point to stop iteration and return n neighbors. 160 | Default is ``numpy.inf``. 161 | 162 | Returns 163 | ------- 164 | distance : list 165 | List of distances from vertex to all other vertices. 166 | pred : list 167 | List of preceeding vertices for traversal route. 168 | 169 | Notes 170 | ----- 171 | 172 | Based on :cite:`Dijkstra1959a`. 173 | 174 | Examples 175 | -------- 176 | 177 | >>> import spaghetti 178 | >>> from libpysal import examples 179 | >>> ntw = spaghetti.Network(examples.get_path("streets.shp")) 180 | >>> distance, pred = spaghetti.util.dijkstra(ntw, 0) 181 | >>> round(distance[196], 4) 182 | 5505.6682 183 | >>> pred[196] 184 | np.int64(133) 185 | 186 | """ 187 | 188 | # cost per arc to travel, e.g. distance 189 | cost = ntw.arc_lengths 190 | 191 | # initialize travel costs as `inf` for all distances 192 | distance = [initial_dist for x in ntw.vertex_list] 193 | 194 | # label distance to self as 0 195 | distance[ntw.vertex_list.index(v0)] = 0 196 | 197 | # instantiate set of unvisited vertices 198 | unvisited = {v0} 199 | 200 | # initially label as predecessor vertices with -1 as path 201 | pred = [-1 for x in ntw.vertex_list] 202 | 203 | # iterate over `unvisited` until all vertices have been visited 204 | while len(unvisited) > 0: 205 | # get vertex with the lowest value from distance 206 | dist = initial_dist 207 | 208 | for vertex in unvisited: 209 | if distance[vertex] < dist: 210 | dist = distance[vertex] 211 | current = vertex 212 | 213 | # remove that vertex from the set 214 | unvisited.remove(current) 215 | 216 | # get the neighbors (and costs) to the current vertex 217 | neighbors = get_neighbor_distances(ntw, current, cost) 218 | 219 | # iterate over neighbors to find least cost along path 220 | for v1, indiv_cost in neighbors.items(): 221 | # if the labeled cost is greater than 222 | # the currently calculated cost 223 | if distance[v1] > distance[current] + indiv_cost: 224 | # relabel to the currently calculated cost 225 | distance[v1] = distance[current] + indiv_cost 226 | 227 | # set the current vertex as a predecessor on the path 228 | pred[v1] = current 229 | 230 | # add the neighbor vertex to `unvisted` 231 | unvisited.add(v1) 232 | 233 | # cast preceding vertices list as an array of integers 234 | pred = numpy.array(pred, dtype=int) 235 | 236 | return distance, pred 237 | 238 | 239 | def dijkstra_mp(ntw_vertex): 240 | """Compute the shortest path between a start vertex and all other 241 | vertices in the matrix utilizing multiple cores upon request. 242 | 243 | Parameters 244 | ---------- 245 | ntw_vertex : tuple 246 | Tuple of arguments to pass into ``dijkstra()`` as 247 | (1) ``ntw`` - ``spaghetti.Network object``; 248 | (2) ``vertex`` - int (start node ID) 249 | 250 | Returns 251 | ------- 252 | distance : list 253 | List of distances from vertex to all other vertices. 254 | pred : list 255 | List of preceeding vertices for traversal route. 256 | 257 | Notes 258 | ----- 259 | 260 | Based on :cite:`Dijkstra1959a`. 261 | 262 | Examples 263 | -------- 264 | 265 | >>> import spaghetti 266 | >>> from libpysal import examples 267 | >>> ntw = spaghetti.Network(examples.get_path("streets.shp")) 268 | >>> distance, pred = spaghetti.util.dijkstra_mp((ntw, 0)) 269 | >>> round(distance[196], 4) 270 | 5505.6682 271 | >>> pred[196] 272 | np.int64(133) 273 | 274 | """ 275 | 276 | # unpack network object and source vertex 277 | ntw, vertex = ntw_vertex 278 | 279 | # calculate shortest path distances and predecessor vertices 280 | distance, pred = dijkstra(ntw, vertex) 281 | 282 | return distance, pred 283 | 284 | 285 | def squared_distance_point_link(point, link): 286 | """Find the squared distance between a point and a link. 287 | 288 | Parameters 289 | ---------- 290 | point : tuple 291 | Point coordinates (x,y). 292 | link : list 293 | List of 2 point coordinate tuples [(x0, y0), (x1, y1)]. 294 | 295 | Returns 296 | ------- 297 | sqd : float 298 | The distance squared between the point and edge. 299 | nearp : numpy.ndarray 300 | An array of (xb, yb); the nearest point on the edge. 301 | 302 | Examples 303 | -------- 304 | 305 | >>> import spaghetti 306 | >>> point, link = (1,1), ((0,0), (2,0)) 307 | >>> spaghetti.util.squared_distance_point_link(point, link) 308 | (np.float64(1.0), array([1., 0.])) 309 | 310 | """ 311 | 312 | # cast vertices comprising the network link as an array 313 | p0, p1 = (numpy.array(p) for p in link) 314 | 315 | # cast the observation point as an array 316 | p = numpy.array(point) 317 | 318 | # subtract point 0 coords from point 1 319 | v = p1 - p0 320 | # subtract point 0 coords from the observation coords 321 | w = p - p0 322 | 323 | # if the point 0 vertex is the closest point along the link 324 | c1 = numpy.dot(w, v) 325 | if c1 <= 0.0: 326 | sqd = numpy.dot(w.T, w) 327 | nearp = p0 328 | 329 | return sqd, nearp 330 | 331 | # if the point 1 vertex is the closest point along the link 332 | c2 = numpy.dot(v, v) 333 | if c2 <= c1: 334 | dp1 = p - p1 335 | sqd = numpy.dot(dp1.T, dp1) 336 | nearp = p1 337 | 338 | return sqd, nearp 339 | 340 | # otherwise the closest point along the link lies between p0 and p1 341 | b = c1 / c2 342 | bv = numpy.dot(b, v) 343 | pb = p0 + bv 344 | d2 = p - pb 345 | sqd = numpy.dot(d2, d2) 346 | nearp = pb 347 | 348 | return sqd, nearp 349 | 350 | 351 | def snap_points_to_links(points, links): 352 | """Place points onto closest link in a set of links (arc/edges). 353 | 354 | Parameters 355 | ---------- 356 | points : dict 357 | Point ID as key and (x,y) coordinate as value. 358 | links : list 359 | Elements are of type ``libpysal.cg.shapes.Chain`` 360 | ** Note ** each element is a link represented as a chain with 361 | *one head and one tail vertex* in other words one link only. 362 | 363 | Returns 364 | ------- 365 | point2link : dict 366 | Key [point ID (see points in arguments)]; value [a 2-tuple 367 | ((head, tail), point) where (head, tail) is the target link, 368 | and point is the snapped location on the link. 369 | 370 | Examples 371 | -------- 372 | 373 | >>> import spaghetti 374 | >>> from libpysal.cg.shapes import Point, Chain 375 | >>> points = {0: Point((1,1))} 376 | >>> link = [Chain([Point((0,0)), Point((2,0))])] 377 | >>> spaghetti.util.snap_points_to_links(points, link) 378 | {0: ([(0.0, 0.0), (2.0, 0.0)], array([1., 0.]))} 379 | 380 | """ 381 | 382 | # instantiate an rtree 383 | rtree = Rtree() 384 | # set the smallest possible float epsilon on machine 385 | small = numpy.finfo(float).eps 386 | 387 | # initialize network vertex to link lookup 388 | vertex_2_link = {} 389 | 390 | # iterate over network links 391 | for i, link in enumerate(links): 392 | # extract network link (x,y) vertex coordinates 393 | head, tail = link.vertices 394 | x0, y0 = head 395 | x1, y1 = tail 396 | 397 | if (x0, y0) not in vertex_2_link: 398 | vertex_2_link[(x0, y0)] = [] 399 | 400 | if (x1, y1) not in vertex_2_link: 401 | vertex_2_link[(x1, y1)] = [] 402 | 403 | vertex_2_link[(x0, y0)].append(link) 404 | vertex_2_link[(x1, y1)].append(link) 405 | 406 | # minimally increase the bounding box exterior 407 | bx0, by0, bx1, by1 = link.bounding_box 408 | bx0 -= small 409 | by0 -= small 410 | bx1 += small 411 | by1 += small 412 | 413 | # insert the network link and its associated 414 | # rectangle into the rtree 415 | rtree.insert(i, (bx0, by0, bx1, by1), obj=link) 416 | 417 | # build a KDtree on link vertices 418 | kdtree = cg.KDTree(list(vertex_2_link.keys())) 419 | 420 | point2link = {} 421 | 422 | for pt_idx, point in points.items(): 423 | # first, find nearest neighbor link vertices for the point 424 | dmin, vertex = kdtree.query(point, k=1) 425 | vertex = tuple(kdtree.data[vertex]) 426 | closest = vertex_2_link[vertex][0].vertices 427 | 428 | # Use this link as the candidate closest link: closest 429 | # Use the distance as the distance to beat: dmin 430 | point2link[pt_idx] = (closest, numpy.array(vertex)) 431 | x0 = point[0] - dmin 432 | y0 = point[1] - dmin 433 | x1 = point[0] + dmin 434 | y1 = point[1] + dmin 435 | 436 | # Find all links with bounding boxes that intersect 437 | # a query rectangle centered on the point with sides 438 | # of length dmin * dmin 439 | rtree_lookup = rtree.intersection([x0, y0, x1, y1], objects=True) 440 | candidates = [cand.object.vertices for cand in rtree_lookup] 441 | 442 | # Sorting the candidate ensures reproducible results from OS to OS. 443 | # See: 444 | # https://github.com/pysal/spaghetti/pull/595 445 | # https://github.com/pysal/spaghetti/issues/598 446 | # https://github.com/pysal/spaghetti/pull/599 447 | candidates.sort(reverse=True) 448 | dmin += small 449 | dmin2 = dmin * dmin 450 | 451 | # of the candidate arcs, find the nearest to the query point 452 | for candidate in candidates: 453 | dist2cand, nearp = squared_distance_point_link(point, candidate) 454 | if dist2cand <= dmin2: 455 | closest = candidate 456 | dmin2 = dist2cand 457 | point2link[pt_idx] = (closest, nearp) 458 | 459 | return point2link 460 | 461 | 462 | def network_has_cycle(adjacency): 463 | """Searches for a cycle in the complete network/graph. 464 | 465 | Parameters 466 | ---------- 467 | adjacency : spaghetti.Network.adjacencylist 468 | Vertex adjacency relationships. 469 | 470 | Returns 471 | ------- 472 | network_cycle_found : bool 473 | ``True`` for a cycle being found in the network/graph, 474 | otherwise ``False``. 475 | 476 | """ 477 | 478 | def tree_has_cycle(_parent, _v): 479 | """Searches for a cycle in the subtree. 480 | 481 | Parameters 482 | ---------- 483 | _parent : int 484 | Root vertex for the subnetwork/graph. 485 | _v : int 486 | Current vertex index of in the complete network. 487 | 488 | Returns 489 | ------- 490 | subtree_cycle_found : bool 491 | Current recursion found a cycle in the subtree. 492 | 493 | """ 494 | 495 | # Set current cycle tag as False 496 | subtree_cycle_found = False 497 | 498 | # Label the current network vertex as seen 499 | seen[_v] = True 500 | 501 | # Perform recursion for all adjacent network/graph vertices 502 | for rv in adjacency[_v]: 503 | # If vertex already seen, skip it 504 | if not seen[rv]: 505 | # Perform recursion down the depth-first search tree 506 | if tree_has_cycle(_v, rv): 507 | subtree_cycle_found = True 508 | break 509 | 510 | # If an adjacent vertex has not been seen and it is not the 511 | # parent of current vertex, then a cycle is present 512 | elif _parent != rv: 513 | subtree_cycle_found = True 514 | break 515 | 516 | return subtree_cycle_found 517 | 518 | # set initial cycle tag as False 519 | network_cycle_found = False 520 | 521 | # Label all network/graph vertices as not seen 522 | vids = list(adjacency.keys()) 523 | seen = {vid: False for vid in vids} 524 | 525 | # Perform depth-first search recursion to isolate cycles 526 | for v in vids: 527 | # If vertex already seen, skip it; or recurse down the depth-first search tree 528 | if not seen[v] and tree_has_cycle(-1, v): 529 | network_cycle_found = True 530 | break 531 | 532 | return network_cycle_found 533 | 534 | 535 | def chain_constr(vcoords, arcs): 536 | """Create the spatial representation of a network arc. 537 | 538 | Parameters 539 | ---------- 540 | vcoords : dict 541 | Vertex to coordinate lookup (see ``spaghetti.Network.vertex_coords``). 542 | arcs : list 543 | Arcs represented as start and end vertices. 544 | 545 | Returns 546 | ------- 547 | spatial_reps : list 548 | Spatial representations of arcs - ``libpysal.cg.Chain`` objects. 549 | 550 | """ 551 | spatial_reps = [_chain_constr(vcoords, vs) for vs in arcs] 552 | return spatial_reps 553 | 554 | 555 | def _chain_constr(_vcoords, _vs): 556 | """Construct a libpysal.cg.Chain object. 557 | 558 | Parameters 559 | ---------- 560 | _vcoords : {dict, None} 561 | See ``vcoords`` in ``get_chains()``. 562 | _vs : tuple 563 | Start and end vertex IDs of arc. 564 | 565 | Returns 566 | ------- 567 | libpysal.cg.Chain 568 | Spatial representation of the arc. 569 | 570 | """ 571 | 572 | return cg.Chain([cg.Point(_vcoords[v]) for v in _vs] if _vcoords else _vs) 573 | 574 | 575 | def build_chains(space_h, space_v, exterior, bounds, h=True): 576 | """Generate line segments for a lattice. 577 | 578 | Parameters 579 | ---------- 580 | space_h : list 581 | Horizontal spacing. 582 | space_v : list 583 | Vertical spacing. 584 | exterior : bool 585 | Flag for including the outer bounding box segments. 586 | bounds : list 587 | Area bounds in the form - . 588 | h : bool 589 | Generate horizontal line segments. 590 | Default is ``True``. ``False`` generates vertical segments. 591 | 592 | Returns 593 | ------- 594 | chains : list 595 | All horizontal or vertical line segments in the lattice. 596 | 597 | """ 598 | 599 | # Initialize starting and ending indices 600 | start_h, end_h, start_v, end_v = 0, len(space_h), 0, len(space_v) 601 | 602 | # set inital index track back to 0 603 | minus_y, minus_x = 0, 0 604 | 605 | if h: # start track back at 1 for horizontal lines 606 | minus_x = 1 607 | if not exterior: # do not include borders 608 | start_v += 1 609 | end_v -= 1 610 | 611 | else: # start track back at 1 for vertical lines 612 | minus_y = 1 613 | if not exterior: # do not include borders 614 | start_h += 1 615 | end_h -= 1 616 | 617 | # Create empty line list and fill 618 | chains = [] 619 | 620 | # for element in the horizontal index 621 | for plus_h in range(start_h, end_h): 622 | # for element in the vertical index 623 | for plus_v in range(start_v, end_v): 624 | # ignore if a -1 index 625 | if plus_h - minus_x == -1 or plus_v - minus_y == -1: 626 | continue 627 | else: 628 | # Point 1 (start point + previous slot in 629 | # horizontal or vertical space index) 630 | p1x = bounds[0] + space_h[plus_h - minus_x] 631 | p1y = bounds[1] + space_v[plus_v - minus_y] 632 | p1 = cg.Point((p1x, p1y)) 633 | 634 | # Point 2 (start point + current slot in 635 | # horizontal or vertical space index) 636 | p2x = bounds[0] + space_h[plus_h] 637 | p2y = bounds[1] + space_v[plus_v] 638 | p2 = cg.Point((p2x, p2y)) 639 | 640 | # libpysal.cg.Chain 641 | chains.append(_chain_constr(None, [p1, p2])) 642 | 643 | return chains 644 | 645 | 646 | def _points_as_gdf(net, vertices, vertices_for_arcs, pp_name, snapped, id_col=None): 647 | """Internal function for returning a point ``geopandas.GeoDataFrame`` 648 | called from within ``spaghetti.element_as_gdf()``. 649 | 650 | Parameters 651 | ---------- 652 | vertices_for_arcs : bool 653 | Flag for points being an object returned (``False``) or for merely 654 | creating network arcs (``True``). Set from within the parent 655 | function (``spaghetti.element_as_gdf()``). 656 | 657 | Raises 658 | ------ 659 | 660 | KeyError 661 | In order to extract a ``network.PointPattern`` it must already 662 | be a part of the network object. This exception is raised 663 | when a ``network.PointPattern`` is being extracted that does not 664 | exist within the network object. 665 | 666 | Returns 667 | ------- 668 | points : geopandas.GeoDataFrame 669 | Network point elements (either vertices or ``network.PointPattern`` 670 | points) as a simple ``geopandas.GeoDataFrame`` of 671 | ``shapely.geometry.Point`` objects with an ``"id"`` column and 672 | ``"geometry"`` column. 673 | 674 | Notes 675 | ----- 676 | 677 | See ``spaghetti.element_as_gdf()`` for description of arguments. 678 | 679 | """ 680 | 681 | # vertices / nodes 682 | if vertices or vertices_for_arcs: 683 | pts_dict = net.vertex_coords 684 | 685 | if pp_name: 686 | try: 687 | pp = net.pointpatterns[pp_name] 688 | except KeyError as err: 689 | err_msg = f"Available point patterns are {net.pointpatterns.keys()}" 690 | raise KeyError(err_msg) from err 691 | 692 | # raw point pattern 693 | if not snapped: 694 | pp_pts = pp.points 695 | n_pp_pts = range(len(pp_pts)) 696 | pts_dict = {point: pp_pts[point]["coordinates"] for point in n_pp_pts} 697 | 698 | # snapped point pattern 699 | else: 700 | pts_dict = pp.snapped_coordinates 701 | 702 | # instantiate geopandas.GeoDataFrame 703 | points = geopandas.GeoDataFrame( 704 | pts_dict.keys(), 705 | columns=[id_col], 706 | geometry=shapely.points(numpy.asarray(list(pts_dict.values()))), 707 | ) 708 | 709 | # additional columns 710 | if not pp_name: 711 | ncv_tag = "network_component_vertices" 712 | if hasattr(net, ncv_tag): 713 | ncv = getattr(net, ncv_tag) 714 | ncv_map = {v: k for k, verts in ncv.items() for v in verts} 715 | points["comp_label"] = points[id_col].map(ncv_map) 716 | if pp_name: 717 | c2o_tag = "component_to_obs" 718 | if hasattr(pp, c2o_tag): 719 | c2o = getattr(pp, c2o_tag) 720 | o2c_map = {o: c for c, obs in c2o.items() for o in obs} 721 | points["comp_label"] = points[id_col].map(o2c_map) 722 | 723 | return points 724 | 725 | 726 | def _arcs_as_gdf(net, points, id_col=None): 727 | """Internal function for returning an arc ``geopandas.GeoDataFrame`` 728 | called from within ``spaghetti.element_as_gdf()``. 729 | 730 | Returns 731 | ------- 732 | arcs : geopandas.GeoDataFrame 733 | Network arc elements as a ``geopandas.GeoDataFrame`` of 734 | ``shapely.geometry.LineString`` objects with an ``"id"`` 735 | column and ``geometry`` column. 736 | 737 | Notes 738 | ----- 739 | 740 | See ``spaghetti.element_as_gdf()`` for description of arguments. 741 | 742 | """ 743 | 744 | def _line_coords(loc): 745 | indpoints = points.set_index(id_col, inplace=False) 746 | return ( 747 | (indpoints.loc[loc[0]].geometry.x, indpoints.loc[loc[0]].geometry.y), 748 | (indpoints.loc[loc[1]].geometry.x, indpoints.loc[loc[1]].geometry.y), 749 | ) 750 | 751 | # instantiate GeoDataFrame 752 | arcs = pandas.DataFrame(zip(sorted(net.arcs), strict=True), columns=[id_col]) 753 | arcs = arcs.set_geometry( 754 | shapely.linestrings(arcs[id_col].map(_line_coords).values.tolist()) 755 | ) 756 | 757 | # additional columns 758 | if hasattr(net, "network_component_labels"): 759 | arcs["comp_label"] = net.network_component_labels 760 | 761 | return arcs 762 | 763 | 764 | def _routes_as_gdf(paths, id_col): 765 | """Internal function for returning a shortest paths 766 | ``geopandas.GeoDataFrame`` called from within 767 | ``spaghetti.element_as_gdf()``. 768 | 769 | Returns 770 | ------- 771 | paths : geopandas.GeoDataFrame 772 | Network shortest paths as a ``geopandas.GeoDataFrame`` of 773 | ``shapely.geometry.LineString`` objects with an ``"O"`` (origin), 774 | ``D`` (destination), and ``geometry`` column. An additional 775 | column storing the ID as a tuple is available. 776 | 777 | Notes 778 | ----- 779 | 780 | See ``spaghetti.element_as_gdf()`` for description of arguments. 781 | 782 | """ 783 | 784 | # instantiate as a geodataframe 785 | paths = dict(paths) 786 | ids, geoms = ( 787 | zip(paths.keys(), strict=True), 788 | [LineString(g.vertices) for g in paths.values()], 789 | ) 790 | paths = geopandas.GeoDataFrame(ids, columns=[id_col], geometry=geoms) 791 | 792 | return paths 793 | --------------------------------------------------------------------------------