[GRASS-git] [OSGeo/grass-addons] 6b92b3: r.richdem: add collection of 7 terrain-analysis mo...

Andy Wickert noreply at github.com
Sat May 30 09:40:46 PDT 2026


  Branch: refs/heads/grass8
  Home:   https://github.com/OSGeo/grass-addons
  Commit: 6b92b3683d38e4bda357b11ae56563b8ba60d0ce
      https://github.com/OSGeo/grass-addons/commit/6b92b3683d38e4bda357b11ae56563b8ba60d0ce
  Author: Andy Wickert <andy at northernwidget.com>
  Date:   2026-05-30 (Sat, 30 May 2026)

  Changed paths:
    R src/raster/r.richdem.breachdepressions/CMakeLists.txt
    R src/raster/r.richdem.breachdepressions/Makefile
    R src/raster/r.richdem.breachdepressions/r.richdem.breachdepressions.html
    R src/raster/r.richdem.breachdepressions/r.richdem.breachdepressions.md
    R src/raster/r.richdem.breachdepressions/r.richdem.breachdepressions.py
    R src/raster/r.richdem.filldepressions/CMakeLists.txt
    R src/raster/r.richdem.filldepressions/Makefile
    R src/raster/r.richdem.filldepressions/r.richdem.filldepressions.html
    R src/raster/r.richdem.filldepressions/r.richdem.filldepressions.md
    R src/raster/r.richdem.filldepressions/r.richdem.filldepressions.py
    R src/raster/r.richdem.flowaccumulation/CMakeLists.txt
    R src/raster/r.richdem.flowaccumulation/Makefile
    R src/raster/r.richdem.flowaccumulation/r.richdem.flowaccumulation.html
    R src/raster/r.richdem.flowaccumulation/r.richdem.flowaccumulation.md
    R src/raster/r.richdem.flowaccumulation/r.richdem.flowaccumulation.py
    R src/raster/r.richdem.resolveflats/CMakeLists.txt
    R src/raster/r.richdem.resolveflats/Makefile
    R src/raster/r.richdem.resolveflats/r.richdem.resolveflats.html
    R src/raster/r.richdem.resolveflats/r.richdem.resolveflats.md
    R src/raster/r.richdem.resolveflats/r.richdem.resolveflats.py
    R src/raster/r.richdem.terrainattribute/CMakeLists.txt
    R src/raster/r.richdem.terrainattribute/Makefile
    R src/raster/r.richdem.terrainattribute/r.richdem.terrainattribute.html
    R src/raster/r.richdem.terrainattribute/r.richdem.terrainattribute.md
    R src/raster/r.richdem.terrainattribute/r.richdem.terrainattribute.py
    A src/raster/r.richdem/CITATION.cff
    A src/raster/r.richdem/CMakeLists.txt
    A src/raster/r.richdem/Makefile
    A src/raster/r.richdem/README.md
    A src/raster/r.richdem/librichdem/CMakeLists.txt
    A src/raster/r.richdem/librichdem/Makefile
    A src/raster/r.richdem/librichdem/__init__.py
    A src/raster/r.richdem/librichdem/raster.py
    A src/raster/r.richdem/librichdem/vector.py
    A src/raster/r.richdem/r.richdem.breachdepressions/CMakeLists.txt
    A src/raster/r.richdem/r.richdem.breachdepressions/Makefile
    A src/raster/r.richdem/r.richdem.breachdepressions/r.richdem.breachdepressions.html
    A src/raster/r.richdem/r.richdem.breachdepressions/r.richdem.breachdepressions.md
    A src/raster/r.richdem/r.richdem.breachdepressions/r.richdem.breachdepressions.py
    A src/raster/r.richdem/r.richdem.breachdepressions/testsuite/test_r_richdem_breachdepressions.py
    A src/raster/r.richdem/r.richdem.dephier/CMakeLists.txt
    A src/raster/r.richdem/r.richdem.dephier/Makefile
    A src/raster/r.richdem/r.richdem.dephier/esurf-8-431-2020-f01.png
    A src/raster/r.richdem/r.richdem.dephier/esurf-8-431-2020-f09.png
    A src/raster/r.richdem/r.richdem.dephier/esurf-9-105-2021-f02.png
    A src/raster/r.richdem/r.richdem.dephier/r.richdem.dephier.html
    A src/raster/r.richdem/r.richdem.dephier/r.richdem.dephier.md
    A src/raster/r.richdem/r.richdem.dephier/r.richdem.dephier.py
    A src/raster/r.richdem/r.richdem.dephier/testsuite/test_r_richdem_dephier.py
    A src/raster/r.richdem/r.richdem.filldepressions/CMakeLists.txt
    A src/raster/r.richdem/r.richdem.filldepressions/Makefile
    A src/raster/r.richdem/r.richdem.filldepressions/r.richdem.filldepressions.html
    A src/raster/r.richdem/r.richdem.filldepressions/r.richdem.filldepressions.md
    A src/raster/r.richdem/r.richdem.filldepressions/r.richdem.filldepressions.py
    A src/raster/r.richdem/r.richdem.filldepressions/testsuite/test_r_richdem_filldepressions.py
    A src/raster/r.richdem/r.richdem.flowaccumulation/CMakeLists.txt
    A src/raster/r.richdem/r.richdem.flowaccumulation/Makefile
    A src/raster/r.richdem/r.richdem.flowaccumulation/r.richdem.flowaccumulation.html
    A src/raster/r.richdem/r.richdem.flowaccumulation/r.richdem.flowaccumulation.md
    A src/raster/r.richdem/r.richdem.flowaccumulation/r.richdem.flowaccumulation.py
    A src/raster/r.richdem/r.richdem.flowaccumulation/testsuite/test_r_richdem_flowaccumulation.py
    A src/raster/r.richdem/r.richdem.fsm/CMakeLists.txt
    A src/raster/r.richdem/r.richdem.fsm/Makefile
    A src/raster/r.richdem/r.richdem.fsm/esurf-9-105-2021-f04.png
    A src/raster/r.richdem/r.richdem.fsm/r.richdem.fsm.html
    A src/raster/r.richdem/r.richdem.fsm/r.richdem.fsm.md
    A src/raster/r.richdem/r.richdem.fsm/r.richdem.fsm.py
    A src/raster/r.richdem/r.richdem.fsm/testsuite/test_r_richdem_fsm.py
    A src/raster/r.richdem/r.richdem.html
    A src/raster/r.richdem/r.richdem.md
    A src/raster/r.richdem/r.richdem.py
    A src/raster/r.richdem/r.richdem.resolveflats/CMakeLists.txt
    A src/raster/r.richdem/r.richdem.resolveflats/Makefile
    A src/raster/r.richdem/r.richdem.resolveflats/r.richdem.resolveflats.html
    A src/raster/r.richdem/r.richdem.resolveflats/r.richdem.resolveflats.md
    A src/raster/r.richdem/r.richdem.resolveflats/r.richdem.resolveflats.py
    A src/raster/r.richdem/r.richdem.resolveflats/testsuite/test_r_richdem_resolveflats.py
    A src/raster/r.richdem/r.richdem.terrainattribute/CMakeLists.txt
    A src/raster/r.richdem/r.richdem.terrainattribute/Makefile
    A src/raster/r.richdem/r.richdem.terrainattribute/r.richdem.terrainattribute.html
    A src/raster/r.richdem/r.richdem.terrainattribute/r.richdem.terrainattribute.md
    A src/raster/r.richdem/r.richdem.terrainattribute/r.richdem.terrainattribute.py
    A src/raster/r.richdem/r.richdem.terrainattribute/testsuite/test_r_richdem_terrainattribute.py

  Log Message:
  -----------
  r.richdem: add collection of 7 terrain-analysis modules wrapping RichDEM (#1700)

* Initial commit

* Add collection Makefile and librichdem shared library

Establishes the r.richdem collection scaffold. librichdem provides
shared GRASS↔RichDEM conversion utilities (raster.py, vector.py)
installed to $GISBASE/etc/r.richdem/librichdem/ and found at runtime
via grass.pygrass.utils.get_lib_path(). depressions_to_grass in
vector.py is stubbed pending implementation.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Add simple raster modules (fill, breach, resolveflats, flowaccum, terrain)

Each module wraps the corresponding RichDEM Python function:
- r.richdem.fill: FillDepressions (epsilon flag, D8/D4 topology)
- r.richdem.breach: BreachDepressions (D8/D4 topology)
- r.richdem.resolveflats: ResolveFlats
- r.richdem.flowaccum: FlowAccumulation (13 methods, optional exponent and weights)
- r.richdem.terrain: TerrainAttribute (slope, aspect, curvature variants, zscale)

All modules load librichdem via get_lib_path() for GRASS↔rdarray conversion.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Add depression hierarchy modules (dephier, fsm)

r.richdem.dephier wraps get_depression_hierarchy(), outputting a labels
raster, flow directions raster, and a two-layer GRASS vector map encoding
the full depression hierarchy (depressions_to_grass in librichdem/vector.py,
currently stubbed).

r.richdem.fsm wraps fill_spill_merge() as a single step — no internal time
loop, since FSM is path-independent for uniform static input. Reads the
depression hierarchy vector map, runs FSM, writes updated water depth and
water_vol back to GRASS.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Implement depressions_to_grass and fix round-trip sentinel handling

depressions_to_grass builds the two-layer GRASS vector map:
- Step 1: writes labels raster, nulls OCEAN cells, runs r.to.vect
  to create leaf depression area polygons
- Step 2: adds metadepression points at out_cell (the saddle) via
  VectorTopo with cat=dep_label
- Step 3: adds schema columns via v.db.addcolumn
- Step 4: UPDATEs leaf rows (exist from r.to.vect) and INSERTs meta
  rows by dep_label; NO_VALUE sentinels stored as NULL, inf as NULL
- Step 5: creates ocean_links junction table and connects as layer 2

Also fixes depressions_from_grass and update_water_vol to use the
actual table name from gs.vector_db() rather than hardcoded
"depressions". Fixes NULL→sentinel round-trip: NULL restores to
_NO_VALUE for uint32 fields and float("inf") for elevation fields
so FSM receives correct values on reconstruction.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Fix raster I/O API and librichdem import paths

Three bugs found during testing of r.richdem.fill:

1. raster.py: gs.raster_info() has no null_value key and gs.array
   does not exist. Rewrite using grass.pygrass.raster.RasterRow for
   both reading and writing. GRASS nulls (NaN for DCELL) are mapped
   to/from a fixed _NO_DATA sentinel (-9999).

2. All modules: get_lib_path() returns the parent directory
   (etc/r.richdem/), not the librichdem/ subdirectory. Imports must
   use `from librichdem.raster import ...` not `from raster import ...`.

3. librichdem/vector.py: same import path fix for its internal call
   to rdarray_to_grass.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Fix vector.py: lazy richdem imports, correct cat assignment for metadepressions, deduplicate dep_label rows on read

- Move `from _richdem.depression_hierarchy import ...` inside functions to
  avoid triggering richdem's rasterio import at module load time (which
  crashes under system Python due to libstdc++ ABI conflict with anaconda)
- Use direct `_richdem.depression_hierarchy` rather than `richdem.depression_hierarchy`
  to bypass richdem's __init__.py entirely
- Fix metadepression cat assignment: query MAX(cat) from the table after
  r.to.vect and assign sequential cats above that, so metadep dep_labels
  don't collide with the sequential leaf cats r.to.vect assigns
- Remove `layer=1` kwarg from VectorTopo.write() which doesn't accept it
- depressions_from_grass: GROUP BY dep_label so multi-patch leaf depressions
  (where r.to.vect creates multiple polygons per dep_label) produce one
  Depression object per dep_label

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Fix r.richdem.fsm: cast labels→uint32 and flowdirs→int8 after reading from GRASS

GRASS stores all rasters as DCELL (float64); fill_spill_merge requires
labels as Array2D_uint32_t and flowdirs as Array2D_int8_t. Cast them to
the correct dtypes after rdarray_from_grass before wrapping for C++.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Add HTML documentation for all seven r.richdem modules

References Barnes et al. (2020, ESurf) for the depression hierarchy and
Barnes et al. (2021, ESurf) for Fill-Spill-Merge; includes per-method
references for all flow accumulation algorithms and terrain attributes.
Style follows GRASS r.watershed and r.stream.extract conventions.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Fix AUTHORS attribution and standardize references across all HTML docs

- All modules: algorithm credit to Richard Barnes (RichDEM library);
  GRASS GIS bindings credited to Andrew D. Wickert with Claude Sonnet 4.6
- r.richdem.dephier, r.richdem.fsm: list all three paper authors
  (Barnes, Callaghan, Wickert) before the bindings note
- Add Barnes (2016) general RichDEM software citation to every module
- Reformat all references to GRASS GIS style:
  Author (Year). Title. Journal Vol X(Y), pp pages. DOI: number.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Add CC-BY figures from ESurf 2020/2021 to dephier and fsm man pages

Embeds four figures with captions and attribution in the HTML documentation:
- r.richdem.dephier: ESurf 2020 Fig 1 (binary-tree forest schematic),
  ESurf 2021 Fig 2 (depression terminology/nesting), and ESurf 2020 Fig 9
  (Madagascar leaf/top/filtered label output).
- r.richdem.fsm: ESurf 2021 Fig 4 (three-stage FSM algorithm overview).

Also stores all downloaded figures in figures/ subdirectories for reference.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Add README, CITATION.cff, and fix module keyword headers

- README.md: project overview, module table, both workflows (classic
  flow-accumulation and Fill-Spill-Merge), dependency/build instructions,
  references, and license/authors section.
- CITATION.cff: CFF 1.2.0 with author ORCID, AI contributor note,
  and references to the two ESurf papers and RichDEM software.
- Fix keyword mismatch: add 'flow direction' to r.richdem.dephier and
  r.richdem.flowaccum; add 'depression' and 'water table' to r.richdem.fsm,
  bringing .py headers into sync with the HTML keyword sections.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Add Lindsay (2016) citation to r.richdem.breach; update README and CITATION.cff

The RichDEM source (Lindsay2016.hpp) cites Lindsay (2016) directly in the
RDLOG_CITATION macro. Updates:
- r.richdem.breach.html: name the complete-breaching algorithm, add Lindsay
  (2016) to REFERENCES, credit Lindsay and Barnes separately in AUTHORS.
- README.md: add Lindsay (2016) to the references section.
- CITATION.cff: add Lindsay (2016) as a referenced article.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Expand CITATION.cff with all algorithm references

Adds Barnes et al. 2014a (Priority-Flood filling), 2014b (flat resolution),
O'Callaghan & Mark 1984, Fairfield & Leymarie 1991, Freeman 1991,
Quinn et al. 1991, Holmgren 1994, Tarboton 1997 (flow accumulation),
Horn 1981, and Zevenbergen & Thorne 1987 (terrain attributes) to the
references block, grouped with comments by functional area.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Expand r.richdem.breach man page with algorithm detail from Lindsay (2016)

Add three-phase algorithm description (pit shallowing, priority-flood
traversal with back-links, complete-breaching output), note that only
the complete-breaching mode (not SB/CB hybrid modes) is implemented,
performance figure (~87% of fill time), and clearer guidance on when
to breach versus fill or use Fill-Spill-Merge. Simplify NOTES to cover
only the flat-handling point that is not already in DESCRIPTION.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Add gunittest test suite for r.richdem.fill

Five tests covering: output creation, pit raised to pour-point
elevation, no cell lowered by filling, epsilon flag removes flats,
and D4 topology option. Suite is skipped gracefully when the
RichDEM _richdem extension is not available.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Add gunittest test suite for r.richdem.dephier; fix flowdirs HTML

Ten tests covering: output existence (labels, flowdirs, hierarchy
vector), depression label > 0, OCEAN label == 0 (min of DCELL
raster), flowdirs in [0, 8] (8 = pit cell), leaf depression present,
dep_vol > 0, expected schema columns, and layer-2 ocean_links present.

Also corrects the man page: pit cells in the flow-direction raster
are stored as integer value 8, not as GRASS null.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Add gunittest test suite for r.richdem.breach

Four tests: output creation, max elevation unchanged (borders untouched),
pit raised above its input elevation by the shallowing phase, and D4
topology option. Suite is skipped gracefully without the RichDEM extension.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Add gunittest test suite for r.richdem.resolveflats

Four tests: output creation, no null cells introduced, minimum
elevation unchanged (resolveflats never lowers cells), and maximum
elevation unchanged (borders untouched). The sub-epsilon gradient
adjustments are not representable as distinct DCELL values, so
tests focus on conservative invariants rather than direct gradient
measurement.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Add gunittest test suite for r.richdem.flowaccum

Five tests on a uniform south-facing slope DEM (no depressions or
flats): output creation (D8), min FA == 1 (border cells count only
themselves), max FA > 1 (interior accumulation occurred), no null
cells, and Dinf method produces output. Suite is skipped gracefully
without the RichDEM extension.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Add gunittest test suite for r.richdem.terrain

Five tests on a uniform south-facing 1:1 slope: all eight attribute
options produce output (via subTest loop), slope_degrees max == 45°,
slope_radians max == π/4, aspect mean == 180° (south-facing), and
curvature mean == 0 (planar surface). Suite is skipped gracefully
without the RichDEM extension.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Add gunittest test suite for r.richdem.fsm

Four tests: FSM runs without error on a dry domain, minimum wtd is
preserved at -1 when there is no ponded water, mean wtd decreases
after ponded water in the depression spills out, and the hierarchy
vector remains readable after the run.

A note on a known NaN artefact in border cells of the output raster
is included in the module docstring; tests use min and mean rather
than max to avoid it.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Fix rdarray_to_grass to catch algorithm-produced NaN; correct FSM test note

rdarray_to_grass previously used == rda.no_data to find null cells, but
NaN == NaN is False in IEEE 754, so any NaN value produced by a C++
algorithm edge case would pass through un-normalised. Add | np.isnan(data)
to the null mask so all NaN is written as canonical Python NaN (which
PyGRASS maps to the GRASS DCELL null bit pattern).

Also corrects an incorrect note in the FSM test docstring: the r.univar
max=nan behaviour is a GRASS bug triggered when max==0.0 on a DCELL map,
not an artefact of rdarray_to_grass.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Add note to rdarray_to_grass linking upstream GRASS r.univar bug

Documents that the r.univar max=nan issue on GRASS <= 8.3.2 (when the
true maximum of a DCELL raster is exactly 0.0) is a separate upstream
bug fixed in OSGeo/grass PR #3512 and included in GRASS 8.4.0.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Adjust MODULE_TOPDIR for placement in grass-addons src/raster/

The collection will live at src/raster/r.richdem/ in OSGeo/grass-addons,
which is two levels below the grass-addons root rather than one. Each
MODULE_TOPDIR gains one extra ../ level to match (following the same
pattern as src/raster/r.agent/).

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Rename modules to descriptive names; add REQUIREMENTS; validate flowaccumulation exponent

- r.richdem.fill → r.richdem.filldepressions
- r.richdem.breach → r.richdem.breachdepressions
- r.richdem.flowaccum → r.richdem.flowaccumulation
- r.richdem.terrain → r.richdem.terrainattribute
- Add REQUIREMENTS section to all seven HTML man pages documenting
  the non-standard RichDEM dependency and pip/source install instructions
- Add exponent validation to r.richdem.flowaccumulation: fatal error
  if Holmgren or Freeman method is selected without an exponent

* Add CMakeLists.txt for CMake build support

Collection-level and per-module CMakeLists.txt files following the
r.agent pattern (nested collection with shared library). Per-module
files adapted from prior r.richdem.* CMakeLists.txt files already
in OSGeo/grass-addons (authored by A. Wickert, 2019).

* Add markdown man pages; fix errors in HTML and .md

- Add .md man pages for all seven modules, converted from HTML and
  cleaned up to grass-addons gold standard (following r.stream.*)
- Fix r.lakefill -> r.lake in fsm (non-existent module)
- Fix (2014b) -> (2014) in resolveflats (disambiguation only meaningful
  when both 2014 Barnes papers appear together)
- Fix misleading zscale description in terrainattribute (was "convert
  degrees to meters" — corrected to feet-to-meters example)
- Fix escaped apostrophes left by pandoc conversion
- Fix pandoc image attributes ({style=...}) and caption spans ({.small})
  that do not render on GitHub

* raster: remove old r.richdem.* placeholder modules

The five standalone modules (r.richdem.filldepressions,
r.richdem.breachdepressions, r.richdem.resolveflats,
r.richdem.flowaccumulation, r.richdem.terrainattribute) are superseded
by the new r.richdem collection added in this branch, which provides
the same functionality with complete documentation, test suites, shared
infrastructure (librichdem), and two additional modules
(r.richdem.dephier, r.richdem.fsm) not previously available.

* r.richdem.dephier, r.richdem.fsm: add figure PNGs to CMakeLists DOCFILES

* r.richdem.dephier, r.richdem.fsm: remove unused figures/ subdirectories

* gitignore: add test_keyvalue_result.txt

* r.richdem: remove LICENSE and .gitignore from subtree (not appropriate in grass-addons)

* Set executable bit on all module Python scripts

* Add collection-level man page (r.richdem.html/.md) and update CMakeLists

* r.richdem.fsm: fix r.lakefill -> r.lake in SEE ALSO

r.lakefill does not exist in GRASS GIS; r.lake is the correct module.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* Apply suggestions from code review

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* r.richdem: fix markdownlint violations in collection-level docs

README.md: fix MD060 table separator style (|---|---| → | --- | --- |),
fix MD036 no-emphasis-as-heading, wrap prose lines to ≤80 chars.
r.richdem.md: convert RST-style grid table to pipe table (exempt from
MD013), wrap all prose lines to ≤80 chars, split SEE ALSO section.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* r.richdem: fix markdownlint line-length in DEM conditioning man pages

Wrap all prose lines to ≤80 chars in r.richdem.filldepressions,
r.richdem.breachdepressions, and r.richdem.resolveflats. Split
SEE ALSO sections to one item per line; wrap REQUIREMENTS and
REFERENCES with 2-space continuation indent.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* r.richdem: fix markdownlint line-length in flow/terrain man pages

Wrap all prose lines to ≤80 chars in r.richdem.flowaccumulation and
r.richdem.terrainattribute. Split long method-list items and SEE ALSO
sections; wrap REQUIREMENTS and REFERENCES with 2-space continuation.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* r.richdem: fix markdownlint line-length in FSM workflow man pages

Wrap all prose lines to ≤80 chars in r.richdem.dephier and
r.richdem.fsm. Shorten image alt texts that exceeded the limit,
wrap definition-list prose, REQUIREMENTS, and REFERENCES; split
SEE ALSO sections to one item per line.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* r.richdem: fix collection-level Makefile for g.extension install

Add PGM = r.richdem so the html target has a non-empty name (without
it the target expands to $(HTMLDIR)/.html and make fails). Add the
collection HTML to the install target so r.richdem.html is copied to
the addons docs directory.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* r.richdem: add collection entry-point script and fix Makefile install

Add r.richdem.py so g.extension can fetch interface description
metadata for the collection (without it the install warns 'No such
file or directory: r.richdem'). Update Makefile to install the script
to the addons scripts directory alongside the collection HTML page.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* r.richdem: fix help labels, water_depth scalar input, optional dephier outputs

r.richdem.fsm:
- Fix hierarchy display: G_OPT_V_INPUT requires 'label:' not 'description:'
  to override the default 'Name of input vector map' in --help output
- water_depth now accepts either a float (creates a uniform depth array)
  or a raster map name; replaces G_OPT_R_INPUT with a plain string option

r.richdem.dephier:
- All three outputs (output_labels, output_flowdirs, output_hierarchy) are
  now optional; a rules block requires at least one to be provided
- Fix output_hierarchy display: same label: fix for G_OPT_V_OUTPUT

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* r.richdem: strengthen testsuites with distinct fill/breach DEMs and explicit dephier outlet

Fill and breach tests now use a closed-rim DEM (border=5, rim=4, pit=1).
This makes the two algorithms produce verifiably different minimum values:
fill raises the interior to the rim (min==4.0); breach cuts a channel
without raising to rim level (min<4.0).  The original rim=3 DEM gave
min==3 for both algorithms and could not distinguish filling from breaching.

Dephier and FSM tests add an explicit low-elevation outlet at the right
border (row=3, col=5 set to 3 instead of 5), making the pour-point
unambiguous for hierarchy and spill assertions.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* r.richdem: fix fill/breach test DEMs — use outlet on right border

The closed-rim DEM (all borders at 5) caused Priority-Flood to flood
the entire interior to elevation 5, making fill assertions wrong.
Replace with a DEM that has a low outlet (elevation 3) on the right
border: ring=4, pit=1, outlet=3, other borders=5.

Fill: pit raised to pour-point 4; min=3 (outlet), max=5 (border).
Breach: CompleteBreaching_Lindsay2016 gives the same result for this
simple single-depression case (pit raised to lowest-neighbor=4; no
path cell is above target height so carving is a no-op). Docstrings
and assertions updated to reflect actual algorithm behavior honestly.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* r.richdem: redesign breach/fill tests; add Lindsay2016 eps machinery

New shared DEM (ring=9, pit=1, saddle=4 adjacent to border=5) makes all
three algorithms produce distinct, directly comparable results:
  fill              -> min=5.0 (pit and saddle raised to pour-point)
  CompleteBreaching -> min=4.0 (pit raised to saddle; saddle preserved)
  Lindsay2016 eps   -> min<4.0 (pit shallowed to nextafter(4,-inf))

r.richdem.breachdepressions: add -e flag that calls rd.BreachDepressionsEps
when available; gs.fatal() with a pointer to the needed upstream patch when
the binding is absent.

test_r_richdem_breachdepressions: add TestRichdemBreachEps class guarded by
@skipUnless(HAS_LINDSAY2016, ...) so the eps tests are silently skipped on
unpatched RichDEM builds and activate automatically once the binding lands.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* r.richdem.breachdepressions: guard -e against D4 topology request

Lindsay2016 (the -e flag path) has no D4 variant; call gs.fatal() with
a clear message rather than letting the missing topology kwarg propagate
as a confusing Python error.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* r.richdem: strip outer HTML wrapper from all man pages

GRASS GIS renders man pages by embedding the file content into its own
HTML template.  Providing full HTML pages (DOCTYPE, head, body tags) and
the auto-generated NAME/KEYWORDS/SYNOPSIS/footer sections causes duplicate
content in the rendered output.  Strip all eight HTML files to inner-body
format: start at <h2>DESCRIPTION</h2>, end before the footer <hr>.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* librichdem/vector.py: fix ocean_links overwrite crash and per-map naming

CREATE TABLE ocean_links failed on --overwrite because the mapset's shared
SQLite DB retains tables that GRASS doesn't know about (only the main layer-1
table is dropped/recreated by r.to.vect). Fix with DROP TABLE IF EXISTS before
CREATE TABLE.

Also rename the junction table from the hardcoded "ocean_links" to
"{map_table}_ocean_links" so that multiple hierarchy maps in the same mapset
each get their own table rather than clobbering each other.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* r.richdem.breachdepressions tests: remove test_never_lowers; fix docstring

CompleteBreaching_Lindsay2016 raises the pit to lowest_neighbour (target_height)
then walks the backlink path lowering every cell >= target_height to
target_height.  For the saddle-adjacent-to-border DEM the border cell (5) is
on the path and gets lowered to 4, so the assumption "breaching never lowers
cells" is wrong.  Remove the test and correct the module docstring to describe
the actual per-cell outcome.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* r.richdem.breachdepressions tests: fix HAS_LINDSAY2016 check and D4 eps test

HAS_LINDSAY2016 now checks for rdBreachDepressionsEpsD8 in the C++ _richdem
extension directly rather than BreachDepressionsEps on the Python wrapper.
The Python wrapper defines the function unconditionally; the C++ symbol is
absent until the extension is rebuilt with the patched pywrapper.hpp, so
checking the Python attribute gave a false positive and caused runtime errors.

test_d4_topology in TestRichdemBreachEps changed to test_d4_topology_rejected
using assertModuleFail: Lindsay2016 eps is D8-only and the module explicitly
rejects D4 with gs.fatal, so the test should expect failure not success.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* r.richdem modules: defer pygrass import past gs.parser()

Move `from grass.pygrass.utils import get_lib_path` inside main() after
gs.parser(), and switch to grass.script.utils.get_lib_path.

grass.pygrass.utils loads GRASS C ctypes wrappers at import time, which
pulls in libgdal/libgeos via ctypes.  This fails with conda Python due to
a libstdc++ version mismatch, causing --interface-description (called by
gunittest to introspect modules) to crash before producing any XML.

grass.script.utils.get_lib_path is a pure-Python equivalent that only
needs os.path and os.getenv — no C libraries.  Moving the import after
gs.parser() ensures the module can always describe its interface regardless
of which Python interpreter runs it.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* librichdem/raster.py: replace grass.pygrass.raster with r.out.bin/r.in.bin

grass.pygrass.raster (RasterRow, Buffer) loads grass_raster.8.3 via
ctypes, which depends on libgdal → libstdc++.  This fails with conda
Python due to a GLIBCXX version mismatch, breaking any subprocess that
imports the library.

Replace with r.out.bin -f bytes=8 (IEEE-754 double read via numpy.fromfile)
and r.in.bin -d (double write from numpy.tobytes).  Both are pure GRASS
commands invoked via gs.run_command; no C library loading is required.

The null-value handling changes: instead of relying on PyGRASS writing NaN,
we now write _NO_DATA=-9999.0 for all null/NaN cells and pass anull=-9999
to r.in.bin so GRASS identifies them as null.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* librichdem/vector.py: replace grass.pygrass.vector with v.in.ascii/v.patch

grass.pygrass.vector (VectorTopo, Point) loads grass_vector.8.3 via
ctypes, which depends on libgeos_c → libstdc++.  This fails with conda
Python due to a GLIBCXX version mismatch.

Replace VectorTopo.write(Point, cat) with:
  1. Write metadepression points to a GRASS standard ASCII temp file
     (ORGANIZATION:/VERTI: header + P records with explicit cat values).
  2. v.in.ascii format=standard to create a temporary point map.
  3. v.patch -a to append the points into the existing area map while
     preserving its attribute table and cat values.

This removes all grass.pygrass C library dependencies from vector.py.
The only imports are stdlib (math, os, sqlite3, tempfile) and
grass.script (pure-Python + subprocess wrappers).

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* test_r_richdem_breachdepressions: fix eps test to use binary comparison

r.univar rounds nextafter(4.0, -inf) to "4" in text output because the
difference from 4.0 is ~1 ULP (~1.8e-15), below the precision of any
GRASS text-reporting tool (r.univar, r.info, r.stats).

The value IS stored correctly as IEEE-754 double in the DCELL raster.
Verify it via r.out.bin binary export + numpy.fromfile instead of
r.univar -g min.

Also adds numpy/os/tempfile imports to the test module and updates the
docstring to explain the r.univar precision limitation.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* r.richdem.breachdepressions tests: remove test_d4_topology_rejected

The test printed an ERROR: line to the terminal (from gs.fatal via
assertModuleFail) making passing output look like a failure. The
constraint (eps/-e is D8-only) is enforced in the module itself; a
dedicated test for the rejection adds noise without meaningful coverage.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* r.richdem: apply ruff formatting

Auto-formatted by ruff to satisfy CI style checks.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* r.richdem man pages: add missing trailing newline to all HTML files

Fixes end-of-file-fixer pre-commit hook failure in CI.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* r.richdem man pages: fix trailing newlines in HTML files

Previous fix added an extra newline to files that already had one,
leaving two trailing newlines. Strip to exactly one to satisfy the
end-of-file-fixer pre-commit hook.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

* r.richdem man pages: use full URLs for core GRASS module links

Relative hrefs (e.g. r.watershed.html) break on the server because core
module pages live at a different path than addon pages. Replace all
relative links to core GRASS modules with full grass.osgeo.org URLs,
per review feedback from @neteler.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply at anthropic.com>
Co-authored-by: Edouard Choinière <27212526+echoix at users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>



To unsubscribe from these emails, change your notification settings at https://github.com/OSGeo/grass-addons/settings/notifications


More information about the grass-commit mailing list