From 1d8a5f158d9ea442e583e036a197641f77d28f6c Mon Sep 17 00:00:00 2001 From: Chris Leaman Date: Mon, 12 Nov 2018 15:39:41 +1100 Subject: [PATCH] Add function to extract site orientations This function takes the profiles.mat file and uses the spiral log transformation tool to calculate the orientation of each beach profile cross section. Orientations are needed to relate dune toes and crests (from shape files) to each of the profiles. Note there are some beaches which don't have the correct parameters in the log-spiral tool folder, so these should be followed up with Mitch. --- src/data/beach_orientations.m | 73 +++++++++++++++++++++++++++++++++++ src/data/mat_to_csv.py | 48 +++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 src/data/beach_orientations.m diff --git a/src/data/beach_orientations.m b/src/data/beach_orientations.m new file mode 100644 index 0000000..d537ad6 --- /dev/null +++ b/src/data/beach_orientations.m @@ -0,0 +1,73 @@ +% Calculate orientation the beach profile at each unique site and save to .mat file +% Needs the following coastal tools: +% J:\Coastal\Tools\MALT Logspiral Transformation +% J:\Coastal\Tools\Coordinate Transformations + +clear +clc + +% Load profile data, this is where we want to calculate orientations. +warning('off','all') +data = load('C:\Users\z5189959\Desktop\nsw_2016_storm_impact\data\raw\processed_shorelines\profiles.mat'); +data = data.data; + +output = []; + +for ii = 1:n + disp(num2str(ii)) + lat = data(ii).lat; + lon = data(ii).lon; + beach = data(ii).site{1}; + [x,y,utmzone] = deg2utm(lat,lon); + + if strcmp(beach, 'BOOM') == 1 || strcmp(beach, 'HARGn') == 1 || strcmp(beach, 'BILG') == 1 || strcmp(beach, 'HARGs') == 1 || strcmp(beach, 'DEEWHYn') == 1 + % log spiral transformation file is out of date. Talk to Mitch + continue + end + + if strcmp(beach, 'AVOCAs') == 1 + % negative solution. Talk to Mitch + continue + end + + % Get the sp log spiral transformed coordinates + xyz.x = x; + xyz.y = y; + xyz.z = 0; + out = xyz2spz(xyz,beach); + + % Define new bounds towards land and towards sea + % Distance is the number of m in the section to go towards + distance = 200; + s = out.s; + p_center = out.p; + + % Convert landwards and seawrds point back to lat/lon + spz_land.s = out.s; + spz_land.p = out.p-distance; + spz_land.z = 0; + xyz_land = spz2xyz(spz_land,beach); + [lat_land,lon_land] = utm2deg(xyz_land.x,xyz_land.y,utmzone); + + spz_sea.s = out.s; + spz_sea.p = out.p+distance; + spz_sea.z = 0; + xyz_sea = spz2xyz(spz_sea,beach); + [lat_sea,lon_sea] = utm2deg(xyz_sea.x,xyz_sea.y,utmzone); + + % Orientation in degrees anticlockwise from east, pointing towards land + orientation = radtodeg(atan2((xyz_land.y - xyz_sea.y), (xyz_land.x - xyz_sea.x))); + + row.lat_center = lat; + row.lon_center = lon; + row.lat_land = lat_land; + row.lon_land = lat_land; + row.lat_sea = lat_sea; + row.lon_sea = lon_sea; + row.orientation = orientation; + row.beach = beach; + output = [output; row]; +end +warning('on','all') + +save('orientations.mat','output','-v7') diff --git a/src/data/mat_to_csv.py b/src/data/mat_to_csv.py index 6643d35..2bbf471 100644 --- a/src/data/mat_to_csv.py +++ b/src/data/mat_to_csv.py @@ -12,6 +12,50 @@ logging.config.fileConfig('../logging.conf', disable_existing_loggers=False) logger = logging.getLogger(__name__) +def parse_orientations(orientations_mat): + """ + Parses the raw orientations.mat file and returns a pandas dataframe. Note that orientations are the direction + towards land measured in degrees anti-clockwise from east. + :param orientations_mat: + :return: + """ + logger.info('Parsing %s', orientations_mat) + mat_data = loadmat(orientations_mat)['output'] + rows = [] + for i in range(0, len(mat_data['beach'])): + rows.append({ + 'beach': mat_data['beach'][i], + 'orientation': mat_data['orientation'][i], + 'lat_center': mat_data['lat_center'][i], + 'lon_center': mat_data['lon_center'][i], + 'lat_land': mat_data['lat_land'][i], + 'lon_land': mat_data['lon_land'][i], + 'lat_sea': mat_data['lat_sea'][i], + 'lon_sea': mat_data['lon_sea'][i], + }) + + df = pd.DataFrame(rows) + return df + +def combine_sites_and_orientaions(df_sites, df_orientations): + """ + Replaces beach/lat/lon columns with the unique site_id + :param dfs: + :param df_sites: + :return: + """ + + df_merged_sites = df_sites.merge(df_orientations[['beach', 'lat_center', 'lon_center', 'orientation']], + left_on=['beach', 'lat', 'lon'], + right_on=['beach', 'lat_center', 'lon_center']) + + # Check that all our records have a unique site identifier + n_unmatched = len(df_sites) - len(df_merged_sites) + if n_unmatched > 0: + logger.warning('Not all records (%d of %d) matched with an orientation', n_unmatched, len(df_sites)) + + return df_merged_sites + def parse_waves(waves_mat): """ Parses the raw waves.mat file and returns a pandas dataframe @@ -156,12 +200,16 @@ def main(): df_tides = parse_tides(tides_mat='../../data/raw/tides.mat') df_profiles = parse_profiles(profiles_mat='../../data/raw/profiles.mat') df_sites = get_unique_sites(dfs=[df_waves, df_tides, df_profiles]) + df_orientations = parse_orientations(orientations_mat='./data/raw/processed_shorelines/orientations.mat') logger.info('Identifying unique sites') df_waves = replace_unique_sites(df_waves, df_sites) df_tides = replace_unique_sites(df_tides, df_sites) df_profiles = replace_unique_sites(df_profiles, df_sites) + logger.info('Combine orientations into sites') + df_sites = combine_sites_and_orientaions(df_sites, df_orientations) + logger.info('Setting pandas index') df_profiles.set_index(['site_id', 'profile_type', 'x'], inplace=True) df_waves.set_index(['site_id', 'datetime'], inplace=True)