h3-lowpoints 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- h3_lowpoints-0.1.0/.github/workflows/ci.yml +23 -0
- h3_lowpoints-0.1.0/.github/workflows/release.yml +25 -0
- h3_lowpoints-0.1.0/.gitignore +237 -0
- h3_lowpoints-0.1.0/CHANGELOG.md +150 -0
- h3_lowpoints-0.1.0/CITATION.cff +17 -0
- h3_lowpoints-0.1.0/CONTRIBUTING.md +53 -0
- h3_lowpoints-0.1.0/LICENSE +201 -0
- h3_lowpoints-0.1.0/NOTICE +7 -0
- h3_lowpoints-0.1.0/PKG-INFO +143 -0
- h3_lowpoints-0.1.0/README.md +94 -0
- h3_lowpoints-0.1.0/docs/cli.md +181 -0
- h3_lowpoints-0.1.0/docs/concepts.md +204 -0
- h3_lowpoints-0.1.0/docs/data-sources.md +200 -0
- h3_lowpoints-0.1.0/docs/definition.md +172 -0
- h3_lowpoints-0.1.0/docs/getting-started.md +157 -0
- h3_lowpoints-0.1.0/docs/glossary.md +101 -0
- h3_lowpoints-0.1.0/docs/index.md +72 -0
- h3_lowpoints-0.1.0/docs/large-regions.md +275 -0
- h3_lowpoints-0.1.0/docs/limitations.md +111 -0
- h3_lowpoints-0.1.0/docs/map.md +98 -0
- h3_lowpoints-0.1.0/docs/outputs.md +224 -0
- h3_lowpoints-0.1.0/docs/python-api.md +291 -0
- h3_lowpoints-0.1.0/docs/roadmap.md +94 -0
- h3_lowpoints-0.1.0/docs/verification.md +106 -0
- h3_lowpoints-0.1.0/examples/gallery.py +69 -0
- h3_lowpoints-0.1.0/examples/quickstart.py +58 -0
- h3_lowpoints-0.1.0/pyproject.toml +74 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/__init__.py +9 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/cli.py +315 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/clip.py +104 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/context/__init__.py +0 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/context/buildings.py +25 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/context/drains.py +45 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/context/readers.py +49 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/coverage.py +290 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/dem_io.py +39 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/geometry.py +94 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/h3agg/__init__.py +0 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/h3agg/aggregate.py +86 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/hydro/__init__.py +0 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/hydro/depressions.py +154 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/hydro/tiled.py +213 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/pipeline.py +316 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/py.typed +0 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/resolution.py +96 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/sources/__init__.py +0 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/sources/_common.py +179 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/sources/boundaries.py +52 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/sources/copernicus.py +105 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/sources/landcover.py +340 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/sources/rainfall.py +65 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/sources/usgs.py +260 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/store.py +314 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/tiling.py +636 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/verify.py +243 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/viz/__init__.py +0 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/viz/deck.py +78 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/viz/map_template.html +313 -0
- h3_lowpoints-0.1.0/src/h3lowpoints/viz/webmap.py +156 -0
- h3_lowpoints-0.1.0/tests/conftest.py +28 -0
- h3_lowpoints-0.1.0/tests/test_cli.py +33 -0
- h3_lowpoints-0.1.0/tests/test_clip.py +139 -0
- h3_lowpoints-0.1.0/tests/test_context.py +40 -0
- h3_lowpoints-0.1.0/tests/test_copernicus.py +308 -0
- h3_lowpoints-0.1.0/tests/test_coverage.py +362 -0
- h3_lowpoints-0.1.0/tests/test_depressions.py +21 -0
- h3_lowpoints-0.1.0/tests/test_docs.py +111 -0
- h3_lowpoints-0.1.0/tests/test_edge.py +14 -0
- h3_lowpoints-0.1.0/tests/test_fill_reference.py +76 -0
- h3_lowpoints-0.1.0/tests/test_geometry.py +263 -0
- h3_lowpoints-0.1.0/tests/test_import.py +5 -0
- h3_lowpoints-0.1.0/tests/test_lowest_point.py +13 -0
- h3_lowpoints-0.1.0/tests/test_outputs.py +92 -0
- h3_lowpoints-0.1.0/tests/test_pipeline.py +57 -0
- h3_lowpoints-0.1.0/tests/test_resolution.py +234 -0
- h3_lowpoints-0.1.0/tests/test_resume.py +253 -0
- h3_lowpoints-0.1.0/tests/test_sources.py +453 -0
- h3_lowpoints-0.1.0/tests/test_store.py +209 -0
- h3_lowpoints-0.1.0/tests/test_tiling.py +727 -0
- h3_lowpoints-0.1.0/tests/test_verify.py +263 -0
- h3_lowpoints-0.1.0/tests/test_water.py +205 -0
- h3_lowpoints-0.1.0/tests/test_webmap.py +220 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on: [push, pull_request]
|
|
3
|
+
jobs:
|
|
4
|
+
test:
|
|
5
|
+
runs-on: ${{ matrix.os }}
|
|
6
|
+
strategy:
|
|
7
|
+
fail-fast: false
|
|
8
|
+
matrix:
|
|
9
|
+
os: [ubuntu-latest, windows-latest]
|
|
10
|
+
python: ["3.10", "3.12", "3.13", "3.14"]
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with:
|
|
15
|
+
python-version: ${{ matrix.python }}
|
|
16
|
+
- run: python -m pip install --upgrade pip
|
|
17
|
+
- run: pip install -e ".[sources,viz,dev]"
|
|
18
|
+
- run: ruff check src tests
|
|
19
|
+
- run: pytest
|
|
20
|
+
- name: The built wheel contains the map page
|
|
21
|
+
run: |
|
|
22
|
+
pip wheel . --no-deps -w dist
|
|
23
|
+
python -c "import glob, zipfile; names = zipfile.ZipFile(glob.glob('dist/*.whl')[0]).namelist(); assert any(n.endswith('viz/map_template.html') for n in names), names"
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
# Publishing a GitHub release builds the package and uploads it to PyPI through PyPI's trusted
|
|
3
|
+
# publishing: PyPI trusts this workflow, so no password or token is stored anywhere.
|
|
4
|
+
on:
|
|
5
|
+
release:
|
|
6
|
+
types: [published]
|
|
7
|
+
jobs:
|
|
8
|
+
pypi:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
environment: pypi
|
|
11
|
+
permissions:
|
|
12
|
+
id-token: write
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: actions/setup-python@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.12"
|
|
18
|
+
- run: python -m pip install --upgrade build twine
|
|
19
|
+
- run: python -m build
|
|
20
|
+
- run: python -m twine check dist/*
|
|
21
|
+
- name: The version matches the release tag
|
|
22
|
+
run: |
|
|
23
|
+
version=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
24
|
+
test "v$version" = "${GITHUB_REF_NAME}" || { echo "tag ${GITHUB_REF_NAME} but pyproject says $version"; exit 1; }
|
|
25
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
*.lcov
|
|
51
|
+
.hypothesis/
|
|
52
|
+
.pytest_cache/
|
|
53
|
+
cover/
|
|
54
|
+
|
|
55
|
+
# Translations
|
|
56
|
+
*.mo
|
|
57
|
+
*.pot
|
|
58
|
+
|
|
59
|
+
# Django stuff:
|
|
60
|
+
*.log
|
|
61
|
+
local_settings.py
|
|
62
|
+
db.sqlite3
|
|
63
|
+
db.sqlite3-journal
|
|
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
|
+
.pybuilder/
|
|
77
|
+
target/
|
|
78
|
+
|
|
79
|
+
# Jupyter Notebook
|
|
80
|
+
.ipynb_checkpoints
|
|
81
|
+
|
|
82
|
+
# IPython
|
|
83
|
+
profile_default/
|
|
84
|
+
ipython_config.py
|
|
85
|
+
|
|
86
|
+
# pyenv
|
|
87
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
88
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
89
|
+
# .python-version
|
|
90
|
+
|
|
91
|
+
# pipenv
|
|
92
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
93
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
94
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
95
|
+
# install all needed dependencies.
|
|
96
|
+
# Pipfile.lock
|
|
97
|
+
|
|
98
|
+
# uv
|
|
99
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
100
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
101
|
+
# commonly ignored for libraries.
|
|
102
|
+
# uv.lock
|
|
103
|
+
|
|
104
|
+
# poetry
|
|
105
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
106
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
107
|
+
# commonly ignored for libraries.
|
|
108
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
109
|
+
# poetry.lock
|
|
110
|
+
# poetry.toml
|
|
111
|
+
|
|
112
|
+
# pdm
|
|
113
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
114
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
115
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
116
|
+
# pdm.lock
|
|
117
|
+
# pdm.toml
|
|
118
|
+
.pdm-python
|
|
119
|
+
.pdm-build/
|
|
120
|
+
|
|
121
|
+
# pixi
|
|
122
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
123
|
+
# pixi.lock
|
|
124
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
125
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
126
|
+
.pixi/*
|
|
127
|
+
!.pixi/config.toml
|
|
128
|
+
|
|
129
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
130
|
+
__pypackages__/
|
|
131
|
+
|
|
132
|
+
# Celery stuff
|
|
133
|
+
celerybeat-schedule*
|
|
134
|
+
celerybeat.pid
|
|
135
|
+
|
|
136
|
+
# Redis
|
|
137
|
+
*.rdb
|
|
138
|
+
*.aof
|
|
139
|
+
*.pid
|
|
140
|
+
|
|
141
|
+
# RabbitMQ
|
|
142
|
+
mnesia/
|
|
143
|
+
rabbitmq/
|
|
144
|
+
rabbitmq-data/
|
|
145
|
+
|
|
146
|
+
# ActiveMQ
|
|
147
|
+
activemq-data/
|
|
148
|
+
|
|
149
|
+
# SageMath parsed files
|
|
150
|
+
*.sage.py
|
|
151
|
+
|
|
152
|
+
# Environments
|
|
153
|
+
.env
|
|
154
|
+
.envrc
|
|
155
|
+
.venv
|
|
156
|
+
env/
|
|
157
|
+
venv/
|
|
158
|
+
ENV/
|
|
159
|
+
env.bak/
|
|
160
|
+
venv.bak/
|
|
161
|
+
|
|
162
|
+
# Spyder project settings
|
|
163
|
+
.spyderproject
|
|
164
|
+
.spyproject
|
|
165
|
+
|
|
166
|
+
# Rope project settings
|
|
167
|
+
.ropeproject
|
|
168
|
+
|
|
169
|
+
# mkdocs/Zensical documentation
|
|
170
|
+
/site
|
|
171
|
+
|
|
172
|
+
# mypy
|
|
173
|
+
.mypy_cache/
|
|
174
|
+
.dmypy.json
|
|
175
|
+
dmypy.json
|
|
176
|
+
|
|
177
|
+
# Pyre type checker
|
|
178
|
+
.pyre/
|
|
179
|
+
|
|
180
|
+
# pytype static type analyzer
|
|
181
|
+
.pytype/
|
|
182
|
+
|
|
183
|
+
# Cython debug symbols
|
|
184
|
+
cython_debug/
|
|
185
|
+
|
|
186
|
+
# PyCharm
|
|
187
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
188
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
189
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
190
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
191
|
+
# .idea/
|
|
192
|
+
|
|
193
|
+
# Abstra
|
|
194
|
+
# Abstra is an AI-powered process automation framework.
|
|
195
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
196
|
+
# Learn more at https://abstra.io/docs
|
|
197
|
+
.abstra/
|
|
198
|
+
|
|
199
|
+
# Visual Studio Code
|
|
200
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore that
|
|
201
|
+
# can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
202
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer, you
|
|
203
|
+
# could uncomment the following to ignore the entire vscode folder
|
|
204
|
+
# .vscode/
|
|
205
|
+
# Temporary file for partial code execution
|
|
206
|
+
tempCodeRunnerFile.py
|
|
207
|
+
|
|
208
|
+
# Ruff stuff:
|
|
209
|
+
.ruff_cache/
|
|
210
|
+
|
|
211
|
+
# PyPI configuration file
|
|
212
|
+
.pypirc
|
|
213
|
+
|
|
214
|
+
# Marimo
|
|
215
|
+
marimo/_static/
|
|
216
|
+
marimo/_lsp/
|
|
217
|
+
__marimo__/
|
|
218
|
+
|
|
219
|
+
# Streamlit
|
|
220
|
+
.streamlit/secrets.toml
|
|
221
|
+
data_tiles.json
|
|
222
|
+
*.html
|
|
223
|
+
outputs/
|
|
224
|
+
!docs/**/*.html
|
|
225
|
+
|
|
226
|
+
# generated data, downloads and large files (never commit these)
|
|
227
|
+
data/
|
|
228
|
+
*.tif
|
|
229
|
+
|
|
230
|
+
# but keep the demo maps under docs/
|
|
231
|
+
!docs/**/*.html
|
|
232
|
+
|
|
233
|
+
# generated maps
|
|
234
|
+
maps/
|
|
235
|
+
|
|
236
|
+
# the map page is package data and must be committed and shipped
|
|
237
|
+
!src/h3lowpoints/viz/map_template.html
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 (first release; date set when published)
|
|
4
|
+
|
|
5
|
+
The first public release. The API may still change before 1.0.
|
|
6
|
+
|
|
7
|
+
### Direction
|
|
8
|
+
- The library is now a general depression layer on H3 (see [docs/roadmap.md](docs/roadmap.md)).
|
|
9
|
+
The Austin-only waterlogging classes (`drained`, `unexplained`, `suspect_pit`), their thresholds,
|
|
10
|
+
the City of Austin asset reader and the 311 comparison were removed: they rested on one city
|
|
11
|
+
and are not evidence for a global method. The research scripts remain in git history
|
|
12
|
+
(commit `ca4af00`).
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- Depressions are 8-connected, matching the fill. Pits touching at a corner are one depression.
|
|
16
|
+
On a 1 m lidar test DEM this merged 75 of 1,700 depressions; max depths were unchanged.
|
|
17
|
+
- All distances are in metres: the old `radius` (cells) became `drain_radius_m`.
|
|
18
|
+
- Code is organised as `hydro` and `h3agg` (core), `context` (drains, buildings, readers),
|
|
19
|
+
`sources` (USGS 3DEP) and `viz`. `analyze` takes keyword arguments after the DEM path.
|
|
20
|
+
- Drain and building columns appear only when drains or buildings are supplied.
|
|
21
|
+
- The deepest point of a depression is the first tied cell in row-major order.
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
- **`deeper_than_wide` column.** Flags depressions deeper than the side of a square of their own
|
|
25
|
+
area, which are almost always errors in the elevation data (43 of 10.6 million in the national
|
|
26
|
+
3DEP 30 m run, up to 620 m deep); nothing is removed. `verify` checks the flag.
|
|
27
|
+
- **Release automation.** Publishing a GitHub release builds the package and uploads it to PyPI
|
|
28
|
+
through trusted publishing (`.github/workflows/release.yml`); package metadata lists the
|
|
29
|
+
documentation site, changelog and issues, with the SPDX licence expression Apache-2.0.
|
|
30
|
+
- **Clipping a result to a boundary, after the analysis.** `clip.clip_result` and
|
|
31
|
+
`h3lowpoints clip --boundary FILE|us48` keep cells whose centre is inside and depressions whose
|
|
32
|
+
lowest point's cell is kept, in pieces for results of any size; `meta["clip"]` records it and
|
|
33
|
+
`verify` skips only the total comparisons clipping makes inexact. `sources.boundaries.us_lower48`
|
|
34
|
+
reads the Census Bureau's lower-48 outline. The national water-masked layer clipped to the
|
|
35
|
+
lower 48 keeps 10,573,090 depressions and 34,046,636 cells.
|
|
36
|
+
- `pipeline.rollup_saved` rolls up a saved cell table in pieces; `build_map(subtitle=...)`, and maps
|
|
37
|
+
of a roll-up say so instead of calling it the analysis.
|
|
38
|
+
- **A water mask from ESA WorldCover.** `build_water_mask` puts WorldCover's permanent water on an
|
|
39
|
+
elevation raster's own pixel grid (water when at least 5 of 9 sample points are), and
|
|
40
|
+
`h3lowpoints build --water-mask` uses it, so lakes and reservoirs act as open water instead of
|
|
41
|
+
filling as giant depressions (the national run found the Great Lakes as its five largest).
|
|
42
|
+
Checked pixel for pixel against sampling by hand, and on Lake Erie and the Great Salt Lake.
|
|
43
|
+
- **One outlet node per tile.** Cells beside nodata, a mask or open water are outlets whose level
|
|
44
|
+
is their own elevation; they are merged into one node of the border graph per tile, keeping
|
|
45
|
+
each region's lowest crossing, and pass 2 finds the seeds again from the tile instead of
|
|
46
|
+
keeping them. Identical results; with water masked the graph shrank 12 times on western Lake
|
|
47
|
+
Erie and 20 times on Great Salt Lake (now no larger than without the mask), which keeps a
|
|
48
|
+
national run with the mask within a laptop's memory. Checkpoints now record a format version,
|
|
49
|
+
so older ones are refused instead of misread.
|
|
50
|
+
- **The water mask is built tile by tile and much faster.** `build_water_mask(..., "water.vrt")`
|
|
51
|
+
writes one small file per tile holding water, continues where it stopped, and honours `hours`;
|
|
52
|
+
rasters in longitude/latitude are sampled directly instead of through GDAL's warper.
|
|
53
|
+
`build --water-mask` uses it and shares the `--hours` budget with the analysis.
|
|
54
|
+
- **Coverage no longer re-reads the elevation data.** Pass 1 records each tile's invalid pixels
|
|
55
|
+
(compressed, saved with the checkpoints), and partly covered cells are measured from those
|
|
56
|
+
records. With the water mask, where every shoreline cell is partly covered, the Lake Erie box
|
|
57
|
+
went from 72 s to 10 s and the Great Salt Lake box from 75 s to 25 s, with identical results.
|
|
58
|
+
- Converting coordinates reuses one transformer per CRS (building one took 10 ms, which dominated
|
|
59
|
+
coverage measured cell by cell).
|
|
60
|
+
- **Long runs in parts, safe against interruption.** A run written to disk saves every tile's
|
|
61
|
+
result as soon as it is computed (`out_dir/_work`), so after a crash, shutdown or lost
|
|
62
|
+
connection the same call continues where it stopped, redoing at most one tile, with exactly the
|
|
63
|
+
result of an uninterrupted run. `hours=` / `build --hours H` stop cleanly after H hours (exit
|
|
64
|
+
code 3) to split a run into parts. A folder refuses to continue with other settings. Remote reads
|
|
65
|
+
retry ten times with growing delays (at least 17 minutes of lost connection).
|
|
66
|
+
- **Results written to disk as they are produced, in CSV or Parquet.** `analyze_tiled(out_dir=...)`
|
|
67
|
+
keeps its per-cell sums in partition files on disk (one per coarse H3 parent) and writes the
|
|
68
|
+
depression and cell tables piece by piece, so the size of the output no longer limits the size
|
|
69
|
+
of the region; `h3lowpoints build` and `analyze --tile` work this way. `Result.save(format=
|
|
70
|
+
"parquet")`, `--format parquet` and the `parquet` extra add Parquet output; `read_result` loads
|
|
71
|
+
any saved result; `check_saved` and `h3lowpoints verify` check the cell table in pieces.
|
|
72
|
+
- **Complete documentation** in `docs/`, starting at `docs/index.md`: getting started, concepts
|
|
73
|
+
from scratch, every output column and metadata field, the Python API, the command line, data
|
|
74
|
+
sources, large regions, the map, verification, accuracy and limits, and a glossary.
|
|
75
|
+
`tests/test_docs.py` keeps it in step with the code (links, options, functions, columns, fields).
|
|
76
|
+
|
|
77
|
+
### Fixed
|
|
78
|
+
- **A dropped connection while checking which tiles exist stopped a build.** The readers' own
|
|
79
|
+
requests (tile checks, catalogues, small downloads, cached tile downloads) now retry passing
|
|
80
|
+
failures (a reset or dropped connection, a timeout, HTTP 408, 429 and 5xx) after 1 to 128
|
|
81
|
+
seconds, about 4 minutes in all; a 404 still means "missing" at once, and a lasting outage
|
|
82
|
+
still raises. Found when the USGS server reset one of the 1,450 checks of a national build.
|
|
83
|
+
- **H3 ids that look like numbers.** Reading a saved CSV turned resolution-14 ids such as
|
|
84
|
+
`8e4516503458447` (about one in ten thousand) into infinity, corrupting `h3lowpoints map` and
|
|
85
|
+
`verify` on 1 m lidar results. H3 columns are now always read as text, and CSV numbers read back
|
|
86
|
+
exactly (`float_precision="round_trip"`).
|
|
87
|
+
- **A CRS given as text.** `analyze_array(arr, transform, "EPSG:32614")` crashed although any CRS
|
|
88
|
+
pyproj accepts is documented as valid.
|
|
89
|
+
- Empty tables keep their column types (integers, booleans, text) instead of becoming text.
|
|
90
|
+
|
|
91
|
+
### Added (earlier in this release)
|
|
92
|
+
- **A third of the memory for large regions.** `analyze_tiled` releases each tile once it is
|
|
93
|
+
summarised and assembles the cell table with sorted arrays instead of pandas groupby; coverage
|
|
94
|
+
is measured in chunks with a summed-area table; GDAL's read cache is capped at 256 MB. The 3DEP
|
|
95
|
+
Texas box (1.82 Gpx) now peaks at 3.6 GB extra instead of 11.0 GB, with identical output.
|
|
96
|
+
- **Regions of any size and general verification.** `build_3dep_vrt` writes a virtual raster over
|
|
97
|
+
3DEP tiles (each pixel from its own tile, optional resumable tile cache), and `h3lowpoints build`
|
|
98
|
+
runs it through the exact tiled analysis. `verify.check_result` / `h3lowpoints verify` test
|
|
99
|
+
invariants that hold for every correct result, and `spot_check` re-runs random windows of any
|
|
100
|
+
DEM tiled and whole; `build` runs both and writes `verify.json`. Its first run on real data found
|
|
101
|
+
coverage values of 1 + 2e-16 from float rounding, now clamped. See docs/verification.md.
|
|
102
|
+
- **US data readers.** `fetch_3dep` (USGS 3DEP bare earth, 30 m and 10 m), `fetch_lidar` (3DEP 1 m;
|
|
103
|
+
newest survey wins, other UTM zones reprojected), `fetch_impervious` (NLCD percent impervious),
|
|
104
|
+
`fetch_worldcover` (global 10 m land cover) and `design_rainfall` (NOAA Atlas 14), with the CLI
|
|
105
|
+
commands `fetch-dem --from ...`, `fetch-landcover` and `rainfall`. They read only the parts of
|
|
106
|
+
cloud-optimised files a box needs and use only the standard library and rasterio; the old
|
|
107
|
+
untested `sources/dem.py` and the `requests` dependency are gone.
|
|
108
|
+
Found while testing on real data: neighbouring 3DEP tiles' 6-pixel overlaps differ by
|
|
109
|
+
centimetres, so each pixel is now taken from the tile whose own cell holds it; and NLCD tags 0%
|
|
110
|
+
impervious as "no data", which would have erased every park and field.
|
|
111
|
+
- **Finest resolution by default.** `res="auto"` (now the default, also `--res auto`) picks the finest
|
|
112
|
+
H3 resolution the DEM supports (14 for 1 m lidar, 10 for 30 m). Every depression carries
|
|
113
|
+
`h3_lowpoint_15`, its deepest point as a resolution-15 cell. Cells gain `n_lowpoints` and
|
|
114
|
+
`depressed_share`; `resolution_guide(dem)` explains each resolution for a DEM; the map opens
|
|
115
|
+
resolutions 0 to 3 on the share and can colour cells by the number of depressions.
|
|
116
|
+
- **Exact tiled analysis.** `analyze_tiled` now equals `analyze` for any tile size (Barnes 2016: two
|
|
117
|
+
passes joined by a graph over the tile-border cells), including depressions that span many
|
|
118
|
+
tiles. The earlier buffer-based version could silently split one large depression into several
|
|
119
|
+
pieces; the `buffer` option and the `level_uncertain` flag are gone because they are no longer
|
|
120
|
+
needed. Measured on 38.9 Mpx of real Copernicus terrain: identical results, 30 s against 28 s,
|
|
121
|
+
0.3 GB against 1.4 GB of memory. Checked on the Dutch coast, Death Valley, the Alps and Norway.
|
|
122
|
+
- **Geographic coordinates.** DEMs in latitude/longitude are analyzed natively with exact WGS 84
|
|
123
|
+
pixel areas, and projected DEMs in feet work too. Drain distances are geodesic.
|
|
124
|
+
- **Copernicus GLO-30.** `h3lowpoints fetch-dem` and `sources.copernicus.fetch_dem` read the global
|
|
125
|
+
30 m DEM from AWS open data.
|
|
126
|
+
- **Interactive map.** `Result.to_html`, `h3lowpoints map` and `analyze --map` write one
|
|
127
|
+
self-contained HTML page: 3D H3 cells over a basemap, every resolution from the analysis one down,
|
|
128
|
+
several measures, and each depression's lowest point. `examples/gallery.py` builds five real
|
|
129
|
+
places. A headless-browser test fails on any console error.
|
|
130
|
+
- **Coverage.** Every cell has `coverage` and `covered_area_m2`, the share of the cell the DEM
|
|
131
|
+
covers, exact against a brute-force oracle and identical between whole and tiled runs
|
|
132
|
+
(about 2 to 3 s extra on 38.9 Mpx).
|
|
133
|
+
- **Mask.** `analyze(mask=...)` and `analyze_tiled(mask_path=...)` ignore cells such as open water.
|
|
134
|
+
- `Result.at_resolution` and `rollup`: exact roll-up to coarser H3 resolutions.
|
|
135
|
+
- `Result.to_deck` and `viz.deck.cells_to_geojson`: 3D map and GeoJSON export.
|
|
136
|
+
- Resolution tiers, warnings for DEMs too coarse for the requested analysis, `reproject_to_utm`
|
|
137
|
+
and `--reproject`.
|
|
138
|
+
- Tests: fill against an independent method, tiled against whole (with mutation checks),
|
|
139
|
+
geographic areas against an independent ellipsoid implementation, vectorized against loop.
|
|
140
|
+
|
|
141
|
+
### Fixed
|
|
142
|
+
- Tiled runs could drop depressions with a flat floor, because the tie-break for the deepest
|
|
143
|
+
cell depended on the window.
|
|
144
|
+
- The `edge` flag did not fire for depressions cut off by the raster border.
|
|
145
|
+
- Reprojecting a DEM to UTM blurred sharp depression rims and inflated areas; geographic DEMs
|
|
146
|
+
are now analyzed without resampling.
|
|
147
|
+
|
|
148
|
+
### Performance
|
|
149
|
+
- Region summaries run over depression pixels only (about 25% faster on a 13-megapixel DEM);
|
|
150
|
+
the drain check no longer loops over drains.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use this software, please cite it as below."
|
|
3
|
+
title: "h3-lowpoints"
|
|
4
|
+
type: software
|
|
5
|
+
authors:
|
|
6
|
+
- family-names: "Patel"
|
|
7
|
+
given-names: "Ujjawal"
|
|
8
|
+
repository-code: "https://github.com/UJJAWAL-01/h3-lowpoints"
|
|
9
|
+
license: Apache-2.0
|
|
10
|
+
version: "0.1.0"
|
|
11
|
+
keywords:
|
|
12
|
+
- H3
|
|
13
|
+
- DEM
|
|
14
|
+
- depressions
|
|
15
|
+
- hydrology
|
|
16
|
+
- GIS
|
|
17
|
+
abstract: "Find closed depressions in elevation models and map them on H3 cells at any resolution."
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for helping. The aim is a small library people can trust, so correctness and honesty
|
|
4
|
+
about limits matter more than features.
|
|
5
|
+
|
|
6
|
+
## Setup
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
git clone https://github.com/UJJAWAL-01/h3-lowpoints
|
|
10
|
+
cd h3-lowpoints
|
|
11
|
+
pip install -e ".[sources,viz,dev]"
|
|
12
|
+
pytest
|
|
13
|
+
ruff check src tests
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
CI runs both on Linux and Windows. The first call compiles the numba fill; later runs use a cache.
|
|
17
|
+
Tests marked `network` read real data and drive a headless browser; run them with
|
|
18
|
+
`H3LP_NETWORK=1 pytest` (they need internet, and Chrome or Edge for the map test).
|
|
19
|
+
|
|
20
|
+
## Ground rules
|
|
21
|
+
|
|
22
|
+
- **Tests with every change.** Prefer properties (tiled equals whole, vectorized equals the
|
|
23
|
+
simple loop, fill equals the reference method) over single examples. A bug fix needs a test
|
|
24
|
+
that failed before it.
|
|
25
|
+
- **Say what is validated.** The depression core is checked against an independent method.
|
|
26
|
+
Anything beyond that must state its evidence and its limits in `docs/`. One city is not
|
|
27
|
+
evidence for a global method.
|
|
28
|
+
- **Parameters in metres**, never cells, so settings mean the same at any resolution.
|
|
29
|
+
- **Keep layers separate.** `hydro/` and `h3agg/` are the general core; `context/` (drains,
|
|
30
|
+
buildings) is optional; `sources/` holds data downloaders for specific places. `hydro/` and `h3agg/` must not import the others; only `pipeline.py` wires the layers together.
|
|
31
|
+
- **Document what you add.** The documentation in `docs/` is published with the library, so a
|
|
32
|
+
new parameter, column, command option or metadata field needs its line there.
|
|
33
|
+
`tests/test_docs.py` fails when a command option, API function, output column or metadata field
|
|
34
|
+
is missing from the docs, or when a link or anchor is broken.
|
|
35
|
+
- Research scripts and exploratory notebooks do not belong in this repository.
|
|
36
|
+
|
|
37
|
+
## Good first contributions
|
|
38
|
+
|
|
39
|
+
- A `sources/` adapter for another country or city (DEM, drains, buildings).
|
|
40
|
+
- A `sources/` reader for another open DEM (national lidar programs, FABDEM, NASADEM), or a
|
|
41
|
+
water-mask source to pair with it. Follow `sources/usgs.py`: read only the windows a box needs,
|
|
42
|
+
treat only HTTP 404 as "missing" (`_common.url_exists`), and test against fake tiles that copy the
|
|
43
|
+
real layout, plus a `network` test against the real service.
|
|
44
|
+
- Validation of the planned susceptibility index against local flood records, including negative results.
|
|
45
|
+
- Test terrain from varied places: karst, glacial potholes, flat coast, steep hills.
|
|
46
|
+
- A hierarchical version of the exact tiling for continental rasters (the border graph is the
|
|
47
|
+
last part of memory that grows with the region). Compare against `analyze`, as
|
|
48
|
+
`tests/test_tiling.py` does.
|
|
49
|
+
- A vertical-units option for DEMs whose heights are in feet (see docs/limitations.md).
|
|
50
|
+
|
|
51
|
+
## Reporting a problem
|
|
52
|
+
|
|
53
|
+
Open an issue with the DEM's cell size and CRS, the call you made, and the `meta.json`.
|