Short answer

Ridges and valleys are the convex and concave skeleton of a terrain surface, and you extract them from a DEM with one of three families of method: curvature (sign of profile or plan curvature), Topographic Position Index (cell elevation minus neighbourhood mean), or flow accumulation (drainage of the surface for valleys, drainage of the inverted surface for ridges). For a clean drainage-style channel network, flow accumulation is the workhorse; for landform classification across scales, TPI is more flexible. Whichever you pick, the result is only as good as your sink filling, smoothing, and threshold choices.

What "ridge" and "valley" actually mean numerically

A ridge is a line of local elevation maxima transverse to the slope — water flows away from it on both sides. A valley (thalweg) is a line of local minima — water converges into it. In raster terms:

  • Plan (planform) curvature measures convergence/divergence of flow across the slope. Negative plan curvature = converging flow = valley; positive = diverging = ridge.
  • Profile curvature measures acceleration along the slope and controls deposition vs. erosion, not ridge/valley membership directly.
  • TPI = z(cell) − mean(z in annulus/window). Strongly positive TPI sits above its surroundings (crest/ridge); strongly negative sits below (channel/valley); near-zero is a planar slope or flat.

The drainage definition is the most robust because it is global, not just local: a valley cell is one that collects flow from many upstream cells, so it survives noise that fools a purely local curvature operator.

Prepare the DEM first

Two preprocessing steps change the outcome more than the extraction method itself.

Reproject to a metric, equal-cell CRS. Curvature and slope assume square cells in linear units. If your DEM is in geographic degrees (EPSG:4326), reproject to the matching UTM zone so X and Y resolution are equal metres:

gdalwarp -t_srs EPSG:32633 -tr 10 10 -r bilinear srtm_deg.tif dem_utm.tif

Fill sinks for any flow-based method. Spurious one-cell pits stop flow routing and fragment your valley network. In SAGA: Fill Sinks (Wang & Liu); in QGIS Processing the algorithm is sagang:fillsinkswangliu. In WhiteboxTools, BreachDepressionsLeastCost is usually preferable to filling because it carves rather than floods, preserving valley depth.

Smooth if the DEM is high resolution. A 1 m lidar DEM will encode tree pits, building edges, and survey noise as false micro-ridges. A 3×3 or 5×5 Gaussian/low-pass pass, or simply computing TPI at a larger radius, removes them.

Method 1 — Flow accumulation (best for valley/channel networks)

This is the standard hydrological route and gives you connected, hierarchical valleys.

  1. Fill or breach depressions (above).
  2. Compute D8 flow direction, then flow accumulation. GDAL alone does not do flow accumulation; use SAGA, WhiteboxTools, or GRASS r.watershed.
  3. Threshold accumulation to define channels. A common starting point is 1,000 cells of contributing area, but the right value depends on cell size and climate — tune it against a known stream layer.

In GRASS GIS:

r.watershed elevation=dem_utm accumulation=accum drainage=draindir
r.mapcalc "streams = if(abs(accum) > 1000, 1, null())"
r.thin input=streams output=streams_thin
r.to.vect input=streams_thin output=valleys type=line

For ridges, run the identical pipeline on the inverted DEM. Ridges are the "drainage divides" of the real surface, which are the valleys of -z:

r.mapcalc "dem_inv = -1.0 * dem_utm"
r.watershed elevation=dem_inv accumulation=accum_inv ...

The accumulation threshold for ridges trades completeness against clutter exactly as it does for valleys.

Method 2 — Topographic Position Index (best for landform classification)

TPI is fast, scale-tunable, and needs no sink filling. In SAGA the tool is Topographic Position Index (TPI); in QGIS Processing, sagang:topographicpositionindextpi. GDAL even has a quick single-cell version:

gdaldem TPI dem_utm.tif tpi.tif

gdaldem TPI uses a fixed 3×3 window, so it only finds the smallest features. For real landform work, set an inner/outer annulus radius in SAGA (for example 0–300 m) so the index responds to the landform scale you care about. Then classify:

  • TPI > +1 standard deviation → ridge / upper slope crest
  • TPI < −1 standard deviation → valley / channel
  • between → mid-slope or flat (split further by slope angle)

Weiss's slope-position classification (the basis of most "landform" tools) combines TPI at two radii with slope to label ridge, upper slope, flat, valley, etc. This is the right approach when you want a wall-to-wall classified raster rather than vector lines.

Method 3 — Curvature thresholding

Compute plan curvature and threshold its sign. In QGIS/SAGA, Slope, Aspect, Curvature outputs plan curvature; thresholding the strongly negative tail isolates concave convergence zones (valleys) and the strongly positive tail isolates convex divergence (ridges). Curvature is noisy on raw high-resolution data, so smooth first. Use this when you need the geometric character of the surface (e.g. flow convergence for erosion modelling) rather than a connected network.

Worked example: drainage and divides from a 10 m DEM

For a 10 m UTM DEM of a mid-relief catchment:

  1. gdalwarp to EPSG:32633, 10 m, bilinear.
  2. Breach depressions (BreachDepressionsLeastCost).
  3. r.watershed → accumulation.
  4. Channels at accumulation > 1,000 cells (≈ 0.1 km² contributing area). Thin, vectorise → valley network.
  5. Invert DEM, repeat, channels at > 2,000 cells → ridge/divide network.
  6. Overlay both on a hillshade (gdaldem hillshade -z 2 -az 315 -alt 45) and visually confirm ridges sit on crests and valleys sit in hollows.

The two thresholds rarely match: ridge crests are broader and more diffuse than channels, so a higher accumulation cutoff usually gives cleaner divides.

Common pitfalls and why they happen

  • Skipping sink filling. Flow stalls in pits and the valley network breaks into disconnected stubs. The DEM looks fine on screen; the routing does not.
  • Using gdaldem TPI for regional landforms. Its fixed 3×3 window only sees one-cell features, so you get speckle instead of ridges. Use SAGA TPI with a real radius.
  • Comparing ridge and valley layers built at different cell sizes. Curvature and accumulation are resolution-dependent; mixing a 1 m and a 10 m product produces incompatible feature density.
  • Thresholding accumulation by a fixed number copied from a tutorial. The right cutoff scales with cell area and drainage density. Calibrate against a mapped stream layer.
  • Forgetting vertical vs horizontal unit mismatch. If elevation is in metres but cells are in degrees, every curvature and slope value is meaningless.

Quality checks

  • Overlay extracted valleys on an independent hydrography layer (e.g. national stream dataset) and check that main channels coincide.
  • Confirm ridges fall on hillshade crests and never cross a valley line.
  • Inspect the DEM edges and NoData margins, where flow accumulation often produces artificial channels along the boundary.
  • Report the method, cell size, smoothing, and threshold alongside the output so it can be reproduced.

Bathyl perspective

We treat ridge and valley extraction as an interpretation aid, not a finished map. The accumulation threshold and TPI radius encode a decision about scale, so we document those values and show the hillshade behind every extracted line, letting a geologist judge whether a "valley" is a real thalweg or a routing artefact.

Related reading

Sources