Narrabeen beach profile adjustment

This notebook is a companion to your practical.

After following the instructuions in the practical, you will have already downloaded your profiles as a .csv files (comma-separated values).

Note

CSV is a commonly used data format that opens easily in a range of software such as Notepad, Microsoft Excel, and obviously Python.

Loading the necessary libraries

import os
import pickle
import joblib
import numpy as np
import pandas as pd
import datetime as dt

import matplotlib.pyplot as plt
import matplotlib.dates as mdates

from beach.beach import Profile
from beach.support import data_support as ds
from beach.support import classifier_support as cs

from pylab import *

from pandas.plotting import register_matplotlib_converters
register_matplotlib_converters()

import warnings
warnings.simplefilter("ignore", UserWarning)

# Plotting Paramaters
params = {'legend.fontsize': 10,
           'axes.labelsize': 10,
           'axes.labelweight': 'regular',
           'axes.titlesize': 11,
           'axes.titleweight': 'regular',
           'xtick.labelsize': 10,
           'ytick.labelsize': 10,
           'mathtext.default':'regular'}
plt.rcParams.update(params)

%config InlineBackend.figure_format = 'svg' 
%matplotlib inline

View profiles

The downloaded data can be visualised using Pandas. Here, I have extracted some of Narrabeen profiles in a file located in the pracenv/dataset folder:

dataframe = pd.read_csv('../pracenv/dataset/Narrabeen_Profiles.csv', encoding="utf8") 
dataframe
Site Profile ID Date Chainage Elevation Flag
0 NARRA PF4 1976-04-27 0 9.52 EMERY
1 NARRA PF4 1976-04-27 10 7.12 EMERY
2 NARRA PF4 1976-04-27 20 5.88 EMERY
3 NARRA PF4 1976-04-27 30 3.88 EMERY
4 NARRA PF4 1976-04-27 40 1.56 EMERY
... ... ... ... ... ... ...
158583 NARRA PF2 2019-11-27 124 -0.92 GPS
158584 NARRA PF2 2019-11-27 125 -0.95 GPS
158585 NARRA PF2 2019-11-27 126 -0.96 GPS
158586 NARRA PF2 2019-11-27 127 -0.98 GPS
158587 NARRA PF2 2019-11-27 128 -1.04 GPS

158588 rows × 6 columns

We will redefine the header to make it easier for the rest of the manipulation:

# Manually define names for each column
names = [
    "Site",
    "Profile",
    "date",
    "x",
    "z",
    "Flag"
]
df = pd.read_csv('../pracenv/dataset/Narrabeen_Profiles.csv', encoding="utf8", names=names, skiprows=1)
dates = pd.to_datetime(df['date'], format = '%Y-%m-%d')

# Append a new column at the end of our iribarren values
df["time"] = dates
df
Site Profile date x z Flag time
0 NARRA PF4 1976-04-27 0 9.52 EMERY 1976-04-27
1 NARRA PF4 1976-04-27 10 7.12 EMERY 1976-04-27
2 NARRA PF4 1976-04-27 20 5.88 EMERY 1976-04-27
3 NARRA PF4 1976-04-27 30 3.88 EMERY 1976-04-27
4 NARRA PF4 1976-04-27 40 1.56 EMERY 1976-04-27
... ... ... ... ... ... ... ...
158583 NARRA PF2 2019-11-27 124 -0.92 GPS 2019-11-27
158584 NARRA PF2 2019-11-27 125 -0.95 GPS 2019-11-27
158585 NARRA PF2 2019-11-27 126 -0.96 GPS 2019-11-27
158586 NARRA PF2 2019-11-27 127 -0.98 GPS 2019-11-27
158587 NARRA PF2 2019-11-27 128 -1.04 GPS 2019-11-27

158588 rows × 7 columns

Plotting beach profiles

We will now define a function that will take a specific profile ID PID and a start and end time and will plot the existing profiles for the considered period:

# Function definition
def plotProfiles(PID, start_date, end_date):
    '''
    This function plot the profiles for a specific transect based on specified period of time.
    
    args:
    - PID: transect ID
    - start_date: initial date requested in year-month-day
    - end_date: end date requested in year-month-day
    '''
    
    mask = (df['time'] >= start_date) & (df['time'] <= end_date)
    df_time = df.loc[mask]

    timeval = df_time.time.unique()
    timeval = pd.to_datetime(timeval)

    profiletime = []
    for k in range(len(timeval)):
        profiletime.append(str(timeval[k].date()))
    
    MyDateFormatter = DateFormatter('%d-%b-%Y')
    fig = plt.figure(figsize=(11,4), dpi=160) 

    ax1 = plt.gca()

    ax1.plot([0, 200], [0, 0], "k--", linewidth=1)

    maxx = 0.

    for k in range(len(profiletime)):
        ids1 = np.where(np.logical_and(df_time.Profile==PID,df_time.time==profiletime[k]))[0]
        maxx = max(maxx,df_time['x'].iloc[ids1].max())
        ax1.plot(df_time['x'].iloc[ids1],df_time['z'].iloc[ids1],linewidth=2,label=profiletime[k]) 


    ax1.set_ylim(-5, 10)
    ax1.set_xlim(0, maxx)

    ax1.grid(which='major', axis='y', linestyle='--')

    ax1.set_ylabel('Elevation (AHD)')
    ax1.set_xlabel('Chainage (m)')
    ax1.legend()

    plt.title('Narrabeen-Collaroy Beach Profiles '+str(PID),fontsize=11)
    ax1.legend(loc='upper center', bbox_to_anchor=(0.5, -0.2), shadow=False, ncol=5, frameon=False)
    
    return

We now apply the function. Here we chose the profile PF1

plotProfiles(PID='PF1', start_date = '2007-5-1', end_date = '2007-8-1')
../_images/selectprofiles_9_0.svg

We can now call the function to plot another profile at another time interval:

plotProfiles(PID='PF4', start_date = '2007-2-1', end_date = '2007-6-1')
../_images/selectprofiles_11_0.svg

Extract one single profile

We can slightly change our intial function to only plot the profile closer to a requested time:

def getOneProfile(PID, date):
    '''
    Plot one single profile based on a specific data.
     
    args:
    - PID: transect ID
    - date: initial date requested in year-month-day
    '''
    
    mask = (df['time'] >= date) & (df['time'] <= date)
    df_time = df.loc[mask]

    timeval = df_time.time.unique()
    timeval = pd.to_datetime(timeval)

    profiletime = []
    profiletime.append(str(timeval[0].date()))
    
    MyDateFormatter = DateFormatter('%d-%b-%Y')
    fig = plt.figure(figsize=(11,4), dpi=160) 

    ax1 = plt.gca()

    ax1.plot([0, 200], [0, 0], "k--", linewidth=1)

    maxx = 0.

    ids1 = np.where(np.logical_and(df_time.Profile==PID,df_time.time==profiletime[0]))[0]
    maxx = max(maxx,df_time['x'].iloc[ids1].max())
    ax1.plot(df_time['x'].iloc[ids1],df_time['z'].iloc[ids1],linewidth=2,label=profiletime[0]) 


    ax1.set_ylim(-5, 10)
    ax1.set_xlim(0, maxx)

    ax1.grid(which='major', axis='y', linestyle='--')

    ax1.set_ylabel('Elevation (AHD)')
    ax1.set_xlabel('Chainage (m)')
    ax1.legend()

    plt.title('Narrabeen-Collaroy Beach Profiles '+str(PID),fontsize=11)
    ax1.legend(loc='upper center', bbox_to_anchor=(0.5, -0.2), shadow=False, ncol=5, frameon=False)
    
    profile = pd.concat([df_time['x'].iloc[ids1], df_time['z'].iloc[ids1]], axis=1, keys=['x','z'])
    
    return profile
prof = getOneProfile(PID='PF2', date='2007-05-14')
../_images/selectprofiles_14_0.svg

Locating dune toe on transect

Using pybeach package as in this notebook, we can locate the dune toe on our cross-shore beach profile transect.

We can identify the location of the dune toe using the following methods:

  1. Maximum curvature (Stockdon, Sallenger, Holman, & Howd, 2007) - the dune toe is defined as the location of maximum slope change;

  2. Relative relief (Wernette, Houser, & Bishop, 2016) - the dune toe is defined based on relative relief (the ratio of local morphology to computational scale);

  3. Perpendicular distance - the dune toe is defined as the point of maximum perpendicular distance from the straight line drawn between the dune crest and shoreline; and,

  4. Machine learning using Random Forest classification

Stockdon, H. F., Sallenger, A. H., Holman, R. A., & Howd, P. A. (2007). A simple model for the spatially-variable coastal response to hurricanes. Marine Geology, 238(1-4), 1–20. doi:10.1016/j.margeo.2006.11.004

Wernette, P., Houser, C., & Bishop, M. P. (2016). An automated approach for extracting barrier island morphology from digital elevation models. Geomorphology, 262, 1–7. doi:10. 1016/j.geomorph.2016.02.024

Let’s try it on the latest profile we plotted:

# Profile data
x = prof['x'].to_numpy()
z = prof['z'].to_numpy()

# Instantiate
p = Profile(x, z)

Predict dune toe location

For the ML algorithm, three pre-trained ML models are provided with the pybeach package:

  1. a “barrier-island” model. This model was developed using 1046 pre- and post- “Hurricane Ivan” airborne LIDAR profiles from Santa-Rosa Island Florida (this data was collected in 2004 and is described in (Doran et al., 2018));

  2. a “wave-embayed” model. This model was developed using 1768 pre- and post- “June 2016 storm” airborne LIDAR profiles from the wave-dominated, embayed southeast Australian coastline (this data was collected in 2016 and is described in (Harley et al., 2017)).

  3. a “mixed” model. Developed using a combination of the two above datasets.

Let’s use in our case the “mixed” model for the Narrabeen beach:

# Make predictions of dune toe
toe_ml, prob_ml = p.predict_dunetoe_ml('mixed_clf') # use the machine learning (ML) method
toe_mc = p.predict_dunetoe_mc() # use the maximum curvature (MC) method
toe_rr = p.predict_dunetoe_rr() # use the relative relief (RR) method
toe_pd = p.predict_dunetoe_pd() # use the perpendicular distance (PD) method

# Predict dune crest
crest = p.predict_dunecrest()

# Predict shoreline
shoreline = p.predict_shoreline()

Plots

We will plot the profiles and the associated dune done predicted position:

n=0

fig, axes = plt.subplots(1, 1, figsize=(10, 4))
toes = [toe_ml[n], toe_mc[n], toe_rr[n], toe_pd[n]]

labels = ['Machine learning', 'Maximum curvature', 'Relative relief', 'Perpendicular distance']
colors = ['tomato', 'cornflowerblue', 'gold', 'limegreen']

axes.plot(x, z, '-k')
axes.fill_between([70, 105], [0, 0], y2=-1, color='lightskyblue', alpha=0.5)
axes.fill_between(x, z, y2=-1, color='cornsilk', alpha=1)
axes.axvspan(-10, -9, color='tomato', alpha = 0.6, label='ML Toe probability') # legend placeholder
for i, itoe in enumerate(toes): 
    axes.plot(x[itoe], z[itoe],
              'o', color=colors[i], ms=12, mec='k', label=labels[i])
axes.plot(x[crest[n]], z[crest[n]], 'v', color='k', ms=12, mec='k', label='Crest')
axes.plot(x[shoreline[n]], z[shoreline[n]], '^', color='k', ms=12, mec='k', label='Shoreline')
axes.set_xlim(0, 105)
axes.set_ylim(0, 10)
axes.set_title('Example profile')
axes.set_xlabel('Cross-shore distance (m)')
axes.set_ylabel('Elevation (m)')
axes.grid()
axes.legend(framealpha=1)

# Plot dune toe ML probability
axes2 = axes.twinx() 
axes2.fill_between(x, prob_ml[n], color='tomato', alpha=0.5)
axes2.set_ylim(0, 6)
axes2.set_yticks([0, 1])
axes2.tick_params(axis='y', labelcolor='tomato')
axes2.yaxis.set_label_coords(1.1, 0.1)
axes2.set_ylabel('Toe probability', color='tomato', rotation=270);
fig.show()
../_images/selectprofiles_21_0.svg