Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
*~

**/*.quarto_ipynb
_freeze/site_libs/
16 changes: 16 additions & 0 deletions _freeze/posts/20260428-xopr-v0p5/index/execute-results/html.json

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
163 changes: 163 additions & 0 deletions posts/20260428-xopr-v0p5/index.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
---
title: "xOPR: Open Polar Radar data in Python"
author: "Thomas Teisberg, Shane Grigsby, John Paden, Reece Matthews"
date: "2026-04-29"
image: images/xopr-totten.png
jupyter: python3
format:
html:
code-line-numbers: true
---

Polar radar sounder data is collected by more than a dozen different institutions, with more distinct instruments and platforms than most of us can count. This international collaboration over the last 60 years has generated an enormous wealth of data. But actually using that data is hard. Different institutions release data in different formats and levels of processing. Or they may not release it at all.

Open Polar Radar is a multi-institution collaboration whose goal is to solve this data access problem by providing unified data processing tools, products, and access to the several millions of line-km of existing radar sounder data. If you haven't seen it before, you should check out [Open Polar Radar](https://openpolarradar.org/), which features a growing number of datasets from different providers.

As those of you who are familiar with OPR know, though, it's been a mostly Matlab-focused ecosystem.
For the last 6 months, we've been quietly working away on changing that through a lightweight Python library called `xOPR`.

::: {.callout-tip}
## Give us feedback on our roadmap
We've reached a `v0.5` release and we're looking for feedback on what works for you, what doesn't, and what you'd like to see next. If you haven't played with `xOPR` before, check it out. Then do us a huge favor and [rank your preferences for the future roadmap in our survey here](https://forms.englacial.org/xopr-roadmap).
:::

`xOPR` provides access to the entire catalog of processed Open Polar Radar data products, cleanly integrated into the Python data ecosystem. You don't need to worry about what type of `.mat` file the source data is in, you just query for the data and get back an `xarray.Dataset`.

Let's take a look at how it works.

```{python}
#| echo: false
#| output: false
import warnings
warnings.filterwarnings('ignore')

import numpy as np
import matplotlib.pyplot as plt
import geopandas as gpd
import holoviews as hv
import hvplot.pandas

hvplot.extension('matplotlib')
```

## What's in the catalog?

You can install `xOPR` with `uv add xopr` or `pip install xopr`. Once installed, connecting to the catalog is a single line:

```{python}
import xopr

opr = xopr.OPRConnection()
```

When you create an `OPRConnection`, xOPR syncs a local copy of the STAC catalog — the index of all available data. After the first sync, subsequent startups check only download changes, so it's fast.

Here's how you find all of the Antarctic radar lines available in the `xOPR` catalog (so far). (Don't worry — we do Greenland too!) If you haven't checked in on OPR in a while, you might be surprised by how much ICECAP data from the University of Texas Institute for Geophysics is now included.

```{python}
#| label: fig-antarctic-lines
#| fig-cap: "All Antarctic radar sounder lines in the xOPR catalog, colored by institution. CReSIS (University of Kansas) lines are shown in blue; UTIG (University of Texas) lines in orange."
#| warning: false
#| message: false

import cartopy.crs as ccrs

antarctica = xopr.geometry.get_antarctic_regions(simplify_tolerance=10000)
antarctic_frames = opr.query_frames(geometry=antarctica)

antarctic_frames = antarctic_frames.assign(
provider=antarctic_frames['properties'].str['opr:provider']
)

antarctic_frames.hvplot(by='provider', features=['coastline', 'ocean'],
projection='EPSG:3031', height=600, linewidth=0.5)
```

## Querying by region

If you want to pull data for your favorite glacier, we make that easy too. You can query the catalog by many criteria, including by passing a polygon outlining your area of interest. `xOPR` also ships with a helper to load named regions from the [MEaSUREs Antarctic Boundaries](https://nsidc.org/data/nsidc-0709) dataset, so you can ask for a region by name.

Here's everything in the catalog over Totten Glacier:

```{python}
#| label: fig-totten-lines
#| fig-cap: "Radar flight lines over the Totten region (black outline) available in the xOPR catalog."

# Load higher resolution background map features
import geoviews as gv
import cartopy.feature as cfeature
coast = gv.feature.Feature(cfeature.COASTLINE.with_scale('10m'))
ocean = gv.feature.Feature(cfeature.OCEAN.with_scale('10m'))

totten = xopr.geometry.get_antarctic_regions(name='Totten', merge_regions=True, simplify_tolerance=1000)
totten_frames = opr.query_frames(geometry=totten)

region_outline = gv.Polygons([totten]).opts(facecolor='none', edgecolor='black', linewidth=2)

flight_lines = totten_frames.hvplot(by='collection', projection='EPSG:3031', height=600)

coast * ocean * region_outline * flight_lines
```

## Loading radar data

Obviously we want to do more than just look at maps. When you call `load_frame()`, you get back an `xarray.Dataset`:

```{python}
frame = opr.load_frame(totten_frames.iloc[103])
frame.xopr
```

The `Data` variable is the raw radar echogram — a 2D array with `slow_time` (along-track) and `twtt` (two-way travel time, the vertical axis) as dimensions. `xOPR` is a lightweight data access tool, so we're not reinventing the wheel here. The format is the same as any OPR output product, as documented in the OPR [Echogram File Guide](https://gitlab.com/openpolarradar/opr/-/wikis/Echogram-File-Guide).

`xOPR` handles both the modern HDF5 `.mat` format and legacy MATLAB v5 files transparently, so you don't need to think about what format the source file is in.

If you expand the "Attributes" tab above, you'll see that the processing parameters are encoded under `param_array`, `param_records`, and `param_sar`.

## Plotting a radargram

Frames (or combinations of frames created by passing multiple frames to `xopr.merge_frames()`) can be plotted as radargrams. Here we resample by stacking to get a consistent 2 second spacing and transform the data to log scale before plotting.

```{python}
#| label: fig-radargram
#| fig-cap: "An example radargram over Totten Glacier."

# Resample to uniform slow_time spacing
stacked = frame.resample(slow_time='2s').mean()
data_dB = 10 * np.log10(np.abs(stacked['Data']))

# Plot the radargram
fig, ax = plt.subplots(figsize=(8, 3.5))
data_dB.plot.imshow(x='slow_time', cmap='gray', ax=ax)
ax.set_title(f"{stacked.attrs['collection']} — {stacked.attrs['segment_path']}")
ax.invert_yaxis()
fig
```

The OPR dataset also includes surface and bed picks for most data. Layers (including the surface and bed) are loaded by calling `opr.get_layers()` with any radar dataset as the argument:

```{python}
#| label: fig-radargram-layers
#| fig-cap: "The same radargram as above with added surface and bed layer picks loaded from OPR."

# Load and plot available layer picks
layers = opr.get_layers(stacked)

if layers:
for layer_name, layer_ds in layers.items():
layer_ds['twtt'].plot(ax=ax, x='slow_time',
label=layer_name, linestyle=':')
ax.legend(loc='upper right', fontsize=9, framealpha=0.6)

fig
```

## Doing more with `xOPR`

We want `xOPR` to be your go-to tool for anything involving radar sounder data. It's designed to help you find data that matches your needs, prototype new processing or analysis methods, and then scale those across every byte of data that's available.

To get started quickly, check out the notebooks in our [full documentation](https://docs.englacial.org/xopr/). These include tutorials on narrowing down on the data you need, scaling your analysis to cloud workers, extracting surface and bed information, doing crossover analysis, and more.

`xOPR` is an open-source tool and we welcome your input. If you run into problems or have questions, feel free to [open an issue](https://github.com/englacial/xopr/issues). If you want to help us make `xOPR` better, we'd love to see your pull request too!

Most importantly, tell us what you want to see next by [ranking your preferences for the future roadmap in our survey here](https://forms.englacial.org/xopr-roadmap).
5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ requires-python = ">=3.13"
dependencies = [
"ipykernel>=7.1.0",
"matplotlib>=3.10.8",
"nbclient>=0.10.4",
"nbformat>=5.10.4",
"numpy>=2.3.5",
"xopr>=0.3.0",
"xopr>=0.5.0",
"hvplot",
]
Loading
Loading