Skip to content

Commit

Permalink
#228 add new tests and update shoreline.py
Browse files Browse the repository at this point in the history
  • Loading branch information
2320sharon committed Feb 22, 2024
1 parent 8e16d98 commit 9701a4a
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 178 deletions.
39 changes: 33 additions & 6 deletions src/coastseg/shoreline.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,14 @@ def __str__(self):
if not self.gdf.empty:
geom_str = str(self.gdf.iloc[0]["geometry"])[:100] + "...)"
# Get CRS information
crs_info = f"CRS: {self.gdf.crs}" if self.gdf.crs else "CRS: None"
if self.gdf.empty:
crs_info = "CRS: None"
else:
if self.gdf is not None and hasattr(self.gdf, 'crs'):
crs_info = f"CRS: {self.gdf.crs}" if self.gdf.crs else "CRS: None"
else:
crs_info = "CRS: None"
ids = []
if "id" in self.gdf.columns:
ids = self.gdf["id"].astype(str)
return f"Shoreline:\nself.gdf:\n\n{crs_info}\n- Columns and Data Types:\n{col_info}\n\n- First 3 Rows:\n{first_rows}\n geometry: {geom_str}\nIDs:\n{ids}"
Expand All @@ -110,11 +117,18 @@ def __repr__(self):
if not self.gdf.empty:
geom_str = str(self.gdf.iloc[0]["geometry"])[:100] + "...)"
# Get CRS information
crs_info = f"CRS: {self.gdf.crs}" if self.gdf.crs else "CRS: None"
if self.gdf.empty:
crs_info = "CRS: None"
else:
if self.gdf is not None and hasattr(self.gdf, 'crs'):
crs_info = f"CRS: {self.gdf.crs}" if self.gdf.crs else "CRS: None"
else:
crs_info = "CRS: None"

ids = []
if "id" in self.gdf.columns:
ids = self.gdf["id"].astype(str)
return f"Shoreline:\nself.gdf:\n\n{crs_info}\n- Columns and Data Types:\n{col_info}\n\n- First 3 Rows:\n{first_rows}\n geometry: {geom_str}\nIDs:\n{ids}"

def initialize_shorelines(
self,
bbox: Optional[gpd.GeoDataFrame] = None,
Expand Down Expand Up @@ -210,6 +224,21 @@ def get_clipped_shoreline(
def get_intersecting_shoreline_files(
self, bbox: gpd.GeoDataFrame, bounding_boxes_location: str = ""
) -> List[str]:
"""
Retrieves a list of intersecting shoreline files based on the given bounding box.
Args:
bbox (gpd.GeoDataFrame): The bounding box to use for finding intersecting shoreline files.
bounding_boxes_location (str, optional): The location to store the bounding box files. If not provided,
it defaults to the download location specified during object initialization.
Returns:
List[str]: A list of intersecting shoreline file paths.
Raises:
ValueError: If no intersecting shorelines were available within the bounding box.
FileNotFoundError: If no shoreline files were found at the download location.
"""
# load the intersecting shoreline files
bounding_boxes_location = (
bounding_boxes_location
Expand All @@ -220,10 +249,8 @@ def get_intersecting_shoreline_files(
intersecting_files = get_intersecting_files(bbox, bounding_boxes_location)

if not intersecting_files:
logger.warning("No intersecting shoreline files were found.")
return []
raise ValueError(
"No intersecting shorelines shorelines were available within the bounding box. Try drawing a new bounding box elsewhere."
)

# Download any missing shoreline files
shoreline_files = self.get_shoreline_files(
Expand Down
17 changes: 17 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# This file is meant to hold fixtures that can be used for testing
# These fixtures set up data that can be used as inputs for the tests, so that no code is repeated
import os
import io
import json
import pytest
import tempfile
Expand All @@ -16,6 +17,22 @@

script_dir = os.path.dirname(os.path.abspath(__file__))

@pytest.fixture(scope="session")
def box_no_shorelines_transects():
geojson = {
"type": "FeatureCollection",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -82.823127, 44.023466 ], [ -82.823127, 44.041917 ], [ -82.802875, 44.041917 ], [ -82.802875, 44.023466 ], [ -82.823127, 44.023466 ] ] ] } }
]
}

# Convert the GeoJSON into a string
geojson_str = json.dumps(geojson)
# Convert the string into a file-like object
geojson_file = io.StringIO(geojson_str)
# Read the GeoJSON file into a GeoDataFrame
return gpd.read_file(geojson_file)

@pytest.fixture(scope="session")
def config_json_no_sitename_dir():
Expand Down
123 changes: 101 additions & 22 deletions tests/test_coastseg_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
from coastseg import transects
from coastseg import roi
from coastseg import exceptions
from coastseg import bbox
from coastseg import file_utilities
from coastseg import coastseg_map
from coastseg import common
from leafmap import Map
import pytest
import geopandas as gpd
from ipyleaflet import GeoJSON
from shapely import geometry

# def test_set_roi_settings():

Expand All @@ -28,7 +28,26 @@ def test_imports():
def test_init_coastseg_map_no_map():
coastsegmap=coastseg_map.CoastSeg_Map(create_map=False)
assert coastsegmap.map ==None

def test_get_roi_ids_no_rois():
"""
Test case to verify the behavior of get_roi_ids() method when there are no ROIs present.
"""
coastsegmap = coastseg_map.CoastSeg_Map()
assert coastsegmap.get_roi_ids() == []

def test_get_roi_ids_with_rois(valid_coastseg_map_with_settings, valid_rois_filepath):
"""
Test the get_roi_ids() method of CoastSegMap class when ROIs are loaded onto the map.
Args:
valid_coastseg_map_with_settings (CoastSegMap): A valid instance of CoastSegMap class.
valid_rois_filepath (str): The filepath of the valid ROIs file.
"""
actual_coastsegmap = valid_coastseg_map_with_settings
# test if rois will be correctly loaded onto map
actual_coastsegmap.load_feature_on_map("rois", file=valid_rois_filepath)
assert actual_coastsegmap.get_roi_ids() == ["17","30","35"]

def test_save_config_invalid_inputs(
valid_coastseg_map,
Expand All @@ -54,6 +73,40 @@ def test_save_config_invalid_inputs(
# save config will not work without ROIs loaded onto map
valid_coastseg_map_with_settings.save_config()

def test_load_feature_on_map_fail_load_default_shorelines(box_no_shorelines_transects):
"""Fail to load default shorelines if the bbox doesn't contain any shorelines"""
coastsegmap=coastseg_map.CoastSeg_Map(create_map=False)
coastsegmap.bbox = bbox.Bounding_Box(box_no_shorelines_transects)
# attempt to load default shorelines on the map ( it should fail)
with pytest.raises(exceptions.Object_Not_Found):
coastsegmap.load_feature_on_map("shorelines")

def test_load_feature_on_map_fail_load_default_transects(box_no_shorelines_transects):
"""Fail to load default transects if the bbox doesn't contain any transects"""
coastsegmap=coastseg_map.CoastSeg_Map(create_map=False)
coastsegmap.bbox = bbox.Bounding_Box(box_no_shorelines_transects)
# attempt to load default transects on the map ( it should fail)
with pytest.raises(exceptions.Object_Not_Found):
coastsegmap.load_feature_on_map("transects")


def test_load_feature_on_map_map_off(valid_bbox_gdf ):
"""Fail to load default transects if the bbox doesn't contain any transects"""
coastsegmap=coastseg_map.CoastSeg_Map(create_map=False)
coastsegmap.bbox = bbox.Bounding_Box(valid_bbox_gdf )
# attempt to load default transects on the map
coastsegmap.load_feature_on_map("transects")
# attempt to load default shorelines on the map
coastsegmap.load_feature_on_map("transects")

def test_load_feature_on_map_map_on(valid_bbox_gdf ):
"""Fail to load default transects if the bbox doesn't contain any transects"""
coastsegmap=coastseg_map.CoastSeg_Map(create_map=True)
coastsegmap.bbox = bbox.Bounding_Box(valid_bbox_gdf )
# attempt to load default transects on the map
coastsegmap.load_feature_on_map("transects")
# attempt to load default shorelines on the map
coastsegmap.load_feature_on_map("transects")

def test_save_config(coastseg_map_with_selected_roi_layer, tmp_path):
"""tests if save configs will save both a config.json and
Expand Down Expand Up @@ -182,27 +235,6 @@ def test_load_json_config_downloaded(
for roi_id, item in actual_config.get("settings", {}).items():
assert actual_coastsegmap.settings[roi_id] == item


# I don't think this can even happen in the current version of coastseg
# this test is a relic from when coastseg had a save_config button that allowed users to save
# not downloaded sessions.
# def test_load_json_config_when_data_path_not_exist(
# valid_coastseg_map_with_settings,
# valid_rois_filepath,
# config_json_no_sitename_dir,
# ):
# config_path, temp_dir = config_json_no_sitename_dir
# # tests if load_json_config will load contents into rois.roi_settings when rois have not been downloaded before
# # create instance of Coastseg_Map with settings and ROIs initially loaded
# actual_coastsegmap = valid_coastseg_map_with_settings
# actual_coastsegmap.load_feature_on_map("rois", file=valid_rois_filepath)
# # test if settings are correctly loaded when valid json config without 'filepath' & 'sitename' keys is loaded

# json_data = actual_coastsegmap.load_json_config(config_path)
# actual_coastsegmap.roi_settings = common.process_roi_settings(json_data, temp_dir)
# with pytest.raises(exceptions.WarningException):


def test_valid_shoreline_gdf(valid_shoreline_gdf: gpd.GeoDataFrame):
"""tests if a Shoreline will be created from a valid shoreline thats a gpd.GeoDataFrame
Args:
Expand Down Expand Up @@ -396,3 +428,50 @@ def test_load_rois_on_map_with_file(
# test if roi layer was added to map
existing_layer = actual_coastsegmap.map.find_layer(roi.ROI.LAYER_NAME)
assert existing_layer is not None


def test_load_feature_on_map_generate_rois(valid_bbox_gdf):
coastsegmap=coastseg_map.CoastSeg_Map()
# if no bounding box loaded on map this should raise an error
with pytest.raises(exceptions.Object_Not_Found):
coastsegmap.load_feature_on_map(
"rois",
lg_area=20,
sm_area=0,
units='km²',
)
# load bbox on map
coastsegmap.load_feature_on_map('bbox',gdf=valid_bbox_gdf)
# now that bbox is loaded on map, this should work
# this will automatically load a shoreline within the bbox
coastsegmap.load_feature_on_map(
"rois",
lg_area=20,
sm_area=0,
units='km²',
)
assert coastsegmap.rois is not None

def test_load_feature_on_map_rois_no_shoreline(box_no_shorelines_transects):
coastsegmap=coastseg_map.CoastSeg_Map()
# load bbox on map where no default shorelines are available
coastsegmap.load_feature_on_map('bbox',gdf=box_no_shorelines_transects)
# now that bbox is loaded on map, this should work
# this will automatically load a shoreline within the bbox
with pytest.raises(exceptions.Object_Not_Found):
coastsegmap.load_feature_on_map(
"rois",
lg_area=20,
sm_area=0,
units='km²',
)

def test_load_feature_on_map_rois_custom(box_no_shorelines_transects):
# this box has no default shorelines available but it can still load because its a custom ROI
coastsegmap=coastseg_map.CoastSeg_Map()
coastsegmap.load_feature_on_map(
"rois",
gdf=box_no_shorelines_transects
)


Loading

0 comments on commit 9701a4a

Please sign in to comment.