Siphon Overview

Unidata Logo

Siphon Overview

Unidata Python Workshop


TDS

Questions

  1. What is a THREDDS Data Server (TDS)?
  2. How can I use Siphon to access a TDS?

Objectives

  1. Use Siphon to Access a THREDDS Catalog
  2. Filtering Data
  3. Use Siphon to Perform Remote Data Access

1. Use Siphon to Access a THREDDS Catalog

THREDDS is a server for providing remote access to datasets and a variety of server-side services. THREDDS make data access more uniform regardless of the on-disk format.

  • Data Access Services:
    • HTTP Download
    • Web Mapping/Coverage Service (WMS/WCS)
    • OPeNDAP
    • NetCDF Subset Service
    • CDMRemote

There is a server with real-time data setup at http://thredds.ucar.edu that we'll use to explore the capability of THREDDS and learn how to access data. Let's open that link and explore in the browser what's available on THREDDS. Explore the NEXRAD level 3 data specifically.

THREDDS Catalogs

  • XML descriptions of data and metadata
  • Access methods
  • Easily processed with siphon.catalog.TDSCatalog
In [1]:
from datetime import datetime, timedelta

from siphon.catalog import TDSCatalog

Let's get data from yesterday at this time. We'll use the timedelta object to do this in an easy way.

In [2]:
date = datetime.utcnow() - timedelta(days=1)
print(date)
2020-09-03 15:33:56.810427

We'll then go find the URL for the level 3 radar data. Let's get the N0Q (tilt 1 base reflectivity) for the LRX radar. Notice that we change the html extension to xml. Siphon will do that for us, but issue a warning.

In [3]:
cat = TDSCatalog('http://thredds.ucar.edu/thredds/catalog/nexrad/level3/'
                 f'N0Q/LRX/{date:%Y%m%d}/catalog.xml')
In [4]:
cat.datasets
Out[4]:
['Level3_LRX_N0Q_20200903_2357.nids', 'Level3_LRX_N0Q_20200903_2347.nids', 'Level3_LRX_N0Q_20200903_2338.nids', 'Level3_LRX_N0Q_20200903_2328.nids', 'Level3_LRX_N0Q_20200903_2319.nids', 'Level3_LRX_N0Q_20200903_2309.nids', 'Level3_LRX_N0Q_20200903_2257.nids', 'Level3_LRX_N0Q_20200903_2248.nids', 'Level3_LRX_N0Q_20200903_2238.nids', 'Level3_LRX_N0Q_20200903_2229.nids', 'Level3_LRX_N0Q_20200903_2219.nids', 'Level3_LRX_N0Q_20200903_2209.nids', 'Level3_LRX_N0Q_20200903_2200.nids', 'Level3_LRX_N0Q_20200903_2150.nids', 'Level3_LRX_N0Q_20200903_2140.nids', 'Level3_LRX_N0Q_20200903_2131.nids', 'Level3_LRX_N0Q_20200903_2121.nids', 'Level3_LRX_N0Q_20200903_2112.nids', 'Level3_LRX_N0Q_20200903_2102.nids', 'Level3_LRX_N0Q_20200903_2052.nids', 'Level3_LRX_N0Q_20200903_2043.nids', 'Level3_LRX_N0Q_20200903_2033.nids', 'Level3_LRX_N0Q_20200903_2024.nids', 'Level3_LRX_N0Q_20200903_2014.nids', 'Level3_LRX_N0Q_20200903_2004.nids', 'Level3_LRX_N0Q_20200903_1955.nids', 'Level3_LRX_N0Q_20200903_1945.nids', 'Level3_LRX_N0Q_20200903_1936.nids', 'Level3_LRX_N0Q_20200903_1926.nids', 'Level3_LRX_N0Q_20200903_1916.nids', 'Level3_LRX_N0Q_20200903_1907.nids', 'Level3_LRX_N0Q_20200903_1857.nids', 'Level3_LRX_N0Q_20200903_1847.nids', 'Level3_LRX_N0Q_20200903_1838.nids', 'Level3_LRX_N0Q_20200903_1828.nids', 'Level3_LRX_N0Q_20200903_1819.nids', 'Level3_LRX_N0Q_20200903_1809.nids', 'Level3_LRX_N0Q_20200903_1759.nids', 'Level3_LRX_N0Q_20200903_1750.nids', 'Level3_LRX_N0Q_20200903_1740.nids', 'Level3_LRX_N0Q_20200903_1731.nids', 'Level3_LRX_N0Q_20200903_1721.nids', 'Level3_LRX_N0Q_20200903_1711.nids', 'Level3_LRX_N0Q_20200903_1702.nids', 'Level3_LRX_N0Q_20200903_1652.nids', 'Level3_LRX_N0Q_20200903_1643.nids', 'Level3_LRX_N0Q_20200903_1633.nids', 'Level3_LRX_N0Q_20200903_1623.nids', 'Level3_LRX_N0Q_20200903_1614.nids', 'Level3_LRX_N0Q_20200903_1604.nids', 'Level3_LRX_N0Q_20200903_1555.nids', 'Level3_LRX_N0Q_20200903_1545.nids', 'Level3_LRX_N0Q_20200903_1535.nids', 'Level3_LRX_N0Q_20200903_1526.nids', 'Level3_LRX_N0Q_20200903_1516.nids', 'Level3_LRX_N0Q_20200903_1506.nids', 'Level3_LRX_N0Q_20200903_1455.nids', 'Level3_LRX_N0Q_20200903_1445.nids', 'Level3_LRX_N0Q_20200903_1436.nids', 'Level3_LRX_N0Q_20200903_1426.nids', 'Level3_LRX_N0Q_20200903_1416.nids', 'Level3_LRX_N0Q_20200903_1407.nids', 'Level3_LRX_N0Q_20200903_1357.nids', 'Level3_LRX_N0Q_20200903_1348.nids', 'Level3_LRX_N0Q_20200903_1338.nids', 'Level3_LRX_N0Q_20200903_1328.nids', 'Level3_LRX_N0Q_20200903_1319.nids', 'Level3_LRX_N0Q_20200903_1309.nids', 'Level3_LRX_N0Q_20200903_1300.nids', 'Level3_LRX_N0Q_20200903_1250.nids', 'Level3_LRX_N0Q_20200903_1240.nids', 'Level3_LRX_N0Q_20200903_1231.nids', 'Level3_LRX_N0Q_20200903_1221.nids', 'Level3_LRX_N0Q_20200903_1212.nids', 'Level3_LRX_N0Q_20200903_1202.nids', 'Level3_LRX_N0Q_20200903_1152.nids', 'Level3_LRX_N0Q_20200903_1143.nids', 'Level3_LRX_N0Q_20200903_1133.nids', 'Level3_LRX_N0Q_20200903_1124.nids', 'Level3_LRX_N0Q_20200903_1114.nids', 'Level3_LRX_N0Q_20200903_1104.nids', 'Level3_LRX_N0Q_20200903_1055.nids', 'Level3_LRX_N0Q_20200903_1045.nids', 'Level3_LRX_N0Q_20200903_1036.nids', 'Level3_LRX_N0Q_20200903_1026.nids', 'Level3_LRX_N0Q_20200903_1016.nids', 'Level3_LRX_N0Q_20200903_1007.nids', 'Level3_LRX_N0Q_20200903_0957.nids', 'Level3_LRX_N0Q_20200903_0948.nids', 'Level3_LRX_N0Q_20200903_0938.nids', 'Level3_LRX_N0Q_20200903_0928.nids', 'Level3_LRX_N0Q_20200903_0919.nids', 'Level3_LRX_N0Q_20200903_0909.nids', 'Level3_LRX_N0Q_20200903_0900.nids', 'Level3_LRX_N0Q_20200903_0850.nids', 'Level3_LRX_N0Q_20200903_0840.nids', 'Level3_LRX_N0Q_20200903_0831.nids', 'Level3_LRX_N0Q_20200903_0821.nids', 'Level3_LRX_N0Q_20200903_0812.nids', 'Level3_LRX_N0Q_20200903_0802.nids', 'Level3_LRX_N0Q_20200903_0752.nids', 'Level3_LRX_N0Q_20200903_0743.nids', 'Level3_LRX_N0Q_20200903_0733.nids', 'Level3_LRX_N0Q_20200903_0723.nids', 'Level3_LRX_N0Q_20200903_0714.nids', 'Level3_LRX_N0Q_20200903_0704.nids', 'Level3_LRX_N0Q_20200903_0653.nids', 'Level3_LRX_N0Q_20200903_0643.nids', 'Level3_LRX_N0Q_20200903_0633.nids', 'Level3_LRX_N0Q_20200903_0624.nids', 'Level3_LRX_N0Q_20200903_0614.nids', 'Level3_LRX_N0Q_20200903_0605.nids', 'Level3_LRX_N0Q_20200903_0555.nids', 'Level3_LRX_N0Q_20200903_0545.nids', 'Level3_LRX_N0Q_20200903_0536.nids', 'Level3_LRX_N0Q_20200903_0526.nids', 'Level3_LRX_N0Q_20200903_0517.nids', 'Level3_LRX_N0Q_20200903_0507.nids', 'Level3_LRX_N0Q_20200903_0457.nids', 'Level3_LRX_N0Q_20200903_0448.nids', 'Level3_LRX_N0Q_20200903_0438.nids', 'Level3_LRX_N0Q_20200903_0429.nids', 'Level3_LRX_N0Q_20200903_0419.nids', 'Level3_LRX_N0Q_20200903_0409.nids', 'Level3_LRX_N0Q_20200903_0400.nids', 'Level3_LRX_N0Q_20200903_0350.nids', 'Level3_LRX_N0Q_20200903_0341.nids', 'Level3_LRX_N0Q_20200903_0331.nids', 'Level3_LRX_N0Q_20200903_0321.nids', 'Level3_LRX_N0Q_20200903_0312.nids', 'Level3_LRX_N0Q_20200903_0302.nids', 'Level3_LRX_N0Q_20200903_0253.nids', 'Level3_LRX_N0Q_20200903_0243.nids', 'Level3_LRX_N0Q_20200903_0233.nids', 'Level3_LRX_N0Q_20200903_0224.nids', 'Level3_LRX_N0Q_20200903_0214.nids', 'Level3_LRX_N0Q_20200903_0205.nids', 'Level3_LRX_N0Q_20200903_0155.nids', 'Level3_LRX_N0Q_20200903_0145.nids', 'Level3_LRX_N0Q_20200903_0136.nids', 'Level3_LRX_N0Q_20200903_0126.nids', 'Level3_LRX_N0Q_20200903_0117.nids', 'Level3_LRX_N0Q_20200903_0107.nids', 'Level3_LRX_N0Q_20200903_0057.nids', 'Level3_LRX_N0Q_20200903_0048.nids', 'Level3_LRX_N0Q_20200903_0038.nids', 'Level3_LRX_N0Q_20200903_0029.nids', 'Level3_LRX_N0Q_20200903_0019.nids', 'Level3_LRX_N0Q_20200903_0009.nids', 'Level3_LRX_N0Q_20200903_0000.nids']

Top


2. Filtering Data

We could manually look through that list above and figure out what dataset we're looking for and generate that name (or index). Siphon provides some helpers to simplify this process, provided the names of the dataset follow a pattern with the timestamp in the name:

In [5]:
request_time = date.replace(hour=18, minute=30, second=0, microsecond=0)
ds = cat.datasets.filter_time_nearest(request_time)
ds
Out[5]:
Level3_LRX_N0Q_20200903_1828.nids

We can also find the list of datasets within a time range:

In [6]:
datasets = cat.datasets.filter_time_range(request_time, request_time + timedelta(hours=1))
print(datasets)
[Level3_LRX_N0Q_20200903_1926.nids, Level3_LRX_N0Q_20200903_1916.nids, Level3_LRX_N0Q_20200903_1907.nids, Level3_LRX_N0Q_20200903_1857.nids, Level3_LRX_N0Q_20200903_1847.nids, Level3_LRX_N0Q_20200903_1838.nids]
EXERCISE:
  • Starting from http://thredds.ucar.edu/, find the level 2 radar data for the Tulsa, OK radar (KINX) for the previous day.
  • Grab the URL and create a TDSCatalog instance.
  • Using Siphon, find the data available in the catalog between 12Z and 18Z on the previous day.
In [7]:
# YOUR CODE GOES HERE
SOLUTION
In [8]:
# %load solutions/datasets.py

# Cell content replaced by load magic replacement.
# Solution from above in case you had trouble
date = datetime.utcnow() - timedelta(days=1)
cat = TDSCatalog(f'https://thredds.ucar.edu/thredds/catalog/nexrad/level2/KINX/{date:%Y%m%d}/catalog.xml')
request_time = date.replace(hour=12, minute=0, second=0, microsecond=0)
datasets = cat.datasets.filter_time_range(request_time, request_time + timedelta(hours=6))
print(datasets)
[Level2_KINX_20200903_1800.ar2v, Level2_KINX_20200903_1752.ar2v, Level2_KINX_20200903_1743.ar2v, Level2_KINX_20200903_1737.ar2v, Level2_KINX_20200903_1732.ar2v, Level2_KINX_20200903_1723.ar2v, Level2_KINX_20200903_1718.ar2v, Level2_KINX_20200903_1416.ar2v, Level2_KINX_20200903_1407.ar2v, Level2_KINX_20200903_1358.ar2v, Level2_KINX_20200903_1349.ar2v, Level2_KINX_20200903_1341.ar2v, Level2_KINX_20200903_1332.ar2v, Level2_KINX_20200903_1323.ar2v, Level2_KINX_20200903_1314.ar2v, Level2_KINX_20200903_1306.ar2v, Level2_KINX_20200903_1257.ar2v, Level2_KINX_20200903_1248.ar2v, Level2_KINX_20200903_1239.ar2v, Level2_KINX_20200903_1231.ar2v, Level2_KINX_20200903_1222.ar2v, Level2_KINX_20200903_1213.ar2v, Level2_KINX_20200903_1204.ar2v]

Top


3. Use Siphon to Perform Remote Data Access

Accessing catalogs is only part of the story; Siphon is much more useful if you're trying to access/download datasets.

For instance, using our data that we just retrieved:

In [9]:
# Solution from above in case you had trouble
date = datetime.utcnow() - timedelta(days=1)
cat = TDSCatalog(f'https://thredds.ucar.edu/thredds/catalog/nexrad/level2/KINX/{date:%Y%m%d}/catalog.xml')
request_time = date.replace(hour=12, minute=0, second=0, microsecond=0)
datasets = cat.datasets.filter_time_range(request_time, request_time + timedelta(hours=6))
In [10]:
ds = datasets[0]

We can ask Siphon to download the file locally:

In [11]:
ds.download()

Look in your file explorer panel or run the cell below to verify that we did actually download the file!

In [12]:
import os; os.listdir()
Out[12]:
['Level2_KINX_20200903_1800.ar2v', 'solutions', 'Siphon Overview.ipynb']

Or better yet, get a file-like object that lets us read from the file as if it were local:

In [13]:
fobj = ds.remote_open()
data = fobj.read()
print(len(data))
11870695

This is handy if you have Python code to read a particular format.

It's also possible to get access to the file through services that provide netCDF4-like access, but for the remote file. This access allows downloading information only for variables of interest, or for (index-based) subsets of that data:

In [14]:
nc = ds.remote_access()

By default this uses CDMRemote (if available), but it's also possible to ask for OPeNDAP (using netCDF4-python). There is even XArray support which is great with the declarative plotting interface - more on that later.

In [15]:
print(list(nc.variables))
['Reflectivity_HI', 'timeR_HI', 'elevationR_HI', 'azimuthR_HI', 'distanceR_HI', 'numRadialsR_HI', 'numGatesR_HI', 'Reflectivity', 'timeR', 'elevationR', 'azimuthR', 'distanceR', 'numRadialsR', 'numGatesR', 'RadialVelocity_HI', 'timeV_HI', 'elevationV_HI', 'azimuthV_HI', 'distanceV_HI', 'numRadialsV_HI', 'numGatesV_HI', 'RadialVelocity', 'timeV', 'elevationV', 'azimuthV', 'distanceV', 'numRadialsV', 'numGatesV', 'SpectrumWidth_HI', 'SpectrumWidth', 'DifferentialReflectivity_HI', 'timeD_HI', 'elevationD_HI', 'azimuthD_HI', 'distanceD_HI', 'numRadialsD_HI', 'numGatesD_HI', 'DifferentialReflectivity', 'timeD', 'elevationD', 'azimuthD', 'distanceD', 'numRadialsD', 'numGatesD', 'CorrelationCoefficient_HI', 'timeC_HI', 'elevationC_HI', 'azimuthC_HI', 'distanceC_HI', 'numRadialsC_HI', 'numGatesC_HI', 'CorrelationCoefficient', 'timeC', 'elevationC', 'azimuthC', 'distanceC', 'numRadialsC', 'numGatesC', 'DifferentialPhase_HI', 'timeP_HI', 'elevationP_HI', 'azimuthP_HI', 'distanceP_HI', 'numRadialsP_HI', 'numGatesP_HI', 'DifferentialPhase', 'timeP', 'elevationP', 'azimuthP', 'distanceP', 'numRadialsP', 'numGatesP']

Top