In [ ]:
from datetime import datetime, timedelta
from siphon.catalog import TDSCatalog
from siphon.ncss import NCSS
import numpy as np
import metpy.calc as mpcalc
from metpy.units import units, concatenate
from metpy.plots import SkewT
Just some helper code to make things easier. metpy_units_handler plugins into siphon to automatically add units to variables. post_process_data is used to clean up some oddities from the NCSS point feature collection.
In [ ]:
units.define('degrees_north = 1 degree')
units.define('degrees_east = 1 degree')
unit_remap = dict(inches='inHg', Celsius='celsius')
def metpy_units_handler(vals, unit):
arr = np.array(vals)
if unit:
unit = unit_remap.get(unit, unit)
arr = arr * units(unit)
return arr
# Fix dates and sorting
def sort_list(list1, list2):
return [l1 for (l1, l2) in sorted(zip(list1, list2), key=lambda i: i[1])]
def post_process_data(data):
data['time'] = [datetime.strptime(d.decode('ascii'), '%Y-%m-%d %H:%M:%SZ') for d in data['time']]
ret = dict()
for key,val in data.items():
try:
val = units.Quantity(sort_list(val.magnitude.tolist(), data['time']), val.units)
except AttributeError:
val = sort_list(val, data['time'])
ret[key] = val
return ret
First we need to grab the catalog for the METAR feature collection data from http://thredds.ucar.edu/thredds/catalog.html
In [ ]:
cat = TDSCatalog('http://thredds.ucar.edu/thredds/catalog/nws/metar/ncdecoded/catalog.xml?dataset=nws/metar/ncdecoded/Metar_Station_Data_fc.cdmr')
Set up NCSS access to the dataset
In [ ]:
ds = list(cat.datasets.values())[0]
In [ ]:
ncss = NCSS(ds.access_urls['NetcdfSubset'])
ncss.unit_handler = metpy_units_handler
What variables do we have available?
In [ ]:
ncss.variables
Create a query for the last 7 days of data for a specific lon/lat point. We should ask for: air temperature, dewpoint temperature, wind speed, and wind direction.
In [ ]:
now = datetime.utcnow()
query = ncss.query().accept('csv')
query.lonlat_point(-97, 35.25).time_range(now - timedelta(days=7), now)
query.variables('air_temperature', 'dew_point_temperature', 'wind_speed', 'wind_from_direction')
Get the data
In [ ]:
data = ncss.get_data(query)
data = post_process_data(data) # Fixes for NCSS point
First, we need relative humidity: $$RH = e / e_s$$
In [ ]:
e = mpcalc.saturation_vapor_pressure(data['dew_point_temperature'])
e_s = mpcalc.saturation_vapor_pressure(data['air_temperature'])
rh = e / e_s
Calculate heat index:
In [ ]:
# RH should be [0, 100]
hi = mpcalc.heat_index(data['air_temperature'], rh * 100)
Plot the temperature, dewpoint, and heat index. Bonus points to also plot wind speed and direction.
In [ ]:
import matplotlib.pyplot as plt
times = data['time']
fig, axes = plt.subplots(2, 1, figsize=(9, 9))
axes[0].plot(times, data['air_temperature'].to('degF'), 'r', linewidth=2)
axes[0].plot(times, data['dew_point_temperature'].to('degF'), 'g', linewidth=2)
axes[0].plot(times, hi, color='darkred', linestyle='--', linewidth=2)
axes[0].grid(True)
axes[1].plot(times, data['wind_speed'].to('mph'), 'b')
twin = plt.twinx(axes[1])
twin.plot(times, data['wind_from_direction'], 'kx')
First grab the catalog for the Best dataset from the GSD HRRR from http://thredds.ucar.edu/thredds/catalog.html
In [ ]:
cat = TDSCatalog('http://thredds-jumbo.unidata.ucar.edu/thredds/catalog/grib/HRRR/CONUS_3km/wrfprs/catalog.xml?dataset=grib/HRRR/CONUS_3km/wrfprs/Best')
Set up NCSS access to the dataset
In [ ]:
best_ds = list(cat.datasets.values())[0]
In [ ]:
ncss = NCSS(best_ds.access_urls['NetcdfSubset'])
ncss.unit_handler = metpy_units_handler
What variables do we have?
In [ ]:
ncss.variables
Set up a query for the most recent set of data from a point. We should request temperature, dewpoint, and U and V.
In [ ]:
query = ncss.query().accept('csv')
query.lonlat_point(-105, 40).time(datetime.utcnow())
query.variables('Temperature_isobaric', 'Dewpoint_temperature_isobaric',
'u-component_of_wind_isobaric', 'v-component_of_wind_isobaric')
Get the data
In [ ]:
data = ncss.get_data(query)
In [ ]:
T = data['Temperature_isobaric'].to('degC')
Td = data['Dewpoint_temperature_isobaric'].to('degC')
p = data['vertCoord'].to('mbar')
Plot a sounding of the data
In [ ]:
fig = plt.figure(figsize=(9, 9))
skew = SkewT(fig=fig)
skew.plot(p, T, 'r')
skew.plot(p, Td, 'g')
skew.ax.set_ylim(1050, 100)
skew.plot_mixing_lines()
skew.plot_dry_adiabats()
skew.plot_moist_adiabats()
Also calculate the parcel profile and add that to the plot
In [ ]:
prof = mpcalc.parcel_profile(p[::-1], T[-1], Td[-1])
skew.plot(p[::-1], prof.to('degC'), 'k', linewidth=2)
fig
Let's also plot the location of the LCL and the 0 isotherm as well:
In [ ]:
lcl = mpcalc.lcl(p[-1], T[-1], Td[-1])
lcl_temp = mpcalc.dry_lapse(concatenate((p[-1], lcl)), T[-1])[-1].to('degC')
skew.plot(lcl, lcl_temp, 'bo')
skew.ax.axvline(0, color='blue', linestyle='--', linewidth=2)
fig