coastseg 1.0.0.dev6__tar.gz → 1.0.0.dev7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. {coastseg-1.0.0.dev6/src/coastseg.egg-info → coastseg-1.0.0.dev7}/PKG-INFO +2 -2
  2. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/pyproject.toml +2 -2
  3. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/coastseg_map.py +95 -29
  4. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/common.py +48 -35
  5. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/file_utilities.py +6 -4
  6. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7/src/coastseg.egg-info}/PKG-INFO +2 -2
  7. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg.egg-info/requires.txt +1 -1
  8. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/tests/test_common.py +117 -0
  9. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/LICENSE +0 -0
  10. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/MANIFEST.in +0 -0
  11. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/README.md +0 -0
  12. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/docs/config.md +0 -0
  13. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/docs/extract_shorelines.md +0 -0
  14. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/docs/gifs/change_layer_demo.gif +0 -0
  15. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/docs/gifs/colab_demo.gif +0 -0
  16. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/docs/gifs/colab_downloads_location.gif +0 -0
  17. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/docs/gifs/create_rois_demo.gif +0 -0
  18. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/docs/gifs/extract_shorelines_and_transects.gif +0 -0
  19. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/docs/gifs/gee_auth_tutorial.gif +0 -0
  20. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/docs/gifs/generate_rois_and_display_area.gif +0 -0
  21. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/docs/gifs/load_config_demo.gif +0 -0
  22. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/docs/gifs/load_geojson_demo.gif +0 -0
  23. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/docs/gifs/load_geojson_files_demo.gif +0 -0
  24. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/docs/gifs/load_shoreline_transect_demo.gif +0 -0
  25. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/docs/gifs/settings_demo.gif +0 -0
  26. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/docs/map_guide.md +0 -0
  27. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/docs/models.md +0 -0
  28. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/docs/roi.md +0 -0
  29. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/docs/settings.md +0 -0
  30. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/docs/transect_time_series.md +0 -0
  31. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/scripts/apply_tidal_correction.py +0 -0
  32. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/scripts/aviso_fes_tides.py +0 -0
  33. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/scripts/clip_and_write_new_nc_files_cmd_line.py +0 -0
  34. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/scripts/get_transects_points.py +0 -0
  35. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/scripts/shorten_transects.py +0 -0
  36. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/scripts/tide_regions_map.geojson +0 -0
  37. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/scripts/transects_swap_points.py +0 -0
  38. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/setup.cfg +0 -0
  39. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/setup.py +0 -0
  40. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/__init__.py +0 -0
  41. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/bbox.py +0 -0
  42. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/bounding_boxes/shorelines_bounding_boxes.csv +0 -0
  43. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/bounding_boxes/transects_bounding_boxes.csv +0 -0
  44. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/bounding_boxes/usa_shorelines_bounding_boxes.csv +0 -0
  45. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/bounding_boxes/usa_shorelines_bounding_boxes.geojson +0 -0
  46. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/bounding_boxes/world_reference_shorelines_bboxes.geojson +0 -0
  47. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/bounding_boxes/world_shorelines_bounding_boxes.csv +0 -0
  48. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/coastseg_logs.py +0 -0
  49. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/download_tide_model.py +0 -0
  50. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/downloads.py +0 -0
  51. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/exception_handler.py +0 -0
  52. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/exceptions.py +0 -0
  53. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/extracted_shoreline.py +0 -0
  54. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/factory.py +0 -0
  55. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/geodata_processing.py +0 -0
  56. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/map_UI.py +0 -0
  57. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/models_UI.py +0 -0
  58. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/roi.py +0 -0
  59. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/sessions.py +0 -0
  60. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/settings_UI.py +0 -0
  61. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/shoreline.py +0 -0
  62. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/tide_correction.py +0 -0
  63. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/tide_model/__init__.py +0 -0
  64. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/tide_model/tide_regions_map.geojson +0 -0
  65. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_0.geojson +0 -0
  66. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_100.geojson +0 -0
  67. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_102.geojson +0 -0
  68. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_103.geojson +0 -0
  69. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_106.geojson +0 -0
  70. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_109.geojson +0 -0
  71. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_110.geojson +0 -0
  72. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_117.geojson +0 -0
  73. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_15.geojson +0 -0
  74. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_16.geojson +0 -0
  75. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_2.geojson +0 -0
  76. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_22.geojson +0 -0
  77. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_23.geojson +0 -0
  78. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_24.geojson +0 -0
  79. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_29.geojson +0 -0
  80. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_30.geojson +0 -0
  81. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_31.geojson +0 -0
  82. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_32.geojson +0 -0
  83. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_33.geojson +0 -0
  84. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_34.geojson +0 -0
  85. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_8.geojson +0 -0
  86. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_95.geojson +0 -0
  87. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_96.geojson +0 -0
  88. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects/Transects_grid_region_99.geojson +0 -0
  89. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/transects.py +0 -0
  90. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/upload_feature_widget.py +0 -0
  91. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/watchable_slider.py +0 -0
  92. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg/zoo_model.py +0 -0
  93. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg.egg-info/SOURCES.txt +0 -0
  94. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg.egg-info/dependency_links.txt +0 -0
  95. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/src/coastseg.egg-info/top_level.txt +0 -0
  96. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/tests/__init__.py +0 -0
  97. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/tests/conftest.py +0 -0
  98. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/tests/test_bbox.py +0 -0
  99. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/tests/test_coastseg_map.py +0 -0
  100. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/tests/test_data/config.json +0 -0
  101. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/tests/test_data/config_gdf_id_2.geojson +0 -0
  102. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/tests/test_data/config_id_2.json +0 -0
  103. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/tests/test_data/transect_compatible_bbox.geojson +0 -0
  104. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/tests/test_data/transect_compatible_rois.geojson +0 -0
  105. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/tests/test_data/transect_compatible_shoreline.geojson +0 -0
  106. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/tests/test_data/transect_compatible_transects.geojson +0 -0
  107. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/tests/test_data/valid_bbox.geojson +0 -0
  108. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/tests/test_data/valid_rois.geojson +0 -0
  109. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/tests/test_extracted_shoreline.py +0 -0
  110. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/tests/test_imports.py +0 -0
  111. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/tests/test_roi.py +0 -0
  112. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/tests/test_shoreline.py +0 -0
  113. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/tests/test_transects.py +0 -0
  114. {coastseg-1.0.0.dev6 → coastseg-1.0.0.dev7}/tests/test_upload_widget.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: coastseg
3
- Version: 1.0.0.dev6
3
+ Version: 1.0.0.dev7
4
4
  Summary: An interactive jupyter notebook for downloading satellite imagery
5
5
  Author-email: Sharon Fitzpatrick <sharon.fitzpatrick23@gmail.com>
6
6
  License: MIT License
@@ -35,7 +35,7 @@ Classifier: Topic :: Scientific/Engineering :: GIS
35
35
  Requires-Python: >=3.9
36
36
  Description-Content-Type: text/markdown
37
37
  License-File: LICENSE
38
- Requires-Dist: coastsat-package>=0.1.16
38
+ Requires-Dist: coastsat-package>=0.1.18
39
39
  Requires-Dist: area
40
40
  Requires-Dist: doodleverse-utils>=0.0.35
41
41
  Requires-Dist: ipyfilechooser>=0.6.0
@@ -5,14 +5,14 @@ build-backend = "setuptools.build_meta"
5
5
  [project]
6
6
  name = "coastseg"
7
7
  dynamic = ["readme"]
8
- version = "1.0.0dev6"
8
+ version = "1.0.0dev7"
9
9
  authors = [
10
10
  { name=" Sharon Fitzpatrick", email="sharon.fitzpatrick23@gmail.com" },
11
11
  ]
12
12
  # find` directive with `include` or `exclude`
13
13
  description = "An interactive jupyter notebook for downloading satellite imagery"
14
14
  dependencies = [
15
- "coastsat-package>=0.1.16",
15
+ "coastsat-package>=0.1.18",
16
16
  "area",
17
17
  "doodleverse-utils>=0.0.35",
18
18
  "ipyfilechooser>=0.6.0",
@@ -35,7 +35,7 @@ from coastseg import (
35
35
  exception_handler,
36
36
  )
37
37
  from coastsat import SDS_download
38
-
38
+ from coastsat.SDS_download import get_metadata
39
39
 
40
40
  logger = logging.getLogger(__name__)
41
41
 
@@ -207,6 +207,63 @@ class CoastSeg_Map:
207
207
  else:
208
208
  print("\ntidal corrections completed")
209
209
 
210
+ def load_metadata(self, settings: dict = {}, ids: Collection = set([])):
211
+ """
212
+ Loads metadata either based on user-provided settings or a collection of ROI IDs.
213
+
214
+ This method either takes in a dictionary with site-specific settings to load metadata
215
+ for a particular site, or iterates over a collection of ROI IDs to load their respective
216
+ metadata using the settings associated with those ROI IDs.
217
+
218
+ Note that coastsat's `get_metadata` is used to actually perform the metadata loading.
219
+
220
+ Parameters:
221
+ -----------
222
+ settings: dict, optional
223
+ A dictionary containing settings for a specific site. The settings should
224
+ include 'sitename' and 'filepath_data' keys, among others. Default is an empty dict.
225
+
226
+ ids: Collection, optional
227
+ A collection (e.g., set, list) of ROI IDs to load metadata for. Default is an empty set.
228
+
229
+ Raises:
230
+ -----------
231
+ FileNotFoundError:
232
+ If the directory specified in the settings or by ROI IDs does not exist.
233
+
234
+ Exception:
235
+ If neither settings nor ids are provided.
236
+
237
+ Returns:
238
+ -----------
239
+ None
240
+ metadata dictionary
241
+
242
+ Examples:
243
+ -----------
244
+ >>> load_metadata(settings={'sitename': 'site1', 'filepath_data': '/path/to/data'})
245
+ >>> load_metadata(ids={1, 2, 3})
246
+
247
+ """
248
+ if settings and isinstance(settings, dict):
249
+ return get_metadata(settings)
250
+ elif ids:
251
+ for roi_id in ids:
252
+ # if the ROI directory did not exist then print a warning and proceed
253
+ try:
254
+ logger.info(
255
+ f"Loading metadata using {self.rois.roi_settings[str(roi_id)]}"
256
+ )
257
+ metadata = get_metadata(self.rois.roi_settings[str(roi_id)])
258
+ logger.info(f"Metadata for ROI ID {str(roi_id)}:{metadata}")
259
+ return metadata
260
+ except FileNotFoundError as e:
261
+ logger.error(f"Metadata not loaded for ROI ID {str(roi_id)} {e}")
262
+ print(f"Metadata not loaded for ROI ID {str(roi_id)} {e}")
263
+
264
+ else:
265
+ raise Exception(f"Must provide settings or list of IDs to load metadata.")
266
+
210
267
  def load_session_files(self, dir_path: str) -> None:
211
268
  """
212
269
  Load the configuration files from the given directory.
@@ -229,6 +286,11 @@ class CoastSeg_Map:
229
286
  # load the config files if they exist
230
287
  data_path = file_utilities.create_directory(os.getcwd(), "data")
231
288
  config_loaded = self.load_config_files(dir_path, data_path)
289
+ # create metadata files for each ROI loaded in using coastsat's get_metadata()
290
+ if self.rois and getattr(self.rois, "roi_settings"):
291
+ self.load_metadata(ids=list(self.rois.roi_settings.keys()))
292
+ else:
293
+ logger.warning(f"No ROIs were able to have their metadata loaded.")
232
294
  # load in settings files
233
295
  for file_name in os.listdir(dir_path):
234
296
  file_path = os.path.join(dir_path, file_name)
@@ -612,15 +674,15 @@ class CoastSeg_Map:
612
674
  """
613
675
 
614
676
  self.validate_download_imagery_inputs()
615
- # selected_layer contains the selected ROIs
616
- selected_layer = self.map.find_layer(ROI.SELECTED_LAYER_NAME)
617
- logger.info(f"selected_layer: {selected_layer}")
618
677
 
619
678
  # Get the location where the downloaded imagery will be saved
620
679
  file_path = os.path.abspath(os.path.join(os.getcwd(), "data"))
621
680
  date_str = file_utilities.generate_datestring()
622
-
623
681
  settings = self.get_settings()
682
+
683
+ # selected_layer contains the selected ROIs
684
+ selected_layer = self.map.find_layer(ROI.SELECTED_LAYER_NAME)
685
+ logger.info(f"selected_layer: {selected_layer}")
624
686
  # Create a list of download settings for each ROI
625
687
  roi_settings = common.create_roi_settings(
626
688
  settings, selected_layer.data, file_path, date_str
@@ -633,6 +695,9 @@ class CoastSeg_Map:
633
695
  inputs_list = list(roi_settings.values())
634
696
  logger.info(f"inputs_list {inputs_list}")
635
697
 
698
+ # Save settings used to download rois and the objects on map to config files
699
+ self.save_config()
700
+
636
701
  # 2. For each ROI use download settings to download imagery and save to jpg
637
702
  print("Download in progress")
638
703
  # for each ROI use the ROI settings to download imagery and save to jpg
@@ -644,8 +709,6 @@ class CoastSeg_Map:
644
709
  save_jpg=True,
645
710
  apply_cloud_mask=settings.get("apply_cloud_mask", True),
646
711
  )
647
- # 3.save settings used to download rois and the objects on map to config files
648
- self.save_config()
649
712
  logger.info("Done downloading")
650
713
 
651
714
  def _extract_and_validate_roi_settings(
@@ -794,6 +857,8 @@ class CoastSeg_Map:
794
857
  exception_handler.check_empty_roi_layer(selected_layer)
795
858
  logger.info(f"self.rois.roi_settings: {self.rois.roi_settings}")
796
859
 
860
+ # @todo can this be removed since we don't have a save_config button anymore?
861
+ # if the rois do not have any settings then save the currently loaded settings to the ROIs
797
862
  if not self.rois.roi_settings:
798
863
  filepath = filepath or os.path.abspath(os.getcwd())
799
864
  roi_settings = common.create_roi_settings(
@@ -801,20 +866,23 @@ class CoastSeg_Map:
801
866
  )
802
867
  self.rois.set_roi_settings(roi_settings)
803
868
 
804
- # create dictionary to be saved to config.json
869
+ # create dictionary of settings for each ROI to be saved to config.json
805
870
  roi_ids = self.get_selected_roi_ids()
806
871
  selected_roi_settings = {
807
872
  roi_id: self.rois.roi_settings[roi_id] for roi_id in roi_ids
808
873
  }
874
+ # combine the settings for each ROI with the rest of the currently loaded settings
809
875
  config_json = common.create_json_config(selected_roi_settings, settings)
810
876
 
811
- shorelines_gdf = self.shoreline.gdf if self.shoreline else None
812
- transects_gdf = self.transects.gdf if self.transects else None
813
- bbox_gdf = self.bbox.gdf if self.bbox else None
877
+ shorelines_gdf = (
878
+ getattr(self.shoreline, "gdf", None) if self.shoreline else None
879
+ )
880
+ transects_gdf = getattr(self.transects, "gdf", None) if self.transects else None
881
+ bbox_gdf = getattr(self.bbox, "gdf", None) if self.bbox else None
814
882
  selected_rois = self.get_selected_rois(roi_ids)
883
+ logger.info(f"selected_rois: {selected_rois}")
815
884
 
816
885
  # save all selected rois, shorelines, transects and bbox to config geodataframe
817
- logger.info(f"selected_rois: {selected_rois}")
818
886
  if selected_rois is not None:
819
887
  if not selected_rois.empty:
820
888
  epsg_code = selected_rois.crs
@@ -828,13 +896,17 @@ class CoastSeg_Map:
828
896
  logger.info(f"config_gdf: {config_gdf} ")
829
897
  is_downloaded = common.were_rois_downloaded(self.rois.roi_settings, roi_ids)
830
898
 
831
- if filepath is not None:
832
- # if a filepath is provided then save the config.json and config_gdf.geojson immediately
833
- file_utilities.config_to_file(config_json, filepath)
834
- file_utilities.config_to_file(config_gdf, filepath)
835
- elif filepath is None:
836
- # data has been downloaded before so inputs have keys 'filepath' and 'sitename'
837
- if is_downloaded == True:
899
+ def save_config_files(config_json, config_gdf, path):
900
+ """Helper function to save config files."""
901
+ file_utilities.config_to_file(config_json, path)
902
+ file_utilities.config_to_file(config_gdf, path)
903
+
904
+ if filepath:
905
+ # If a filepath is provided then save the config.json and config_gdf.geojson immediately
906
+ save_config_files(config_json, config_gdf, filepath)
907
+ else:
908
+ # if data has been downloaded before then inputs have keys 'filepath' and 'sitename'
909
+ if is_downloaded:
838
910
  # write config_json file to each directory where a roi was saved
839
911
  roi_ids = config_json["roi_ids"]
840
912
  for roi_id in roi_ids:
@@ -842,19 +914,13 @@ class CoastSeg_Map:
842
914
  filepath = os.path.abspath(
843
915
  os.path.join(config_json[roi_id]["filepath"], sitename)
844
916
  )
845
- # save to config.json
846
- file_utilities.config_to_file(config_json, filepath)
847
- # save to config_gdf.geojson
848
- file_utilities.config_to_file(config_gdf, filepath)
917
+ save_config_files(config_json, config_gdf, filepath)
849
918
  print("Saved config files for each ROI")
850
- elif is_downloaded == False:
919
+ else:
851
920
  # if data is not downloaded save to coastseg directory
852
921
  filepath = os.path.abspath(os.getcwd())
853
- # save to config.json
854
- file_utilities.config_to_file(config_json, filepath)
855
- # save to config_gdf.geojson
856
- file_utilities.config_to_file(config_gdf, filepath)
857
- print("Saved config files for each ROI")
922
+ save_config_files(config_json, config_gdf, filepath)
923
+ print(f"Saved config files for each ROI to {filepath}")
858
924
 
859
925
  def set_settings(self, **kwargs):
860
926
  """
@@ -1343,10 +1343,17 @@ def create_json_config(inputs: dict, settings: dict, roi_ids: list[str] = []) ->
1343
1343
  config = {**inputs}
1344
1344
  config["roi_ids"] = roi_ids
1345
1345
  config["settings"] = settings
1346
- logger.info(f"config_json: {config} ")
1346
+ logger.info(f"config_json: {config}")
1347
1347
  return config
1348
1348
 
1349
1349
 
1350
+ def set_crs_or_initialize_empty(gdf: gpd.GeoDataFrame, epsg_code: str):
1351
+ """Set the CRS for the given GeoDataFrame or initialize an empty one."""
1352
+ if gdf is not None and not gdf.empty:
1353
+ return gdf.to_crs(epsg_code)
1354
+ return gpd.GeoDataFrame(geometry=[], crs=epsg_code)
1355
+
1356
+
1350
1357
  def create_config_gdf(
1351
1358
  rois_gdf: gpd.GeoDataFrame,
1352
1359
  shorelines_gdf: gpd.GeoDataFrame = None,
@@ -1354,48 +1361,54 @@ def create_config_gdf(
1354
1361
  bbox_gdf: gpd.GeoDataFrame = None,
1355
1362
  epsg_code: int = None,
1356
1363
  ) -> gpd.GeoDataFrame:
1357
- if epsg_code is None and rois_gdf is None:
1358
- raise ValueError(
1359
- "Cannot create config GeoDataFrame without a CRS or an empty ROI GeoDataFrame"
1360
- )
1361
- # Check if CRS is provided or if the ROI GeoDataFrame is empty
1362
- if epsg_code is None and rois_gdf.empty:
1364
+ """
1365
+ Create a concatenated GeoDataFrame from provided GeoDataFrames with a consistent CRS.
1366
+
1367
+ Parameters:
1368
+ - rois_gdf (gpd.GeoDataFrame): The GeoDataFrame containing Regions of Interest (ROIs).
1369
+ - shorelines_gdf (gpd.GeoDataFrame, optional): The GeoDataFrame containing shorelines. Defaults to None.
1370
+ - transects_gdf (gpd.GeoDataFrame, optional): The GeoDataFrame containing transects. Defaults to None.
1371
+ - bbox_gdf (gpd.GeoDataFrame, optional): The GeoDataFrame containing bounding boxes. Defaults to None.
1372
+ - epsg_code (int, optional): The EPSG code for the desired CRS. If not provided and rois_gdf is non-empty,
1373
+ the CRS of rois_gdf will be used. If not provided and rois_gdf is empty, an error will be raised.
1374
+
1375
+ Returns:
1376
+ - gpd.GeoDataFrame: A concatenated GeoDataFrame with a consistent CRS, and a "type" column
1377
+ indicating the type of each geometry (either "roi", "shoreline", "transect", or "bbox").
1378
+
1379
+ Raises:
1380
+ - ValueError: If both epsg_code is None and rois_gdf is None or empty.
1381
+
1382
+ Notes:
1383
+ - The function will convert each provided GeoDataFrame to the specified CRS.
1384
+ - If any of the input GeoDataFrames is None or empty, it will be initialized as an empty GeoDataFrame
1385
+ with the specified CRS.
1386
+ """
1387
+ # Determine CRS
1388
+ if not epsg_code and (rois_gdf is None or rois_gdf.empty):
1363
1389
  raise ValueError(
1364
- "Cannot create config GeoDataFrame without a CRS or an empty ROI GeoDataFrame"
1390
+ "Either provide a valid epsg code or a non-empty rois_gdf to determine the CRS."
1365
1391
  )
1366
- if epsg_code is None:
1392
+ if not epsg_code:
1367
1393
  epsg_code = rois_gdf.crs
1368
1394
 
1369
- # Set CRS for the non-empty GeoDataFrames
1370
- if rois_gdf is not None and not rois_gdf.empty:
1371
- rois_gdf = rois_gdf.to_crs(epsg_code)
1372
- else:
1373
- rois_gdf = gpd.GeoDataFrame(geometry=[], crs=epsg_code)
1374
- if shorelines_gdf is not None and not shorelines_gdf.empty:
1375
- shorelines_gdf = shorelines_gdf.to_crs(epsg_code)
1376
- else:
1377
- shorelines_gdf = gpd.GeoDataFrame(geometry=[], crs=epsg_code)
1378
- if transects_gdf is not None and not transects_gdf.empty:
1379
- transects_gdf = transects_gdf.to_crs(epsg_code)
1380
- else:
1381
- transects_gdf = gpd.GeoDataFrame(geometry=[], crs=epsg_code)
1382
- if bbox_gdf is not None and not bbox_gdf.empty:
1383
- bbox_gdf = bbox_gdf.to_crs(epsg_code)
1384
- else:
1385
- bbox_gdf = gpd.GeoDataFrame(geometry=[], crs=epsg_code)
1395
+ # Dictionary to map gdf variables to their types
1396
+ gdfs = {
1397
+ "roi": rois_gdf,
1398
+ "shoreline": shorelines_gdf,
1399
+ "transect": transects_gdf,
1400
+ "bbox": bbox_gdf,
1401
+ }
1386
1402
 
1387
- # Assign "type" column values
1388
- rois_gdf["type"] = "roi"
1389
- shorelines_gdf["type"] = "shoreline"
1390
- transects_gdf["type"] = "transect"
1391
- bbox_gdf["type"] = "bbox"
1403
+ # Process each GeoDataFrame
1404
+ for gdf_type, gdf in gdfs.items():
1405
+ gdfs[gdf_type] = set_crs_or_initialize_empty(gdf, epsg_code)
1406
+ gdfs[gdf_type]["type"] = gdf_type
1392
1407
 
1393
1408
  # Concatenate GeoDataFrames
1394
- config_gdf = pd.concat(
1395
- [rois_gdf, shorelines_gdf, transects_gdf, bbox_gdf], ignore_index=True
1396
- )
1409
+ config_gdf = pd.concat(gdfs.values(), ignore_index=True)
1397
1410
 
1398
- return gpd.GeoDataFrame(config_gdf)
1411
+ return config_gdf
1399
1412
 
1400
1413
 
1401
1414
  def get_jpgs_from_data() -> str:
@@ -272,23 +272,24 @@ def find_parent_directory(
272
272
  path = parent_dir
273
273
 
274
274
 
275
- def config_to_file(config: Union[dict, gpd.GeoDataFrame], file_path: str):
275
+ def config_to_file(config: Union[dict, gpd.GeoDataFrame], filepath: str):
276
276
  """Saves config to config.json or config_gdf.geojson
277
277
  config's type is dict or geodataframe respectively
278
278
 
279
279
  Args:
280
280
  config (Union[dict, gpd.GeoDataFrame]): data to save to config file
281
- file_path (str): full path to directory to save config file
281
+ filepath (str): full path to directory to save config file
282
282
  """
283
283
  if isinstance(config, dict):
284
284
  filename = f"config.json"
285
- save_path = os.path.abspath(os.path.join(file_path, filename))
285
+ save_path = os.path.abspath(os.path.join(filepath, filename))
286
286
  write_to_json(save_path, config)
287
287
  logger.info(f"Saved config json: {filename} \nSaved to {save_path}")
288
288
  elif isinstance(config, gpd.GeoDataFrame):
289
289
  filename = f"config_gdf.geojson"
290
- save_path = os.path.abspath(os.path.join(file_path, filename))
290
+ save_path = os.path.abspath(os.path.join(filepath, filename))
291
291
  logger.info(f"Saving config gdf:{config} \nSaved to {save_path}")
292
+ os.makedirs(os.path.dirname(filepath), exist_ok=True)
292
293
  config.to_file(save_path, driver="GeoJSON")
293
294
 
294
295
 
@@ -497,6 +498,7 @@ def create_directory(file_path: str, name: str) -> str:
497
498
 
498
499
  def write_to_json(filepath: str, settings: dict):
499
500
  """ "Write the settings dictionary to json file"""
501
+ os.makedirs(os.path.dirname(filepath), exist_ok=True)
500
502
  to_file(settings, filepath)
501
503
  # with open(filepath, "w", encoding="utf-8") as output_file:
502
504
  # json.dump(settings, output_file)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: coastseg
3
- Version: 1.0.0.dev6
3
+ Version: 1.0.0.dev7
4
4
  Summary: An interactive jupyter notebook for downloading satellite imagery
5
5
  Author-email: Sharon Fitzpatrick <sharon.fitzpatrick23@gmail.com>
6
6
  License: MIT License
@@ -35,7 +35,7 @@ Classifier: Topic :: Scientific/Engineering :: GIS
35
35
  Requires-Python: >=3.9
36
36
  Description-Content-Type: text/markdown
37
37
  License-File: LICENSE
38
- Requires-Dist: coastsat-package>=0.1.16
38
+ Requires-Dist: coastsat-package>=0.1.18
39
39
  Requires-Dist: area
40
40
  Requires-Dist: doodleverse-utils>=0.0.35
41
41
  Requires-Dist: ipyfilechooser>=0.6.0
@@ -1,4 +1,4 @@
1
- coastsat-package>=0.1.16
1
+ coastsat-package>=0.1.18
2
2
  area
3
3
  doodleverse-utils>=0.0.35
4
4
  ipyfilechooser>=0.6.0
@@ -14,6 +14,123 @@ import geopandas as gpd
14
14
  import numpy as np
15
15
  from shapely import geometry
16
16
 
17
+ import geopandas as gpd
18
+ import pandas as pd
19
+ from shapely.geometry import Point
20
+ import pytest
21
+
22
+
23
+ def test_valid_input():
24
+ rois = gpd.GeoDataFrame({"geometry": [Point(1, 1)]}, crs="EPSG:4326")
25
+
26
+ result = common.create_config_gdf(rois)
27
+ assert not result.empty
28
+ assert result.crs == "EPSG:4326"
29
+ assert "type" in result.columns
30
+ assert result["type"][0] == "roi"
31
+
32
+
33
+ def test_with_multiple_gdfs():
34
+ rois = gpd.GeoDataFrame({"geometry": [Point(1, 1), Point(2, 2)]}, crs="EPSG:4326")
35
+
36
+ shorelines = gpd.GeoDataFrame(
37
+ {"geometry": [LineString([(0, 0), (1, 1)])]}, crs="EPSG:4326"
38
+ )
39
+
40
+ result = common.create_config_gdf(rois, shorelines_gdf=shorelines, epsg_code=3857)
41
+
42
+ assert not result.empty
43
+ assert result.crs == "EPSG:3857"
44
+ assert result["type"].nunique() == 2
45
+ assert set(result["type"].unique()) == {"roi", "shoreline"}
46
+
47
+
48
+ def test_with_empty_rois_and_no_epsg():
49
+ with pytest.raises(ValueError):
50
+ result = common.create_config_gdf(None)
51
+
52
+
53
+ def test_with_empty_rois_and_valid_epsg():
54
+ result = common.create_config_gdf(None, epsg_code=3857)
55
+ assert result.empty
56
+ assert result.crs == "EPSG:3857"
57
+
58
+
59
+ def test_with_all_gdfs():
60
+ rois = gpd.GeoDataFrame({"geometry": [Point(1, 1), Point(2, 2)]}, crs="EPSG:4326")
61
+
62
+ shorelines = gpd.GeoDataFrame(
63
+ {"geometry": [LineString([(0, 0), (1, 1)])]}, crs="EPSG:4326"
64
+ )
65
+
66
+ transects = gpd.GeoDataFrame(
67
+ {"geometry": [LineString([(2, 2), (3, 3)])]}, crs="EPSG:4326"
68
+ )
69
+
70
+ bbox = gpd.GeoDataFrame({"geometry": [Point(3, 3), Point(4, 4)]}, crs="EPSG:4326")
71
+
72
+ result = common.create_config_gdf(
73
+ rois,
74
+ shorelines_gdf=shorelines,
75
+ transects_gdf=transects,
76
+ bbox_gdf=bbox,
77
+ epsg_code=3857,
78
+ )
79
+
80
+ assert not result.empty
81
+ assert result.crs == "EPSG:3857"
82
+ assert result["type"].nunique() == 4
83
+ assert set(result["type"].unique()) == {"roi", "shoreline", "transect", "bbox"}
84
+
85
+
86
+ def test_set_crs_for_non_empty_gdf_with_same_epsg():
87
+ # Create a GeoDataFrame with some data
88
+ gdf = gpd.GeoDataFrame({"geometry": [Point(1, 1)]}, crs="EPSG:4326")
89
+
90
+ result = common.set_crs_or_initialize_empty(gdf, "EPSG:4326")
91
+
92
+ # Check that the resulting GeoDataFrame has the correct CRS
93
+ assert result.crs == "EPSG:4326"
94
+ # Check that the data remains unchanged
95
+ assert not result.empty
96
+
97
+
98
+ def test_set_crs_for_non_empty_gdf():
99
+ # Create a GeoDataFrame with some data
100
+ gdf = gpd.GeoDataFrame({"geometry": [Point(1, 1)]}, crs="EPSG:4326")
101
+
102
+ result = common.set_crs_or_initialize_empty(gdf, "EPSG:3857")
103
+
104
+ # Check that the resulting GeoDataFrame has the correct CRS
105
+ assert result.crs == "EPSG:3857"
106
+ # Check that the data remains unchanged
107
+ assert not result.empty
108
+
109
+
110
+ def test_initialize_empty_for_none_gdf():
111
+ result = common.set_crs_or_initialize_empty(None, "EPSG:3857")
112
+
113
+ # Check that the resulting GeoDataFrame is empty but has the correct CRS
114
+ assert result.crs == "EPSG:3857"
115
+ assert result.empty
116
+
117
+
118
+ def test_initialize_empty_for_empty_gdf():
119
+ gdf = gpd.GeoDataFrame(geometry=[], crs="EPSG:4326")
120
+ result = common.set_crs_or_initialize_empty(gdf, "EPSG:3857")
121
+
122
+ # Check that the resulting GeoDataFrame is empty but has the correct CRS
123
+ assert result.crs == "EPSG:3857"
124
+ assert result.empty
125
+
126
+
127
+ def test_raise_exception_on_invalid_crs():
128
+ gdf = gpd.GeoDataFrame({"geometry": [Point(1, 1)]}, crs="EPSG:4326")
129
+
130
+ with pytest.raises(Exception):
131
+ # Attempt to convert to a non-existent EPSG code
132
+ common.set_crs_or_initialize_empty(gdf, "EPSG:999999")
133
+
17
134
 
18
135
  # Test data
19
136
  @pytest.fixture(scope="function")
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes