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.
Files changed (82) hide show
  1. h3_lowpoints-0.1.0/.github/workflows/ci.yml +23 -0
  2. h3_lowpoints-0.1.0/.github/workflows/release.yml +25 -0
  3. h3_lowpoints-0.1.0/.gitignore +237 -0
  4. h3_lowpoints-0.1.0/CHANGELOG.md +150 -0
  5. h3_lowpoints-0.1.0/CITATION.cff +17 -0
  6. h3_lowpoints-0.1.0/CONTRIBUTING.md +53 -0
  7. h3_lowpoints-0.1.0/LICENSE +201 -0
  8. h3_lowpoints-0.1.0/NOTICE +7 -0
  9. h3_lowpoints-0.1.0/PKG-INFO +143 -0
  10. h3_lowpoints-0.1.0/README.md +94 -0
  11. h3_lowpoints-0.1.0/docs/cli.md +181 -0
  12. h3_lowpoints-0.1.0/docs/concepts.md +204 -0
  13. h3_lowpoints-0.1.0/docs/data-sources.md +200 -0
  14. h3_lowpoints-0.1.0/docs/definition.md +172 -0
  15. h3_lowpoints-0.1.0/docs/getting-started.md +157 -0
  16. h3_lowpoints-0.1.0/docs/glossary.md +101 -0
  17. h3_lowpoints-0.1.0/docs/index.md +72 -0
  18. h3_lowpoints-0.1.0/docs/large-regions.md +275 -0
  19. h3_lowpoints-0.1.0/docs/limitations.md +111 -0
  20. h3_lowpoints-0.1.0/docs/map.md +98 -0
  21. h3_lowpoints-0.1.0/docs/outputs.md +224 -0
  22. h3_lowpoints-0.1.0/docs/python-api.md +291 -0
  23. h3_lowpoints-0.1.0/docs/roadmap.md +94 -0
  24. h3_lowpoints-0.1.0/docs/verification.md +106 -0
  25. h3_lowpoints-0.1.0/examples/gallery.py +69 -0
  26. h3_lowpoints-0.1.0/examples/quickstart.py +58 -0
  27. h3_lowpoints-0.1.0/pyproject.toml +74 -0
  28. h3_lowpoints-0.1.0/src/h3lowpoints/__init__.py +9 -0
  29. h3_lowpoints-0.1.0/src/h3lowpoints/cli.py +315 -0
  30. h3_lowpoints-0.1.0/src/h3lowpoints/clip.py +104 -0
  31. h3_lowpoints-0.1.0/src/h3lowpoints/context/__init__.py +0 -0
  32. h3_lowpoints-0.1.0/src/h3lowpoints/context/buildings.py +25 -0
  33. h3_lowpoints-0.1.0/src/h3lowpoints/context/drains.py +45 -0
  34. h3_lowpoints-0.1.0/src/h3lowpoints/context/readers.py +49 -0
  35. h3_lowpoints-0.1.0/src/h3lowpoints/coverage.py +290 -0
  36. h3_lowpoints-0.1.0/src/h3lowpoints/dem_io.py +39 -0
  37. h3_lowpoints-0.1.0/src/h3lowpoints/geometry.py +94 -0
  38. h3_lowpoints-0.1.0/src/h3lowpoints/h3agg/__init__.py +0 -0
  39. h3_lowpoints-0.1.0/src/h3lowpoints/h3agg/aggregate.py +86 -0
  40. h3_lowpoints-0.1.0/src/h3lowpoints/hydro/__init__.py +0 -0
  41. h3_lowpoints-0.1.0/src/h3lowpoints/hydro/depressions.py +154 -0
  42. h3_lowpoints-0.1.0/src/h3lowpoints/hydro/tiled.py +213 -0
  43. h3_lowpoints-0.1.0/src/h3lowpoints/pipeline.py +316 -0
  44. h3_lowpoints-0.1.0/src/h3lowpoints/py.typed +0 -0
  45. h3_lowpoints-0.1.0/src/h3lowpoints/resolution.py +96 -0
  46. h3_lowpoints-0.1.0/src/h3lowpoints/sources/__init__.py +0 -0
  47. h3_lowpoints-0.1.0/src/h3lowpoints/sources/_common.py +179 -0
  48. h3_lowpoints-0.1.0/src/h3lowpoints/sources/boundaries.py +52 -0
  49. h3_lowpoints-0.1.0/src/h3lowpoints/sources/copernicus.py +105 -0
  50. h3_lowpoints-0.1.0/src/h3lowpoints/sources/landcover.py +340 -0
  51. h3_lowpoints-0.1.0/src/h3lowpoints/sources/rainfall.py +65 -0
  52. h3_lowpoints-0.1.0/src/h3lowpoints/sources/usgs.py +260 -0
  53. h3_lowpoints-0.1.0/src/h3lowpoints/store.py +314 -0
  54. h3_lowpoints-0.1.0/src/h3lowpoints/tiling.py +636 -0
  55. h3_lowpoints-0.1.0/src/h3lowpoints/verify.py +243 -0
  56. h3_lowpoints-0.1.0/src/h3lowpoints/viz/__init__.py +0 -0
  57. h3_lowpoints-0.1.0/src/h3lowpoints/viz/deck.py +78 -0
  58. h3_lowpoints-0.1.0/src/h3lowpoints/viz/map_template.html +313 -0
  59. h3_lowpoints-0.1.0/src/h3lowpoints/viz/webmap.py +156 -0
  60. h3_lowpoints-0.1.0/tests/conftest.py +28 -0
  61. h3_lowpoints-0.1.0/tests/test_cli.py +33 -0
  62. h3_lowpoints-0.1.0/tests/test_clip.py +139 -0
  63. h3_lowpoints-0.1.0/tests/test_context.py +40 -0
  64. h3_lowpoints-0.1.0/tests/test_copernicus.py +308 -0
  65. h3_lowpoints-0.1.0/tests/test_coverage.py +362 -0
  66. h3_lowpoints-0.1.0/tests/test_depressions.py +21 -0
  67. h3_lowpoints-0.1.0/tests/test_docs.py +111 -0
  68. h3_lowpoints-0.1.0/tests/test_edge.py +14 -0
  69. h3_lowpoints-0.1.0/tests/test_fill_reference.py +76 -0
  70. h3_lowpoints-0.1.0/tests/test_geometry.py +263 -0
  71. h3_lowpoints-0.1.0/tests/test_import.py +5 -0
  72. h3_lowpoints-0.1.0/tests/test_lowest_point.py +13 -0
  73. h3_lowpoints-0.1.0/tests/test_outputs.py +92 -0
  74. h3_lowpoints-0.1.0/tests/test_pipeline.py +57 -0
  75. h3_lowpoints-0.1.0/tests/test_resolution.py +234 -0
  76. h3_lowpoints-0.1.0/tests/test_resume.py +253 -0
  77. h3_lowpoints-0.1.0/tests/test_sources.py +453 -0
  78. h3_lowpoints-0.1.0/tests/test_store.py +209 -0
  79. h3_lowpoints-0.1.0/tests/test_tiling.py +727 -0
  80. h3_lowpoints-0.1.0/tests/test_verify.py +263 -0
  81. h3_lowpoints-0.1.0/tests/test_water.py +205 -0
  82. 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`.