water-column-sonar-annotation 26.1.8__py3-none-any.whl

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 (27) hide show
  1. tests/__init__.py +0 -0
  2. tests/astronomical/__init__.py +0 -0
  3. tests/astronomical/test_astronomical_manager.py +148 -0
  4. tests/conftest.py +60 -0
  5. tests/cruise/__init__.py +0 -0
  6. tests/cruise/test_cruise_manager.py +80 -0
  7. tests/geospatial/__init__.py +0 -0
  8. tests/geospatial/test_geospatial_manager.py +86 -0
  9. tests/record/test_echoview_record_manager.py +160 -0
  10. water_column_sonar_annotation/__init__.py +5 -0
  11. water_column_sonar_annotation/astronomical/__init__.py +5 -0
  12. water_column_sonar_annotation/astronomical/astronomical_manager.py +82 -0
  13. water_column_sonar_annotation/cruise/__init__.py +5 -0
  14. water_column_sonar_annotation/cruise/cruise_manager.py +104 -0
  15. water_column_sonar_annotation/geospatial/__init__.py +5 -0
  16. water_column_sonar_annotation/geospatial/geospatial_manager.py +143 -0
  17. water_column_sonar_annotation/record/__init__.py +9 -0
  18. water_column_sonar_annotation/record/echoview_record_manager.py +426 -0
  19. water_column_sonar_annotation/record/graph_record_manager.py +82 -0
  20. water_column_sonar_annotation/record/parquet_record_manager.py +83 -0
  21. water_column_sonar_annotation/shape/__init__.py +5 -0
  22. water_column_sonar_annotation/shape/shape_manager.py +29 -0
  23. water_column_sonar_annotation-26.1.8.dist-info/METADATA +109 -0
  24. water_column_sonar_annotation-26.1.8.dist-info/RECORD +27 -0
  25. water_column_sonar_annotation-26.1.8.dist-info/WHEEL +5 -0
  26. water_column_sonar_annotation-26.1.8.dist-info/licenses/LICENSE +21 -0
  27. water_column_sonar_annotation-26.1.8.dist-info/top_level.txt +2 -0
@@ -0,0 +1,104 @@
1
+ import numpy as np
2
+ import xarray as xr
3
+
4
+
5
+ class CruiseManager:
6
+ #######################################################
7
+ def __init__(
8
+ self,
9
+ bucket_name: str = "noaa-wcsd-zarr-pds",
10
+ ship_name: str = "Henry_B._Bigelow",
11
+ cruise_name: str = "HB1906",
12
+ sensor_name: str = "EK60",
13
+ ):
14
+ self.level: str = "level_2a"
15
+ self.bucket_name: str = bucket_name
16
+ self.ship_name: str = ship_name
17
+ self.cruise_name: str = cruise_name
18
+ self.sensor_name: str = sensor_name
19
+ try:
20
+ zarr_store = f"{self.cruise_name}.zarr"
21
+ s3_zarr_store_path = f"{self.bucket_name}/{self.level}/{self.ship_name}/{self.cruise_name}/{self.sensor_name}/{zarr_store}"
22
+
23
+ kwargs = {"consolidated": False}
24
+ cruise = xr.open_dataset(
25
+ f"s3://{s3_zarr_store_path}",
26
+ engine="zarr",
27
+ storage_options={"anon": True},
28
+ # chunks={},
29
+ **kwargs,
30
+ )
31
+ self.cruise = cruise
32
+ except Exception as e:
33
+ print(f"Could not open cruise: {e}")
34
+
35
+ def get_cruise(
36
+ self,
37
+ ):
38
+ try:
39
+ return self.cruise
40
+ except Exception as e:
41
+ print(f"Could not open cruise: {e}")
42
+
43
+ def get_coordinates(
44
+ self,
45
+ start_time, # ="2019-10-16T16:20:00", # In UTC
46
+ end_time,
47
+ ):
48
+ """gets the gps coordinates using the time & cruise"""
49
+ try:
50
+ cruise_select = self.cruise.sel(time=slice(start_time, end_time))
51
+ return cruise_select.latitude.values[0], cruise_select.longitude.values[0]
52
+ except Exception as e:
53
+ print(f"Could not find depth: {e}")
54
+
55
+ def get_depth(
56
+ self,
57
+ start_time="2019-10-16T16:20:00",
58
+ end_time="2019-10-16T16:50:00",
59
+ ):
60
+ """
61
+ Returns the bottom depth in meters for a given interval of ISO
62
+ timestamps. Value returned is the minimum depth over that interval.
63
+ """
64
+ try:
65
+ cruise = self.cruise # get_cruise()
66
+ # bottom_depths = cruise.time.where(
67
+ # (
68
+ # (cruise.time > np.datetime64(start_time))
69
+ # & (cruise.time < np.datetime64(end_time))
70
+ # ),
71
+ # drop=True,
72
+ # ).bottom.values
73
+ time_slice = slice(start_time, end_time)
74
+ bottom_depths = cruise.sel(time=time_slice).bottom.values
75
+ if np.all(np.isnan(bottom_depths)):
76
+ return np.nan
77
+ return np.round(np.nanmin(bottom_depths), 2)
78
+ except Exception as e:
79
+ print(f"Could not find depth: {e}")
80
+
81
+ def get_altitude(
82
+ self,
83
+ start_time: str = "2019-10-16T16:20:00",
84
+ end_time: str = "2019-10-16T16:50:00",
85
+ bbox_max: float = 0.0,
86
+ ):
87
+ """
88
+ Will need a lot of improvement but this will do for first pass. In the
89
+ future need to check for each point the differential and find the
90
+ minimum.
91
+ """
92
+ try:
93
+ depth_min = self.get_depth(start_time, end_time)
94
+ if np.isnan(depth_min):
95
+ return 0.0
96
+ return np.round(depth_min - bbox_max, 2)
97
+ except Exception as get_altitude_exception:
98
+ print(f"Problem getting altitude: {get_altitude_exception}")
99
+
100
+
101
+ # if __name__ == "__main__":
102
+ # astronomical_manager = AstronomicalManager()
103
+ # azimuth = astronomical_manager.get_solar_azimuth()
104
+ # print(azimuth)
@@ -0,0 +1,5 @@
1
+ from .geospatial_manager import GeospatialManager
2
+
3
+ __all__ = [
4
+ GeospatialManager,
5
+ ]
@@ -0,0 +1,143 @@
1
+ from datetime import datetime
2
+ from pathlib import Path
3
+
4
+ import geopandas as gpd
5
+ import numpy as np
6
+ import pooch
7
+ from dateutil import tz
8
+ from shapely import Point
9
+ from timezonefinder import TimezoneFinder
10
+
11
+ """
12
+ Gets the distance between a point and a coastline
13
+ https://www.kaggle.com/code/notcostheta/shortest-distance-to-a-coastline
14
+ https://www.naturalearthdata.com/downloads/50m-physical-vectors/50m-coastline/
15
+
16
+ Well known text map: https://wktmap.com/
17
+ Calculate distance map: https://www.calcmaps.com/map-distance/
18
+ """
19
+
20
+ HB1906_DATA = pooch.create(
21
+ path=pooch.os_cache("water-column-sonar-annotation"),
22
+ base_url="https://github.com/CI-CMG/water-column-sonar-annotation/releases/download/v26.1.0/",
23
+ retry_if_failed=1,
24
+ registry={
25
+ # "HB201906_BOTTOMS.zip": "sha256:20609581493ea3326c1084b6868e02aafbb6c0eae871d946f30b8b5f0e7ba059",
26
+ # "HB201906_EVR.zip": "sha256:ceed912a25301be8f1b8f91e134d0ca4cff717f52b6623a58677832fd60c2990",
27
+ #
28
+ # "ne_50m_coastline.shp": "sha256:797d675af9613f80b51ab6049fa32e589974d7a97c6497ca56772965f179ed26",
29
+ # "ne_50m_coastline.shx": "sha256:0ff1792f2d16b58246d074215edd9d12fa280880ecaad61a91b9382fee854065",
30
+ #
31
+ "ne_10m_coastline.shp": "sha256:459a4a97c09db19aadf5244026612de9d43748be27f83a360242b99f7fabb3c1",
32
+ "ne_10m_coastline.shx": "sha256:f873afee7f56779ce52253f740ec251c2f12244aea911dc40f0a85d75de8d5f2",
33
+ },
34
+ )
35
+
36
+
37
+ def fetch_raw_files():
38
+ HB1906_DATA.fetch(fname="ne_10m_coastline.shp", progressbar=True)
39
+ file_name = HB1906_DATA.fetch(fname="ne_10m_coastline.shx", progressbar=True)
40
+ return Path(file_name).parent
41
+
42
+
43
+ def data_path():
44
+ return {
45
+ "DATA_PATH": fetch_raw_files(),
46
+ }
47
+
48
+
49
+ class GeospatialManager:
50
+ #######################################################
51
+ def __init__(
52
+ self,
53
+ ):
54
+ self.DECIMAL_PRECISION = 6
55
+ self.crs = "EPSG:4326" # "EPSG:3857" # "EPSG:4326"
56
+
57
+ def check_distance_from_coastline(
58
+ self, # -30.410156 51.508742)
59
+ latitude: float = 51.508742, # 42.682435,
60
+ longitude: float = -30.410156, # -68.741455,
61
+ shapefile_path: str = data_path()["DATA_PATH"],
62
+ ) -> np.float32 | None:
63
+ """
64
+ # Note this takes about 14 seconds each, very slow!!!
65
+ """
66
+ try:
67
+ # requires the shape file too
68
+ geometry_one = gpd.read_file(f"{shapefile_path}/ne_10m_coastline.shp")
69
+ geometry_one = geometry_one.set_crs(self.crs)
70
+ geometry_two = Point([longitude, latitude])
71
+ gdf_p = gpd.GeoDataFrame(geometry=[geometry_two], crs=self.crs)
72
+ gdf_l = geometry_one
73
+ gdf_p = gdf_p.to_crs(gdf_p.estimate_utm_crs())
74
+ # print(gdf_p.to_string())
75
+ gdf_l = gdf_l.to_crs(gdf_p.crs)
76
+ # TODO: index 1399 has inf values, investigate
77
+ # RuntimeWarning: invalid value encountered in distance
78
+ # return lib.distance(a, b, **kwargs)
79
+ all_distances = [
80
+ gdf_p.geometry.distance(gdf_l.get_geometry(0)[i])[0]
81
+ for i in range(len(gdf_l.get_geometry(0)))
82
+ if gdf_l.get_geometry(0)[i].is_valid
83
+ ]
84
+ return np.round(np.min(all_distances), 0)
85
+ except Exception as e:
86
+ print(f"Could not process the distance: {e}")
87
+
88
+ @staticmethod
89
+ def get_local_time(
90
+ # self,
91
+ iso_time: str = "2026-01-26T20:35:00Z",
92
+ latitude: float = 51.508742,
93
+ longitude: float = -30.410156,
94
+ ) -> str:
95
+ # https://www.geeksforgeeks.org/python/get-time-zone-of-a-given-location-using-python/
96
+ obj = TimezoneFinder()
97
+ calculated_timezone = obj.timezone_at(lng=longitude, lat=latitude)
98
+ from_zone = tz.gettz("UTC")
99
+ to_zone = tz.gettz(calculated_timezone)
100
+ utc = datetime.fromisoformat(iso_time)
101
+ utc = utc.replace(tzinfo=from_zone)
102
+ local_time = utc.astimezone(to_zone)
103
+ return local_time.isoformat() # [:19]
104
+
105
+ def get_local_hour_of_day(
106
+ self,
107
+ iso_time: str = "2026-01-26T20:35:00Z",
108
+ latitude: float = 51.508742,
109
+ longitude: float = -30.410156,
110
+ ) -> int:
111
+ obj = TimezoneFinder()
112
+ calculated_timezone = obj.timezone_at(lng=longitude, lat=latitude)
113
+ from_zone = tz.gettz("UTC")
114
+ to_zone = tz.gettz(calculated_timezone)
115
+ utc = datetime.fromisoformat(iso_time)
116
+ utc = utc.replace(tzinfo=from_zone)
117
+ local_time = utc.astimezone(to_zone)
118
+ return local_time.hour
119
+
120
+ def get_month_of_year(
121
+ self,
122
+ iso_time: str = "2026-01-26T20:35:00Z",
123
+ latitude: float = 51.508742,
124
+ longitude: float = -30.410156,
125
+ ):
126
+ local_time = self.get_local_time(
127
+ iso_time=iso_time,
128
+ latitude=latitude,
129
+ longitude=longitude,
130
+ )
131
+ return int(local_time[5:7])
132
+
133
+
134
+ #
135
+ # if __name__ == "__main__":
136
+ # geospatial_manager = GeospatialManager()
137
+ # # x = geospatial_manager.check_distance_from_coastline()
138
+ # x = geospatial_manager.get_local_time(
139
+ # iso_time="2026-01-26T20:35:00Z",
140
+ # latitude=51.508742,
141
+ # longitude=-30.410156,
142
+ # )
143
+ # print(x)
@@ -0,0 +1,9 @@
1
+ from .echoview_record_manager import EchoviewRecordManager
2
+ from .graph_record_manager import GraphRecordManager
3
+ from .parquet_record_manager import ParquetRecordManager
4
+
5
+ __all__ = [
6
+ GraphRecordManager,
7
+ EchoviewRecordManager,
8
+ ParquetRecordManager,
9
+ ]