Skip to content

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:

bash
python -m pip install "xeo[plot]"
python
import matplotlib.pyplot as plt
import xeo

Choose 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.

python
ax = xeo.plot_bands(
    "MSI_S2A",
    figsize=(12, 4),
    title="Sentinel-2A MSI spectral bands",
)
ax

Pass a dictionary when only particular bands are relevant. A value of None can also be used to select all bands for an instrument.

python
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",
)
ax

Handle 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.

python
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",
)
ax

Plot 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.

python
ax = xeo.plot_srf(
    {"MSI_S2A": ["B2", "B3", "B4", "B8"]},
    figsize=(11, 5),
    title="Selected Sentinel-2A MSI responses",
)
ax

Use a list of dictionaries to compare different bands and styles across instruments. A custom color overrides the default instrument color for that curve.

python
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",
)
ax

Customize 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=.

python
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)
ax

Find instruments with SRFs ​

Every catalogue instrument has materialized band definitions, but only some provide SRFs. Search first when building a reusable plotting workflow.

python
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.

Released under the MIT License.