<jupyter_start><jupyter_text>Working with Surface Observations in Siphon and MetPyUnidata Python Workshop Questions1. What's the best way to get surface station data from a THREDDS data server?1. What's the best way to make a station plot of data?1. How can I request a time series of data for a single station? Objectives1. Getting METARs from THREDDS1. Parse the Data1. Making a Station Plot1. Time Series Request and Plot Getting METARs from THREDDS 1. Getting METARs from THREDDSWe can get the current METARS from the THREDDS test server (it's not yet available on the main thredds.ucar.edu). Head over to https://thredds-test.unidata.ucar.edu/ and navigate to the NOAAport products and find the files.<jupyter_code>from siphon.catalog import TDSCatalog
cat = TDSCatalog('https://thredds-test.unidata.ucar.edu/thredds/catalog/noaaport/text/metar/catalog.xml')
ds = cat.datasets[0]
# Ideally we would use remote_open, but because of a soon to be fixed bug we
# have to download locally and open that way.
# fobj = ds.remote_open()
ds.download()<jupyter_output><empty_output><jupyter_text>Open the file and take a look - aren't you glad MetPy can parse this for you? Top  2. Parse the DataMetPy can parse the METAR data for us into a dataframe that the declarative plotting inface can work with nicely.<jupyter_code>from metpy.io import parse_metar_file
df = parse_metar_file(ds.name)
df.head()<jupyter_output><empty_output><jupyter_text>Let's take a bit of a closer look at the columns that are available to use as well.<jupyter_code>list(df.columns)<jupyter_output><empty_output><jupyter_text>Top  3. Making a Station Plot<jupyter_code>from datetime import datetime, timedelta

import cartopy.crs as ccrs

from metpy.plots.declarative import *
obs = PlotObs()
obs.data = df
obs.time = datetime.utcnow()
obs.level = None
obs.fields = ['air_temperature']
obs.locations = ['NW']
obs.colors = ['tab:red']
obs.formats = [None]
obs.vector_field = ['eastward_wind', 'northward_wind']
obs.reduce_points = 0.5

panel = MapPanel()
panel.area = 'ma'
panel.projection = ccrs.PlateCarree()
panel.layers = ['coastline', 'borders', 'states']

panel.plots = [obs]

pc = PanelContainer()
pc.size = (10, 10)
pc.panels = [panel]

pc.show()<jupyter_output><empty_output><jupyter_text>EXERCISE:Make a surface plot that plots:      Temperature in red, NW plot area     Dewpoint in green, SW plot area     Altimeter setting in black, NE plot area     Sky coverage in black, Center plot area     Set the title to something relevantBONUS: Format the altimeter setting in the "traditional" way (multiply by 10 and show only the last three digits of the integer value). i.e. 1014.56 becomes 145.<jupyter_code># Make the observation plot

# Make the map panel

# Make the panel container

# Show the plot<jupyter_output><empty_output><jupyter_text>SOLUTION<jupyter_code># %load solutions/dec_basic_sfc_plot.py<jupyter_output><empty_output><jupyter_text>Top  4. Time Series Request and PlotLet's say we want the past days worth of data for a lat/lon point for the variables mean sea level pressure, air temperature, wind direction, and wind speed. We do not have time series plots in a declarative way yet, but these plots are relatively straight forward to create and it's another way we can query THREDDS to get data.<jupyter_code>metar_cat_url = ('http://thredds.ucar.edu/thredds/catalog/'
                 'irma/metar/catalog.xml?dataset=irma/metar/Metar_Station_Data_-_Irma_fc.cdmr')

catalog = TDSCatalog(metar_cat_url)

metar_dataset = catalog.datasets['Feature Collection']

ncss = metar_dataset.subset()
# Define the time range we are interested in
end_time = datetime(2017, 9, 12, 0)
start_time = end_time - timedelta(days=2)

# Build the query
query = ncss.query()
query.lonlat_point(-80.25, 25.8)
query.time_range(start_time, end_time)
query.variables('altimeter_setting', 'temperature', 'dewpoint',
                'wind_direction', 'wind_speed')
query.accept('csv')
# Get the data
data = ncss.get_data(query)
# Make sure we got what we asked for
print(list(data.keys()))<jupyter_output><empty_output><jupyter_text>Cleanup the Data<jupyter_code>import pandas as pd
df = pd.DataFrame(data)
df.head()
# Parse the date time stamps
df['time'] = pd.to_datetime(df['time'].str.decode('utf-8'), infer_datetime_format=True)
df.head()
# Station names are bytes, we need to convert them to strings
df['station'] = df['station'].str.decode('utf-8')
df.head()<jupyter_output><empty_output><jupyter_text>Make a Time Series Plot<jupyter_code>ax = df.plot(x='time', y='wind_speed',
             title=f"{df['station'][0]} {df['time'][0]:%Y/%m/%d}",
             grid=True,
             figsize=(10, 6))
from matplotlib.dates import DateFormatter, AutoDateLocator
# Improve on the default ticking
locator = AutoDateLocator()
hoursFmt = DateFormatter('%H')
ax.xaxis.set_major_locator(locator)
ax.xaxis.set_major_formatter(hoursFmt)
fig = ax.get_figure()
fig<jupyter_output><empty_output><jupyter_text>EXERCISE:             Pick a different location.        Plot temperature and dewpoint together on the same plot.        Use ax.set.xlabel and the corresponding y label to set sensible labels.<jupyter_code># Your code goes here

# %load solutions/pd_time_series.py<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09990503323836657, 'fraction_numerical': 0.009876543209876543, 'mean_word_length': 5.0389908256880735, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 41, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '10', 'dir': 'jupyter-structured-clean-dedup', 'id': '159360', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 1773, 'n_tokens_neox': 1700, 'n_words': 549, 'path': 'pages/workshop/Surface_Data/Declarative Surface Observations.ipynb', 'repo_name': 'julienchastang/python-training', 'size': '5265'}