Skip to content

Commit

Permalink
#198 colormap the shorelines + roi dropdown works
Browse files Browse the repository at this point in the history
  • Loading branch information
2320sharon committed Oct 25, 2023
1 parent 616d126 commit 81aae9a
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 69 deletions.
169 changes: 111 additions & 58 deletions src/coastseg/coastseg_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ class ExtractShorelinesContainer(traitlets.HasTraits):
# list of roi ids that have extracted shorelines
roi_ids_list = traitlets.List(trait=traitlets.Unicode())

def __init__(self, load_list_widget=None, trash_list_widget=None,roi_list_widget=None):
def __init__(
self, load_list_widget=None, trash_list_widget=None, roi_list_widget=None
):
super().__init__()
if load_list_widget:
self.link_load_list(load_list_widget)
Expand All @@ -80,7 +82,7 @@ def link_trash_list(self, widget):

def link_roi_list(self, widget):
if hasattr(widget, "options"):
traitlets.dlink((self, "ROI_ids"), (widget, "options"))
traitlets.dlink((self, "roi_ids_list"), (widget, "options"))


class CoastSeg_Map:
Expand Down Expand Up @@ -147,31 +149,88 @@ def get_session_name(self):
def set_session_name(self, name: str):
self.session_name = name

def load_extracted_shoreline_layer(self, gdf, layer_name, style):
def load_extracted_shoreline_layer(self, gdf, layer_name, colormap):
map_crs = "epsg:4326"
# create a layer with the extracted shorelines selected
points_gdf = extracted_shoreline.convert_linestrings_to_multipoints(gdf)
projected_gdf = points_gdf.to_crs(map_crs)

import matplotlib.pyplot as plt
import numpy as np

# convert date column to datetime
projected_gdf["date"] = pd.to_datetime(projected_gdf["date"])
# Sort the GeoDataFrame based on the 'date' column
projected_gdf = projected_gdf.sort_values(by="date")
# normalize the dates to 0-1 scale
min_date = projected_gdf["date"].min()
max_date = projected_gdf["date"].max()
if min_date == max_date:
# If there's only one date, set delta to 0.25
delta = np.array([0.25])
else:
delta = (projected_gdf["date"] - min_date) / (max_date - min_date)
# get the colors from the colormap
colors = plt.cm.get_cmap(colormap)(delta)

# convert RGBA colors to Hex
colors_hex = [
"#%02x%02x%02x" % (int(r * 255), int(g * 255), int(b * 255))
for r, g, b, a in colors
]

# add the colors to the GeoDataFrame
projected_gdf["color"] = colors_hex

projected_gdf = common.stringify_datetime_columns(projected_gdf)
# Convert GeoDataFrame to GeoJSON
features_json = json.loads(projected_gdf.to_json())

# Add 'id' field to features in GeoJSON
for feature, index in zip(
features_json["features"], range(len(features_json["features"]))
):
feature["id"] = str(index)

# define a style callback function that takes a feature and returns a style dictionary
def style_callback(feature):
# find the color for the current feature based on its id
color = projected_gdf.iloc[int(feature["id"])]["color"]
return {"color": color, "weight": 5, "fillColor": color, "fillOpacity": 0.5}

# create an ipyleaflet GeoJSON layer with the extracted shorelines selected
new_layer = GeoJSON(
data=features_json, name=layer_name, style=style, point_style=style
data=features_json,
name=layer_name,
style_callback=style_callback,
point_style={
"radius": 1,
"opacity": 1,
},
)
# features_json = json.loads(projected_gdf.to_json())
# # create an ipyleaflet GeoJSON layer with the extracted shorelines selected
# new_layer = GeoJSON(
# data=features_json, name=layer_name, style=style, point_style=style
# )
self.replace_layer_by_name(layer_name, new_layer, on_hover=None, on_click=None)

def delete_selected_shorelines(
self, layer_name: str, selected_shorelines: List = None
self, layer_name: str, selected_id: str, selected_shorelines: List = None
) -> None:
if selected_shorelines:
if selected_shorelines and selected_id:
pass
# this will remove the selected shorelines from the files
# do some fancy logic to remove the selected shorelines from the files
print(f"Deleting {selected_shorelines} ")
self.remove_layer_by_name(layer_name)

def load_selected_shorelines_on_map(
self, selected_shorelines: List, layer_name: str, style: dict
self,
selected_id: str,
selected_shorelines: List,
layer_name: str,
colormap: str,
) -> None:
def get_selected_shorelines(gdf, selected_items) -> gpd.GeoDataFrame:
# Filtering criteria
Expand All @@ -193,38 +252,27 @@ def get_selected_shorelines(gdf, selected_items) -> gpd.GeoDataFrame:
filtered_gdf = pd.concat(frames)
return filtered_gdf

# @todo pass in the ROI ID that the extracted shorelines are from
# @todo this code is temporary
# Get the list of the ROI IDs that have extracted shorelines
ids_with_extracted_shorelines = self.get_roi_ids(has_extracted_shorelines=True)
if ids_with_extracted_shorelines == []:
logger.warning("No ROIs with extracted shorelines found")
return
# select the first ROI ID with extracted shorelines
selected_id = ids_with_extracted_shorelines[0]
# -------------------------------------------
# load the extracted shorelines for the selected ROI ID
extracted_shorelines = self.rois.get_extracted_shoreline(selected_id)

# get the geodataframe for the extracted shorelines
selected_gdf = get_selected_shorelines(
extracted_shorelines.gdf, selected_shorelines
)
if not selected_gdf.empty:
self.load_extracted_shoreline_layer(selected_gdf, layer_name, style)
if hasattr(extracted_shorelines, "gdf"):
selected_gdf = get_selected_shorelines(
extracted_shorelines.gdf, selected_shorelines
)
if not selected_gdf.empty:
self.load_extracted_shoreline_layer(selected_gdf, layer_name, colormap)

def on_ROI_change(
def on_roi_change(
self,
new_roi_id: str,
selected_id: str,
) -> None:
# @todo pass in the ROI ID that the extracted shorelines are from
# @todo this code is temporary
# remove the old layer
self.remove_extracted_shorelines()

# -------------------------------------------
# load first the extracted shorelines for the selected ROI ID
extracted_shorelines = self.rois.get_extracted_shoreline(new_roi_id)
# remove the old layers
self.remove_extracted_shoreline_layers()
# # update the load_list and trash_list
extracted_shorelines = self.update_loadable_shorelines(selected_id)
self.extract_shorelines_container.trash_list = []
# load the new extracted shorelines onto the map
self.load_extracted_shorelines_on_map(extracted_shorelines, 1)

def create_map(self):
Expand Down Expand Up @@ -1504,6 +1552,9 @@ def extract_all_shorelines(self) -> None:
# Get ROI ids that are selected on map and have had their shorelines extracted
roi_ids = self.get_roi_ids_with_extracted_shorelines(is_selected=True)
self.compute_transects(self.transects.gdf, self.get_settings(), roi_ids)

# Get the list of the ROI IDs that have extracted shorelines
ids_with_extracted_shorelines = self.update_roi_ids_with_shorelines()
# load extracted shorelines to map
self.load_extracted_shorelines_to_map()

Expand Down Expand Up @@ -2114,20 +2165,40 @@ def get_roi_ids(
return matching_ids

def update_roi_ids_with_shorelines(self):
# Get the list of the ROI IDs that have extracted shorelines
# Get the list of the ROI IDs that have extracted shorelines
ids_with_extracted_shorelines = self.get_roi_ids(has_extracted_shorelines=True)

# if no ROIs have extracted shorelines, return otherwise load extracted shorelines for the first ROI ID with extracted shorelines
if not ids_with_extracted_shorelines:
self.id_container.ids = []
self.extract_shorelines_container.roi_ids_list = []
self.extract_shorelines_container.load_list = []
logger.warning("No ROIs found with extracted shorelines.")
return []

self.id_container.ids = list(ids_with_extracted_shorelines)
self.extract_shorelines_container.load_list = list(ids_with_extracted_shorelines)
self.extract_shorelines_container.roi_ids_list = list(
ids_with_extracted_shorelines
)
return ids_with_extracted_shorelines

def update_loadable_shorelines(self, selected_id: str):
extracted_shorelines = self.rois.get_extracted_shoreline(selected_id)
logger.info(f"ROI ID {selected_id} extracted shorelines {extracted_shorelines}")
# if extracted shorelines exist, load them onto map, if none exist nothing loads
if hasattr(extracted_shorelines, "gdf"):
if not extracted_shorelines.gdf.empty:
self.extract_shorelines_container.load_list = (
extracted_shorelines.gdf["satname"]
+ "_"
+ extracted_shorelines.gdf["date"].apply(
lambda x: x.strftime("%Y-%m-%d %H:%M:%S")
)
).tolist()
else:
return None
return extracted_shorelines

def load_extracted_shorelines_to_map(self, row_number: int = 0) -> None:
"""Loads stylized extracted shorelines onto the map for a single selected region of interest (ROI).
Expand All @@ -2151,22 +2222,12 @@ def load_extracted_shorelines_to_map(self, row_number: int = 0) -> None:
# Load extracted shorelines for the first ROI ID with extracted shorelines
# select the first ROI ID with extracted shorelines
selected_id = ids_with_extracted_shorelines[0]
# load the extracted shorelines for the selected ROI ID
extracted_shorelines = self.rois.get_extracted_shoreline(selected_id)
logger.info(f"ROI ID {selected_id} extracted shorelines {extracted_shorelines}")
# if extracted shorelines exist, load them onto map, if none exist nothing loads
if hasattr(extracted_shorelines, "gdf"):
if not extracted_shorelines.gdf.empty:
self.extract_shorelines_container.load_list = (
extracted_shorelines.gdf["satname"]
+ "_"
+ extracted_shorelines.gdf["date"].apply(
lambda x: x.strftime("%Y-%m-%d %H:%M:%S")
)
).tolist()

# load the extracted shorelines for the selected ROI ID
extracted_shorelines = self.update_loadable_shorelines(selected_id)
self.extract_shorelines_container.trash_list = []
self.load_extracted_shorelines_on_map(extracted_shorelines, row_number)
if extracted_shorelines:
self.load_extracted_shorelines_on_map(extracted_shorelines, row_number)

def load_extracted_shorelines_on_map(
self,
Expand All @@ -2182,18 +2243,10 @@ def load_extracted_shorelines_on_map(
"""
if extracted_shorelines is None:
return
style = {
"color": "#001aff", # Outline color
"opacity": 1, # opacity 1 means no transparency
"weight": 3, # Width
"fillColor": "#001aff", # Fill color
"fillOpacity": 0.8, # Fill opacity.
"radius": 1,
}
# create the extracted shoreline layer and add it to the map
layer_name = "extracted shoreline"
self.load_extracted_shoreline_layer(
extracted_shorelines.gdf.iloc[[row_number]], layer_name, style
extracted_shorelines.gdf.iloc[[row_number]], layer_name, colormap="viridis"
)

def load_feature_on_map(
Expand Down
34 changes: 26 additions & 8 deletions src/coastseg/extract_shorelines_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,18 @@ def __init__(self, extracted_shoreline_traitlet):
options=[],
layout=ipywidgets.Layout(padding="0px", margin="0px"),
)
self.ROI_list_widget = ipywidgets.Dropdown(
description="Available ROIs",
self.roi_list_widget = ipywidgets.Dropdown(
description="ROI Ids",
options=[],
layout=ipywidgets.Layout(width="90%", padding="0px", margin="0px"),
layout=ipywidgets.Layout(width="60%", padding="0px", margin="0px"),
)
# Define a lambda function that selects the first option in the options list
select_first_option = lambda change: self.roi_list_widget.set_trait(
"value", self.roi_list_widget.options[0]
)

# Register the callback function with the observe method
self.roi_list_widget.observe(select_first_option, names="options")

# Buttons
self.load_trash_button = ipywidgets.Button(
Expand Down Expand Up @@ -129,7 +136,7 @@ def __init__(self, extracted_shoreline_traitlet):
self.empty_trash_button.on_click(self.delete_all_button_clicked)

# callback function for when a roi is selected
self.ROI_list_widget.observe(self.on_roi_selected, names="value")
self.roi_list_widget.observe(self.on_roi_selected, names="value")
# callback function for when a load shoreline item is selected
self.load_list_widget.observe(self.on_load_selected, names="value")
# callback function for when a trash item is selected
Expand Down Expand Up @@ -164,7 +171,7 @@ def __init__(self, extracted_shoreline_traitlet):
total_VBOX = ipywidgets.VBox(
[
title_html,
self.ROI_list_widget,
self.roi_list_widget,
load_instruction_box,
load_list_vbox,
trash_instruction_box,
Expand Down Expand Up @@ -243,9 +250,14 @@ def on_load_selected(self, change: dict):
"radius": 1,
}
layer_name = "extracted shoreline"
# get the selected shorelines
selected_items = self.load_list_widget.value
# get the selected ROI ID
selected_id = self.roi_list_widget.value
if selected_items and self.load_callback:
self.load_callback(selected_items, layer_name, style)
self.load_callback(
selected_id, selected_items, layer_name, colormap="viridis"
)
else:
# if no items are selected remove the shorelines from the map
if self.remove_callback:
Expand All @@ -262,10 +274,13 @@ def on_trash_selected(self, change: dict):
"fillOpacity": 0.8, # Fill opacity.
"radius": 1,
}
# get the selected shorelines
selected_items = self.trash_list_widget.value
layer_name = "delete"
# get the selected ROI ID
selected_id = self.roi_list_widget.value
if selected_items and self.load_callback:
self.load_callback(selected_items, layer_name, style)
self.load_callback(selected_id, selected_items, layer_name, colormap="Reds")
else:
# if no items are selected remove the shorelines from the map
if self.remove_callback:
Expand Down Expand Up @@ -305,6 +320,9 @@ def delete_all_button_clicked(self, btn):
self.extracted_shoreline_traitlet.load_list = list(
set(self.load_list_widget.options) - set(selected_items)
)
# get the selected ROI ID
selected_id = self.roi_list_widget.value

if self.remove_all_callback:
layer_name = "delete"
self.remove_all_callback(layer_name, selected_items)
self.remove_all_callback(layer_name, selected_id, selected_items)
5 changes: 2 additions & 3 deletions src/coastseg/map_UI.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,8 @@ def __init__(self, coastseg_map, **kwargs):
self.extract_shorelines_widget.add_load_callback(
coastseg_map.load_selected_shorelines_on_map
)
self.extract_shorelines_widget.add_ROI_callback(
coastseg_map.load_selected_shorelines_on_map
)
self.extract_shorelines_widget.add_ROI_callback(coastseg_map.on_roi_change)

self.extract_shorelines_widget.add_remove_all_callback(
coastseg_map.delete_selected_shorelines
)
Expand Down

0 comments on commit 81aae9a

Please sign in to comment.