Compare NOAA Tides
This (notebook
) downloads and plots measurements and forecasts from NOAA Tides and Currents. Predictions use the harmonic constituents provided by the National Oceanic and Atmospheric Administration (NOAA) for the selected station.
Python Dependencies
Program Dependencies
arguments.py
: load the nodal corrections for tidal constituentsastro.py
: computes the basic astronomical mean longitudesio.constituents.py
: basic tide model constituent classpredict.py
: predict tidal values using harmonic constantstime.py
: utilities for calculating time operations
This notebook uses Jupyter widgets to set parameters for calculating the tidal time series.
import pyTMD
import pandas
import logging
import timescale
import traceback
import ipywidgets
import numpy as np
import matplotlib.dates as mdates
import matplotlib.pyplot as plt
# create logger
logging.basicConfig(level=logging.INFO)
Functions for querying NOAA webservices
def build_query(api, **kwargs):
# NOAA webservices hosts
HOST = 'https://tidesandcurrents.noaa.gov/axis/webservices'
OPENDAP = 'https://opendap.co-ops.nos.noaa.gov/axis/webservices'
# NOAA webservices query arguments
arguments = '?format=xml'
for key, value in kwargs.items():
arguments += f'&{key}={value}'
arguments += '&Submit=Submit'
# NOAA API query url
url = f'{HOST}/{api}/response.jsp{arguments}'
# lxml namespaces for parsing
namespaces = {}
namespaces['wsdl'] = f'{OPENDAP}/{api}/wsdl'
return (url, namespaces)
def from_xml(url, **kwargs):
# query the NOAA webservices API
try:
logging.debug(url)
df = pandas.read_xml(url, **kwargs)
except ValueError:
logging.error(traceback.format_exc())
# return the dataframe
else:
return df
Query NOAA webservices for list of tide stations
xpath = '//wsdl:station'
url, namespaces = build_query('tidepredictionstations')
stations = from_xml(url, xpath=xpath, namespaces=namespaces).set_index('name')
stations = stations.sort_index().drop(columns=['metadata'])
Select NOAA station and dates for prediction
# display widgets
TMDwidgets = pyTMD.tools.widgets()
# create dropdown with all tide prediction station data
TMDwidgets.stations = ipywidgets.Dropdown(
options=stations.index,
value='La Jolla (Scripps Institution Wharf)',
description='Tide Stations:',
disabled=False,
style=TMDwidgets.style,
)
# create date pickers for start and end dates
end_date = pandas.Timestamp.now().floor(freq='d')
start_date = end_date - pandas.Timedelta('2 day')
TMDwidgets.start_date = ipywidgets.DatePicker(
description='Start Date:',
value=start_date,
disabled=False,
style=TMDwidgets.style,
)
TMDwidgets.end_date = ipywidgets.DatePicker(
description='End Date:',
value=end_date,
disabled=False,
style=TMDwidgets.style,
)
# display widgets
TMDwidgets.VBox([
TMDwidgets.stations,
TMDwidgets.start_date,
TMDwidgets.end_date
])
Predict tidal time series
# get station name and indicator
station_row = stations.iloc[TMDwidgets.stations.index]
station_name = station_row.name
station_id = station_row.ID
# set query parameters
xpath = '//wsdl:item'
unit = 0
timeZone = 0
startdate = TMDwidgets.start_date.value.strftime('%Y%m%d')
enddate = TMDwidgets.end_date.value.strftime('%Y%m%d')
datum = 'MSL'
# get harmonic constituents for station
url, namespaces = build_query('harmonicconstituents',
stationId=station_id, unit=unit, timeZone=timeZone)
hcons = from_xml(url, xpath=xpath, namespaces=namespaces)
# get water levels for station and date range
url, namespaces = build_query('waterlevelrawsixmin',
stationId=station_id, unit=unit, timeZone=timeZone,
beginDate=startdate, endDate=enddate, datum=datum)
wlevel = from_xml(url, xpath=xpath, namespaces=namespaces,
parse_dates=['timeStamp'])
# parse harmonic constituents
c = [pyTMD.io.constituents.parse(row['name']) for i, row in hcons.iterrows()]
# calculate complex phase in radians for Euler's
cph = -1j*hcons.phase*np.pi/180.0
# calculate constituent oscillation
hc = hcons.amplitude*np.exp(cph)
# predict tides at water level timestamps
ts = timescale.from_datetime(wlevel.timeStamp)
TIDE = pyTMD.predict.time_series(ts.tide, hc.values, c,
deltat=ts.tt_ut1, corrections='GOT')
# infer minor tidal constituents
TIDE += pyTMD.predict.infer_minor(ts.tide, hc.values, c,
deltat=ts.tt_ut1, corrections='GOT')
Compare measured tide values with predictions
fig, ax = plt.subplots(num=1)
ax.plot(wlevel.timeStamp, wlevel.WL, color='mediumseagreen', label='MSL')
ax.fill_between(wlevel.timeStamp, wlevel.WL-wlevel.sigma,
y2=wlevel.WL+wlevel.sigma, zorder=1,
color='mediumseagreen', alpha=0.35)
ax.plot(wlevel.timeStamp, TIDE, color='darkorchid', label='Tides')
ax.grid(linestyle='-', axis='x')
ax.set_title(station_name)
ax.set_ylabel('Water Level Height [m]')
lgd = ax.legend(frameon=True)
lgd.get_frame().set_boxstyle('square,pad=0.0')
lgd.get_frame().set_edgecolor("white")
lgd.get_frame().set_alpha(1.0)
for line in lgd.get_lines():
line.set_linewidth(6)
date_formatter = mdates.DateFormatter("%Y-%m-%d %H:%M")
ax.xaxis.set_major_formatter(date_formatter)
fig.autofmt_xdate()
plt.show()