From cbc34b021a0505fe8a2e08b7f2ae5f97356122c1 Mon Sep 17 00:00:00 2001 From: rehsani Date: Sun, 3 May 2026 20:40:44 -0700 Subject: [PATCH] Restructure as installable floodpath package Renames the project from "flood_inundation" to "floodpath" and prepares a v0.1.0 release suitable for PyPI: - Move dem/, hydrology/, exposure/, damage/ under floodpath/ namespace so users get safe, project-prefixed imports (no "from dem import ...") - Rewrite all cross-package imports to floodpath.. - Update tests/conftest.py, fixture generators and the pytest config so the same suite runs against the new package - Add LICENSE (MIT), README.md (badges, install, quickstart, citations), and a [project] / [build-system] block in pyproject.toml - Move per-user caches from ~/.cache/flood_inundation/* to ~/.cache/floodpath/* and update OSM User-Agent + GHSL/WorldPop docstrings accordingly - Rename the conda env (1c_setup_env.sh) and the GHA workflow's micromamba environment from "flood_inundation" to "floodpath" - Add smoke_test.py and .claude/ to .gitignore The GitHub repo has been renamed in parallel (rehsani/flood_inundation -> rehsani/floodpath); badge and project URLs updated to match. 122 tests still pass (offline + integration). --- .github/workflows/tests.yml | 4 +- .gitignore | 6 + 1c_setup_env.sh | 2 +- LICENSE | 21 +++ README.md | 121 ++++++++++++++++++ floodpath/__init__.py | 3 + {damage => floodpath/damage}/__init__.py | 0 {damage => floodpath/damage}/compute.py | 2 +- {damage => floodpath/damage}/constants.py | 0 {damage => floodpath/damage}/curves.py | 0 {damage => floodpath/damage}/inundation.py | 2 +- {damage => floodpath/damage}/models.py | 2 +- {dem => floodpath/dem}/__init__.py | 0 {dem => floodpath/dem}/constants.py | 0 {dem => floodpath/dem}/fetcher.py | 0 {dem => floodpath/dem}/models.py | 0 {dem => floodpath/dem}/utils.py | 0 {exposure => floodpath/exposure}/__init__.py | 0 {exposure => floodpath/exposure}/constants.py | 6 +- {exposure => floodpath/exposure}/ghsl.py | 6 +- {exposure => floodpath/exposure}/models.py | 2 +- {exposure => floodpath/exposure}/osm.py | 4 +- {exposure => floodpath/exposure}/utils.py | 2 +- {exposure => floodpath/exposure}/worldpop.py | 6 +- .../hydrology}/__init__.py | 0 {hydrology => floodpath/hydrology}/basins.py | 0 .../hydrology}/constants.py | 0 {hydrology => floodpath/hydrology}/flow.py | 2 +- {hydrology => floodpath/hydrology}/hand.py | 2 +- {hydrology => floodpath/hydrology}/models.py | 2 +- {hydrology => floodpath/hydrology}/streams.py | 0 {hydrology => floodpath/hydrology}/utils.py | 0 main.py | 2 +- pyproject.toml | 43 +++++++ tests/conftest.py | 12 +- tests/damage/test_compute.py | 12 +- tests/damage/test_curves.py | 2 +- tests/damage/test_inundation.py | 4 +- tests/dem/test_fetcher.py | 4 +- tests/dem/test_fixture_regression.py | 4 +- tests/dem/test_models.py | 2 +- tests/dem/test_utils.py | 6 +- tests/exposure/test_fixture_regression.py | 4 +- tests/exposure/test_ghsl.py | 2 +- tests/exposure/test_models.py | 2 +- tests/exposure/test_osm.py | 6 +- tests/exposure/test_utils.py | 6 +- tests/exposure/test_worldpop.py | 6 +- tests/fixtures/_generate_robit_bata.py | 2 +- tests/fixtures/_generate_robit_bata_ghsl.py | 2 +- tests/fixtures/_generate_robit_bata_osm.py | 6 +- .../fixtures/_generate_robit_bata_worldpop.py | 2 +- tests/hydrology/test_basins.py | 4 +- tests/hydrology/test_flow.py | 6 +- tests/hydrology/test_hand.py | 2 +- tests/hydrology/test_models.py | 2 +- tests/hydrology/test_streams.py | 4 +- tests/hydrology/test_utils.py | 2 +- 58 files changed, 269 insertions(+), 75 deletions(-) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 floodpath/__init__.py rename {damage => floodpath/damage}/__init__.py (100%) rename {damage => floodpath/damage}/compute.py (98%) rename {damage => floodpath/damage}/constants.py (100%) rename {damage => floodpath/damage}/curves.py (100%) rename {damage => floodpath/damage}/inundation.py (96%) rename {damage => floodpath/damage}/models.py (96%) rename {dem => floodpath/dem}/__init__.py (100%) rename {dem => floodpath/dem}/constants.py (100%) rename {dem => floodpath/dem}/fetcher.py (100%) rename {dem => floodpath/dem}/models.py (100%) rename {dem => floodpath/dem}/utils.py (100%) rename {exposure => floodpath/exposure}/__init__.py (100%) rename {exposure => floodpath/exposure}/constants.py (90%) rename {exposure => floodpath/exposure}/ghsl.py (96%) rename {exposure => floodpath/exposure}/models.py (97%) rename {exposure => floodpath/exposure}/osm.py (98%) rename {exposure => floodpath/exposure}/utils.py (98%) rename {exposure => floodpath/exposure}/worldpop.py (97%) rename {hydrology => floodpath/hydrology}/__init__.py (100%) rename {hydrology => floodpath/hydrology}/basins.py (100%) rename {hydrology => floodpath/hydrology}/constants.py (100%) rename {hydrology => floodpath/hydrology}/flow.py (96%) rename {hydrology => floodpath/hydrology}/hand.py (97%) rename {hydrology => floodpath/hydrology}/models.py (98%) rename {hydrology => floodpath/hydrology}/streams.py (100%) rename {hydrology => floodpath/hydrology}/utils.py (100%) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 00d1958..097e8b6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -20,13 +20,13 @@ jobs: - name: Set up micromamba environment uses: mamba-org/setup-micromamba@v2 with: - environment-name: flood_inundation + environment-name: floodpath create-args: >- python=3.12 init-shell: bash - name: Install conda packages - run: micromamba install -y -n flood_inundation -c conda-forge --file 1a_conda_requirements.txt + run: micromamba install -y -n floodpath -c conda-forge --file 1a_conda_requirements.txt - name: Install pip packages run: pip install -r 1b_pip_requirements.txt diff --git a/.gitignore b/.gitignore index 15bd81f..f35b67f 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,12 @@ htmlcov/ # Smoke-test outputs plots/ +# Local-only experimentation script (not part of the published package). +smoke_test.py + +# Per-machine Claude Code settings. +.claude/ + # Local virtual envs (the project uses a named conda env, not a local one, # but guard against accidental commits if a contributor sets up differently). .venv/ diff --git a/1c_setup_env.sh b/1c_setup_env.sh index 15b3067..0d1e18f 100755 --- a/1c_setup_env.sh +++ b/1c_setup_env.sh @@ -3,7 +3,7 @@ set -e -ENV_NAME="flood_inundation" +ENV_NAME="floodpath" PYTHON_VERSION="3.12.3" SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" CONDA_REQUIREMENTS="${SCRIPT_DIR}/1a_conda_requirements.txt" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5249af1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Reza Ehsani + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c846926 --- /dev/null +++ b/README.md @@ -0,0 +1,121 @@ +# floodpath + +[![tests](https://github.com/rehsani/floodpath/actions/workflows/tests.yml/badge.svg)](https://github.com/rehsani/floodpath/actions/workflows/tests.yml) +[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) + +**A modular Python pipeline for HAND-based flood inundation and damage estimation.** + +`floodpath` chains together everything you need to go from a `(lat, lon)` point to a per-cell flood damage estimate: + +``` +DEM → flow direction → streams → HAND + ↓ + + GHSL built-up + WorldPop + OSM buildings + ↓ + + JRC Huizinga 2017 depth-damage curves + ↓ + → 2D damage map +``` + +Each layer is a small, well-tested module. Plug in the parts you need, swap in your own data, or extend with new sources. + +## Install + +```bash +pip install floodpath +``` + +`floodpath` depends on `rasterio` and `pyflwdir`, both of which install cleanly via pip on Linux. On macOS arm64, conda-forge is the smoother path: + +```bash +conda install -c conda-forge rasterio pyflwdir numpy +pip install floodpath +``` + +## Quickstart + +```python +from floodpath.dem import get_dem +from floodpath.hydrology import build_flow_grid, extract_streams, compute_hand +from floodpath.exposure import get_ghsl_built +from floodpath.damage import ( + JRC_AFRICA_RESIDENTIAL, + compute_inundation_depth, + compute_damage, +) + +# 1. Fetch a DEM patch (Copernicus GLO-30, ~30 m, no auth) +dem = get_dem(lat=11.805, lon=37.5625, buffer_deg=0.0375) + +# 2. Terrain hydrology +grid = build_flow_grid(dem) +streams = extract_streams(grid, threshold=200) +hand = compute_hand(grid, streams, dem) + +# 3. Exposure (GHS-BUILT-S, ~90 m built-up surface per cell) +exposure = get_ghsl_built(lat=11.805, lon=37.5625, buffer_deg=0.0375, epoch=2020) + +# 4. Damage at a 5 m water level +depth = compute_inundation_depth(hand, water_level=5.0) +damage = compute_damage(depth, exposure, JRC_AFRICA_RESIDENTIAL) + +print(f"Total damaged built-up: {damage.values.sum():,.0f} m²") +``` + +## Modules + +| Module | Source | What it provides | +|---|---|---| +| `floodpath.dem` | Copernicus GLO-30 (AWS Open Data, COG) | Elevation patch around any (lat, lon) | +| `floodpath.hydrology` | derived from DEM via `pyflwdir` | Flow direction + accumulation, stream networks (with Strahler order), basin delineation, HAND | +| `floodpath.exposure` | GHSL R2023A, WorldPop, OpenStreetMap (Overpass) | Built-up surface, population, building footprints | +| `floodpath.damage` | JRC Huizinga 2017 + DEM/HAND/GHSL | Per-cell flood depth and damage in m² of built-up surface | + +## Depth-damage curves + +`floodpath.damage` ships **26 continental-average curves** from JRC's Huizinga et al. 2017 *Global flood depth-damage functions* report — covering residential, commerce, industry, transport, infrastructure and agriculture asset classes across up to six continents. + +```python +from floodpath.damage import jrc_curve + +curve = jrc_curve(asset_class="residential", continent="north_america") +fractions = curve(depths_m=np.array([0.0, 0.5, 1.0, 2.0, 5.0])) +``` + +Coverage gaps from the original report are preserved: `jrc_curve("commerce", "africa")` raises `KeyError` rather than fabricating data. + +## Test fixtures and offline development + +`floodpath` ships with a small set of committed test fixtures (Robit Bata watershed, northern Ethiopia) so contributors can iterate without hitting the network: + +```bash +pytest -m "not integration" # ~0.1 s, no network +pytest # full suite, ~1 minute (downloads ~25 MB) +``` + +The fixtures (committed binaries totalling ~330 KB) are regenerated by scripts under `tests/fixtures/_generate_*.py` whenever an upstream source changes. + +## Status + +`floodpath` is **alpha**. The pipeline produces sensible flood/damage maps for static water-level scenarios, but does not yet model: + +- Time-resolved hydraulics (no Saint-Venant solver) +- Rainfall → runoff routing (planned for v0.2) +- Soil moisture / infiltration (planned for v0.2) +- 2D shallow-water dynamics (not planned yet!) + +If you need those, look at [LISFLOOD-FP](https://www.bristol.ac.uk/geography/research/hydrology/models/lisflood/), [HEC-RAS 2D](https://www.hec.usace.army.mil/software/hec-ras/), or [WFlow](https://github.com/Deltares/Wflow.jl). + +## Citation + +If you use `floodpath` in academic work, please cite the underlying datasets too: + +- **DEM**: Copernicus DEM GLO-30, ESA / Airbus, doi:10.5270/ESA-c5d3d65 +- **Built-up surface**: GHSL Data Package 2023, JRC, doi:10.2760/098587 +- **Population**: WorldPop, University of Southampton, doi:10.5258/SOTON/WP00674 +- **Damage curves**: Huizinga, J., de Moel, H. and Szewczyk, W. (2017). *Global flood depth-damage functions: Methodology and the database with guidelines.* JRC Technical Report EUR 28552 EN, doi:10.2760/16510 + +## License + +MIT — see [LICENSE](LICENSE). diff --git a/floodpath/__init__.py b/floodpath/__init__.py new file mode 100644 index 0000000..0ff9125 --- /dev/null +++ b/floodpath/__init__.py @@ -0,0 +1,3 @@ +"""floodpath — modular Python pipeline for HAND-based flood inundation and damage estimation.""" + +__version__ = "0.1.0" diff --git a/damage/__init__.py b/floodpath/damage/__init__.py similarity index 100% rename from damage/__init__.py rename to floodpath/damage/__init__.py diff --git a/damage/compute.py b/floodpath/damage/compute.py similarity index 98% rename from damage/compute.py rename to floodpath/damage/compute.py index daabb6a..af886aa 100644 --- a/damage/compute.py +++ b/floodpath/damage/compute.py @@ -2,7 +2,7 @@ import numpy as np -from exposure.models import ExposureGrid +from floodpath.exposure.models import ExposureGrid from .constants import RATIO_TOLERANCE from .curves import DepthDamageCurve diff --git a/damage/constants.py b/floodpath/damage/constants.py similarity index 100% rename from damage/constants.py rename to floodpath/damage/constants.py diff --git a/damage/curves.py b/floodpath/damage/curves.py similarity index 100% rename from damage/curves.py rename to floodpath/damage/curves.py diff --git a/damage/inundation.py b/floodpath/damage/inundation.py similarity index 96% rename from damage/inundation.py rename to floodpath/damage/inundation.py index fcf6897..910ea26 100644 --- a/damage/inundation.py +++ b/floodpath/damage/inundation.py @@ -2,7 +2,7 @@ import numpy as np -from hydrology.models import Hand +from floodpath.hydrology.models import Hand from .models import InundationDepth diff --git a/damage/models.py b/floodpath/damage/models.py similarity index 96% rename from damage/models.py rename to floodpath/damage/models.py index fd65f3b..1abbb50 100644 --- a/damage/models.py +++ b/floodpath/damage/models.py @@ -5,7 +5,7 @@ import numpy as np from rasterio.transform import Affine -from dem.models import BoundingBox +from floodpath.dem.models import BoundingBox @dataclass diff --git a/dem/__init__.py b/floodpath/dem/__init__.py similarity index 100% rename from dem/__init__.py rename to floodpath/dem/__init__.py diff --git a/dem/constants.py b/floodpath/dem/constants.py similarity index 100% rename from dem/constants.py rename to floodpath/dem/constants.py diff --git a/dem/fetcher.py b/floodpath/dem/fetcher.py similarity index 100% rename from dem/fetcher.py rename to floodpath/dem/fetcher.py diff --git a/dem/models.py b/floodpath/dem/models.py similarity index 100% rename from dem/models.py rename to floodpath/dem/models.py diff --git a/dem/utils.py b/floodpath/dem/utils.py similarity index 100% rename from dem/utils.py rename to floodpath/dem/utils.py diff --git a/exposure/__init__.py b/floodpath/exposure/__init__.py similarity index 100% rename from exposure/__init__.py rename to floodpath/exposure/__init__.py diff --git a/exposure/constants.py b/floodpath/exposure/constants.py similarity index 90% rename from exposure/constants.py rename to floodpath/exposure/constants.py index d363311..37f2726 100644 --- a/exposure/constants.py +++ b/floodpath/exposure/constants.py @@ -30,7 +30,7 @@ GHSL_UNITS: str = "m^2 of built-up surface per cell" # Per-user cache directory; siblings to other potential exposure sources later. -DEFAULT_CACHE_DIR: Path = Path.home() / ".cache" / "flood_inundation" / "ghsl" +DEFAULT_CACHE_DIR: Path = Path.home() / ".cache" / "floodpath" / "ghsl" # --- WorldPop (constrained 100 m population, R2020 maxar_v1) --------------- @@ -49,7 +49,7 @@ # windowed reads are not viable. Download whole country files (~tens of MB) # on first hit and read windows from the local cache. WORLDPOP_CACHE_DIR: Path = ( - Path.home() / ".cache" / "flood_inundation" / "worldpop" + Path.home() / ".cache" / "floodpath" / "worldpop" ) @@ -58,4 +58,4 @@ OVERPASS_URL: str = "https://overpass-api.de/api/interpreter" OSM_BUFFER_DEG: float = 0.1 OSM_DEFAULT_TIMEOUT_S: int = 60 -OSM_USER_AGENT: str = "flood_inundation/0.1 (research; geospatial pipeline)" +OSM_USER_AGENT: str = "floodpath/0.1 (research; geospatial pipeline)" diff --git a/exposure/ghsl.py b/floodpath/exposure/ghsl.py similarity index 96% rename from exposure/ghsl.py rename to floodpath/exposure/ghsl.py index 0f140a8..df8ae4f 100644 --- a/exposure/ghsl.py +++ b/floodpath/exposure/ghsl.py @@ -9,8 +9,8 @@ import rasterio from rasterio.merge import merge -from dem.models import BoundingBox -from dem.utils import bbox_from_point +from floodpath.dem.models import BoundingBox +from floodpath.dem.utils import bbox_from_point from .constants import ( DEFAULT_CACHE_DIR, @@ -84,7 +84,7 @@ def get_ghsl_built( buffer_deg: Half-width of the square bbox around the point, degrees. epoch: Year of the GHSL product (one of GHSL_AVAILABLE_EPOCHS). cache_dir: Where to store extracted tiles. Defaults to - ~/.cache/flood_inundation/ghsl/. + ~/.cache/floodpath/ghsl/. Returns: ExposureGrid carrying the m^2 built-up array, georef, and metadata. diff --git a/exposure/models.py b/floodpath/exposure/models.py similarity index 97% rename from exposure/models.py rename to floodpath/exposure/models.py index 80229cb..1a3cf94 100644 --- a/exposure/models.py +++ b/floodpath/exposure/models.py @@ -6,7 +6,7 @@ import numpy as np from rasterio.transform import Affine -from dem.models import BoundingBox +from floodpath.dem.models import BoundingBox @dataclass diff --git a/exposure/osm.py b/floodpath/exposure/osm.py similarity index 98% rename from exposure/osm.py rename to floodpath/exposure/osm.py index 87e5758..a7fcccc 100644 --- a/exposure/osm.py +++ b/floodpath/exposure/osm.py @@ -7,8 +7,8 @@ from collections.abc import Iterable from types import MappingProxyType -from dem.models import BoundingBox -from dem.utils import bbox_from_point +from floodpath.dem.models import BoundingBox +from floodpath.dem.utils import bbox_from_point from .constants import ( OSM_BUFFER_DEG, diff --git a/exposure/utils.py b/floodpath/exposure/utils.py similarity index 98% rename from exposure/utils.py rename to floodpath/exposure/utils.py index ac7a3bb..347ae2e 100644 --- a/exposure/utils.py +++ b/floodpath/exposure/utils.py @@ -2,7 +2,7 @@ import math -from dem.models import BoundingBox +from floodpath.dem.models import BoundingBox from .constants import ( GHSL_BASE_URL, diff --git a/exposure/worldpop.py b/floodpath/exposure/worldpop.py similarity index 97% rename from exposure/worldpop.py rename to floodpath/exposure/worldpop.py index a67a13c..b3c3159 100644 --- a/exposure/worldpop.py +++ b/floodpath/exposure/worldpop.py @@ -9,8 +9,8 @@ import rasterio from rasterio.windows import from_bounds, transform as window_transform -from dem.models import BoundingBox -from dem.utils import bbox_from_point +from floodpath.dem.models import BoundingBox +from floodpath.dem.utils import bbox_from_point from .constants import ( WORLDPOP_AVAILABLE_YEARS, @@ -106,7 +106,7 @@ def get_worldpop_population( buffer_deg: Half-width of the square bbox around the point, degrees. year: One of WORLDPOP_AVAILABLE_YEARS (2000-2020 inclusive). cache_dir: Where to store country files. Defaults to - ~/.cache/flood_inundation/worldpop/. + ~/.cache/floodpath/worldpop/. Returns: ExposureGrid with population per cell. diff --git a/hydrology/__init__.py b/floodpath/hydrology/__init__.py similarity index 100% rename from hydrology/__init__.py rename to floodpath/hydrology/__init__.py diff --git a/hydrology/basins.py b/floodpath/hydrology/basins.py similarity index 100% rename from hydrology/basins.py rename to floodpath/hydrology/basins.py diff --git a/hydrology/constants.py b/floodpath/hydrology/constants.py similarity index 100% rename from hydrology/constants.py rename to floodpath/hydrology/constants.py diff --git a/hydrology/flow.py b/floodpath/hydrology/flow.py similarity index 96% rename from hydrology/flow.py rename to floodpath/hydrology/flow.py index 9e19f42..0877d48 100644 --- a/hydrology/flow.py +++ b/floodpath/hydrology/flow.py @@ -2,7 +2,7 @@ import pyflwdir -from dem.models import DEM +from floodpath.dem.models import DEM from .constants import DEM_NODATA_SENTINEL from .models import FlowGrid diff --git a/hydrology/hand.py b/floodpath/hydrology/hand.py similarity index 97% rename from hydrology/hand.py rename to floodpath/hydrology/hand.py index 39e8cb5..2c62768 100644 --- a/hydrology/hand.py +++ b/floodpath/hydrology/hand.py @@ -1,6 +1,6 @@ """Compute Height Above Nearest Drainage (HAND) per cell.""" -from dem.models import DEM +from floodpath.dem.models import DEM from .models import FlowGrid, Hand, StreamNetwork diff --git a/hydrology/models.py b/floodpath/hydrology/models.py similarity index 98% rename from hydrology/models.py rename to floodpath/hydrology/models.py index e747eb5..1cfd528 100644 --- a/hydrology/models.py +++ b/floodpath/hydrology/models.py @@ -6,7 +6,7 @@ from pyflwdir import FlwdirRaster from rasterio.transform import Affine -from dem.models import BoundingBox +from floodpath.dem.models import BoundingBox @dataclass diff --git a/hydrology/streams.py b/floodpath/hydrology/streams.py similarity index 100% rename from hydrology/streams.py rename to floodpath/hydrology/streams.py diff --git a/hydrology/utils.py b/floodpath/hydrology/utils.py similarity index 100% rename from hydrology/utils.py rename to floodpath/hydrology/utils.py diff --git a/main.py b/main.py index bb1d8ac..00397ed 100644 --- a/main.py +++ b/main.py @@ -4,7 +4,7 @@ import numpy as np -from dem import get_dem +from floodpath.dem import get_dem def _summarize_dem(lat: float, lon: float) -> None: diff --git a/pyproject.toml b/pyproject.toml index 9cd9852..46d3fae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,46 @@ +[build-system] +requires = ["setuptools>=68.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "floodpath" +version = "0.1.0" +description = "Modular Python pipeline for HAND-based flood inundation and damage estimation." +readme = "README.md" +requires-python = ">=3.10" +license = "MIT" +license-files = ["LICENSE"] +authors = [ + { name = "Reza Ehsani", email = "rezaa.ehsaani@gmail.com" }, +] +keywords = ["flood", "hydrology", "geospatial", "DEM", "HAND", "inundation", "damage", "remote-sensing"] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Science/Research", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Scientific/Engineering :: GIS", + "Topic :: Scientific/Engineering :: Hydrology", + "Typing :: Typed", +] +dependencies = [ + "numpy>=1.24", + "rasterio>=1.3", + "pyflwdir>=0.5", +] + +[project.urls] +Homepage = "https://github.com/rehsani/floodpath" +Repository = "https://github.com/rehsani/floodpath" +Issues = "https://github.com/rehsani/floodpath/issues" + +[tool.setuptools.packages.find] +where = ["."] +include = ["floodpath*"] + [tool.pytest.ini_options] testpaths = ["tests"] pythonpath = ["."] diff --git a/tests/conftest.py b/tests/conftest.py index f2dee12..7019248 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,12 +8,12 @@ import json -from dem import get_dem -from dem.models import DEM, BoundingBox -from exposure import parse_overpass_response -from exposure.models import BuildingCollection, ExposureGrid -from hydrology import build_flow_grid, compute_hand, extract_streams -from hydrology.models import FlowGrid, Hand, StreamNetwork +from floodpath.dem import get_dem +from floodpath.dem.models import DEM, BoundingBox +from floodpath.exposure import parse_overpass_response +from floodpath.exposure.models import BuildingCollection, ExposureGrid +from floodpath.hydrology import build_flow_grid, compute_hand, extract_streams +from floodpath.hydrology.models import FlowGrid, Hand, StreamNetwork ROBIT_BATA_STREAM_THRESHOLD: int = 200 ROBIT_BATA_GHSL_FIXTURE: Path = ( diff --git a/tests/damage/test_compute.py b/tests/damage/test_compute.py index 5b55bf6..34e85cf 100644 --- a/tests/damage/test_compute.py +++ b/tests/damage/test_compute.py @@ -4,16 +4,16 @@ import pytest from rasterio.transform import from_bounds -from damage import ( +from floodpath.damage import ( JRC_AFRICA_RESIDENTIAL, compute_damage, compute_inundation_depth, ) -from damage.compute import _upsample_preserving_total -from damage.models import InundationDepth -from dem.models import BoundingBox -from exposure.models import ExposureGrid -from hydrology.models import Hand +from floodpath.damage.compute import _upsample_preserving_total +from floodpath.damage.models import InundationDepth +from floodpath.dem.models import BoundingBox +from floodpath.exposure.models import ExposureGrid +from floodpath.hydrology.models import Hand class TestUpsamplePreservingTotal: diff --git a/tests/damage/test_curves.py b/tests/damage/test_curves.py index 4664bb3..8e7cf46 100644 --- a/tests/damage/test_curves.py +++ b/tests/damage/test_curves.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from damage.curves import ( +from floodpath.damage.curves import ( JRC_AFRICA_RESIDENTIAL, JRC_ASIA_RESIDENTIAL, JRC_ASSET_CLASSES, diff --git a/tests/damage/test_inundation.py b/tests/damage/test_inundation.py index a201174..de066f2 100644 --- a/tests/damage/test_inundation.py +++ b/tests/damage/test_inundation.py @@ -2,8 +2,8 @@ import pytest -from damage import compute_inundation_depth -from hydrology.models import Hand +from floodpath.damage import compute_inundation_depth +from floodpath.hydrology.models import Hand class TestComputeInundationDepth: diff --git a/tests/dem/test_fetcher.py b/tests/dem/test_fetcher.py index f8f526f..002ba04 100644 --- a/tests/dem/test_fetcher.py +++ b/tests/dem/test_fetcher.py @@ -3,8 +3,8 @@ import numpy as np import pytest -from dem import get_dem -from dem.models import DEM +from floodpath.dem import get_dem +from floodpath.dem.models import DEM @pytest.mark.integration diff --git a/tests/dem/test_fixture_regression.py b/tests/dem/test_fixture_regression.py index 5f153d9..4ad4752 100644 --- a/tests/dem/test_fixture_regression.py +++ b/tests/dem/test_fixture_regression.py @@ -8,8 +8,8 @@ import numpy as np import pytest -from dem import get_dem -from dem.models import DEM +from floodpath.dem import get_dem +from floodpath.dem.models import DEM from tests.fixtures._generate_robit_bata import ( FIXTURE_BUFFER_DEG, FIXTURE_LAT, diff --git a/tests/dem/test_models.py b/tests/dem/test_models.py index 277f112..882a081 100644 --- a/tests/dem/test_models.py +++ b/tests/dem/test_models.py @@ -6,7 +6,7 @@ import pytest from rasterio.transform import from_bounds -from dem.models import DEM, BoundingBox +from floodpath.dem.models import DEM, BoundingBox class TestBoundingBox: diff --git a/tests/dem/test_utils.py b/tests/dem/test_utils.py index 4dd48a7..8a1475c 100644 --- a/tests/dem/test_utils.py +++ b/tests/dem/test_utils.py @@ -2,9 +2,9 @@ import pytest -from dem.constants import COPERNICUS_DEM_BASE_URL -from dem.models import BoundingBox -from dem.utils import bbox_from_point, copernicus_tile_id, copernicus_tile_url, tiles_for_bbox +from floodpath.dem.constants import COPERNICUS_DEM_BASE_URL +from floodpath.dem.models import BoundingBox +from floodpath.dem.utils import bbox_from_point, copernicus_tile_id, copernicus_tile_url, tiles_for_bbox class TestBboxFromPoint: diff --git a/tests/exposure/test_fixture_regression.py b/tests/exposure/test_fixture_regression.py index 061fc6d..5daa461 100644 --- a/tests/exposure/test_fixture_regression.py +++ b/tests/exposure/test_fixture_regression.py @@ -10,8 +10,8 @@ import numpy as np import pytest -from exposure import get_ghsl_built -from exposure.models import ExposureGrid +from floodpath.exposure import get_ghsl_built +from floodpath.exposure.models import ExposureGrid from tests.fixtures._generate_robit_bata_ghsl import ( FIXTURE_BUFFER_DEG, FIXTURE_EPOCH, diff --git a/tests/exposure/test_ghsl.py b/tests/exposure/test_ghsl.py index 22b6094..026e8c3 100644 --- a/tests/exposure/test_ghsl.py +++ b/tests/exposure/test_ghsl.py @@ -4,7 +4,7 @@ import pytest -from exposure import get_ghsl_built +from floodpath.exposure import get_ghsl_built class TestGetGhslBuiltOffline: diff --git a/tests/exposure/test_models.py b/tests/exposure/test_models.py index dd5445d..889f5eb 100644 --- a/tests/exposure/test_models.py +++ b/tests/exposure/test_models.py @@ -1,6 +1,6 @@ """Unit tests for exposure.models.""" -from exposure.models import ExposureGrid +from floodpath.exposure.models import ExposureGrid class TestExposureGrid: diff --git a/tests/exposure/test_osm.py b/tests/exposure/test_osm.py index 5e845dd..e4775ad 100644 --- a/tests/exposure/test_osm.py +++ b/tests/exposure/test_osm.py @@ -4,9 +4,9 @@ import pytest -from exposure import get_osm_buildings, parse_overpass_response -from exposure.models import BuildingCollection, BuildingFootprint -from exposure.osm import _polygon_area_m2 +from floodpath.exposure import get_osm_buildings, parse_overpass_response +from floodpath.exposure.models import BuildingCollection, BuildingFootprint +from floodpath.exposure.osm import _polygon_area_m2 class TestPolygonAreaApproximation: diff --git a/tests/exposure/test_utils.py b/tests/exposure/test_utils.py index 3effce2..d2ed187 100644 --- a/tests/exposure/test_utils.py +++ b/tests/exposure/test_utils.py @@ -1,8 +1,8 @@ """Unit tests for exposure.utils.""" -from dem.models import BoundingBox -from exposure.constants import GHSL_BASE_URL -from exposure.utils import ( +from floodpath.dem.models import BoundingBox +from floodpath.exposure.constants import GHSL_BASE_URL +from floodpath.exposure.utils import ( ghsl_tile_filename, ghsl_tile_id, ghsl_tile_url, diff --git a/tests/exposure/test_worldpop.py b/tests/exposure/test_worldpop.py index ba77733..22bdebd 100644 --- a/tests/exposure/test_worldpop.py +++ b/tests/exposure/test_worldpop.py @@ -4,9 +4,9 @@ import pytest -from exposure import get_worldpop_population -from exposure.models import ExposureGrid -from exposure.worldpop import worldpop_country_filename, worldpop_country_url +from floodpath.exposure import get_worldpop_population +from floodpath.exposure.models import ExposureGrid +from floodpath.exposure.worldpop import worldpop_country_filename, worldpop_country_url class TestWorldPopUrlBuilder: diff --git a/tests/fixtures/_generate_robit_bata.py b/tests/fixtures/_generate_robit_bata.py index a521f3e..47589a5 100644 --- a/tests/fixtures/_generate_robit_bata.py +++ b/tests/fixtures/_generate_robit_bata.py @@ -20,7 +20,7 @@ import rasterio from rasterio.profiles import default_gtiff_profile -from dem import get_dem +from floodpath.dem import get_dem FIXTURE_PATH: Path = Path(__file__).resolve().parent / "robit_bata.tif" FIXTURE_LAT: float = 11.805 diff --git a/tests/fixtures/_generate_robit_bata_ghsl.py b/tests/fixtures/_generate_robit_bata_ghsl.py index e433f4d..28337e3 100644 --- a/tests/fixtures/_generate_robit_bata_ghsl.py +++ b/tests/fixtures/_generate_robit_bata_ghsl.py @@ -20,7 +20,7 @@ import rasterio from rasterio.profiles import default_gtiff_profile -from exposure import get_ghsl_built +from floodpath.exposure import get_ghsl_built FIXTURE_PATH: Path = Path(__file__).resolve().parent / "robit_bata_ghsl.tif" FIXTURE_LAT: float = 11.805 diff --git a/tests/fixtures/_generate_robit_bata_osm.py b/tests/fixtures/_generate_robit_bata_osm.py index 62c2518..ae83458 100644 --- a/tests/fixtures/_generate_robit_bata_osm.py +++ b/tests/fixtures/_generate_robit_bata_osm.py @@ -20,9 +20,9 @@ import urllib.request -from dem.utils import bbox_from_point -from exposure.constants import OSM_USER_AGENT, OVERPASS_URL -from exposure.osm import _build_overpass_query +from floodpath.dem.utils import bbox_from_point +from floodpath.exposure.constants import OSM_USER_AGENT, OVERPASS_URL +from floodpath.exposure.osm import _build_overpass_query FIXTURE_PATH: Path = Path(__file__).resolve().parent / "robit_bata_osm.json" FIXTURE_LAT: float = 11.805 diff --git a/tests/fixtures/_generate_robit_bata_worldpop.py b/tests/fixtures/_generate_robit_bata_worldpop.py index e55b479..c4a0cb1 100644 --- a/tests/fixtures/_generate_robit_bata_worldpop.py +++ b/tests/fixtures/_generate_robit_bata_worldpop.py @@ -18,7 +18,7 @@ import rasterio from rasterio.profiles import default_gtiff_profile -from exposure import get_worldpop_population +from floodpath.exposure import get_worldpop_population FIXTURE_PATH: Path = Path(__file__).resolve().parent / "robit_bata_worldpop.tif" FIXTURE_LAT: float = 11.805 diff --git a/tests/hydrology/test_basins.py b/tests/hydrology/test_basins.py index bb1c95a..a3e8d6c 100644 --- a/tests/hydrology/test_basins.py +++ b/tests/hydrology/test_basins.py @@ -3,8 +3,8 @@ import numpy as np from rasterio.transform import xy -from hydrology import delineate_basin -from hydrology.models import FlowGrid +from floodpath.hydrology import delineate_basin +from floodpath.hydrology.models import FlowGrid class TestDelineateBasin: diff --git a/tests/hydrology/test_flow.py b/tests/hydrology/test_flow.py index 23b276c..ba9fb9e 100644 --- a/tests/hydrology/test_flow.py +++ b/tests/hydrology/test_flow.py @@ -2,9 +2,9 @@ import numpy as np -from dem.models import DEM -from hydrology import build_flow_grid -from hydrology.models import FlowGrid +from floodpath.dem.models import DEM +from floodpath.hydrology import build_flow_grid +from floodpath.hydrology.models import FlowGrid class TestBuildFlowGrid: diff --git a/tests/hydrology/test_hand.py b/tests/hydrology/test_hand.py index e0af010..84b5a17 100644 --- a/tests/hydrology/test_hand.py +++ b/tests/hydrology/test_hand.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from hydrology.models import Hand, StreamNetwork +from floodpath.hydrology.models import Hand, StreamNetwork class TestComputeHand: diff --git a/tests/hydrology/test_models.py b/tests/hydrology/test_models.py index f6fb1b0..e28e197 100644 --- a/tests/hydrology/test_models.py +++ b/tests/hydrology/test_models.py @@ -3,7 +3,7 @@ import numpy as np from rasterio.transform import from_bounds -from hydrology.models import Basin +from floodpath.hydrology.models import Basin class TestBasin: diff --git a/tests/hydrology/test_streams.py b/tests/hydrology/test_streams.py index 68f0b11..4747f2b 100644 --- a/tests/hydrology/test_streams.py +++ b/tests/hydrology/test_streams.py @@ -2,8 +2,8 @@ import numpy as np -from hydrology import extract_streams -from hydrology.models import FlowGrid +from floodpath.hydrology import extract_streams +from floodpath.hydrology.models import FlowGrid class TestExtractStreams: diff --git a/tests/hydrology/test_utils.py b/tests/hydrology/test_utils.py index 1d78e61..519878a 100644 --- a/tests/hydrology/test_utils.py +++ b/tests/hydrology/test_utils.py @@ -3,7 +3,7 @@ import numpy as np from rasterio.transform import from_bounds -from hydrology.utils import lonlat_to_rowcol, replace_nan_with_sentinel +from floodpath.hydrology.utils import lonlat_to_rowcol, replace_nan_with_sentinel class TestReplaceNanWithSentinel: