water-column-sonar-annotation 26.1.8__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.
- water_column_sonar_annotation-26.1.8/LICENSE +21 -0
- water_column_sonar_annotation-26.1.8/PKG-INFO +109 -0
- water_column_sonar_annotation-26.1.8/README.md +72 -0
- water_column_sonar_annotation-26.1.8/pyproject.toml +68 -0
- water_column_sonar_annotation-26.1.8/setup.cfg +4 -0
- water_column_sonar_annotation-26.1.8/tests/__init__.py +0 -0
- water_column_sonar_annotation-26.1.8/tests/astronomical/__init__.py +0 -0
- water_column_sonar_annotation-26.1.8/tests/astronomical/test_astronomical_manager.py +148 -0
- water_column_sonar_annotation-26.1.8/tests/conftest.py +60 -0
- water_column_sonar_annotation-26.1.8/tests/cruise/__init__.py +0 -0
- water_column_sonar_annotation-26.1.8/tests/cruise/test_cruise_manager.py +80 -0
- water_column_sonar_annotation-26.1.8/tests/geospatial/__init__.py +0 -0
- water_column_sonar_annotation-26.1.8/tests/geospatial/test_geospatial_manager.py +86 -0
- water_column_sonar_annotation-26.1.8/tests/record/test_echoview_record_manager.py +160 -0
- water_column_sonar_annotation-26.1.8/water_column_sonar_annotation/__init__.py +5 -0
- water_column_sonar_annotation-26.1.8/water_column_sonar_annotation/astronomical/__init__.py +5 -0
- water_column_sonar_annotation-26.1.8/water_column_sonar_annotation/astronomical/astronomical_manager.py +82 -0
- water_column_sonar_annotation-26.1.8/water_column_sonar_annotation/cruise/__init__.py +5 -0
- water_column_sonar_annotation-26.1.8/water_column_sonar_annotation/cruise/cruise_manager.py +104 -0
- water_column_sonar_annotation-26.1.8/water_column_sonar_annotation/geospatial/__init__.py +5 -0
- water_column_sonar_annotation-26.1.8/water_column_sonar_annotation/geospatial/geospatial_manager.py +143 -0
- water_column_sonar_annotation-26.1.8/water_column_sonar_annotation/record/__init__.py +9 -0
- water_column_sonar_annotation-26.1.8/water_column_sonar_annotation/record/echoview_record_manager.py +426 -0
- water_column_sonar_annotation-26.1.8/water_column_sonar_annotation/record/graph_record_manager.py +82 -0
- water_column_sonar_annotation-26.1.8/water_column_sonar_annotation/record/parquet_record_manager.py +83 -0
- water_column_sonar_annotation-26.1.8/water_column_sonar_annotation/shape/__init__.py +5 -0
- water_column_sonar_annotation-26.1.8/water_column_sonar_annotation/shape/shape_manager.py +29 -0
- water_column_sonar_annotation-26.1.8/water_column_sonar_annotation.egg-info/PKG-INFO +109 -0
- water_column_sonar_annotation-26.1.8/water_column_sonar_annotation.egg-info/SOURCES.txt +30 -0
- water_column_sonar_annotation-26.1.8/water_column_sonar_annotation.egg-info/dependency_links.txt +1 -0
- water_column_sonar_annotation-26.1.8/water_column_sonar_annotation.egg-info/requires.txt +20 -0
- water_column_sonar_annotation-26.1.8/water_column_sonar_annotation.egg-info/top_level.txt +3 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Cooperative Institutes, Coastal and Marine Geophysics
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: water-column-sonar-annotation
|
|
3
|
+
Version: 26.1.8
|
|
4
|
+
Summary: Processing Tool for Working with Water Column Sonar Annotations
|
|
5
|
+
Author-email: Rudy Klucik <rudy.klucik@noaa.gov>
|
|
6
|
+
Maintainer-email: Rudy Klucik <rudy.klucik@noaa.gov>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://echo.fish
|
|
9
|
+
Project-URL: Repository, https://github.com/CI-CMG/water-column-sonar-annotation
|
|
10
|
+
Project-URL: Documentation, https://github.com/CI-CMG/water-column-sonar-annotation
|
|
11
|
+
Project-URL: Issues, https://github.com/CI-CMG/water-column-sonar-annotation/issues
|
|
12
|
+
Keywords: ocean,sonar,water column
|
|
13
|
+
Requires-Python: >=3.12
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Requires-Dist: build
|
|
17
|
+
Requires-Dist: fiona
|
|
18
|
+
Requires-Dist: folium
|
|
19
|
+
Requires-Dist: geopandas
|
|
20
|
+
Requires-Dist: netcdf4
|
|
21
|
+
Requires-Dist: numpy
|
|
22
|
+
Requires-Dist: pandas
|
|
23
|
+
Requires-Dist: pandas-stubs
|
|
24
|
+
Requires-Dist: pooch
|
|
25
|
+
Requires-Dist: pvlib
|
|
26
|
+
Requires-Dist: pyarrow
|
|
27
|
+
Requires-Dist: pyogrio
|
|
28
|
+
Requires-Dist: python-dotenv
|
|
29
|
+
Requires-Dist: s3fs
|
|
30
|
+
Requires-Dist: scipy
|
|
31
|
+
Requires-Dist: shapely
|
|
32
|
+
Requires-Dist: setuptools
|
|
33
|
+
Requires-Dist: timezonefinder
|
|
34
|
+
Requires-Dist: xarray
|
|
35
|
+
Requires-Dist: zarr
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
# Water Column Sonar Annotation
|
|
39
|
+
|
|
40
|
+
Tool for converting EVR files to annotated regions of interest in parquet format
|
|
41
|
+
|
|
42
|
+

|
|
43
|
+
  
|
|
44
|
+
|
|
45
|
+
# Setting up the Python Environment
|
|
46
|
+
|
|
47
|
+
> Python 3.12.12
|
|
48
|
+
|
|
49
|
+
# Installing Dependencies
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
source .venv/bin/activate
|
|
53
|
+
|
|
54
|
+
uv pip install --upgrade pip
|
|
55
|
+
|
|
56
|
+
uv pip install -r pyproject.toml --all-extras
|
|
57
|
+
|
|
58
|
+
uv run pre-commit install
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
# Pytest
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
uv run pytest tests -W ignore::DeprecationWarning
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
or
|
|
68
|
+
> uv run pytest tests/cruise --cov=water_column_sonar_annotation --cov-report term-missing
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
uv run pre-commit install --allow-missing-config
|
|
72
|
+
# or
|
|
73
|
+
uv run pre-commit install
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
# Test Coverage
|
|
77
|
+
|
|
78
|
+
TODO
|
|
79
|
+
|
|
80
|
+
# Tag a Release
|
|
81
|
+
|
|
82
|
+
Step 1 --> increment the semantic version in the zarr_manager.py "metadata" & the "pyproject.toml"
|
|
83
|
+
|
|
84
|
+
```commandline
|
|
85
|
+
git tag -a v26.1.0 -m "Releasing v26.1.0"
|
|
86
|
+
git push origin --tags
|
|
87
|
+
gh release create v26.1.0
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
# To Publish To PROD
|
|
91
|
+
|
|
92
|
+
```commandline
|
|
93
|
+
uv build --no-sources
|
|
94
|
+
uv publish
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
# UV Debugging
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
uv lock --check
|
|
101
|
+
uv lock
|
|
102
|
+
uv sync --extra dev
|
|
103
|
+
#uv run pytest tests
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Annotation format
|
|
107
|
+
|
|
108
|
+
- https://roboflow.com/formats/coco-json
|
|
109
|
+
- https://www.v7labs.com/blog/coco-dataset-guide
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Water Column Sonar Annotation
|
|
2
|
+
|
|
3
|
+
Tool for converting EVR files to annotated regions of interest in parquet format
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
  
|
|
7
|
+
|
|
8
|
+
# Setting up the Python Environment
|
|
9
|
+
|
|
10
|
+
> Python 3.12.12
|
|
11
|
+
|
|
12
|
+
# Installing Dependencies
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
source .venv/bin/activate
|
|
16
|
+
|
|
17
|
+
uv pip install --upgrade pip
|
|
18
|
+
|
|
19
|
+
uv pip install -r pyproject.toml --all-extras
|
|
20
|
+
|
|
21
|
+
uv run pre-commit install
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
# Pytest
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
uv run pytest tests -W ignore::DeprecationWarning
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
or
|
|
31
|
+
> uv run pytest tests/cruise --cov=water_column_sonar_annotation --cov-report term-missing
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
uv run pre-commit install --allow-missing-config
|
|
35
|
+
# or
|
|
36
|
+
uv run pre-commit install
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
# Test Coverage
|
|
40
|
+
|
|
41
|
+
TODO
|
|
42
|
+
|
|
43
|
+
# Tag a Release
|
|
44
|
+
|
|
45
|
+
Step 1 --> increment the semantic version in the zarr_manager.py "metadata" & the "pyproject.toml"
|
|
46
|
+
|
|
47
|
+
```commandline
|
|
48
|
+
git tag -a v26.1.0 -m "Releasing v26.1.0"
|
|
49
|
+
git push origin --tags
|
|
50
|
+
gh release create v26.1.0
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
# To Publish To PROD
|
|
54
|
+
|
|
55
|
+
```commandline
|
|
56
|
+
uv build --no-sources
|
|
57
|
+
uv publish
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
# UV Debugging
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
uv lock --check
|
|
64
|
+
uv lock
|
|
65
|
+
uv sync --extra dev
|
|
66
|
+
#uv run pytest tests
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Annotation format
|
|
70
|
+
|
|
71
|
+
- https://roboflow.com/formats/coco-json
|
|
72
|
+
- https://www.v7labs.com/blog/coco-dataset-guide
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "water-column-sonar-annotation"
|
|
3
|
+
version = "26.1.8"
|
|
4
|
+
authors = [
|
|
5
|
+
{ name = "Rudy Klucik", email = "rudy.klucik@noaa.gov" },
|
|
6
|
+
]
|
|
7
|
+
maintainers = [
|
|
8
|
+
{ name = "Rudy Klucik", email = "rudy.klucik@noaa.gov" },
|
|
9
|
+
]
|
|
10
|
+
description = "Processing Tool for Working with Water Column Sonar Annotations"
|
|
11
|
+
keywords = ["ocean", "sonar", "water column"]
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.12"
|
|
14
|
+
license = "MIT"
|
|
15
|
+
license-files = ["LICENSE"]
|
|
16
|
+
|
|
17
|
+
# uv sync --all-groups --upgrade
|
|
18
|
+
dependencies = [
|
|
19
|
+
"build",
|
|
20
|
+
"fiona",
|
|
21
|
+
"folium",
|
|
22
|
+
"geopandas",
|
|
23
|
+
"netcdf4",
|
|
24
|
+
"numpy",
|
|
25
|
+
"pandas",
|
|
26
|
+
"pandas-stubs",
|
|
27
|
+
"pooch",
|
|
28
|
+
"pvlib",
|
|
29
|
+
"pyarrow",
|
|
30
|
+
"pyogrio",
|
|
31
|
+
"python-dotenv",
|
|
32
|
+
"s3fs", # problem here, uv finds the wrong one
|
|
33
|
+
"scipy",
|
|
34
|
+
"shapely",
|
|
35
|
+
"setuptools",
|
|
36
|
+
"timezonefinder",
|
|
37
|
+
# "wheel",
|
|
38
|
+
"xarray",
|
|
39
|
+
"zarr",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
# uv sync --all-groups --upgrade
|
|
43
|
+
[dependency-groups]
|
|
44
|
+
dev = [
|
|
45
|
+
"bandit[toml]",
|
|
46
|
+
"build",
|
|
47
|
+
"pooch",
|
|
48
|
+
"pre-commit",
|
|
49
|
+
"pytest",
|
|
50
|
+
"pytest-cov",
|
|
51
|
+
"ruff",
|
|
52
|
+
"tqdm",
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
[project.urls]
|
|
56
|
+
Homepage = "https://echo.fish"
|
|
57
|
+
Repository = "https://github.com/CI-CMG/water-column-sonar-annotation"
|
|
58
|
+
Documentation = "https://github.com/CI-CMG/water-column-sonar-annotation"
|
|
59
|
+
Issues = "https://github.com/CI-CMG/water-column-sonar-annotation/issues"
|
|
60
|
+
|
|
61
|
+
[tool.bandit]
|
|
62
|
+
exclude_dirs = ["tests"]
|
|
63
|
+
|
|
64
|
+
[tool.pre-commit-hooks.bandit]
|
|
65
|
+
exclude = ["*/tests/*"]
|
|
66
|
+
|
|
67
|
+
[tool.setuptools.packages.find]
|
|
68
|
+
exclude = ["data*"]
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from water_column_sonar_annotation.astronomical import AstronomicalManager
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
#######################################################
|
|
7
|
+
def setup_module():
|
|
8
|
+
print("setup")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def teardown_module():
|
|
12
|
+
print("teardown")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_get_solar_azimuth():
|
|
16
|
+
astronomical_manager = AstronomicalManager()
|
|
17
|
+
# https://www.suncalc.org/#/39.9812,-105.2495,13/2026.01.26/11:52/1/3
|
|
18
|
+
azimuth_noon = astronomical_manager.get_solar_azimuth(
|
|
19
|
+
iso_time="2026-01-26T19:00:00Z", # noon
|
|
20
|
+
latitude=39.9674884, # Boulder
|
|
21
|
+
longitude=-105.2532602,
|
|
22
|
+
)
|
|
23
|
+
assert np.isclose(azimuth_noon, 31.38) # 15)
|
|
24
|
+
|
|
25
|
+
azimuth_sunset = astronomical_manager.get_solar_azimuth(
|
|
26
|
+
iso_time="2026-01-26T00:00:00Z", # sunset
|
|
27
|
+
latitude=39.9674884,
|
|
28
|
+
longitude=-105.2532602,
|
|
29
|
+
)
|
|
30
|
+
assert np.isclose(azimuth_sunset, 1.25) # 27)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_get_solar_azimuth_boulder_2pm():
|
|
34
|
+
# 2026-01-29 @2pm is UTC: "2026-01-29T21:02:23Z"
|
|
35
|
+
astronomical_manager = AstronomicalManager()
|
|
36
|
+
# https://www.suncalc.org/#/39.9812,-105.2495,13/2026.01.26/11:52/1/3
|
|
37
|
+
azimuth_noon = astronomical_manager.get_solar_azimuth(
|
|
38
|
+
iso_time="2026-01-29T21:02:23Z", # 2pm
|
|
39
|
+
latitude=39.9674884, # Boulder
|
|
40
|
+
longitude=-105.2532602,
|
|
41
|
+
)
|
|
42
|
+
assert np.isclose(azimuth_noon, 27.01)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
### PHASE OF DAY ###
|
|
46
|
+
# { 'dawn': 1, 'day': 2, 'dusk': 3, 'night': 4 }
|
|
47
|
+
def test_phase_of_day_at_noon():
|
|
48
|
+
astronomical_manager = AstronomicalManager()
|
|
49
|
+
phase = astronomical_manager.phase_of_day(
|
|
50
|
+
iso_time="2026-01-27T19:00:00Z", # noon
|
|
51
|
+
latitude=39.9674884, # Boulder
|
|
52
|
+
longitude=-105.2532602,
|
|
53
|
+
)
|
|
54
|
+
assert phase == 2
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_phase_of_day_at_midnight():
|
|
58
|
+
astronomical_manager = AstronomicalManager()
|
|
59
|
+
phase = astronomical_manager.phase_of_day(
|
|
60
|
+
iso_time="2026-01-28T07:00:00Z",
|
|
61
|
+
latitude=39.9674884, # Boulder
|
|
62
|
+
longitude=-105.2532602,
|
|
63
|
+
)
|
|
64
|
+
assert phase == 4
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_phase_of_day_before_sunset():
|
|
68
|
+
astronomical_manager = AstronomicalManager()
|
|
69
|
+
phase = astronomical_manager.phase_of_day(
|
|
70
|
+
# sunset is at 5:13pm on jan 27th, per https://psl.noaa.gov/boulder/boulder.sunset.html
|
|
71
|
+
iso_time="2026-01-28T00:09:00Z", # sunset @ 5:13pm, nautical sunset @6:16pm
|
|
72
|
+
latitude=39.9674884, # Boulder
|
|
73
|
+
longitude=-105.2532602,
|
|
74
|
+
)
|
|
75
|
+
assert phase == 2 # day
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_phase_of_day_after_sunset():
|
|
79
|
+
astronomical_manager = AstronomicalManager()
|
|
80
|
+
phase = astronomical_manager.phase_of_day(
|
|
81
|
+
# sunset is at 5:13pm on jan 27th, per https://psl.noaa.gov/boulder/boulder.sunset.html
|
|
82
|
+
iso_time="2026-01-28T00:10:00Z", # sunset @ 5:13pm, nautical sunset @6:16pm
|
|
83
|
+
latitude=39.9674884, # Boulder
|
|
84
|
+
longitude=-105.2532602,
|
|
85
|
+
)
|
|
86
|
+
assert phase == 3 # dusk
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_phase_of_day_before_nautical_sunset():
|
|
90
|
+
astronomical_manager = AstronomicalManager()
|
|
91
|
+
# an hour'ish later is nautical sunset
|
|
92
|
+
phase_before_nautical_sunset = astronomical_manager.phase_of_day(
|
|
93
|
+
iso_time="2026-01-28T01:15:00Z", # sunset @5:13pm, nautical sunset @6:16pm
|
|
94
|
+
latitude=39.9674884, # Boulder
|
|
95
|
+
longitude=-105.2532602,
|
|
96
|
+
)
|
|
97
|
+
assert phase_before_nautical_sunset == 3 # dusk
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_phase_of_day_after_nautical_sunset():
|
|
101
|
+
astronomical_manager = AstronomicalManager()
|
|
102
|
+
phase_after_nautical_sunset = astronomical_manager.phase_of_day(
|
|
103
|
+
iso_time="2026-01-28T01:17:00Z", # sunset @5:13pm, nautical sunset @6:16pm
|
|
104
|
+
latitude=39.9674884, # Boulder
|
|
105
|
+
longitude=-105.2532602,
|
|
106
|
+
)
|
|
107
|
+
assert phase_after_nautical_sunset == 4 # night
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_phase_of_day_before_sunrise():
|
|
111
|
+
astronomical_manager = AstronomicalManager()
|
|
112
|
+
phase_at_sunrise = astronomical_manager.phase_of_day(
|
|
113
|
+
iso_time="2026-01-27T14:13:00Z", # sunrise @7:13am, nautical sunrise @6:12am
|
|
114
|
+
latitude=39.9674884, # Boulder
|
|
115
|
+
longitude=-105.2532602,
|
|
116
|
+
)
|
|
117
|
+
assert phase_at_sunrise == 1 # dusk
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def test_phase_of_day_after_sunrise():
|
|
121
|
+
astronomical_manager = AstronomicalManager()
|
|
122
|
+
phase_at_sunrise = astronomical_manager.phase_of_day(
|
|
123
|
+
iso_time="2026-01-27T14:20:00Z", # sunrise @7:13am, nautical sunrise @6:12am
|
|
124
|
+
latitude=39.9674884, # Boulder
|
|
125
|
+
longitude=-105.2532602,
|
|
126
|
+
)
|
|
127
|
+
assert phase_at_sunrise == 2 # day
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_phase_of_day_before_nautical_sunrise():
|
|
131
|
+
astronomical_manager = AstronomicalManager()
|
|
132
|
+
# about an hour before is nautical sunrise
|
|
133
|
+
phase_before_nautical_sunrise = astronomical_manager.phase_of_day(
|
|
134
|
+
iso_time="2026-01-27T13:12:00Z", # sunrise @7:13am, nautical sunrise @6:12am
|
|
135
|
+
latitude=39.9674884, # Boulder
|
|
136
|
+
longitude=-105.2532602,
|
|
137
|
+
)
|
|
138
|
+
assert phase_before_nautical_sunrise == 4 # night
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def test_phase_of_day_after_nautical_sunrise():
|
|
142
|
+
astronomical_manager = AstronomicalManager()
|
|
143
|
+
phase = astronomical_manager.phase_of_day(
|
|
144
|
+
iso_time="2026-01-27T14:13:00Z", # sunrise @7:13am, nautical sunrise @6:12am
|
|
145
|
+
latitude=39.9674884, # Boulder
|
|
146
|
+
longitude=-105.2532602,
|
|
147
|
+
)
|
|
148
|
+
assert phase == 1
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import pooch
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
HERE = Path(__file__).parent.absolute()
|
|
7
|
+
TEST_DATA_FOLDER = HERE / "test_resources"
|
|
8
|
+
|
|
9
|
+
HB1906_DATA = pooch.create(
|
|
10
|
+
path=pooch.os_cache("water-column-sonar-annotation"),
|
|
11
|
+
base_url="https://github.com/CI-CMG/water-column-sonar-annotation/releases/download/v26.1.0/",
|
|
12
|
+
retry_if_failed=1,
|
|
13
|
+
registry={
|
|
14
|
+
"HB201906_BOTTOMS.zip": "sha256:20609581493ea3326c1084b6868e02aafbb6c0eae871d946f30b8b5f0e7ba059",
|
|
15
|
+
"HB201906_EVR.zip": "sha256:ceed912a25301be8f1b8f91e134d0ca4cff717f52b6623a58677832fd60c2990",
|
|
16
|
+
#
|
|
17
|
+
# "ne_50m_coastline.shp": "sha256:797d675af9613f80b51ab6049fa32e589974d7a97c6497ca56772965f179ed26",
|
|
18
|
+
# "ne_50m_coastline.shx": "sha256:0ff1792f2d16b58246d074215edd9d12fa280880ecaad61a91b9382fee854065",
|
|
19
|
+
#
|
|
20
|
+
"ne_10m_coastline.shp": "sha256:459a4a97c09db19aadf5244026612de9d43748be27f83a360242b99f7fabb3c1",
|
|
21
|
+
"ne_10m_coastline.shx": "sha256:f873afee7f56779ce52253f740ec251c2f12244aea911dc40f0a85d75de8d5f2",
|
|
22
|
+
},
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def fetch_raw_files():
|
|
27
|
+
HB1906_DATA.fetch(fname="HB201906_BOTTOMS.zip", progressbar=True)
|
|
28
|
+
HB1906_DATA.fetch(fname="HB201906_EVR.zip", progressbar=True)
|
|
29
|
+
|
|
30
|
+
# HB1906_DATA.fetch(fname="ne_50m_coastline.shp", progressbar=True)
|
|
31
|
+
# HB1906_DATA.fetch(fname="ne_50m_coastline.shx", progressbar=True)
|
|
32
|
+
|
|
33
|
+
HB1906_DATA.fetch(fname="ne_10m_coastline.shp", progressbar=True)
|
|
34
|
+
HB1906_DATA.fetch(fname="ne_10m_coastline.shx", progressbar=True)
|
|
35
|
+
|
|
36
|
+
file_name = HB1906_DATA.fetch(fname="HB201906_EVR.zip", progressbar=True)
|
|
37
|
+
|
|
38
|
+
"""
|
|
39
|
+
water-column-sonar-annotation user$ ls /Users/user/Library/Caches/water-column-sonar-annotation
|
|
40
|
+
HB201906_BOTTOMS.zip HB201906_EVR.zip ne_10m_coastline.shp ne_10m_coastline.shx
|
|
41
|
+
"""
|
|
42
|
+
return Path(file_name).parent
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@pytest.fixture(scope="session")
|
|
46
|
+
def test_path():
|
|
47
|
+
return {
|
|
48
|
+
"DATA_TEST_PATH": fetch_raw_files(),
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# """
|
|
53
|
+
# Folder locations in mac and windows:
|
|
54
|
+
#
|
|
55
|
+
# Windows
|
|
56
|
+
# C:\Users\<user>\AppData\Local\echopype\Cache\2024.12.23.10.10
|
|
57
|
+
#
|
|
58
|
+
# MacOS
|
|
59
|
+
# /Users//Library/Caches/echopype/2024.12.23.10.10
|
|
60
|
+
# """
|
|
File without changes
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from water_column_sonar_annotation.cruise import CruiseManager
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
#######################################################
|
|
8
|
+
def setup_module():
|
|
9
|
+
print("setup")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def teardown_module():
|
|
13
|
+
print("teardown")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@pytest.fixture
|
|
17
|
+
def process_cruise_path(test_path):
|
|
18
|
+
return test_path["DATA_TEST_PATH"]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
#######################################################
|
|
22
|
+
# def test_get_cruise_bottom_nan(process_cruise_path, tmp_path):
|
|
23
|
+
# cruise_manager = CruiseManager()
|
|
24
|
+
# cruise = cruise_manager.get_cruise()
|
|
25
|
+
# # count of non-nan values np.count_nonzero(~np.isnan(cruise.bottom.values)) / cruise.Sv.shape[1]
|
|
26
|
+
# assert len(cruise.Sv.shape) == 3
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_get_cruise(process_cruise_path, tmp_path):
|
|
30
|
+
cruise_manager = CruiseManager()
|
|
31
|
+
cruise = cruise_manager.get_cruise()
|
|
32
|
+
assert len(cruise.Sv.shape) == 3
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_get_coordinates():
|
|
36
|
+
"""This only gets the depth over the interval, need to calculate the 'altitude'"""
|
|
37
|
+
cruise_manager = CruiseManager()
|
|
38
|
+
lat, lon = cruise_manager.get_coordinates(
|
|
39
|
+
start_time="2019-10-16T16:20:00",
|
|
40
|
+
end_time="2019-10-16T16:30:00",
|
|
41
|
+
)
|
|
42
|
+
assert np.isclose(lat, 41.48177)
|
|
43
|
+
assert np.isclose(lon, -68.50478)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_get_depth():
|
|
47
|
+
"""This only gets the depth over the interval, need to calculate the 'altitude'"""
|
|
48
|
+
cruise_manager = CruiseManager()
|
|
49
|
+
depth_value = cruise_manager.get_depth(
|
|
50
|
+
start_time="2019-10-16T16:20:00",
|
|
51
|
+
end_time="2019-10-16T16:50:00",
|
|
52
|
+
)
|
|
53
|
+
assert np.isclose(depth_value, 96.36) # 96.356674
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_get_altitude():
|
|
57
|
+
"""This gets the distance from EVR to the bottom"""
|
|
58
|
+
cruise_manager = CruiseManager()
|
|
59
|
+
altitude_value = cruise_manager.get_altitude(
|
|
60
|
+
start_time="2019-10-16T16:20:00",
|
|
61
|
+
end_time="2019-10-16T16:50:00",
|
|
62
|
+
bbox_max=80.0,
|
|
63
|
+
)
|
|
64
|
+
# bottom is at 96.356674
|
|
65
|
+
# setting the bbox at 80.
|
|
66
|
+
assert np.isclose(altitude_value, 16.36) # 96.356674
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_get_altitude_nan_bottom():
|
|
70
|
+
"""This gets the distance from EVR to the bottom when there are nan values"""
|
|
71
|
+
cruise_manager = CruiseManager()
|
|
72
|
+
altitude_value = cruise_manager.get_altitude(
|
|
73
|
+
start_time="2019-09-26T10:02:39.03",
|
|
74
|
+
end_time="2019-09-26T10:02:40.00",
|
|
75
|
+
bbox_max=10.0,
|
|
76
|
+
)
|
|
77
|
+
assert np.isclose(altitude_value, 0.0)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# get_gps
|
|
File without changes
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from water_column_sonar_annotation.geospatial import GeospatialManager
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
#######################################################
|
|
8
|
+
def setup_module():
|
|
9
|
+
print("setup")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def teardown_module():
|
|
13
|
+
print("teardown")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@pytest.fixture
|
|
17
|
+
def process_check_distance_from_coastline(test_path):
|
|
18
|
+
return test_path["DATA_TEST_PATH"]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
#######################################################
|
|
22
|
+
def test_check_distance_from_coastline(process_check_distance_from_coastline, tmp_path):
|
|
23
|
+
geospatial_manager = GeospatialManager()
|
|
24
|
+
# Point in middle of atlantic https://wktmap.com/?ab28cbae
|
|
25
|
+
distance = geospatial_manager.check_distance_from_coastline(
|
|
26
|
+
latitude=51.508742,
|
|
27
|
+
longitude=-30.410156,
|
|
28
|
+
shapefile_path=process_check_distance_from_coastline,
|
|
29
|
+
)
|
|
30
|
+
# assert np.isclose(distance, 1_236_212.37356) # 1,200 km
|
|
31
|
+
assert np.isclose(distance, 1_233_910.720702243)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_check_distance_from_coastline_woods_hole(
|
|
35
|
+
process_check_distance_from_coastline, tmp_path
|
|
36
|
+
):
|
|
37
|
+
geospatial_manager = GeospatialManager()
|
|
38
|
+
# Point in middle of woods hole vineyard sound: https://wktmap.com/?9b405aa9
|
|
39
|
+
distance = geospatial_manager.check_distance_from_coastline(
|
|
40
|
+
latitude=41.494692,
|
|
41
|
+
longitude=-70.647926,
|
|
42
|
+
shapefile_path=process_check_distance_from_coastline,
|
|
43
|
+
)
|
|
44
|
+
# The sound is 5 km across
|
|
45
|
+
# assert np.isclose(distance, 4_457.0347) # 4.5 km --> should be 2.5 km?
|
|
46
|
+
assert np.isclose(distance, 3_093) # 3_093.3015 km is close enough
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_get_local_time():
|
|
50
|
+
geospatial_manager = GeospatialManager()
|
|
51
|
+
local_time = geospatial_manager.get_local_time(
|
|
52
|
+
iso_time="2026-01-26T20:35:00Z",
|
|
53
|
+
latitude=51.508742,
|
|
54
|
+
longitude=-30.410156,
|
|
55
|
+
)
|
|
56
|
+
assert local_time == "2026-01-26T18:35:00-02:00"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_get_local_hour_of_day():
|
|
60
|
+
geospatial_manager = GeospatialManager()
|
|
61
|
+
local_hour_of_day = geospatial_manager.get_local_hour_of_day(
|
|
62
|
+
iso_time="2026-01-26T20:35:00Z",
|
|
63
|
+
latitude=51.508742,
|
|
64
|
+
longitude=-30.410156,
|
|
65
|
+
)
|
|
66
|
+
assert local_hour_of_day == 18
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_get_month_of_year_january():
|
|
70
|
+
geospatial_manager = GeospatialManager()
|
|
71
|
+
month_of_year = geospatial_manager.get_month_of_year(
|
|
72
|
+
iso_time="2026-01-26T20:35:00Z",
|
|
73
|
+
latitude=51.508742,
|
|
74
|
+
longitude=-30.410156,
|
|
75
|
+
)
|
|
76
|
+
assert month_of_year == 1
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_get_month_of_year_july():
|
|
80
|
+
geospatial_manager = GeospatialManager()
|
|
81
|
+
month_of_year = geospatial_manager.get_month_of_year(
|
|
82
|
+
iso_time="2026-07-13T12:00:00Z",
|
|
83
|
+
latitude=51.508742,
|
|
84
|
+
longitude=-30.410156,
|
|
85
|
+
)
|
|
86
|
+
assert month_of_year == 7
|