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.

169 lines
5.8 KiB
Python

"""
This file can probably be removed since we are not reading from shp files any more
"""
import click
import fiona
import numpy as np
import pandas as pd
from shapely.geometry import LineString, Point
from shapely.geometry import shape
from logs import setup_logging
from utils import convert_coord_systems
logger = setup_logging()
def shapes_from_shp(shp_file):
"""
Parses a shape file and returns a list of shapely shapes, ids and properties
:param shp_file:
:return:
"""
shapes = []
ids = []
properties = []
for feat in fiona.open(shp_file, "r"):
shapes.append(shape(feat["geometry"]))
ids.append(feat["id"])
properties.append(feat["properties"])
return shapes, ids, properties
def distance_to_intersection(
lat, lon, landward_orientation, beach, line_strings, line_properties
):
"""
Returns the distance at whjch a line drawn from a lat/lon at an orientation intersects a line stinrg
:param lat:
:param lon:
:param landward_orientation: Angle, anticlockwise positive from east in degrees, towards the land
towards the
land.
:param line_string:
:return:
"""
start_point = Point(lon, lat)
start_point = convert_coord_systems(start_point)
distance = 1000 # m look up to 1000m for an intersection
landward_point = Point(
start_point.coords.xy[0] + distance * np.cos(np.deg2rad(landward_orientation)),
start_point.coords.xy[1] + distance * np.sin(np.deg2rad(landward_orientation)),
)
landward_line = LineString([start_point, landward_point])
seaward_point = Point(
start_point.coords.xy[0] - distance * np.cos(np.deg2rad(landward_orientation)),
start_point.coords.xy[1] - distance * np.sin(np.deg2rad(landward_orientation)),
)
seaward_line = LineString([start_point, seaward_point])
# Look at relevant line_strings which have the same beach property in order to reduce computation time
line_strings = [
s for s, p in zip(line_strings, line_properties) if p["beach"] == beach
]
# Check whether profile_line intersects with any lines in line_string. If intersection point is landwards,
# consider this negative, otherwise seawards is positive.
for line_string in line_strings:
land_intersect_points = landward_line.intersection(line_string)
if not land_intersect_points.is_empty:
return -land_intersect_points.distance(start_point)
sea_intersect_points = seaward_line.intersection(line_string)
if not sea_intersect_points.is_empty:
return sea_intersect_points.distance(start_point)
# If no intersections are found, return nothing.
return None
def beach_profile_elevation(x_coord, df_profiles, profile_type, site_id):
"""
Returns the beach profile elevation at a particular x-coordinate
:param x_coord:
:param df_profiles:
:param profile_type: "prestorm" or "poststorm"
:param site_id:
:return:
"""
if np.isnan(x_coord):
return None
# Get profile
df_profile = df_profiles.query(
'profile_type == "{}" and site_id =="{}"'.format(profile_type, site_id)
)
return np.interp(x_coord, df_profile.index.get_level_values("x"), df_profile["z"])
def parse_profile_features(df_sites, df_profiles, dune_crest_shp, dune_toe_shp):
"""
Reads dune crest and toe files and creates a pandas dataframe with crest/toe locations at each site
:return:
"""
# Get site information. Base our profile features on each site
df_profile_features = df_sites
features = {
"dune_crest": {"file": dune_crest_shp},
"dune_toe": {"file": dune_toe_shp},
}
# Import our dune crest and toes
for feat in features.keys():
shapes, _, properties = shapes_from_shp(features[feat]["file"])
shapes = [convert_coord_systems(x) for x in shapes]
# Figure out the x coordinates of our crest and toes, by looking at where our beach sections intersect our
# shape files.
col_name = "{}_x".format(feat)
df_profile_features[col_name] = df_profile_features[
"profile_x_lat_lon"
] + df_profile_features.apply(
lambda row: distance_to_intersection(
row["lat"],
row["lon"],
row["orientation"],
row["beach"],
shapes,
properties,
),
axis=1,
)
# Get the elevations of the crest and toe
col_name = "{}_z".format(feat)
df_profile_features[col_name] = df_profile_features.apply(
lambda row: beach_profile_elevation(
row["{}_x".format(feat)], df_profiles, "prestorm", row.name
),
axis=1,
)
df_profile_features = df_profile_features.drop(
columns=["beach", "lat", "lon", "orientation", "profile_x_lat_lon"]
)
return df_profile_features
@click.command(short_help="create .csv of dune toe and crest positions")
@click.option("--dune-crest-shp", required=True, help=".csv file to convert")
@click.option("--dune-toe-shp", required=True, help="where to store .shp file")
@click.option("--sites-csv", required=True, help="where to store .shp file")
@click.option("--profiles-csv", required=True, help="where to store .shp file")
@click.option("--output-csv", required=True, help="where to store .shp file")
def create_profile_features(
dune_crest_shp, dune_toe_shp, sites_csv, profiles_csv, output_csv
):
logger.info("Creating .csv of dune crests and toes")
df_sites = pd.read_csv(sites_csv, index_col=[0])
df_profiles = pd.read_csv(profiles_csv, index_col=[0, 1, 2])
df_profile_features = parse_profile_features(
df_sites, df_profiles, dune_crest_shp, dune_toe_shp
)
df_profile_features.to_csv(output_csv)
logger.info("Done!")