Stream Order Explained: Strahler, Shreve and What They Measure

Problem statement

"A fifth-order stream" sounds like a property of a river. It is a property of a network drawn at a particular level of detail, counted with a particular rule. Strahler order rises only where two equal orders meet; Shreve magnitude adds up every headwater; Horton order relabels the main stem with the highest order. Computed on a DEM network, all three change with the accumulation threshold, and a published order from a mapped network may not match any of them.

Measured on the Esopus Creek catchment above Coldbrook, New York, with a stream network derived from the 10 m 3DEP DEM at a 0.5 kmยฒ threshold, and against the USGS NHDPlus High Resolution network:

  • At the outlet, Strahler order was 5, Shreve magnitude 251 and Horton order 5.
  • Inside the basin polygon the network had 254 first-order links, 118 second-order, 64 third-order, 50 fourth-order and 21 fifth-order.
  • The same river's Strahler order at the outlet ranged from 7 to 4 as the threshold went from 0.05 kmยฒ to 10 kmยฒ.
  • NHDPlus HR also labels Esopus Creek at Coldbrook as order 5; first-order streams made up 443.6 km of its 805.7 km in the basin.

Quick answer

rule                 at a junction of orders a and b                 at the outlet it counts
Strahler             max(a, b), plus 1 if a == b                     levels of equal-order joining
Shreve               a + b                                           headwater sources upstream
Horton               Strahler, then the main stem gets the maximum   the trunk as one ordered path

With WhiteboxTools, given a D8 pointer and a stream raster:

import whitebox

wbt = whitebox.WhiteboxTools()
wbt.set_verbose_mode(False)
wbt.strahler_stream_order("d8.tif", "streams.tif", "strahler.tif", zero_background=True)
wbt.shreve_stream_magnitude("d8.tif", "streams.tif", "shreve.tif", zero_background=True)
wbt.horton_stream_order("d8.tif", "streams.tif", "horton.tif", zero_background=True)

State the threshold and the rule whenever you report an order.

Table of the Strahler, Shreve and Horton ordering rules, how each combines two links at a junction, and the value each gave at the Coldbrook gauge.
The same network, three numbering rules: Strahler counts levels, Shreve counts sources, Horton follows the trunk.

Step-by-step solution

Stream order is defined on a network of links โ€” stretches between a source, a junction or the outlet. On a raster, WhiteboxTools' stream_link_identifier numbers each link; a D8 pointer defines which link drains into which. The network here came from a 0.5 kmยฒ accumulation threshold on the least-cost breached 10 m DEM: 501 segments and 443.7 km of channel.

2. Strahler order: levels of branching

Every source link is order 1. Where two links of the same order n meet, the link below is order n + 1; where different orders meet, the link below takes the higher. The Esopus network reached order 5 at the outlet. Strahler order grows slowly โ€” it takes at least two order-4 streams joining to make an order 5 โ€” so it compresses a river's size into a handful of classes.

3. Shreve magnitude: sources upstream

Every source link is magnitude 1, and every junction adds the magnitudes that meet. At the gauge the Shreve magnitude was 251: the number of first-order links upstream of it. Counting links inside the NLDI basin polygon found 254, because the polygon's boundary does not follow the DEM's divides exactly. Magnitude therefore scales with the number of headwaters and, roughly, with drainage area, which makes it more useful than Strahler order for anything proportional to size.

4. Horton order: the trunk carries one label

Horton's scheme starts from Strahler order, then traces the main stream upstream from each junction โ€” following the longer or larger branch โ€” and gives that whole path the higher order. At the outlet it was 5, as for Strahler; upstream, the main stem of Esopus Creek keeps order 5 all the way to its source instead of dropping through 4, 3, 2 and 1.

Strahler's rule produces a characteristic geometric series: many first-order links, fewer second-order, and so on.

order   links   cells
1        254   19,816
2        118    9,456
3         64    4,417
4         50    3,364
5         21    1,059

The ratio of links from one order to the next โ€” the bifurcation ratio โ€” was 2.2, 1.8, 1.3 and 2.4. Natural networks typically show ratios between 3 and 5; a DEM network with a single threshold, a clipped headwater area and one basin outlet need not.

6. Expect order to change with the threshold

A lower threshold adds first-order links at the tips of the network, which creates more junctions of equal order downstream. Maximum Strahler order in the basin was 7 at 0.05 kmยฒ and 0.1 kmยฒ, 6 at 0.25 kmยฒ and 0.5 kmยฒ, 5 at 1 and 2 kmยฒ, and 4 at 5 and 10 kmยฒ. pysheds' own ordering at 0.5 kmยฒ put a single cell at order 6 where WhiteboxTools' maximum was 5 โ€” a reminder that small differences in how tools resolve junctions can shift an order at the top of the network.

7. Compare with mapped networks carefully

NHDPlus HR assigns order 5 to Esopus Creek near Coldbrook, matching the DEM network at 0.5โ€“2 kmยฒ thresholds. Its own lengths by order inside the basin were 443.6 km of order 1, 191.1 km of order 2, 87.0 km of order 3, 55.0 km of order 4 and 29.0 km of order 5. A mapped network's order reflects its cartographic detail, not a physical threshold, so agreement at one threshold is a calibration, not a confirmation.

8. Use order for classes, magnitude for size

Strahler order is good for grouping streams โ€” sampling designs, habitat classes, symbology. It is poor as a size measure because one order can span a large range of drainage areas. Where size matters, use accumulation or Shreve magnitude.

Bar chart of the number of stream links in each Strahler order for the Esopus network at a 0.5 km2 threshold.
254 first-order links and 21 fifth-order ones: most of any network is headwater.

Code examples

Example 1 โ€” derive a network and order it three ways

import os

import numpy as np
import rasterio
import whitebox

os.makedirs("order", exist_ok=True)
wbt = whitebox.WhiteboxTools()
wbt.set_verbose_mode(False)
wbt.set_working_dir(os.path.abspath("order"))
wbt.d8_pointer(os.path.abspath("esopus_breached.tif"), "d8.tif")
wbt.d8_flow_accumulation("d8.tif", "acc.tif", out_type="cells", pntr=True)
wbt.extract_streams("acc.tif", "streams.tif", threshold=5000, zero_background=True)   # 0.5 km2 on a 10 m grid
wbt.strahler_stream_order("d8.tif", "streams.tif", "strahler.tif", zero_background=True)
wbt.shreve_stream_magnitude("d8.tif", "streams.tif", "shreve.tif", zero_background=True)
wbt.horton_stream_order("d8.tif", "streams.tif", "horton.tif", zero_background=True)
wbt.stream_link_identifier("d8.tif", "streams.tif", "links.tif", zero_background=True)


def read(name):
    with rasterio.open(f"order/{name}.tif") as src:
        return src.read(1)


acc, strahler, shreve, horton, links = (read(n) for n in ("acc", "strahler", "shreve", "horton", "links"))

from pyproj import Transformer

with rasterio.open("order/acc.tif") as src:
    transform, crs = src.transform, src.crs
gx, gy = Transformer.from_crs("EPSG:4269", crs, always_xy=True).transform(-74.2701944, 42.0144722)   # Coldbrook gauge
c0, r0 = ~transform * (gx, gy)
r0, c0 = int(r0), int(c0)
window = np.where(strahler[r0 - 15:r0 + 16, c0 - 15:c0 + 16] > 0, acc[r0 - 15:r0 + 16, c0 - 15:c0 + 16], -1)
i = np.unravel_index(window.argmax(), window.shape)
row, col = r0 - 15 + i[0], c0 - 15 + i[1]                     # the largest network cell within 150 m
print(f"outlet ({row}, {col}): drains {acc[row, col] / 1e4:.2f} km2, Strahler {strahler[row, col]:.0f}, "
      f"Shreve {shreve[row, col]:.0f}, Horton {horton[row, col]:.0f}")
outlet (2139, 2163): drains 492.44 km2, Strahler 5, Shreve 251, Horton 5

The outlet is the largest network cell within 150 m of the Coldbrook gauge. The DEM's own lowest network cell, where Esopus Creek leaves the grid below the gauge, drained 513.48 kmยฒ and was already Strahler order 6 with Shreve magnitude 260, because another tributary joins in between.

import pandas as pd

from rasterio import features
import geopandas as gpd

with rasterio.open("order/acc.tif") as src:
    transform, crs = src.transform, src.crs
basin = gpd.read_file("nldi_basin_01362500.geojson").to_crs(crs)
inside = features.rasterize(basin.geometry, out_shape=acc.shape, transform=transform).astype(bool)

cells = pd.DataFrame({"link": links[inside & (links > 0)], "order": strahler[inside & (links > 0)]})
per_link = cells.groupby("link").order.max()
table = pd.DataFrame({"links": per_link.value_counts().sort_index(), "cells": cells.groupby("order").size()})
table["bifurcation_ratio"] = (table.links / table.links.shift(-1)).round(2)
print(table.to_string())
print("first-order links:", int(table.links.iloc[0]))
       links  cells  bifurcation_ratio
order                                 
1.0      254  19816               2.15
2.0      118   9456               1.84
3.0       64   4417               1.28
4.0       50   3364               2.38
5.0       21   1059                NaN
first-order links: 254

Example 3 โ€” orders in a mapped network

mapped = gpd.read_file("nhdplushr_flowlines_esopus.gpkg").to_crs(crs)
mapped = gpd.clip(mapped[mapped.ftype.isin([460, 558, 334])], basin)
by_order = mapped.assign(km=mapped.length / 1000).groupby("streamorde").agg(flowlines=("km", "size"), km=("km", "sum"))
print(by_order.round(1).to_string())
            flowlines     km
streamorde                  
1                1066  443.6
2                 458  191.1
3                 232   87.0
4                 125   55.0
5                  66   29.0
6                   1    0.0

streamorde is NHDPlus' Strahler order attribute, computed on its own mapped network.

Explanation

Why Strahler order rises so slowly

To reach order n + 1, two order-n streams must meet. Each of those needed two order n โˆ’ 1 streams, and so on. An order-5 outlet therefore needs at least 16 first-order sources arranged just so, and in practice hundreds; the Esopus had 251. Orders grow with the logarithm of network size, which is why rivers the size of the Mississippi are only order 10 to 12 on typical maps.

Why Shreve magnitude equals the number of sources

Each source contributes 1, and magnitudes only ever add at junctions. Nothing is lost or created along the way, so the value at any link is the count of sources upstream of it. That makes magnitude additive, like drainage area, where Strahler order is not.

Why thresholds move orders

Adding tiny headwater links does not change the main river, but each one can turn an unequal junction into an equal one and push orders up by one all the way down the network. The network's order is as much a statement about the smallest streams included as about the largest river.

Why tools can disagree at the top

Order depends on which links meet at each junction. Where D8 routes two streams into the same cell from different sides, or where a tool treats a cell as a junction slightly differently, the order of one short link can change, and the change propagates downstream. A single cell of order 6 in pysheds against a maximum of 5 in WhiteboxTools is such a case.

Bar chart of maximum Strahler order in the Esopus network for accumulation thresholds from 0.05 to 10 km2.
The river is the same at every threshold; its order is not.

Edge cases or notes

  • Braided channels and canals create loops that ordering rules do not handle; D8 networks have none, mapped networks may.
  • Clipped networks lose headwaters outside the clip and understate orders.
  • Horton order depends on the rule for the main stem โ€” longest path or largest area โ€” which differs between tools.
  • Vector ordering on mapped lines needs topology: every junction snapped and every line directed downstream.
  • Hack order numbers streams from the outlet upstream, the reverse of Strahler.
  • Order classes for sampling should be defined with a fixed threshold across all basins compared.
  • Stream order is dimensionless; do not average it or use it in regressions as if it were a size.

FAQ

What is Strahler stream order?

A classification in which headwater streams are order 1 and the order increases by one only where two streams of equal order meet. The Esopus network at a 0.5 kmยฒ threshold reached order 5 at Coldbrook.

What is the difference between Strahler order and Shreve magnitude?

Strahler order takes the maximum at junctions and rises only on equal orders; Shreve magnitude adds the values. Shreve magnitude at the Esopus outlet was 251, the number of headwater links.

Why does my river's stream order change?

Because the network's level of detail changed. The Esopus outlet was order 7 with a 0.05 kmยฒ threshold and order 4 with 10 kmยฒ.

Is stream order a good measure of river size?

Only roughly. One Strahler order covers a wide range of drainage areas; use accumulation or Shreve magnitude when size matters.

What is a bifurcation ratio?

The number of links of one order divided by the number of the next order. For the Esopus DEM network the ratios were 2.2, 1.8, 1.3 and 2.4.

Do DEM-derived orders match NHDPlus stream orders?

They can at a suitable threshold: NHDPlus HR gives Esopus Creek at Coldbrook order 5, as did the DEM network at 0.5โ€“2 kmยฒ. Agreement depends on both networks' level of detail.