├── .gitignore
├── .readthedocs-environment.yml
├── .readthedocs.yml
├── .travis.yml
├── LICENSE.txt
├── README.md
├── cw_geodata
├── __init__.py
├── data
│ ├── __init__.py
│ ├── aff_gdf_result.csv
│ ├── geotiff_labels.geojson
│ ├── gj_to_px_result.geojson
│ ├── sample.csv
│ ├── sample.geojson
│ ├── sample_b_from_df2px.tif
│ ├── sample_b_mask_inner.tif
│ ├── sample_b_mask_outer.tif
│ ├── sample_b_mask_outer_10.tif
│ ├── sample_c_from_df2px.tif
│ ├── sample_c_mask.tif
│ ├── sample_fbc_from_df2px.tif
│ ├── sample_fp_from_df2px.tif
│ ├── sample_fp_mask.tif
│ ├── sample_fp_mask_from_geojson.tif
│ ├── sample_geotiff.tif
│ ├── sample_graph.pkl
│ ├── sample_roads.geojson
│ ├── split_multi_grouped_result.json
│ ├── split_multi_result.csv
│ ├── split_multi_result.json
│ ├── test_overlap_output.txt
│ └── w_multipolygon.csv
├── raster_image
│ ├── __init__.py
│ └── image.py
├── utils
│ ├── __init__.py
│ ├── core.py
│ └── geo.py
└── vector_label
│ ├── __init__.py
│ ├── graph.py
│ ├── mask.py
│ └── polygon.py
├── docs
├── Makefile
├── make.bat
└── source
│ ├── api.rst
│ ├── conf.py
│ └── index.rst
├── environment.yml
├── setup.py
└── tests
├── __init__.py
├── test_imports.py
├── test_raster_image
├── __init__.py
└── test_image.py
├── test_utils
├── __init__.py
├── test_core.py
└── test_geo.py
└── test_vector_label
├── __init__.py
├── test_graph.py
├── test_mask.py
└── test_polygon.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # MacOS stuff:
7 | .DS_Store
8 |
9 | # C extensions
10 | *.so
11 |
12 | # Distribution / packaging
13 | .Python
14 | build/
15 | develop-eggs/
16 | dist/
17 | downloads/
18 | eggs/
19 | .eggs/
20 | lib/
21 | lib64/
22 | parts/
23 | sdist/
24 | var/
25 | wheels/
26 | pip-wheel-metadata/
27 | share/python-wheels/
28 | *.egg-info/
29 | .installed.cfg
30 | *.egg
31 | MANIFEST
32 |
33 | # PyInstaller
34 | # Usually these files are written by a python script from a template
35 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
36 | *.manifest
37 | *.spec
38 |
39 | # Installer logs
40 | pip-log.txt
41 | pip-delete-this-directory.txt
42 |
43 | # Unit test / coverage reports
44 | htmlcov/
45 | .tox/
46 | .nox/
47 | .coverage
48 | .coverage.*
49 | .cache
50 | nosetests.xml
51 | coverage.xml
52 | *.cover
53 | .hypothesis/
54 | .pytest_cache/
55 |
56 | # Translations
57 | *.mo
58 | *.pot
59 |
60 | # Django stuff:
61 | *.log
62 | local_settings.py
63 | db.sqlite3
64 |
65 | # Flask stuff:
66 | instance/
67 | .webassets-cache
68 |
69 | # Scrapy stuff:
70 | .scrapy
71 |
72 | # Sphinx documentation
73 | docs/_build/
74 |
75 | # PyBuilder
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | .python-version
87 |
88 | # celery beat schedule file
89 | celerybeat-schedule
90 |
91 | # SageMath parsed files
92 | *.sage.py
93 |
94 | # Environments
95 | .env
96 | .venv
97 | env/
98 | venv/
99 | ENV/
100 | env.bak/
101 | venv.bak/
102 |
103 | # Spyder project settings
104 | .spyderproject
105 | .spyproject
106 |
107 | # Rope project settings
108 | .ropeproject
109 |
110 | # mkdocs documentation
111 | /site
112 |
113 | # mypy
114 | .mypy_cache/
115 | .dmypy.json
116 | dmypy.json
117 |
118 | # Pyre type checker
119 | .pyre/
120 |
121 | # Project-specific
122 | sandbox.ipynb
123 |
--------------------------------------------------------------------------------
/.readthedocs-environment.yml:
--------------------------------------------------------------------------------
1 | name: cw-geodata
2 | dependencies:
3 | - python=3.6
4 | - pip
5 | - shapely
6 | - fiona
7 | - pandas
8 | - geopandas
9 | - numpy
10 | - scikit-image
11 | - networkx
12 | - rasterio
13 | - pip:
14 | - affine
15 |
--------------------------------------------------------------------------------
/.readthedocs.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | formats:
3 | - htmlzip
4 |
5 | python:
6 | version: 3.6
7 | install:
8 | - method: pip
9 | path: .
10 | conda:
11 | environment: .readthedocs-environment.yml
12 |
13 | build:
14 | image: latest
15 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | sudo: false
3 | python:
4 | - "3.6"
5 |
6 | # command to install dependencies
7 | install:
8 | - sudo apt-get update
9 | # We do this conditionally because it saves us some downloading if the
10 | # version is the same.
11 | - if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then
12 | wget https://repo.continuum.io/miniconda/Miniconda2-latest-Linux-x86_64.sh -O miniconda.sh;
13 | else
14 | wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh;
15 | fi
16 | - bash miniconda.sh -b -p $HOME/miniconda
17 | - export PATH="$HOME/miniconda/bin:$PATH"
18 | - hash -r
19 | - conda config --set always_yes yes --set changeps1 no
20 | - conda update -q conda
21 | # Useful for debugging any issues with conda
22 | - conda info -a
23 | # switch python version spec in environment.yml to match TRAVIS_PYTHON_VERSION
24 | # annoying workaround to `conda env create python=$TRAVIS_PYTHON_VERSION` not working
25 | - sed -i -E 's/(python=)(.*)/\1'$TRAVIS_PYTHON_VERSION'/' ./environment.yml
26 | - conda env create -n cw-geodata --file=environment.yml
27 | - source activate cw-geodata
28 | - python --version
29 | - pip install -q -e .[test]
30 | - pip install codecov pytest pytest-cov
31 | # command to run tests
32 | script:
33 | - pytest --cov=./
34 |
35 | after_success:
36 | - codecov
37 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2019 CosmiQ Works, an IQT Lab
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # This repository is no longer being updated. Future development of code tools for geospatial machine learning analysis will be done at https://github.com/cosmiq/solaris.
2 |
3 |
CosmiQ Works Geospatial Data Processing Tools for ML
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | __This package is currently under active development. Check back soon for a mature version.__
19 |
20 | - [Installation Instructions](#installation-instructions)
21 | - [API Documentation](https://cw-geodata.readthedocs.io/)
22 | - [Dependencies](#dependencies)
23 | - [License](#license)
24 | ---
25 | This package is built to:
26 | - Enable management and interconversion of geospatial data files without requiring understanding of coordinate reference systems, geospatial transforms, etc.
27 | - Enable creation of training targets for segmentation and object detection from geospatial vector data (_i.e._ geojsons of labels) without requiring understanding of ML training target formats.
28 |
29 | ## Installation Instructions
30 | Several packages require binaries to be installed before pip installing the other packages. We recommend creating a conda environment and installing dependencies there from [environment.yml](./environment.yml), then using `pip` to install this package.
31 |
32 | First, clone this repo to your computer and navigate into the folder:
33 | ```
34 | git clone https://github.com/cosmiq/cw-geodata.git
35 | cd cw-geodata
36 | ```
37 | Next, create a [conda](https://anaconda.com/distribution/) environment with dependencies installed as defined in the environment.yml file.
38 | ```
39 | conda env create -f environment.yml
40 | source activate cw-geodata
41 | ```
42 | Finally, use `pip` to install this package.
43 | ```
44 | pip install .
45 | ```
46 | For bleeding-edge versions (use at your own risk), `pip install` from the dev branch of this repository:
47 | ```
48 | pip install --upgrade git+https://github.com/CosmiQ/cw-geodata.git@dev
49 | ```
50 |
51 | ## API Documentation
52 | API documentation can be found [here](https://cw-geodata.readthedocs.io)
53 |
54 | ## Dependencies
55 | All dependencies can be found in [environment.yml](./environment.yml)
56 |
57 | ## License
58 | See [LICENSE](./LICENSE.txt).
59 |
--------------------------------------------------------------------------------
/cw_geodata/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CosmiQ/cw-geodata/19a290c20ec4672dc2e9be37f451d136166bb28a/cw_geodata/__init__.py
--------------------------------------------------------------------------------
/cw_geodata/data/__init__.py:
--------------------------------------------------------------------------------
1 | import os
2 | import rasterio
3 | import gdal
4 | import geopandas as gpd
5 |
6 | data_dir = os.path.abspath(os.path.dirname(__file__))
7 |
8 |
9 | def sample_load_rasterio():
10 | return rasterio.open(os.path.join(data_dir, 'sample_geotiff.tif'))
11 |
12 |
13 | def sample_load_gdal():
14 | return gdal.Open(os.path.join(data_dir, 'sample_geotiff.tif'))
15 |
16 |
17 | def sample_load_geojson():
18 | return gpd.read_file(os.path.join(data_dir, 'sample.geojson'))
19 |
20 |
21 | def sample_load_csv():
22 | return pd.read_file(os.path.join(data_dir, 'sample.csv'))
23 |
--------------------------------------------------------------------------------
/cw_geodata/data/sample.geojson:
--------------------------------------------------------------------------------
1 | {
2 | "type": "FeatureCollection",
3 | "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } },
4 | "features": [
5 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736348.0, 3722762.5 ], [ 736353.0, 3722762.0 ], [ 736354.0, 3722759.0 ], [ 736352.0, 3722755.5 ], [ 736348.5, 3722755.5 ], [ 736346.0, 3722757.5 ], [ 736348.0, 3722762.5 ] ] ] } },
6 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736301.0, 3722760.5 ], [ 736310.5, 3722760.0 ], [ 736314.0, 3722758.0 ], [ 736315.0, 3722752.0 ], [ 736310.5, 3722746.5 ], [ 736308.0, 3722746.0 ], [ 736306.0, 3722750.0 ], [ 736301.0, 3722752.0 ], [ 736301.0, 3722760.5 ] ] ] } },
7 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736306.5, 3722614.0 ], [ 736334.5, 3722611.0 ], [ 736339.5, 3722610.0 ], [ 736339.5, 3722608.5 ], [ 736335.0, 3722603.0 ], [ 736316.0, 3722603.5 ], [ 736304.0, 3722607.5 ], [ 736306.5, 3722614.0 ] ] ] } },
8 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736359.5, 3722611.5 ], [ 736379.5, 3722608.5 ], [ 736398.0, 3722609.0 ], [ 736399.5, 3722600.0 ], [ 736396.5, 3722596.5 ], [ 736380.5, 3722600.5 ], [ 736373.5, 3722598.5 ], [ 736365.0, 3722600.5 ], [ 736364.0, 3722598.5 ], [ 736360.0, 3722600.0 ], [ 736356.0, 3722605.0 ], [ 736356.0, 3722609.5 ], [ 736359.5, 3722611.5 ] ] ] } },
9 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736412.5, 3722602.0 ], [ 736429.5, 3722598.0 ], [ 736439.0, 3722601.5 ], [ 736443.5, 3722600.0 ], [ 736454.0, 3722600.0 ], [ 736455.0, 3722594.0 ], [ 736453.5, 3722591.5 ], [ 736444.0, 3722589.5 ], [ 736440.5, 3722587.0 ], [ 736432.5, 3722589.5 ], [ 736427.5, 3722587.5 ], [ 736419.5, 3722587.5 ], [ 736416.0, 3722589.0 ], [ 736410.0, 3722596.0 ], [ 736409.5, 3722600.0 ], [ 736412.5, 3722602.0 ] ] ] } },
10 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736311.5, 3722591.0 ], [ 736323.5, 3722589.0 ], [ 736325.5, 3722582.0 ], [ 736324.5, 3722579.5 ], [ 736320.5, 3722577.5 ], [ 736302.5, 3722579.0 ], [ 736301.0, 3722580.5 ], [ 736301.0, 3722588.5 ], [ 736311.5, 3722591.0 ] ] ] } },
11 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736544.0, 3722576.0 ], [ 736547.5, 3722576.0 ], [ 736551.5, 3722573.0 ], [ 736552.0, 3722564.0 ], [ 736550.0, 3722561.0 ], [ 736543.5, 3722559.0 ], [ 736538.5, 3722561.0 ], [ 736537.5, 3722570.5 ], [ 736540.0, 3722575.0 ], [ 736544.0, 3722576.0 ] ] ] } },
12 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736508.0, 3722564.5 ], [ 736513.5, 3722562.0 ], [ 736515.0, 3722557.0 ], [ 736510.0, 3722539.0 ], [ 736495.0, 3722539.0 ], [ 736491.0, 3722537.0 ], [ 736485.5, 3722537.0 ], [ 736478.5, 3722540.0 ], [ 736472.5, 3722549.0 ], [ 736471.0, 3722556.5 ], [ 736473.0, 3722559.0 ], [ 736497.5, 3722559.0 ], [ 736505.0, 3722561.5 ], [ 736508.0, 3722564.5 ] ] ] } },
13 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736552.0, 3722551.0 ], [ 736557.0, 3722550.0 ], [ 736559.0, 3722548.0 ], [ 736560.0, 3722543.0 ], [ 736554.5, 3722539.5 ], [ 736547.5, 3722538.5 ], [ 736542.5, 3722535.0 ], [ 736536.0, 3722533.0 ], [ 736534.0, 3722537.0 ], [ 736535.0, 3722543.0 ], [ 736538.0, 3722548.0 ], [ 736552.0, 3722551.0 ] ] ] } },
14 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736361.0, 3722579.0 ], [ 736365.0, 3722579.0 ], [ 736364.0, 3722522.0 ], [ 736360.0, 3722525.0 ], [ 736357.5, 3722544.0 ], [ 736358.0, 3722565.5 ], [ 736359.5, 3722569.0 ], [ 736358.0, 3722575.5 ], [ 736361.0, 3722579.0 ] ] ] } },
15 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736319.0, 3722572.0 ], [ 736326.0, 3722570.0 ], [ 736328.0, 3722566.0 ], [ 736330.5, 3722565.0 ], [ 736329.5, 3722547.5 ], [ 736331.0, 3722528.5 ], [ 736330.0, 3722524.5 ], [ 736326.5, 3722520.5 ], [ 736320.0, 3722523.0 ], [ 736318.0, 3722527.0 ], [ 736317.5, 3722570.5 ], [ 736319.0, 3722572.0 ] ] ] } },
16 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736414.0, 3722573.0 ], [ 736417.5, 3722572.5 ], [ 736420.0, 3722568.0 ], [ 736421.0, 3722556.0 ], [ 736418.5, 3722538.0 ], [ 736424.0, 3722532.5 ], [ 736424.0, 3722527.0 ], [ 736422.5, 3722525.5 ], [ 736412.0, 3722524.0 ], [ 736410.5, 3722521.5 ], [ 736407.0, 3722520.5 ], [ 736383.5, 3722521.0 ], [ 736376.5, 3722528.5 ], [ 736378.0, 3722532.5 ], [ 736402.0, 3722532.0 ], [ 736410.0, 3722539.0 ], [ 736411.0, 3722544.0 ], [ 736408.5, 3722553.5 ], [ 736409.0, 3722569.0 ], [ 736414.0, 3722573.0 ] ] ] } },
17 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736451.0, 3722575.0 ], [ 736455.0, 3722574.0 ], [ 736457.0, 3722569.0 ], [ 736455.5, 3722555.5 ], [ 736457.0, 3722531.5 ], [ 736454.5, 3722525.0 ], [ 736454.5, 3722516.5 ], [ 736449.0, 3722518.0 ], [ 736449.0, 3722524.0 ], [ 736446.0, 3722525.5 ], [ 736443.5, 3722547.0 ], [ 736445.0, 3722564.5 ], [ 736443.0, 3722569.0 ], [ 736446.0, 3722574.0 ], [ 736451.0, 3722575.0 ] ] ] } },
18 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736733.5, 3722519.5 ], [ 736735.5, 3722519.0 ], [ 736738.0, 3722512.5 ], [ 736738.0, 3722510.0 ], [ 736736.0, 3722508.0 ], [ 736732.0, 3722510.0 ], [ 736730.5, 3722514.5 ], [ 736732.0, 3722519.0 ], [ 736733.5, 3722519.5 ] ] ] } },
19 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736448.0, 3722488.5 ], [ 736450.5, 3722488.0 ], [ 736450.5, 3722484.5 ], [ 736448.0, 3722485.5 ], [ 736448.0, 3722488.5 ] ] ] } },
20 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736412.0, 3722492.0 ], [ 736418.0, 3722492.0 ], [ 736422.0, 3722487.5 ], [ 736423.5, 3722481.5 ], [ 736422.0, 3722478.5 ], [ 736415.0, 3722478.0 ], [ 736408.0, 3722484.5 ], [ 736408.0, 3722490.0 ], [ 736412.0, 3722492.0 ] ] ] } },
21 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736477.0, 3722491.5 ], [ 736483.0, 3722490.0 ], [ 736482.5, 3722482.5 ], [ 736476.5, 3722474.0 ], [ 736473.0, 3722474.0 ], [ 736469.5, 3722477.5 ], [ 736469.0, 3722482.5 ], [ 736470.0, 3722486.0 ], [ 736477.0, 3722491.5 ] ] ] } },
22 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736498.0, 3722478.0 ], [ 736500.0, 3722477.5 ], [ 736500.5, 3722473.5 ], [ 736496.0, 3722470.5 ], [ 736494.0, 3722474.5 ], [ 736498.0, 3722478.0 ] ] ] } },
23 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736744.0, 3722505.0 ], [ 736750.0, 3722505.0 ], [ 736751.0, 3722503.5 ], [ 736751.0, 3722460.0 ], [ 736749.5, 3722459.5 ], [ 736746.0, 3722460.5 ], [ 736741.5, 3722466.0 ], [ 736739.5, 3722492.0 ], [ 736745.0, 3722497.0 ], [ 736745.5, 3722500.5 ], [ 736742.5, 3722502.0 ], [ 736744.0, 3722505.0 ] ] ] } },
24 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736700.0, 3722476.5 ], [ 736706.0, 3722476.5 ], [ 736708.0, 3722474.0 ], [ 736708.0, 3722465.0 ], [ 736705.5, 3722461.5 ], [ 736696.0, 3722463.0 ], [ 736692.0, 3722468.5 ], [ 736693.5, 3722474.0 ], [ 736700.0, 3722476.5 ] ] ] } },
25 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736417.5, 3722456.5 ], [ 736422.0, 3722456.5 ], [ 736422.5, 3722454.5 ], [ 736418.0, 3722453.5 ], [ 736417.5, 3722456.5 ] ] ] } },
26 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736395.5, 3722456.0 ], [ 736401.5, 3722456.0 ], [ 736404.0, 3722452.5 ], [ 736406.5, 3722453.0 ], [ 736406.5, 3722449.0 ], [ 736404.0, 3722449.0 ], [ 736402.0, 3722446.5 ], [ 736396.0, 3722447.0 ], [ 736394.0, 3722448.5 ], [ 736395.5, 3722456.0 ] ] ] } },
27 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736311.5, 3722494.5 ], [ 736314.5, 3722494.5 ], [ 736318.0, 3722491.5 ], [ 736319.0, 3722484.0 ], [ 736318.0, 3722479.0 ], [ 736316.0, 3722478.5 ], [ 736316.0, 3722475.5 ], [ 736319.0, 3722472.5 ], [ 736316.0, 3722465.0 ], [ 736316.0, 3722462.0 ], [ 736318.5, 3722459.0 ], [ 736318.5, 3722454.5 ], [ 736315.5, 3722452.5 ], [ 736316.0, 3722446.0 ], [ 736314.5, 3722444.5 ], [ 736310.0, 3722445.0 ], [ 736308.5, 3722454.0 ], [ 736308.0, 3722470.5 ], [ 736309.5, 3722480.0 ], [ 736307.5, 3722492.0 ], [ 736311.5, 3722494.5 ] ] ] } },
28 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736364.0, 3722484.5 ], [ 736365.0, 3722445.5 ], [ 736363.0, 3722439.0 ], [ 736356.0, 3722439.0 ], [ 736353.0, 3722454.5 ], [ 736357.5, 3722463.0 ], [ 736362.5, 3722467.5 ], [ 736357.5, 3722473.0 ], [ 736356.5, 3722478.0 ], [ 736364.0, 3722484.5 ] ] ] } },
29 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736494.5, 3722447.5 ], [ 736501.0, 3722447.0 ], [ 736501.5, 3722444.0 ], [ 736498.5, 3722442.0 ], [ 736495.0, 3722442.0 ], [ 736495.0, 3722439.0 ], [ 736490.5, 3722439.0 ], [ 736492.0, 3722446.0 ], [ 736494.5, 3722447.5 ] ] ] } },
30 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736399.0, 3722441.5 ], [ 736403.0, 3722439.0 ], [ 736397.0, 3722439.0 ], [ 736399.0, 3722441.5 ] ] ] } },
31 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736472.0, 3722466.5 ], [ 736476.0, 3722465.5 ], [ 736477.5, 3722458.0 ], [ 736477.0, 3722443.5 ], [ 736475.5, 3722439.0 ], [ 736469.5, 3722439.0 ], [ 736467.0, 3722456.0 ], [ 736468.5, 3722463.5 ], [ 736472.0, 3722466.5 ] ] ] } },
32 | { "type": "Feature", "properties": { "rasterVal": 1.0, "conf": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 736660.0, 3722528.5 ], [ 736672.5, 3722528.5 ], [ 736679.5, 3722525.5 ], [ 736688.0, 3722527.5 ], [ 736698.5, 3722524.5 ], [ 736704.0, 3722515.5 ], [ 736705.0, 3722503.5 ], [ 736702.0, 3722501.5 ], [ 736702.0, 3722498.0 ], [ 736704.0, 3722496.0 ], [ 736701.5, 3722491.5 ], [ 736694.0, 3722489.0 ], [ 736684.0, 3722482.0 ], [ 736674.0, 3722482.5 ], [ 736668.0, 3722479.0 ], [ 736666.0, 3722465.5 ], [ 736664.5, 3722463.5 ], [ 736665.5, 3722457.0 ], [ 736664.0, 3722452.5 ], [ 736664.0, 3722440.0 ], [ 736650.5, 3722439.0 ], [ 736649.0, 3722445.5 ], [ 736644.0, 3722452.0 ], [ 736642.5, 3722471.5 ], [ 736650.0, 3722501.0 ], [ 736656.0, 3722515.5 ], [ 736660.0, 3722519.5 ], [ 736657.5, 3722528.0 ], [ 736660.0, 3722528.5 ] ], [ [ 736700.0, 3722500.0 ], [ 736698.0, 3722503.0 ], [ 736695.0, 3722502.5 ], [ 736697.0, 3722499.0 ], [ 736700.0, 3722500.0 ] ] ] } }
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/cw_geodata/data/sample_b_from_df2px.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CosmiQ/cw-geodata/19a290c20ec4672dc2e9be37f451d136166bb28a/cw_geodata/data/sample_b_from_df2px.tif
--------------------------------------------------------------------------------
/cw_geodata/data/sample_b_mask_inner.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CosmiQ/cw-geodata/19a290c20ec4672dc2e9be37f451d136166bb28a/cw_geodata/data/sample_b_mask_inner.tif
--------------------------------------------------------------------------------
/cw_geodata/data/sample_b_mask_outer.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CosmiQ/cw-geodata/19a290c20ec4672dc2e9be37f451d136166bb28a/cw_geodata/data/sample_b_mask_outer.tif
--------------------------------------------------------------------------------
/cw_geodata/data/sample_b_mask_outer_10.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CosmiQ/cw-geodata/19a290c20ec4672dc2e9be37f451d136166bb28a/cw_geodata/data/sample_b_mask_outer_10.tif
--------------------------------------------------------------------------------
/cw_geodata/data/sample_c_from_df2px.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CosmiQ/cw-geodata/19a290c20ec4672dc2e9be37f451d136166bb28a/cw_geodata/data/sample_c_from_df2px.tif
--------------------------------------------------------------------------------
/cw_geodata/data/sample_c_mask.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CosmiQ/cw-geodata/19a290c20ec4672dc2e9be37f451d136166bb28a/cw_geodata/data/sample_c_mask.tif
--------------------------------------------------------------------------------
/cw_geodata/data/sample_fbc_from_df2px.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CosmiQ/cw-geodata/19a290c20ec4672dc2e9be37f451d136166bb28a/cw_geodata/data/sample_fbc_from_df2px.tif
--------------------------------------------------------------------------------
/cw_geodata/data/sample_fp_from_df2px.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CosmiQ/cw-geodata/19a290c20ec4672dc2e9be37f451d136166bb28a/cw_geodata/data/sample_fp_from_df2px.tif
--------------------------------------------------------------------------------
/cw_geodata/data/sample_fp_mask.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CosmiQ/cw-geodata/19a290c20ec4672dc2e9be37f451d136166bb28a/cw_geodata/data/sample_fp_mask.tif
--------------------------------------------------------------------------------
/cw_geodata/data/sample_fp_mask_from_geojson.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CosmiQ/cw-geodata/19a290c20ec4672dc2e9be37f451d136166bb28a/cw_geodata/data/sample_fp_mask_from_geojson.tif
--------------------------------------------------------------------------------
/cw_geodata/data/sample_geotiff.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CosmiQ/cw-geodata/19a290c20ec4672dc2e9be37f451d136166bb28a/cw_geodata/data/sample_geotiff.tif
--------------------------------------------------------------------------------
/cw_geodata/data/sample_graph.pkl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CosmiQ/cw-geodata/19a290c20ec4672dc2e9be37f451d136166bb28a/cw_geodata/data/sample_graph.pkl
--------------------------------------------------------------------------------
/cw_geodata/data/sample_roads.geojson:
--------------------------------------------------------------------------------
1 | {
2 | "type": "FeatureCollection",
3 | "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
4 | "features": [
5 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "2", "lane_number": "2", "one_way_ty": "2", "paved": "1", "road_id": 14655, "road_type": "5", "origarea": 0, "origlen": 0.0017089240319089305, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.305334270350002, 36.158651982430001 ], [ -115.30533142486, 36.158449259580003 ], [ -115.305334246699999, 36.158405521070001 ], [ -115.305358232339998, 36.158351906109999 ], [ -115.305387861650004, 36.158325098630002 ], [ -115.305568459400007, 36.158171308370001 ], [ -115.305710962310002, 36.1580457365 ], [ -115.305754700820003, 36.158009052579999 ], [ -115.305761755419994, 36.157982245100001 ], [ -115.30576316634, 36.157942739349998 ], [ -115.305764577260007, 36.157628104209998 ], [ -115.30576316634, 36.157506765100003 ], [ -115.305756111739996, 36.157477135779999 ], [ -115.305637880419994, 36.15715224209 ] ] } },
6 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "2", "lane_number": "2", "one_way_ty": "2", "paved": "1", "road_id": 19941, "road_type": "5", "origarea": 0, "origlen": 0.0021840289174551468, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.3040076, 36.158656103202638 ], [ -115.304718257900007, 36.1586538711 ] ] } },
7 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "1", "lane_number": "1", "one_way_ty": "2", "paved": "1", "road_id": 16932, "road_type": "5", "origarea": 0, "origlen": 0.00061020691230463111, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.304794494109998, 36.158316912030003 ], [ -115.304854175469998, 36.158295077390001 ], [ -115.304859998040001, 36.158216472680003 ], [ -115.304842530320002, 36.158165525180003 ], [ -115.304800316680002, 36.158142234899998 ], [ -115.304727534549997, 36.158140779260002 ], [ -115.304664941910005, 36.158180081609999 ], [ -115.304654752410002, 36.158228117820002 ], [ -115.304664941910005, 36.158274698390002 ], [ -115.304696966050003, 36.15830672253 ], [ -115.304771203830001, 36.158327101529999 ] ] } },
8 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "1", "lane_number": "1", "one_way_ty": "2", "paved": "1", "road_id": 20674, "road_type": "5", "origarea": 0, "origlen": 2.5421704359567551e-05, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.304794494109998, 36.158316912030003 ], [ -115.304771203830001, 36.158327101529999 ] ] } },
9 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "1", "lane_number": "1", "one_way_ty": "2", "paved": "1", "road_id": 4759, "road_type": "5", "origarea": 0, "origlen": 0.00036751717873743738, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.304777087779996, 36.158654716450002 ], [ -115.304819240040004, 36.158599306729997 ], [ -115.304810506180004, 36.158344569240001 ], [ -115.304771203830001, 36.158327101529999 ] ] } },
10 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "1", "lane_number": "1", "one_way_ty": "2", "paved": "1", "road_id": 277, "road_type": "5", "origarea": 0, "origlen": 0.00038473841342077095, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.304777087779996, 36.158654716450002 ], [ -115.304718800689997, 36.158612407509999 ], [ -115.304718800689997, 36.158363492600003 ], [ -115.304771203830001, 36.158327101529999 ] ] } },
11 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "2", "lane_number": "2", "one_way_ty": "2", "paved": "1", "road_id": 21517, "road_type": "5", "origarea": 0, "origlen": 9.5177264571465376e-05, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.304813425340001, 36.158655238599998 ], [ -115.304777087779996, 36.158654716450002 ], [ -115.304718257900007, 36.1586538711 ] ] } },
12 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "1", "lane_number": "1", "one_way_ty": "2", "paved": "1", "road_id": 9198, "road_type": "5", "origarea": 0, "origlen": 0.00028644810990776168, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.304777087779996, 36.158654716450002 ], [ -115.304714433759997, 36.158708479929999 ], [ -115.304712978119994, 36.158829298279997 ], [ -115.304763937019999, 36.158894891229998 ] ] } },
13 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "1", "lane_number": "1", "one_way_ty": "2", "paved": "1", "road_id": 13669, "road_type": "5", "origarea": 0, "origlen": 0.00026815643391558204, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.304763937019999, 36.158894891229998 ], [ -115.304816328749993, 36.158806008 ], [ -115.304816328749993, 36.158704113 ], [ -115.304777087779996, 36.158654716450002 ] ] } },
14 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "3", "lane_number": "3", "one_way_ty": "2", "paved": "1", "road_id": 15490, "road_type": "2", "origarea": 0, "origlen": 0.0017015904383822285, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.307235461239998, 36.158885233829999 ], [ -115.305533891859994, 36.158893699350003 ] ] } },
15 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "4", "lane_number": "4", "one_way_ty": "2", "paved": "1", "road_id": 22773, "road_type": "2", "origarea": 0, "origlen": 0.00091145533203984097, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.305533891859994, 36.158893699350003 ], [ -115.304812210250006, 36.158894816500002 ], [ -115.304763937019999, 36.158894891229998 ], [ -115.304716265750002, 36.158894965019996 ], [ -115.304622437619997, 36.158895110270002 ] ] } },
16 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "3", "lane_number": "3", "one_way_ty": "2", "paved": "1", "road_id": 11013, "road_type": "2", "origarea": 0, "origlen": 0.00075908014504109467, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.304622437619997, 36.158895110270002 ], [ -115.3040076, 36.158897395911893 ] ] } },
17 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "2", "lane_number": "2", "one_way_ty": "2", "paved": "1", "road_id": 13039, "road_type": "2", "origarea": 0, "origlen": 0.00014077602261271154, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.304763937019999, 36.158894891229998 ], [ -115.304762782699996, 36.159035662519997 ] ] } },
18 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "3", "lane_number": "3", "one_way_ty": "2", "paved": "1", "road_id": 13547, "road_type": "2", "origarea": 0, "origlen": 0.00076778803435859967, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.306466509909995, 36.159031264040003 ], [ -115.307234296139995, 36.159032928590001 ] ] } },
19 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "3", "lane_number": "3", "one_way_ty": "2", "paved": "1", "road_id": 10504, "road_type": "2", "origarea": 0, "origlen": 0.0015294496788786783, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.306466509909995, 36.159031264040003 ], [ -115.304937068170005, 36.159036191939997 ] ] } },
20 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "4", "lane_number": "4", "one_way_ty": "2", "paved": "1", "road_id": 22869, "road_type": "2", "origarea": 0, "origlen": 0.00092556313019524479, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.304937068170005, 36.159036191939997 ], [ -115.304762782699996, 36.159035662519997 ], [ -115.304011509310001, 36.159033380419999 ] ] } },
21 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "3", "lane_number": "3", "one_way_ty": "2", "paved": "1", "road_id": 9733, "road_type": "2", "origarea": 0, "origlen": 0.0010807946163444123, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.304011509310001, 36.159033380419999 ], [ -115.3040076, 36.159033399520865 ] ] } },
22 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "2", "lane_number": "2", "one_way_ty": "2", "paved": "1", "road_id": 2207, "road_type": "5", "origarea": 0, "origlen": 0.0021187288976393381, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.304031262180004, 36.159379055789998 ], [ -115.304038316779994, 36.159325440830003 ], [ -115.30406371334, 36.159292989679997 ], [ -115.304101808179993, 36.159271825879998 ], [ -115.304165299570002, 36.159261949440001 ], [ -115.305879567229994, 36.159256305760003 ], [ -115.305933182190003, 36.159261949440001 ], [ -115.30596281151, 36.159295811520003 ], [ -115.305974098869996, 36.159346604630002 ], [ -115.305972513970005, 36.159396974929997 ] ] } },
23 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "2", "lane_number": "2", "one_way_ty": "2", "paved": "1", "road_id": 14360, "road_type": "5", "origarea": 0, "origlen": 0.00060620635352942218, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.306909865430001, 36.159582676630002 ], [ -115.306901073229994, 36.159404452350003 ], [ -115.306918004270003, 36.159325440830003 ], [ -115.306957510030003, 36.159277469560003 ], [ -115.307028056020002, 36.159249251159999 ], [ -115.307236872160004, 36.15924642932 ] ] } },
24 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "2", "lane_number": "2", "one_way_ty": "2", "paved": "1", "road_id": 18886, "road_type": "5", "origarea": 0, "origlen": 0.00017671102624952563, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.304031262180004, 36.159379055789998 ], [ -115.304035910479996, 36.159555705670002 ] ] } },
25 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "4", "lane_number": "4", "one_way_ty": "2", "paved": "1", "road_id": 4414, "road_type": "5", "origarea": 0, "origlen": 0.00055551672896427928, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.305960011426549, 36.159887699800002 ], [ -115.305972513970005, 36.159396974929997 ] ] } },
26 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "4", "lane_number": "4", "one_way_ty": "2", "paved": "1", "road_id": 8866, "road_type": "5", "origarea": 0, "origlen": 0.00040571587428926529, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.306898346152181, 36.159887699800002 ], [ -115.306908096840004, 36.159764841159998 ], [ -115.306909865430001, 36.159582676630002 ] ] } },
27 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "2", "lane_number": "2", "one_way_ty": "2", "paved": "1", "road_id": 5865, "road_type": "5", "origarea": 0, "origlen": 0.00056254711252776385, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.304035910479996, 36.159555705670002 ], [ -115.304028602435167, 36.159887699800002 ] ] } },
28 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "2", "lane_number": "2", "one_way_ty": "2", "paved": "1", "road_id": 4214, "road_type": "5", "origarea": 0, "origlen": 0.0030770784666620775, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.304540695628134, 36.156377699799997 ], [ -115.304225969130002, 36.15646409531 ], [ -115.3040076, 36.156523805617901 ] ] } },
29 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "2", "lane_number": "2", "one_way_ty": "2", "paved": "1", "road_id": 6668, "road_type": "5", "origarea": 0, "origlen": 0.00030445562381666708, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.304564589899996, 36.157148391450001 ], [ -115.304675347110006, 36.157431986349998 ] ] } },
30 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "2", "lane_number": "2", "one_way_ty": "2", "paved": "1", "road_id": 11117, "road_type": "5", "origarea": 0, "origlen": 0.00028852803692290427, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.304475211440007, 36.157495147520002 ], [ -115.304364239280005, 36.157228813880003 ] ] } },
31 | { "type": "Feature", "properties": { "bridge_typ": "2", "heading": "0", "lane_numbe": "2", "lane_number": "2", "one_way_ty": "2", "paved": "1", "road_id": 17863, "road_type": "5", "origarea": 0, "origlen": 0.006987667279550927, "partialDec": 1, "truncated": 0 }, "geometry": { "type": "LineString", "coordinates": [ [ -115.304813425340001, 36.158655238599998 ], [ -115.305334270350002, 36.158651982430001 ], [ -115.305488036970004, 36.158651021129998 ], [ -115.30614834747, 36.158652432049998 ], [ -115.30689472409, 36.158653842969997 ], [ -115.306967386470006, 36.158653137510001 ], [ -115.307004775839999, 36.158647493830003 ], [ -115.307027350560006, 36.158634090089997 ], [ -115.307047808899995, 36.158612220830001 ], [ -115.307061212639994, 36.158561427720002 ], [ -115.307018885039994, 36.157412233469998 ], [ -115.307016063199995, 36.157364262190001 ], [ -115.306996310320002, 36.157331811040002 ], [ -115.306963859169997, 36.15729794896 ], [ -115.30691588789, 36.157268319640004 ], [ -115.30674798842, 36.157173788009999 ], [ -115.306716948190001, 36.157155446049998 ], [ -115.306622416549999, 36.157121583970003 ], [ -115.30652647399999, 36.157103242010002 ], [ -115.306252755540001, 36.15704116154 ], [ -115.306186442309993, 36.157029874179997 ], [ -115.306077801480001, 36.157032696020003 ], [ -115.305988913530001, 36.157051037979997 ], [ -115.305847821539999, 36.157096187409998 ], [ -115.305702496790005, 36.157134282249999 ], [ -115.305637880419994, 36.15715224209 ], [ -115.305235482309996, 36.157264086879998 ], [ -115.304985749490001, 36.157337454710003 ], [ -115.304788220700004, 36.157388247829999 ], [ -115.304675347110006, 36.157431986349998 ], [ -115.304569528119998, 36.15747290302 ], [ -115.304475211440007, 36.157495147520002 ], [ -115.304419970609999, 36.157508176020002 ], [ -115.304277467700004, 36.157543449019997 ], [ -115.3040076, 36.157610573470834 ] ] } }
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/cw_geodata/data/split_multi_grouped_result.json:
--------------------------------------------------------------------------------
1 | {"type": "FeatureCollection", "features": [{"id": "0", "type": "Feature", "properties": {"field_1": 1, "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "8086", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "137.5869833444336", "origlen": "0", "partialDec": "1.0", "truncated": "0"}, "geometry": {"type": "Polygon", "coordinates": [[[742959.5157261142, 3739469.858595584], [742964.2412947685, 3739469.934550551], [742964.2835617226, 3739467.18306186], [742968.7404872194, 3739467.252177057], [742968.7827553968, 3739464.50068832], [742970.1818431471, 3739464.5252224], [742970.3714329029, 3739451.621851874], [742963.6070103869, 3739451.527265817], [742963.4608848467, 3739461.268512606], [742959.6434285438, 3739461.204586885], [742959.5157261142, 3739469.858595584]]]}}, {"id": "1", "type": "Feature", "properties": {"field_1": 2, "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "8229", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "232.75674536334253", "origlen": "0", "partialDec": "1.0", "truncated": "0"}, "geometry": {"type": "Polygon", "coordinates": [[[743020.5285401859, 3739472.034202539], [743027.7523358399, 3739473.017358276], [743027.9560968133, 3739471.568572332], [743030.4316793848, 3739471.909114651], [743031.0890603127, 3739467.208761358], [743031.6330556386, 3739464.048288816], [743033.5432770674, 3739464.385528093], [743035.4323498101, 3739453.545475867], [743022.4840836683, 3739451.306581199], [743020.9414738066, 3739460.1909273], [743022.0743392245, 3739460.841333462], [743020.5285401859, 3739472.034202539]]]}}, {"id": "2", "type": "Feature", "properties": {"field_1": 3, "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "8228", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "115.83593626400437", "origlen": "0", "partialDec": "1.0", "truncated": "0"}, "geometry": {"type": "Polygon", "coordinates": [[[743003.1399996518, 3739467.617796136], [743015.3542409713, 3739467.740245839], [743015.3977939934, 3739462.391609387], [743017.5941185456, 3739462.414260812], [743017.6373293742, 3739457.442983567], [743011.2706792641, 3739457.380694342], [743011.2414975561, 3739460.709668207], [743005.2546582168, 3739460.657057802], [743005.2795396517, 3739457.860729276], [743003.231432164, 3739457.841856197], [743003.1399996518, 3739467.617796136]]]}}, {"id": "3", "type": "Feature", "properties": {"field_1": 4, "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "7812", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "1117.803722432596", "origlen": "0", "partialDec": "1.0", "truncated": "0"}, "geometry": {"type": "Polygon", "coordinates": [[[742737.216587933, 3739529.428720969], [742737.2726519796, 3739527.953976202], [742741.2542792484, 3739528.121883995], [742741.9995199341, 3739510.127116858], [742744.0181279983, 3739510.211778471], [742744.1330298374, 3739507.517637256], [742742.058839861, 3739507.431561362], [742742.5669390478, 3739495.113465066], [742744.3449797556, 3739495.180906138], [742745.0815230057, 3739477.163720175], [742722.4685948562, 3739476.244281095], [742720.3770908483, 3739527.479686408], [742728.5166346187, 3739527.808876015], [742728.4659341584, 3739529.072875594], [742737.216587933, 3739529.428720969]]]}}, {"id": "4", "type": "Feature", "properties": {"field_1": 5, "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "120819", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "868.6465770175508", "origlen": "0", "partialDec": "1.0", "truncated": "0"}, "geometry": {"type": "Polygon", "coordinates": [[[742779.0589662758, 3739517.785098479], [742785.7264630584, 3739518.043576586], [742785.7797116076, 3739516.679750225], [742787.9002177101, 3739516.767014552], [742788.018007018, 3739513.595687717], [742789.4963797061, 3739512.689894631], [742794.7850222825, 3739512.85779129], [742794.7384953512, 3739514.321680113], [742800.8142568704, 3739514.520712196], [742800.8579616819, 3739513.167741883], [742804.8404431387, 3739513.302400339], [742806.719179487, 3739511.962838294], [742809.5193322457, 3739513.754459812], [742810.8216116317, 3739515.39696687], [742810.8425385487, 3739518.216655498], [742815.6076734723, 3739518.193654652], [742817.1555002977, 3739514.559269692], [742818.4727785089, 3739513.427399684], [742820.7698578427, 3739513.496967576], [742821.0155526869, 3739505.30103076], [742818.8296341306, 3739505.234292389], [742818.9082965751, 3739502.872199133], [742817.049548522, 3739503.79050431], [742814.6007570606, 3739498.755805385], [742814.9873573856, 3739494.492515358], [742817.0303021728, 3739493.257025897], [742819.6744798073, 3739492.436405651], [742805.5144255088, 3739491.920608293], [742805.3067322085, 3739497.531435517], [742789.7855063399, 3739496.958818469], [742787.9062348148, 3739496.134058065], [742787.21331656, 3739494.95102441], [742788.1948983755, 3739493.533130954], [742788.6153025262, 3739492.311837214], [742788.6233625122, 3739490.902464679], [742775.3039837473, 3739490.86316813], [742776.4324709803, 3739492.778722805], [742776.4256696098, 3739495.231436581], [742779.1062013684, 3739497.352975133], [742778.8313719494, 3739499.41040163], [742777.2480186591, 3739502.255856039], [742776.2833758653, 3739503.008239368], [742773.9964049477, 3739502.905642527], [742773.7390761343, 3739508.282126087], [742779.373780194, 3739510.878410715], [742779.8477320484, 3739514.109192995], [742779.1370028395, 3739515.811456004], [742779.0589662758, 3739517.785098479]]]}}, {"id": "5", "type": "Feature", "properties": {"field_1": 1, "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "7813", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "446.8580438238858", "origlen": "0", "partialDec": "0.4599659863913662", "truncated": "1"}, "geometry": {"type": "Polygon", "coordinates": [[[742693.3122179032, 3739539.0], [742694.5911374168, 3739534.692852943], [742701.8596583691, 3739536.831200783], [742703.3902353786, 3739531.686894269], [742687.859493556, 3739527.118546354], [742687.4643547137, 3739528.440377403], [742683.2411743457, 3739527.200840465], [742681.172764875, 3739530.167156974], [742679.1856038158, 3739536.498545914], [742677.5853192207, 3739536.002776217], [742676.6595880231, 3739539.0], [742693.3122179032, 3739539.0]]]}}, {"id": "6", "type": "Feature", "properties": {"field_1": 2, "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "120818", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "921.0009706238087", "origlen": "0", "partialDec": "0.13212004440894673", "truncated": "1"}, "geometry": {"type": "MultiPolygon", "coordinates": [[[[742820.9164519846, 3739539.0], [742819.2061650158, 3739537.419993884], [742819.3015388706, 3739534.037214165], [742810.5031569091, 3739533.735573504], [742810.4563450023, 3739535.210554277], [742808.8447725036, 3739535.158436355], [742808.7962669341, 3739536.699968245], [742807.3359817594, 3739538.716122539], [742806.6252731845, 3739539.0], [742820.9164519846, 3739539.0]]], [[[742784.7538719983, 3739539.0], [742786.5276360018, 3739535.73363368], [742785.0281770587, 3739534.918539529], [742785.0537910719, 3739532.455205162], [742775.2400383659, 3739532.349736623], [742775.1763434813, 3739538.130713525], [742775.8829740167, 3739539.0], [742784.7538719983, 3739539.0]]]]}}, {"id": "7", "type": "Feature", "properties": {"field_1": 3, "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "122421", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "628.0617074431335", "origlen": "0", "partialDec": "0.6854550148892067", "truncated": "1"}, "geometry": {"type": "Polygon", "coordinates": [[[743051.0, 3739240.429075356], [743019.7339072112, 3739241.220183459], [743020.0951837152, 3739255.058807612], [743051.0, 3739254.279686034], [743051.0, 3739240.429075356]]]}}, {"id": "8", "type": "Feature", "properties": {"field_1": 4, "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "122449", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "164.34510745720252", "origlen": "0", "partialDec": "0.3268898818394317", "truncated": "1"}, "geometry": {"type": "Polygon", "coordinates": [[[743046.4084919207, 3739315.608520228], [743051.0, 3739315.560417519], [743051.0, 3739304.00325697], [743046.2950427994, 3739304.051510714], [743046.4084919207, 3739315.608520228]]]}}, {"id": "9", "type": "Feature", "properties": {"field_1": 6, "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "84910", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "67.0691122589702", "origlen": "0", "partialDec": "1.0", "truncated": "0"}, "geometry": {"type": "Polygon", "coordinates": [[[741360.3983888365, 3743875.358136298], [741367.4885894872, 3743871.975018986], [741362.9199707231, 3743862.502813682], [741357.5458472944, 3743865.074794827], [741360.0725632336, 3743870.299843532], [741360.3983888365, 3743875.358136298]]]}}, {"id": "10", "type": "Feature", "properties": {"field_1": 5, "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "120818", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "921.0009706238087", "origlen": "0", "partialDec": "0.13212004440894673", "truncated": "1"}, "geometry": {"type": "Polygon", "coordinates": [[[742820.9164519846, 3739539.0], [742819.2061650158, 3739537.419993884], [742819.3015388706, 3739534.037214165], [742810.5031569091, 3739533.735573504], [742810.4563450023, 3739535.210554277], [742808.8447725036, 3739535.158436355], [742808.7962669341, 3739536.699968245], [742807.3359817594, 3739538.716122539], [742806.6252731845, 3739539.0], [742820.9164519846, 3739539.0]]]}}, {"id": "11", "type": "Feature", "properties": {"field_1": 6, "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "120818", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "921.0009706238087", "origlen": "0", "partialDec": "0.13212004440894673", "truncated": "1"}, "geometry": {"type": "Polygon", "coordinates": [[[742784.7538719983, 3739539.0], [742786.5276360018, 3739535.73363368], [742785.0281770587, 3739534.918539529], [742785.0537910719, 3739532.455205162], [742775.2400383659, 3739532.349736623], [742775.1763434813, 3739538.130713525], [742775.8829740167, 3739539.0], [742784.7538719983, 3739539.0]]]}}]}
--------------------------------------------------------------------------------
/cw_geodata/data/split_multi_result.csv:
--------------------------------------------------------------------------------
1 | ,field_1,access,addr_house,addr_hou_1,addr_inter,admin_leve,aerialway,aeroway,amenity,area,barrier,bicycle,boundary,brand,bridge,building,constructi,covered,culvert,cutting,denominati,disused,embankment,foot,generator_,harbour,highway,historic,horse,intermitte,junction,landuse,layer,leisure,lock,man_made,military,motorcar,name,natural,office,oneway,operator,osm_id,place,population,power,power_sour,public_tra,railway,ref,religion,route,service,shop,sport,surface,tags,toll,tourism,tower_type,tunnel,water,waterway,wetland,width,wood,z_order,tracktype,way_area,origarea,origlen,partialDec,truncated,geometry
2 | 0,660,,,,,,,,,,,,,,,yes,,,,,,,,,,,,,,,,,,,,,,,Occlusion,,,,,8086,,,,,,,,,,,,,,"""security:classification""=>""UNCLASSIFIED"",""source""=>""Unknown""",,,,,,,,,,-999999,,-999999.0,137.5869833444336,0,1.0,0,"POLYGON ((742959.5157261142 3739469.858595584, 742964.2412947685 3739469.934550551, 742964.2835617226 3739467.18306186, 742968.7404872194 3739467.252177057, 742968.7827553968 3739464.50068832, 742970.1818431471 3739464.5252224, 742970.3714329029 3739451.621851874, 742963.6070103869 3739451.527265817, 742963.4608848467 3739461.268512606, 742959.6434285438 3739461.204586885, 742959.5157261142 3739469.858595584))"
3 | 1,661,,,,,,,,,,,,,,,yes,,,,,,,,,,,,,,,,,,,,,,,Occlusion,,,,,8229,,,,,,,,,,,,,,"""security:classification""=>""UNCLASSIFIED"",""source""=>""Unknown""",,,,,,,,,,-999999,,-999999.0,232.75674536334253,0,1.0,0,"POLYGON ((743020.5285401859 3739472.034202539, 743027.7523358399 3739473.017358276, 743027.9560968133 3739471.568572332, 743030.4316793848 3739471.909114651, 743031.0890603127 3739467.208761358, 743031.6330556386 3739464.048288816, 743033.5432770674 3739464.385528093, 743035.4323498101 3739453.545475867, 743022.4840836683 3739451.306581199, 743020.9414738066 3739460.1909273, 743022.0743392245 3739460.841333462, 743020.5285401859 3739472.034202539))"
4 | 2,662,,,,,,,,,,,,,,,yes,,,,,,,,,,,,,,,,,,,,,,,Occlusion,,,,,8228,,,,,,,,,,,,,,"""security:classification""=>""UNCLASSIFIED"",""source""=>""Unknown""",,,,,,,,,,-999999,,-999999.0,115.83593626400437,0,1.0,0,"POLYGON ((743003.1399996518 3739467.617796136, 743015.3542409713 3739467.740245839, 743015.3977939934 3739462.391609387, 743017.5941185456 3739462.414260812, 743017.6373293742 3739457.442983567, 743011.2706792641 3739457.380694342, 743011.2414975561 3739460.709668207, 743005.2546582168 3739460.657057802, 743005.2795396517 3739457.860729276, 743003.231432164 3739457.841856197, 743003.1399996518 3739467.617796136))"
5 | 3,663,,,,,,,,,,,,,,,yes,,,,,,,,,,,,,,,,,,,,,,,,,,,,7812,,,,,,,,,,,,,,"""security:classification""=>""UNCLASSIFIED"",""source""=>""Unknown""",,,,,,,,,,-999999,,-999999.0,1117.803722432596,0,1.0,0,"POLYGON ((742737.216587933 3739529.428720969, 742737.2726519796 3739527.953976202, 742741.2542792484 3739528.121883995, 742741.9995199341 3739510.127116858, 742744.0181279983 3739510.211778471, 742744.1330298374 3739507.517637256, 742742.058839861 3739507.431561362, 742742.5669390478 3739495.113465066, 742744.3449797556 3739495.180906138, 742745.0815230057 3739477.163720175, 742722.4685948562 3739476.244281095, 742720.3770908483 3739527.479686408, 742728.5166346187 3739527.808876015, 742728.4659341584 3739529.072875594, 742737.216587933 3739529.428720969))"
6 | 4,664,,,,,,,,,,,,,,,yes,,,,,,,,,,,,,,,,,,,,,,,Occlusion,,,,,120819,,,,,,,,,,,,,,"""security:classification""=>""UNCLASSIFIED"",""source""=>""Unknown""",,,,,,,,,,-999999,,-999999.0,868.6465770175508,0,1.0,0,"POLYGON ((742779.0589662758 3739517.785098479, 742785.7264630584 3739518.043576586, 742785.7797116076 3739516.679750225, 742787.9002177101 3739516.767014552, 742788.018007018 3739513.595687717, 742789.4963797061 3739512.689894631, 742794.7850222825 3739512.85779129, 742794.7384953512 3739514.321680113, 742800.8142568704 3739514.520712196, 742800.8579616819 3739513.167741883, 742804.8404431387 3739513.302400339, 742806.719179487 3739511.962838294, 742809.5193322457 3739513.754459812, 742810.8216116317 3739515.39696687, 742810.8425385487 3739518.216655498, 742815.6076734723 3739518.193654652, 742817.1555002977 3739514.559269692, 742818.4727785089 3739513.427399684, 742820.7698578427 3739513.496967576, 742821.0155526869 3739505.30103076, 742818.8296341306 3739505.234292389, 742818.9082965751 3739502.872199133, 742817.049548522 3739503.79050431, 742814.6007570606 3739498.755805385, 742814.9873573856 3739494.492515358, 742817.0303021728 3739493.257025897, 742819.6744798073 3739492.436405651, 742805.5144255088 3739491.920608293, 742805.3067322085 3739497.531435517, 742789.7855063399 3739496.958818469, 742787.9062348148 3739496.134058065, 742787.21331656 3739494.95102441, 742788.1948983755 3739493.533130954, 742788.6153025262 3739492.311837214, 742788.6233625122 3739490.902464679, 742775.3039837473 3739490.86316813, 742776.4324709803 3739492.778722805, 742776.4256696098 3739495.231436581, 742779.1062013684 3739497.352975133, 742778.8313719494 3739499.41040163, 742777.2480186591 3739502.255856039, 742776.2833758653 3739503.008239368, 742773.9964049477 3739502.905642527, 742773.7390761343 3739508.282126087, 742779.373780194 3739510.878410715, 742779.8477320484 3739514.109192995, 742779.1370028395 3739515.811456004, 742779.0589662758 3739517.785098479))"
7 | 5,665,,,,,,,,,,,,,,,yes,,,,,,,,,,,,,,,,,,,,,,,Occlusion,,,,,7813,,,,,,,,,,,,,,"""security:classification""=>""UNCLASSIFIED"",""source""=>""Unknown""",,,,,,,,,,-999999,,-999999.0,446.8580438238858,0,0.4599659863913662,1,"POLYGON ((742693.3122179032 3739539, 742694.5911374168 3739534.692852943, 742701.8596583691 3739536.831200783, 742703.3902353786 3739531.686894269, 742687.859493556 3739527.118546354, 742687.4643547137 3739528.440377403, 742683.2411743457 3739527.200840465, 742681.172764875 3739530.167156974, 742679.1856038158 3739536.498545914, 742677.5853192207 3739536.002776217, 742676.6595880231 3739539, 742693.3122179032 3739539))"
8 | 6,666,,,,,,,,,,,,,,,yes,,,,,,,,,,,,,,,,,,,,,,,Occlusion,,,,,120818,,,,,,,,,,,,,,"""security:classification""=>""UNCLASSIFIED"",""source""=>""Unknown""",,,,,,,,,,-999999,,-999999.0,921.0009706238087,0,0.13212004440894673,1,"MULTIPOLYGON (((742820.9164519846 3739539, 742819.2061650158 3739537.419993884, 742819.3015388706 3739534.037214165, 742810.5031569091 3739533.735573504, 742810.4563450023 3739535.210554277, 742808.8447725036 3739535.158436355, 742808.7962669341 3739536.699968245, 742807.3359817594 3739538.716122539, 742806.6252731845 3739539, 742820.9164519846 3739539)), ((742784.7538719983 3739539, 742786.5276360018 3739535.73363368, 742785.0281770587 3739534.918539529, 742785.0537910719 3739532.455205162, 742775.2400383659 3739532.349736623, 742775.1763434813 3739538.130713525, 742775.8829740167 3739539, 742784.7538719983 3739539)))"
9 | 7,667,,,,,,,,,,,,,,,yes,,,,,,,,,,,,,,,,,,,,,,,Occlusion,,,,,122421,,,,,,,,,,,,,,"""security:classification""=>""UNCLASSIFIED"",""source""=>""Unknown""",,,,,,,,,,-999999,,-999999.0,628.0617074431335,0,0.6854550148892067,1,"POLYGON ((743051 3739240.429075356, 743019.7339072112 3739241.220183459, 743020.0951837152 3739255.058807612, 743051 3739254.279686034, 743051 3739240.429075356))"
10 | 8,668,,,,,,,,,,,,,,,yes,,,,,,,,,,,,,,,,,,,,,,,Occlusion,,,,,122449,,,,,,,,,,,,,,"""security:classification""=>""UNCLASSIFIED"",""source""=>""Unknown""",,,,,,,,,,-999999,,-999999.0,164.34510745720252,0,0.3268898818394317,1,"POLYGON ((743046.4084919207 3739315.608520228, 743051 3739315.560417519, 743051 3739304.00325697, 743046.2950427994 3739304.051510714, 743046.4084919207 3739315.608520228))"
11 | 9,669,,,,,,,,,,,,,,,yes,,,,,,,,,,,,,,,,,,,,,,,Occlusion,,,,,84910,,,,,,,,,,,,,,"""security:classification""=>""UNCLASSIFIED"",""source""=>""Unknown""",,,,,,,,,,-999999,,-999999.0,67.0691122589702,0,1.0,0,"POLYGON ((741360.3983888365 3743875.358136298, 741367.4885894872 3743871.975018986, 741362.9199707231 3743862.502813682, 741357.5458472944 3743865.074794827, 741360.0725632336 3743870.299843532, 741360.3983888365 3743875.358136298))"
12 | 10,666,,,,,,,,,,,,,,,yes,,,,,,,,,,,,,,,,,,,,,,,Occlusion,,,,,120818,,,,,,,,,,,,,,"""security:classification""=>""UNCLASSIFIED"",""source""=>""Unknown""",,,,,,,,,,-999999,,-999999.0,921.0009706238087,0,0.13212004440894673,1,"POLYGON ((742820.9164519846 3739539, 742819.2061650158 3739537.419993884, 742819.3015388706 3739534.037214165, 742810.5031569091 3739533.735573504, 742810.4563450023 3739535.210554277, 742808.8447725036 3739535.158436355, 742808.7962669341 3739536.699968245, 742807.3359817594 3739538.716122539, 742806.6252731845 3739539, 742820.9164519846 3739539))"
13 | 11,666,,,,,,,,,,,,,,,yes,,,,,,,,,,,,,,,,,,,,,,,Occlusion,,,,,120818,,,,,,,,,,,,,,"""security:classification""=>""UNCLASSIFIED"",""source""=>""Unknown""",,,,,,,,,,-999999,,-999999.0,921.0009706238087,0,0.13212004440894673,1,"POLYGON ((742784.7538719983 3739539, 742786.5276360018 3739535.73363368, 742785.0281770587 3739534.918539529, 742785.0537910719 3739532.455205162, 742775.2400383659 3739532.349736623, 742775.1763434813 3739538.130713525, 742775.8829740167 3739539, 742784.7538719983 3739539))"
14 |
--------------------------------------------------------------------------------
/cw_geodata/data/split_multi_result.json:
--------------------------------------------------------------------------------
1 | {"type": "FeatureCollection", "features": [{"id": "0", "type": "Feature", "properties": {"field_1": "660", "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "8086", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "137.5869833444336", "origlen": "0", "partialDec": "1.0", "truncated": "0"}, "geometry": {"type": "Polygon", "coordinates": [[[742959.5157261142, 3739469.858595584], [742964.2412947685, 3739469.934550551], [742964.2835617226, 3739467.18306186], [742968.7404872194, 3739467.252177057], [742968.7827553968, 3739464.50068832], [742970.1818431471, 3739464.5252224], [742970.3714329029, 3739451.621851874], [742963.6070103869, 3739451.527265817], [742963.4608848467, 3739461.268512606], [742959.6434285438, 3739461.204586885], [742959.5157261142, 3739469.858595584]]]}}, {"id": "1", "type": "Feature", "properties": {"field_1": "661", "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "8229", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "232.75674536334253", "origlen": "0", "partialDec": "1.0", "truncated": "0"}, "geometry": {"type": "Polygon", "coordinates": [[[743020.5285401859, 3739472.034202539], [743027.7523358399, 3739473.017358276], [743027.9560968133, 3739471.568572332], [743030.4316793848, 3739471.909114651], [743031.0890603127, 3739467.208761358], [743031.6330556386, 3739464.048288816], [743033.5432770674, 3739464.385528093], [743035.4323498101, 3739453.545475867], [743022.4840836683, 3739451.306581199], [743020.9414738066, 3739460.1909273], [743022.0743392245, 3739460.841333462], [743020.5285401859, 3739472.034202539]]]}}, {"id": "2", "type": "Feature", "properties": {"field_1": "662", "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "8228", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "115.83593626400437", "origlen": "0", "partialDec": "1.0", "truncated": "0"}, "geometry": {"type": "Polygon", "coordinates": [[[743003.1399996518, 3739467.617796136], [743015.3542409713, 3739467.740245839], [743015.3977939934, 3739462.391609387], [743017.5941185456, 3739462.414260812], [743017.6373293742, 3739457.442983567], [743011.2706792641, 3739457.380694342], [743011.2414975561, 3739460.709668207], [743005.2546582168, 3739460.657057802], [743005.2795396517, 3739457.860729276], [743003.231432164, 3739457.841856197], [743003.1399996518, 3739467.617796136]]]}}, {"id": "3", "type": "Feature", "properties": {"field_1": "663", "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "7812", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "1117.803722432596", "origlen": "0", "partialDec": "1.0", "truncated": "0"}, "geometry": {"type": "Polygon", "coordinates": [[[742737.216587933, 3739529.428720969], [742737.2726519796, 3739527.953976202], [742741.2542792484, 3739528.121883995], [742741.9995199341, 3739510.127116858], [742744.0181279983, 3739510.211778471], [742744.1330298374, 3739507.517637256], [742742.058839861, 3739507.431561362], [742742.5669390478, 3739495.113465066], [742744.3449797556, 3739495.180906138], [742745.0815230057, 3739477.163720175], [742722.4685948562, 3739476.244281095], [742720.3770908483, 3739527.479686408], [742728.5166346187, 3739527.808876015], [742728.4659341584, 3739529.072875594], [742737.216587933, 3739529.428720969]]]}}, {"id": "4", "type": "Feature", "properties": {"field_1": "664", "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "120819", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "868.6465770175508", "origlen": "0", "partialDec": "1.0", "truncated": "0"}, "geometry": {"type": "Polygon", "coordinates": [[[742779.0589662758, 3739517.785098479], [742785.7264630584, 3739518.043576586], [742785.7797116076, 3739516.679750225], [742787.9002177101, 3739516.767014552], [742788.018007018, 3739513.595687717], [742789.4963797061, 3739512.689894631], [742794.7850222825, 3739512.85779129], [742794.7384953512, 3739514.321680113], [742800.8142568704, 3739514.520712196], [742800.8579616819, 3739513.167741883], [742804.8404431387, 3739513.302400339], [742806.719179487, 3739511.962838294], [742809.5193322457, 3739513.754459812], [742810.8216116317, 3739515.39696687], [742810.8425385487, 3739518.216655498], [742815.6076734723, 3739518.193654652], [742817.1555002977, 3739514.559269692], [742818.4727785089, 3739513.427399684], [742820.7698578427, 3739513.496967576], [742821.0155526869, 3739505.30103076], [742818.8296341306, 3739505.234292389], [742818.9082965751, 3739502.872199133], [742817.049548522, 3739503.79050431], [742814.6007570606, 3739498.755805385], [742814.9873573856, 3739494.492515358], [742817.0303021728, 3739493.257025897], [742819.6744798073, 3739492.436405651], [742805.5144255088, 3739491.920608293], [742805.3067322085, 3739497.531435517], [742789.7855063399, 3739496.958818469], [742787.9062348148, 3739496.134058065], [742787.21331656, 3739494.95102441], [742788.1948983755, 3739493.533130954], [742788.6153025262, 3739492.311837214], [742788.6233625122, 3739490.902464679], [742775.3039837473, 3739490.86316813], [742776.4324709803, 3739492.778722805], [742776.4256696098, 3739495.231436581], [742779.1062013684, 3739497.352975133], [742778.8313719494, 3739499.41040163], [742777.2480186591, 3739502.255856039], [742776.2833758653, 3739503.008239368], [742773.9964049477, 3739502.905642527], [742773.7390761343, 3739508.282126087], [742779.373780194, 3739510.878410715], [742779.8477320484, 3739514.109192995], [742779.1370028395, 3739515.811456004], [742779.0589662758, 3739517.785098479]]]}}, {"id": "5", "type": "Feature", "properties": {"field_1": "665", "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "7813", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "446.8580438238858", "origlen": "0", "partialDec": "0.4599659863913662", "truncated": "1"}, "geometry": {"type": "Polygon", "coordinates": [[[742693.3122179032, 3739539.0], [742694.5911374168, 3739534.692852943], [742701.8596583691, 3739536.831200783], [742703.3902353786, 3739531.686894269], [742687.859493556, 3739527.118546354], [742687.4643547137, 3739528.440377403], [742683.2411743457, 3739527.200840465], [742681.172764875, 3739530.167156974], [742679.1856038158, 3739536.498545914], [742677.5853192207, 3739536.002776217], [742676.6595880231, 3739539.0], [742693.3122179032, 3739539.0]]]}}, {"id": "6", "type": "Feature", "properties": {"field_1": "666", "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "120818", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "921.0009706238087", "origlen": "0", "partialDec": "0.13212004440894673", "truncated": "1"}, "geometry": {"type": "MultiPolygon", "coordinates": [[[[742820.9164519846, 3739539.0], [742819.2061650158, 3739537.419993884], [742819.3015388706, 3739534.037214165], [742810.5031569091, 3739533.735573504], [742810.4563450023, 3739535.210554277], [742808.8447725036, 3739535.158436355], [742808.7962669341, 3739536.699968245], [742807.3359817594, 3739538.716122539], [742806.6252731845, 3739539.0], [742820.9164519846, 3739539.0]]], [[[742784.7538719983, 3739539.0], [742786.5276360018, 3739535.73363368], [742785.0281770587, 3739534.918539529], [742785.0537910719, 3739532.455205162], [742775.2400383659, 3739532.349736623], [742775.1763434813, 3739538.130713525], [742775.8829740167, 3739539.0], [742784.7538719983, 3739539.0]]]]}}, {"id": "7", "type": "Feature", "properties": {"field_1": "667", "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "122421", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "628.0617074431335", "origlen": "0", "partialDec": "0.6854550148892067", "truncated": "1"}, "geometry": {"type": "Polygon", "coordinates": [[[743051.0, 3739240.429075356], [743019.7339072112, 3739241.220183459], [743020.0951837152, 3739255.058807612], [743051.0, 3739254.279686034], [743051.0, 3739240.429075356]]]}}, {"id": "8", "type": "Feature", "properties": {"field_1": "668", "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "122449", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "164.34510745720252", "origlen": "0", "partialDec": "0.3268898818394317", "truncated": "1"}, "geometry": {"type": "Polygon", "coordinates": [[[743046.4084919207, 3739315.608520228], [743051.0, 3739315.560417519], [743051.0, 3739304.00325697], [743046.2950427994, 3739304.051510714], [743046.4084919207, 3739315.608520228]]]}}, {"id": "9", "type": "Feature", "properties": {"field_1": "669", "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "84910", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "67.0691122589702", "origlen": "0", "partialDec": "1.0", "truncated": "0"}, "geometry": {"type": "Polygon", "coordinates": [[[741360.3983888365, 3743875.358136298], [741367.4885894872, 3743871.975018986], [741362.9199707231, 3743862.502813682], [741357.5458472944, 3743865.074794827], [741360.0725632336, 3743870.299843532], [741360.3983888365, 3743875.358136298]]]}}, {"id": "10", "type": "Feature", "properties": {"field_1": "666", "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "120818", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "921.0009706238087", "origlen": "0", "partialDec": "0.13212004440894673", "truncated": "1"}, "geometry": {"type": "Polygon", "coordinates": [[[742820.9164519846, 3739539.0], [742819.2061650158, 3739537.419993884], [742819.3015388706, 3739534.037214165], [742810.5031569091, 3739533.735573504], [742810.4563450023, 3739535.210554277], [742808.8447725036, 3739535.158436355], [742808.7962669341, 3739536.699968245], [742807.3359817594, 3739538.716122539], [742806.6252731845, 3739539.0], [742820.9164519846, 3739539.0]]]}}, {"id": "11", "type": "Feature", "properties": {"field_1": "666", "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": "120818", "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": "-999999", "tracktype": "", "way_area": "-999999.0", "origarea": "921.0009706238087", "origlen": "0", "partialDec": "0.13212004440894673", "truncated": "1"}, "geometry": {"type": "Polygon", "coordinates": [[[742784.7538719983, 3739539.0], [742786.5276360018, 3739535.73363368], [742785.0281770587, 3739534.918539529], [742785.0537910719, 3739532.455205162], [742775.2400383659, 3739532.349736623], [742775.1763434813, 3739538.130713525], [742775.8829740167, 3739539.0], [742784.7538719983, 3739539.0]]]}}]}
--------------------------------------------------------------------------------
/cw_geodata/data/w_multipolygon.csv:
--------------------------------------------------------------------------------
1 | ,access,addr_house,addr_hou_1,addr_inter,admin_leve,aerialway,aeroway,amenity,area,barrier,bicycle,boundary,brand,bridge,building,constructi,covered,culvert,cutting,denominati,disused,embankment,foot,generator_,harbour,highway,historic,horse,intermitte,junction,landuse,layer,leisure,lock,man_made,military,motorcar,name,natural,office,oneway,operator,osm_id,place,population,power,power_sour,public_tra,railway,ref,religion,route,service,shop,sport,surface,tags,toll,tourism,tower_type,tunnel,water,waterway,wetland,width,wood,z_order,tracktype,way_area,origarea,origlen,partialDec,truncated,geometry
2 | 660,,,,,,,,,,,,,,,yes,,,,,,,,,,,,,,,,,,,,,,,Occlusion,,,,,8086,,,,,,,,,,,,,,"""security:classification""=>""UNCLASSIFIED"",""source""=>""Unknown""",,,,,,,,,,-999999,,-999999.0,137.5869833444336,0,1.0,0,"POLYGON ((742959.5157261142 3739469.858595584, 742964.2412947685 3739469.934550551, 742964.2835617226 3739467.18306186, 742968.7404872194 3739467.252177057, 742968.7827553968 3739464.50068832, 742970.1818431471 3739464.5252224, 742970.3714329029 3739451.621851874, 742963.6070103869 3739451.527265817, 742963.4608848467 3739461.268512606, 742959.6434285438 3739461.204586885, 742959.5157261142 3739469.858595584))"
3 | 661,,,,,,,,,,,,,,,yes,,,,,,,,,,,,,,,,,,,,,,,Occlusion,,,,,8229,,,,,,,,,,,,,,"""security:classification""=>""UNCLASSIFIED"",""source""=>""Unknown""",,,,,,,,,,-999999,,-999999.0,232.75674536334253,0,1.0,0,"POLYGON ((743020.5285401859 3739472.034202539, 743027.7523358399 3739473.017358276, 743027.9560968133 3739471.568572332, 743030.4316793848 3739471.909114651, 743031.0890603127 3739467.208761358, 743031.6330556386 3739464.048288816, 743033.5432770674 3739464.385528093, 743035.4323498101 3739453.545475867, 743022.4840836683 3739451.306581199, 743020.9414738066 3739460.1909273, 743022.0743392245 3739460.841333462, 743020.5285401859 3739472.034202539))"
4 | 662,,,,,,,,,,,,,,,yes,,,,,,,,,,,,,,,,,,,,,,,Occlusion,,,,,8228,,,,,,,,,,,,,,"""security:classification""=>""UNCLASSIFIED"",""source""=>""Unknown""",,,,,,,,,,-999999,,-999999.0,115.83593626400437,0,1.0,0,"POLYGON ((743003.1399996518 3739467.617796136, 743015.3542409713 3739467.740245839, 743015.3977939934 3739462.391609387, 743017.5941185456 3739462.414260812, 743017.6373293742 3739457.442983567, 743011.2706792641 3739457.380694342, 743011.2414975561 3739460.709668207, 743005.2546582168 3739460.657057802, 743005.2795396517 3739457.860729276, 743003.231432164 3739457.841856197, 743003.1399996518 3739467.617796136))"
5 | 663,,,,,,,,,,,,,,,yes,,,,,,,,,,,,,,,,,,,,,,,,,,,,7812,,,,,,,,,,,,,,"""security:classification""=>""UNCLASSIFIED"",""source""=>""Unknown""",,,,,,,,,,-999999,,-999999.0,1117.803722432596,0,1.0,0,"POLYGON ((742737.216587933 3739529.428720969, 742737.2726519796 3739527.953976202, 742741.2542792484 3739528.121883995, 742741.9995199341 3739510.127116858, 742744.0181279983 3739510.211778471, 742744.1330298374 3739507.517637256, 742742.058839861 3739507.431561362, 742742.5669390478 3739495.113465066, 742744.3449797556 3739495.180906138, 742745.0815230057 3739477.163720175, 742722.4685948562 3739476.244281095, 742720.3770908483 3739527.479686408, 742728.5166346187 3739527.808876015, 742728.4659341584 3739529.072875594, 742737.216587933 3739529.428720969))"
6 | 664,,,,,,,,,,,,,,,yes,,,,,,,,,,,,,,,,,,,,,,,Occlusion,,,,,120819,,,,,,,,,,,,,,"""security:classification""=>""UNCLASSIFIED"",""source""=>""Unknown""",,,,,,,,,,-999999,,-999999.0,868.6465770175508,0,1.0,0,"POLYGON ((742779.0589662758 3739517.785098479, 742785.7264630584 3739518.043576586, 742785.7797116076 3739516.679750225, 742787.9002177101 3739516.767014552, 742788.018007018 3739513.595687717, 742789.4963797061 3739512.689894631, 742794.7850222825 3739512.85779129, 742794.7384953512 3739514.321680113, 742800.8142568704 3739514.520712196, 742800.8579616819 3739513.167741883, 742804.8404431387 3739513.302400339, 742806.719179487 3739511.962838294, 742809.5193322457 3739513.754459812, 742810.8216116317 3739515.39696687, 742810.8425385487 3739518.216655498, 742815.6076734723 3739518.193654652, 742817.1555002977 3739514.559269692, 742818.4727785089 3739513.427399684, 742820.7698578427 3739513.496967576, 742821.0155526869 3739505.30103076, 742818.8296341306 3739505.234292389, 742818.9082965751 3739502.872199133, 742817.049548522 3739503.79050431, 742814.6007570606 3739498.755805385, 742814.9873573856 3739494.492515358, 742817.0303021728 3739493.257025897, 742819.6744798073 3739492.436405651, 742805.5144255088 3739491.920608293, 742805.3067322085 3739497.531435517, 742789.7855063399 3739496.958818469, 742787.9062348148 3739496.134058065, 742787.21331656 3739494.95102441, 742788.1948983755 3739493.533130954, 742788.6153025262 3739492.311837214, 742788.6233625122 3739490.902464679, 742775.3039837473 3739490.86316813, 742776.4324709803 3739492.778722805, 742776.4256696098 3739495.231436581, 742779.1062013684 3739497.352975133, 742778.8313719494 3739499.41040163, 742777.2480186591 3739502.255856039, 742776.2833758653 3739503.008239368, 742773.9964049477 3739502.905642527, 742773.7390761343 3739508.282126087, 742779.373780194 3739510.878410715, 742779.8477320484 3739514.109192995, 742779.1370028395 3739515.811456004, 742779.0589662758 3739517.785098479))"
7 | 665,,,,,,,,,,,,,,,yes,,,,,,,,,,,,,,,,,,,,,,,Occlusion,,,,,7813,,,,,,,,,,,,,,"""security:classification""=>""UNCLASSIFIED"",""source""=>""Unknown""",,,,,,,,,,-999999,,-999999.0,446.8580438238858,0,0.4599659863913662,1,"POLYGON ((742693.3122179032 3739539, 742694.5911374168 3739534.692852943, 742701.8596583691 3739536.831200783, 742703.3902353786 3739531.686894269, 742687.859493556 3739527.118546354, 742687.4643547137 3739528.440377403, 742683.2411743457 3739527.200840465, 742681.172764875 3739530.167156974, 742679.1856038158 3739536.498545914, 742677.5853192207 3739536.002776217, 742676.6595880231 3739539, 742693.3122179032 3739539))"
8 | 666,,,,,,,,,,,,,,,yes,,,,,,,,,,,,,,,,,,,,,,,Occlusion,,,,,120818,,,,,,,,,,,,,,"""security:classification""=>""UNCLASSIFIED"",""source""=>""Unknown""",,,,,,,,,,-999999,,-999999.0,921.0009706238087,0,0.13212004440894673,1,"MULTIPOLYGON (((742820.9164519846 3739539, 742819.2061650158 3739537.419993884, 742819.3015388706 3739534.037214165, 742810.5031569091 3739533.735573504, 742810.4563450023 3739535.210554277, 742808.8447725036 3739535.158436355, 742808.7962669341 3739536.699968245, 742807.3359817594 3739538.716122539, 742806.6252731845 3739539, 742820.9164519846 3739539)), ((742784.7538719983 3739539, 742786.5276360018 3739535.73363368, 742785.0281770587 3739534.918539529, 742785.0537910719 3739532.455205162, 742775.2400383659 3739532.349736623, 742775.1763434813 3739538.130713525, 742775.8829740167 3739539, 742784.7538719983 3739539)))"
9 | 667,,,,,,,,,,,,,,,yes,,,,,,,,,,,,,,,,,,,,,,,Occlusion,,,,,122421,,,,,,,,,,,,,,"""security:classification""=>""UNCLASSIFIED"",""source""=>""Unknown""",,,,,,,,,,-999999,,-999999.0,628.0617074431335,0,0.6854550148892067,1,"POLYGON ((743051 3739240.429075356, 743019.7339072112 3739241.220183459, 743020.0951837152 3739255.058807612, 743051 3739254.279686034, 743051 3739240.429075356))"
10 | 668,,,,,,,,,,,,,,,yes,,,,,,,,,,,,,,,,,,,,,,,Occlusion,,,,,122449,,,,,,,,,,,,,,"""security:classification""=>""UNCLASSIFIED"",""source""=>""Unknown""",,,,,,,,,,-999999,,-999999.0,164.34510745720252,0,0.3268898818394317,1,"POLYGON ((743046.4084919207 3739315.608520228, 743051 3739315.560417519, 743051 3739304.00325697, 743046.2950427994 3739304.051510714, 743046.4084919207 3739315.608520228))"
11 | 669,,,,,,,,,,,,,,,yes,,,,,,,,,,,,,,,,,,,,,,,Occlusion,,,,,84910,,,,,,,,,,,,,,"""security:classification""=>""UNCLASSIFIED"",""source""=>""Unknown""",,,,,,,,,,-999999,,-999999.0,67.0691122589702,0,1.0,0,"POLYGON ((741360.3983888365 3743875.358136298, 741367.4885894872 3743871.975018986, 741362.9199707231 3743862.502813682, 741357.5458472944 3743865.074794827, 741360.0725632336 3743870.299843532, 741360.3983888365 3743875.358136298))"
12 |
--------------------------------------------------------------------------------
/cw_geodata/raster_image/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CosmiQ/cw-geodata/19a290c20ec4672dc2e9be37f451d136166bb28a/cw_geodata/raster_image/__init__.py
--------------------------------------------------------------------------------
/cw_geodata/raster_image/image.py:
--------------------------------------------------------------------------------
1 | from osgeo import gdal
2 | import rasterio
3 | from affine import Affine
4 |
5 |
6 | def get_geo_transform(raster_src):
7 | """Get the geotransform for a raster image source.
8 |
9 | Arguments
10 | ---------
11 | raster_src : str, :class:`rasterio.DatasetReader`, or `osgeo.gdal.Dataset`
12 | Path to a raster image with georeferencing data to apply to `geom`.
13 | Alternatively, an opened :class:`rasterio.Band` object or
14 | :class:`osgeo.gdal.Dataset` object can be provided. Required if not
15 | using `affine_obj`.
16 |
17 | Returns
18 | -------
19 | transform : :class:`affine.Affine`
20 | An affine transformation object to the image's location in its CRS.
21 | """
22 |
23 | if isinstance(raster_src, str):
24 | affine_obj = rasterio.open(raster_src).transform
25 | elif isinstance(raster_src, rasterio.DatasetReader):
26 | affine_obj = raster_src.transform
27 | elif isinstance(raster_src, gdal.Dataset):
28 | affine_obj = Affine.from_gdal(*raster_src.GetGeoTransform())
29 |
30 | return affine_obj
31 |
--------------------------------------------------------------------------------
/cw_geodata/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CosmiQ/cw-geodata/19a290c20ec4672dc2e9be37f451d136166bb28a/cw_geodata/utils/__init__.py
--------------------------------------------------------------------------------
/cw_geodata/utils/core.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import geopandas as gpd
3 | import rasterio
4 |
5 |
6 | def _check_rasterio_im_load(im):
7 | """Check if `im` is already loaded in; if not, load it in."""
8 | if isinstance(im, str):
9 | return rasterio.open(im)
10 | elif isinstance(im, rasterio.DatasetReader):
11 | return im
12 | else:
13 | raise ValueError(
14 | "{} is not an accepted image format for rasterio.".format(im))
15 |
16 |
17 | def _check_df_load(df):
18 | """Check if `df` is already loaded in, if not, load from file."""
19 | if isinstance(df, str):
20 | if df.lower().endswith('json'):
21 | return _check_gdf_load(df)
22 | else:
23 | return pd.read_csv(df)
24 | elif isinstance(df, pd.DataFrame):
25 | return df
26 | else:
27 | raise ValueError("{} is not an accepted DataFrame format.".format(df))
28 |
29 |
30 | def _check_gdf_load(gdf):
31 | """Check if `gdf` is already loaded in, if not, load from geojson."""
32 | if isinstance(gdf, str):
33 | return gpd.read_file(gdf)
34 | elif isinstance(gdf, gpd.GeoDataFrame):
35 | return gdf
36 | else:
37 | raise ValueError(
38 | "{} is not an accepted GeoDataFrame format.".format(gdf))
39 |
--------------------------------------------------------------------------------
/cw_geodata/utils/geo.py:
--------------------------------------------------------------------------------
1 | from .core import _check_gdf_load
2 | import numpy as np
3 | import pandas as pd
4 | from affine import Affine
5 | import geopandas as gpd
6 | import os
7 | import rasterio
8 | from rasterio.enums import Resampling
9 | import ogr
10 | import shapely
11 | from shapely.errors import WKTReadingError
12 | from shapely.wkt import loads
13 | from shapely.geometry import MultiLineString, MultiPolygon, mapping, shape
14 | from shapely.ops import cascaded_union
15 | from warnings import warn
16 |
17 |
18 | def list_to_affine(xform_mat):
19 | """Create an Affine from a list or array-formatted [a, b, d, e, xoff, yoff]
20 |
21 | Arguments
22 | ---------
23 | xform_mat : `list` or :class:`numpy.array`
24 | A `list` of values to convert to an affine object.
25 |
26 | Returns
27 | -------
28 | aff : :class:`affine.Affine`
29 | An affine transformation object.
30 | """
31 | # first make sure it's not in gdal order
32 | if len(xform_mat) > 6:
33 | xform_mat = xform_mat[0:6]
34 | if rasterio.transform.tastes_like_gdal(xform_mat):
35 | return Affine.from_gdal(*xform_mat)
36 | else:
37 | return Affine(*xform_mat)
38 |
39 |
40 | def geometries_internal_intersection(polygons):
41 | """Get the intersection geometries between all geometries in a set.
42 |
43 | Arguments
44 | ---------
45 | polygons : `list`-like
46 | A `list`-like containing geometries. These will be placed in a
47 | :class:`geopandas.GeoSeries` object to take advantage of `rtree`
48 | spatial indexing.
49 |
50 | Returns
51 | -------
52 | intersect_list
53 | A `list` of geometric intersections between polygons in `polygons`, in
54 | the same CRS as the input.
55 | """
56 | # convert `polygons` to geoseries and get spatialindex
57 | # TODO: Implement test to see if `polygon` items are actual polygons or
58 | # WKT strings
59 | if isinstance(polygons, gpd.GeoSeries):
60 | gs = polygons
61 | else:
62 | gs = gpd.GeoSeries(polygons).reset_index(drop=True)
63 | sindex = gs.sindex
64 | gs_bboxes = gs.apply(lambda x: x.bounds)
65 |
66 | # find indices of polygons that overlap in gs
67 | intersect_lists = gs_bboxes.apply(lambda x: list(sindex.intersection(x)))
68 | intersect_lists = intersect_lists.dropna()
69 | # drop all objects that only have self-intersects
70 | # first, filter down to the ones that have _some_ intersection with others
71 | intersect_lists = intersect_lists[
72 | intersect_lists.apply(lambda x: len(x) > 1)]
73 | # the below is a royal pain to follow. what it does is create a dataframe
74 | # with two columns: 'gs_idx' and 'intersectors'. 'gs_idx' corresponds to
75 | # a polygon's original index in gs, and 'intersectors' gives a list of
76 | # gs indices for polygons that intersect with its bbox.
77 | intersect_lists.name = 'intersectors'
78 | intersect_lists.index.name = 'gs_idx'
79 | intersect_lists = intersect_lists.reset_index()
80 | # first, we get rid of self-intersection indices in 'intersectors':
81 | intersect_lists['intersectors'] = intersect_lists.apply(
82 | lambda x: [i for i in x['intersectors'] if i != x['gs_idx']],
83 | axis=1)
84 | # for each row, we next create a union of the polygons in 'intersectors',
85 | # and find the intersection of that with the polygon at gs[gs_idx]. this
86 | # (Multi)Polygon output corresponds to all of the intersections for the
87 | # polygon at gs[gs_idx]. we add that to a list of intersections stored in
88 | # output_polys.
89 | output_polys = []
90 | _ = intersect_lists.apply(lambda x: output_polys.append(
91 | gs[x['gs_idx']].intersection(cascaded_union(gs[x['intersectors']]))
92 | ), axis=1)
93 | # we then generate the union of all of these intersections and return it.
94 | return cascaded_union(output_polys)
95 |
96 |
97 | def split_multi_geometries(gdf, obj_id_col=None, group_col=None,
98 | geom_col='geometry'):
99 | """Split apart MultiPolygon or MultiLineString geometries.
100 |
101 | Arguments
102 | ---------
103 | gdf : :class:`geopandas.GeoDataFrame` or `str`
104 | A :class:`geopandas.GeoDataFrame` or path to a geojson containing
105 | geometries.
106 | obj_id_col : str, optional
107 | If one exists, the name of the column that uniquely identifies each
108 | geometry (e.g. the ``"BuildingId"`` column in many SpaceNet datasets).
109 | This will be tracked so multiple objects don't get produced with
110 | the same ID. Note that object ID column will be renumbered on output.
111 | If passed, `group_col` must also be provided.
112 | group_col : str, optional
113 | A column to identify groups for sequential numbering (for example,
114 | ``'ImageId'`` for sequential number of ``'BuildingId'``). Must be
115 | provided if `obj_id_col` is passed.
116 | geom_col : str, optional
117 | The name of the column in `gdf` that corresponds to geometry. Defaults
118 | to ``'geometry'``.
119 |
120 | Returns
121 | -------
122 | :class:`geopandas.GeoDataFrame`
123 | A `geopandas.GeoDataFrame` that's identical to the input, except with
124 | the multipolygons split into separate rows, and the object ID column
125 | renumbered (if one exists).
126 |
127 | """
128 | if obj_id_col and not group_col:
129 | raise ValueError('group_col must be provided if obj_id_col is used.')
130 | gdf2 = _check_gdf_load(gdf)
131 | # drop duplicate columns (happens if loading a csv with geopandas)
132 | gdf2 = gdf2.loc[:, ~gdf2.columns.duplicated()]
133 | # check if the values in gdf2[geometry] are polygons; if strings, do loads
134 | if isinstance(gdf2[geom_col][0], str):
135 | gdf2[geom_col] = gdf2[geom_col].apply(loads)
136 | split_geoms_gdf = pd.concat(
137 | gdf2.apply(_split_multigeom_row, axis=1, geom_col=geom_col).tolist())
138 | gdf2.drop(index=split_geoms_gdf.index.unique()) # remove multipolygons
139 | gdf2 = gpd.GeoDataFrame(pd.concat([gdf2, split_geoms_gdf],
140 | ignore_index=True), crs=gdf2.crs)
141 |
142 | if obj_id_col:
143 | gdf2[obj_id_col] = gdf2.groupby(group_col).cumcount()+1
144 |
145 | return gdf2
146 |
147 |
148 | def get_subgraph(G, node_subset):
149 | """
150 | Create a subgraph from G. Code almost directly copied from osmnx.
151 |
152 | Arguments
153 | ---------
154 | G : :class:`networkx.MultiDiGraph`
155 | A graph to be subsetted
156 | node_subset : `list`-like
157 | The subset of nodes to induce a subgraph of `G`
158 |
159 | Returns
160 | -------
161 | G2 : :class:`networkx`.MultiDiGraph
162 | The subgraph of G that includes node_subset
163 | """
164 |
165 | node_subset = set(node_subset)
166 |
167 | # copy nodes into new graph
168 | G2 = G.fresh_copy()
169 | G2.add_nodes_from((n, G.nodes[n]) for n in node_subset)
170 |
171 | # copy edges to new graph, including parallel edges
172 | if G2.is_multigraph:
173 | G2.add_edges_from(
174 | (n, nbr, key, d)
175 | for n, nbrs in G.adj.items() if n in node_subset
176 | for nbr, keydict in nbrs.items() if nbr in node_subset
177 | for key, d in keydict.items())
178 | else:
179 | G2.add_edges_from(
180 | (n, nbr, d)
181 | for n, nbrs in G.adj.items() if n in node_subset
182 | for nbr, d in nbrs.items() if nbr in node_subset)
183 |
184 | # update graph attribute dict, and return graph
185 | G2.graph.update(G.graph)
186 | return G2
187 |
188 |
189 | def _split_multigeom_row(gdf_row, geom_col):
190 | new_rows = []
191 | if isinstance(gdf_row[geom_col], MultiPolygon) \
192 | or isinstance(gdf_row[geom_col], MultiLineString):
193 | new_polys = _split_multigeom(gdf_row[geom_col])
194 | for poly in new_polys:
195 | row_w_poly = gdf_row.copy()
196 | row_w_poly[geom_col] = poly
197 | new_rows.append(row_w_poly)
198 | return pd.DataFrame(new_rows)
199 |
200 |
201 | def _split_multigeom(multigeom):
202 | return list(multigeom)
203 |
204 |
205 | def _reduce_geom_precision(geom, precision=2):
206 | geojson = mapping(geom)
207 | geojson['coordinates'] = np.round(np.array(geojson['coordinates']),
208 | precision)
209 |
210 | return shape(geojson)
211 |
212 |
213 | def _check_wkt_load(x):
214 | """Check if an object is a loaded polygon or not. If not, load it."""
215 | if isinstance(x, str):
216 | try:
217 | x = loads(x)
218 | except WKTReadingError:
219 | warn('{} is not a WKT-formatted string.'.format(x))
220 |
221 | return x
222 |
223 |
224 |
225 | # PRETEND THIS ISN'T HERE AT THE MOMENT
226 | # class CoordTransformer(object):
227 | # """A transformer class to change coordinate space using affine transforms.
228 | #
229 | # Notes
230 | # -----
231 | # This class will take in an image or geometric object (Shapely or GDAL)
232 | # and transform its coordinate space based on `dest_obj` . `dest_obj`
233 | # should be an instance of :class:`rasterio.DatasetReader` .
234 | #
235 | # Arguments
236 | # ---------
237 | # src_obj
238 | # A source image or geometric object to transform. The function will
239 | # first try to extract georegistration information from this object
240 | # if it exists; if it doesn't, it will assume unit (pixel) coords.
241 | # dest_obj
242 | # Object with a destination coordinate reference system to apply to
243 | # `src_obj` . This can be in the form of an ``[a, b, d, e, xoff, yoff]``
244 | # `list` , an :class:`affine.Affine` instance, or a source
245 | # :class:`geopandas.GeoDataFrame` or geotiff with `crs` metadata to
246 | # produce the transform from, or even just a crs string.
247 | # src_crs : optional
248 | # Source coordinate reference in the form of a :class:`rasterio.crs.CRS`
249 | # object or an epsg string. Only needed if the source object provided
250 | # does not have CRS metadata attached to it.
251 | # src_transform : :class:`affine.Affine` or :class:`list`
252 | # The source affine transformation matrix as a :class:`affine.Affine`
253 | # object or in an ``[a, b, c, d, xoff, yoff]`` `list`. Required if
254 | # `src_obj` is a :class:`numpy.array` .
255 | # dest_transform : :class:`affine.Affine` or :class:`list`
256 | # The destination affine transformation matrix as a
257 | # :class:`affine.Affine` object or in an ``[a, b, c, d, xoff, yoff]``
258 | # `list` . Required if `dest_obj` is a :class:`numpy.array` .
259 | # """
260 | # def __init__(self, src_obj=None, dest_obj=None, src_crs=None,
261 | # src_transform=None, dest_transform=None):
262 | # self.src_obj = src_obj
263 | # self.src_type = None
264 | # self.dest_obj = dest_obj
265 | # self.dest_type = None
266 | # self.get_obj_types() # replaces the None values above
267 | # self.src_crs = src_crs
268 | # if isinstance(self.src_crs, dict):
269 | # self.src_crs = self.src_crs['init']
270 | # if not self.src_crs:
271 | # self.src_crs = self._get_crs(self.src_obj, self.src_type)
272 | # self.dest_crs = self._get_crs(self.dest_obj, self.dest_type)
273 | # self.src_transform = src_transform
274 | # self.dest_transform = dest_transform
275 | #
276 | # def __repr__(self):
277 | # print('CoordTransformer for {}'.format(self.src_obj))
278 | #
279 | # def load_src_obj(self, src_obj, src_crs=None):
280 | # """Load in a new source object for transformation."""
281 | # self.src_obj = src_obj
282 | # self.src_type = None # replaced in self._get_src_crs()
283 | # self.src_type = self._get_type(self.src_obj)
284 | # self.src_crs = src_crs
285 | # if self.src_crs is None:
286 | # self.src_crs = self._get_crs(self.src_obj, self.src_type)
287 | #
288 | # def load_dest_obj(self, dest_obj):
289 | # """Load in a new destination object for transformation."""
290 | # self.dest_obj = dest_obj
291 | # self.dest_type = None
292 | # self.dest_type = self._get_type(self.dest_obj)
293 | # self.dest_crs = self._get_crs(self.dest_obj, self.dest_type)
294 | #
295 | # def load_src_crs(self, src_crs):
296 | # """Load in a new source coordinate reference system."""
297 | # self.src_crs = self._get_crs(src_crs)
298 | #
299 | # def get_obj_types(self):
300 | # if self.src_obj is not None:
301 | # self.src_type = self._get_type(self.src_obj)
302 | # if self.src_type is None:
303 | # warn('The src_obj type is not compatible with this package.')
304 | # if self.dest_obj is not None:
305 | # self.dest_type = self._get_type(self.dest_obj)
306 | # if self.dest_type is None:
307 | # warn('The dest_obj type is not compatible with this package.')
308 | # elif self.dest_type == 'shapely Geometry':
309 | # warn('Shapely geometries cannot provide a destination CRS.')
310 | #
311 | # @staticmethod
312 | # def _get_crs(obj, obj_type):
313 | # """Get the destination coordinate reference system."""
314 | # # get the affine transformation out of dest_obj
315 | # if obj_type == "transform matrix":
316 | # return Affine(obj)
317 | # elif obj_type == 'Affine':
318 | # return obj
319 | # elif obj_type == 'GeoTIFF':
320 | # return rasterio.open(obj).crs
321 | # elif obj_type == 'GeoDataFrame':
322 | # if isinstance(obj, str): # if it's a path to a gdf
323 | # return gpd.read_file(obj).crs
324 | # else: # assume it's a GeoDataFrame object
325 | # return obj.crs
326 | # elif obj_type == 'epsg string':
327 | # if obj.startswith('{init'):
328 | # return rasterio.crs.CRS.from_string(
329 | # obj.lstrip('{init: ').rstrip('}'))
330 | # elif obj.lower().startswith('epsg'):
331 | # return rasterio.crs.CRS.from_string(obj)
332 | # elif obj_type == 'OGR Geometry':
333 | # return get_crs_from_ogr(obj)
334 | # elif obj_type == 'shapely Geometry':
335 | # raise TypeError('Cannot extract a coordinate system from a ' +
336 | # 'shapely.Geometry')
337 | # else:
338 | # raise TypeError('Cannot extract CRS from this object type.')
339 | #
340 | # @staticmethod
341 | # def _get_type(obj):
342 | # if isinstance(obj, gpd.GeoDataFrame):
343 | # return 'GeoDataFrame'
344 | # elif isinstance(obj, str):
345 | # if os.path.isfile(obj):
346 | # if os.path.splitext(obj)[1].lower() in ['tif', 'tiff',
347 | # 'geotiff']:
348 | # return 'GeoTIFF'
349 | # elif os.path.splitext(obj)[1] in ['csv', 'geojson']:
350 | # # assume it can be loaded as a geodataframe
351 | # return 'GeoDataFrame'
352 | # else: # assume it's a crs string
353 | # if obj.startswith('{init'):
354 | # return "epsg string"
355 | # elif obj.lower().startswith('epsg'):
356 | # return "epsg string"
357 | # else:
358 | # raise ValueError('{} is not an accepted crs type.'.format(
359 | # obj))
360 | # elif isinstance(obj, ogr.Geometry):
361 | # # ugh. Try to get the EPSG code out.
362 | # return 'OGR Geometry'
363 | # elif isinstance(obj, shapely.Geometry):
364 | # return "shapely Geometry"
365 | # elif isinstance(obj, list):
366 | # return "transform matrix"
367 | # elif isinstance(obj, Affine):
368 | # return "Affine transform"
369 | # elif isinstance(obj, np.array):
370 | # return "numpy array"
371 | # else:
372 | # return None
373 | #
374 | # def transform(self, output_loc):
375 | # """Transform `src_obj` from `src_crs` to `dest_crs`.
376 | #
377 | # Arguments
378 | # ---------
379 | # output_loc : `str` or `var`
380 | # Object or location to output transformed src_obj to. If it's a
381 | # string, it's assumed to be a path.
382 | # """
383 | # if not self.src_crs or not self.dest_crs:
384 | # raise AttributeError('The source or destination CRS is missing.')
385 | # if not self.src_obj:
386 | # raise AttributeError('The source object to transform is missing.')
387 | # if isinstance(output_loc, str):
388 | # out_file = True
389 | # if self.src_type == 'GeoTIFF':
390 | # return rasterio.warp.reproject(rasterio.open(self.src_obj),
391 | # output_loc,
392 | # src_transform=self.src_transform,
393 | # src_crs=self.src_crs,
394 | # dst_trasnform=self.dest_transform,
395 | # dst_crs=self.dest_crs,
396 | # resampling=Resampling.bilinear)
397 | # elif self.src_type == 'GeoDataFrame':
398 | # if isinstance(self.src_obj, str):
399 | # # load the gdf and transform it
400 | # tmp_src = gpd.read_file(self.src_obj).to_crs(self.dest_crs)
401 | # else:
402 | # # just transform it
403 | # tmp_src = self.src_obj.to_crs(self.dest_crs)
404 | # if out_file:
405 | # # save to file
406 | # if output_loc.lower().endswith('json'):
407 | # tmp_src.to_file(output_loc, driver="GeoJSON")
408 | # else:
409 | # tmp_src.to_file(output_loc) # ESRI shapefile
410 | # return
411 | # else:
412 | # # assign to the variable and return
413 | # output_loc = tmp_src
414 | # return output_loc
415 | # elif self.src_type == 'OGR Geometry':
416 | # dest_sr = ogr.SpatialReference().ImportFromEPSG(
417 | # int(self.dest_crs.lstrip('epsg')))
418 | # output_loc = self.src_obj.TransformTo(dest_sr)
419 | # return output_loc
420 | # elif self.src_type == 'shapely Geometry':
421 | # if self.dest_type not in [
422 | # 'Affine transform', 'transform matrix'
423 | # ] and not self.dest_transform:
424 | # raise ValueError('Transforming shapely objects requires ' +
425 | # 'an affine transformation matrix.')
426 | # elif self.dest_type == 'Affine transform':
427 | # output_loc = shapely.affinity.affine_transform(
428 | # self.src_obj, [self.dest_obj.a, self.dest_obj.b,
429 | # self.dest_obj.d, self.dest_obj.e,
430 | # self.dest_obj.xoff, self.dest_obj.yoff]
431 | # )
432 | # return output_loc
433 | # elif self.dest_type == 'transform matrix':
434 | # output_loc = shapely.affinity.affine_transform(self.src_obj,
435 | # self.dest_obj)
436 | # return output_loc
437 | # else:
438 | # if isinstance(self.dest_transform, Affine):
439 | # xform_mat = [self.dest_transform.a, self.dest_transform.b,
440 | # self.dest_transform.d, self.dest_transform.e,
441 | # self.dest_transform.xoff,
442 | # self.dest_transform.yoff]
443 | # else:
444 | # xform_mat = self.dest_transform
445 | # output_loc = shapely.affinity.affine_transform(self.src_obj,
446 | # xform_mat)
447 | # return output_loc
448 | # elif self.src_type == 'numpy array':
449 | # return rasterio.warp.reproject(
450 | # self.src_obj, output_loc, src_transform=self.src_transform,
451 | # src_crs=self.src_crs, dst_transform=self.dest_transform,
452 | # dst_crs=self.dest_crs)
453 | #
454 | #
455 | # def get_crs_from_ogr(annoying_OGR_geometry):
456 | # """Get a CRS from an :class:`osgeo.ogr.Geometry` object.
457 | #
458 | # Arguments
459 | # ---------
460 | # annoying_OGR_geometry: :class:`osgeo.ogr.Geometry`
461 | # An OGR object which stores crs information in an annoying fashion.
462 | #
463 | # Returns
464 | # -------
465 | # An extremely clear, easy to work with ``'epsg[number]'`` string.
466 | # """
467 | # srs = annoying_OGR_geometry.GetSpatialReference()
468 | # result_of_ID = srs.AutoIdentifyEPSG() # if success, returns 0
469 | # if result_of_ID == 0:
470 | # return 'epsg:' + str(srs.GetAuthorityCode(None))
471 | # else:
472 | # raise ValueError('Could not determine EPSG code.')
473 |
--------------------------------------------------------------------------------
/cw_geodata/vector_label/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CosmiQ/cw-geodata/19a290c20ec4672dc2e9be37f451d136166bb28a/cw_geodata/vector_label/__init__.py
--------------------------------------------------------------------------------
/cw_geodata/vector_label/graph.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function, division, absolute_import
2 | import numpy as np
3 | import geopandas as gpd
4 | from ..utils.geo import get_subgraph
5 | import shapely
6 | from shapely.geometry import Point
7 | import networkx as nx
8 | import geopandas as gpd
9 | import fiona
10 | from multiprocessing import Pool
11 |
12 |
13 | class Node(object):
14 | """An object to hold node attributes.
15 |
16 | Attributes
17 | ----------
18 | idx : int
19 | The numerical index of the node. Used as a unique identifier
20 | when the nodes are added to the graph.
21 | x : `int` or `float`
22 | Numeric x location of the node, in either a geographic CRS or in pixel
23 | coordinates.
24 | y : `int` or `float`
25 | Numeric y location of the node, in either a geographic CRS or in pixel
26 | coordinates.
27 |
28 | """
29 |
30 | def __init__(self, idx, x, y):
31 | self.idx = idx
32 | self.x = x
33 | self.y = y
34 |
35 | def __repr__(self):
36 | return 'Node {} at ({}, {})'.format(self.idx, self.x, self.y)
37 |
38 |
39 | class Edge(object):
40 | """An object to hold edge attributes.
41 |
42 | Attributes
43 | ----------
44 | nodes : 2-`tuple` of :class:`Node` s
45 | :class:`Node` instances connected by the edge.
46 | weight : int or float
47 | The weight of the edge.
48 |
49 | """
50 |
51 | def __init__(self, nodes, edge_weight=None):
52 | self.nodes = nodes
53 | self.weight = edge_weight
54 |
55 | def __repr__(self):
56 | return 'Edge between {} and {} with weight {}'.format(self.nodes[0],
57 | self.nodes[1],
58 | self.weight)
59 |
60 | def set_edge_weight(self, normalize_factor=None, inverse=False):
61 | """Get the edge weight based on Euclidean distance between nodes.
62 |
63 | Note
64 | ----
65 | This method does not account for spherical deformation (i.e. does not
66 | use the Haversine equation). It is a simple linear distance.
67 |
68 | Arguments
69 | ---------
70 | normalize_factor : `int` or `float`, optional
71 | a number to multiply (or divide, if
72 | ``inverse=True``) the Euclidean distance by. Defaults to ``None``
73 | (no normalization)
74 | inverse : bool, optional
75 | if ``True``, the Euclidean distance weight will be divided by
76 | ``normalize_factor`` instead of multiplied by it.
77 | """
78 | weight = np.linalg.norm(
79 | np.array((self.nodes[0].x, self.nodes[0].y)) -
80 | np.array((self.nodes[1].x, self.nodes[1].y)))
81 |
82 | if normalize_factor is not None:
83 | if inverse:
84 | weight = weight/normalize_factor
85 | else:
86 | weight = weight*normalize_factor
87 | self.weight = weight
88 |
89 | def get_node_idxs(self):
90 | """Return the Node.idx for the nodes in the edge."""
91 | return (self.nodes[0].idx, self.nodes[1].idx)
92 |
93 |
94 | class Path(object):
95 | """An object to hold :class:`Edge` s with common properties.
96 |
97 | Attributes
98 | ----------
99 | edges : `list` of :class:`Edge` s
100 | A `list` of :class:`Edge` s
101 | properties : dict
102 | A dictionary of property: value pairs that provide relevant metadata
103 | about edges along the path (e.g. road type, speed limit, etc.)
104 |
105 | """
106 |
107 | def __init__(self, edges=None, properties=None):
108 | self.edges = edges
109 | if properties is None:
110 | properties = {}
111 | self.properties = properties
112 |
113 | def __repr__(self):
114 | return 'Path including {}'.format([e for e in self.edges])
115 |
116 | def add_edge(self, edge):
117 | """Add an edge to the path."""
118 | self.edges.append(edge)
119 |
120 | def set_edge_weights(self, data_key=None, inverse=False, overwrite=True):
121 | """Calculate edge weights for all edges in the Path."""
122 | for edge in self.edges:
123 | if not overwrite and edge.weight is not None:
124 | continue
125 | if data_key is not None:
126 | edge.set_edge_weight(
127 | normalize_factor=self.properties[data_key],
128 | inverse=inverse)
129 | else:
130 | edge.set_edge_weight()
131 |
132 | def add_data(self, property, value):
133 | """Add a property: value pair to the Path.properties attribute."""
134 | self.properties[property] = value
135 |
136 | def __iter__(self):
137 | """Iterate through edges in the path."""
138 | yield from self.edges
139 |
140 |
141 | def geojson_to_graph(geojson, graph_name=None, retain_all=True,
142 | valid_road_types=None, road_type_field='type', edge_idx=0,
143 | first_node_idx=0, weight_norm_field=None, inverse=False,
144 | workers=1, verbose=False):
145 | """Convert a geojson of path strings to a network graph.
146 |
147 | Arguments
148 | ---------
149 | geojson : str
150 | Path to a geojson file (or any other OGR-compatible vector file) to
151 | load network edges and nodes from.
152 | graph_name : str, optional
153 | Name of the graph. If not provided, graph will be named ``'unnamed'`` .
154 | retain_all : bool, optional
155 | If ``True`` , the entire graph will be returned even if some parts are
156 | not connected. Defaults to ``True``.
157 | valid_road_types : :class:`list` of :class:`int` s, optional
158 | The road types to permit in the graph. If not provided, it's assumed
159 | that all road types are permitted. The possible values are integers
160 | ``1``-``7``, which map as follows::
161 |
162 | 1: Motorway
163 | 2: Primary
164 | 3: Secondary
165 | 4: Tertiary
166 | 5: Residential
167 | 6: Unclassified
168 | 7: Cart track
169 |
170 | road_type_field : str, optional
171 | The name of the property in the vector data that delineates road type.
172 | Defaults to ``'type'`` .
173 | edge_idx : int, optional
174 | The first index to use for an edge. This can be set to a higher value
175 | so that a graph's edge indices don't overlap with existing values in
176 | another graph.
177 | first_node_idx : int, optional
178 | The first index to use for a node. This can be set to a higher value
179 | so that a graph's node indices don't overlap with existing values in
180 | another graph.
181 | weight_norm_field : str, optional
182 | The name of a field in `geojson` to pass to argument ``data_key`` in
183 | :func:`Path.set_edge_weights`. Defaults to ``None``, in which case
184 | no weighting is performed (weights calculated solely using Euclidean
185 | distance.)
186 | workers : int, optional
187 | Number of parallel processes to run for parallelization. Defaults to 1.
188 | Should not be greater than the number of CPUs available.
189 | verbose : bool, optional
190 | Verbose print output. Defaults to ``False`` .
191 |
192 | Returns
193 | -------
194 | G : :class:`networkx.MultiDiGraph`
195 | A :class:`networkx.MultiDiGraph` containing all of the nodes and edges
196 | from the geojson (or only the largest connected component if
197 | `retain_all` = ``False``). Edge lengths are weighted based on
198 | geographic distance.
199 |
200 | """
201 | # due to an annoying feature of loading these graphs, the numeric road
202 | # type identifiers are presented as string versions. we therefore reformat
203 | # the valid_road_types list as strings.
204 | if valid_road_types is not None:
205 | valid_road_types = [str(i) for i in valid_road_types]
206 |
207 | # create the graph as a MultiGraph and set the original CRS to EPSG 4326
208 |
209 | # extract nodes and paths
210 | nodes, paths = get_nodes_paths(geojson,
211 | valid_road_types=valid_road_types,
212 | first_node_idx=first_node_idx,
213 | road_type_field=road_type_field,
214 | workers=workers, verbose=verbose)
215 | # nodes is a dict of node_idx: node_params (e.g. location, metadata)
216 | # pairs.
217 | # paths is a dict of path dicts. the path key is the path_idx.
218 | # each path dict has a list of node_idxs as well as properties metadata.
219 |
220 | # initialize the graph object
221 | G = nx.MultiDiGraph(name=graph_name, crs={'init': 'epsg:4326'})
222 | if not nodes: # if there are no nodes in the graph
223 | return G
224 | if verbose:
225 | print("nodes:", nodes)
226 | print("paths:", paths)
227 | # add each osm node to the graph
228 | for node in nodes:
229 | G.add_node(node, **{'x': node.x, 'y': node.y})
230 | # add each path to the graph
231 | for path in paths:
232 | # calculate edge length using euclidean distance and a weighting term
233 | path.set_edge_weights(data_key=weight_norm_field, inverse=inverse)
234 | edges = [(*edge.nodes, edge.weight) for edge in path]
235 | if verbose:
236 | print(edges)
237 | G.add_weighted_edges_from(edges)
238 | if not retain_all:
239 | # keep only largest connected component of graph unless retain_all
240 | # code modified from osmnx.core.get_largest_component & induce_subgraph
241 | largest_cc = max(nx.weakly_connected_components(G), key=len)
242 | G = get_subgraph(G, largest_cc)
243 |
244 | return G
245 |
246 |
247 | def get_nodes_paths(vector_file, first_node_idx=0, node_gdf=gpd.GeoDataFrame(),
248 | valid_road_types=None, road_type_field='type', workers=1,
249 | verbose=False):
250 | """
251 | Extract nodes and paths from a vector file.
252 |
253 | Arguments
254 | ---------
255 | vector_file : str
256 | Path to an OGR-compatible vector file containing line segments (e.g.,
257 | JSON response from from the Overpass API, or a SpaceNet GeoJSON).
258 | first_path_idx : int, optional
259 | The first index to use for a path. This can be set to a higher value
260 | so that a graph's path indices don't overlap with existing values in
261 | another graph.
262 | first_node_idx : int, optional
263 | The first index to use for a node. This can be set to a higher value
264 | so that a graph's node indices don't overlap with existing values in
265 | another graph.
266 | node_gdf : :class:`geopandas.GeoDataFrame` , optional
267 | A :class:`geopandas.GeoDataFrame` containing nodes to add to the graph.
268 | New nodes will be added to this object incrementally during the
269 | function call.
270 | valid_road_types : :class:`list` of :class:`int` s, optional
271 | The road types to permit in the graph. If not provided, it's assumed
272 | that all road types are permitted. The possible values are integers
273 | ``1``-``7``, which map as follows::
274 |
275 | 1: Motorway
276 | 2: Primary
277 | 3: Secondary
278 | 4: Tertiary
279 | 5: Residential
280 | 6: Unclassified
281 | 7: Cart track
282 |
283 | road_type_field : str, optional
284 | The name of the attribute containing road type information in
285 | `vector_file`. Defaults to ``'type'``.
286 | workers : int, optional
287 | Number of worker processes to use for parallelization. Defaults to 1.
288 | Should not exceed the number of CPUs available.
289 | verbose : bool, optional
290 | Verbose print output. Defaults to ``False``.
291 |
292 | Returns
293 | -------
294 | nodes, paths : `tuple` of `dict` s
295 | nodes : list
296 | A `list` of :class:`Node` s to be added to the graph.
297 | paths : list
298 | A list of :class:`Path` s containing the :class:`Edge` s and
299 | :class:`Node` s to be added to the graph.
300 |
301 | """
302 | if valid_road_types is None:
303 | valid_road_types = ['1', '2', '3', '4', '5', '6', '7']
304 |
305 | with fiona.open(vector_file, 'r') as source:
306 |
307 | with Pool(processes=workers) as pool:
308 | node_list = pool.map(_get_all_nodes, source,
309 | chunksize=10)
310 | pool.close()
311 | source.close()
312 |
313 | # convert to geoseries and drop duplicates (have to flatten first)
314 | node_series = gpd.GeoSeries([i for sublist in node_list for i in sublist])
315 | # NOTE: It is ESSENTIAL to use keep='last' in the line below; otherwise, it
316 | # misses a duplicate if it includes the first element of the series.
317 | node_series = node_series.drop_duplicates(keep='last')
318 | node_series = node_series.reset_index(drop=True)
319 | node_series.name = 'geometry'
320 | node_series.index.name = 'node_idx'
321 | node_gdf = gpd.GeoDataFrame(node_series.reset_index())
322 | node_gdf['node'] = node_gdf.apply(
323 | lambda p: Node(p['node_idx'], p['geometry'].x, p['geometry'].y),
324 | axis=1)
325 |
326 | # create another parallelized operation to iterate through edges
327 | # _init_worker passes the node_series to every process in the pool
328 | with fiona.open(vector_file, 'r') as source:
329 | with Pool(
330 | processes=workers, initializer=_init_worker,
331 | initargs=(node_gdf, valid_road_types, road_type_field)
332 | ) as pool:
333 | zipped_edges_properties = pool.map(parallel_linestring_to_path,
334 | source, chunksize=10)
335 | pool.close()
336 | source.close()
337 |
338 | nodes = node_gdf['node'].tolist()
339 | paths = []
340 | # it would've been better to do this within the multiprocessing pool but
341 | # it's REALLY hard to share objects in memory across processes without
342 | # copies being made (and therefore nodes being duplicated)
343 | for edges, properties in zipped_edges_properties:
344 | path = Path(
345 | edges=[Edge((nodes[edge[0]], nodes[edge[1]])) for edge in edges],
346 | properties=properties
347 | )
348 | paths.append(path)
349 | return nodes, paths
350 |
351 |
352 | def parallel_linestring_to_path(feature):
353 | """Read in a feature line from a fiona-opened shapefile and get the edges.
354 |
355 | Arguments
356 | ---------
357 | feature : dict
358 | An item from a :class:`fiona.open` iterable with the key ``'geometry'``
359 | containing :class:`shapely.geometry.line.LineString` s or
360 | :class:`shapely.geometry.line.MultiLineString` s.
361 |
362 | Returns
363 | -------
364 | A list of :class:`Path` s containing all edges in the LineString or
365 | MultiLineString.
366 |
367 | Notes
368 | -----
369 | This function depends on ``node_series`` and ``valid_road_types``, which
370 | are passed by an initializer as items in ``var_dict``.
371 |
372 | """
373 |
374 | properties = feature['properties']
375 | # TODO: create more adjustable filter
376 | if var_dict['road_type_field'] in properties:
377 | road_type = properties[var_dict['road_type_field']]
378 | elif 'highway' in properties:
379 | road_type = properties['highway']
380 | elif 'road_type' in properties:
381 | road_type = properties['road_type']
382 | else:
383 | road_type = 'None'
384 |
385 | geom = feature['geometry']
386 | if geom['type'] == 'LineString' or \
387 | geom['type'] == 'MultiLineString':
388 | if road_type not in var_dict['valid_road_types'] or \
389 | 'LINESTRING EMPTY' in properties.values():
390 | return
391 |
392 | if geom['type'] == 'LineString':
393 | linestring = shapely.geometry.shape(geom)
394 | edges = linestring_to_edges(linestring, var_dict['node_gdf'])
395 |
396 | elif geom['type'] == 'MultiLineString':
397 | # do the same thing as above, but do it for each piece
398 | edges = []
399 | for linestring in shapely.geometry.shape(geom):
400 | edge_set, node_idx, node_gdf = linestring_to_edges(
401 | linestring, var_dict['node_gdf'])
402 | edges.extend(edge_set)
403 |
404 | return edges, properties
405 |
406 |
407 | def linestring_to_edges(linestring, node_gdf):
408 | """Collect nodes in a linestring and add them to an edge.
409 |
410 | Arguments
411 | ---------
412 | linestring : :class:`shapely.geometry.LineString`
413 | A :class:`shapely.geometry.LineString` object to extract nodes and
414 | edges from.
415 | node_series : :class:`geopandas.GeoSeries`
416 | A :class:`geopandas.GeoSeries` containing a
417 | :class:`shapely.geometry.point.Point` for every node to be added to the
418 | graph.
419 |
420 | Returns
421 | -------
422 | edges : list
423 | A list of :class:`Edge` s from ``linestring``.
424 |
425 | """
426 | edges = []
427 | nodes = []
428 |
429 | for point in linestring.coords:
430 | point_shp = shapely.geometry.shape(Point(point))
431 | nodes.append(
432 | node_gdf.node_idx[node_gdf.distance(point_shp) == 0.0].values[0]
433 | )
434 | if len(nodes) > 1:
435 | edges.append(nodes[-2:])
436 |
437 | return edges
438 |
439 |
440 | def _get_all_nodes(feature):
441 | """Create a list of node geometries from a geojson of (multi)linestrings.
442 |
443 | Note
444 | ----
445 | This function is intended to be used with pool.imap_unordered for
446 | parallelization.
447 |
448 | Returns
449 | -------
450 | A list of :class:`shapely.geometry.Point` instances. DUPLICATES CAN EXIST.
451 | """
452 | points = []
453 | geom = feature['geometry']
454 | if geom['type'] == 'LineString':
455 | linestring = shapely.geometry.shape(geom)
456 | points.extend(_get_linestring_points(linestring))
457 | elif geom['type'] == 'MultiLineString':
458 | for linestring in shapely.geometry.shape(geom):
459 | points.extend(_get_linestring_points(linestring))
460 |
461 | return points
462 |
463 |
464 | def _get_linestring_points(linestring):
465 | points = []
466 | for point in linestring.coords:
467 | points.append(shapely.geometry.shape(Point(point)))
468 | return points
469 |
470 |
471 | def _init_worker(node_gdf, valid_road_types, road_type_field):
472 | the_dict = {'node_gdf': node_gdf,
473 | 'valid_road_types': valid_road_types,
474 | 'road_type_field': road_type_field}
475 | global var_dict
476 | var_dict = the_dict
477 |
--------------------------------------------------------------------------------
/cw_geodata/vector_label/mask.py:
--------------------------------------------------------------------------------
1 | from ..utils.core import _check_df_load, _check_rasterio_im_load
2 | from ..utils.geo import geometries_internal_intersection, _check_wkt_load
3 | import numpy as np
4 | import pandas as pd
5 | import rasterio
6 | from rasterio import features
7 | from affine import Affine
8 | from skimage.morphology import square, erosion, dilation
9 |
10 |
11 | def df_to_px_mask(df, channels=['footprint'], out_file=None, reference_im=None,
12 | geom_col='geometry', affine_obj=None, shape=(900, 900),
13 | out_type='int', burn_value=255, **kwargs):
14 | """Convert a dataframe of geometries to a pixel mask.
15 |
16 | Arguments
17 | ---------
18 | df : :class:`pandas.DataFrame` or :class:`geopandas.GeoDataFrame`
19 | A :class:`pandas.DataFrame` or :class:`geopandas.GeoDataFrame` instance
20 | with a column containing geometries (identified by `geom_col`). If the
21 | geometries in `df` are not in pixel coordinates, then `affine` or
22 | `reference_im` must be passed to provide the transformation to convert.
23 | channels : list, optional
24 | The mask channels to generate. There are three values that this can
25 | contain:
26 |
27 | - ``"footprint"``: Create a full footprint mask, with 0s at pixels
28 | that don't fall within geometries and `burn_value` at pixels that
29 | do.
30 | - ``"boundary"``: Create a mask with geometries outlined. Use
31 | `boundary_width` to set how thick the boundary will be drawn.
32 | - ``"contact"``: Create a mask with regions between >= 2 closely
33 | juxtaposed geometries labeled. Use `contact_spacing` to set the
34 | maximum spacing between polygons to be labeled.
35 |
36 | Each channel correspond to its own `shape` plane in the output.
37 | out_file : str, optional
38 | Path to an image file to save the output to. Must be compatible with
39 | :class:`rasterio.DatasetReader`. If provided, a `reference_im` must be
40 | provided (for metadata purposes).
41 | reference_im : :class:`rasterio.DatasetReader` or `str`, optional
42 | An image to extract necessary coordinate information from: the
43 | affine transformation matrix, the image extent, etc. If provided,
44 | `affine_obj` and `shape` are ignored.
45 | geom_col : str, optional
46 | The column containing geometries in `df`. Defaults to ``"geometry"``.
47 | affine_obj : `list` or :class:`affine.Affine`, optional
48 | Affine transformation to use to convert from geo coordinates to pixel
49 | space. Only provide this argument if `df` is a
50 | :class:`geopandas.GeoDataFrame` with coordinates in a georeferenced
51 | coordinate space. Ignored if `reference_im` is provided.
52 | shape : tuple, optional
53 | An ``(x_size, y_size)`` tuple defining the pixel extent of the output
54 | mask. Ignored if `reference_im` is provided.
55 | burn_value : `int` or `float`
56 | The value to use for labeling objects in the mask. Defaults to 255 (the
57 | max value for ``uint8`` arrays). The mask array will be set to the same
58 | dtype as `burn_value`.
59 | kwargs
60 | Additional arguments to pass to `boundary_mask` or `contact_mask`. See
61 | those functions for requirements.
62 |
63 | Returns
64 | -------
65 | mask : :class:`numpy.array`
66 | A pixel mask with 0s for non-object pixels and `burn_value` at object
67 | pixels. `mask` dtype will coincide with `burn_value`. Shape will be
68 | ``(shape[0], shape[1], len(channels))``, with channels ordered per the
69 | provided `channels` `list`.
70 |
71 | """
72 | if isinstance(channels, str): # e.g. if "contact", not ["contact"]
73 | channels = [channels]
74 |
75 | mask_dict = {}
76 | if 'footprint' in channels:
77 | mask_dict['footprint'] = footprint_mask(
78 | df=df, reference_im=reference_im, geom_col=geom_col,
79 | affine_obj=affine_obj, shape=shape, out_type=out_type,
80 | burn_value=burn_value
81 | )
82 | if 'boundary' in channels:
83 | mask_dict['boundary'] = boundary_mask(
84 | footprint_msk=mask_dict.get('footprint', None),
85 | reference_im=reference_im, geom_col=geom_col,
86 | boundary_width=kwargs.get('boundary_width', 3),
87 | boundary_type=kwargs.get('boundary_type', 'inner'),
88 | burn_value=burn_value, df=df, affine_obj=affine_obj,
89 | shape=shape, out_type=out_type
90 | )
91 | if 'contact' in channels:
92 | mask_dict['contact'] = contact_mask(
93 | df=df, reference_im=reference_im, geom_col=geom_col,
94 | affine_obj=affine_obj, shape=shape, out_type=out_type,
95 | contact_spacing=kwargs.get('contact_spacing', 10),
96 | burn_value=burn_value
97 | )
98 |
99 | output_arr = np.stack([mask_dict[c] for c in channels], axis=-1)
100 |
101 | if reference_im:
102 | reference_im = _check_rasterio_im_load(reference_im)
103 | if out_file:
104 | meta = reference_im.meta.copy()
105 | meta.update(count=output_arr.shape[-1])
106 | meta.update(dtype='uint8')
107 | with rasterio.open(out_file, 'w', **meta) as dst:
108 | # I hate band indexing.
109 | for c in range(1, 1 + output_arr.shape[-1]):
110 | dst.write(output_arr[:, :, c-1], indexes=c)
111 |
112 | return output_arr
113 |
114 |
115 | def footprint_mask(df, out_file=None, reference_im=None, geom_col='geometry',
116 | do_transform=False, affine_obj=None, shape=(900, 900),
117 | out_type='int', burn_value=255, burn_field=None):
118 | """Convert a dataframe of geometries to a pixel mask.
119 |
120 | Arguments
121 | ---------
122 | df : :class:`pandas.DataFrame` or :class:`geopandas.GeoDataFrame`
123 | A :class:`pandas.DataFrame` or :class:`geopandas.GeoDataFrame` instance
124 | with a column containing geometries (identified by `geom_col`). If the
125 | geometries in `df` are not in pixel coordinates, then `affine` or
126 | `reference_im` must be passed to provide the transformation to convert.
127 | out_file : str, optional
128 | Path to an image file to save the output to. Must be compatible with
129 | :class:`rasterio.DatasetReader`. If provided, a `reference_im` must be
130 | provided (for metadata purposes).
131 | reference_im : :class:`rasterio.DatasetReader` or `str`, optional
132 | An image to extract necessary coordinate information from: the
133 | affine transformation matrix, the image extent, etc. If provided,
134 | `affine_obj` and `shape` are ignored.
135 | geom_col : str, optional
136 | The column containing geometries in `df`. Defaults to ``"geometry"``.
137 | do_transform : bool, optional
138 | Should the values in `df` be transformed from geospatial coordinates
139 | to pixel coordinates? Defaults to no (False). If True, either
140 | `reference_im` or `affine_obj` must be provided as a source for the
141 | the required affine transformation matrix.
142 | affine_obj : `list` or :class:`affine.Affine`, optional
143 | Affine transformation to use to convert from geo coordinates to pixel
144 | space. Only provide this argument if `df` is a
145 | :class:`geopandas.GeoDataFrame` with coordinates in a georeferenced
146 | coordinate space. Ignored if `reference_im` is provided or if
147 | ``do_transform=False``.
148 | shape : tuple, optional
149 | An ``(x_size, y_size)`` tuple defining the pixel extent of the output
150 | mask. Ignored if `reference_im` is provided.
151 | out_type : 'float' or 'int'
152 | burn_value : `int` or `float`, optional
153 | The value to use for labeling objects in the mask. Defaults to 255 (the
154 | max value for ``uint8`` arrays). The mask array will be set to the same
155 | dtype as `burn_value`. Ignored if `burn_field` is provided.
156 | burn_field : str, optional
157 | Name of a column in `df` that provides values for `burn_value` for each
158 | independent object. If provided, `burn_value` is ignored.
159 |
160 | Returns
161 | -------
162 | mask : :class:`numpy.array`
163 | A pixel mask with 0s for non-object pixels and `burn_value` at object
164 | pixels. `mask` dtype will coincide with `burn_value`.
165 |
166 | """
167 |
168 | # start with required checks and pre-population of values
169 | if out_file and not reference_im:
170 | raise ValueError(
171 | 'If saving output to file, `reference_im` must be provided.')
172 | df = _check_df_load(df)
173 | df[geom_col] = df[geom_col].apply(_check_wkt_load) # load in geoms if wkt
174 | if not do_transform:
175 | affine_obj = Affine(1, 0, 0, 0, 1, 0) # identity transform
176 |
177 | if reference_im:
178 | reference_im = _check_rasterio_im_load(reference_im)
179 | shape = reference_im.shape
180 | if do_transform:
181 | affine_obj = reference_im.transform
182 |
183 | # extract geometries and pair them with burn values
184 | if burn_field:
185 | if out_type == 'int':
186 | feature_list = list(zip(df[geom_col],
187 | df[burn_field].astype('uint8')))
188 | else:
189 | feature_list = list(zip(df[geom_col],
190 | df[burn_field].astype('uint8')))
191 | else:
192 | feature_list = list(zip(df[geom_col], [burn_value]*len(df)))
193 |
194 | output_arr = features.rasterize(shapes=feature_list, out_shape=shape,
195 | transform=affine_obj)
196 | if out_file:
197 | meta = reference_im.meta.copy()
198 | meta.update(count=1)
199 | if out_type == 'int':
200 | meta.update(dtype='uint8')
201 | with rasterio.open(out_file, 'w', **meta) as dst:
202 | dst.write(output_arr, indexes=1)
203 |
204 | return output_arr
205 |
206 |
207 | def boundary_mask(footprint_msk=None, out_file=None, reference_im=None,
208 | boundary_width=3, boundary_type='inner', burn_value=255,
209 | **kwargs):
210 | """Convert a dataframe of geometries to a pixel mask.
211 |
212 | Notes
213 | -----
214 | This function requires creation of a footprint mask before it can operate;
215 | therefore, if there is no footprint mask already present, it will create
216 | one. In that case, additional arguments for :func:`footprint_mask` (e.g.
217 | ``df``) must be passed.
218 |
219 | Arguments
220 | ---------
221 | footprint_msk : :class:`numpy.array`, optional
222 | A filled in footprint mask created using :func:`footprint_mask`. If not
223 | provided, one will be made by calling :func:`footprint_mask` before
224 | creating the boundary mask, and the required arguments for that
225 | function must be provided as kwargs.
226 | out_file : str, optional
227 | Path to an image file to save the output to. Must be compatible with
228 | :class:`rasterio.DatasetReader`. If provided, a `reference_im` must be
229 | provided (for metadata purposes).
230 | reference_im : :class:`rasterio.DatasetReader` or `str`, optional
231 | An image to extract necessary coordinate information from: the
232 | affine transformation matrix, the image extent, etc. If provided,
233 | `affine_obj` and `shape` are ignored
234 | boundary_width : int, optional
235 | The width of the boundary to be created in pixels. Defaults to 3.
236 | boundary_type : ``"inner"`` or ``"outer"``, optional
237 | Where to draw the boundaries: within the object (``"inner"``) or
238 | outside of it (``"outer"``). Defaults to ``"inner"``.
239 | burn_value : `int`, optional
240 | The value to use for labeling objects in the mask. Defaults to 255 (the
241 | max value for ``uint8`` arrays). The mask array will be set to the same
242 | dtype as `burn_value`. Ignored if `burn_field` is provided.
243 | **kwargs : optional
244 | Additional arguments to pass to :func:`footprint_mask` if one needs to
245 | be created.
246 |
247 | Returns
248 | -------
249 | boundary_mask : :class:`numpy.array`
250 | A pixel mask with 0s for non-object pixels and the same value as the
251 | footprint mask `burn_value` for the boundaries of each object.
252 |
253 | Note: This function draws the boundaries within the edge of the object.
254 |
255 | """
256 | if out_file and not reference_im:
257 | raise ValueError(
258 | 'If saving output to file, `reference_im` must be provided.')
259 | if reference_im:
260 | reference_im = _check_rasterio_im_load(reference_im)
261 | # need to have a footprint mask for this function, so make it if not given
262 | if footprint_msk is None:
263 | footprint_msk = footprint_mask(reference_im=reference_im,
264 | burn_value=burn_value, **kwargs)
265 |
266 | # perform dilation or erosion of `footprint_mask` to get the boundary
267 | strel = square(boundary_width)
268 | if boundary_type == 'outer':
269 | boundary_mask = dilation(footprint_msk, strel)
270 | elif boundary_type == 'inner':
271 | boundary_mask = erosion(footprint_msk, strel)
272 | # use xor operator between border and footprint mask to get _just_ boundary
273 | boundary_mask = boundary_mask ^ footprint_msk
274 | # scale the `True` values to burn_value and return
275 | boundary_mask = boundary_mask > 0 # need to binarize to get burn val right
276 | output_arr = boundary_mask.astype('uint8')*burn_value
277 |
278 | if out_file:
279 | meta = reference_im.meta.copy()
280 | meta.update(count=1)
281 | meta.update(dtype='uint8')
282 | with rasterio.open(out_file, 'w', **meta) as dst:
283 | dst.write(output_arr, indexes=1)
284 |
285 | return output_arr
286 |
287 |
288 | def contact_mask(df, out_file=None, reference_im=None, geom_col='geometry',
289 | affine_obj=None, shape=(900, 900), out_type='int',
290 | contact_spacing=10, burn_value=255):
291 | """Create a pixel mask labeling closely juxtaposed objects.
292 |
293 | Notes
294 | -----
295 | This function identifies pixels in an image that do not correspond to
296 | objects, but fall within `contact_spacing` of >1 labeled object.
297 |
298 | Arguments
299 | ---------
300 | df : :class:`pandas.DataFrame` or :class:`geopandas.GeoDataFrame`
301 | A :class:`pandas.DataFrame` or :class:`geopandas.GeoDataFrame` instance
302 | with a column containing geometries (identified by `geom_col`). If the
303 | geometries in `df` are not in pixel coordinates, then `affine` or
304 | `reference_im` must be passed to provide the transformation to convert.
305 | out_file : str, optional
306 | Path to an image file to save the output to. Must be compatible with
307 | :class:`rasterio.DatasetReader`. If provided, a `reference_im` must be
308 | provided (for metadata purposes).
309 | reference_im : :class:`rasterio.DatasetReader` or `str`, optional
310 | An image to extract necessary coordinate information from: the
311 | affine transformation matrix, the image extent, etc. If provided,
312 | `affine_obj` and `shape` are ignored.
313 | geom_col : str, optional
314 | The column containing geometries in `df`. Defaults to ``"geometry"``.
315 | affine_obj : `list` or :class:`affine.Affine`, optional
316 | Affine transformation to use to convert from geo coordinates to pixel
317 | space. Only provide this argument if `df` is a
318 | :class:`geopandas.GeoDataFrame` with coordinates in a georeferenced
319 | coordinate space. Ignored if `reference_im` is provided.
320 | shape : tuple, optional
321 | An ``(x_size, y_size)`` tuple defining the pixel extent of the output
322 | mask. Ignored if `reference_im` is provided.
323 | out_type : 'float' or 'int'
324 | contact_spacing : `int` or `float`, optional
325 | The desired maximum distance between adjacent polygons to be labeled
326 | as contact. `contact_spacing` will be in the same units as `df` 's
327 | geometries, not necessarily in pixel units.
328 | burn_value : `int` or `float`, optional
329 | The value to use for labeling objects in the mask. Defaults to 255 (the
330 | max value for ``uint8`` arrays). The mask array will be set to the same
331 | dtype as `burn_value`.
332 |
333 | """
334 | if out_file and not reference_im:
335 | raise ValueError(
336 | 'If saving output to file, `reference_im` must be provided.')
337 | df = _check_df_load(df)
338 | df[geom_col] = df[geom_col].apply(_check_wkt_load) # load in geoms if wkt
339 | if reference_im:
340 | reference_im = _check_rasterio_im_load(reference_im)
341 | # grow geometries by half `contact_spacing` to find overlaps
342 | buffered_geoms = df[geom_col].apply(lambda x: x.buffer(contact_spacing/2))
343 | # create a single multipolygon that covers all of the intersections
344 | intersect_poly = geometries_internal_intersection(buffered_geoms)
345 | # create a small df containing the intersections to make a footprint from
346 | df_for_footprint = pd.DataFrame({'shape_name': ['overlap'],
347 | 'geometry': [intersect_poly]})
348 | # use `footprint_mask` to create the overlap mask
349 | contact_msk = footprint_mask(df_for_footprint, reference_im=reference_im,
350 | geom_col='geometry', affine_obj=affine_obj,
351 | shape=shape, out_type=out_type,
352 | burn_value=burn_value)
353 | footprint_msk = footprint_mask(df, reference_im=reference_im,
354 | geom_col=geom_col, affine_obj=affine_obj,
355 | shape=shape, out_type=out_type,
356 | burn_value=burn_value)
357 | contact_msk[footprint_msk > 0] = 0
358 | contact_msk = contact_msk > 0
359 | output_arr = contact_msk.astype('uint8')*burn_value
360 |
361 | if out_file:
362 | meta = reference_im.meta.copy()
363 | meta.update(count=1)
364 | if out_type == 'int':
365 | meta.update(dtype='uint8')
366 | with rasterio.open(out_file, 'w', **meta) as dst:
367 | dst.write(output_arr, indexes=1)
368 |
369 | return output_arr
370 |
--------------------------------------------------------------------------------
/cw_geodata/vector_label/polygon.py:
--------------------------------------------------------------------------------
1 | import os
2 | import shapely
3 | from affine import Affine
4 | import rasterio
5 | from rasterio.warp import transform_bounds
6 | from ..utils.geo import list_to_affine, _reduce_geom_precision
7 | from ..utils.core import _check_gdf_load
8 | from ..raster_image.image import get_geo_transform
9 | from shapely.geometry import box, Polygon
10 | import pandas as pd
11 | import geopandas as gpd
12 | from rtree.core import RTreeError
13 |
14 |
15 | def convert_poly_coords(geom, raster_src=None, affine_obj=None, inverse=False,
16 | precision=None):
17 | """Georegister geometry objects currently in pixel coords or vice versa.
18 |
19 | Arguments
20 | ---------
21 | geom : :class:`shapely.geometry.shape` or str
22 | A :class:`shapely.geometry.shape`, or WKT string-formatted geometry
23 | object currently in pixel coordinates.
24 | raster_src : str, optional
25 | Path to a raster image with georeferencing data to apply to `geom`.
26 | Alternatively, an opened :class:`rasterio.Band` object or
27 | :class:`osgeo.gdal.Dataset` object can be provided. Required if not
28 | using `affine_obj`.
29 | affine_obj: list or :class:`affine.Affine`
30 | An affine transformation to apply to `geom` in the form of an
31 | ``[a, b, d, e, xoff, yoff]`` list or an :class:`affine.Affine` object.
32 | Required if not using `raster_src`.
33 | inverse : bool, optional
34 | If true, will perform the inverse affine transformation, going from
35 | geospatial coordinates to pixel coordinates.
36 | precision : int, optional
37 | Decimal precision for the polygon output. If not provided, rounding
38 | is skipped.
39 |
40 | Returns
41 | -------
42 | out_geom
43 | A geometry in the same format as the input with its coordinate system
44 | transformed to match the destination object.
45 | """
46 |
47 | if not raster_src and not affine_obj:
48 | raise ValueError("Either raster_src or affine_obj must be provided.")
49 |
50 | if raster_src is not None:
51 | affine_xform = get_geo_transform(raster_src)
52 | else:
53 | if isinstance(affine_obj, Affine):
54 | affine_xform = affine_obj
55 | else:
56 | # assume it's a list in either gdal or "standard" order
57 | # (list_to_affine checks which it is)
58 | if len(affine_obj) == 9: # if it's straight from rasterio
59 | affine_obj = affine_obj[0:6]
60 | affine_xform = list_to_affine(affine_obj)
61 |
62 | if inverse: # geo->px transform
63 | affine_xform = ~affine_xform
64 |
65 | if isinstance(geom, str):
66 | # get the polygon out of the wkt string
67 | g = shapely.wkt.loads(geom)
68 | elif isinstance(geom, shapely.geometry.base.BaseGeometry):
69 | g = geom
70 | else:
71 | raise TypeError('The provided geometry is not an accepted format. ' +
72 | 'This function can only accept WKT strings and ' +
73 | 'shapely geometries.')
74 |
75 | xformed_g = shapely.affinity.affine_transform(g, [affine_xform.a,
76 | affine_xform.b,
77 | affine_xform.d,
78 | affine_xform.e,
79 | affine_xform.xoff,
80 | affine_xform.yoff])
81 | if isinstance(geom, str):
82 | # restore to wkt string format
83 | xformed_g = shapely.wkt.dumps(xformed_g)
84 | if precision is not None:
85 | xformed_g = _reduce_geom_precision(xformed_g, precision=precision)
86 |
87 | return xformed_g
88 |
89 |
90 | def affine_transform_gdf(gdf, affine_obj, inverse=False, geom_col="geometry",
91 | precision=None):
92 | """Perform an affine transformation on a GeoDataFrame.
93 |
94 | Arguments
95 | ---------
96 | gdf : :class:`geopandas.GeoDataFrame`, :class:`pandas.DataFrame`, or `str`
97 | A GeoDataFrame, pandas DataFrame with a ``"geometry"`` column (or a
98 | different column containing geometries, identified by `geom_col` -
99 | note that this column will be renamed ``"geometry"`` for ease of use
100 | with geopandas), or the path to a saved file in .geojson or .csv
101 | format.
102 | affine_obj : list or :class:`affine.Affine`
103 | An affine transformation to apply to `geom` in the form of an
104 | ``[a, b, d, e, xoff, yoff]`` list or an :class:`affine.Affine` object.
105 | inverse : bool, optional
106 | Use this argument to perform the inverse transformation.
107 | geom_col : str, optional
108 | The column in `gdf` corresponding to the geometry. Defaults to
109 | ``'geometry'``.
110 | precision : int, optional
111 | Decimal precision to round the geometries to. If not provided, no
112 | rounding is performed.
113 | """
114 | if isinstance(gdf, str): # assume it's a geojson
115 | if gdf.lower().endswith('json'):
116 | gdf = gpd.read_file(gdf)
117 | elif gdf.lower().endswith('csv'):
118 | gdf = pd.read_csv(gdf)
119 | gdf = gdf.rename(columns={geom_col: 'geometry'})
120 | if not isinstance(gdf['geometry'][0], Polygon):
121 | gdf['geometry'] = gdf['geometry'].apply(shapely.wkt.loads)
122 | else:
123 | raise ValueError(
124 | "The file format is incompatible with this function.")
125 | gdf["geometry"] = gdf["geometry"].apply(convert_poly_coords,
126 | affine_obj=affine_obj,
127 | inverse=inverse)
128 | if precision is not None:
129 | gdf['geometry'] = gdf['geometry'].apply(
130 | _reduce_geom_precision, precision=precision)
131 | return gdf
132 |
133 |
134 | def georegister_px_df(df, im_fname=None, affine_obj=None, crs=None,
135 | geom_col='geometry', precision=None):
136 | """Convert a dataframe of geometries in pixel coordinates to a geo CRS.
137 |
138 | Arguments
139 | ---------
140 | df : :class:`pandas.DataFrame`
141 | A :class:`pandas.DataFrame` with polygons in a column named
142 | ``"geometry"``.
143 | im_fname : str, optional
144 | A filename or :class:`rasterio.DatasetReader` object containing an
145 | image that has the same bounds as the pixel coordinates in `df`. If
146 | not provided, `affine_obj` and `crs` must both be provided.
147 | affine_obj : `list` or :class:`affine.Affine`, optional
148 | An affine transformation to apply to `geom` in the form of an
149 | ``[a, b, d, e, xoff, yoff]`` list or an :class:`affine.Affine` object.
150 | Required if not using `raster_src`.
151 | crs : dict, optional
152 | The coordinate reference system for the output GeoDataFrame. Required
153 | if not providing a raster image to extract the information from. Format
154 | should be ``{'init': 'epsgxxxx'}``, replacing xxxx with the EPSG code.
155 | geom_col : str, optional
156 | The column containing geometry in `df`. If not provided, defaults to
157 | ``"geometry"``.
158 | precision : int, optional
159 | The decimal precision for output geometries. If not provided, the
160 | vertex locations won't be rounded.
161 |
162 | """
163 | if im_fname is not None:
164 | affine_obj = rasterio.open(im_fname).transform
165 | crs = rasterio.open(im_fname).crs
166 | else:
167 | if not affine_obj or not crs:
168 | raise ValueError(
169 | 'If an image path is not provided, ' +
170 | 'affine_obj and crs must be.')
171 | tmp_df = affine_transform_gdf(df, affine_obj, geom_col=geom_col,
172 | precision=precision)
173 |
174 | return gpd.GeoDataFrame(tmp_df, crs=crs)
175 |
176 |
177 | def geojson_to_px_gdf(geojson, im_path, precision=None):
178 | """Convert a geojson or set of geojsons from geo coords to px coords.
179 |
180 | Arguments
181 | ---------
182 | geojson : str
183 | Path to a geojson. This function will also accept a
184 | :class:`pandas.DataFrame` or :class:`geopandas.GeoDataFrame` with a
185 | column named ``'geometry'`` in this argument.
186 | im_path : str
187 | Path to a georeferenced image (ie a GeoTIFF) that geolocates to the
188 | same geography as the `geojson`(s). If a directory, the bounds of each
189 | GeoTIFF will be loaded in and all overlapping geometries will be
190 | transformed. This function will also accept a
191 | :class:`osgeo.gdal.Dataset` or :class:`rasterio.DatasetReader` with
192 | georeferencing information in this argument.
193 | precision : int, optional
194 | The decimal precision for output geometries. If not provided, the
195 | vertex locations won't be rounded.
196 |
197 | Returns
198 | -------
199 | output_df : :class:`pandas.DataFrame`
200 | A :class:`pandas.DataFrame` with all geometries in `geojson` that
201 | overlapped with the image at `im_path` converted to pixel coordinates.
202 | Additional columns are included with the filename of the source
203 | geojson (if available) and images for reference.
204 |
205 | """
206 | # get the bbox and affine transforms for the image
207 | if isinstance(im_path, str):
208 | bbox = box(*rasterio.open(im_path).bounds)
209 | affine_obj = rasterio.open(im_path).transform
210 | im_crs = rasterio.open(im_path).crs
211 |
212 | else:
213 | bbox = box(im_path.bounds)
214 | affine_obj = im_path.transform
215 | im_crs = im_path.crs
216 |
217 | # make sure the geo vector data is loaded in as geodataframe(s)
218 | gdf = _check_gdf_load(geojson)
219 |
220 | overlap_gdf = get_overlapping_subset(gdf, bbox=bbox, bbox_crs=im_crs)
221 | transformed_gdf = affine_transform_gdf(overlap_gdf, affine_obj=affine_obj,
222 | inverse=True, precision=precision)
223 | transformed_gdf['image_fname'] = os.path.split(im_path)[1]
224 |
225 | return transformed_gdf
226 |
227 |
228 | def get_overlapping_subset(gdf, im=None, bbox=None, bbox_crs=None):
229 | """Extract a subset of geometries in a GeoDataFrame that overlap with `im`.
230 |
231 | Notes
232 | -----
233 | This function uses RTree's spatialindex, which is much faster (but slightly
234 | less accurate) than direct comparison of each object for overlap.
235 |
236 | Arguments
237 | ---------
238 | gdf : :class:`geopandas.GeoDataFrame`
239 | A :class:`geopandas.GeoDataFrame` instance or a path to a geojson.
240 | im : :class:`rasterio.DatasetReader` or `str`, optional
241 | An image object loaded with `rasterio` or a path to a georeferenced
242 | image (i.e. a GeoTIFF).
243 | bbox : `list` or :class:`shapely.geometry.Polygon`, optional
244 | A bounding box (either a :class:`shapely.geometry.Polygon` or a
245 | ``[bottom, left, top, right]`` `list`) from an image. Has no effect
246 | if `im` is provided (`bbox` is inferred from the image instead.) If
247 | `bbox` is passed and `im` is not, a `bbox_crs` should be provided to
248 | ensure correct geolocation - if it isn't, it will be assumed to have
249 | the same crs as `gdf`.
250 |
251 | Returns
252 | -------
253 | output_gdf : :class:`geopandas.GeoDataFrame`
254 | A :class:`geopandas.GeoDataFrame` with all geometries in `gdf` that
255 | overlapped with the image at `im`.
256 | Coordinates are kept in the CRS of `gdf`.
257 |
258 | """
259 | if not im and not bbox:
260 | raise ValueError('Either `im` or `bbox` must be provided.')
261 | if isinstance(gdf, str):
262 | gdf = gpd.read_file(gdf)
263 | if isinstance(im, str):
264 | im = rasterio.open(im)
265 | sindex = gdf.sindex
266 | # use transform_bounds in case the crs is different - no effect if not
267 | if im:
268 | bbox = transform_bounds(im.crs, gdf.crs, *im.bounds)
269 | else:
270 | if isinstance(bbox, Polygon):
271 | bbox = bbox.bounds
272 | if not bbox_crs:
273 | bbox_crs = gdf.crs
274 | bbox = transform_bounds(bbox_crs, gdf.crs, *bbox)
275 | try:
276 | intersectors = list(sindex.intersection(bbox))
277 | except RTreeError:
278 | intersectors = []
279 |
280 | return gdf.iloc[intersectors, :]
281 |
--------------------------------------------------------------------------------
/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 | SOURCEDIR = source
8 | BUILDDIR = build
9 |
10 | # Put it first so that "make" without argument is like "make help".
11 | help:
12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
13 |
14 | .PHONY: help Makefile
15 |
16 | # Catch-all target: route all unknown targets to Sphinx using the new
17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
18 | %: Makefile
19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=source
11 | set BUILDDIR=build
12 |
13 | if "%1" == "" goto help
14 |
15 | %SPHINXBUILD% >NUL 2>NUL
16 | if errorlevel 9009 (
17 | echo.
18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19 | echo.installed, then set the SPHINXBUILD environment variable to point
20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
21 | echo.may add the Sphinx directory to PATH.
22 | echo.
23 | echo.If you don't have Sphinx installed, grab it from
24 | echo.http://sphinx-doc.org/
25 | exit /b 1
26 | )
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/docs/source/api.rst:
--------------------------------------------------------------------------------
1 | .. title:: API reference
2 |
3 | CosmiQ Works GeoData API reference
4 | =====================================
5 |
6 | .. contents::
7 |
8 | cw-geodata class and function list
9 | ----------------------------------
10 |
11 | .. autosummary::
12 |
13 | cw_geodata.raster_image.image.get_geo_transform
14 | cw_geodata.vector_label.polygon.affine_transform_gdf
15 | cw_geodata.vector_label.polygon.convert_poly_coords
16 | cw_geodata.vector_label.polygon.geojson_to_px_gdf
17 | cw_geodata.vector_label.polygon.georegister_px_df
18 | cw_geodata.vector_label.polygon.get_overlapping_subset
19 | cw_geodata.vector_label.graph.geojson_to_graph
20 | cw_geodata.vector_label.graph.get_nodes_paths
21 | cw_geodata.vector_label.graph.process_linestring
22 | cw_geodata.vector_label.mask.boundary_mask
23 | cw_geodata.vector_label.mask.contact_mask
24 | cw_geodata.vector_label.mask.df_to_px_mask
25 | cw_geodata.vector_label.mask.footprint_mask
26 | cw_geodata.utils.geo.geometries_internal_intersection
27 | cw_geodata.utils.geo.list_to_affine
28 | cw_geodata.utils.geo.split_multi_geometries
29 |
30 |
31 |
32 | Raster/Image functionality
33 | --------------------------
34 |
35 | Image submodule
36 | ~~~~~~~~~~~~~~~
37 |
38 | .. automodule:: cw_geodata.raster_image.image
39 | :members:
40 |
41 | Vector/Label functionality
42 | --------------------------
43 |
44 | Polygon submodule
45 | ~~~~~~~~~~~~~~~~~
46 |
47 | .. automodule:: cw_geodata.vector_label.polygon
48 | :members:
49 |
50 | Graph submodule
51 | ~~~~~~~~~~~~~~~
52 |
53 | .. automodule:: cw_geodata.vector_label.graph
54 | :members:
55 |
56 | Mask submodule
57 | ~~~~~~~~~~~~~~
58 |
59 | .. automodule:: cw_geodata.vector_label.mask
60 | :members:
61 |
62 | Utility functions
63 | -----------------
64 |
65 | Core utility submodule
66 | ~~~~~~~~~~~~~~~~~~~~~~
67 |
68 | .. automodule:: cw_geodata.utils.core
69 | :members:
70 |
71 | Geo utility submodule
72 | ~~~~~~~~~~~~~~~~~~~~~
73 |
74 | .. automodule:: cw_geodata.utils.geo
75 | :members:
76 |
--------------------------------------------------------------------------------
/docs/source/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Configuration file for the Sphinx documentation builder.
4 | #
5 | # This file does only contain a selection of the most common options. For a
6 | # full list see the documentation:
7 | # http://www.sphinx-doc.org/en/master/config
8 |
9 | # -- Path setup --------------------------------------------------------------
10 |
11 | # If extensions (or modules to document with autodoc) are in another directory,
12 | # add these directories to sys.path here. If the directory is relative to the
13 | # documentation root, use os.path.abspath to make it absolute, like shown here.
14 | #
15 | # import os
16 | # import sys
17 | # sys.path.insert(0, os.path.abspath('.'))
18 |
19 |
20 | # -- Project information -----------------------------------------------------
21 |
22 | project = 'cw-geodata'
23 | copyright = '2019, CosmiQ Works'
24 | author = 'Nick Weir, CosmiQ Works'
25 |
26 | # The short X.Y version
27 | version = '0.0'
28 | # The full version, including alpha/beta/rc tags
29 | release = '0.0.3'
30 |
31 |
32 | # -- General configuration ---------------------------------------------------
33 |
34 | # If your documentation needs a minimal Sphinx version, state it here.
35 | #
36 | # needs_sphinx = '1.0'
37 |
38 | # Add any Sphinx extension module names here, as strings. They can be
39 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
40 | # ones.
41 | extensions = [
42 | 'sphinx.ext.napoleon',
43 | 'sphinx.ext.autodoc',
44 | 'sphinx.ext.doctest',
45 | 'sphinx.ext.intersphinx',
46 | 'sphinx.ext.mathjax',
47 | 'sphinx.ext.viewcode',
48 | 'sphinx.ext.githubpages',
49 | 'sphinx.ext.autosummary'
50 | ]
51 |
52 | # Add any paths that contain templates here, relative to this directory.
53 | templates_path = ['_templates']
54 |
55 | # The suffix(es) of source filenames.
56 | # You can specify multiple suffix as a list of string:
57 | #
58 | # source_suffix = ['.rst', '.md']
59 | source_suffix = '.rst'
60 |
61 | # The master toctree document.
62 | master_doc = 'index'
63 |
64 | # The language for content autogenerated by Sphinx. Refer to documentation
65 | # for a list of supported languages.
66 | #
67 | # This is also used if you do content translation via gettext catalogs.
68 | # Usually you set "language" from the command line for these cases.
69 | language = None
70 |
71 | # List of patterns, relative to source directory, that match files and
72 | # directories to ignore when looking for source files.
73 | # This pattern also affects html_static_path and html_extra_path.
74 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', '**.ipynb_checkpoints']
75 |
76 | # The name of the Pygments (syntax highlighting) style to use.
77 | pygments_style = None
78 |
79 |
80 | # -- Options for HTML output -------------------------------------------------
81 |
82 | # The theme to use for HTML and HTML Help pages. See the documentation for
83 | # a list of builtin themes.
84 | #
85 | html_theme = 'sphinx_rtd_theme'
86 |
87 | # Theme options are theme-specific and customize the look and feel of a theme
88 | # further. For a list of options available for each theme, see the
89 | # documentation.
90 | #
91 | # html_theme_options = {}
92 |
93 | # Add any paths that contain custom static files (such as style sheets) here,
94 | # relative to this directory. They are copied after the builtin static files,
95 | # so a file named "default.css" will overwrite the builtin "default.css".
96 | html_static_path = ['_static']
97 |
98 | # Custom sidebar templates, must be a dictionary that maps document names
99 | # to template names.
100 | #
101 | # The default sidebars (for documents that don't match any pattern) are
102 | # defined by theme itself. Builtin themes are using these templates by
103 | # default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
104 | # 'searchbox.html']``.
105 | #
106 | # html_sidebars = {}
107 |
108 |
109 | # -- Options for HTMLHelp output ---------------------------------------------
110 |
111 | # Output file base name for HTML help builder.
112 | htmlhelp_basename = 'cw-geodatadoc'
113 |
114 |
115 | # -- Options for LaTeX output ------------------------------------------------
116 |
117 | latex_elements = {
118 | # The paper size ('letterpaper' or 'a4paper').
119 | #
120 | # 'papersize': 'letterpaper',
121 |
122 | # The font size ('10pt', '11pt' or '12pt').
123 | #
124 | # 'pointsize': '10pt',
125 |
126 | # Additional stuff for the LaTeX preamble.
127 | #
128 | # 'preamble': '',
129 |
130 | # Latex figure (float) alignment
131 | #
132 | # 'figure_align': 'htbp',
133 | }
134 |
135 | # Grouping the document tree into LaTeX files. List of tuples
136 | # (source start file, target name, title,
137 | # author, documentclass [howto, manual, or own class]).
138 | latex_documents = [
139 | (master_doc, 'cw-geodata.tex', 'cw-geodata Documentation',
140 | 'CosmiQ Works', 'manual'),
141 | ]
142 |
143 |
144 | # -- Options for manual page output ------------------------------------------
145 |
146 | # One entry per manual page. List of tuples
147 | # (source start file, name, description, authors, manual section).
148 | man_pages = [
149 | (master_doc, 'cw-geodata', 'cw-geodata Documentation',
150 | [author], 1)
151 | ]
152 |
153 |
154 | # -- Options for Texinfo output ----------------------------------------------
155 |
156 | # Grouping the document tree into Texinfo files. List of tuples
157 | # (source start file, target name, title, author,
158 | # dir menu entry, description, category)
159 | texinfo_documents = [
160 | (master_doc, 'cw-geodata', 'cw-geodata Documentation',
161 | author, 'cw-geodata', 'CosmiQ Works Tools for coversion between CV and GeoSpatial formats.',
162 | 'Miscellaneous'),
163 | ]
164 |
165 |
166 | # -- Options for Epub output -------------------------------------------------
167 |
168 | # Bibliographic Dublin Core info.
169 | epub_title = project
170 |
171 | # The unique identifier of the text. This can be a ISBN number
172 | # or the project homepage.
173 | #
174 | # epub_identifier = ''
175 |
176 | # A unique identification for the text.
177 | #
178 | # epub_uid = ''
179 |
180 | # A list of files that should not be packed into the epub file.
181 | epub_exclude_files = ['search.html']
182 |
183 |
184 | # -- Extension configuration -------------------------------------------------
185 |
186 | # -- Options for intersphinx extension ---------------------------------------
187 |
188 | # Example configuration for intersphinx: refer to the Python standard library.
189 | intersphinx_mapping = intersphinx_mapping = {
190 | "python": ('https://docs.python.org/', None),
191 | "rasterio": ('https://rasterio.readthedocs.io/en/latest/', None),
192 | "pandas": ('http://pandas.pydata.org/pandas-docs/stable/', None),
193 | "geopandas": ('http://geopandas.org/', None),
194 | "rtree": ('http://toblerity.org/rtree/', None),
195 | "shapely": ('https://shapely.readthedocs.io/en/stable/', None),
196 | "networkx": ('https://networkx.github.io/documentation/stable/', None)
197 | }
198 |
--------------------------------------------------------------------------------
/docs/source/index.rst:
--------------------------------------------------------------------------------
1 | CosmiQ Works Evaluation (`cw-geodata `__) Documentation
2 | =============================================================================================
3 | :Author: `CosmiQ Works `__
4 | :Release: |release|
5 | :Copyright: 2018, CosmiQ Works
6 | :License: This work is licensed under an `Apache 2.0 License`__.
7 |
8 | .. __: https://www.apache.org/licenses/LICENSE-2.0
9 |
10 | .. toctree::
11 | :name: mastertoc
12 | :maxdepth: 3
13 | :glob:
14 |
15 | api
16 |
17 | Indices and tables
18 | ------------------
19 |
20 | * :ref:`genindex`
21 | * :ref:`modindex`
22 | * :ref:`search`
23 |
--------------------------------------------------------------------------------
/environment.yml:
--------------------------------------------------------------------------------
1 | name: cw-geodata
2 | channels:
3 | - conda-forge
4 | - defaults
5 | dependencies:
6 | - python=3.6
7 | - pip
8 | - shapely
9 | - pandas
10 | - geopandas
11 | - numpy
12 | - scipy
13 | - scikit-image
14 | - rtree
15 | - networkx
16 | - rasterio
17 | - pip:
18 | - affine
19 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup, find_packages
2 | version = '0.0.3'
3 |
4 | # Runtime requirements.
5 | inst_reqs = ["shapely", "rtree", "geopandas", "pandas", "networkx"]
6 |
7 | extra_reqs = {
8 | 'test': ['mock', 'pytest', 'pytest-cov', 'codecov']}
9 |
10 | setup(name='cw_geodata',
11 | version=version,
12 | description=u"""Geospatial raster and vector data processing for ML""",
13 | classifiers=[
14 | 'Intended Audience :: Information Technology',
15 | 'Intended Audience :: Science/Research',
16 | 'License :: OSI Approved :: BSD License',
17 | 'Programming Language :: Python :: 3.6',
18 | 'Programming Language :: Python :: 2.7',
19 | 'Topic :: Scientific/Engineering :: GIS'],
20 | keywords='spacenet machinelearning gis geojson',
21 | author=u"Nicholas Weir",
22 | author_email='nweir@iqt.org',
23 | url='https://github.com/CosmiQ/cw-geodata',
24 | license='Apache-2.0',
25 | packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
26 | zip_safe=False,
27 | include_package_data=True,
28 | install_requires=inst_reqs,
29 | extras_require=extra_reqs,
30 | entry_points={}
31 | )
32 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CosmiQ/cw-geodata/19a290c20ec4672dc2e9be37f451d136166bb28a/tests/__init__.py
--------------------------------------------------------------------------------
/tests/test_imports.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 |
4 | class TestImports(object):
5 | def test_imports(self):
6 | from cw_geodata.utils import core, geo
7 | from cw_geodata.raster_image import image
8 | from cw_geodata.vector_label import polygon, graph, mask
9 |
--------------------------------------------------------------------------------
/tests/test_raster_image/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CosmiQ/cw-geodata/19a290c20ec4672dc2e9be37f451d136166bb28a/tests/test_raster_image/__init__.py
--------------------------------------------------------------------------------
/tests/test_raster_image/test_image.py:
--------------------------------------------------------------------------------
1 | import os
2 | from cw_geodata.data import data_dir, sample_load_rasterio, sample_load_gdal
3 | from cw_geodata.raster_image.image import get_geo_transform
4 | from affine import Affine
5 |
6 |
7 | class TestGetGeoTransform(object):
8 | """Tests for cw_geodata.raster_image.image.get_geo_transform."""
9 |
10 | def test_get_from_file(self):
11 | affine_obj = get_geo_transform(os.path.join(data_dir,
12 | 'sample_geotiff.tif'))
13 | assert affine_obj == Affine(0.5, 0.0, 733601.0, 0.0, -0.5, 3725139.0)
14 |
15 | def test_get_from_opened_raster(self):
16 | src_obj = sample_load_rasterio()
17 | affine_obj = get_geo_transform(src_obj)
18 | assert affine_obj == Affine(0.5, 0.0, 733601.0, 0.0, -0.5, 3725139.0)
19 | src_obj.close()
20 |
21 | def test_get_from_gdal(self):
22 | src_obj = sample_load_gdal()
23 | affine_obj = get_geo_transform(src_obj)
24 | assert affine_obj == Affine(0.5, 0.0, 733601.0, 0.0, -0.5, 3725139.0)
25 |
--------------------------------------------------------------------------------
/tests/test_utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CosmiQ/cw-geodata/19a290c20ec4672dc2e9be37f451d136166bb28a/tests/test_utils/__init__.py
--------------------------------------------------------------------------------
/tests/test_utils/test_core.py:
--------------------------------------------------------------------------------
1 | import os
2 | import numpy as np
3 | import pandas as pd
4 | import geopandas as gpd
5 | import rasterio
6 | from cw_geodata.data import data_dir
7 | from cw_geodata.utils.core import _check_df_load, _check_gdf_load
8 | from cw_geodata.utils.core import _check_rasterio_im_load
9 |
10 |
11 | class TestLoadCheckers(object):
12 | """Test objects for checking loading of various objects."""
13 |
14 | def test_unloaded_geojson(self):
15 | geojson_path = os.path.join(data_dir, 'sample.geojson')
16 | truth_gdf = gpd.read_file(geojson_path)
17 | test_gdf = _check_gdf_load(geojson_path)
18 |
19 | assert truth_gdf.equals(test_gdf)
20 |
21 | def test_loaded_geojson(self):
22 | geojson_path = os.path.join(data_dir, 'sample.geojson')
23 | truth_gdf = gpd.read_file(geojson_path)
24 | test_gdf = _check_gdf_load(truth_gdf.copy())
25 |
26 | assert truth_gdf.equals(test_gdf)
27 |
28 | def test_unloaded_df(self):
29 | csv_path = os.path.join(data_dir, 'sample.csv')
30 | truth_df = pd.read_csv(csv_path)
31 | test_df = _check_df_load(csv_path)
32 |
33 | assert truth_df.equals(test_df)
34 |
35 | def test_loaded_df(self):
36 | csv_path = os.path.join(data_dir, 'sample.csv')
37 | truth_df = pd.read_csv(csv_path)
38 | test_df = _check_df_load(truth_df.copy())
39 |
40 | assert truth_df.equals(test_df)
41 |
42 | def test_unloaded_image(self):
43 | im_path = os.path.join(data_dir, 'sample_geotiff.tif')
44 | truth_im = rasterio.open(im_path)
45 | test_im = _check_rasterio_im_load(im_path)
46 |
47 | assert truth_im.profile == test_im.profile
48 | assert np.array_equal(truth_im.read(1), test_im.read(1))
49 |
50 | truth_im.close() # need to close the rasterio datasetreader objects
51 | test_im.close()
52 |
53 | def test_loaded_image(self):
54 | im_path = os.path.join(data_dir, 'sample_geotiff.tif')
55 | truth_im = rasterio.open(im_path)
56 | test_im = _check_rasterio_im_load(truth_im)
57 |
58 | assert truth_im.profile == test_im.profile
59 | assert np.array_equal(truth_im.read(1), test_im.read(1))
60 |
61 | truth_im.close() # need to close the rasterio datasetreader objects
62 | test_im.close()
63 |
--------------------------------------------------------------------------------
/tests/test_utils/test_geo.py:
--------------------------------------------------------------------------------
1 | import os
2 | import pandas as pd
3 | import geopandas as gpd
4 | import shapely
5 | from affine import Affine
6 | from shapely.wkt import loads
7 | from cw_geodata.data import data_dir
8 | from cw_geodata.utils.geo import list_to_affine, split_multi_geometries, \
9 | geometries_internal_intersection
10 |
11 |
12 | class TestCoordTransformer(object):
13 | """Tests for the utils.geo.CoordTransformer."""
14 |
15 | def test_convert_image_crs(self):
16 | pass
17 |
18 |
19 | class TestListToAffine(object):
20 | """Tests for utils.geo.list_to_affine()."""
21 |
22 | def test_rasterio_order_list(self):
23 | truth_affine_obj = Affine(0.5, 0.0, 733601.0, 0.0, -0.5, 3725139.0)
24 | affine_list = [0.5, 0.0, 733601.0, 0.0, -0.5, 3725139.0]
25 | test_affine = list_to_affine(affine_list)
26 |
27 | assert truth_affine_obj == test_affine
28 |
29 | def test_gdal_order_list(self):
30 | truth_affine_obj = Affine(0.5, 0.0, 733601.0, 0.0, -0.5, 3725139.0)
31 | gdal_affine_list = [733601.0, 0.5, 0.0, 3725139.0, 0.0, -0.5]
32 | test_affine = list_to_affine(gdal_affine_list)
33 |
34 | assert truth_affine_obj == test_affine
35 |
36 |
37 | class TestGeometriesInternalIntersection(object):
38 | """Tests for utils.geo.geometries_internal_intersection."""
39 |
40 | def test_no_overlap(self):
41 | """Test creation of an overlap object with no intersection."""
42 | poly_df = pd.read_csv(os.path.join(data_dir, 'sample.csv'))
43 | polygons = poly_df['PolygonWKT_Pix'].apply(loads).values
44 | preds = geometries_internal_intersection(polygons)
45 | # there's no overlap, so result should be an empty GeometryCollection
46 | assert preds == shapely.geometry.collection.GeometryCollection()
47 |
48 | def test_with_overlap(self):
49 | poly_df = pd.read_csv(os.path.join(data_dir, 'sample.csv'))
50 | # expand the polygons to generate some overlap
51 | polygons = poly_df['PolygonWKT_Pix'].apply(
52 | lambda x: loads(x).buffer(15)).values
53 | preds = geometries_internal_intersection(polygons)
54 | with open(os.path.join(data_dir, 'test_overlap_output.txt'), 'r') as f:
55 | truth = f.read()
56 | f.close()
57 | truth = loads(truth)
58 | # set a threshold for how good overlap with truth has to be in case of
59 | # rounding errors
60 | assert truth.intersection(preds).area/truth.area > 0.99
61 |
62 |
63 | class TestSplitMultiGeometries(object):
64 | """Test for splittling MultiPolygons."""
65 |
66 | def test_simple_split_multipolygon(self):
67 | output = split_multi_geometries(os.path.join(data_dir,
68 | 'w_multipolygon.csv'))
69 | expected = gpd.read_file(os.path.join(
70 | data_dir, 'split_multi_result.json')).drop(columns='id')
71 |
72 | assert expected.equals(output)
73 |
74 | def test_grouped_split_multipolygon(self):
75 | output = split_multi_geometries(
76 | os.path.join(data_dir, 'w_multipolygon.csv'), obj_id_col='field_1',
77 | group_col='truncated')
78 | expected = gpd.read_file(os.path.join(
79 | data_dir, 'split_multi_grouped_result.json')).drop(columns='id')
80 |
81 | assert expected.equals(output)
82 |
--------------------------------------------------------------------------------
/tests/test_vector_label/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CosmiQ/cw-geodata/19a290c20ec4672dc2e9be37f451d136166bb28a/tests/test_vector_label/__init__.py
--------------------------------------------------------------------------------
/tests/test_vector_label/test_graph.py:
--------------------------------------------------------------------------------
1 | import os
2 | from cw_geodata.data import data_dir
3 | from cw_geodata.vector_label.graph import geojson_to_graph
4 | import pickle
5 | import networkx as nx
6 |
7 |
8 | class TestGeojsonToGraph(object):
9 | """Tests for cw_geodata.vector_label.graph.geojson_to_graph."""
10 |
11 | def test_graph_creation(self):
12 | """Test if a newly created graph is identical to an existing one."""
13 | with open(os.path.join(data_dir, 'sample_graph.pkl'), 'rb') as f:
14 | truth_graph = pickle.load(f)
15 | f.close()
16 | output_graph = geojson_to_graph(os.path.join(data_dir,
17 | 'sample_roads.geojson'))
18 |
19 | assert nx.is_isomorphic(truth_graph, output_graph)
20 |
--------------------------------------------------------------------------------
/tests/test_vector_label/test_mask.py:
--------------------------------------------------------------------------------
1 | import os
2 | import numpy as np
3 | import pandas as pd
4 | from affine import Affine
5 | from shapely.geometry import Polygon
6 | import skimage
7 | import rasterio
8 | from cw_geodata.data import data_dir
9 | from cw_geodata.vector_label.mask import footprint_mask, boundary_mask, \
10 | contact_mask, df_to_px_mask
11 |
12 |
13 | class TestFootprintMask(object):
14 | """Tests for cw_geodata.vector_label.mask.footprint_mask."""
15 |
16 | def test_make_mask(self):
17 | """test creating a basic mask using a csv input."""
18 | output_mask = footprint_mask(os.path.join(data_dir, 'sample.csv'),
19 | geom_col="PolygonWKT_Pix")
20 | truth_mask = skimage.io.imread(os.path.join(data_dir,
21 | 'sample_fp_mask.tif'))
22 |
23 | assert np.array_equal(output_mask, truth_mask)
24 |
25 | def test_make_mask_w_output_file(self):
26 | """test creating a basic mask and saving the output to a file."""
27 | output_mask = footprint_mask(
28 | os.path.join(data_dir, 'sample.csv'),
29 | geom_col="PolygonWKT_Pix",
30 | reference_im=os.path.join(data_dir, "sample_geotiff.tif"),
31 | out_file=os.path.join(data_dir, 'test_out.tif')
32 | )
33 | truth_mask = skimage.io.imread(os.path.join(data_dir,
34 | 'sample_fp_mask.tif'))
35 | saved_output_mask = skimage.io.imread(os.path.join(data_dir,
36 | 'test_out.tif'))
37 |
38 | assert np.array_equal(output_mask, truth_mask)
39 | assert np.array_equal(saved_output_mask, truth_mask)
40 | # clean up
41 | os.remove(os.path.join(data_dir, 'test_out.tif'))
42 |
43 | def test_make_mask_w_file_and_transform(self):
44 | """Test creating a mask using a geojson and an affine xform."""
45 | output_mask = footprint_mask(
46 | os.path.join(data_dir, 'geotiff_labels.geojson'),
47 | reference_im=os.path.join(data_dir, 'sample_geotiff.tif'),
48 | do_transform=True,
49 | out_file=os.path.join(data_dir, 'test_out.tif')
50 | )
51 | truth_mask = skimage.io.imread(
52 | os.path.join(data_dir, 'sample_fp_mask_from_geojson.tif')
53 | )
54 | saved_output_mask = skimage.io.imread(os.path.join(data_dir,
55 | 'test_out.tif'))
56 |
57 | assert np.array_equal(output_mask, truth_mask)
58 | assert np.array_equal(saved_output_mask, truth_mask)
59 | # clean up
60 | os.remove(os.path.join(data_dir, 'test_out.tif'))
61 |
62 |
63 | class TestBoundaryMask(object):
64 | """Tests for cw_geodata.vector_label.mask.boundary_mask."""
65 |
66 | def test_make_inner_mask_from_fp(self):
67 | """test creating a boundary mask using an existing footprint mask."""
68 | fp_mask = skimage.io.imread(os.path.join(data_dir,
69 | 'sample_fp_mask.tif'))
70 | output_mask = boundary_mask(fp_mask)
71 | truth_mask = skimage.io.imread(os.path.join(data_dir,
72 | 'sample_b_mask_inner.tif'))
73 |
74 | assert np.array_equal(output_mask, truth_mask)
75 |
76 | def test_make_outer_mask_from_fp(self):
77 | """test creating a boundary mask using an existing footprint mask."""
78 | fp_mask = skimage.io.imread(os.path.join(data_dir,
79 | 'sample_fp_mask.tif'))
80 | output_mask = boundary_mask(fp_mask, boundary_type="outer")
81 | truth_mask = skimage.io.imread(os.path.join(data_dir,
82 | 'sample_b_mask_outer.tif'))
83 |
84 | assert np.array_equal(output_mask, truth_mask)
85 |
86 | def test_make_thick_outer_mask_from_fp(self):
87 | """test creating a 10-px thick boundary mask."""
88 | fp_mask = skimage.io.imread(os.path.join(data_dir,
89 | 'sample_fp_mask.tif'))
90 | output_mask = boundary_mask(fp_mask, boundary_type="outer",
91 | boundary_width=10)
92 | truth_mask = skimage.io.imread(
93 | os.path.join(data_dir, 'sample_b_mask_outer_10.tif')
94 | )
95 |
96 | assert np.array_equal(output_mask, truth_mask)
97 |
98 | def test_make_binary_and_fp(self):
99 | """test creating a boundary mask and a fp mask together."""
100 | output_mask = boundary_mask(df=os.path.join(data_dir, 'sample.csv'),
101 | geom_col="PolygonWKT_Pix")
102 | truth_mask = skimage.io.imread(os.path.join(data_dir,
103 | 'sample_b_mask_inner.tif'))
104 |
105 | assert np.array_equal(output_mask, truth_mask)
106 |
107 |
108 | class TestContactMask(object):
109 | """Tests for cw_geodata.vector_label.mask.contact_mask."""
110 |
111 | def test_make_contact_mask_w_save(self):
112 | """test creating a contact point mask."""
113 | output_mask = contact_mask(
114 | os.path.join(data_dir, 'sample.csv'), geom_col="PolygonWKT_Pix",
115 | contact_spacing=10,
116 | reference_im=os.path.join(data_dir, "sample_geotiff.tif"),
117 | out_file=os.path.join(data_dir, 'test_out.tif')
118 | )
119 | truth_mask = skimage.io.imread(os.path.join(data_dir,
120 | 'sample_c_mask.tif'))
121 | saved_output_mask = skimage.io.imread(os.path.join(data_dir,
122 | 'test_out.tif'))
123 |
124 | assert np.array_equal(output_mask, truth_mask)
125 | assert np.array_equal(saved_output_mask, truth_mask)
126 | os.remove(os.path.join(data_dir, 'test_out.tif')) # clean up after
127 |
128 |
129 | class TestDFToPxMask(object):
130 | """Tests for cw_geodata.vector_label.mask.df_to_px_mask."""
131 |
132 | def test_basic_footprint_w_save(self):
133 | output_mask = df_to_px_mask(
134 | os.path.join(data_dir, 'sample.csv'),
135 | geom_col='PolygonWKT_Pix',
136 | reference_im=os.path.join(data_dir, "sample_geotiff.tif"),
137 | out_file=os.path.join(data_dir, 'test_out.tif'))
138 | truth_mask = skimage.io.imread(os.path.join(data_dir,
139 | 'sample_fp_from_df2px.tif')
140 | )
141 | saved_output_mask = skimage.io.imread(os.path.join(data_dir,
142 | 'test_out.tif'))
143 |
144 | assert np.array_equal(output_mask, truth_mask)
145 | assert np.array_equal(saved_output_mask, truth_mask[:, :, 0])
146 | os.remove(os.path.join(data_dir, 'test_out.tif')) # clean up after
147 |
148 | def test_border_footprint_w_save(self):
149 | output_mask = df_to_px_mask(
150 | os.path.join(data_dir, 'sample.csv'), channels=['boundary'],
151 | geom_col='PolygonWKT_Pix',
152 | reference_im=os.path.join(data_dir, "sample_geotiff.tif"),
153 | out_file=os.path.join(data_dir, 'test_out.tif'))
154 | truth_mask = skimage.io.imread(os.path.join(data_dir,
155 | 'sample_b_from_df2px.tif')
156 | )
157 | saved_output_mask = skimage.io.imread(os.path.join(data_dir,
158 | 'test_out.tif'))
159 |
160 | assert np.array_equal(output_mask, truth_mask)
161 | assert np.array_equal(saved_output_mask, truth_mask[:, :, 0])
162 | os.remove(os.path.join(data_dir, 'test_out.tif')) # clean up after
163 |
164 | def test_contact_footprint_w_save(self):
165 | output_mask = df_to_px_mask(
166 | os.path.join(data_dir, 'sample.csv'), channels=['contact'],
167 | geom_col='PolygonWKT_Pix',
168 | reference_im=os.path.join(data_dir, "sample_geotiff.tif"),
169 | out_file=os.path.join(data_dir, 'test_out.tif'))
170 | truth_mask = skimage.io.imread(os.path.join(data_dir,
171 | 'sample_c_from_df2px.tif')
172 | )
173 | saved_output_mask = skimage.io.imread(os.path.join(data_dir,
174 | 'test_out.tif'))
175 |
176 | assert np.array_equal(output_mask, truth_mask)
177 | assert np.array_equal(saved_output_mask, truth_mask[:, :, 0])
178 | os.remove(os.path.join(data_dir, 'test_out.tif')) # clean up after
179 |
180 | def test_all_three_w_save(self):
181 | """Test creating a 3-channel mask."""
182 | output_mask = df_to_px_mask(
183 | os.path.join(data_dir, 'sample.csv'),
184 | channels=['footprint', 'boundary', 'contact'],
185 | boundary_type='outer', boundary_width=5, contact_spacing=15,
186 | geom_col='PolygonWKT_Pix',
187 | reference_im=os.path.join(data_dir, "sample_geotiff.tif"),
188 | out_file=os.path.join(data_dir, 'test_out.tif'))
189 | truth_mask = skimage.io.imread(
190 | os.path.join(data_dir, 'sample_fbc_from_df2px.tif')
191 | )
192 | saved_output_mask = skimage.io.imread(os.path.join(data_dir,
193 | 'test_out.tif'))
194 |
195 | assert np.array_equal(output_mask, truth_mask)
196 | assert np.array_equal(saved_output_mask, truth_mask)
197 | os.remove(os.path.join(data_dir, 'test_out.tif')) # clean up after
198 |
--------------------------------------------------------------------------------
/tests/test_vector_label/test_polygon.py:
--------------------------------------------------------------------------------
1 | import os
2 | import pandas as pd
3 | from affine import Affine
4 | from shapely.geometry import Polygon
5 | from shapely.wkt import loads, dumps
6 | import geopandas as gpd
7 | import rasterio
8 | from cw_geodata.data import data_dir
9 | from cw_geodata.vector_label.polygon import convert_poly_coords, \
10 | affine_transform_gdf, georegister_px_df, geojson_to_px_gdf
11 |
12 | square = Polygon([(10, 20), (10, 10), (20, 10), (20, 20)])
13 | forward_result = loads("POLYGON ((733606 3725129, 733606 3725134, 733611 3725134, 733611 3725129, 733606 3725129))")
14 | reverse_result = loads("POLYGON ((-1467182 7450238, -1467182 7450258, -1467162 7450258, -1467162 7450238, -1467182 7450238))")
15 | # note that the xform below is the same as in cw_geodata/data/sample_geotiff.tif
16 | aff = Affine(0.5, 0.0, 733601.0, 0.0, -0.5, 3725139.0)
17 | affine_list = [0.5, 0.0, 733601.0, 0.0, -0.5, 3725139.0]
18 | long_affine_list = [0.5, 0.0, 733601.0, 0.0, -0.5, 3725139.0,
19 | 0.0, 0.0, 1.0]
20 | gdal_affine_list = [733601.0, 0.5, 0.0, 3725139.0, 0.0, -0.5]
21 |
22 |
23 | class TestConvertPolyCoords(object):
24 | """Test the convert_poly_coords functionality."""
25 |
26 | def test_square_pass_affine(self):
27 | """Test both forward and inverse affine transforms when passed affine obj."""
28 | xform_result = convert_poly_coords(square, affine_obj=aff)
29 | assert xform_result == forward_result
30 | rev_xform_result = convert_poly_coords(square,
31 | affine_obj=aff,
32 | inverse=True)
33 | assert rev_xform_result == reverse_result
34 |
35 | def test_square_pass_raster(self):
36 | """Test forward affine transform when passed a raster reference."""
37 | raster_src = os.path.join(data_dir, 'sample_geotiff.tif')
38 | xform_result = convert_poly_coords(square, raster_src=raster_src)
39 | assert xform_result == forward_result
40 |
41 | def test_square_pass_list(self):
42 | """Test forward and reverse affine transform when passed a list."""
43 | fwd_xform_result = convert_poly_coords(square,
44 | affine_obj=affine_list)
45 | assert fwd_xform_result == forward_result
46 | rev_xform_result = convert_poly_coords(square,
47 | affine_obj=affine_list,
48 | inverse=True)
49 | assert rev_xform_result == reverse_result
50 |
51 | def test_square_pass_gdal_list(self):
52 | """Test forward affine transform when passed a list in gdal order."""
53 | fwd_xform_result = convert_poly_coords(square,
54 | affine_obj=gdal_affine_list
55 | )
56 | assert fwd_xform_result == forward_result
57 |
58 | def test_square_pass_long_list(self):
59 | """Test forward affine transform when passed a full 9-element xform."""
60 | fwd_xform_result = convert_poly_coords(
61 | square, affine_obj=long_affine_list
62 | )
63 | assert fwd_xform_result == forward_result
64 |
65 |
66 | class TestAffineTransformGDF(object):
67 | """Test the affine_transform_gdf functionality."""
68 |
69 | def test_transform_csv(self):
70 | truth_gdf = pd.read_csv(os.path.join(data_dir, 'aff_gdf_result.csv'))
71 | input_df = os.path.join(data_dir, 'sample.csv')
72 | output_gdf = affine_transform_gdf(input_df, aff,
73 | geom_col="PolygonWKT_Pix",
74 | precision=0)
75 | output_gdf['geometry'] = output_gdf['geometry'].apply(dumps, trim=True)
76 | assert output_gdf.equals(truth_gdf)
77 |
78 |
79 | class TestGeoregisterPxDF(object):
80 | """Test the georegister_px_df functionality."""
81 |
82 | def test_transform_using_raster(self):
83 | input_df = os.path.join(data_dir, 'sample.csv')
84 | input_im = os.path.join(data_dir, 'sample_geotiff.tif')
85 | output_gdf = georegister_px_df(input_df, im_fname=input_im,
86 | geom_col='PolygonWKT_Pix', precision=0)
87 | truth_df = pd.read_csv(os.path.join(data_dir, 'aff_gdf_result.csv'))
88 | truth_df['geometry'] = truth_df['geometry'].apply(loads)
89 | truth_gdf = gpd.GeoDataFrame(
90 | truth_df,
91 | crs=rasterio.open(os.path.join(data_dir, 'sample_geotiff.tif')).crs
92 | )
93 |
94 | assert truth_gdf.equals(output_gdf)
95 |
96 | def test_transform_using_aff_crs(self):
97 | input_df = os.path.join(data_dir, 'sample.csv')
98 | crs = rasterio.open(os.path.join(data_dir, 'sample_geotiff.tif')).crs
99 | output_gdf = georegister_px_df(input_df, affine_obj=aff, crs=crs,
100 | geom_col='PolygonWKT_Pix', precision=0)
101 | truth_df = pd.read_csv(os.path.join(data_dir, 'aff_gdf_result.csv'))
102 | truth_df['geometry'] = truth_df['geometry'].apply(loads)
103 | truth_gdf = gpd.GeoDataFrame(
104 | truth_df,
105 | crs=rasterio.open(os.path.join(data_dir, 'sample_geotiff.tif')).crs
106 | )
107 |
108 | assert truth_gdf.equals(output_gdf)
109 |
110 |
111 | class TestGeojsonToPxGDF(object):
112 | """Tests for geojson_to_px_gdf."""
113 |
114 | def test_transform_to_px_coords(self):
115 | output_gdf = geojson_to_px_gdf(
116 | os.path.join(data_dir, 'geotiff_labels.geojson'),
117 | os.path.join(data_dir, 'sample_geotiff.tif'),
118 | precision=0
119 | )
120 | truth_gdf = gpd.read_file(os.path.join(data_dir,
121 | 'gj_to_px_result.geojson'))
122 | truth_subset = truth_gdf[['geometry']]
123 | output_subset = output_gdf[['geometry']].reset_index(drop=True)
124 |
125 | assert truth_subset.equals(output_subset)
126 |
--------------------------------------------------------------------------------