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.
Step-by-step solution
1. Build the network as links and junctions
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.
5. Count links per order
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.
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.
Example 2 โ links per order and bifurcation ratios
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.
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.
Internal links
- How to calculate Strahler stream order in Python โ ordering in practice
- How to extract a stream network from a DEM โ the thresholds that set the order
- How to calculate flow accumulation from a DEM in Python โ size measured directly
- How to split a catchment into sub-basins at many outlets โ another way to structure a network
- How DEM resolution and source change a drainage network โ resolution effects on networks
- DEM hydrology explained: from elevation to where water goes โ the pipeline
- Flow direction explained: D8, D-infinity and MFD compared โ why D8 networks have no loops
- How to burn a known river network into a DEM โ aligning DEM and mapped networks
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.