├── .coveragerc ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── pull_request_template.md └── workflows │ ├── deploy-docs.yml │ ├── python-package.yml │ └── python-publish.yml ├── .gitignore ├── CHANGELOG.md ├── CODE-OF-CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.md ├── data ├── bigfoot_sightings.csv ├── texas_cities.csv ├── texas_sample.png └── texas_sample_folium.png ├── environment.yaml ├── map_maker ├── __init__.py ├── _version.py ├── bokeh │ ├── __init__.py │ └── map_maker.py ├── cli.py ├── folium │ ├── __init__.py │ └── map_maker.py └── util │ ├── __init__.py │ └── util.py ├── notebooks ├── bigfoot_cities_texas_bokeh.png ├── bigfoot_cities_texas_folium.png ├── bigfoot_texas_bokeh.png ├── bigfoot_texas_folium.png ├── bokeh_example.ipynb └── folium_example.ipynb ├── setup.cfg ├── setup.py ├── sphinx-docs ├── Makefile ├── _static │ └── .gitkeep ├── _templates │ └── .gitkeep ├── conf.py ├── index.rst └── source │ ├── map_maker_bokeh.rst │ └── map_maker_folium.rst ├── tests ├── test_data │ ├── test_data_points.csv │ └── test_data_polygons_lines.csv ├── test_map_maker_bokeh.py ├── test_map_maker_folium.py └── test_util.py └── versioneer.py /.coveragerc: -------------------------------------------------------------------------------- 1 | 2 | [run] 3 | omit = 4 | # Remove CLI. 5 | map_maker/cli.py 6 | **/__init__.py 7 | map_maker/_version.py -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | map_maker/_version.py export-subst 2 | -------------------------------------------------------------------------------- /.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: '' 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 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.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: '' 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 | Fixes # 2 | 3 | Changes proposed in this pull request: 4 | - 5 | - 6 | - 7 | 8 | @ExpediaGroup/map-maker-committers 9 | -------------------------------------------------------------------------------- /.github/workflows/deploy-docs.yml: -------------------------------------------------------------------------------- 1 | # This workflow builds and deploys Sphinx documentation to the gh-pages branch 2 | # It builds when a tag is _released_ (meaning not a prerelease). 3 | 4 | name: Build and Deploy Sphinx Docs 5 | 6 | on: 7 | release: 8 | types: [released] 9 | 10 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 11 | # follows https://github.com/marketplace/actions/deploy-to-github-pages 12 | jobs: 13 | build-and-deploy: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 18 | - uses: actions/checkout@v2 19 | with: 20 | persist-credentials: false 21 | 22 | # Set up the python environment. 23 | - name: Setup Python 24 | uses: actions/setup-python@v2 25 | with: 26 | python-version: 3.7 27 | 28 | # Set up dependencies. Must include the package itself, as sphinx 29 | # needs to import the package to read the docstrings. 30 | - name: Install dependencies 31 | run: | 32 | python -m pip install --upgrade pip 33 | pip install -e . 34 | pip install sphinx sphinx_rtd_theme 35 | 36 | # Build the documentation 37 | - name: Build Docs 38 | run: | 39 | make documentation 40 | 41 | # Use https://github.com/marketplace/actions/deploy-to-github-pages to deploy docs. 42 | - name: Deploy Docs 43 | uses: JamesIves/github-pages-deploy-action@3.7.1 44 | with: 45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 46 | BRANCH: gh-pages 47 | FOLDER: docs/html 48 | CLEAN: true 49 | -------------------------------------------------------------------------------- /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: Python package 5 | 6 | on: 7 | push: 8 | branches: [ main ] 9 | pull_request: 10 | branches: [ main ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | python-version: [3.6, 3.7, 3.8] 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | - name: Set up Python ${{ matrix.python-version }} 23 | uses: actions/setup-python@v2 24 | with: 25 | python-version: ${{ matrix.python-version }} 26 | - name: Install dependencies 27 | run: | 28 | python -m pip install --upgrade pip 29 | pip install flake8 pytest pytest-cov coveralls 30 | pip install -e . 31 | - name: Lint with flake8 32 | run: | 33 | make lint 34 | - name: Test with pytest 35 | env: 36 | COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} 37 | run: | 38 | make test 39 | coveralls 40 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflows will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | name: Release Python Package 5 | 6 | on: 7 | release: 8 | types: [published] 9 | 10 | jobs: 11 | deploy: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up Python 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: '3.x' 21 | - name: Install dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | pip install setuptools wheel twine 25 | - name: Build and publish 26 | env: 27 | TWINE_USERNAME: __token__ 28 | TWINE_PASSWORD: ${{ secrets.PY_PI_TOKEN }} 29 | run: | 30 | python setup.py sdist bdist_wheel 31 | twine upload --repository pypi dist/* 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.egg-info 2 | **/__pycache__/ 3 | src/ 4 | .env 5 | build/ 6 | dist/ 7 | .doctrees/ 8 | .buildinfo 9 | scripts/*.html 10 | .coverage 11 | docs/ 12 | .ipynb_checkpoints -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaGroup/map-maker/95fd76cdb9adade6862473068b5fcc1ceeb9f6ee/CHANGELOG.md -------------------------------------------------------------------------------- /CODE-OF-CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. 8 | 9 | ## Our Standards 10 | 11 | Examples of behavior that contributes to a positive environment for our community include: 12 | 13 | * Demonstrating empathy and kindness toward other people 14 | * Being respectful of differing opinions, viewpoints, and experiences 15 | * Giving and gracefully accepting constructive feedback 16 | * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience 17 | * Focusing on what is best not just for us as individuals, but for the overall community 18 | 19 | Examples of unacceptable behavior include: 20 | 21 | * The use of sexualized language or imagery, and sexual attention or 22 | advances of any kind 23 | * Trolling, insulting or derogatory comments, and personal or political attacks 24 | * Public or private harassment 25 | * Publishing others' private information, such as a physical or email 26 | address, without their explicit permission 27 | * Other conduct which could reasonably be considered inappropriate in a 28 | professional setting 29 | 30 | ## Enforcement Responsibilities 31 | 32 | Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. 33 | 34 | Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. 35 | 36 | ## Scope 37 | 38 | This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. 39 | 40 | ## Enforcement 41 | 42 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [map-maker-committers](https://github.com/orgs/ExpediaGroup/teams/map-maker-committers/members). All complaints will be reviewed and investigated promptly and fairly. 43 | 44 | All community leaders are obligated to respect the privacy and security of the reporter of any incident. 45 | 46 | ## Enforcement Guidelines 47 | 48 | Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: 49 | 50 | ### 1. Correction 51 | 52 | **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. 53 | 54 | **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. 55 | 56 | ### 2. Warning 57 | 58 | **Community Impact**: A violation through a single incident or series of actions. 59 | 60 | **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. 61 | 62 | ### 3. Temporary Ban 63 | 64 | **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. 65 | 66 | **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. 67 | 68 | ### 4. Permanent Ban 69 | 70 | **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. 71 | 72 | **Consequence**: A permanent ban from any sort of public interaction within the community. 73 | 74 | ## Attribution 75 | 76 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, 77 | available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 78 | 79 | Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). 80 | 81 | [homepage]: https://www.contributor-covenant.org 82 | 83 | For answers to common questions about this code of conduct, see the FAQ at 84 | https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. 85 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How To Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are just a few guidelines you need to follow which are described in detail below. 4 | 5 | ## 1. Fork this repo 6 | 7 | You should create a fork of this project in your account and work from there. You can create a fork by clicking the fork button in GitHub. 8 | 9 | ## 2. One feature, one branch 10 | 11 | Work for each new feature/issue should occur in its own branch. To create a new branch from the command line: 12 | ```shell 13 | git checkout -b my-new-feature 14 | ``` 15 | where "my-new-feature" describes what you're working on. 16 | 17 | ## 3. Add unit tests 18 | If your contribution modifies existing or adds new code please add corresponding unit tests for this. 19 | 20 | ## 4. Ensure that the build passes 21 | 22 | Run 23 | ```shell 24 | make lint test 25 | ``` 26 | and check that there are no errors. 27 | 28 | ## 5. Add documentation for new or updated functionality 29 | 30 | Please review all of the .md files in this project to see if they are impacted by your change and update them accordingly. 31 | 32 | ## 6. Add to CHANGELOG.md 33 | 34 | Any notable changes should be recorded in the CHANGELOG.md following the [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) conventions. 35 | 36 | ## 7. Submit a pull request and describe the change 37 | 38 | Push your changes to your branch and open a pull request against the parent repo on GitHub. The project administrators will review your pull request and respond with feedback. 39 | 40 | # How your contribution gets merged 41 | 42 | Upon pull request submission, your code will be reviewed by the maintainers. They will confirm at least the following: 43 | 44 | - Tests run successfully (unit, coverage, integration, style). 45 | - Contribution policy has been followed. 46 | 47 | Two (human) reviewers will need to sign off on your pull request before it can be merged. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2020 Expedia, Inc. 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include versioneer.py 2 | include map_maker/_version.py 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | clean: 2 | find . -name "*.pyc" -exec rm {} \; 3 | 4 | documentation: 5 | sphinx-build -E sphinx-docs/ docs/html/ 6 | 7 | test: clean 8 | pytest --cov-config .coveragerc \ 9 | --cov=map_maker \ 10 | tests/ 11 | 12 | lint: 13 | flake8 map_maker/ 14 | flake8 tests/ 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Map Maker 2 | 3 | Simple maps from the command line. 4 | 5 | ![Build](https://github.com/ExpediaGroup/map-maker/workflows/Python%20package/badge.svg) [![Coverage Status](https://coveralls.io/repos/github/ExpediaGroup/map-maker/badge.svg?branch=main)](https://coveralls.io/github/ExpediaGroup/map-maker?branch=main) 6 | 7 | ## API Docs 8 | 9 | [https://expediagroup.github.io/map-maker/](https://expediagroup.github.io/map-maker/) 10 | 11 | ## Installation 12 | 13 | Installation is simple: install with `pip` (or `pip3` depending on your setup) 14 | 15 | ``` 16 | pip install map_maker 17 | ``` 18 | 19 | ## Quickstart 20 | 21 | To test, run the following command (in the cloned repository): 22 | 23 | ``` 24 | map_maker data/bigfoot_sightings.csv data/texas_cities.csv 25 | ``` 26 | 27 | After it runs, a browser opens with a map of Bigfoot sightings in Texas, along with some polygons and lines around major metropolitan areas. 28 | 29 | ![](data/texas_sample.png) 30 | 31 | By default map maker uses Bokeh as a backend. 32 | To see the same map with Folium / Leaflet, run this command: 33 | 34 | ``` 35 | map_maker data/bigfoot_sightings.csv data/texas_cities.csv --backend folium 36 | ``` 37 | 38 | ![](data/texas_sample_folium.png) 39 | 40 | In the project directory you'll see a file `map.html` - this is the map file. 41 | 42 | ## How to Use - Command Line 43 | 44 | As of right now, the data must be in csv format with the following fields: 45 | 46 | | name | type | description | 47 | | ----- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- | 48 | | shape | string | A shape encoded as WKT or GeoJSON in EPSG:4326 lon/lat coordinates. | 49 | | color | string | The color for the shape. For polygons this is fill and line color. Optional - default black. | 50 | | alpha | float | The alpha value of the shape. For polygons this is the fill alpha. Set to zero for a transparent polygon. Optional - default light blue. | 51 | | size | float | For points, the size of the point. For polygons and lines it's the width of the line. Default: 2.0 | 52 | | other | anything | Other columns are turned into tooltips on the map. Tooltip behavior is controlled with `--tooltip`. | 53 | 54 | Other fields are permitted, but the above are required. 55 | 56 | This is the only required argument to run map maker. 57 | Map maker supports multiple csv files, which can be useful if you need different tooltip fields for different datasets (i.e. polygons get one kind of tooltip, points get another). 58 | Options are: 59 | 60 | ``` 61 | Usage: map_maker [OPTIONS] [MAP_DATA_FILES]... 62 | 63 | Creates a map from the input files 64 | 65 | Arguments: 66 | 67 | MAP_DATA_FILES - The csv input file(s), requires the following columns: 68 | 69 | shape - The shape in WKT format, in EPSG:4326 lat/lon coordinates. 70 | 71 | color [optional] - The color as a string. 72 | 73 | alpha [optional] - The alpha as a float. 74 | 75 | size [optional] - For points, the size of the point. For linestrings 76 | and polygons, the width of the lines. 77 | 78 | others [optional] - Other fields turn into tooltips. 79 | 80 | Supports multiple files, which can be useful if the tooltip columns are 81 | different. 82 | 83 | Options: 84 | -h, --plot-height INTEGER The height of the plot. Default: 800 85 | -w, --plot-width INTEGER The width of the plot. Default: 800 86 | -s, --point-size FLOAT The size of the points. Default: 2.0. 87 | -l, --polygon-line-width FLOAT Line width of the polygon outline. Default 88 | 2.0. Set to 0 to disable polygon outlines. 89 | -L, --linestring-width FLOAT Width of the linestrings. Default 2.0. 90 | -o, --output-file TEXT The name of the output file for the map. 91 | Default: map.html. 92 | -t, --tooltip [points|linestrings|polygons] 93 | Whether to display tooltips for the points, 94 | linestrings, or polygons. Stackable. 95 | Default: points. 96 | -b, --backend [bokeh|folium] The backend to draw the map with. Current 97 | choices are folium, bokeh. Default: bokeh 98 | --help Show this message and exit. 99 | 100 | ``` 101 | 102 | ## How to Use - Library 103 | 104 | As a library, `map_maker` exposes one function: `make_map_plot`. 105 | `make_map_plot` is imported from one of two backends: [Bokeh](https://bokeh.pydata.org/en/latest/docs/user_guide.html) or [Folium](https://python-visualization.github.io/folium/). 106 | Bokeh draws the map using custom Javascript, while Folium is powered by [Leaflet](https://leafletjs.com/), a popular Javascript mapping library. 107 | 108 | The function can be imported as follows: 109 | 110 | ``` 111 | from map_maker.bokeh import make_map_plot 112 | from map_maker.folium import make_map_plot 113 | ``` 114 | 115 | Both return a map as their respective plot objects, but they take the same data structure. 116 | The `tiles` keyword argument is different for both. 117 | Consult the documentation for Bokeh and Folium if you want to change the tiles. 118 | 119 | `make_map_plot` returns a populated map, built from a list of dictionaries with the same schema as the CSV file command line input. 120 | 121 | ```python 122 | # If shape1 and shape2 are Shapely objects in EPSG:4326, 123 | map_data = [ 124 | { 125 | "shape": shape1, 126 | "color": "red", 127 | "alpha": 0.1, 128 | "size": 1.0, 129 | "number": 5 # <- becomes a tooltip 130 | }, 131 | { 132 | "shape": shape2, 133 | "color": "blue" # alpha, color and size are optional, just like CSV. 134 | "number": 1 # <- becomes a tooltip 135 | } 136 | ] 137 | 138 | map_plot = make_map_plot(map_data) 139 | show(map_plot) 140 | ``` 141 | 142 | `shape1` and `shape2` can be Shapely shapes, WKT strings, GeoJSON strings or dictionaries. 143 | 144 | It has the following signature: 145 | 146 | ```python 147 | def make_map_plot( 148 | map_data, 149 | plot_height=800, 150 | plot_width=800, 151 | tiles="cartodbpositron", 152 | # The size fields are defaults. They're overridden by entries in map_data. 153 | point_size=2, 154 | polygon_line_width=2, 155 | linestring_width=2, 156 | tooltips={"points"}, # Can also be linestrings and polygons. 157 | ) 158 | ``` 159 | 160 | To see more detailed examples, check the `notebooks/` directory. 161 | 162 | ## Development 163 | 164 | ### Environment Setup 165 | 166 | For development we've created a [conda](https://docs.conda.io/en/latest/) environment with the development dependencies. 167 | [Miniconda](https://docs.conda.io/en/latest/miniconda.html) is the simplest but regular Anaconda installations will work too. 168 | 169 | To get started developing, clone the repo and build the development conda environment 170 | 171 | ``` 172 | # Create the environment. 173 | conda env create -f environment.yaml 174 | # Activate it. 175 | conda activate map-maker 176 | ``` 177 | 178 | Then install the package as editable 179 | 180 | ``` 181 | # pip or pip3 depending on your setup. 182 | pip install -e . 183 | ``` 184 | 185 | Note conda isn't required - you can use a [virtualenv](https://virtualenv.pypa.io/en/latest/) if you like and install the following packages yourself: 186 | 187 | - flake8 188 | - pytest 189 | - pytest-cov 190 | - sphinx 191 | - sphinx_rtd_theme 192 | 193 | Once those are installed you then need to run `pip install -e .` inside the virtualenv to install a local copy of the package for editing and testing. 194 | 195 | ### Make Targets 196 | 197 | Once the environment and package is installed, you can run the make targets for testing, linting, and building the docs. 198 | 199 | ``` 200 | # Runs pytest with pytest-cov for code coverage. 201 | make test 202 | 203 | # Runs flake8 on the `map_maker` and `tests` directories. 204 | make lint 205 | 206 | # Builds the sphinx docs. 207 | make documentation 208 | ``` 209 | 210 | ## Contents 211 | 212 | `Makefile` - for development. Testing, linting and doc targets. 213 | 214 | `README.md` - Documentation. 215 | 216 | `map_maker/` - Source code. 217 | 218 | `environment.yaml` - Contains the runtime dependencies for development. 219 | 220 | `data` - Sample csvs and screen shots of outputs. 221 | 222 | `setup.py` - Defines python packaging. 223 | 224 | `sphinx-docs` - Documentation. 225 | 226 | `MANIFEST.in / setup.cfg / versioneer.py` - For versioneer. 227 | 228 | `notebooks` - Example notebooks. 229 | 230 | # Legal 231 | 232 | This project is available under the [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0.html). 233 | 234 | Copyright 2020 Expedia, Inc. 235 | -------------------------------------------------------------------------------- /data/bigfoot_sightings.csv: -------------------------------------------------------------------------------- 1 | shape,color,alpha,date,classification 2 | POINT (-95.5425 32.7943),black,1.0,1996-12-22,Class A 3 | POINT (-95.61056000000001 32.69028),black,1.0,2004-03-25,Class A 4 | POINT (-95.46671000000001 32.85232),black,1.0,2006-02-01,Class A 5 | POINT (-97.83750000000001 33.29083),black,1.0,2003-10-10,Class A 6 | POINT (-99.295 34.31),black,1.0,2016-07-23,Class B 7 | POINT (-97.67403 30.51559),black,1.0,2017-06-23,Class A 8 | POINT (-96.25 29.38333),black,1.0,2002-04-29,Class A 9 | POINT (-95.75645 30.812),black,1.0,1961-11-11,Class B 10 | POINT (-97.06667 28.725),black,1.0,1997-08-22,Class B 11 | POINT (-95.80556 32.57306),black,1.0,2004-08-30,Class A 12 | POINT (-94.86958 32.58125),black,1.0,1976-10-01,Class A 13 | POINT (-94.41528 30.775),black,1.0,2003-12-12,Class A 14 | POINT (-94.96611 31.22333),black,1.0,1995-11-15,Class C 15 | POINT (-98.08611000000001 30.38667),black,1.0,2004-04-24,Class A 16 | POINT (-100.4161 31.45417),black,1.0,2004-11-19,Class A 17 | POINT (-99.18778 33.16972),black,1.0,1988-10-15,Class A 18 | POINT (-97.44499999999999 32.84),black,1.0,1976-12-23,Class B 19 | POINT (-100.545 30.566),black,1.0,2006-07-19,Class B 20 | POINT (-95.12872 32.24271),black,1.0,1972-07-15,Class A 21 | POINT (-95.12381000000001 30.43703),black,1.0,1990-05-18,Class A 22 | POINT (-93.87194 31.56),black,1.0,2004-02-03,Class B 23 | POINT (-94.89986 31.87791),black,1.0,1980-09-15,Class C 24 | POINT (-94.63458 31.91708),black,1.0,1998-01-11,Class A 25 | POINT (-94.965 32.27444000000001),black,1.0,2003-10-08,Class A 26 | POINT (-94.90528 32.35306),black,1.0,2003-12-12,Class A 27 | POINT (-94.86833 32.28944),black,1.0,2003-12-30,Class B 28 | POINT (-94.7375 32.33528),black,1.0,2003-12-30,Class A 29 | POINT (-94.68333 32.01667),black,1.0,2004-11-26,Class A 30 | POINT (-94.95061 31.86794),black,1.0,2017-11-15,Class A 31 | POINT (-95.3 30.63333),black,1.0,2004-01-08,Class A 32 | POINT (-95.09166999999999 30.475),black,1.0,2004-09-12,Class A 33 | POINT (-95.12305000000001 30.55),black,1.0,2004-11-24,Class B 34 | POINT (-95.13 30.46),black,1.0,2005-06-27,Class B 35 | POINT (-95.17966 30.31363),black,1.0,2007-11-15,Class A 36 | POINT (-95.31999999999999 32.13972),black,1.0,2003-10-22,Class A 37 | POINT (-95.21666999999999 32.18333),black,1.0,2004-10-03,Class A 38 | POINT (-95.41817 32.51556),black,1.0,2004-10-03,Class B 39 | POINT (-95.35257 32.19202),black,1.0,2011-04-03,Class A 40 | POINT (-97.02500000000001 32.675),black,1.0,2016-04-02,Class B 41 | POINT (-100.4219 31.44815),black,1.0,2006-08-01,Class B 42 | POINT (-94.47902999999999 31.0232),black,1.0,2005-06-15,Class A 43 | POINT (-95.48672000000001 32.31565),black,1.0,2015-10-30,Class A 44 | POINT (-95.52722 30.61667000000001),black,1.0,1975-07-04,Class A 45 | POINT (-95.37666 30.75),black,1.0,1996-12-31,Class A 46 | POINT (-95.58334000000001 30.52361),black,1.0,2001-09-11,Class A 47 | POINT (-95.63 30.6),black,1.0,2005-01-28,Class A 48 | POINT (-96.29222 29.17805),black,1.0,2004-09-12,Class A 49 | POINT (-100.6767 35.93985),black,1.0,2014-07-14,Class A 50 | POINT (-95.16028 33.64555),black,1.0,1982-04-15,Class A 51 | POINT (-101.667 34.93243),black,1.0,2006-10-21,Class A 52 | POINT (-94.89778 30.73861),black,1.0,1964-04-15,Class A 53 | POINT (-102.6922 31.27278),black,1.0,2000-02-15,Class A 54 | POINT (-97.75 32.75),black,1.0,1976-11-30,Class B 55 | POINT (-94.20695000000001 32.1),black,1.0,1992-11-25,Class B 56 | POINT (-98.32980000000001 32.8269),black,1.0,1969-07-20,Class A 57 | POINT (-93.8117 30.12615),black,1.0,2007-08-13,Class A 58 | POINT (-93.7 30.8),black,1.0,1981-11-25,Class A 59 | POINT (-94.33334000000001 31.33333),black,1.0,2003-10-15,Class B 60 | POINT (-98.53998 31.4771),black,1.0,2007-11-17,Class B 61 | POINT (-96.04472 28.8),black,1.0,2004-06-30,Class B 62 | POINT (-101.5361 33.45278),black,1.0,2001-01-06,Class B 63 | POINT (-96.01666 28.97944),black,1.0,2005-01-17,Class A 64 | POINT (-95.5 29),black,1.0,2005-02-01,Class B 65 | POINT (-95.60834 30.23333),black,1.0,1983-07-15,Class A 66 | POINT (-95.31999999999999 30.31667),black,1.0,2002-04-17,Class A 67 | POINT (-95.71278 30.52694),black,1.0,2004-10-29,Class B 68 | POINT (-95.36166 30.405),black,1.0,2004-11-26,Class B 69 | POINT (-95.28653 30.3343),black,1.0,2005-05-02,Class A 70 | POINT (-95.47556 30.14378),black,1.0,2005-10-18,Class B 71 | POINT (-95.77907999999999 30.54366),black,1.0,2006-04-01,Class B 72 | POINT (-95.72607000000001 30.52897),black,1.0,2016-01-04,Class B 73 | POINT (-94.72333999999999 33.03333),black,1.0,2004-11-27,Class A 74 | POINT (-93.68333 30.76667),black,1.0,2004-09-06,Class B 75 | POINT (-93.61667 30.72028),black,1.0,2004-09-26,Class A 76 | POINT (-98.38333 32.83333),black,1.0,2004-10-31,Class B 77 | POINT (-98.34489000000001 32.817),black,1.0,2008-01-10,Class B 78 | POINT (-94.37444000000001 32.04361),black,1.0,2003-12-28,Class A 79 | POINT (-94.2 32.05806),black,1.0,2004-04-11,Class A 80 | POINT (-94.14306000000001 32.01083),black,1.0,2005-01-18,Class A 81 | POINT (-98.03333000000001 32.7),black,1.0,2003-10-17,Class A 82 | POINT (-97.83334000000001 32.78611),black,1.0,2004-11-20,Class A 83 | POINT (-98 32.755),black,1.0,2010-10-15,Class B 84 | POINT (-97.61 32.675),black,1.0,2011-06-30,Class A 85 | POINT (-97.61 32.675),black,1.0,2011-08-27,Class A 86 | POINT (-102.3881 31.06278),black,1.0,2005-02-01,Class A 87 | POINT (-94.70139 30.71139),black,1.0,2005-04-18,Class A 88 | POINT (-94.58 30.71139),black,1.0,2005-04-26,Class B 89 | POINT (-94.66652999999999 30.66986),black,1.0,2005-05-08,Class B 90 | POINT (-95.06667 33.67055999999999),black,1.0,1987-11-15,Class A 91 | POINT (-95.27361000000001 33.66666),black,1.0,2002-11-02,Class A 92 | POINT (-95.19722 33.845),black,1.0,2003-11-16,Class A 93 | POINT (-95.70860999999999 31.40444),black,1.0,1930-09-30,Class B 94 | POINT (-97.28360000000001 30.40335),black,1.0,2014-12-08,Class A 95 | POINT (-98.05500000000001 31.11611),black,1.0,1977-08-15,Class A 96 | POINT (-100.4104 29.24665),black,1.0,1994-08-13,Class B 97 | POINT (-97.52499 32.15),black,1.0,1986-11-01,Class B 98 | POINT (-103.7439 30.77722),black,1.0,2003-10-08,Class A 99 | POINT (-93.94 30.41667),black,1.0,1983-12-24,Class A 100 | POINT (-98.34999999999999 33.13333),black,1.0,2003-10-21,Class A 101 | POINT (-96.10222 32.94722),black,1.0,2003-12-24,Class A 102 | POINT (-101.3903 32.24083),black,1.0,2004-10-03,Class A 103 | POINT (-95.48833 31.35833),black,1.0,1980-06-30,Class A 104 | POINT (-95.54277999999999 33.33),black,1.0,2004-01-09,Class B 105 | POINT (-95.58334000000001 30.95972),black,1.0,2004-01-09,Class B 106 | POINT (-95.20690999999999 31.50773),black,1.0,2006-08-27,Class B 107 | POINT (-95.59744999999999 31.1683),black,1.0,2007-12-20,Class A 108 | POINT (-95.38226 31.55668),black,1.0,2017-03-15,Class A 109 | POINT (-96.00306 33.19944),black,1.0,2003-12-29,Class A 110 | POINT (-95.84 33.25),black,1.0,2015-01-02,Class B 111 | POINT (-98.40000000000001 33.2),black,1.0,2015-07-13,Class B 112 | POINT (-98.325 33.275),black,1.0,2016-07-02,Class A 113 | POINT (-98.3 33.375),black,1.0,2016-07-18,Class B 114 | POINT (-96.70999999999999 29.02611),black,1.0,2004-09-22,Class A 115 | POINT (-93.97199999999999 30.83315),black,1.0,2003-01-15,Class A 116 | POINT (-93.89 30.43444),black,1.0,2005-04-25,Class B 117 | POINT (-93.97194 30.33778),black,1.0,2005-08-14,Class B 118 | POINT (-94.04528999999999 30.7114),black,1.0,2013-11-02,Class B 119 | POINT (-94.20775999999999 31.05591),black,1.0,2014-03-25,Class B 120 | POINT (-97.54944 32.17194),black,1.0,2003-12-10,Class A 121 | POINT (-95.62860999999999 33.82722),black,1.0,2001-12-29,Class B 122 | POINT (-95.83638999999999 33.82083),black,1.0,2004-01-13,Class A 123 | POINT (-95.89323 31.18523),black,1.0,2018-02-13,Class B 124 | POINT (-95.10654 30.302),black,1.0,1980-04-19,Class A 125 | POINT (-94.84999999999999 30.26667),black,1.0,2004-05-04,Class A 126 | POINT (-94.79333 30.26861),black,1.0,2004-06-03,Class A 127 | POINT (-94.81999999999999 30.10305),black,1.0,2004-06-15,Class A 128 | POINT (-94.86667 30.08333),black,1.0,2004-07-22,Class A 129 | POINT (-94.83334000000001 29.88528),black,1.0,2004-08-19,Class A 130 | POINT (-94.86667 30.41667),black,1.0,2004-11-26,Class A 131 | POINT (-94.84999999999999 30.01667),black,1.0,2005-01-17,Class A 132 | POINT (-95.00833 30.40833),black,1.0,2005-02-02,Class A 133 | POINT (-98.05 32.53333),black,1.0,2003-12-12,Class B 134 | POINT (-95.59999999999999 32.16139),black,1.0,2004-12-03,Class A 135 | POINT (-100.5 36),black,1.0,2000-07-05,Class A 136 | POINT (-97.81083 29.385),black,1.0,2003-10-08,Class B 137 | POINT (-94.81806 32.4125),black,1.0,1978-06-30,Class B 138 | POINT (-96.53 33.75556),black,1.0,1975-06-30,Class B 139 | POINT (-97.77225 29.32862),black,1.0,1993-04-15,Class A 140 | POINT (-96.2 31.87333),black,1.0,2004-04-28,Class B 141 | POINT (-95.24917000000001 33.34139),black,1.0,2003-12-30,Class B 142 | POINT (-95.8775 33.46333),black,1.0,2003-12-08,Class A 143 | POINT (-98.09233999999999 32.04417),black,1.0,2005-03-19,Class A 144 | POINT (-96.80417 32.5175),black,1.0,1938-06-30,Class B 145 | POINT (-98.68000000000001 32.375),black,1.0,1977-09-30,Class B 146 | POINT (-100.8361 33.62167),black,1.0,2003-11-06,Class B 147 | POINT (-97.02298999999999 33.33614),black,1.0,1990-09-14,Class B 148 | POINT (-97.03784 33.27303),black,1.0,2008-04-08,Class B 149 | POINT (-97.05 33.25),black,1.0,2017-11-25,Class B 150 | POINT (-97.18000000000001 33.04),black,1.0,2017-12-20,Class B 151 | POINT (-96.69583 32.26889),black,1.0,1964-06-30,Class B 152 | POINT (-96.66 32.36),black,1.0,1972-07-15,Class A 153 | POINT (-96.63 32.375),black,1.0,1988-04-15,Class A 154 | POINT (-96.27306 33.59),black,1.0,2003-12-15,Class B 155 | POINT (-95.90694000000001 33.61666),black,1.0,2004-01-30,Class A 156 | POINT (-95.92831 33.79094),black,1.0,2009-08-22,Class B 157 | POINT (-95.23958 33.36867),black,1.0,2004-10-20,Class A 158 | POINT (-96.12524000000001 31.93425),black,1.0,2009-08-25,Class A 159 | POINT (-96.84806 33.69),black,1.0,2003-12-31,Class A 160 | POINT (-96.78917 33.71472),black,1.0,2004-02-19,Class A 161 | POINT (-96.76199 33.81435),black,1.0,2006-07-04,Class B 162 | POINT (-94.90000000000001 32.51667),black,1.0,2004-08-16,Class A 163 | POINT (-97.47971 29.30877000000001),black,1.0,2010-05-15,Class B 164 | POINT (-94.3 32.68),black,1.0,1979-04-15,Class A 165 | POINT (-94.59222 32.68945),black,1.0,2003-11-29,Class A 166 | POINT (-94.16665999999999 32.8),black,1.0,2004-02-22,Class A 167 | POINT (-94.2 32.62194),black,1.0,2004-02-27,Class A 168 | POINT (-94.52 32.62389),black,1.0,2004-02-27,Class A 169 | POINT (-94.15000000000001 32.58333),black,1.0,2004-02-28,Class A 170 | POINT (-94.37361 32.65),black,1.0,2004-02-28,Class B 171 | POINT (-97.815 32.475),black,1.0,2011-10-19,Class B 172 | POINT (-95.76861 33.48),black,1.0,1996-12-15,Class A 173 | POINT (-98.03082999999999 29.57444000000001),black,1.0,1993-07-01,Class B 174 | POINT (-100.5167 31.84167),black,1.0,2004-01-22,Class B 175 | POINT (-98.22499999999999 33.675),black,1.0,2015-06-17,Class A 176 | POINT (-95.37101 31.82347),black,1.0,1981-04-30,Class A 177 | POINT (-94.25556 33.18528),black,1.0,1997-07-15,Class B 178 | POINT (-96.55 30.33333),black,1.0,1972-07-15,Class B 179 | POINT (-94.11611000000001 33.37917),black,1.0,1978-10-28,Class B 180 | POINT (-97.5 32.15),black,1.0,1979-08-25,Class A 181 | POINT (-98.68465 29.56835),black,1.0,2008-09-15,Class A 182 | POINT (-94.76362 31.30853),black,1.0,1968-10-18,Class A 183 | POINT (-95.6669 31.6212),black,1.0,2001-06-15,Class A 184 | POINT (-95.68333 31.78333),black,1.0,2003-10-17,Class B 185 | POINT (-94.45095000000001 31.2371),black,1.0,1979-08-15,Class A 186 | POINT (-98.56143 29.26258),black,1.0,2014-01-22,Class B 187 | POINT (-94.35722 33.28333),black,1.0,2004-02-19,Class A 188 | POINT (-96.59166999999999 30.525),black,1.0,2005-01-17,Class B 189 | POINT (-96.42506 30.55738),black,1.0,2017-09-02,Class B 190 | POINT (-94.52925 33.05685),black,1.0,1998-10-15,Class B 191 | POINT (-94.59999999999999 32.9),black,1.0,2004-03-01,Class B 192 | POINT (-94.25694 33.07639),black,1.0,2004-08-10,Class A 193 | POINT (-94.32138999999999 33.09625),black,1.0,2005-04-21,Class A 194 | POINT (-94.14798999999999 33.17162),black,1.0,2014-10-23,Class B 195 | POINT (-94.12258 33.2784),black,1.0,2014-11-29,Class B 196 | POINT (-95.01264 31.90791),black,1.0,2003-10-22,Class A 197 | POINT (-98.15555999999999 29.79893),black,1.0,2012-09-15,Class B 198 | POINT (-98.15854 29.52533),black,1.0,2015-01-09,Class A 199 | POINT (-97.04361 33.48028),black,1.0,1990-05-30,Class A 200 | POINT (-96.96333 33.81861),black,1.0,2004-05-24,Class B 201 | POINT (-97.05903000000001 33.77528),black,1.0,2004-12-27,Class A 202 | POINT (-97.59945999999999 31.37756),black,1.0,2004-12-01,Class B 203 | POINT (-95.77 33.24722),black,1.0,2003-12-15,Class B 204 | POINT (-95.82056 33.26222),black,1.0,2003-12-16,Class A 205 | -------------------------------------------------------------------------------- /data/texas_cities.csv: -------------------------------------------------------------------------------- 1 | shape,color,alpha,city,size 2 | "POLYGON ((-97.22350292727579 30.26616919987027, -97.22623625137895 30.22197191747919, -97.23393993043679 30.17821940127733, -97.24653344599081 30.13533208660833, -97.26388973914614 30.09372157455523, -97.28583664961917 30.05378672643568, -97.31215875798264 30.01590989689431, -97.34259960700051 29.9804533388066, -97.37686427581362 29.94775581117659, -97.4146222789758 29.9181294190158, -97.45551076090537 29.89185671185636, -97.4991379551622 29.86918806509103, -97.5450868770413 29.85033936576154, -97.59291921725431 29.83549002174735, -97.64217940391153 29.82478131054827, -97.69239879959494 29.81831508102085, -97.7431 29.81615281852924, -97.79380120040504 29.81831508102085, -97.84402059608847 29.82478131054827, -97.89328078274569 29.83549002174735, -97.9411131229587 29.85033936576154, -97.98706204483778 29.86918806509103, -98.03068923909461 29.89185671185636, -98.0715777210242 29.9181294190158, -98.10933572418637 29.94775581117659, -98.14360039299947 29.9804533388066, -98.17404124201735 30.01590989689431, -98.2003633503808 30.05378672643568, -98.22231026085385 30.09372157455523, -98.23966655400918 30.13533208660833, -98.25226006956319 30.17821940127733, -98.25996374862103 30.22197191747919, -98.26269707272421 30.26616919987027, -98.26042707814055 30.31038598788612, -98.25316892683567 30.35419627162079, -98.2409860190461 30.39717739646515, -98.22398963635209 30.43891415732379, -98.20233810862176 30.47900284245881, -98.17623550314832 30.51705518660599, -98.14592983968386 30.55270219302227, -98.11171084082292 30.5855977845897, -98.07390723321356 30.61542224505967, -98.03288362126298 30.64188541299734, -97.98903696122173 30.66472959300075, -97.94279266962967 30.68373215132397, -97.8946004059256 30.69870776612192, -97.84492957439835 30.7095103061286, -97.79426459542952 30.71603431563855, -97.7431 30.71821608812668, -97.69193540457046 30.71603431563855, -97.64127042560166 30.7095103061286, -97.59159959407438 30.69870776612193, -97.54340733037031 30.68373215132397, -97.49716303877825 30.66472959300075, -97.453316378737 30.64188541299734, -97.41229276678644 30.61542224505967, -97.37448915917709 30.5855977845897, -97.34027016031612 30.55270219302228, -97.30996449685168 30.517055186606, -97.28386189137822 30.47900284245882, -97.2622103636479 30.43891415732379, -97.24521398095389 30.39717739646515, -97.23303107316433 30.3541962716208, -97.22577292185944 30.31038598788612, -97.22350292727579 30.26616919987027, -97.22350292727579 30.26616919987027))",lightblue,0.2,Austin,2.5 3 | "POLYGON ((-97.97834927165552 29.42310357869992, -97.98105205093982 29.37890029029344, -97.9886839215369 29.33514118160966, -98.00116532009127 29.29224678095873, -98.01837050844138 29.25062880429557, -98.0401289909185 29.21068624676017, -98.06622733280157 29.17280161206356, -98.09641135632232 29.13733731306515, -98.13038868843758 29.10463227487791, -98.16783163276672 29.07499876965875, -98.2083803365944 29.04871950991759, -98.25164622261408 29.02604502472159, -98.29721565410162 29.00719134059742, -98.34465380141927 28.99233798625473, -98.39350867713028 28.98162633748139, -98.44331530652505 28.97515831570595, -98.4936 28.97299545079909, -98.54388469347495 28.97515831570595, -98.59369132286972 28.98162633748139, -98.64254619858075 28.99233798625473, -98.6899843458984 29.00719134059742, -98.73555377738592 29.02604502472159, -98.7788196634056 29.04871950991759, -98.81936836723328 29.07499876965875, -98.85681131156244 29.10463227487791, -98.89078864367768 29.13733731306515, -98.92097266719843 29.17280161206356, -98.9470710090815 29.21068624676017, -98.96882949155864 29.25062880429557, -98.98603467990873 29.29224678095873, -98.99851607846311 29.33514118160966, -99.00614794906018 29.37890029029344, -99.0088507283445 29.42310357869992, -99.00659202288566 29.46732571730274, -98.99938716411022 29.51114065240984, -98.98729930851967 29.55412571100706, -98.97043907262541 29.59586569421435, -98.94896369646411 29.63595691943874, -98.92307573444327 29.6740111709436, -98.89302127756621 29.70965951859968, -98.85908771673195 29.7425559650817, -98.82160106271067 29.77238088275622, -98.78092284445178 29.79884420300338, -98.73744661345718 29.82168832273986, -98.6915940879046 29.84069069546968, -98.64381097588033 29.85566607727309, -98.5945625223182 29.86646840172498, -98.54432882888928 29.87299226177207, -98.4936 29.87517398103587, -98.44287117111074 29.87299226177207, -98.39263747768183 29.86646840172498, -98.34338902411969 29.8556660772731, -98.29560591209541 29.84069069546968, -98.24975338654284 29.82168832273986, -98.20627715554824 29.79884420300338, -98.16559893728933 29.77238088275622, -98.12811228326805 29.7425559650817, -98.0941787224338 29.70965951859969, -98.06412426555676 29.6740111709436, -98.0382363035359 29.63595691943874, -98.01676092737459 29.59586569421436, -97.99990069148035 29.55412571100707, -97.9878128358898 29.51114065240984, -97.98060797711436 29.46732571730274, -97.97834927165552 29.42310357869992, -97.97834927165552 29.42310357869992))",lightblue,0.2,San Antonio,2.5 4 | "POLYGON ((-96.26332323120107 32.77556329501405, -96.2661551392288 32.73138447383263, -96.27409108399705 32.68765222752245, -96.28704746690268 32.64478670499, -96.30489296366524 32.60319915338926, -96.32745003329144 32.56328802174849, -96.35449683689413 32.52543520550353, -96.385769540654 32.4900024647453, -96.4209649752289 32.45732804688847, -96.4597436223366 32.42772354222453, -96.5017328980049 32.4014709984568, -96.54653070104391 32.37882031784505, -96.59370919459501 32.35998695802537, -96.64281878810283 32.34514995493036, -96.69339228669693 32.33445028352372, -96.74494917472299 32.32798956929265, -96.79700000000001 32.32582916061954, -96.84905082527702 32.32798956929265, -96.90060771330307 32.33445028352372, -96.95118121189718 32.34514995493036, -97.000290805405 32.35998695802537, -97.0474692989561 32.37882031784505, -97.09226710199511 32.4014709984568, -97.13425637766341 32.42772354222453, -97.17303502477111 32.45732804688847, -97.20823045934601 32.4900024647453, -97.23950316310588 32.52543520550353, -97.26654996670857 32.56328802174848, -97.28910703633477 32.60319915338926, -97.30695253309733 32.64478670499, -97.31990891600296 32.68765222752244, -97.32784486077121 32.73138447383263, -97.33067676879894 32.77556329501405, -97.3283698429972 32.81976364294841, -97.32093871017011 32.86355964254468, -97.30844757308559 32.90652869571424, -97.2910098794305 32.94825557790741, -97.26878749944663 32.9883364871487, -97.24198940919368 33.02638300499369, -97.2108698820347 33.06202592873645, -97.17572619701649 33.09491893456926, -97.13689587922663 33.12474203227585, -97.09475349382402 33.1512047734567, -97.04970702211409 33.17404917726238, -97.00219385460981 33.19305234015435, -96.95267644230539 33.20802869931354, -96.90163765321134 33.2188319229512, -96.84957588637754 33.22535640489695, -96.79700000000001 33.22753834538967, -96.74442411362247 33.22535640489695, -96.69236234678867 33.2188319229512, -96.64132355769462 33.20802869931355, -96.59180614539019 33.19305234015435, -96.54429297788592 33.17404917726238, -96.49924650617599 33.1512047734567, -96.45710412077338 33.12474203227585, -96.41827380298352 33.09491893456926, -96.38313011796531 33.06202592873645, -96.35201059080633 33.0263830049937, -96.32521250055339 32.98833648714871, -96.3029901205695 32.94825557790741, -96.28555242691442 32.90652869571425, -96.2730612898299 32.86355964254469, -96.2656301570028 32.81976364294841, -96.26332323120107 32.77556329501405, -96.26332323120107 32.77556329501405))",lightblue,0.2,Dallas,2.5 5 | "POLYGON ((-94.85283773661263 29.75938993355712, -94.85555255242065 29.71518902836866, -94.86321270836898 29.67143253564737, -94.87573826467887 29.62854094681859, -94.8930029628049 29.58692593225815, -94.9148356513102 29.54698643400474, -94.9410221137005 29.5091048966164, -94.97130727441314 29.47364366946468, -95.00539775699791 29.44094161174174, -95.04296476673061 29.4113109292703, -95.08364726842713 29.38503426987834, -95.12705542902903 29.36236210164189, -95.17277429357155 29.34351039572697, -95.22036766238409 29.32865863288574, -95.26938213677651 29.31794814989501, -95.31935130000883 29.31148083937879, -95.3698 29.3093182135426, -95.42024869999116 29.31148083937879, -95.47021786322347 29.31794814989501, -95.51923233761589 29.32865863288574, -95.56682570642843 29.34351039572697, -95.61254457097097 29.36236210164189, -95.65595273157285 29.38503426987834, -95.69663523326939 29.4113109292703, -95.73420224300209 29.44094161174174, -95.76829272558685 29.47364366946468, -95.7985778862995 29.5091048966164, -95.82476434868978 29.54698643400474, -95.8465970371951 29.58692593225815, -95.86386173532112 29.62854094681859, -95.87638729163102 29.67143253564737, -95.88404744757935 29.71518902836866, -95.88676226338737 29.75938993355712, -95.88449912072385 29.80360994904438, -95.87727328396572 29.84742303850543, -95.86514800529349 29.89040653622155, -95.84823416284208 29.93214524083893, -95.82668942557619 29.97223545819324, -95.80071694347156 30.01028895288992, -95.77056356691433 30.04593676836364, -95.73651760491894 30.0788328756255, -95.69890613771659 30.10865761187943, -95.65809190537496 30.1351208716781, -95.61446980024188 30.15796501530889, -95.5684629970152 30.17696746165863, -95.52051875997276 30.1919429358907, -95.47110397218822 30.20274534585479, -95.42070043625539 30.20926926519546, -95.3698 30.21145100557402, -95.31889956374459 30.20926926519546, -95.26849602781178 30.20274534585479, -95.21908124002722 30.19194293589071, -95.17113700298479 30.17696746165863, -95.12513019975812 30.15796501530889, -95.08150809462502 30.1351208716781, -95.04069386228339 30.10865761187943, -95.00308239508104 30.0788328756255, -94.96903643308566 30.04593676836365, -94.93888305652844 30.01028895288992, -94.91291057442379 29.97223545819325, -94.89136583715791 29.93214524083894, -94.8744519947065 29.89040653622155, -94.86232671603427 29.84742303850543, -94.85510087927614 29.80360994904438, -94.85283773661263 29.75938993355712, -94.85283773661263 29.75938993355712))",lightblue,0.2,Houston,2.5 6 | "LINESTRING (-97.7431 30.2672, -96.797 32.7767)",green,1.0,Austin-Dallas,3.0 7 | "LINESTRING (-96.797 32.7767, -95.3698 29.7604)",green,1.0,Dallas-Houston,3.0 8 | "LINESTRING (-95.3698 29.7604, -98.4936 29.4241)",green,1.0,Houston-SanAntonio,3.0 9 | -------------------------------------------------------------------------------- /data/texas_sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaGroup/map-maker/95fd76cdb9adade6862473068b5fcc1ceeb9f6ee/data/texas_sample.png -------------------------------------------------------------------------------- /data/texas_sample_folium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaGroup/map-maker/95fd76cdb9adade6862473068b5fcc1ceeb9f6ee/data/texas_sample_folium.png -------------------------------------------------------------------------------- /environment.yaml: -------------------------------------------------------------------------------- 1 | name: map-maker 2 | channels: 3 | - defaults 4 | dependencies: 5 | - python=3.6 6 | - pip: 7 | - pytest 8 | - pytest-cov 9 | - sphinx 10 | - sphinx_rtd_theme 11 | - flake8 12 | - black 13 | -------------------------------------------------------------------------------- /map_maker/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Expedia, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | from ._version import get_versions 18 | 19 | __version__ = get_versions()["version"] 20 | del get_versions 21 | -------------------------------------------------------------------------------- /map_maker/_version.py: -------------------------------------------------------------------------------- 1 | # This file helps to compute a version number in source trees obtained from 2 | # git-archive tarball (such as those provided by githubs download-from-tag 3 | # feature). Distribution tarballs (built by setup.py sdist) and build 4 | # directories (produced by setup.py build) will contain a much shorter file 5 | # that just contains the computed version number. 6 | 7 | # This file is released into the public domain. Generated by 8 | # versioneer-0.18 (https://github.com/warner/python-versioneer) 9 | 10 | """Git implementation of _version.py.""" 11 | 12 | import errno 13 | import os 14 | import re 15 | import subprocess 16 | import sys 17 | 18 | 19 | def get_keywords(): 20 | """Get the keywords needed to look up the version information.""" 21 | # these strings will be replaced by git during git-archive. 22 | # setup.py/versioneer.py will grep for the variable names, so they must 23 | # each be defined on a line of their own. _version.py will just call 24 | # get_keywords(). 25 | git_refnames = " (HEAD -> main)" 26 | git_full = "95fd76cdb9adade6862473068b5fcc1ceeb9f6ee" 27 | git_date = "2020-11-11 12:59:34 -0600" 28 | keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} 29 | return keywords 30 | 31 | 32 | class VersioneerConfig: 33 | """Container for Versioneer configuration parameters.""" 34 | 35 | 36 | def get_config(): 37 | """Create, populate and return the VersioneerConfig() object.""" 38 | # these strings are filled in when 'setup.py versioneer' creates 39 | # _version.py 40 | cfg = VersioneerConfig() 41 | cfg.VCS = "git" 42 | cfg.style = "pep440" 43 | cfg.tag_prefix = "" 44 | cfg.parentdir_prefix = "map_maker-" 45 | cfg.versionfile_source = "map_maker/_version.py" 46 | cfg.verbose = False 47 | return cfg 48 | 49 | 50 | class NotThisMethod(Exception): 51 | """Exception raised if a method is not valid for the current scenario.""" 52 | 53 | 54 | LONG_VERSION_PY = {} 55 | HANDLERS = {} 56 | 57 | 58 | def register_vcs_handler(vcs, method): # decorator 59 | """Decorator to mark a method as the handler for a particular VCS.""" 60 | 61 | def decorate(f): 62 | """Store f in HANDLERS[vcs][method].""" 63 | if vcs not in HANDLERS: 64 | HANDLERS[vcs] = {} 65 | HANDLERS[vcs][method] = f 66 | return f 67 | 68 | return decorate 69 | 70 | 71 | def run_command( 72 | commands, args, cwd=None, verbose=False, hide_stderr=False, env=None 73 | ): 74 | """Call the given command(s).""" 75 | assert isinstance(commands, list) 76 | p = None 77 | for c in commands: 78 | try: 79 | dispcmd = str([c] + args) 80 | # remember shell=False, so use git.cmd on windows, not just git 81 | p = subprocess.Popen( 82 | [c] + args, 83 | cwd=cwd, 84 | env=env, 85 | stdout=subprocess.PIPE, 86 | stderr=(subprocess.PIPE if hide_stderr else None), 87 | ) 88 | break 89 | except EnvironmentError: 90 | e = sys.exc_info()[1] 91 | if e.errno == errno.ENOENT: 92 | continue 93 | if verbose: 94 | print("unable to run %s" % dispcmd) 95 | print(e) 96 | return None, None 97 | else: 98 | if verbose: 99 | print("unable to find command, tried %s" % (commands,)) 100 | return None, None 101 | stdout = p.communicate()[0].strip() 102 | if sys.version_info[0] >= 3: 103 | stdout = stdout.decode() 104 | if p.returncode != 0: 105 | if verbose: 106 | print("unable to run %s (error)" % dispcmd) 107 | print("stdout was %s" % stdout) 108 | return None, p.returncode 109 | return stdout, p.returncode 110 | 111 | 112 | def versions_from_parentdir(parentdir_prefix, root, verbose): 113 | """Try to determine the version from the parent directory name. 114 | 115 | Source tarballs conventionally unpack into a directory that includes both 116 | the project name and a version string. We will also support searching up 117 | two directory levels for an appropriately named parent directory 118 | """ 119 | rootdirs = [] 120 | 121 | for i in range(3): 122 | dirname = os.path.basename(root) 123 | if dirname.startswith(parentdir_prefix): 124 | return { 125 | "version": dirname[len(parentdir_prefix) :], 126 | "full-revisionid": None, 127 | "dirty": False, 128 | "error": None, 129 | "date": None, 130 | } 131 | else: 132 | rootdirs.append(root) 133 | root = os.path.dirname(root) # up a level 134 | 135 | if verbose: 136 | print( 137 | "Tried directories %s but none started with prefix %s" 138 | % (str(rootdirs), parentdir_prefix) 139 | ) 140 | raise NotThisMethod("rootdir doesn't start with parentdir_prefix") 141 | 142 | 143 | @register_vcs_handler("git", "get_keywords") 144 | def git_get_keywords(versionfile_abs): 145 | """Extract version information from the given file.""" 146 | # the code embedded in _version.py can just fetch the value of these 147 | # keywords. When used from setup.py, we don't want to import _version.py, 148 | # so we do it with a regexp instead. This function is not used from 149 | # _version.py. 150 | keywords = {} 151 | try: 152 | f = open(versionfile_abs, "r") 153 | for line in f.readlines(): 154 | if line.strip().startswith("git_refnames ="): 155 | mo = re.search(r'=\s*"(.*)"', line) 156 | if mo: 157 | keywords["refnames"] = mo.group(1) 158 | if line.strip().startswith("git_full ="): 159 | mo = re.search(r'=\s*"(.*)"', line) 160 | if mo: 161 | keywords["full"] = mo.group(1) 162 | if line.strip().startswith("git_date ="): 163 | mo = re.search(r'=\s*"(.*)"', line) 164 | if mo: 165 | keywords["date"] = mo.group(1) 166 | f.close() 167 | except EnvironmentError: 168 | pass 169 | return keywords 170 | 171 | 172 | @register_vcs_handler("git", "keywords") 173 | def git_versions_from_keywords(keywords, tag_prefix, verbose): 174 | """Get version information from git keywords.""" 175 | if not keywords: 176 | raise NotThisMethod("no keywords at all, weird") 177 | date = keywords.get("date") 178 | if date is not None: 179 | # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant 180 | # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 181 | # -like" string, which we must then edit to make compliant), because 182 | # it's been around since git-1.5.3, and it's too difficult to 183 | # discover which version we're using, or to work around using an 184 | # older one. 185 | date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) 186 | refnames = keywords["refnames"].strip() 187 | if refnames.startswith("$Format"): 188 | if verbose: 189 | print("keywords are unexpanded, not using") 190 | raise NotThisMethod("unexpanded keywords, not a git-archive tarball") 191 | refs = set([r.strip() for r in refnames.strip("()").split(",")]) 192 | # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of 193 | # just "foo-1.0". If we see a "tag: " prefix, prefer those. 194 | TAG = "tag: " 195 | tags = set([r[len(TAG) :] for r in refs if r.startswith(TAG)]) 196 | if not tags: 197 | # Either we're using git < 1.8.3, or there really are no tags. We use 198 | # a heuristic: assume all version tags have a digit. The old git %d 199 | # expansion behaves like git log --decorate=short and strips out the 200 | # refs/heads/ and refs/tags/ prefixes that would let us distinguish 201 | # between branches and tags. By ignoring refnames without digits, we 202 | # filter out many common branch names like "release" and 203 | # "stabilization", as well as "HEAD" and "master". 204 | tags = set([r for r in refs if re.search(r"\d", r)]) 205 | if verbose: 206 | print("discarding '%s', no digits" % ",".join(refs - tags)) 207 | if verbose: 208 | print("likely tags: %s" % ",".join(sorted(tags))) 209 | for ref in sorted(tags): 210 | # sorting will prefer e.g. "2.0" over "2.0rc1" 211 | if ref.startswith(tag_prefix): 212 | r = ref[len(tag_prefix) :] 213 | if verbose: 214 | print("picking %s" % r) 215 | return { 216 | "version": r, 217 | "full-revisionid": keywords["full"].strip(), 218 | "dirty": False, 219 | "error": None, 220 | "date": date, 221 | } 222 | # no suitable tags, so version is "0+unknown", but full hex is still there 223 | if verbose: 224 | print("no suitable tags, using unknown + full revision id") 225 | return { 226 | "version": "0+unknown", 227 | "full-revisionid": keywords["full"].strip(), 228 | "dirty": False, 229 | "error": "no suitable tags", 230 | "date": None, 231 | } 232 | 233 | 234 | @register_vcs_handler("git", "pieces_from_vcs") 235 | def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): 236 | """Get version from 'git describe' in the root of the source tree. 237 | 238 | This only gets called if the git-archive 'subst' keywords were *not* 239 | expanded, and _version.py hasn't already been rewritten with a short 240 | version string, meaning we're inside a checked out source tree. 241 | """ 242 | GITS = ["git"] 243 | if sys.platform == "win32": 244 | GITS = ["git.cmd", "git.exe"] 245 | 246 | out, rc = run_command( 247 | GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True 248 | ) 249 | if rc != 0: 250 | if verbose: 251 | print("Directory %s not under git control" % root) 252 | raise NotThisMethod("'git rev-parse --git-dir' returned error") 253 | 254 | # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] 255 | # if there isn't one, this yields HEX[-dirty] (no NUM) 256 | describe_out, rc = run_command( 257 | GITS, 258 | [ 259 | "describe", 260 | "--tags", 261 | "--dirty", 262 | "--always", 263 | "--long", 264 | "--match", 265 | "%s*" % tag_prefix, 266 | ], 267 | cwd=root, 268 | ) 269 | # --long was added in git-1.5.5 270 | if describe_out is None: 271 | raise NotThisMethod("'git describe' failed") 272 | describe_out = describe_out.strip() 273 | full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) 274 | if full_out is None: 275 | raise NotThisMethod("'git rev-parse' failed") 276 | full_out = full_out.strip() 277 | 278 | pieces = {} 279 | pieces["long"] = full_out 280 | pieces["short"] = full_out[:7] # maybe improved later 281 | pieces["error"] = None 282 | 283 | # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] 284 | # TAG might have hyphens. 285 | git_describe = describe_out 286 | 287 | # look for -dirty suffix 288 | dirty = git_describe.endswith("-dirty") 289 | pieces["dirty"] = dirty 290 | if dirty: 291 | git_describe = git_describe[: git_describe.rindex("-dirty")] 292 | 293 | # now we have TAG-NUM-gHEX or HEX 294 | 295 | if "-" in git_describe: 296 | # TAG-NUM-gHEX 297 | mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe) 298 | if not mo: 299 | # unparseable. Maybe git-describe is misbehaving? 300 | pieces["error"] = ( 301 | "unable to parse git-describe output: '%s'" % describe_out 302 | ) 303 | return pieces 304 | 305 | # tag 306 | full_tag = mo.group(1) 307 | if not full_tag.startswith(tag_prefix): 308 | if verbose: 309 | fmt = "tag '%s' doesn't start with prefix '%s'" 310 | print(fmt % (full_tag, tag_prefix)) 311 | pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % ( 312 | full_tag, 313 | tag_prefix, 314 | ) 315 | return pieces 316 | pieces["closest-tag"] = full_tag[len(tag_prefix) :] 317 | 318 | # distance: number of commits since tag 319 | pieces["distance"] = int(mo.group(2)) 320 | 321 | # commit: short hex revision ID 322 | pieces["short"] = mo.group(3) 323 | 324 | else: 325 | # HEX: no tags 326 | pieces["closest-tag"] = None 327 | count_out, rc = run_command( 328 | GITS, ["rev-list", "HEAD", "--count"], cwd=root 329 | ) 330 | pieces["distance"] = int(count_out) # total number of commits 331 | 332 | # commit date: see ISO-8601 comment in git_versions_from_keywords() 333 | date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[ 334 | 0 335 | ].strip() 336 | pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) 337 | 338 | return pieces 339 | 340 | 341 | def plus_or_dot(pieces): 342 | """Return a + if we don't already have one, else return a .""" 343 | if "+" in pieces.get("closest-tag", ""): 344 | return "." 345 | return "+" 346 | 347 | 348 | def render_pep440(pieces): 349 | """Build up version string, with post-release "local version identifier". 350 | 351 | Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you 352 | get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty 353 | 354 | Exceptions: 355 | 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] 356 | """ 357 | if pieces["closest-tag"]: 358 | rendered = pieces["closest-tag"] 359 | if pieces["distance"] or pieces["dirty"]: 360 | rendered += plus_or_dot(pieces) 361 | rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) 362 | if pieces["dirty"]: 363 | rendered += ".dirty" 364 | else: 365 | # exception #1 366 | rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) 367 | if pieces["dirty"]: 368 | rendered += ".dirty" 369 | return rendered 370 | 371 | 372 | def render_pep440_pre(pieces): 373 | """TAG[.post.devDISTANCE] -- No -dirty. 374 | 375 | Exceptions: 376 | 1: no tags. 0.post.devDISTANCE 377 | """ 378 | if pieces["closest-tag"]: 379 | rendered = pieces["closest-tag"] 380 | if pieces["distance"]: 381 | rendered += ".post.dev%d" % pieces["distance"] 382 | else: 383 | # exception #1 384 | rendered = "0.post.dev%d" % pieces["distance"] 385 | return rendered 386 | 387 | 388 | def render_pep440_post(pieces): 389 | """TAG[.postDISTANCE[.dev0]+gHEX] . 390 | 391 | The ".dev0" means dirty. Note that .dev0 sorts backwards 392 | (a dirty tree will appear "older" than the corresponding clean one), 393 | but you shouldn't be releasing software with -dirty anyways. 394 | 395 | Exceptions: 396 | 1: no tags. 0.postDISTANCE[.dev0] 397 | """ 398 | if pieces["closest-tag"]: 399 | rendered = pieces["closest-tag"] 400 | if pieces["distance"] or pieces["dirty"]: 401 | rendered += ".post%d" % pieces["distance"] 402 | if pieces["dirty"]: 403 | rendered += ".dev0" 404 | rendered += plus_or_dot(pieces) 405 | rendered += "g%s" % pieces["short"] 406 | else: 407 | # exception #1 408 | rendered = "0.post%d" % pieces["distance"] 409 | if pieces["dirty"]: 410 | rendered += ".dev0" 411 | rendered += "+g%s" % pieces["short"] 412 | return rendered 413 | 414 | 415 | def render_pep440_old(pieces): 416 | """TAG[.postDISTANCE[.dev0]] . 417 | 418 | The ".dev0" means dirty. 419 | 420 | Eexceptions: 421 | 1: no tags. 0.postDISTANCE[.dev0] 422 | """ 423 | if pieces["closest-tag"]: 424 | rendered = pieces["closest-tag"] 425 | if pieces["distance"] or pieces["dirty"]: 426 | rendered += ".post%d" % pieces["distance"] 427 | if pieces["dirty"]: 428 | rendered += ".dev0" 429 | else: 430 | # exception #1 431 | rendered = "0.post%d" % pieces["distance"] 432 | if pieces["dirty"]: 433 | rendered += ".dev0" 434 | return rendered 435 | 436 | 437 | def render_git_describe(pieces): 438 | """TAG[-DISTANCE-gHEX][-dirty]. 439 | 440 | Like 'git describe --tags --dirty --always'. 441 | 442 | Exceptions: 443 | 1: no tags. HEX[-dirty] (note: no 'g' prefix) 444 | """ 445 | if pieces["closest-tag"]: 446 | rendered = pieces["closest-tag"] 447 | if pieces["distance"]: 448 | rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) 449 | else: 450 | # exception #1 451 | rendered = pieces["short"] 452 | if pieces["dirty"]: 453 | rendered += "-dirty" 454 | return rendered 455 | 456 | 457 | def render_git_describe_long(pieces): 458 | """TAG-DISTANCE-gHEX[-dirty]. 459 | 460 | Like 'git describe --tags --dirty --always -long'. 461 | The distance/hash is unconditional. 462 | 463 | Exceptions: 464 | 1: no tags. HEX[-dirty] (note: no 'g' prefix) 465 | """ 466 | if pieces["closest-tag"]: 467 | rendered = pieces["closest-tag"] 468 | rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) 469 | else: 470 | # exception #1 471 | rendered = pieces["short"] 472 | if pieces["dirty"]: 473 | rendered += "-dirty" 474 | return rendered 475 | 476 | 477 | def render(pieces, style): 478 | """Render the given version pieces into the requested style.""" 479 | if pieces["error"]: 480 | return { 481 | "version": "unknown", 482 | "full-revisionid": pieces.get("long"), 483 | "dirty": None, 484 | "error": pieces["error"], 485 | "date": None, 486 | } 487 | 488 | if not style or style == "default": 489 | style = "pep440" # the default 490 | 491 | if style == "pep440": 492 | rendered = render_pep440(pieces) 493 | elif style == "pep440-pre": 494 | rendered = render_pep440_pre(pieces) 495 | elif style == "pep440-post": 496 | rendered = render_pep440_post(pieces) 497 | elif style == "pep440-old": 498 | rendered = render_pep440_old(pieces) 499 | elif style == "git-describe": 500 | rendered = render_git_describe(pieces) 501 | elif style == "git-describe-long": 502 | rendered = render_git_describe_long(pieces) 503 | else: 504 | raise ValueError("unknown style '%s'" % style) 505 | 506 | return { 507 | "version": rendered, 508 | "full-revisionid": pieces["long"], 509 | "dirty": pieces["dirty"], 510 | "error": None, 511 | "date": pieces.get("date"), 512 | } 513 | 514 | 515 | def get_versions(): 516 | """Get version information or return default if unable to do so.""" 517 | # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have 518 | # __file__, we can work backwards from there to the root. Some 519 | # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which 520 | # case we can only use expanded keywords. 521 | 522 | cfg = get_config() 523 | verbose = cfg.verbose 524 | 525 | try: 526 | return git_versions_from_keywords( 527 | get_keywords(), cfg.tag_prefix, verbose 528 | ) 529 | except NotThisMethod: 530 | pass 531 | 532 | try: 533 | root = os.path.realpath(__file__) 534 | # versionfile_source is the relative path from the top of the source 535 | # tree (where the .git directory might live) to this file. Invert 536 | # this to find the root from __file__. 537 | for i in cfg.versionfile_source.split("/"): 538 | root = os.path.dirname(root) 539 | except NameError: 540 | return { 541 | "version": "0+unknown", 542 | "full-revisionid": None, 543 | "dirty": None, 544 | "error": "unable to find root of source tree", 545 | "date": None, 546 | } 547 | 548 | try: 549 | pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) 550 | return render(pieces, cfg.style) 551 | except NotThisMethod: 552 | pass 553 | 554 | try: 555 | if cfg.parentdir_prefix: 556 | return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) 557 | except NotThisMethod: 558 | pass 559 | 560 | return { 561 | "version": "0+unknown", 562 | "full-revisionid": None, 563 | "dirty": None, 564 | "error": "unable to compute version", 565 | "date": None, 566 | } 567 | -------------------------------------------------------------------------------- /map_maker/bokeh/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Expedia, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | from .map_maker import base_map_plot, make_map_plot 18 | 19 | __all__ = ["base_map_plot", "make_map_plot"] 20 | -------------------------------------------------------------------------------- /map_maker/bokeh/map_maker.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Expedia, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | import bokeh.plotting as bp 18 | import json 19 | 20 | from bokeh.tile_providers import get_provider, Vendors 21 | from bokeh.models import GeoJSONDataSource, HoverTool 22 | from toolz import compose, dissoc 23 | from map_maker.util import extract_geometries, get_bounds, to_geojson 24 | 25 | listmap = compose(list, map) 26 | 27 | 28 | def _get_tooltips(shape_datum): 29 | """ Generate the tooltip specifier for the plot. 30 | """ 31 | return [ 32 | (k, "@{}".format(k)) 33 | for k in dissoc( 34 | shape_datum, "shape", "shape_obj", "color", "alpha", "size" 35 | ) 36 | ] 37 | 38 | 39 | def base_map_plot( 40 | x_range, 41 | y_range, 42 | tools="pan,wheel_zoom,reset", 43 | plot_height=800, 44 | plot_width=800, 45 | tiles=get_provider(Vendors.CARTODBPOSITRON_RETINA), 46 | ): 47 | 48 | """ Builds a blank map tile plot. 49 | 50 | Builds a blank figure with map tiles, but without axes, labels and ticks. 51 | 52 | Parameters 53 | ---------- 54 | x_range : :obj:`list` of float 55 | The minimum and maximum x coordinates for the plot. 56 | 57 | y_range : :obj:`list` of float 58 | The minimum and maximum y coordinates for the plot. 59 | 60 | tools : str 61 | A comma separated list of strings defining the tools for the Bokeh 62 | plot widget. 63 | 64 | plot_height : float 65 | The height of the plot in px. 66 | 67 | plot_width : float 68 | The width of the plot in px. 69 | 70 | tiles : :obj:`bokeh.models.tiles.WMSTTileSource` 71 | The tile source for the blank map. Default is CartoDB Positron Retina. 72 | 73 | Returns 74 | ------- 75 | fig : :obj:`bokeh.plotting.Figure` 76 | The Bokeh Figure for the blank map. 77 | """ 78 | 79 | # Draw the main figure. 80 | p = bp.figure( 81 | tools=tools, 82 | plot_width=plot_width, 83 | plot_height=plot_height, 84 | x_range=x_range, 85 | y_range=y_range, 86 | # These are for axes and stuff. 87 | min_border=0, 88 | min_border_left=0, 89 | min_border_right=0, 90 | min_border_top=0, 91 | min_border_bottom=0, 92 | ) 93 | 94 | p.axis.visible = False 95 | p.xgrid.grid_line_color = None 96 | p.ygrid.grid_line_color = None 97 | 98 | # Now add the map tiles. 99 | p.add_tile(tiles) 100 | 101 | return p 102 | 103 | 104 | def make_map_plot( 105 | map_data, 106 | plot_height=800, 107 | plot_width=800, 108 | tiles=get_provider(Vendors.CARTODBPOSITRON_RETINA), 109 | point_size=2, 110 | polygon_line_width=2, 111 | linestring_width=2, 112 | tooltips={"points"}, 113 | ): 114 | """ Creates a Bokeh map from a list of dicts with geometry / style info. 115 | 116 | Takes a list of dictionaries, each of which has the following fields: 117 | 118 | .. code-block :: python 119 | 120 | { 121 | "shape": # WKT | GeoJSON String | Geo-Interface | Shapely Geometry 122 | "alpha": # Alpha value of the fill. 123 | "color": # Color of the shape 124 | } 125 | 126 | Parameters 127 | ---------- 128 | map_data : :obj:`list` of :obj:`dict` 129 | A list of dictionaries defining the shape and style to map with the 130 | fields defined above. 131 | 132 | plot_height : int 133 | The height of the plot in px. 134 | 135 | plot_width : int 136 | The width of the plot in px. 137 | 138 | tiles : :obj:`bokeh.models.tiles.WMSTTileSource` 139 | The tile source for the map. Default is CartoDB Positron Retina. 140 | 141 | point_size : float, default 2. 142 | The size of the points to plot, if there are any points present. 143 | 144 | polygon_line_width: float, default 2. 145 | The line width of the polygon outline, if there are any polygons 146 | present. Set to 0 to disable polygon outlines. 147 | 148 | linestring_width : float, default 2. 149 | The line width of the linestring, if there are any linestrings present. 150 | 151 | tooltips : set 152 | The labels of tooltips to display, can be "points", "linestrings", 153 | "polygons" or both any combination. 154 | 155 | Returns 156 | ------- 157 | fig : :obj:`bokeh.plotting.Figure` 158 | The Bokeh Figure for the map. 159 | """ 160 | # Collect the points and polygons. 161 | # The data's the same, with a 'shape_obj' field containing the shape 162 | # object. 163 | points, linestrings, polygons = extract_geometries(map_data) 164 | 165 | min_x, min_y, max_x, max_y = get_bounds(points + polygons) 166 | 167 | map_figure = base_map_plot( 168 | [min_x, max_x], 169 | [min_y, max_y], 170 | plot_height=plot_height, 171 | plot_width=plot_width, 172 | tiles=tiles, 173 | ) 174 | 175 | # NOTE This assumes the first point / polygon is representative of all 176 | # fields. For CLI this is true, for a library call this may not be true. 177 | point_tooltips = _get_tooltips(points[0]) if points else None 178 | linestring_tooltips = ( 179 | _get_tooltips(linestrings[0]) if linestrings else None 180 | ) 181 | polygon_tooltips = _get_tooltips(polygons[0]) if polygons else None 182 | 183 | if point_tooltips and ("points" in tooltips): 184 | hover = HoverTool(tooltips=point_tooltips, names=["points"]) 185 | 186 | map_figure.add_tools(hover) 187 | 188 | if linestring_tooltips and ("linestrings" in tooltips): 189 | hover = HoverTool(tooltips=linestring_tooltips, names=["linestrings"]) 190 | 191 | map_figure.add_tools(hover) 192 | 193 | if polygon_tooltips and ("polygons" in tooltips): 194 | hover = HoverTool(tooltips=polygon_tooltips, names=["polygons"]) 195 | 196 | map_figure.add_tools(hover) 197 | 198 | if points: 199 | map_figure.circle( 200 | x="x", 201 | y="y", 202 | size="size", 203 | alpha="alpha", # Pulled from geojson properties. 204 | color="color", # Pulled from geojson properties. 205 | line_width=0, 206 | source=GeoJSONDataSource( 207 | geojson=json.dumps( 208 | to_geojson(points, "black", 0.3, point_size) 209 | ) 210 | ), 211 | name="points", 212 | ) 213 | if linestrings: 214 | map_figure.multi_line( 215 | xs="xs", 216 | ys="ys", 217 | line_alpha="alpha", 218 | color="color", 219 | line_width="size", 220 | source=GeoJSONDataSource( 221 | geojson=json.dumps( 222 | to_geojson(linestrings, "black", 0.3, linestring_width) 223 | ) 224 | ), 225 | name="linestrings", 226 | ) 227 | if polygons: 228 | map_figure.patches( 229 | xs="xs", 230 | ys="ys", 231 | fill_alpha="alpha", # Pulled from geojson properties. 232 | color="color", # Pulled from geojson properties. 233 | line_width="size", 234 | source=GeoJSONDataSource( 235 | geojson=json.dumps( 236 | to_geojson(polygons, "lightblue", 0.3, polygon_line_width) 237 | ) 238 | ), 239 | name="polygons", 240 | ) 241 | 242 | return map_figure 243 | -------------------------------------------------------------------------------- /map_maker/cli.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Expedia, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | 18 | import click 19 | import csv 20 | import sys 21 | import webbrowser 22 | import os 23 | 24 | import bokeh.plotting as bp 25 | 26 | from csv import DictReader 27 | 28 | from bokeh.io import show 29 | 30 | from map_maker.bokeh import make_map_plot as make_map_plot_bokeh 31 | from map_maker.folium import make_map_plot as make_map_plot_folium 32 | 33 | csv.field_size_limit(sys.maxsize) 34 | 35 | 36 | @click.command() 37 | @click.argument("map_data_files", type=str, nargs=-1) 38 | @click.option( 39 | "--plot-height", 40 | "-h", 41 | type=int, 42 | help="The height of the plot. Default: 800", 43 | default=800, 44 | ) 45 | @click.option( 46 | "--plot-width", 47 | "-w", 48 | type=int, 49 | help="The width of the plot. Default: 800", 50 | default=800, 51 | ) 52 | @click.option( 53 | "--point-size", 54 | "-s", 55 | type=float, 56 | help="The size of the points. Default: 2.0.", 57 | default=2.0, 58 | ) 59 | @click.option( 60 | "--polygon-line-width", 61 | "-l", 62 | type=float, 63 | help="Line width of the polygon outline. Default 2.0. " 64 | "Set to 0 to disable polygon outlines.", 65 | default=2.0, 66 | ) 67 | @click.option( 68 | "--linestring-width", 69 | "-L", 70 | type=float, 71 | help="Width of the linestrings. Default 2.0. ", 72 | default=2.0, 73 | ) 74 | @click.option( 75 | "--output-file", 76 | "-o", 77 | type=str, 78 | help="The name of the output file for the map. Default: map.html.", 79 | default="map.html", 80 | ) 81 | @click.option( 82 | "--tooltip", 83 | "-t", 84 | type=click.Choice(["points", "linestrings", "polygons"]), 85 | multiple=True, 86 | default={"points"}, 87 | help="Whether to display tooltips for the points, linestrings, " 88 | "or polygons. Stackable. Default: points.", 89 | ) 90 | @click.option( 91 | "--backend", 92 | "-b", 93 | type=click.Choice(["bokeh", "folium"]), 94 | default="bokeh", 95 | help="The backend to draw the map with. Current choices are folium, bokeh." 96 | " Default: bokeh", 97 | ) 98 | def cli( 99 | map_data_files, 100 | plot_height, 101 | plot_width, 102 | point_size, 103 | polygon_line_width, 104 | linestring_width, 105 | output_file, 106 | tooltip, 107 | backend, 108 | ): 109 | """ 110 | Creates a map from the input files 111 | 112 | Arguments: \n 113 | MAP_DATA_FILES - The csv input file(s), requires the following columns:\n 114 | 115 | shape - The shape in WKT format, in EPSG:4326 lat/lon coordinates.\n 116 | color [optional] - The color as a string.\n 117 | alpha [optional] - The alpha as a float.\n 118 | size [optional] - For points, the size of the point. For linestrings 119 | and polygons, the width of the lines. \n 120 | others [optional] - Other fields turn into tooltips.\n 121 | 122 | Supports multiple files, which can be useful if the tooltip columns are 123 | different. 124 | """ 125 | 126 | map_data = [] 127 | for map_data_file in map_data_files: 128 | with open(map_data_file, "r") as map_file: 129 | reader = DictReader(map_file) 130 | map_data.extend([line for line in reader]) 131 | 132 | if backend == "bokeh": 133 | map_plot = make_map_plot_bokeh( 134 | map_data, 135 | plot_height=plot_height, 136 | plot_width=plot_width, 137 | point_size=point_size, 138 | polygon_line_width=polygon_line_width, 139 | linestring_width=linestring_width, 140 | tooltips=tooltip, 141 | ) 142 | 143 | bp.output_file(output_file) 144 | show(map_plot) 145 | elif backend == "folium": 146 | map_plot = make_map_plot_folium( 147 | map_data, 148 | plot_height=plot_height, 149 | plot_width=plot_width, 150 | point_size=point_size, 151 | polygon_line_width=polygon_line_width, 152 | linestring_width=linestring_width, 153 | tooltips=tooltip, 154 | ) 155 | 156 | map_plot.save(output_file) 157 | webbrowser.open( 158 | "file://{}".format(os.path.realpath(output_file)), new=2 159 | ) 160 | -------------------------------------------------------------------------------- /map_maker/folium/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Expedia, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | from .map_maker import make_map_plot 18 | 19 | __all__ = ["make_map_plot"] 20 | -------------------------------------------------------------------------------- /map_maker/folium/map_maker.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Expedia, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | import folium 18 | 19 | from map_maker.util import extract_geometries, get_bounds, to_geojson 20 | from toolz import dissoc 21 | 22 | 23 | def _get_tooltip_keys(shape_datum): 24 | return list( 25 | dissoc( 26 | shape_datum, "shape", "shape_obj", "color", "alpha", "size" 27 | ).keys() 28 | ) 29 | 30 | 31 | def _get_tooltip_values(shape_datum): 32 | return [ 33 | f"{k}: {v}" 34 | for k, v in dissoc( 35 | shape_datum, "shape", "shape_obj", "color", "alpha", "size" 36 | ).items() 37 | ] 38 | 39 | 40 | def make_map_plot( 41 | map_data, 42 | plot_height=800, 43 | plot_width=800, 44 | tiles="cartodbpositron", 45 | point_size=2, 46 | polygon_line_width=2, 47 | linestring_width=2, 48 | tooltips={"points"}, 49 | ): 50 | """ Creates a Folium map from a list of dicts with geometry / style info. 51 | 52 | Takes a list of dictionaries, each of which has the following fields: 53 | 54 | .. code-block :: python 55 | 56 | { 57 | "shape": # WKT | GeoJSON String | Geo-Interface | Shapely Geometry 58 | "alpha": # Alpha value of the fill. 59 | "color": # Color of the shape 60 | } 61 | 62 | Parameters 63 | ---------- 64 | map_data : :obj:`list` of :obj:`dict` 65 | A list of dictionaries defining the shape and style to map with the 66 | fields defined above. 67 | 68 | plot_height : int 69 | The height of the plot in px. 70 | 71 | plot_width : int 72 | The width of the plot in px. 73 | 74 | tiles : str 75 | The tile source for the map. Default is CartoDB Positron. 76 | 77 | point_size : float, default 2. 78 | The size of the points to plot, if there are any points present. 79 | 80 | polygon_line_width: float, default 2. 81 | The line width of the polygon outline, if there are any polygons 82 | present. Set to 0 to disable polygon outlines. 83 | 84 | linestring_width : float, default 2. 85 | The line with of the linestring, if there are any linestrings present. 86 | 87 | tooltips : set 88 | The labels of tooltips to display, can be "points", "linestrings", 89 | "polygons" or any combination of those. 90 | 91 | Returns 92 | ------- 93 | fig : :obj:`folium.Map` 94 | The Folium object for the map. 95 | """ 96 | # Collect the points and polygons. 97 | points, linestrings, polygons = extract_geometries(map_data, project=False) 98 | min_x, min_y, max_x, max_y = get_bounds(points + polygons) 99 | 100 | m = folium.Map( 101 | location=[(min_y + max_y) / 2, (min_x + max_x) / 2], 102 | tiles=tiles, 103 | height=plot_height, 104 | width=plot_width, 105 | ) 106 | 107 | m.fit_bounds([(min_y, min_x), (max_y, max_x)]) 108 | 109 | if polygons: 110 | polygon_geojson = to_geojson( 111 | polygons, "lightblue", 0.3, polygon_line_width 112 | ) 113 | folium.GeoJson( 114 | polygon_geojson, 115 | style_function=lambda x: { 116 | "fillColor": x["properties"]["color"], 117 | "color": x["properties"]["color"], 118 | "weight": x["properties"]["size"], 119 | "fillOpacity": x["properties"]["alpha"], 120 | }, 121 | tooltip=folium.GeoJsonTooltip( 122 | _get_tooltip_keys(polygon_geojson["features"][0]["properties"]) 123 | ) 124 | if "polygons" in tooltips 125 | else None, 126 | ).add_to(m) 127 | 128 | if linestrings: 129 | linestring_geojson = to_geojson( 130 | linestrings, "black", 0.3, linestring_width 131 | ) 132 | folium.GeoJson( 133 | linestring_geojson, 134 | style_function=lambda x: { 135 | "color": x["properties"]["color"], 136 | "weight": x["properties"]["size"], 137 | "opacity": x["properties"]["alpha"], 138 | }, 139 | tooltip=folium.GeoJsonTooltip( 140 | _get_tooltip_keys( 141 | linestring_geojson["features"][0]["properties"] 142 | ) 143 | ) 144 | if "linestrings" in tooltips 145 | else None, 146 | ).add_to(m) 147 | 148 | if points: 149 | point_geojson = to_geojson(points, "black", 0.3, point_size) 150 | # Sigh .. have to manually add the circle markers instead of using the 151 | # geoJson function because geojson function doesn't allow custom 152 | # markers for points. 153 | # There's a PR that _should_ fix this: 154 | # https://github.com/python-visualization/folium/pull/957 155 | for point_feature in point_geojson["features"]: 156 | folium.CircleMarker( 157 | # Needs to be reversed because folium expects lat,lon. 158 | location=point_feature["geometry"]["coordinates"][::-1], 159 | # Divide by 2 to match what bokeh does with the size. 160 | radius=point_feature["properties"]["size"] / 2, 161 | color=point_feature["properties"]["color"], 162 | fill_color=point_feature["properties"]["color"], 163 | opacity=point_feature["properties"]["alpha"], 164 | tooltip="\n".join( 165 | _get_tooltip_values(point_feature["properties"]) 166 | ) 167 | if "points" in tooltips 168 | else None, 169 | ).add_to(m) 170 | 171 | return m 172 | -------------------------------------------------------------------------------- /map_maker/util/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Expedia, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | from .util import to_shape, extract_geometries, get_bounds, to_geojson 18 | 19 | __all__ = ["to_shape", "extract_geometries", "get_bounds", "to_geojson"] 20 | -------------------------------------------------------------------------------- /map_maker/util/util.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Expedia, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | import json 18 | import pyproj 19 | import numpy as np 20 | 21 | from contextlib import redirect_stderr 22 | from collections import Mapping 23 | 24 | from toolz import assoc, curry, dissoc, get 25 | from shapely.wkt import loads as wkt_loads 26 | from shapely.errors import WKTReadingError 27 | from shapely.geometry import ( 28 | shape, 29 | Point, 30 | MultiPoint, 31 | LineString, 32 | MultiLineString, 33 | Polygon, 34 | MultiPolygon, 35 | mapping, 36 | ) 37 | from shapely.ops import transform 38 | 39 | 40 | lla_proj = pyproj.Proj(init="epsg:4326") 41 | merc_proj = pyproj.Proj(init="epsg:3857") 42 | lla_to_merc_proj = curry(pyproj.transform)(lla_proj, merc_proj) 43 | lla_to_merc = curry(transform)(lla_to_merc_proj) 44 | 45 | 46 | def to_shape(shape_ser): 47 | """ Deserializes a shape into a Shapely object - can handle WKT, GeoJSON, 48 | Python dictionaries and Shapely types. 49 | """ 50 | if isinstance(shape_ser, str): 51 | try: 52 | # Redirecting stdout because there's a low level exception that 53 | # prints. 54 | with redirect_stderr("/dev/null"): 55 | shape_obj = wkt_loads(shape_ser) 56 | except WKTReadingError: 57 | try: 58 | shape_obj = shape(json.loads(shape_ser)) 59 | except Exception: 60 | raise TypeError( 61 | "{} is not serializable to a shape.".format(str(shape_ser)) 62 | ) 63 | elif isinstance(shape_ser, Mapping): 64 | shape_obj = shape(shape_ser) 65 | elif isinstance( 66 | shape_ser, 67 | ( 68 | Point, 69 | MultiPoint, 70 | LineString, 71 | MultiLineString, 72 | Polygon, 73 | MultiPolygon, 74 | ), 75 | ): 76 | shape_obj = shape_ser 77 | else: 78 | raise TypeError( 79 | "{} is not serializable to a shape.".format(str(shape_ser)) 80 | ) 81 | 82 | return shape_obj 83 | 84 | 85 | @curry 86 | def _add_shape(sd, shape_obj): 87 | return assoc(sd, "shape_obj", shape_obj) 88 | 89 | 90 | def extract_geometries(shape_data, project=True): 91 | """ Enhances the shape data by adding a 'shape_obj' field with the shapely 92 | object, and splits data into points and polygons. 93 | """ 94 | points = [] 95 | linestrings = [] 96 | polygons = [] 97 | for sd in shape_data: 98 | add_shape = _add_shape(sd) 99 | # Deserialize the shape. 100 | shape_obj = to_shape(sd["shape"]) 101 | # Project to web mercator if required. 102 | if project: 103 | shape_obj = lla_to_merc(shape_obj) 104 | if isinstance(shape_obj, Point): 105 | points.append(add_shape(shape_obj)) 106 | elif isinstance(shape_obj, MultiPoint): 107 | # Multipoints aren't supported in Bokeh so we have to convert. 108 | for point in shape_obj: 109 | points.append(add_shape(point)) 110 | elif isinstance(shape_obj, (Polygon, MultiPolygon)): 111 | polygons.append(add_shape(shape_obj)) 112 | elif isinstance(shape_obj, (MultiLineString, LineString)): 113 | linestrings.append(add_shape(shape_obj)) 114 | 115 | return points, linestrings, polygons 116 | 117 | 118 | def get_bounds(shape_data): 119 | """ Gets the bounds of the shapes. Shapes are shape data dictionaries with 120 | the actual shape in 'shape_obj'. 121 | """ 122 | 123 | bounds_arr = np.array([sd["shape_obj"].bounds for sd in shape_data]) 124 | minx = bounds_arr[:, 0].min() 125 | miny = bounds_arr[:, 1].min() 126 | maxx = bounds_arr[:, 2].max() 127 | maxy = bounds_arr[:, 3].max() 128 | return (minx, miny, maxx, maxy) 129 | 130 | 131 | def to_geojson(shape_data, default_color, default_alpha, default_size): 132 | """ Turns the provided shape data dictionaries into GeoJSON strings. Adds 133 | default "color", "alpha", and "size" properties if they aren't present. 134 | """ 135 | return { 136 | "type": "FeatureCollection", 137 | "features": [ 138 | { 139 | "type": "Feature", 140 | "properties": { 141 | # These will be overwritten if present in the dict. 142 | "color": default_color, 143 | # Add a numeric conversion to the special numeric 144 | # properties. 145 | "alpha": float(get("alpha", sd, default_alpha)), 146 | "size": float(get("size", sd, default_size)), 147 | # Anything other than the shapes is a propertiy. 148 | # Eventually these will turn into tooltips. 149 | **dissoc(sd, "shape", "shape_obj", "size", "alpha"), 150 | }, 151 | "geometry": mapping(sd["shape_obj"]), 152 | } 153 | for sd in shape_data 154 | ], 155 | } 156 | -------------------------------------------------------------------------------- /notebooks/bigfoot_cities_texas_bokeh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaGroup/map-maker/95fd76cdb9adade6862473068b5fcc1ceeb9f6ee/notebooks/bigfoot_cities_texas_bokeh.png -------------------------------------------------------------------------------- /notebooks/bigfoot_cities_texas_folium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaGroup/map-maker/95fd76cdb9adade6862473068b5fcc1ceeb9f6ee/notebooks/bigfoot_cities_texas_folium.png -------------------------------------------------------------------------------- /notebooks/bigfoot_texas_bokeh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaGroup/map-maker/95fd76cdb9adade6862473068b5fcc1ceeb9f6ee/notebooks/bigfoot_texas_bokeh.png -------------------------------------------------------------------------------- /notebooks/bigfoot_texas_folium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaGroup/map-maker/95fd76cdb9adade6862473068b5fcc1ceeb9f6ee/notebooks/bigfoot_texas_folium.png -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [versioneer] 2 | VCS = git 3 | style = pep440 4 | versionfile_source = map_maker/_version.py 5 | versionfile_build = map_maker/_version.py 6 | tag_prefix = 7 | parentdir_prefix = map_maker- 8 | 9 | [flake8] 10 | ignore = E203 -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Expedia, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | from setuptools import setup, find_packages 18 | import pathlib 19 | import versioneer 20 | 21 | HERE = pathlib.Path(__file__).parent 22 | README = (HERE / "README.md").read_text() 23 | 24 | setup( 25 | name="map_maker", 26 | version=versioneer.get_version(), 27 | packages=find_packages(exclude=["data/", "scripts/"]), 28 | author="Tim Renner", 29 | install_requires=[ 30 | "click>=7.0", 31 | "toolz>=0.9", 32 | "bokeh>=1.1.0", 33 | "Shapely>=1.6.4.post2", 34 | "pyproj>=1.9.5.1,<2", 35 | "folium>=0.10.0,<1", 36 | ], 37 | entry_points={"console_scripts": ["map_maker=map_maker.cli:cli"]}, 38 | cmdclass=versioneer.get_cmdclass(), 39 | long_description=README, 40 | long_description_content_type="text/markdown", 41 | url="https://github.com/ExpediaGroup/map-maker", 42 | license="Apache 2.0", 43 | classifiers=[ 44 | # From https://pypi.org/classifiers/ 45 | "Development Status :: 4 - Beta", 46 | "License :: OSI Approved :: Apache Software License", 47 | "Programming Language :: Python :: 3.6", 48 | "Programming Language :: Python :: 3.7", 49 | "Programming Language :: Python :: 3.8", 50 | ], 51 | ) 52 | -------------------------------------------------------------------------------- /sphinx-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 = map_maker 8 | SOURCEDIR = . 9 | BUILDDIR = ../docs 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /sphinx-docs/_static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaGroup/map-maker/95fd76cdb9adade6862473068b5fcc1ceeb9f6ee/sphinx-docs/_static/.gitkeep -------------------------------------------------------------------------------- /sphinx-docs/_templates/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaGroup/map-maker/95fd76cdb9adade6862473068b5fcc1ceeb9f6ee/sphinx-docs/_templates/.gitkeep -------------------------------------------------------------------------------- /sphinx-docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # map_maker documentation build configuration file, created by 5 | # sphinx-quickstart on Mon Dec 18 16:15:29 2017. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | # 20 | import os 21 | import sys 22 | 23 | sys.path.insert(0, os.path.abspath(".")) 24 | sys.path.insert(0, os.path.abspath("../")) 25 | 26 | import map_maker # noqa 27 | 28 | # -- General configuration ------------------------------------------------ 29 | 30 | # If your documentation needs a minimal Sphinx version, state it here. 31 | # 32 | # needs_sphinx = '1.0' 33 | 34 | # Add any Sphinx extension module names here, as strings. They can be 35 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 36 | # ones. 37 | extensions = [ 38 | "sphinx.ext.autodoc", 39 | "sphinx.ext.napoleon", 40 | "sphinx.ext.autosummary", 41 | "sphinx.ext.mathjax", 42 | "sphinx.ext.githubpages", 43 | ] 44 | 45 | # Add any paths that contain templates here, relative to this directory. 46 | templates_path = ["_templates"] 47 | 48 | # The suffix(es) of source filenames. 49 | # You can specify multiple suffix as a list of string: 50 | # 51 | # source_suffix = ['.rst', '.md'] 52 | source_suffix = ".rst" 53 | 54 | # The master toctree document. 55 | master_doc = "index" 56 | 57 | # General information about the project. 58 | project = "map_maker" 59 | copyright = "2020 Expedia, Inc." 60 | author = "Tim Renner" 61 | 62 | # The version info for the project you're documenting, acts as replacement for 63 | # |version| and |release|, also used in various other places throughout the 64 | # built documents. 65 | # 66 | # The short X.Y version. 67 | version = map_maker.__version__ 68 | # The full version, including alpha/beta/rc tags. 69 | release = map_maker.__version__ 70 | 71 | # The language for content autogenerated by Sphinx. Refer to documentation 72 | # for a list of supported languages. 73 | # 74 | # This is also used if you do content translation via gettext catalogs. 75 | # Usually you set "language" from the command line for these cases. 76 | language = None 77 | 78 | # List of patterns, relative to source directory, that match files and 79 | # directories to ignore when looking for source files. 80 | # This patterns also effect to html_static_path and html_extra_path 81 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] 82 | 83 | # The name of the Pygments (syntax highlighting) style to use. 84 | pygments_style = "sphinx" 85 | 86 | # If true, `todo` and `todoList` produce output, else they produce nothing. 87 | todo_include_todos = False 88 | 89 | 90 | # -- Options for HTML output ---------------------------------------------- 91 | 92 | # The theme to use for HTML and HTML Help pages. See the documentation for 93 | # a list of builtin themes. 94 | # 95 | html_theme = "sphinx_rtd_theme" 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 | 103 | # Add any paths that contain custom static files (such as style sheets) here, 104 | # relative to this directory. They are copied after the builtin static files, 105 | # so a file named "default.css" will overwrite the builtin "default.css". 106 | html_static_path = ["_static"] 107 | 108 | 109 | # -- Options for HTMLHelp output ------------------------------------------ 110 | 111 | # Output file base name for HTML help builder. 112 | htmlhelp_basename = "map_makerdoc" 113 | 114 | 115 | # -- Options for LaTeX output --------------------------------------------- 116 | 117 | latex_elements = { 118 | # The paper size ('letterpaper' or 'a4paper'). 119 | # 120 | # 'papersize': 'letterpaper', 121 | # The font size ('10pt', '11pt' or '12pt'). 122 | # 123 | # 'pointsize': '10pt', 124 | # Additional stuff for the LaTeX preamble. 125 | # 126 | # 'preamble': '', 127 | # Latex figure (float) alignment 128 | # 129 | # 'figure_align': 'htbp', 130 | } 131 | 132 | # Grouping the document tree into LaTeX files. List of tuples 133 | # (source start file, target name, title, 134 | # author, documentclass [howto, manual, or own class]). 135 | latex_documents = [ 136 | ( 137 | master_doc, 138 | "map_maker.tex", 139 | "map\\_maker Documentation", 140 | "Tim Renner", 141 | "manual", 142 | ) 143 | ] 144 | 145 | 146 | # -- Options for manual page output --------------------------------------- 147 | 148 | # One entry per manual page. List of tuples 149 | # (source start file, name, description, authors, manual section). 150 | man_pages = [(master_doc, "map_maker", "map_maker Documentation", [author], 1)] 151 | 152 | 153 | # -- Options for Texinfo output ------------------------------------------- 154 | 155 | # Grouping the document tree into Texinfo files. List of tuples 156 | # (source start file, target name, title, author, 157 | # dir menu entry, description, category) 158 | texinfo_documents = [ 159 | ( 160 | master_doc, 161 | "map_maker", 162 | "map_maker Documentation", 163 | author, 164 | "map_maker", 165 | "One line description of project.", 166 | "Miscellaneous", 167 | ) 168 | ] 169 | 170 | -------------------------------------------------------------------------------- /sphinx-docs/index.rst: -------------------------------------------------------------------------------- 1 | Map Maker API Documentation 2 | ===================================== 3 | 4 | A library and command line tool for quickly creating interactive maps with 5 | Bokeh or Folium. 6 | 7 | This documentation is for module usage. 8 | See the README for command line usage: 9 | https://github.com/ExpediaGroup/map-maker 10 | 11 | Contents 12 | ^^^^^^^^ 13 | 14 | .. toctree:: 15 | :maxdepth: 2 16 | 17 | source/map_maker_bokeh 18 | source/map_maker_folium 19 | 20 | 21 | Indices and tables 22 | ================== 23 | 24 | * :ref:`genindex` 25 | * :ref:`modindex` 26 | * :ref:`search` 27 | -------------------------------------------------------------------------------- /sphinx-docs/source/map_maker_bokeh.rst: -------------------------------------------------------------------------------- 1 | map_maker (Bokeh) 2 | ================= 3 | 4 | These are the functions in the map_maker module. 5 | 6 | 7 | map_maker.bokeh functions 8 | -------------------------- 9 | 10 | .. currentmodule:: map_maker.bokeh.map_maker 11 | 12 | .. autosummary:: 13 | base_map_plot 14 | make_map_plot 15 | 16 | Module contents 17 | --------------- 18 | 19 | .. automodule:: map_maker.bokeh.map_maker 20 | :members: 21 | -------------------------------------------------------------------------------- /sphinx-docs/source/map_maker_folium.rst: -------------------------------------------------------------------------------- 1 | map_maker (Folium) 2 | ================== 3 | 4 | These are the functions in the folium.map_maker module. 5 | 6 | 7 | map_maker.folium functions 8 | -------------------------- 9 | 10 | .. currentmodule:: map_maker.folium.map_maker 11 | 12 | .. autosummary:: 13 | make_map_plot 14 | 15 | Module contents 16 | --------------- 17 | 18 | .. automodule:: map_maker.folium.map_maker 19 | :members: 20 | -------------------------------------------------------------------------------- /tests/test_data/test_data_points.csv: -------------------------------------------------------------------------------- 1 | shape,color,alpha,date,classification 2 | POINT (-95.5425 32.7943),black,1.0,1996-12-22,Class A 3 | POINT (-95.61056000000001 32.69028),black,1.0,2004-03-25,Class A 4 | POINT (-95.46671000000001 32.85232),black,1.0,2006-02-01,Class A 5 | POINT (-97.83750000000001 33.29083),black,1.0,2003-10-10,Class A 6 | POINT (-99.295 34.31),black,1.0,2016-07-23,Class B 7 | POINT (-97.67403 30.51559),black,1.0,2017-06-23,Class A 8 | POINT (-96.25 29.38333),black,1.0,2002-04-29,Class A 9 | POINT (-95.75645 30.812),black,1.0,1961-11-11,Class B 10 | POINT (-97.06667 28.725),black,1.0,1997-08-22,Class B 11 | POINT (-95.80556 32.57306),black,1.0,2004-08-30,Class A 12 | POINT (-94.86958 32.58125),black,1.0,1976-10-01,Class A 13 | POINT (-94.41528 30.775),black,1.0,2003-12-12,Class A 14 | POINT (-94.96611 31.22333),black,1.0,1995-11-15,Class C 15 | POINT (-98.08611000000001 30.38667),black,1.0,2004-04-24,Class A 16 | POINT (-100.4161 31.45417),black,1.0,2004-11-19,Class A 17 | POINT (-99.18778 33.16972),black,1.0,1988-10-15,Class A 18 | POINT (-97.44499999999999 32.84),black,1.0,1976-12-23,Class B 19 | POINT (-100.545 30.566),black,1.0,2006-07-19,Class B 20 | POINT (-95.12872 32.24271),black,1.0,1972-07-15,Class A 21 | POINT (-95.12381000000001 30.43703),black,1.0,1990-05-18,Class A 22 | POINT (-93.87194 31.56),black,1.0,2004-02-03,Class B 23 | POINT (-94.89986 31.87791),black,1.0,1980-09-15,Class C 24 | POINT (-94.63458 31.91708),black,1.0,1998-01-11,Class A 25 | POINT (-94.965 32.27444000000001),black,1.0,2003-10-08,Class A 26 | POINT (-94.90528 32.35306),black,1.0,2003-12-12,Class A 27 | POINT (-94.86833 32.28944),black,1.0,2003-12-30,Class B 28 | POINT (-94.7375 32.33528),black,1.0,2003-12-30,Class A 29 | POINT (-94.68333 32.01667),black,1.0,2004-11-26,Class A 30 | POINT (-94.95061 31.86794),black,1.0,2017-11-15,Class A 31 | POINT (-95.3 30.63333),black,1.0,2004-01-08,Class A 32 | POINT (-95.09166999999999 30.475),black,1.0,2004-09-12,Class A 33 | POINT (-95.12305000000001 30.55),black,1.0,2004-11-24,Class B 34 | POINT (-95.13 30.46),black,1.0,2005-06-27,Class B 35 | POINT (-95.17966 30.31363),black,1.0,2007-11-15,Class A 36 | POINT (-95.31999999999999 32.13972),black,1.0,2003-10-22,Class A 37 | POINT (-95.21666999999999 32.18333),black,1.0,2004-10-03,Class A 38 | POINT (-95.41817 32.51556),black,1.0,2004-10-03,Class B 39 | POINT (-95.35257 32.19202),black,1.0,2011-04-03,Class A 40 | POINT (-97.02500000000001 32.675),black,1.0,2016-04-02,Class B 41 | POINT (-100.4219 31.44815),black,1.0,2006-08-01,Class B 42 | POINT (-94.47902999999999 31.0232),black,1.0,2005-06-15,Class A 43 | POINT (-95.48672000000001 32.31565),black,1.0,2015-10-30,Class A 44 | POINT (-95.52722 30.61667000000001),black,1.0,1975-07-04,Class A 45 | POINT (-95.37666 30.75),black,1.0,1996-12-31,Class A 46 | POINT (-95.58334000000001 30.52361),black,1.0,2001-09-11,Class A 47 | POINT (-95.63 30.6),black,1.0,2005-01-28,Class A 48 | POINT (-96.29222 29.17805),black,1.0,2004-09-12,Class A 49 | POINT (-100.6767 35.93985),black,1.0,2014-07-14,Class A 50 | POINT (-95.16028 33.64555),black,1.0,1982-04-15,Class A 51 | POINT (-101.667 34.93243),black,1.0,2006-10-21,Class A 52 | POINT (-94.89778 30.73861),black,1.0,1964-04-15,Class A 53 | POINT (-102.6922 31.27278),black,1.0,2000-02-15,Class A 54 | POINT (-97.75 32.75),black,1.0,1976-11-30,Class B 55 | POINT (-94.20695000000001 32.1),black,1.0,1992-11-25,Class B 56 | POINT (-98.32980000000001 32.8269),black,1.0,1969-07-20,Class A 57 | POINT (-93.8117 30.12615),black,1.0,2007-08-13,Class A 58 | POINT (-93.7 30.8),black,1.0,1981-11-25,Class A 59 | POINT (-94.33334000000001 31.33333),black,1.0,2003-10-15,Class B 60 | POINT (-98.53998 31.4771),black,1.0,2007-11-17,Class B 61 | POINT (-96.04472 28.8),black,1.0,2004-06-30,Class B 62 | POINT (-101.5361 33.45278),black,1.0,2001-01-06,Class B 63 | POINT (-96.01666 28.97944),black,1.0,2005-01-17,Class A 64 | POINT (-95.5 29),black,1.0,2005-02-01,Class B 65 | POINT (-95.60834 30.23333),black,1.0,1983-07-15,Class A 66 | POINT (-95.31999999999999 30.31667),black,1.0,2002-04-17,Class A 67 | POINT (-95.71278 30.52694),black,1.0,2004-10-29,Class B 68 | POINT (-95.36166 30.405),black,1.0,2004-11-26,Class B 69 | POINT (-95.28653 30.3343),black,1.0,2005-05-02,Class A 70 | POINT (-95.47556 30.14378),black,1.0,2005-10-18,Class B 71 | POINT (-95.77907999999999 30.54366),black,1.0,2006-04-01,Class B 72 | POINT (-95.72607000000001 30.52897),black,1.0,2016-01-04,Class B 73 | POINT (-94.72333999999999 33.03333),black,1.0,2004-11-27,Class A 74 | POINT (-93.68333 30.76667),black,1.0,2004-09-06,Class B 75 | POINT (-93.61667 30.72028),black,1.0,2004-09-26,Class A 76 | POINT (-98.38333 32.83333),black,1.0,2004-10-31,Class B 77 | POINT (-98.34489000000001 32.817),black,1.0,2008-01-10,Class B 78 | POINT (-94.37444000000001 32.04361),black,1.0,2003-12-28,Class A 79 | POINT (-94.2 32.05806),black,1.0,2004-04-11,Class A 80 | POINT (-94.14306000000001 32.01083),black,1.0,2005-01-18,Class A 81 | POINT (-98.03333000000001 32.7),black,1.0,2003-10-17,Class A 82 | POINT (-97.83334000000001 32.78611),black,1.0,2004-11-20,Class A 83 | POINT (-98 32.755),black,1.0,2010-10-15,Class B 84 | POINT (-97.61 32.675),black,1.0,2011-06-30,Class A 85 | POINT (-97.61 32.675),black,1.0,2011-08-27,Class A 86 | POINT (-102.3881 31.06278),black,1.0,2005-02-01,Class A 87 | POINT (-94.70139 30.71139),black,1.0,2005-04-18,Class A 88 | POINT (-94.58 30.71139),black,1.0,2005-04-26,Class B 89 | POINT (-94.66652999999999 30.66986),black,1.0,2005-05-08,Class B 90 | POINT (-95.06667 33.67055999999999),black,1.0,1987-11-15,Class A 91 | POINT (-95.27361000000001 33.66666),black,1.0,2002-11-02,Class A 92 | POINT (-95.19722 33.845),black,1.0,2003-11-16,Class A 93 | POINT (-95.70860999999999 31.40444),black,1.0,1930-09-30,Class B 94 | POINT (-97.28360000000001 30.40335),black,1.0,2014-12-08,Class A 95 | POINT (-98.05500000000001 31.11611),black,1.0,1977-08-15,Class A 96 | POINT (-100.4104 29.24665),black,1.0,1994-08-13,Class B 97 | POINT (-97.52499 32.15),black,1.0,1986-11-01,Class B 98 | POINT (-103.7439 30.77722),black,1.0,2003-10-08,Class A 99 | POINT (-93.94 30.41667),black,1.0,1983-12-24,Class A 100 | POINT (-98.34999999999999 33.13333),black,1.0,2003-10-21,Class A 101 | POINT (-96.10222 32.94722),black,1.0,2003-12-24,Class A 102 | POINT (-101.3903 32.24083),black,1.0,2004-10-03,Class A 103 | POINT (-95.48833 31.35833),black,1.0,1980-06-30,Class A 104 | POINT (-95.54277999999999 33.33),black,1.0,2004-01-09,Class B 105 | POINT (-95.58334000000001 30.95972),black,1.0,2004-01-09,Class B 106 | POINT (-95.20690999999999 31.50773),black,1.0,2006-08-27,Class B 107 | POINT (-95.59744999999999 31.1683),black,1.0,2007-12-20,Class A 108 | POINT (-95.38226 31.55668),black,1.0,2017-03-15,Class A 109 | POINT (-96.00306 33.19944),black,1.0,2003-12-29,Class A 110 | POINT (-95.84 33.25),black,1.0,2015-01-02,Class B 111 | POINT (-98.40000000000001 33.2),black,1.0,2015-07-13,Class B 112 | POINT (-98.325 33.275),black,1.0,2016-07-02,Class A 113 | POINT (-98.3 33.375),black,1.0,2016-07-18,Class B 114 | POINT (-96.70999999999999 29.02611),black,1.0,2004-09-22,Class A 115 | POINT (-93.97199999999999 30.83315),black,1.0,2003-01-15,Class A 116 | POINT (-93.89 30.43444),black,1.0,2005-04-25,Class B 117 | POINT (-93.97194 30.33778),black,1.0,2005-08-14,Class B 118 | POINT (-94.04528999999999 30.7114),black,1.0,2013-11-02,Class B 119 | POINT (-94.20775999999999 31.05591),black,1.0,2014-03-25,Class B 120 | POINT (-97.54944 32.17194),black,1.0,2003-12-10,Class A 121 | POINT (-95.62860999999999 33.82722),black,1.0,2001-12-29,Class B 122 | POINT (-95.83638999999999 33.82083),black,1.0,2004-01-13,Class A 123 | POINT (-95.89323 31.18523),black,1.0,2018-02-13,Class B 124 | POINT (-95.10654 30.302),black,1.0,1980-04-19,Class A 125 | POINT (-94.84999999999999 30.26667),black,1.0,2004-05-04,Class A 126 | POINT (-94.79333 30.26861),black,1.0,2004-06-03,Class A 127 | POINT (-94.81999999999999 30.10305),black,1.0,2004-06-15,Class A 128 | POINT (-94.86667 30.08333),black,1.0,2004-07-22,Class A 129 | POINT (-94.83334000000001 29.88528),black,1.0,2004-08-19,Class A 130 | POINT (-94.86667 30.41667),black,1.0,2004-11-26,Class A 131 | POINT (-94.84999999999999 30.01667),black,1.0,2005-01-17,Class A 132 | POINT (-95.00833 30.40833),black,1.0,2005-02-02,Class A 133 | POINT (-98.05 32.53333),black,1.0,2003-12-12,Class B 134 | POINT (-95.59999999999999 32.16139),black,1.0,2004-12-03,Class A 135 | POINT (-100.5 36),black,1.0,2000-07-05,Class A 136 | POINT (-97.81083 29.385),black,1.0,2003-10-08,Class B 137 | POINT (-94.81806 32.4125),black,1.0,1978-06-30,Class B 138 | POINT (-96.53 33.75556),black,1.0,1975-06-30,Class B 139 | POINT (-97.77225 29.32862),black,1.0,1993-04-15,Class A 140 | POINT (-96.2 31.87333),black,1.0,2004-04-28,Class B 141 | POINT (-95.24917000000001 33.34139),black,1.0,2003-12-30,Class B 142 | POINT (-95.8775 33.46333),black,1.0,2003-12-08,Class A 143 | POINT (-98.09233999999999 32.04417),black,1.0,2005-03-19,Class A 144 | POINT (-96.80417 32.5175),black,1.0,1938-06-30,Class B 145 | POINT (-98.68000000000001 32.375),black,1.0,1977-09-30,Class B 146 | POINT (-100.8361 33.62167),black,1.0,2003-11-06,Class B 147 | POINT (-97.02298999999999 33.33614),black,1.0,1990-09-14,Class B 148 | POINT (-97.03784 33.27303),black,1.0,2008-04-08,Class B 149 | POINT (-97.05 33.25),black,1.0,2017-11-25,Class B 150 | POINT (-97.18000000000001 33.04),black,1.0,2017-12-20,Class B 151 | POINT (-96.69583 32.26889),black,1.0,1964-06-30,Class B 152 | POINT (-96.66 32.36),black,1.0,1972-07-15,Class A 153 | POINT (-96.63 32.375),black,1.0,1988-04-15,Class A 154 | POINT (-96.27306 33.59),black,1.0,2003-12-15,Class B 155 | POINT (-95.90694000000001 33.61666),black,1.0,2004-01-30,Class A 156 | POINT (-95.92831 33.79094),black,1.0,2009-08-22,Class B 157 | POINT (-95.23958 33.36867),black,1.0,2004-10-20,Class A 158 | POINT (-96.12524000000001 31.93425),black,1.0,2009-08-25,Class A 159 | POINT (-96.84806 33.69),black,1.0,2003-12-31,Class A 160 | POINT (-96.78917 33.71472),black,1.0,2004-02-19,Class A 161 | POINT (-96.76199 33.81435),black,1.0,2006-07-04,Class B 162 | POINT (-94.90000000000001 32.51667),black,1.0,2004-08-16,Class A 163 | POINT (-97.47971 29.30877000000001),black,1.0,2010-05-15,Class B 164 | POINT (-94.3 32.68),black,1.0,1979-04-15,Class A 165 | POINT (-94.59222 32.68945),black,1.0,2003-11-29,Class A 166 | POINT (-94.16665999999999 32.8),black,1.0,2004-02-22,Class A 167 | POINT (-94.2 32.62194),black,1.0,2004-02-27,Class A 168 | POINT (-94.52 32.62389),black,1.0,2004-02-27,Class A 169 | POINT (-94.15000000000001 32.58333),black,1.0,2004-02-28,Class A 170 | POINT (-94.37361 32.65),black,1.0,2004-02-28,Class B 171 | POINT (-97.815 32.475),black,1.0,2011-10-19,Class B 172 | POINT (-95.76861 33.48),black,1.0,1996-12-15,Class A 173 | POINT (-98.03082999999999 29.57444000000001),black,1.0,1993-07-01,Class B 174 | POINT (-100.5167 31.84167),black,1.0,2004-01-22,Class B 175 | POINT (-98.22499999999999 33.675),black,1.0,2015-06-17,Class A 176 | POINT (-95.37101 31.82347),black,1.0,1981-04-30,Class A 177 | POINT (-94.25556 33.18528),black,1.0,1997-07-15,Class B 178 | POINT (-96.55 30.33333),black,1.0,1972-07-15,Class B 179 | POINT (-94.11611000000001 33.37917),black,1.0,1978-10-28,Class B 180 | POINT (-97.5 32.15),black,1.0,1979-08-25,Class A 181 | POINT (-98.68465 29.56835),black,1.0,2008-09-15,Class A 182 | POINT (-94.76362 31.30853),black,1.0,1968-10-18,Class A 183 | POINT (-95.6669 31.6212),black,1.0,2001-06-15,Class A 184 | POINT (-95.68333 31.78333),black,1.0,2003-10-17,Class B 185 | POINT (-94.45095000000001 31.2371),black,1.0,1979-08-15,Class A 186 | POINT (-98.56143 29.26258),black,1.0,2014-01-22,Class B 187 | POINT (-94.35722 33.28333),black,1.0,2004-02-19,Class A 188 | POINT (-96.59166999999999 30.525),black,1.0,2005-01-17,Class B 189 | POINT (-96.42506 30.55738),black,1.0,2017-09-02,Class B 190 | POINT (-94.52925 33.05685),black,1.0,1998-10-15,Class B 191 | POINT (-94.59999999999999 32.9),black,1.0,2004-03-01,Class B 192 | POINT (-94.25694 33.07639),black,1.0,2004-08-10,Class A 193 | POINT (-94.32138999999999 33.09625),black,1.0,2005-04-21,Class A 194 | POINT (-94.14798999999999 33.17162),black,1.0,2014-10-23,Class B 195 | POINT (-94.12258 33.2784),black,1.0,2014-11-29,Class B 196 | POINT (-95.01264 31.90791),black,1.0,2003-10-22,Class A 197 | POINT (-98.15555999999999 29.79893),black,1.0,2012-09-15,Class B 198 | POINT (-98.15854 29.52533),black,1.0,2015-01-09,Class A 199 | POINT (-97.04361 33.48028),black,1.0,1990-05-30,Class A 200 | POINT (-96.96333 33.81861),black,1.0,2004-05-24,Class B 201 | POINT (-97.05903000000001 33.77528),black,1.0,2004-12-27,Class A 202 | POINT (-97.59945999999999 31.37756),black,1.0,2004-12-01,Class B 203 | POINT (-95.77 33.24722),black,1.0,2003-12-15,Class B 204 | POINT (-95.82056 33.26222),black,1.0,2003-12-16,Class A 205 | -------------------------------------------------------------------------------- /tests/test_data/test_data_polygons_lines.csv: -------------------------------------------------------------------------------- 1 | shape,color,alpha,city,size 2 | "POLYGON ((-97.22350292727579 30.26616919987027, -97.22623625137895 30.22197191747919, -97.23393993043679 30.17821940127733, -97.24653344599081 30.13533208660833, -97.26388973914614 30.09372157455523, -97.28583664961917 30.05378672643568, -97.31215875798264 30.01590989689431, -97.34259960700051 29.9804533388066, -97.37686427581362 29.94775581117659, -97.4146222789758 29.9181294190158, -97.45551076090537 29.89185671185636, -97.4991379551622 29.86918806509103, -97.5450868770413 29.85033936576154, -97.59291921725431 29.83549002174735, -97.64217940391153 29.82478131054827, -97.69239879959494 29.81831508102085, -97.7431 29.81615281852924, -97.79380120040504 29.81831508102085, -97.84402059608847 29.82478131054827, -97.89328078274569 29.83549002174735, -97.9411131229587 29.85033936576154, -97.98706204483778 29.86918806509103, -98.03068923909461 29.89185671185636, -98.0715777210242 29.9181294190158, -98.10933572418637 29.94775581117659, -98.14360039299947 29.9804533388066, -98.17404124201735 30.01590989689431, -98.2003633503808 30.05378672643568, -98.22231026085385 30.09372157455523, -98.23966655400918 30.13533208660833, -98.25226006956319 30.17821940127733, -98.25996374862103 30.22197191747919, -98.26269707272421 30.26616919987027, -98.26042707814055 30.31038598788612, -98.25316892683567 30.35419627162079, -98.2409860190461 30.39717739646515, -98.22398963635209 30.43891415732379, -98.20233810862176 30.47900284245881, -98.17623550314832 30.51705518660599, -98.14592983968386 30.55270219302227, -98.11171084082292 30.5855977845897, -98.07390723321356 30.61542224505967, -98.03288362126298 30.64188541299734, -97.98903696122173 30.66472959300075, -97.94279266962967 30.68373215132397, -97.8946004059256 30.69870776612192, -97.84492957439835 30.7095103061286, -97.79426459542952 30.71603431563855, -97.7431 30.71821608812668, -97.69193540457046 30.71603431563855, -97.64127042560166 30.7095103061286, -97.59159959407438 30.69870776612193, -97.54340733037031 30.68373215132397, -97.49716303877825 30.66472959300075, -97.453316378737 30.64188541299734, -97.41229276678644 30.61542224505967, -97.37448915917709 30.5855977845897, -97.34027016031612 30.55270219302228, -97.30996449685168 30.517055186606, -97.28386189137822 30.47900284245882, -97.2622103636479 30.43891415732379, -97.24521398095389 30.39717739646515, -97.23303107316433 30.3541962716208, -97.22577292185944 30.31038598788612, -97.22350292727579 30.26616919987027, -97.22350292727579 30.26616919987027))",lightblue,0.2,Austin,2.5 3 | "POLYGON ((-97.97834927165552 29.42310357869992, -97.98105205093982 29.37890029029344, -97.9886839215369 29.33514118160966, -98.00116532009127 29.29224678095873, -98.01837050844138 29.25062880429557, -98.0401289909185 29.21068624676017, -98.06622733280157 29.17280161206356, -98.09641135632232 29.13733731306515, -98.13038868843758 29.10463227487791, -98.16783163276672 29.07499876965875, -98.2083803365944 29.04871950991759, -98.25164622261408 29.02604502472159, -98.29721565410162 29.00719134059742, -98.34465380141927 28.99233798625473, -98.39350867713028 28.98162633748139, -98.44331530652505 28.97515831570595, -98.4936 28.97299545079909, -98.54388469347495 28.97515831570595, -98.59369132286972 28.98162633748139, -98.64254619858075 28.99233798625473, -98.6899843458984 29.00719134059742, -98.73555377738592 29.02604502472159, -98.7788196634056 29.04871950991759, -98.81936836723328 29.07499876965875, -98.85681131156244 29.10463227487791, -98.89078864367768 29.13733731306515, -98.92097266719843 29.17280161206356, -98.9470710090815 29.21068624676017, -98.96882949155864 29.25062880429557, -98.98603467990873 29.29224678095873, -98.99851607846311 29.33514118160966, -99.00614794906018 29.37890029029344, -99.0088507283445 29.42310357869992, -99.00659202288566 29.46732571730274, -98.99938716411022 29.51114065240984, -98.98729930851967 29.55412571100706, -98.97043907262541 29.59586569421435, -98.94896369646411 29.63595691943874, -98.92307573444327 29.6740111709436, -98.89302127756621 29.70965951859968, -98.85908771673195 29.7425559650817, -98.82160106271067 29.77238088275622, -98.78092284445178 29.79884420300338, -98.73744661345718 29.82168832273986, -98.6915940879046 29.84069069546968, -98.64381097588033 29.85566607727309, -98.5945625223182 29.86646840172498, -98.54432882888928 29.87299226177207, -98.4936 29.87517398103587, -98.44287117111074 29.87299226177207, -98.39263747768183 29.86646840172498, -98.34338902411969 29.8556660772731, -98.29560591209541 29.84069069546968, -98.24975338654284 29.82168832273986, -98.20627715554824 29.79884420300338, -98.16559893728933 29.77238088275622, -98.12811228326805 29.7425559650817, -98.0941787224338 29.70965951859969, -98.06412426555676 29.6740111709436, -98.0382363035359 29.63595691943874, -98.01676092737459 29.59586569421436, -97.99990069148035 29.55412571100707, -97.9878128358898 29.51114065240984, -97.98060797711436 29.46732571730274, -97.97834927165552 29.42310357869992, -97.97834927165552 29.42310357869992))",lightblue,0.2,San Antonio,2.5 4 | "POLYGON ((-96.26332323120107 32.77556329501405, -96.2661551392288 32.73138447383263, -96.27409108399705 32.68765222752245, -96.28704746690268 32.64478670499, -96.30489296366524 32.60319915338926, -96.32745003329144 32.56328802174849, -96.35449683689413 32.52543520550353, -96.385769540654 32.4900024647453, -96.4209649752289 32.45732804688847, -96.4597436223366 32.42772354222453, -96.5017328980049 32.4014709984568, -96.54653070104391 32.37882031784505, -96.59370919459501 32.35998695802537, -96.64281878810283 32.34514995493036, -96.69339228669693 32.33445028352372, -96.74494917472299 32.32798956929265, -96.79700000000001 32.32582916061954, -96.84905082527702 32.32798956929265, -96.90060771330307 32.33445028352372, -96.95118121189718 32.34514995493036, -97.000290805405 32.35998695802537, -97.0474692989561 32.37882031784505, -97.09226710199511 32.4014709984568, -97.13425637766341 32.42772354222453, -97.17303502477111 32.45732804688847, -97.20823045934601 32.4900024647453, -97.23950316310588 32.52543520550353, -97.26654996670857 32.56328802174848, -97.28910703633477 32.60319915338926, -97.30695253309733 32.64478670499, -97.31990891600296 32.68765222752244, -97.32784486077121 32.73138447383263, -97.33067676879894 32.77556329501405, -97.3283698429972 32.81976364294841, -97.32093871017011 32.86355964254468, -97.30844757308559 32.90652869571424, -97.2910098794305 32.94825557790741, -97.26878749944663 32.9883364871487, -97.24198940919368 33.02638300499369, -97.2108698820347 33.06202592873645, -97.17572619701649 33.09491893456926, -97.13689587922663 33.12474203227585, -97.09475349382402 33.1512047734567, -97.04970702211409 33.17404917726238, -97.00219385460981 33.19305234015435, -96.95267644230539 33.20802869931354, -96.90163765321134 33.2188319229512, -96.84957588637754 33.22535640489695, -96.79700000000001 33.22753834538967, -96.74442411362247 33.22535640489695, -96.69236234678867 33.2188319229512, -96.64132355769462 33.20802869931355, -96.59180614539019 33.19305234015435, -96.54429297788592 33.17404917726238, -96.49924650617599 33.1512047734567, -96.45710412077338 33.12474203227585, -96.41827380298352 33.09491893456926, -96.38313011796531 33.06202592873645, -96.35201059080633 33.0263830049937, -96.32521250055339 32.98833648714871, -96.3029901205695 32.94825557790741, -96.28555242691442 32.90652869571425, -96.2730612898299 32.86355964254469, -96.2656301570028 32.81976364294841, -96.26332323120107 32.77556329501405, -96.26332323120107 32.77556329501405))",lightblue,0.2,Dallas,2.5 5 | "POLYGON ((-94.85283773661263 29.75938993355712, -94.85555255242065 29.71518902836866, -94.86321270836898 29.67143253564737, -94.87573826467887 29.62854094681859, -94.8930029628049 29.58692593225815, -94.9148356513102 29.54698643400474, -94.9410221137005 29.5091048966164, -94.97130727441314 29.47364366946468, -95.00539775699791 29.44094161174174, -95.04296476673061 29.4113109292703, -95.08364726842713 29.38503426987834, -95.12705542902903 29.36236210164189, -95.17277429357155 29.34351039572697, -95.22036766238409 29.32865863288574, -95.26938213677651 29.31794814989501, -95.31935130000883 29.31148083937879, -95.3698 29.3093182135426, -95.42024869999116 29.31148083937879, -95.47021786322347 29.31794814989501, -95.51923233761589 29.32865863288574, -95.56682570642843 29.34351039572697, -95.61254457097097 29.36236210164189, -95.65595273157285 29.38503426987834, -95.69663523326939 29.4113109292703, -95.73420224300209 29.44094161174174, -95.76829272558685 29.47364366946468, -95.7985778862995 29.5091048966164, -95.82476434868978 29.54698643400474, -95.8465970371951 29.58692593225815, -95.86386173532112 29.62854094681859, -95.87638729163102 29.67143253564737, -95.88404744757935 29.71518902836866, -95.88676226338737 29.75938993355712, -95.88449912072385 29.80360994904438, -95.87727328396572 29.84742303850543, -95.86514800529349 29.89040653622155, -95.84823416284208 29.93214524083893, -95.82668942557619 29.97223545819324, -95.80071694347156 30.01028895288992, -95.77056356691433 30.04593676836364, -95.73651760491894 30.0788328756255, -95.69890613771659 30.10865761187943, -95.65809190537496 30.1351208716781, -95.61446980024188 30.15796501530889, -95.5684629970152 30.17696746165863, -95.52051875997276 30.1919429358907, -95.47110397218822 30.20274534585479, -95.42070043625539 30.20926926519546, -95.3698 30.21145100557402, -95.31889956374459 30.20926926519546, -95.26849602781178 30.20274534585479, -95.21908124002722 30.19194293589071, -95.17113700298479 30.17696746165863, -95.12513019975812 30.15796501530889, -95.08150809462502 30.1351208716781, -95.04069386228339 30.10865761187943, -95.00308239508104 30.0788328756255, -94.96903643308566 30.04593676836365, -94.93888305652844 30.01028895288992, -94.91291057442379 29.97223545819325, -94.89136583715791 29.93214524083894, -94.8744519947065 29.89040653622155, -94.86232671603427 29.84742303850543, -94.85510087927614 29.80360994904438, -94.85283773661263 29.75938993355712, -94.85283773661263 29.75938993355712))",lightblue,0.2,Houston,2.5 6 | "LINESTRING (-97.7431 30.2672, -96.797 32.7767)",green,1.0,Austin-Dallas,3.0 7 | "LINESTRING (-96.797 32.7767, -95.3698 29.7604)",green,1.0,Dallas-Houston,3.0 8 | "LINESTRING (-95.3698 29.7604, -98.4936 29.4241)",green,1.0,Houston-SanAntonio,3.0 9 | -------------------------------------------------------------------------------- /tests/test_map_maker_bokeh.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import os 3 | import json 4 | 5 | from csv import DictReader 6 | from shapely.geometry import ( 7 | Point, 8 | MultiPoint, 9 | LineString, 10 | MultiLineString, 11 | Polygon, 12 | MultiPolygon, 13 | mapping, 14 | ) 15 | from shapely.wkt import loads as wkt_loads 16 | from toolz import assoc 17 | 18 | from map_maker.bokeh.map_maker import _get_tooltips, make_map_plot 19 | 20 | 21 | @pytest.fixture() 22 | def test_data(): 23 | """ Fixture that reads in the test data. 24 | """ 25 | current_dir = os.path.dirname(os.path.abspath(__file__)) 26 | test_data_points = os.path.join( 27 | current_dir, "test_data/test_data_points.csv" 28 | ) 29 | test_data_polygons_lines = os.path.join( 30 | current_dir, "test_data/test_data_polygons_lines.csv" 31 | ) 32 | 33 | dataset = [line for line in DictReader(open(test_data_points, "r"))] + [ 34 | line for line in DictReader(open(test_data_polygons_lines, "r")) 35 | ] 36 | 37 | return dataset 38 | 39 | 40 | @pytest.fixture() 41 | def test_data_shape(test_data): 42 | """ Fixture for the test data that has shape-serialized geometries. 43 | """ 44 | return [assoc(td, "shape", wkt_loads(td["shape"])) for td in test_data] 45 | 46 | 47 | @pytest.fixture() 48 | def test_data_dict(test_data_shape): 49 | """ Fixture for the test data that has dict-serialized geometries. 50 | """ 51 | return [assoc(td, "shape", mapping(td["shape"])) for td in test_data_shape] 52 | 53 | 54 | @pytest.fixture() 55 | def test_data_json(test_data_dict): 56 | """ Fixture for the test data that has json-serialized geometries. 57 | """ 58 | return [ 59 | assoc(td, "shape", json.dumps(td["shape"])) for td in test_data_dict 60 | ] 61 | 62 | 63 | @pytest.fixture() 64 | def test_data_multi(test_data_shape): 65 | """ Fixture for the test data that has shapes as multi-geometries. 66 | """ 67 | points = [ 68 | shp["shape"] 69 | for shp in test_data_shape 70 | if shp["shape"].geom_type == "Point" 71 | ] 72 | linestrings = [ 73 | shp["shape"] 74 | for shp in test_data_shape 75 | if shp["shape"].geom_type == "LineString" 76 | ] 77 | polygons = [ 78 | shp["shape"] 79 | for shp in test_data_shape 80 | if shp["shape"].geom_type == "Polygon" 81 | ] 82 | 83 | multi_point = MultiPoint(points) 84 | multi_linestring = MultiLineString(linestrings) 85 | multi_polygon = MultiPolygon(polygons) 86 | 87 | return [ 88 | {"shape": multi_point, "color": "black", "alpha": 0.3}, 89 | {"shape": multi_linestring, "color": "blue", "alpha": 0.2}, 90 | {"shape": multi_polygon, "color": "red", "alpha": 0.1}, 91 | ] 92 | 93 | 94 | def test_get_tooltips(): 95 | """ Tests that the _get_tooltips function returns the correct value. 96 | """ 97 | shape_datum = { 98 | "shape_obj": Point(0.0, 1.0), 99 | "shape": "POINT ( 0.0 1.0 )", 100 | "color": "black", 101 | "alpha": 0.1, 102 | "size": 1.0, 103 | "x": "abc", 104 | "y": 123, 105 | } 106 | 107 | truth = [("x", "@x"), ("y", "@y")] 108 | answer = sorted(_get_tooltips(shape_datum), key=lambda x: x[0]) 109 | 110 | assert truth == answer 111 | 112 | 113 | def test_make_map_plot_wkt(test_data): 114 | """ Tests that the make_map_plot function can run on sample data in WKT 115 | format. 116 | """ 117 | make_map_plot(test_data) 118 | 119 | 120 | def test_make_map_plot_shape(test_data_shape): 121 | """ Tests that the make_map_plot function can run on sample data in shape 122 | format. 123 | """ 124 | make_map_plot(test_data_shape) 125 | 126 | 127 | def test_make_map_plot_dict(test_data_dict): 128 | """ Tests that the make_map_plot function can run on sample data in dict 129 | format. 130 | """ 131 | make_map_plot(test_data_dict) 132 | 133 | 134 | def test_make_map_plot_json(test_data_json): 135 | """ Tests that the make_map_plot function can run on sample data in json 136 | format. 137 | """ 138 | make_map_plot(test_data_json) 139 | 140 | 141 | def test_make_map_plot_multi(test_data_multi): 142 | """ Tests that the make_map_plot function can run on sample data that has 143 | multipolygons and multipoints. 144 | """ 145 | make_map_plot(test_data_multi) 146 | 147 | 148 | def test_make_map_plot_no_points(test_data_shape): 149 | """ Tests that the make_map_plot function can run on data that has no 150 | points. 151 | """ 152 | no_points = [ 153 | s for s in test_data_shape if not isinstance(s["shape"], Point) 154 | ] 155 | 156 | make_map_plot(no_points) 157 | 158 | 159 | def test_make_map_plot_no_polygons(test_data_shape): 160 | """ Tests that the make_map_plot function can run on data that has no 161 | polygons. 162 | """ 163 | no_polygons = [ 164 | s for s in test_data_shape if not isinstance(s["shape"], Polygon) 165 | ] 166 | 167 | make_map_plot(no_polygons) 168 | 169 | 170 | def test_make_map_plot_no_linestrings(test_data_shape): 171 | """ Tests that the make_map_plot function can run on data that has no 172 | linestrings. 173 | """ 174 | no_linestrings = [ 175 | s 176 | for s in test_data_shape 177 | if not isinstance(s["shape"], (LineString, MultiLineString)) 178 | ] 179 | 180 | make_map_plot(no_linestrings) 181 | -------------------------------------------------------------------------------- /tests/test_map_maker_folium.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import os 3 | import json 4 | 5 | from csv import DictReader 6 | from shapely.geometry import ( 7 | Point, 8 | MultiPoint, 9 | LineString, 10 | MultiLineString, 11 | Polygon, 12 | MultiPolygon, 13 | mapping, 14 | ) 15 | from shapely.wkt import loads as wkt_loads 16 | from toolz import assoc 17 | 18 | from map_maker.folium.map_maker import ( 19 | make_map_plot, 20 | _get_tooltip_keys, 21 | _get_tooltip_values, 22 | ) 23 | 24 | 25 | @pytest.fixture() 26 | def test_data(): 27 | """ Fixture that reads in the test data. 28 | """ 29 | current_dir = os.path.dirname(os.path.abspath(__file__)) 30 | test_data_points = os.path.join( 31 | current_dir, "test_data/test_data_points.csv" 32 | ) 33 | test_data_polygons_lines = os.path.join( 34 | current_dir, "test_data/test_data_polygons_lines.csv" 35 | ) 36 | 37 | dataset = [line for line in DictReader(open(test_data_points, "r"))] + [ 38 | line for line in DictReader(open(test_data_polygons_lines, "r")) 39 | ] 40 | 41 | return dataset 42 | 43 | 44 | @pytest.fixture() 45 | def test_data_shape(test_data): 46 | """ Fixture for the test data that has shape-serialized geometries. 47 | """ 48 | return [assoc(td, "shape", wkt_loads(td["shape"])) for td in test_data] 49 | 50 | 51 | @pytest.fixture() 52 | def test_data_dict(test_data_shape): 53 | """ Fixture for the test data that has dict-serialized geometries. 54 | """ 55 | return [assoc(td, "shape", mapping(td["shape"])) for td in test_data_shape] 56 | 57 | 58 | @pytest.fixture() 59 | def test_data_json(test_data_dict): 60 | """ Fixture for the test data that has json-serialized geometries. 61 | """ 62 | return [ 63 | assoc(td, "shape", json.dumps(td["shape"])) for td in test_data_dict 64 | ] 65 | 66 | 67 | @pytest.fixture() 68 | def test_data_multi(test_data_shape): 69 | """ Fixture for the test data that has shapes as multi-geometries. 70 | """ 71 | points = [ 72 | shp["shape"] 73 | for shp in test_data_shape 74 | if shp["shape"].geom_type == "Point" 75 | ] 76 | linestrings = [ 77 | shp["shape"] 78 | for shp in test_data_shape 79 | if shp["shape"].geom_type == "LineString" 80 | ] 81 | polygons = [ 82 | shp["shape"] 83 | for shp in test_data_shape 84 | if shp["shape"].geom_type == "Polygon" 85 | ] 86 | 87 | multi_point = MultiPoint(points) 88 | multi_linestring = MultiLineString(linestrings) 89 | multi_polygon = MultiPolygon(polygons) 90 | 91 | return [ 92 | {"shape": multi_point, "color": "black", "alpha": 0.3}, 93 | {"shape": multi_linestring, "color": "blue", "alpha": 0.2}, 94 | {"shape": multi_polygon, "color": "red", "alpha": 0.1}, 95 | ] 96 | 97 | 98 | def test_get_tooltip_keys(): 99 | """ Tests that the _get_tooltip_keys function returns the correct value. 100 | """ 101 | shape_datum = { 102 | "shape": "a", 103 | "shape_obj": "b", 104 | "color": "blue", 105 | "alpha": 1.0, 106 | "size": 2.0, 107 | "x": 1, 108 | "y": 2, 109 | } 110 | 111 | truth = ["x", "y"] 112 | answer = _get_tooltip_keys(shape_datum) 113 | 114 | assert sorted(truth) == sorted(answer) 115 | 116 | 117 | def test_get_tooltip_values(): 118 | """ Tests that the _get_tooltip_values function returns the correct value. 119 | """ 120 | shape_datum = { 121 | "shape": "a", 122 | "shape_obj": "b", 123 | "color": "blue", 124 | "alpha": 1.0, 125 | "size": 2.0, 126 | "x": 1, 127 | "y": 2, 128 | } 129 | 130 | truth = ["x: 1", "y: 2"] 131 | answer = _get_tooltip_values(shape_datum) 132 | 133 | assert sorted(truth) == sorted(answer) 134 | 135 | 136 | def test_make_map_plot_wkt(test_data): 137 | """ Tests that the make_map_plot function can run on sample data in WKT 138 | format. 139 | """ 140 | make_map_plot(test_data) 141 | 142 | 143 | def test_make_map_plot_shape(test_data_shape): 144 | """ Tests that the make_map_plot function can run on sample data in shape 145 | format. 146 | """ 147 | make_map_plot(test_data_shape) 148 | 149 | 150 | def test_make_map_plot_dict(test_data_dict): 151 | """ Tests that the make_map_plot function can run on sample data in dict 152 | format. 153 | """ 154 | make_map_plot(test_data_dict) 155 | 156 | 157 | def test_make_map_plot_json(test_data_json): 158 | """ Tests that the make_map_plot function can run on sample data in json 159 | format. 160 | """ 161 | make_map_plot(test_data_json) 162 | 163 | 164 | def test_make_map_plot_multi(test_data_multi): 165 | """ Tests that the make_map_plot function can run on sample data that has 166 | multipolygons and multipoints. 167 | """ 168 | make_map_plot(test_data_multi) 169 | 170 | 171 | def test_make_map_plot_no_points(test_data_shape): 172 | """ Tests that the make_map_plot function can run on data that has no 173 | points. 174 | """ 175 | no_points = [ 176 | s for s in test_data_shape if not isinstance(s["shape"], Point) 177 | ] 178 | 179 | make_map_plot(no_points) 180 | 181 | 182 | def test_make_map_plot_no_polygons(test_data_shape): 183 | """ Tests that the make_map_plot function can run on data that has no 184 | polygons. 185 | """ 186 | no_polygons = [ 187 | s for s in test_data_shape if not isinstance(s["shape"], Polygon) 188 | ] 189 | 190 | make_map_plot(no_polygons) 191 | 192 | 193 | def test_make_map_plot_no_linestrings(test_data_shape): 194 | """ Tests that the make_map_plot function can run on data that has no 195 | linestrings. 196 | """ 197 | no_linestrings = [ 198 | s 199 | for s in test_data_shape 200 | if not isinstance(s["shape"], (LineString, MultiLineString)) 201 | ] 202 | 203 | make_map_plot(no_linestrings) 204 | -------------------------------------------------------------------------------- /tests/test_util.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from shapely.geometry import ( 4 | Point, 5 | box, 6 | LineString, 7 | MultiLineString, 8 | Polygon, 9 | MultiPolygon, 10 | MultiPoint, 11 | ) 12 | from map_maker.util import to_shape, extract_geometries, get_bounds, to_geojson 13 | 14 | 15 | def test_to_shape_wkt(): 16 | """ Tests that the to_shape function works when the input shape is WKT. 17 | """ 18 | shape_ser = "POINT( 0.0 0.0 )" 19 | truth = Point(0.0, 0.0) 20 | answer = to_shape(shape_ser) 21 | 22 | assert truth == answer 23 | 24 | 25 | def test_to_shape_geojson(): 26 | """ Tests that the to_shape function works when the input shape is 27 | GeoJSON. 28 | """ 29 | shape_ser = """ 30 | { 31 | "type": "Point", 32 | "coordinates": [0.0, 0.0] 33 | } 34 | """ 35 | 36 | truth = Point(0.0, 0.0) 37 | answer = to_shape(shape_ser) 38 | 39 | assert truth == answer 40 | 41 | 42 | def test_to_shape_non_serializable_string(): 43 | """ Tests that the to_shape function returns a TypeError when the input 44 | is a non-geometric string. 45 | """ 46 | 47 | with pytest.raises(TypeError): 48 | to_shape("POINT ( 0.0") 49 | 50 | 51 | def test_to_shape_mapping(): 52 | """ Tests that the to_shape function returns the correct value when the 53 | input is a mapping. 54 | """ 55 | shape_ser = {"type": "Point", "coordinates": [0.0, 0.0]} 56 | truth = Point(0.0, 0.0) 57 | answer = to_shape(shape_ser) 58 | 59 | assert truth == answer 60 | 61 | 62 | def test_to_shape_shape(): 63 | """ Tests that the to_shape function returns the correct value when the 64 | input is a shape. 65 | """ 66 | shape_ser = Point(0.0, 0.0) 67 | truth = Point(0.0, 0.0) 68 | answer = to_shape(shape_ser) 69 | 70 | assert truth == answer 71 | 72 | 73 | def test_to_shape_non_serializable_object(): 74 | """ Tests that the to_shape function raises a TypeError when the input 75 | is an object that is not serializable to a shape. 76 | """ 77 | with pytest.raises(TypeError): 78 | to_shape(3) 79 | 80 | 81 | def test_extract_geometries(): 82 | """ Tests that the extract_geometries function returns the correct 83 | value. 84 | """ 85 | shape_data = [ 86 | {"shape": Point(0.0, 0.0), "color": "black", "x": 1}, 87 | { 88 | "shape": MultiPoint([Point(1.0, 1.0), Point(2.0, 2.0)]), 89 | "color": "green", 90 | "x": 2, 91 | }, 92 | { 93 | "shape": LineString([[0.0, 0.0], [1.0, 1.0]]), 94 | "color": "blue", 95 | "x": 2, 96 | }, 97 | { 98 | "shape": MultiLineString( 99 | [[[0.0, 0.0], [1.0, 0.0]], [[0.0, 0.0], [0.0, 1.0]]] 100 | ), 101 | "color": "gray", 102 | "x": 3, 103 | }, 104 | {"shape": box(0.0, 0.0, 1.0, 1.0), "color": "red", "x": 4}, 105 | { 106 | "shape": MultiPolygon( 107 | box(0.0, 0.0, 0.5, 0.5), box(0.5, 0.5, 1.0, 1.0) 108 | ), 109 | "color": "green", 110 | "x": 5, 111 | }, 112 | ] 113 | 114 | points, linestrings, polygons = extract_geometries(shape_data) 115 | 116 | assert len(points) == 3 117 | assert isinstance(points[0]["shape_obj"], Point) 118 | assert points[0]["color"] == "black" 119 | assert points[0]["x"] == 1 120 | assert isinstance(points[1]["shape_obj"], Point) 121 | assert points[1]["color"] == "green" 122 | assert points[1]["x"] == 2 123 | assert isinstance(points[2]["shape_obj"], Point) 124 | assert points[2]["color"] == "green" 125 | assert points[2]["x"] == 2 126 | assert isinstance(linestrings[0]["shape_obj"], LineString) 127 | assert linestrings[0]["x"] == 2 128 | assert isinstance(linestrings[1]["shape_obj"], MultiLineString) 129 | assert linestrings[1]["x"] == 3 130 | assert len(polygons) == 2 131 | assert isinstance(polygons[0]["shape_obj"], Polygon) 132 | assert polygons[0]["color"] == "red" 133 | assert polygons[0]["x"] == 4 134 | assert isinstance(polygons[1]["shape_obj"], MultiPolygon) 135 | assert polygons[1]["color"] == "green" 136 | assert polygons[1]["x"] == 5 137 | 138 | 139 | def test_get_bounds(): 140 | """ Tests that the _get_bounds function returns the correct value. 141 | """ 142 | shape_data = [{"shape_obj": box(0.0, 0.0, 1.0, 1.0)}] 143 | 144 | truth = (0.0, 0.0, 1.0, 1.0) 145 | answer = get_bounds(shape_data) 146 | 147 | assert truth == answer 148 | 149 | 150 | def test_to_geojson(): 151 | """ Tests that the _to_geojson function returns the correct value. 152 | """ 153 | shape_data = [ 154 | { 155 | "shape_obj": Point(0.0, 0.0), 156 | "color": "green", 157 | "alpha": 0.3, 158 | "size": 2.0, 159 | "x": 1, 160 | } 161 | ] 162 | 163 | truth = { 164 | "type": "FeatureCollection", 165 | "features": [ 166 | { 167 | "type": "Feature", 168 | "properties": { 169 | "color": "green", 170 | "alpha": 0.3, 171 | "size": 2.0, 172 | "x": 1, 173 | }, 174 | "geometry": {"type": "Point", "coordinates": (0.0, 0.0)}, 175 | } 176 | ], 177 | } 178 | 179 | answer = to_geojson(shape_data, "blue", 1.0, 1.0) 180 | 181 | assert truth == answer 182 | 183 | 184 | def test_to_geojson_default_color_alpha(): 185 | """ Tests that the to_geojson function returns the correct value when 186 | default color, alpha and size are provided. 187 | """ 188 | shape_data = [{"shape_obj": Point(0.0, 0.0), "x": 1}] 189 | 190 | truth = { 191 | "type": "FeatureCollection", 192 | "features": [ 193 | { 194 | "type": "Feature", 195 | "properties": { 196 | "color": "blue", 197 | "alpha": 1.0, 198 | "size": 1.0, 199 | "x": 1, 200 | }, 201 | "geometry": {"type": "Point", "coordinates": (0.0, 0.0)}, 202 | } 203 | ], 204 | } 205 | 206 | answer = to_geojson(shape_data, "blue", 1.0, 1.0) 207 | 208 | assert truth == answer 209 | --------------------------------------------------------------------------------