FlowGlyph¶
FlowGlyph draws a sequence of polylines as a LineCollection. A per-path values array drives colour (with a colorbar) and a per-path widths array scales the line widths by magnitude.
import matplotlib.pyplot as plt
import numpy as np
from cleopatra.styling.colorbar import ColorBar
from cleopatra.glyphs.primitives.flow_glyph import FlowGlyph
1. Colour- and width-scaled flow paths¶
t = np.linspace(0, 1, 20)
paths = [np.column_stack([t, np.sin(t * np.pi) + k]) for k in range(6)]
values = np.linspace(10, 60, 6) # colour
widths = np.linspace(1, 8, 6) # line width
fg = FlowGlyph(paths, values=values, widths=widths, size_legend=True, cmap='magma')
fig, ax, lc = fg.plot(title='Flow paths')
2. River networks by stream order¶
Stream order (Strahler / Shreve) is just a per-segment magnitude, so a river network is a FlowGlyph:
pass the order as both values (colour) and widths (line width). The one extra ingredient is
draw_order='width', which paints paths thinnest-to-thickest so the wide, high-order main channels render
on top of their tributaries where they meet -- the standard cartographic look for drainage networks.
First, build a small synthetic dendritic network: a trunk (highest order) that repeatedly forks into shorter, lower-order tributaries. Each segment carries its Strahler order.
segments = []
def grow(x, y, angle, length, order):
x2, y2 = x + length * np.cos(angle), y + length * np.sin(angle)
segments.append((np.array([[x, y], [x2, y2]]), order))
if order > 1:
for da in (-0.55, 0.55):
grow(x2, y2, angle + da, length * 0.72, order - 1)
grow(6, 0, np.pi / 2, 3.2, 4) # river mouth at the bottom, Strahler order 4
paths = [seg for seg, _ in segments]
order = np.array([o for _, o in segments], dtype=float)
len(paths), sorted(set(order.astype(int)))
(15, [np.int64(1), np.int64(2), np.int64(3), np.int64(4)])
Now draw it: order drives both colour and width, draw_order='width' keeps the trunk on top, and the
size_legend maps line width back to order.
fig, ax = plt.subplots(figsize=(6, 6))
fg = FlowGlyph(
paths,
values=order,
widths=order,
width_limits=(1.0, 7.0),
draw_order='width',
cmap='YlGnBu',
size_legend=True,
size_legend_values=[1, 2, 3, 4],
)
fg.plot(
ax=ax,
title='River network by stream order',
colorbar=ColorBar(label='Stream order'),
)
ax.set_aspect('equal')
ax.set_xticks([])
ax.set_yticks([])
plt.show()
The order-4 trunk is thick and dark at the mouth, tapering through progressively thinner, paler
tributaries out to the order-1 headwaters -- width and colour both encoding order, and the main stem
drawn over its tributaries thanks to draw_order='width'.