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')
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')
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')
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:
Maximum curvature (Stockdon, Sallenger, Holman, & Howd, 2007) - the dune toe is defined as the location of maximum slope change;
Relative relief (Wernette, Houser, & Bishop, 2016) - the dune toe is defined based on relative relief (the ratio of local morphology to computational scale);
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,
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:
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));
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)).
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()