Siphon is a Python module for accessing data hosted on a THREDDS data server. Siphon works by parsing the catalog XML and exposing it with higher level functions.
In this notebook we will explore data available on the Central & Northern California Ocean Observing System (CeNCOOS) THREDDS. The cell below extracts the catalog information
In [1]:
from siphon.catalog import TDSCatalog
catalog = TDSCatalog('https://thredds.cencoos.org/thredds/catalog.xml')
info = """
Catalog information
-------------------
Base THREDDS URL: {}
Catalog name: {}
Catalog URL: {}
Metadata: {}
""".format(catalog.base_tds_url,
catalog.catalog_name,
catalog.catalog_url,
catalog.metadata)
print(info)
Unfortunately this catalog has no metadata. So let's check what kind of services are available.
In [2]:
for service in catalog.services:
print(service.name)
And what datasets are there?
In [3]:
print('\n'.join(catalog.datasets.keys()))
It looks like model runs as well as satellite and HFR data. One can also check the catalog refs for more information
In [4]:
print('\n'.join(catalog.catalog_refs.keys()))
In [5]:
ref = catalog.catalog_refs['Global']
[value for value in dir(ref) if not value.startswith('__')]
Out[5]:
In [6]:
info = """
Href: {}
Name: {}
Title: {}
""".format(
ref.href,
ref.name,
ref.title)
print(info)
The follow method navigates to that catalog ref and returns a new siphon.catalog.TDSCatalog object for that part of the THREDDS catalog.
In [7]:
cat = ref.follow()
print(type(cat))
That makes it easier to explore a small subset of the datasets available in the catalog. Here are the data from the Global subset.
In [8]:
print('\n'.join(cat.datasets.keys()))
Let's extract the Global 1-km Sea Surface Temperature dataset from the global ref.
In [9]:
dataset = 'Global 1-km Sea Surface Temperature (G1SST)'
ds = cat.datasets[dataset]
ds.name, ds.url_path
Out[9]:
Siphon has a ncss (NetCDF subset service) access, here is a quote from the documentation:
This module contains code to support making data requests to the NetCDF subset service (NCSS) on a THREDDS Data Server (TDS). This includes forming proper queries as well as parsing the returned data.
Let's check if the catalog offers the NetcdfSubset in the access_urls.
In [10]:
for name, ds in catalog.datasets.items():
if ds.access_urls:
print(name)
All access_urls returned empty.... Maybe that is just a metadata issue because there is NetcdfSubset access when navigating in the webpage.
In [11]:
from IPython.display import HTML
iframe = '<iframe src="{src}" width="800" height="550" style="border:none;"></iframe>'.format
url = 'https://thredds.cencoos.org/thredds/catalog.html?dataset=G1_SST_US_WEST_COAST'
HTML(iframe(src=url))
Out[11]:
To finish the post let's check if there is any WMS service available and overlay the data in a slippy (interactive) map.
In [12]:
services = [service for service in catalog.services if service.name == 'wms']
services
Out[12]:
Found only one, let's tease that out and check the URL.
In [13]:
service = services[0]
url = service.base
url
Out[13]:
OWSLib helps to inspect the available layers before plotting. Here we will get the first layer that has G1_SST_US_WEST_COAST on it.
Note, however, we are skipping the discovery step of the wms information and hard-coding it instead.
That is to save time because parsing the URL http://pdx.axiomalaska.com/ncWMS/wms takes ~ 10 minutes. See this issue for more information.
In [14]:
from owslib.wms import WebMapService
if False:
web_map_services = WebMapService(url)
layer = [key for key in web_map_services.contents.keys() if 'G1_SST_US_WEST_COAST' in key][0]
wms = web_map_services.contents[layer]
title = wms.title
lon = (wms.boundingBox[0] + wms.boundingBox[2]) / 2.
lat = (wms.boundingBox[1] + wms.boundingBox[3]) / 2.
time = wms.defaulttimeposition
else:
layer = 'G1_SST_US_WEST_COAST/analysed_sst'
title = 'Sea Surface Temperature'
lon, lat = -122.50, 39.50
time = 'undefined'
In [15]:
import folium
m = folium.Map(location=[lat, lon], zoom_start=4)
folium.WmsTileLayer(
name='{} at {}'.format(title, time),
url=url,
layers=layer,
fmt='image/png',
transparent=True,
).add_to(m)
folium.LayerControl().add_to(m)
m
Out[15]:
Last but not least a static image for the page thumbnail.
In [16]:
from IPython.display import Image
Image(m._to_png())
Out[16]: