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.
CoastSat_WRL/sl_comparison_NARRA.py

383 lines
14 KiB
Python

# -*- coding: utf-8 -*-
#==========================================================#
# Compare Narrabeen SDS with 3D quadbike surveys
#==========================================================#
# Initial settings
import os
import numpy as np
import matplotlib.pyplot as plt
import pdb
import ee
import matplotlib.dates as mdates
import matplotlib.cm as cm
from datetime import datetime, timedelta
import pickle
import pytz
import scipy.io as sio
import scipy.interpolate as interpolate
import statsmodels.api as sm
import skimage.measure as measure
# my functions
import functions.utils as utils
# some settings
np.seterr(all='ignore') # raise/ignore divisions by 0 and nans
plt.rcParams['axes.grid'] = True
plt.rcParams['figure.max_open_warning'] = 100
au_tz = pytz.timezone('Australia/Sydney')
# load quadbike dates and convert from datenum to datetime
filename = 'data\quadbike\survey_dates.mat'
filepath = os.path.join(os.getcwd(), filename)
dates_quad = sio.loadmat(filepath)['dates'] # matrix containing year, month, day
dates_quad = [datetime(dates_quad[i,0], dates_quad[i,1], dates_quad[i,2],
tzinfo=au_tz) for i in range(dates_quad.shape[0])]
# load timestamps from satellite images
satname = 'L8'
sitename = 'NARRA'
filepath = os.path.join(os.getcwd(), 'data', satname, sitename)
with open(os.path.join(filepath, sitename + '_output_new' + '.pkl'), 'rb') as f:
output = pickle.load(f)
dates_l8 = output['t']
# convert to AEST
dates_l8 = [_.astimezone(au_tz) for _ in dates_l8]
# remove duplicates
dates_l8_str = [_.strftime('%Y%m%d') for _ in dates_l8]
dupl = utils.duplicates_dict(dates_l8_str)
idx_remove = []
for k,v in dupl.items():
idx1 = v[0]
idx2 = v[1]
c1 = output['cloud_cover'][idx1]
c2 = output['cloud_cover'][idx2]
g1 = output['acc_georef'][idx1]
g2 = output['acc_georef'][idx2]
if c1 < c2 - 0.01:
idx_remove.append(idx2)
elif g1 < g2 - 0.1:
idx_remove.append(idx2)
else:
idx_remove.append(idx1)
idx_remove = sorted(idx_remove)
idx_all = np.linspace(0,70,71)
idx_keep = list(np.where(~np.isin(idx_all,idx_remove))[0])
output['t'] = [output['t'][k] for k in idx_keep]
output['shorelines'] = [output['shorelines'][k] for k in idx_keep]
output['cloud_cover'] = [output['cloud_cover'][k] for k in idx_keep]
output['acc_georef'] = [output['acc_georef'][k] for k in idx_keep]
# convert to AEST
dates_l8 = output['t']
dates_l8 = [_.astimezone(au_tz) for _ in dates_l8]
# load wave data (already AEST)
filename = 'data\wave\SydneyProcessed.mat'
filepath = os.path.join(os.getcwd(), filename)
wave_data = sio.loadmat(filepath)
idx = utils.find_indices(wave_data['dates'][:,0], lambda e: e >= dates_l8[0].year and e <= dates_l8[-1].year)
hsig = np.array([wave_data['Hsig'][i][0] for i in idx])
wdir = np.array([wave_data['Wdir'][i][0] for i in idx])
dates_wave = [datetime(wave_data['dates'][i,0], wave_data['dates'][i,1],
wave_data['dates'][i,2], wave_data['dates'][i,3],
wave_data['dates'][i,4], wave_data['dates'][i,5],
tzinfo=au_tz) for i in idx]
# load tide data (already AEST)
filename = 'SydTideData.mat'
filepath = os.path.join(os.getcwd(), 'data', 'tide', filename)
tide_data = sio.loadmat(filepath)
idx = utils.find_indices(tide_data['dates'][:,0], lambda e: e >= dates_l8[0].year and e <= dates_l8[-1].year)
tide = np.array([tide_data['tide'][i][0] for i in idx])
dates_tide = [datetime(tide_data['dates'][i,0], tide_data['dates'][i,1],
tide_data['dates'][i,2], tide_data['dates'][i,3],
tide_data['dates'][i,4], tide_data['dates'][i,5],
tzinfo=au_tz) for i in idx]
#%% make a plot of all the dates with wave data
orange = [255/255,140/255,0]
blue = [0,191/255,255/255]
f = plt.figure()
months = mdates.MonthLocator()
month_fmt = mdates.DateFormatter('%b %Y')
days = mdates.DayLocator()
years = [2013,2014,2015,2016]
for k in range(len(years)):
sel_year = years[k]
ax = plt.subplot(4,1,k+1)
idx_year = utils.find_indices(dates_wave, lambda e : e.year >= sel_year and e.year <= sel_year)
plt.plot([dates_wave[i] for i in idx_year], [hsig[i] for i in idx_year], 'k-', linewidth=0.5)
hsigmax = np.nanmax([hsig[i] for i in idx_year])
cbool = True
for j in range(len(dates_quad)):
if dates_quad[j].year == sel_year:
if cbool:
plt.plot([dates_quad[j], dates_quad[j]], [0, hsigmax], color=orange, label='survey')
cbool = False
else:
plt.plot([dates_quad[j], dates_quad[j]], [0, hsigmax], color=orange)
cbool = True
for j in range(len(dates_l8)):
if dates_l8[j].year == sel_year:
if cbool:
plt.plot([dates_l8[j], dates_l8[j]], [0, hsigmax], color=blue, label='landsat8')
cbool = False
else:
plt.plot([dates_l8[j], dates_l8[j]], [0, hsigmax], color=blue)
if k == 3:
plt.legend()
plt.xlim((datetime(sel_year,1,1), datetime(sel_year,12,31, tzinfo=au_tz)))
plt.ylim((0, hsigmax))
plt.ylabel('Hs [m]')
ax.xaxis.set_major_locator = months
ax.xaxis.set_major_formatter(month_fmt)
f.subplots_adjust(hspace=0.2)
plt.draw()
#%% calculate difference between dates (quad and sat)
diff_days = [ [(x - _).days for _ in dates_quad] for x in dates_l8]
max_diff = 14
idx_closest = [utils.find_indices(_, lambda e: abs(e) <= max_diff) for _ in diff_days]
# store in dates_diff dictionnary
dates_diff = []
cloud_cover = []
for i in range(len(idx_closest)):
if not idx_closest[i]:
continue
elif len(idx_closest[i]) > 1:
idx_best = np.argmin(np.abs([diff_days[i][_] for _ in idx_closest[i]]))
dates_temp = [dates_quad[_] for _ in idx_closest[i]]
days_temp = [diff_days[i][_] for _ in idx_closest[i]]
dates_diff.append({"date sat": dates_l8[i],
"date quad": dates_temp[idx_best],
"days diff": days_temp[idx_best]})
else:
dates_diff.append({"date sat": dates_l8[i],
"date quad": dates_quad[idx_closest[i][0]],
"days diff": diff_days[i][idx_closest[i][0]]
})
# store cloud data
cloud_cover.append(output['cloud_cover'][i])
# store wave data
wave_hsig = []
for i in range(len(dates_diff)):
wave_hsig.append(hsig[np.argmin(np.abs(np.array([(dates_diff[i]['date sat'] - _).total_seconds() for _ in dates_wave])))])
# make a plot
plt.figure()
counter = 0
for i in range(len(dates_diff)):
counter = counter + 1
if dates_diff[i]['date quad'] > dates_diff[i]['date sat']:
date_min = dates_diff[i]['date sat']
date_max = dates_diff[i]['date quad']
color1 = orange
color2 = blue
else:
date_min = dates_diff[i]['date quad']
date_max = dates_diff[i]['date sat']
color1 = blue
color2 = orange
idx_t = utils.find_indices(dates_wave, lambda e : e >= date_min and e <= date_max)
hsigmax = np.nanmax([hsig[i] for i in idx_t])
hsigmin = np.nanmin([hsig[i] for i in idx_t])
if counter > 9:
counter = 1
plt.figure()
ax = plt.subplot(3,3,counter)
plt.plot([dates_wave[i] for i in idx_t], [hsig[i] for i in idx_t], 'k-', linewidth=1.5)
plt.plot([date_min, date_min], [0, 4.5], color=color2, label='survey')
plt.plot([date_max, date_max], [0, 4.5], color=color1, label='landsat8')
plt.ylabel('Hs [m]')
ax.xaxis.set_major_locator(mdates.DayLocator(tz=au_tz))
ax.xaxis.set_minor_locator(mdates.HourLocator(tz=au_tz))
ax.xaxis.set_major_formatter(mdates.DateFormatter('%d'))
ax.xaxis.set_minor_locator(months)
plt.title(dates_diff[i]['date sat'].strftime('%b %Y') + ' (' + str(abs(dates_diff[i]['days diff'])) + ' days)')
plt.draw()
plt.gcf().subplots_adjust(hspace=0.5)
# mean day difference
np.mean([ np.abs(_['days diff']) for _ in dates_diff])
#%% Compare shorelines in elevation
dist_buffer = 50 # buffer of points selected for interpolation
# load quadbike .mat files
foldername = 'data\quadbike\surveys3D'
folderpath = os.path.join(os.getcwd(), foldername)
filenames = os.listdir(folderpath)
# get the satellite shorelines
sl = output['shorelines']
# get dates from filenames
dates_quad = [datetime(int(_[6:10]), int(_[11:13]), int(_[14:16]), tzinfo= au_tz) for _ in filenames]
zav = []
ztide = []
sl_gt = []
for i in range(len(dates_diff)):
sl_smooth = sl[i]
# select closest 3D survey and load .mat file
idx_closest = np.argmin(np.abs(np.array([(dates_diff[i]['date sat'] - _).days for _ in dates_quad])))
survey3d = sio.loadmat(os.path.join(folderpath, filenames[idx_closest]))
# reshape to a vector
xs = survey3d['x'].reshape(survey3d['x'].shape[0] * survey3d['x'].shape[1])
ys = survey3d['y'].reshape(survey3d['y'].shape[0] * survey3d['y'].shape[1])
zs = survey3d['z'].reshape(survey3d['z'].shape[0] * survey3d['z'].shape[1])
# remove nan values
idx_nan = np.isnan(zs)
xs = xs[~idx_nan]
ys = ys[~idx_nan]
zs = zs[~idx_nan]
# find water level at the time the image was acquired
idx_closest = np.argmin(np.abs(np.array([(dates_diff[i]['date sat'] - _).total_seconds() for _ in dates_tide])))
tide_level = tide[idx_closest]
ztide.append(tide_level)
# find contour corresponding to the water level on 3D surface (if below minimum, add 0.05m increments)
if tide_level < np.nanmin(survey3d['z']):
tide_level = np.nanmin(survey3d['z'])
sl_tide = measure.find_contours(survey3d['z'], tide_level)
sl_tide = sl_tide[np.argmax(np.array([len(_) for _ in sl_tide]))]
count = 0
while len(sl_tide) < 900:
count = count + 1
tide_level = tide_level + 0.05*count
sl_tide = measure.find_contours(survey3d['z'], tide_level)
sl_tide = sl_tide[np.argmax(np.array([len(_) for _ in sl_tide]))]
print('added ' + str(0.05*count) + ' cm - contour with ' + str(len(sl_tide)) + ' points')
else:
sl_tide = measure.find_contours(survey3d['z'], tide_level)
sl_tide = sl_tide[np.argmax(np.array([len(_) for _ in sl_tide]))]
# remove nans
if np.any(np.isnan(sl_tide)):
index_nan = np.where(np.isnan(sl_tide))[0]
sl_tide = np.delete(sl_tide, index_nan, axis=0)
# get x,y coordinates
xtide = [survey3d['x'][int(np.round(sl_tide[m,0])), int(np.round(sl_tide[m,1]))] for m in range(sl_tide.shape[0])]
ytide = [survey3d['y'][int(np.round(sl_tide[m,0])), int(np.round(sl_tide[m,1]))] for m in range(sl_tide.shape[0])]
sl_gt.append(np.transpose(np.array([np.array(xtide), np.array(ytide)])))
# interpolate SDS on 3D surface to get elevation (point by point)
zq = []
for j in range(sl_smooth.shape[0]):
xq = sl_smooth[j,0]
yq = sl_smooth[j,1]
dist_q = np.linalg.norm(np.transpose(np.array([[xq - _ for _ in xs],[yq - _ for _ in ys]])), axis=1)
idx_buffer = dist_q <= dist_buffer
if sum(idx_buffer) > 0:
tck = interpolate.bisplrep(xs[idx_buffer], ys[idx_buffer], zs[idx_buffer])
zq.append(interpolate.bisplev(xq, yq, tck))
zq = np.array(zq)
plt.figure()
plt.hist(zq, bins=100)
plt.draw()
# plt.figure()
# plt.axis('equal')
# plt.scatter(xs, ys, s=10, c=zs, marker='o', cmap=cm.get_cmap('jet'),
# label='quad data')
# plt.plot(xs[idx_buffer], ys[idx_buffer], 'ko')
# plt.plot(xq,yq,'ro')
# plt.draw()
# store the alongshore median elevation
zav.append(np.median(utils.reject_outliers(zq, m=2)))
# make plot
red = [255/255, 0, 0]
gray = [0.75, 0.75, 0.75]
plt.figure()
plt.subplot(121)
plt.axis('equal')
plt.scatter(xs, ys, s=10, c=zs, marker='o', cmap=cm.get_cmap('jet'),
label='3D survey')
plt.plot(xtide, ytide, '--', color=gray, linewidth=2.5, label='tide level contour')
plt.plot(sl_smooth[:,0], sl_smooth[:,1], '-', color=red, linewidth=2.5, label='SDS')
# plt.plot(sl[i][idx_beach,0], sl[i][idx_beach,1], 'w-', linewidth=2)
plt.xlabel('Eastings [m]')
plt.ylabel('Northings [m]')
plt.title('Shoreline comparison')
plt.colorbar(label='mAHD')
plt.legend()
plt.ylim((6266100, 6267000))
plt.subplot(122)
plt.plot(np.linspace(0,1,len(zq)), zq, 'ko-', markersize=5)
plt.plot([0, 1], [zav[i], zav[i]], 'r-', label='median')
plt.plot([0, 1], [ztide[i], ztide[i]], 'g--', label = 'measured tide')
plt.xlabel('Northings [m]')
plt.ylabel('Elevation [mAHD]')
plt.title('Alongshore SDS elevation')
plt.legend()
mng = plt.get_current_fig_manager()
mng.window.showMaximized()
plt.tight_layout()
plt.draw()
print(i)
#%% Calculate some error statistics
zav = np.array(zav)
ztide = np.array(ztide)
f = plt.figure()
plt.subplot(3,1,1)
plt.bar(np.linspace(1,len(zav),len(zav)), zav-ztide)
plt.ylabel('Error in z [m]')
plt.title('Elevation error')
plt.xticks([])
plt.draw()
plt.subplot(3,1,2)
plt.bar(np.linspace(1,len(zav),len(zav)), wave_hsig, color=orange)
plt.ylabel('Hsig [m]')
plt.xticks([])
plt.draw()
plt.subplot(3,1,3)
plt.bar(np.linspace(1,len(zav),len(zav)), np.array(cloud_cover)*100, color='g')
plt.ylabel('Cloud cover %')
plt.xlabel('comparison #')
plt.grid(False)
plt.grid(axis='y')
f.subplots_adjust(hspace=0)
plt.draw()
np.sqrt(np.mean((zav - ztide)**2))
#%% plot to show LOWESS smoothing
#i = 0
#idx_beach = [np.min(np.linalg.norm(sl[i][k,:] - narrabeach, axis=1)) < dist_thresh for k in range(sl[i].shape[0])]
#x = sl[i][idx_beach,0]
#y = sl[i][idx_beach,1]
#sl_smooth = lowess(x,y, frac=1./10, it = 10)
#
#plt.figure()
#plt.axis('equal')
#plt.scatter
#plt.plot(x,y,'bo', linewidth=2, label='original SDS')
#plt.plot(sl_smooth[:,1], sl_smooth[:,0], 'ro', linewidth=2, label='smoothed SDS')
#plt.legend()
#plt.xlabel('Eastings [m]')
#plt.ylabel('Northings [m]')
#plt.title('Local weighted scatterplot smoothing (LOWESS)')
#plt.draw()