Plot TICON Stations
This notebook downloads and calculates tide predictions using constituents provided by the TICON-4 database.
Python Dependencies
Program Dependencies
astro.py: computes the basic astronomical mean longitudesconstituents.py: calculates constituent parameters and nodal argumentspredict.py: predict tidal values using harmonic constants
Note
This notebook uses Jupyter widgets to set parameters for calculating the tidal time series.
import timescale
import ipywidgets
import ipyleaflet
import pandas as pd
import pyTMD.predict
import matplotlib.dates as mdates
import matplotlib.pyplot as plt
Download and read TICON database
# TICON-4: TIdal CONstants based on GESLA-4 sea-level records
url = "https://www.seanoe.org/data/00980/109129/data/122848.csv"
# read tide gauge data
tg = pd.read_csv(url, parse_dates=["start_date", "end_date"], dayfirst=True)
# rename amplitude and phase columns
tg.rename(columns={"amp": "amplitude", "pha": "phase"}, inplace=True)
# convert into a dictionary of dataframes for each tide gauge
dataframes = {k.split("-", 1)[0]: v for k, v in tg.groupby("tide_gauge_name")}
Create leaflet map for selecting TICON station
%matplotlib widget
# widgets for selecting date range
TMDwidgets = pyTMD.tools.widgets()
# use a week of predictions as a default
start_date = pd.Timestamp.now().floor(freq="d")
end_date = start_date + pd.Timedelta("7 day")
# create date pickers for start and end dates
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,
)
# create leaflet map
m = pyTMD.tools.leaflet(
center=(48.375, -124.5),
zoom=9,
layer_control=False,
attribution=False,
)
# use Neah Bay (WA) as default station
m.station_name = "neah_bay"
# create empty plot for tide heights
fig, ax1 = plt.subplots(num=1)
(l1,) = ax1.plot([], [], color="mediumseagreen")
ax1.grid(linestyle="-", axis="x")
date_formatter = mdates.DateFormatter("%Y-%m-%d")
ax1.xaxis.set_major_formatter(date_formatter)
ax1.set_xlabel("Time [UTC]")
ax1.set_ylabel("Water Level Height [cm]")
axtitle = ax1.set_title(None)
fig.subplots_adjust(left=0.15, right=0.98, bottom=0.10, top=0.95)
fig.autofmt_xdate()
# calculate tide prediction and plot
def update_tide_prediction(*args, **kwargs):
# get start and end dates for predictions
startdate = TMDwidgets.start_date.value.strftime("%Y%m%d")
enddate = TMDwidgets.end_date.value.strftime("%Y%m%d")
ts = timescale.from_range(startdate, enddate, 1, "h")
# copy dataframe for station
df = dataframes[m.station_name].copy()
station_title = m.station_name.replace("_", " ")
# station longitude and latitude
lon = df["lon"].iloc[0]
lat = df["lat"].iloc[0]
# set the index to the constituent name
df.set_index("con", inplace=True)
# change notation of some tidal constituents
# use prime notation for 3rd degree tides
df.rename(index={"3L2": "L2'", "3N2": "N2'"}, inplace=True)
# reset and rename station index column
df.reset_index(inplace=True)
df.index.rename("constNum", inplace=True)
# parse harmonic constituents
df["constituent"] = df["con"].apply(pyTMD.constituents._parse_name)
# predict tides using tmd dataframe accessor
tpred = df.tmd.predict(ts.tide, deltat=ts.tt_ut1, corrections="GOT")
# infer minor constituents
tpred += df.tmd.infer(ts.tide, deltat=ts.tt_ut1, corrections="GOT")
# update plot with predicted values
l1.set_data(ts.to_datetime(), tpred)
# update plot title
axtitle.set_text(f"{station_title} ({lat:0.2f}\u00b0N {lon:0.2f}\u00b0E)")
# rescale and redraw figure
ax1.relim()
ax1.autoscale_view()
fig.canvas.draw()
# set station name when feature is clicked
def handle_click(feature, **kwargs):
"""callback for handling mouse clicks"""
m.station_name = feature["properties"]["name"]
# create tooltip when feature is hovered
def handle_hover(feature, **kwargs):
"""callback for creating hover tooltips"""
m.tooltip.value = feature["properties"]["name"]
m.tooltip.layout.height = "10px"
m.tooltip.layout.visibility = "visible"
m.map.add(m.hover_control)
# remove tooltip upon mouseout of feature
def handle_mouseout(_, content, buffers):
"""callback for removing hover tooltips upon mouseout"""
event_type = content.get("type", "")
if event_type == "mouseout":
m.tooltip.value = ""
m.tooltip.layout.visibility = "hidden"
m.map.remove(m.hover_control)
# run for default station
update_tide_prediction()
# create GeoJSON with data for each station
GeoJSON = {"type": "FeatureCollection", "features": []}
for k, v in dataframes.items():
feature = {"type": "Feature"}
feature["properties"] = {"name": k}
lon, lat = (v["lon"].iloc[0], v["lat"].iloc[0])
feature["geometry"] = {"type": "Point", "coordinates": [lon, lat]}
GeoJSON["features"].append(feature)
# create ipyleaflet GeoJSON layer
m.geojson = ipyleaflet.GeoJSON(
data=GeoJSON,
point_style={"radius": 2.0, "fillOpacity": 0.5, "weight": 3.0},
hover_style={"color": "yellow"},
)
# create tooltip for station names
m.tooltip = ipywidgets.HTML()
m.tooltip.layout.margin = "0px 20px 20px 20px"
m.tooltip.layout.visibility = "hidden"
# create widget for hover tooltips
m.hover_control = ipyleaflet.WidgetControl(
widget=m.tooltip, position="bottomright"
)
# handle interactions with GeoJSON points
# set station name upon click
m.geojson.on_click(handle_click)
# update tide predictions for station
m.geojson.on_click(update_tide_prediction)
# show station name upon hover
m.geojson.on_hover(handle_hover)
m.geojson.on_msg(handle_mouseout)
# update tide predictions for date range change
TMDwidgets.start_date.observe(update_tide_prediction)
TMDwidgets.end_date.observe(update_tide_prediction)
# add geojson of stations to map
m.map.add(m.geojson)
# display widgets
TMDwidgets.VBox([m.map, TMDwidgets.start_date, TMDwidgets.end_date])