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 longitudes

  • constituents.py: calculates constituent parameters and nodal arguments

  • predict.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])