08 — Plotting spectral data
xeo can visualize spectral band ranges and spectral response functions through its optional Matplotlib integration. This tutorial covers the accepted selection formats, per-band styling, overlap handling, and further customization through Matplotlib axes.
Install plotting support
Matplotlib is an optional dependency so that the core package remains small. Install the plotting extra before running this tutorial:
python -m pip install "xeo[plot]"import matplotlib.pyplot as plt
import xeoChoose instruments and bands
Both plotting functions accept the same input forms:
- One instrument id plots all available bands.
- A list of instrument ids plots all available bands for each instrument.
- A dictionary maps instrument ids to one band id, a list of band ids, or styled band dictionaries.
- A list of dictionaries gives different selections and styles to different instruments while preserving their order.
Styles use native Matplotlib keywords such as color, alpha, linestyle, and linewidth.
Plot spectral bands
plot_bands() draws each band from its minimum to maximum wavelength. The x-axis is wavelength in nanometres and each main y-axis row represents one instrument.
ax = xeo.plot_bands(
"MSI_S2A",
figsize=(12, 4),
title="Sentinel-2A MSI spectral bands",
)
axPass a dictionary when only particular bands are relevant. A value of None can also be used to select all bands for an instrument.
ax = xeo.plot_bands(
{
"MSI_S2A": ["B2", "B3", "B4", "B8"],
"OLI_L8": ["B2", "B3", "B4", "B5"],
},
figsize=(11, 4),
title="Visible and near-infrared bands",
)
axHandle overlaps and apply styles
Bands whose wavelength ranges overlap are assigned compact sub-lanes inside the same instrument row. Non-overlapping bands reuse a lane. This keeps every band visible without changing the instrument-level y-axis.
The list-of-dictionaries form below also gives each instrument and band its own Matplotlib styling.
band_selections = [
{
"MSI_S2A": [
{"B8": {"color": "royalblue", "alpha": 0.7}},
{"B8A": {"color": "navy", "linestyle": "--", "linewidth": 2}},
]
},
{
"OLI_L8": [
{"B5": {"color": "darkorange", "alpha": 0.8}}
]
},
]
ax = xeo.plot_bands(
band_selections,
figsize=(10, 4),
title="Styled near-infrared bands",
)
axPlot spectral response functions
plot_srf() draws one response curve per selected band. By default, curves share one color per instrument, the legend identifies instruments, and band ids are placed close to their response peaks with collision-aware offsets. It uses the same on-demand, per-user SRF cache as Instrument.srf(), so only the first use of a resource requires a download.
ax = xeo.plot_srf(
{"MSI_S2A": ["B2", "B3", "B4", "B8"]},
figsize=(11, 5),
title="Selected Sentinel-2A MSI responses",
)
axUse a list of dictionaries to compare different bands and styles across instruments. A custom color overrides the default instrument color for that curve.
srf_selections = [
{
"MSI_S2A": [
{"B3": {"color": "seagreen"}},
{"B4": {"color": "crimson", "linestyle": "--", "linewidth": 2}},
]
},
{
"OLI_L8": [
{"B3": {"color": "limegreen"}},
{"B4": {"color": "firebrick", "linewidth": 2}},
]
},
]
ax = xeo.plot_srf(
srf_selections,
figsize=(11, 5),
title="Sentinel-2A and Landsat 8 response comparison",
)
axCustomize the returned axes
Both functions return a Matplotlib Axes. You can pass an existing axes with ax= or modify the returned object. When supplying ax, configure the figure size through plt.subplots() rather than figsize=.
fig, ax = plt.subplots(figsize=(12, 5))
xeo.plot_srf(
{"MSI_S2A": ["B2", "B3", "B4", "B8"]},
ax=ax,
title=None,
legend=False,
)
ax.set_title("Custom Sentinel-2A response plot", loc="left")
ax.set_xlim(430, 900)
ax.set_facecolor("#f7f7f7")
ax.legend(title="Instrument", frameon=False)
axFind instruments with SRFs
Every catalogue instrument has materialized band definitions, but only some provide SRFs. Search first when building a reusable plotting workflow.
instruments_with_srf = xeo.catalogue.search(has_srf=True)
list(instruments_with_srf)Summary
Use plot_bands() to compare wavelength coverage and plot_srf() to inspect measured response curves. Keep selections concise for readable plots, use per-band style dictionaries when comparison requires precise visual encoding, and use the returned axes for any customization beyond the small xeo plotting API.