You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1375 lines
52 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Beach profile classifier\n",
"Using RANSAC algorithm"
]
},
{
"cell_type": "markdown",
"metadata": {
"heading_collapsed": true
},
"source": [
"## Setup notebook"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"hidden": true
},
"outputs": [],
"source": [
"# Enable autoreloading of our modules. \n",
"# Most of the code will be located in the /src/ folder, \n",
"# and then called from the notebook.\n",
"%matplotlib inline\n",
"%reload_ext autoreload\n",
"%autoreload"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"hidden": true
},
"outputs": [],
"source": [
"from IPython.core.debugger import set_trace\n",
"\n",
"import pandas as pd\n",
"import numpy as np\n",
"import os\n",
"import decimal\n",
"import plotly\n",
"import plotly.graph_objs as go\n",
"import plotly.plotly as py\n",
"import plotly.tools as tls\n",
"import plotly.figure_factory as ff\n",
"from plotly import tools\n",
"import plotly.io as pio\n",
"from scipy import stats\n",
"import math\n",
"import matplotlib\n",
"from matplotlib import cm\n",
"import colorlover as cl\n",
"import numpy.ma as ma\n",
"\n",
"from ipywidgets import widgets, Output\n",
"from IPython.display import display, clear_output, Image, HTML\n",
"\n",
"from sklearn.metrics import confusion_matrix\n",
"\n",
"import numpy as np\n",
"from matplotlib import pyplot as plt\n",
"\n",
"from sklearn import linear_model, datasets"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"hidden": true
},
"outputs": [],
"source": [
"# Matplot lib default settings\n",
"plt.rcParams[\"figure.figsize\"] = (10,3)\n",
"plt.rcParams['axes.grid']=True\n",
"plt.rcParams['grid.alpha'] = 0.5\n",
"plt.rcParams['grid.color'] = \"grey\"\n",
"plt.rcParams['grid.linestyle'] = \"--\""
]
},
{
"cell_type": "markdown",
"metadata": {
"heading_collapsed": true
},
"source": [
"## Import data"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"hidden": true
},
"outputs": [],
"source": [
"def df_from_csv(csv, index_col, data_folder='../data/interim'):\n",
" print('Importing {}'.format(csv))\n",
" return pd.read_csv(os.path.join(data_folder,csv), index_col=index_col)\n",
"\n",
"df_waves = df_from_csv('waves.csv', index_col=[0, 1])\n",
"df_tides = df_from_csv('tides.csv', index_col=[0, 1])\n",
"df_profiles = df_from_csv('profiles.csv', index_col=[0, 1, 2])\n",
"df_sites = df_from_csv('sites.csv', index_col=[0])\n",
"df_profile_features_crest_toes = df_from_csv('profile_features_crest_toes.csv', index_col=[0,1])\n",
"\n",
"# Note that the forecasted data sets should be in the same order for impacts and twls\n",
"impacts = {\n",
" 'forecasted': {\n",
" 'foreshore_slope_sto06': df_from_csv('impacts_forecasted_foreshore_slope_sto06.csv', index_col=[0]),\n",
" 'mean_slope_sto06': df_from_csv('impacts_forecasted_mean_slope_sto06.csv', index_col=[0]),\n",
" },\n",
" 'observed': df_from_csv('impacts_observed.csv', index_col=[0])\n",
" }\n",
"\n",
"\n",
"twls = {\n",
" 'forecasted': {\n",
" 'foreshore_slope_sto06': df_from_csv('twl_foreshore_slope_sto06.csv', index_col=[0, 1]),\n",
" 'mean_slope_sto06':df_from_csv('twl_mean_slope_sto06.csv', index_col=[0, 1]),\n",
" }\n",
"}\n",
"print('Done!')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Implement algorithm"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Get profile data"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def get_profile_data(site, profile_type, df_profiles):\n",
" \"\"\"\n",
" Returns a list of x and z coordinates for a given profile.\n",
" \"\"\"\n",
"\n",
" # Get x and z coorindates of profile\n",
" x = df_profiles.loc[(site, profile_type)].index.values\n",
" z = df_profiles.loc[(site, profile_type)].z.values\n",
"\n",
" # Get land limit\n",
" land_lim = df_profiles.loc[(site, 'poststorm')].land_lim.loc[0]\n",
"\n",
" # Remove nan values\n",
" m = ma.masked_invalid(z)\n",
" x = x[~m.mask]\n",
" z = z[~m.mask]\n",
"\n",
"# # Remove landwards of landlim\n",
"# mask = ma.masked_where(x < land_lim, x)\n",
"# x = x[~mask.mask]\n",
"# z = z[~mask.mask]\n",
"\n",
" return x, z\n",
"\n",
"\n",
"# Load profile data\n",
"# site = 'WAMBE0010'\n",
"# site = 'NINEMs0048'\n",
"# site = 'NAMB0013'\n",
"# site = 'AVOCAn0009'\n",
"# site = 'GRANTSs0014'\n",
"site = 'NARRA0001'\n",
"profile_type = 'prestorm'\n",
"x, z = get_profile_data(site, profile_type, df_profiles)\n",
"\n",
"print('x = {} ...'.format(x[0:5]))\n",
"print('z = {} ...'.format(z[0:5]))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"hide_input": true
},
"outputs": [],
"source": [
"\n",
"\n",
"\n",
"# # n_samples = 1000\n",
"# # n_outliers = 50\n",
"\n",
"\n",
"# # X, y, coef = datasets.make_regression(n_samples=n_samples, n_features=1,\n",
"# # n_informative=1, noise=10,\n",
"# # coef=True, random_state=0)\n",
"\n",
"# # # Add outlier data\n",
"# # np.random.seed(0)\n",
"# # X[:n_outliers] = 3 + 0.5 * np.random.normal(size=(n_outliers, 1))\n",
"# # y[:n_outliers] = -3 + 10 * np.random.normal(size=n_outliers)\n",
"\n",
"# # # Fit line using all data\n",
"# # lr = linear_model.LinearRegression()\n",
"# # lr.fit(X, y)\n",
"\n",
"# # Robustly fit linear model with RANSAC algorithm\n",
"# ransac = linear_model.RANSACRegressor()\n",
"# ransac.fit(x, z)\n",
"# inlier_mask = ransac.inlier_mask_\n",
"# outlier_mask = np.logical_not(inlier_mask)\n",
"\n",
"# # Predict data of estimated models\n",
"# line_x = np.arange(x.min(), x.max())[:, np.newaxis]\n",
"# line_y_ransac = ransac.predict(line_x)\n",
"\n",
"# lw = 2\n",
"# plt.scatter(x[inlier_mask], z[inlier_mask], color='yellowgreen', marker='.',\n",
"# label='Inliers')\n",
"# plt.scatter(x[outlier_mask], z[outlier_mask], color='gold', marker='.',\n",
"# label='Outliers')\n",
"# plt.plot(line_x, line_y_ransac, color='cornflowerblue', linewidth=lw,\n",
"# label='RANSAC regressor')\n",
"# plt.legend(loc='lower right')\n",
"# plt.xlabel(\"Input\")\n",
"# plt.ylabel(\"Response\")\n",
"# plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Fit linear interpolated spline to profile\n",
"- Linear interpolated spline used to simplify the profile.\n",
"- Need to do a bit of optimization to calculate the best smoothing parameter for the profile."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"code_folding": [
4,
11,
20,
25,
30,
42,
92
]
},
"outputs": [],
"source": [
"from scipy.interpolate import UnivariateSpline\n",
"from scipy.interpolate import interp1d\n",
"\n",
"\n",
"def pairwise(iterable):\n",
" \"s -> (s0,s1), (s1,s2), (s2, s3), ...\"\n",
" a, b = itertools.tee(iterable)\n",
" next(b, None)\n",
" return zip(a, b)\n",
"\n",
"\n",
"def xz(x_start, x_end, x, z):\n",
" \"\"\"\n",
" Returns a list of x coordinates and z coorindates which lie between x_start and x_end\n",
" \"\"\"\n",
" x_seg = [val for val in x if x_start <= val <= x_end]\n",
" z_seg = [z_val for x_val, z_val in zip(x, z) if x_start <= x_val <= x_end]\n",
" return x_seg, z_seg\n",
"\n",
"\n",
"def get_slope(x, z):\n",
" slope, intercept, r_value, p_value, std_err = linregress(x, z)\n",
" return slope\n",
"\n",
"\n",
"def get_r_squared(x, z):\n",
" slope, intercept, r_value, p_value, std_err = linregress(x, z)\n",
" return r_value**2\n",
"\n",
"\n",
"def rolling_std(a, w):\n",
" \"\"\"\n",
" Calculates a rolling window standard deviation\n",
" https://stackoverflow.com/a/40773366\n",
" \"\"\"\n",
" a = np.array(a)\n",
" nrows = a.size - w + 1\n",
" n = a.strides[0]\n",
" a2D = np.lib.stride_tricks.as_strided(a, shape=(nrows, w), strides=(n, n))\n",
" return np.std(a2D, 1)\n",
"\n",
"\n",
"def iterate_over_smoothing_params(x, z):\n",
" \"\"\"\n",
" Finds the best smoothing parameter for a linear interpolated spline. \n",
" Checks a number of smoothing factors, and then tries to minimize the \n",
" number of knots (changepoints) while retaining decent match to the \n",
" original profile.\n",
" \"\"\"\n",
"\n",
" # Store the results of our iterations in a list of dictionaries\n",
" results = []\n",
"\n",
" # Iterate through different value of smoothing parameters\n",
" s_vals = [x for x in np.arange(0, 10, 0.01)]\n",
"\n",
" r2_vals = []\n",
" n_knots = []\n",
" s_vals_to_keep = []\n",
"\n",
" # Iterate backwards just to make further calcultions more easier.\n",
" for s_val in s_vals[::-1]:\n",
"\n",
" # Fit spline to profile using the s_val for this iteration\n",
" # k=1 is used to force linear line segments between knots\n",
" s = UnivariateSpline(x, z, s=s_val, k=1)\n",
" xs = np.linspace(x[0], x[-1], 1000)\n",
" zs = s(xs)\n",
"\n",
" # Find knots\n",
" x_knots = s.get_knots()\n",
" z_knots = s(x_knots)\n",
"\n",
" # Get number of knots\n",
" n = len(s.get_knots())\n",
"\n",
" # If we've already recorded data about this number of knots, just skip.\n",
" # Need to also, only have increasing results for spline interpolation\n",
" if n in [x['n'] for x in results\n",
" ] or n < max([x['n'] for x in results], default=0):\n",
" continue\n",
"\n",
" # Get r2, how well does the simplification fit the original profile?\n",
" # Create interp model from the knots at our original points.\n",
" f = interp1d(x_knots, z_knots)\n",
" z_lin = f(x)\n",
" r2 = get_r_squared(z, z_lin)\n",
"\n",
" results.append({'n': n, 's': s_val, 'r2': r2})\n",
" return results\n",
"\n",
"\n",
"def find_best_smoothing_param(results, min_std=0.0002):\n",
" \"\"\"\n",
" Given a list of smoothing parameters and their fits, determine what the best smoothing parameter is.\n",
" Larger min_std = more smoothing\n",
" \"\"\"\n",
"\n",
" # Get the 2nd derivate of the n_knots vs r2_vals.\n",
" y_spl = UnivariateSpline([x['n'] for x in results],\n",
" [x['r2'] for x in results],\n",
" s=0)\n",
" y_spl_2d = y_spl.derivative(n=2)\n",
" y_spl_2d_vals = y_spl_2d([x['n'] for x in results])\n",
"\n",
" # Get a rolling standard deviation of the derivative\n",
" std = rolling_std(y_spl_2d_vals, w=3)\n",
"\n",
" # Best smoothing parameter will be when the standard deviation stops changing\n",
" best_i = np.argmax(std < min_std)\n",
" best_s = results[best_i]['s']\n",
" return best_s\n",
"\n",
"\n",
"def simplify_profile(x, z, site):\n",
" results = iterate_over_smoothing_params(x, z)\n",
" best_s = find_best_smoothing_param(results)\n",
"\n",
" # Fit spline to profile\n",
" s = UnivariateSpline(x, z, s=best_s, k=1)\n",
" xs = np.linspace(x[0], x[-1], 1000)\n",
" zs = s(xs)\n",
"\n",
" # Find knots\n",
" x_knots = s.get_knots()\n",
" z_knots = s(x_knots)\n",
"\n",
" # Plot for checking\n",
" plt.title('{}: Linear spline simplification'.format(site))\n",
" plt.xlabel('Distance (m)')\n",
" plt.ylabel('Elevation (m AHD)')\n",
" plt.plot(x, z, 'k.-', label='Raw profile')\n",
" plt.plot(\n",
" xs, zs, 'r-', label='Interpolated profile (s={:.2f})'.format(best_s))\n",
" plt.plot(\n",
" x_knots,\n",
" z_knots,\n",
" 'ro',\n",
" markersize=8,\n",
" markeredgewidth=1.5,\n",
" markeredgecolor='orange',\n",
" label='Interpolated knots (n={})'.format(len(x_knots)))\n",
" plt.legend(loc='best')\n",
"# plt.show()\n",
"\n",
" return x_knots, z_knots, best_s\n",
"\n",
"\n",
"x_knots, z_knots, best_s = simplify_profile(x, z, site)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"code_folding": [
0
]
},
"outputs": [],
"source": [
"def get_x_z_from_knots(x_knots, z_knots):\n",
" # Fit spline to profile\n",
" s = UnivariateSpline(x_knots, z_knots, s=0, k=1)\n",
" xs = np.linspace(x_knots[0], x_knots[-1], 1000)\n",
" zs = s(xs)\n",
" return xs, zs\n",
"\n",
"def simplify_segments_with_same_slopes(x_knots, x, z, site, best_s):\n",
" \"\"\"\n",
" Removes knots which have similar slopes on either side. \n",
" This is an extra simplifcation as the linear splines may still include\n",
" some nodes that aren't required.\n",
" \"\"\"\n",
" removed_x_knots = []\n",
" while True:\n",
" for x_knot1, x_knot2, x_knot3 in zip(x_knots[0:-2], x_knots[1:-1], x_knots[2:]):\n",
"\n",
" # Get slope of segment behind point at x_knot2\n",
" x_seg, z_seg = xz(x_knot1, x_knot2, x, z)\n",
" slope1 = get_slope(x_seg, z_seg)\n",
"\n",
" # Get slope of line in front of point at x_knot2\n",
" x_seg, z_seg = xz(x_knot2, x_knot3, x, z)\n",
" slope2 = get_slope(x_seg, z_seg)\n",
"\n",
" slope_diff = abs(slope1 - slope2)\n",
"\n",
" # Also check if points are too close together\n",
" if x_knot3 - x_knot1 < 3: # m\n",
" too_close = True\n",
" else:\n",
" too_close = False\n",
" \n",
" # Good, the slopes are different on each side, keep checking\n",
" if slope_diff > 0.01 and too_close == False:\n",
" continue\n",
" # Else bad, slopes are the same so we have to remove this point\n",
" else:\n",
" print('Knot at x={} removed (slope_diff={:.3f})'.format(\n",
" x_knot2, slope_diff))\n",
" x_knots = np.delete(x_knots, np.where(x_knots == x_knot2), axis=0)\n",
" removed_x_knots.append(x_knot2)\n",
" break\n",
"\n",
" else:\n",
" print(\"All segments have different slopes\")\n",
" x_knots_simplified = x_knots\n",
" z_knots_simplified = [\n",
" z_val for x_val, z_val in zip(x, z) if x_val in x_knots_simplified\n",
" ]\n",
" break\n",
" \n",
" # Find z location of our removed knots\n",
" removed_z_knots = [\n",
" z_val for x_val, z_val in zip(x, z) if x_val in removed_x_knots\n",
" ]\n",
" \n",
" # Get the new interpolated spline from our simplified knots\n",
" xs, zs = get_x_z_from_knots(x_knots_simplified, z_knots_simplified)\n",
" \n",
" # Plot for checking\n",
"# plt.title('{}: Linear spline simplification by comparing slopes'.format(site))\n",
"# plt.xlabel('Distance (m)')\n",
"# plt.ylabel('Elevation (m AHD)')\n",
"# plt.plot(x, z, 'k.-', label='Raw profile')\n",
"# plt.plot(\n",
"# xs, zs, 'r-', label='Interpolated profile (s={:.2f})'.format(best_s))\n",
"# plt.plot(\n",
"# x_knots_simplified,\n",
"# z_knots_simplified,\n",
"# 'ro',\n",
"# markersize=8,\n",
"# markeredgewidth=1.5,\n",
"# markeredgecolor='orange',\n",
"# label='Interpolated knots (n={})'.format(len(x_knots)))\n",
"# plt.plot(\n",
"# removed_x_knots,\n",
"# removed_z_knots,\n",
"# 'ro',\n",
"# markersize=10,\n",
"# markeredgewidth=2.5,\n",
"# markerfacecolor=\"None\",\n",
"# markeredgecolor='orange',\n",
"# label='Removed knots (n={})'.format(len(removed_x_knots)))\n",
"# plt.legend(loc='best')\n",
"# plt.show()\n",
"\n",
" return x_knots_simplified,z_knots_simplified\n",
"\n",
"x_knots, z_knots = simplify_segments_with_same_slopes(x_knots, x, z, site, best_s)\n",
"xs, zs = get_x_z_from_knots(x_knots, z_knots)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"code_folding": [
1
]
},
"outputs": [],
"source": [
"## BACKUP\n",
"def find_crest_and_toes(x,z,x_knots,best_s):\n",
"\n",
" # Get a cubic spline so we can have continuous second order deriviates for our profile\n",
"# spl = interpolate.splrep(x, z, k=4,t=x_knots[1:-1])\n",
" spl = interpolate.splrep(x, z, k=3,s=best_s)\n",
"\n",
" xx = np.linspace(x[0], x[-1], 1000)\n",
" zz = interpolate.splev(xx, spl)\n",
" zz_d1 = interpolate.splev(xx, spl, der=1)\n",
" zz_d2 = interpolate.splev(xx, spl, der=2)\n",
" zz_d3 = interpolate.splev(xx, spl, der=3)\n",
"\n",
" try:\n",
" # Find dune crest by looking at elevation\n",
" peaks_crest, props_crest = find_peaks(zz, height=3, prominence=0.5)\n",
" \n",
" # Save potential crests\n",
" x_potential_crests = xx[peaks_crest]\n",
" z_potential_crests = zz[peaks_crest]\n",
"\n",
" # Take most seaward peak as dune crest\n",
" i_crest = peaks_crest[-1]\n",
" x_crest = xx[i_crest]\n",
" z_crest = zz[i_crest]\n",
"\n",
" print('Found {} potential dune crests from elevation'.format(len(peaks_crest)))\n",
"\n",
" except:\n",
" print('No crests found from elevation')\n",
" x_crest = np.nan\n",
" z_crest = np.nan\n",
" pass\n",
"\n",
" try:\n",
" if np.isnan(x_crest) and np.isnan(x_crest):\n",
" # Find dune crest by looking at 2nd deriviate\n",
" # Multiply by -1 since crest will have a negative 2nd derivative and we want to find peaks (i.e. positive)\n",
" peaks_crest, props_crest = find_peaks(zz_d2*-1, height=0.02,width=0)\n",
" \n",
" \n",
" # Save potential crests\n",
" x_potential_crests = xx[peaks_crest]\n",
" z_potential_crests = zz[peaks_crest]\n",
" print('Found {} potential dune crests from 2nd derivitive'.format(len(peaks_crest)))\n",
"\n",
" # Take peak with biggest height\n",
" i_biggest_crest = np.argmax(props_crest['peak_heights'])\n",
" i_crest = peaks_crest[i_biggest_crest]\n",
"# x_crest = xx[i_crest]\n",
"# z_crest = zz[i_crest]\n",
"\n",
" # Then take the left base of that peak\n",
" x_crest = xx[props_crest['left_bases'][i_biggest_crest]]\n",
" z_crest= zz[props_crest['left_bases'][i_biggest_crest]]\n",
"\n",
"\n",
" except:\n",
" # Take crest as location with maximum elevation\n",
" i_crest = np.argmax(zz)\n",
" x_crest = xx[i_crest]\n",
" z_crest = zz[i_crest]\n",
" print('No crest found, taking maximum elevation as crest')\n",
"\n",
" try:\n",
" # Find due toe\n",
" peaks_toe, props_toe = find_peaks(zz_d2, height=0, prominence=0.02)\n",
"# peaks_toe, props_toe = find_peaks(zz_d2, height=-3, prominence=0.00)\n",
"\n",
" # Save potential crests\n",
" x_potential_toes = xx[peaks_toe]\n",
" z_potential_toes = zz[peaks_toe]\n",
"\n",
" # Remove toes which are behind dune crest\n",
" heights_toe = props_toe['peak_heights'][peaks_toe > i_crest]\n",
" peaks_toe = peaks_toe[peaks_toe > i_crest]\n",
"\n",
" # Remove toes that are less than 0.5m in height from dune crest\n",
"\n",
" mask = z_crest-0.5>zz[peaks_toe]\n",
" heights_toe = heights_toe[mask]\n",
" peaks_toe = peaks_toe[mask]\n",
"\n",
" # Take toe with biggest 2nd derivative height\n",
" i_toe = peaks_toe[np.argmax(heights_toe)]\n",
" x_toe = xx[i_toe]\n",
" z_toe = zz[i_toe]\n",
" print('Found {} potential dune toes from 2nd derivitive'.format(len(peaks_toe)))\n",
"\n",
"# # The above method usually results in a toe value which is slightly too high up.\n",
"# # We can improve this by moving the toe seaward to the next negative peak in the third\n",
"# # derivative of elevation\n",
"# peaks_toe_d3, props_toe_d3 = find_peaks(zz_d3*-1, height=0.03, prominence=0.02)\n",
"\n",
"# # Filter everything landward of the previous toe position, then take the first (most landward) peak\n",
"# mask = xx[peaks_toe_d3] > x_toe\n",
"# peaks_toe_d3 = peaks_toe_d3[mask]\n",
"\n",
"# if peaks_toe_d3.size != 0:\n",
"# print('Adjusting toe position based on d3')\n",
"# x_toe = xx[peaks_toe_d3[0]]\n",
"# z_toe = zz[peaks_toe_d3[0]]\n",
"\n",
"# # Move toe forward to based on the third derivative of elevation\n",
"# print('Adjusting toe position based on d3')\n",
"# mask = (zz_d3 > -0.0008) & (xx > x_toe) & (np.r_[np.diff(zz_d3)[0],np.diff(zz_d3)] > 0)\n",
"# x_toe = xx[mask][1]\n",
"# z_toe = zz[mask][1]\n",
"\n",
" except:\n",
" x_toe = np.nan\n",
" z_toe = np.nan\n",
" print('No toe found')\n",
" \n",
" return xx,zz, zz_d1,zz_d2,zz_d3,x_crest,z_crest, x_toe,z_toe, x_potential_crests,z_potential_crests, x_potential_toes,z_potential_toes"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"code_folding": [
126
]
},
"outputs": [],
"source": [
"def find_crest_and_toes(x,z,x_knots,best_s):\n",
"\n",
" # Get a cubic spline so we can have continuous second order deriviates for our profile\n",
"# spl = interpolate.splrep(x, z, k=4,t=x_knots[1:-1])\n",
" spl = interpolate.splrep(x, z, k=3,s=best_s)\n",
"\n",
" xx = np.linspace(x[0], x[-1], 1000)\n",
" zz = interpolate.splev(xx, spl)\n",
" zz_d1 = interpolate.splev(xx, spl, der=1)\n",
" zz_d2 = interpolate.splev(xx, spl, der=2)\n",
" zz_d3 = interpolate.splev(xx, spl, der=3)\n",
"\n",
" # Find the biggest slopes\n",
" peaks, props = find_peaks(zz_d1*-1, height=0.15, width=0,prominence=0.1)\n",
"\n",
" if len(peaks) != 0:\n",
"\n",
" x_potential_crests =[xx[val] for val in props['left_bases']]\n",
" x_potential_toes =[xx[val] for val in props['right_bases']]\n",
" x_potential_heights = [val for val in props['peak_heights']]\n",
" x_potential_face_center = xx[peaks]\n",
"\n",
" # Put a limit on how when a slope ends.\n",
" # Dune faces has to be steeper than this value\n",
"# min_slope = -0.10\n",
"\n",
" x_new_potential_crests = []\n",
" x_new_potential_toes = []\n",
" z_new_potential_crests = []\n",
" z_new_potential_toes = []\n",
" dune_heights = []\n",
" slopes = []\n",
"\n",
" for x_crest, x_center, x_toe,height in zip(x_potential_crests, x_potential_face_center, x_potential_toes,x_potential_heights):\n",
" print('Checking x_crest={:.2f}m,x_center={:.2f}m,x_toe={:.2f}m'.format(x_crest, x_center, x_toe))\n",
"\n",
" min_slope = height/-6\n",
" # Check the land side of the dune face\n",
" land_mask = ((x_crest<=xx)&(xx<=x_center))\n",
" land_slope_okay = zz_d1[land_mask]<min_slope\n",
" x_land_slope_okay = xx[land_mask][~land_slope_okay]\n",
" if len(x_land_slope_okay) == 0:\n",
" x_new_crest = x_crest\n",
" else:\n",
" x_new_crest = x_land_slope_okay[-1]\n",
" idx_new_crest = np.where(x_new_crest==xx)[0]\n",
" z_new_crest = zz[idx_new_crest][0]\n",
"\n",
" # # TODO Check, for now, keep crest as detected level\n",
" # # Maybe we don't need to change\n",
" # x_new_crest = x_crest\n",
" # idx_new_crest = np.where(x_new_crest==xx)[0]\n",
" # z_new_crest = zz[idx_new_crest][0]\n",
" # ###\n",
"\n",
" # Check the sea side of the dune face\n",
" sea_mask = ((x_center<=xx)&(xx<=x_toe))\n",
" sea_slope_okay = zz_d1[sea_mask]<min_slope\n",
" x_sea_slope_okay = xx[sea_mask][~sea_slope_okay]\n",
" if len(x_sea_slope_okay) == 0:\n",
" x_new_toe = x_toe\n",
" else:\n",
" x_new_toe = x_sea_slope_okay[0]\n",
" idx_new_toe = np.where(x_new_toe==xx)[0]\n",
" z_new_toe = zz[idx_new_toe][0]\n",
"\n",
" slopes.append((z_new_crest - z_new_toe)/(x_new_crest - x_new_toe))\n",
" dune_heights.append(z_new_crest - z_new_toe)\n",
" x_new_potential_crests.append(x_new_crest)\n",
" x_new_potential_toes.append(x_new_toe)\n",
" z_new_potential_crests.append(z_new_crest)\n",
" z_new_potential_toes.append(z_new_toe)\n",
"\n",
" # Specifiy minimum dune crest elevation and minimum dune height\n",
" min_dune_ele = np.median(zz)/3 # m AHD\n",
" print('min_dune_ele: {:.2f}m'.format(min_dune_ele))\n",
" min_dune_height = 0.5 # m\n",
" mask = (np.array(z_new_potential_crests) > min_dune_ele) & (np.array(dune_heights) > min_dune_height )\n",
"\n",
" slopes = np.array(slopes)[mask]\n",
" dune_heights = np.array(dune_heights)[mask] \n",
" x_potential_crests = np.array(x_new_potential_crests)[mask]\n",
" x_potential_toes = np.array(x_new_potential_toes)[mask]\n",
" z_potential_crests = np.array(z_new_potential_crests)[mask]\n",
" z_potential_toes = np.array(z_new_potential_toes)[mask]\n",
"\n",
"# # Select dune by largest dune height\n",
"# i_dune = np.argmax(dune_heights)\n",
"\n",
" ## TODO Throws error if x_potential_crests is empty\n",
"\n",
" # Select dune by most seaward\n",
" i_dune = np.argmax(x_potential_crests)\n",
"\n",
"\n",
" \n",
"# # Select dune by biggest slope\n",
"# i_dune =np.argmax(slopes*-1)\n",
" x_crest = x_potential_crests[i_dune]\n",
" x_toe = x_potential_toes[i_dune]\n",
" z_crest = z_potential_crests[i_dune]\n",
" z_toe = z_potential_toes[i_dune]\n",
"\n",
" # try:\n",
" # # Find dune crest by looking at elevation\n",
" # peaks_crest, props_crest = find_peaks(zz, height=3, prominence=0.5)\n",
"\n",
" # # Save potential crests\n",
" # x_potential_crests = xx[peaks_crest]\n",
" # z_potential_crests = zz[peaks_crest]\n",
"\n",
" # # Take most seaward peak as dune crest\n",
" # i_crest = peaks_crest[-1]\n",
" # x_crest = xx[i_crest]\n",
" # z_crest = zz[i_crest]\n",
"\n",
" # print('Found {} potential dune crests from elevation'.format(len(peaks_crest)))\n",
"\n",
" # except:\n",
" # print('No crests found from elevation')\n",
" # x_crest = np.nan\n",
" # z_crest = np.nan\n",
" # pass\n",
"\n",
" # try:\n",
" # if np.isnan(x_crest) and np.isnan(x_crest):\n",
" # # Find dune crest by looking at 2nd deriviate\n",
" # # Multiply by -1 since crest will have a negative 2nd derivative and we want to find peaks (i.e. positive)\n",
" # peaks_crest, props_crest = find_peaks(zz_d2*-1, height=0.02,width=0)\n",
"\n",
"\n",
" # # Save potential crests\n",
" # x_potential_crests = xx[peaks_crest]\n",
" # z_potential_crests = zz[peaks_crest]\n",
" # print('Found {} potential dune crests from 2nd derivitive'.format(len(peaks_crest)))\n",
"\n",
" # # Take peak with biggest height\n",
" # i_biggest_crest = np.argmax(props_crest['peak_heights'])\n",
" # i_crest = peaks_crest[i_biggest_crest]\n",
" # # x_crest = xx[i_crest]\n",
" # # z_crest = zz[i_crest]\n",
"\n",
" # # Then take the left base of that peak\n",
" # x_crest = xx[props_crest['left_bases'][i_biggest_crest]]\n",
" # z_crest= zz[props_crest['left_bases'][i_biggest_crest]]\n",
"\n",
"\n",
" # except:\n",
" # # Take crest as location with maximum elevation\n",
" # i_crest = np.argmax(zz)\n",
" # x_crest = xx[i_crest]\n",
" # z_crest = zz[i_crest]\n",
" # print('No crest found, taking maximum elevation as crest')\n",
"\n",
" # try:\n",
" # # Find due toe\n",
" # peaks_toe, props_toe = find_peaks(zz_d2, height=0, prominence=0.02)\n",
" # # peaks_toe, props_toe = find_peaks(zz_d2, height=-3, prominence=0.00)\n",
"\n",
" # # Save potential crests\n",
" # x_potential_toes = xx[peaks_toe]\n",
" # z_potential_toes = zz[peaks_toe]\n",
"\n",
" # # Remove toes which are behind dune crest\n",
" # heights_toe = props_toe['peak_heights'][peaks_toe > i_crest]\n",
" # peaks_toe = peaks_toe[peaks_toe > i_crest]\n",
"\n",
" # # Remove toes that are less than 0.5m in height from dune crest\n",
"\n",
" # mask = z_crest-0.5>zz[peaks_toe]\n",
" # heights_toe = heights_toe[mask]\n",
" # peaks_toe = peaks_toe[mask]\n",
"\n",
" # # Take toe with biggest 2nd derivative height\n",
" # i_toe = peaks_toe[np.argmax(heights_toe)]\n",
" # x_toe = xx[i_toe]\n",
" # z_toe = zz[i_toe]\n",
" # print('Found {} potential dune toes from 2nd derivitive'.format(len(peaks_toe)))\n",
"\n",
" # # # The above method usually results in a toe value which is slightly too high up.\n",
" # # # We can improve this by moving the toe seaward to the next negative peak in the third\n",
" # # # derivative of elevation\n",
" # # peaks_toe_d3, props_toe_d3 = find_peaks(zz_d3*-1, height=0.03, prominence=0.02)\n",
"\n",
" # # # Filter everything landward of the previous toe position, then take the first (most landward) peak\n",
" # # mask = xx[peaks_toe_d3] > x_toe\n",
" # # peaks_toe_d3 = peaks_toe_d3[mask]\n",
"\n",
" # # if peaks_toe_d3.size != 0:\n",
" # # print('Adjusting toe position based on d3')\n",
" # # x_toe = xx[peaks_toe_d3[0]]\n",
" # # z_toe = zz[peaks_toe_d3[0]]\n",
"\n",
" # # # Move toe forward to based on the third derivative of elevation\n",
" # # print('Adjusting toe position based on d3')\n",
" # # mask = (zz_d3 > -0.0008) & (xx > x_toe) & (np.r_[np.diff(zz_d3)[0],np.diff(zz_d3)] > 0)\n",
" # # x_toe = xx[mask][1]\n",
" # # z_toe = zz[mask][1]\n",
"\n",
" # except:\n",
" # x_toe = np.nan\n",
" # z_toe = np.nan\n",
" # print('No toe found')\n",
"\n",
" \n",
" else:\n",
" # Take crest as location with maximum elevation\n",
" i_crest = np.argmax(zz)\n",
" x_crest = xx[i_crest]\n",
" z_crest = zz[i_crest]\n",
" x_potential_crests = np.nan\n",
" z_potential_crests = np.nan\n",
" print('No crest found, taking maximum elevation as crest')\n",
" \n",
" x_toe = np.nan\n",
" z_toe = np.nan\n",
" x_potential_toes=np.nan\n",
" z_potential_toes=np.nan\n",
" \n",
" # Check if toe is necessary. If slope of the dune face is similar to the median slope seaward of the dune\n",
" # toe, let's remove the dune toe\n",
" try:\n",
" if np.isnan(x_toe) == False:\n",
" dune_slope =(z_crest - z_toe) / (x_crest - x_toe)\n",
" median_foreshore_slope = np.median(zz_d1[xx > x_toe])\n",
" std_foreshore_slope = np.std(zz_d1[xx>x_toe])\n",
" print('foreshore_std:{:.4f}'.format(std_foreshore_slope))\n",
" print('foreshore_med_slope:{:.4f}'.format(median_foreshore_slope))\n",
" print('dune_slope:{:.4f}'.format(dune_slope))\n",
" if abs(dune_slope - median_foreshore_slope) < 0.075 and std_foreshore_slope <0.025:\n",
" x_toe = np.nan\n",
" z_toe = np.nan\n",
" except:\n",
" pass\n",
" \n",
" return xx,zz, zz_d1,zz_d2,zz_d3,x_crest,z_crest, x_toe,z_toe, x_potential_crests,z_potential_crests, x_potential_toes,z_potential_toes \n",
" \n",
" \n",
"def plot_profile(site, x,z, xx,zz, zz_d1,zz_d2,zz_d3,x_crest,z_crest, x_toe,z_toe, x_potential_crests,z_potential_crests, x_potential_toes,z_potential_toes):\n",
" plt.figure(figsize=(10,12))\n",
" plt.subplot(411)\n",
" plt.plot(x, z, label='raw')\n",
" plt.plot(xx,zz,label='interpolated')\n",
" plt.plot(x_potential_crests,z_potential_crests,'rv',markersize=10,fillstyle='none',label='Dune crest (potential)')\n",
" plt.plot(x_potential_toes,z_potential_toes,'r^',markersize=10,fillstyle='none',label='Dune toe (potential)')\n",
" plt.plot(x_crest,z_crest,'rv',markersize=10,label='Dune crest (x={:.2f} m, z={:.2f} m)'.format(x_crest,z_crest))\n",
" plt.plot(x_toe,z_toe,'r^',markersize=10,label='Dune toe (x={:.2f} m, z={:.2f} m)'.format(x_toe,z_toe))\n",
" plt.title('{}: elevations'.format(site))\n",
" plt.legend(loc='best')\n",
"\n",
" plt.subplot(412)\n",
" d1 = np.diff(zz) / np.diff(xx)\n",
" plt.plot(xx, zz_d1)\n",
" plt.axvline(x_crest,color='red', label='Dune crest')\n",
" plt.axvline(x_toe,color='red', label='Dune toe')\n",
" # plt.ylim([-0.2,0.2])\n",
" plt.title('first derivative - slope')\n",
"\n",
" plt.subplot(413)\n",
" d2 = np.diff(d1) / np.diff(xx[1:])\n",
" plt.plot(xx, zz_d2)\n",
" plt.axvline(x_crest,color='red', label='Dune crest')\n",
" plt.axvline(x_toe,color='red', label='Dune toe')\n",
" plt.title('second derivative - change in slope')\n",
"# plt.ylim([-0.025,0.025])\n",
"\n",
" plt.subplot(414)\n",
" plt.plot(xx, zz_d3)\n",
" plt.axvline(x_crest,color='red', label='Dune crest')\n",
" plt.axvline(x_toe,color='red', label='Dune toe')\n",
" plt.title('third derivative')\n",
"# plt.ylim([-0.0025,0.0025])\n",
"\n",
" plt.tight_layout()\n",
" plt.show()\n",
"\n",
"# xx,zz, x_crest,z_crest, x_toe,z_toe, x_potential_crests,z_potential_crests, x_potential_toes,z_potential_toes = find_crest_and_toes(x,z,x_knots)\n",
"\n",
"# plot_profile(site, x,z, xx,zz, x_crest,z_crest, x_toe,z_toe, x_potential_crests,z_potential_crests, x_potential_toes,z_potential_toes)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"code_folding": []
},
"outputs": [],
"source": [
"def get_crests_and_toes(site, profile_type, df_profiles):\n",
" x, z = get_profile_data(site, profile_type, df_profiles)\n",
" x_knots, z_knots, best_s = simplify_profile(x, z, site)\n",
" x_knots, z_knots = simplify_segments_with_same_slopes(x_knots, x, z, site, best_s)\n",
"# xs, zs = get_x_z_from_knots(x_knots, z_knots)\n",
" xx,zz, zz_d1,zz_d2,zz_d3,x_crest,z_crest, x_toe,z_toe, x_potential_crests,z_potential_crests, x_potential_toes,z_potential_toes = find_crest_and_toes(x,z,x_knots,best_s)\n",
"\n",
" plot_profile(site, x,z, xx,zz, zz_d1,zz_d2,zz_d3,x_crest,z_crest, x_toe,z_toe, x_potential_crests,z_potential_crests, x_potential_toes,z_potential_toes)\n",
"\n",
" \n",
"# site = 'WAMBE0010'\n",
"# site = 'NINEMs0048'\n",
"# site = 'NAMB0013'\n",
"# site = 'AVOCAn0009'\n",
"# site = 'GRANTSs0014'\n",
"site = 'NARRA0001'\n",
"\n",
"import random\n",
"site = random.sample(df_profiles.index.get_level_values('site_id').unique().tolist(), 1)[0]\n",
"\n",
"print(site)\n",
"# site='NSHORE_s0014'\n",
"get_crests_and_toes(site,'prestorm', df_profiles)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Find berms \n",
"for x_knot1, x_knot2, in zip(x_knots[0:-2], x_knots[1:-1]):\n",
" \n",
" # Berms are only located seaward of the dune toe (but sometimes dune toe is undefined, so use dune crest)\n",
" if x_knot1 < x_crest:\n",
" continue\n",
" \n",
" # Get slope of segment\n",
" x_seg, z_seg = xz(x_knot1, x_knot2, x, z)\n",
" slope = get_slope(x_seg, z_seg)\n",
" z_mean = np.mean(z_seg)\n",
" width = x_knot2 - x_knot1\n",
" \n",
" if slope > -0.01:\n",
" print('{:.1f}m wide berm at x={:.1f}m to x={:.1f}m at z={:.1f}m'.format(width, x_knot1, x_knot2, z_mean))\n",
"\n",
" "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"hide_input": true
},
"outputs": [],
"source": [
"# def reject_outliers(data, m = 5):\n",
"# d = np.abs(data - np.median(data))\n",
"# mdev = np.median(d)\n",
"# s = d/mdev if mdev else 0.\n",
"# return ma.masked_where(s > m, data)\n",
"\n",
"# def hampel(vals_orig, k=7, t0=3):\n",
"# '''\n",
"# vals: pandas series of values from which to remove outliers\n",
"# k: size of window (including the sample; 7 is equal to 3 on either side of value)\n",
"# '''\n",
"# #Make copy so original not edited\n",
"# vals_orig = pd.Series(vals_orig)\n",
"# vals=vals_orig.copy() \n",
"# #Hampel Filter\n",
"# L= 1.4826\n",
"# rolling_median=vals.rolling(k).median()\n",
"# difference=np.abs(rolling_median-vals)\n",
"# median_abs_deviation=difference.rolling(k).median()\n",
"# threshold= t0 *L * median_abs_deviation\n",
"# outlier_idx=difference>threshold\n",
"# vals[outlier_idx]=np.nan\n",
"# return(vals.tolist())\n",
"\n",
"# # Remove segements where slopes are too high, indicative of a structure\n",
"# segments = [xz(x1, x2,x,z) for (x1, x2) in pairwise(x_knots_simplified)]\n",
"# slopes = np.array([get_slope(x1,x2) for x1,x2 in segments])\n",
"# mask = reject_outliers(np.array(slopes))\n",
"# x_knots_filtered = x_knots_simplified[np.append(True,~mask.mask)]\n",
"# z_knots_filtered = [z_val for x_val, z_val in zip(x,z) if x_val in x_knots_filtered]\n",
"\n",
"\n",
"\n",
"# # Hampel filter based on z values\n",
"# mask = ma.masked_invalid(hampel(z_knots_filtered))\n",
"# x_knots_filtered = x_knots_filtered[~mask.mask]\n",
"# z_knots_filtered = [z_val for x_val, z_val in zip(x,z) if x_val in x_knots_filtered]\n",
"\n",
"\n",
"\n",
"# # plt.figure(figsize=(10,5))\n",
"# # plt.plot(x_knots_simplified, np.append(slopes[0],slopes), '.-')\n",
"# # plt.plot(x_knots_simplified[np.append(True,~mask.mask)], np.append(slopes[~mask.mask][0], slopes[~mask.mask]), '.-')\n",
"# # # plt.plot(xs, zs)\n",
"# # # plt.plot(x_knots_filtered, z_knots_filtered,'.',markersize=15)\n",
"# # plt.show()\n",
"\n",
"\n",
"\n",
"\n",
"# plt.figure(figsize=(10,5))\n",
"# plt.plot(x, z, '.-')\n",
"# plt.plot(xs, zs)\n",
"# plt.plot(x_knots_filtered, z_knots_filtered,'.',markersize=15)\n",
"# plt.show()\n",
"# z_knots_filtered"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Find dune crests"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"code_folding": [
18
]
},
"outputs": [],
"source": [
"from scipy.signal import find_peaks\n",
"\n",
"\n",
"def crest_by_deriv_2(z):\n",
" \"\"\"\n",
" Try get crest elevation by taking the difference in 2nd derivative of elevation.\n",
" \"\"\"\n",
"\n",
" z = savgol_filter(z, 15, 3, mode='nearest')\n",
" deriv_2_diff = np.diff(np.gradient(z)) * -1\n",
"\n",
" # Ensure same size arrays for plotting\n",
" deriv_2_diff = np.insert(deriv_2_diff, 0, deriv_2_diff[0])\n",
" med = np.median(deriv_2_diff)\n",
" std = np.std(deriv_2_diff)\n",
" peaks, peak_props = find_peaks(deriv_2_diff, height=med+std*2, distance=10)\n",
" return peaks, peak_props, deriv_2_diff\n",
"\n",
"\n",
"def crest_by_elevation_peaks(z, min_dune_prominence=1.0):\n",
" # Try find peaks in elevation\n",
" peaks, peak_props = find_peaks(z, distance=10, prominence=min_dune_prominence)\n",
" return peaks, peak_props\n",
"\n",
"\n",
"def get_dune_crest(x, z, xs,zs, x_knots, z_knots, site):\n",
" \n",
" peaks, peak_props = crest_by_elevation_peaks(z)\n",
" \n",
" if peaks.size != 0:\n",
" # Choose crest by most landward peak\n",
" x_crest = x[peaks[-1]]\n",
" z_crest = z[peaks[-1]]\n",
" \n",
" # If no peaks in elevation are found, find by maximum second derivative\n",
" else:\n",
" peaks, peak_props, deriv_2_diff = crest_by_deriv_2(z)\n",
" \n",
" plt.title(\n",
" '{}: Difference of 2nd derivative in elevation'.format(site))\n",
" plt.xlabel('Distance (m)')\n",
" plt.ylabel('Diff(2nd Derivative Elevation)')\n",
" plt.plot(x, deriv_2_diff)\n",
"\n",
" \n",
" if peaks.size!= 0:\n",
"\n",
" # Choose crest by highest peak in diff of 2nd derivative\n",
" i_best = np.argmax(peak_props['peak_heights'])\n",
" x_crest = x[peaks[i_best]]\n",
" z_crest = z[peaks[i_best]]\n",
" plt.plot(\n",
" x[peaks],\n",
" deriv_2_diff[peaks],\n",
" 'o',\n",
" markersize=20,\n",
" fillstyle='none')\n",
" plt.show() \n",
" \n",
" else:\n",
" plt.show() \n",
" # If no peaks in diff of 2nd derivative are found, take landward most point\n",
" if peaks.size == 0:\n",
" x_crest = x_knots[0]\n",
" z_crest = z_knots[0]\n",
"\n",
" # Move crest to closest knot\n",
" idx_crest = min(range(len(x_knots)), key=lambda i: abs(x_knots[i]-x_crest))\n",
" x_crest = x_knots[idx_crest]\n",
" z_crest = z_knots[idx_crest]\n",
" \n",
" # Plot for checking\n",
" plt.title('{}: Find dune crest by peak in diff 2nd deriv'.format(site))\n",
" plt.xlabel('Distance (m)')\n",
" plt.ylabel('Elevation (m AHD)')\n",
" plt.plot(x, z, 'k.-', label='Raw profile')\n",
" plt.plot(\n",
" xs,\n",
" zs,\n",
" 'r-',\n",
" label='Interpolated profile (s={:.2f})'.format(best_s))\n",
" plt.plot(\n",
" x_knots,\n",
" z_knots,\n",
" 'ro',\n",
" markersize=8,\n",
" markeredgewidth=1.5,\n",
" markeredgecolor='orange',\n",
" label='Interpolated knots (n={})'.format(len(x_knots)))\n",
" plt.plot(\n",
" x_crest,\n",
" z_crest,\n",
" 'bv',\n",
" markersize=10,\n",
" label='Dune crest (selected) (x={:.1f}m,z={:.1f}m)'.format(x_crest,z_crest))\n",
" plt.plot(\n",
" x[peaks],\n",
" z[peaks],\n",
" 'o',\n",
" markersize=10,\n",
" fillstyle='none',\n",
" label='Dune crest (potential)')\n",
"\n",
" plt.legend(loc='best')\n",
" plt.show()\n",
" \n",
" return x_crest, z_crest\n",
" \n",
"x_crest, z_crest = get_dune_crest(x, z, xs,zs, x_knots, z_knots, site)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Get toe by second derivative\n",
"def toe_by_deriv_2(z):\n",
" \"\"\"\n",
" Try get crest elevation by taking the difference in 2nd derivative of elevation.\n",
" \"\"\"\n",
"\n",
" z = savgol_filter(z,11, 3, mode='nearest')\n",
" deriv_2_diff = np.diff(np.gradient(z))\n",
"\n",
" # Ensure same size arrays for plotting\n",
" deriv_2_diff = np.insert(deriv_2_diff, 0, deriv_2_diff[0])\n",
" med = np.median(deriv_2_diff)\n",
" std = np.std(deriv_2_diff)\n",
" peaks, peak_props = find_peaks(deriv_2_diff, height=max(0.01,med+std*3), distance=10)\n",
" return peaks, peak_props, deriv_2_diff\n",
"\n",
"def get_dune_toe(x, z, xs,zs, x_crest,z_crest,x_knots, z_knots, site):\n",
" peaks, peak_props, deriv_2_diff = toe_by_deriv_2(z)\n",
"\n",
" plt.title('{}: Difference of 2nd derivative in elevation'.format(site))\n",
" plt.xlabel('Distance (m)')\n",
" plt.ylabel('Diff(2nd Derivative Elevation)')\n",
" plt.plot(x, deriv_2_diff)\n",
" \n",
" if peaks.size!= 0:\n",
" \n",
" # Remove peaks which are behind the crest\n",
" # TODO What happens if all peaks are removed?\n",
" mask = ma.masked_where(x[peaks] <= x_crest, x[peaks])\n",
" peaks = peaks[~mask.mask]\n",
"\n",
" if peaks.size!= 0:\n",
" # Choose toe by highest peak in diff of 2nd derivative\n",
" i_best = np.argmax(peak_props['peak_heights'])\n",
" x_toe = x[peaks[i_best]][0]\n",
" z_toe = z[peaks[i_best]][0]\n",
"# # Move crest to closest knot\n",
"# idx_toe = min(range(len(x_knots)), key=lambda i: abs(x_knots[i]-x_toe))\n",
"# x_toe = x_knots[idx_toe]\n",
"# z_toe = z_knots[idx_toe]\n",
"\n",
" plt.plot(\n",
" x[peaks],\n",
" deriv_2_diff[peaks],\n",
" 'o',\n",
" markersize=20,\n",
" fillstyle='none')\n",
" plt.show()\n",
" else:\n",
" x_toe = np.nan\n",
" z_toe = np.nan\n",
" plt.show()\n",
"\n",
" # Plot for checking\n",
" plt.title('{}: Find dune crest by peak in diff 2nd deriv'.format(site))\n",
" plt.xlabel('Distance (m)')\n",
" plt.ylabel('Elevation (m AHD)')\n",
" plt.plot(x, z, 'k.-', label='Raw profile')\n",
" plt.plot(\n",
" xs,\n",
" zs,\n",
" 'r-',\n",
" label='Interpolated profile (s={:.2f})'.format(best_s))\n",
" plt.plot(\n",
" x_knots,\n",
" z_knots,\n",
" 'ro',\n",
" markersize=8,\n",
" markeredgewidth=1.5,\n",
" markeredgecolor='orange',\n",
" label='Interpolated knots (n={})'.format(len(x_knots)))\n",
" plt.plot(\n",
" x_crest,\n",
" z_crest,\n",
" 'bv',\n",
" markersize=10,\n",
" label='Dune crest(x={:.1f} m,z={:.1f} m)'.format(x_crest,z_crest))\n",
" plt.plot(\n",
" x_toe,\n",
" z_toe,\n",
" 'b^',\n",
" markersize=10,\n",
" label='Dune toe (x={:.1f} m,z={:.1f} m)'.format(x_toe,z_toe))\n",
"\n",
" plt.legend(loc='best')\n",
" plt.show()\n",
" return x_toe, z_toe\n",
"x_toe, z_toe = get_dune_toe(x, z, xs,zs, x_crest,z_crest,x_knots, z_knots, site)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def find_dune_crest_and_toe(site, profile_type, df_profiles):\n",
" x, z = get_profile_data(site, profile_type, df_profiles)\n",
" x_knots, z_knots = simplify_profile(x, z, site)\n",
" x_knots, z_knots = simplify_segments_with_same_slopes(x_knots, x, z, site)\n",
" xs, zs = get_x_z_from_knots(x_knots, z_knots)\n",
" x_crest, z_crest = get_dune_crest(x, z, xs,zs, x_knots, z_knots, site)\n",
" x_toe, z_toe = get_dune_toe(x, z, xs,zs, x_crest,z_crest,x_knots, z_knots, site)\n",
" \n",
"find_dune_crest_and_toe('ENTRA0004', 'prestorm', df_profiles)\n"
]
}
],
"metadata": {
"hide_input": false,
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.6"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": false
},
"varInspector": {
"cols": {
"lenName": 16,
"lenType": 16,
"lenVar": 40
},
"kernels_config": {
"python": {
"delete_cmd_postfix": "",
"delete_cmd_prefix": "del ",
"library": "var_list.py",
"varRefreshCmd": "print(var_dic_list())"
},
"r": {
"delete_cmd_postfix": ") ",
"delete_cmd_prefix": "rm(",
"library": "var_list.r",
"varRefreshCmd": "cat(var_dic_list()) "
}
},
"types_to_exclude": [
"module",
"function",
"builtin_function_or_method",
"instance",
"_Feature"
],
"window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 2
}