1
1
import datetime
2
2
import pathlib
3
+ from pathlib import Path
3
4
from typing import Optional , Union
4
5
5
6
import numpy as np
8
9
from ..calibrate .ek80_complex import get_filter_coeff
9
10
from ..echodata import EchoData
10
11
from ..echodata .simrad import retrieve_correct_beam_group
11
- from ..utils .io import validate_source_ds_da
12
+ from ..utils .io import get_file_format , open_source
12
13
from ..utils .prov import add_processing_level
13
- from .split_beam_angle import add_angle_to_ds , get_angle_complex_samples , get_angle_power_samples
14
+ from .split_beam_angle import get_angle_complex_samples , get_angle_power_samples
14
15
15
16
POSITION_VARIABLES = ["latitude" , "longitude" ]
16
17
17
18
18
- def swap_dims_channel_frequency (ds : xr .Dataset ) -> xr .Dataset :
19
+ def swap_dims_channel_frequency (ds : Union [ xr .Dataset , str , pathlib . Path ] ) -> xr .Dataset :
19
20
"""
20
21
Use frequency_nominal in place of channel to be dataset dimension and coorindate.
21
22
@@ -24,8 +25,9 @@ def swap_dims_channel_frequency(ds: xr.Dataset) -> xr.Dataset:
24
25
25
26
Parameters
26
27
----------
27
- ds : xr.Dataset
28
- Dataset for which the dimension will be swapped
28
+ ds : xr.Dataset or str or pathlib.Path
29
+ Dataset or path to a file containing the Dataset
30
+ for which the dimension will be swapped
29
31
30
32
Returns
31
33
-------
@@ -35,6 +37,7 @@ def swap_dims_channel_frequency(ds: xr.Dataset) -> xr.Dataset:
35
37
-----
36
38
This operation is only possible when there are no duplicated frequencies present in the file.
37
39
"""
40
+ ds = open_source (ds , "dataset" , {})
38
41
# Only possible if no duplicated frequencies
39
42
if np .unique (ds ["frequency_nominal" ]).size == ds ["frequency_nominal" ].size :
40
43
return (
@@ -50,7 +53,7 @@ def swap_dims_channel_frequency(ds: xr.Dataset) -> xr.Dataset:
50
53
51
54
52
55
def add_depth (
53
- ds : xr .Dataset ,
56
+ ds : Union [ xr .Dataset , str , pathlib . Path ] ,
54
57
depth_offset : float = 0 ,
55
58
tilt : float = 0 ,
56
59
downward : bool = True ,
@@ -64,8 +67,9 @@ def add_depth(
64
67
65
68
Parameters
66
69
----------
67
- ds : xr.Dataset
68
- Source Sv dataset to which a depth variable will be added.
70
+ ds : xr.Dataset or str or pathlib.Path
71
+ Source Sv dataset or path to a file containing the Source Sv dataset
72
+ to which a depth variable will be added.
69
73
Must contain `echo_range`.
70
74
depth_offset : float
71
75
Offset along the vertical (depth) dimension to account for actual transducer
@@ -114,6 +118,7 @@ def add_depth(
114
118
# else:
115
119
# tilt = 0
116
120
121
+ ds = open_source (ds , "dataset" , {})
117
122
# Multiplication factor depending on if transducers are pointing downward
118
123
mult = 1 if downward else - 1
119
124
@@ -132,7 +137,11 @@ def add_depth(
132
137
133
138
134
139
@add_processing_level ("L2A" )
135
- def add_location (ds : xr .Dataset , echodata : EchoData = None , nmea_sentence : Optional [str ] = None ):
140
+ def add_location (
141
+ ds : Union [xr .Dataset , str , pathlib .Path ],
142
+ echodata : Optional [Union [EchoData , str , pathlib .Path ]],
143
+ nmea_sentence : Optional [str ] = None ,
144
+ ):
136
145
"""
137
146
Add geographical location (latitude/longitude) to the Sv dataset.
138
147
@@ -142,10 +151,12 @@ def add_location(ds: xr.Dataset, echodata: EchoData = None, nmea_sentence: Optio
142
151
143
152
Parameters
144
153
----------
145
- ds : xr.Dataset
146
- An Sv or MVBS dataset for which the geographical locations will be added to
147
- echodata
148
- An `EchoData` object holding the raw data
154
+ ds : xr.Dataset or str or pathlib.Path
155
+ An Sv or MVBS dataset or path to a file containing the Sv or MVBS
156
+ dataset for which the geographical locations will be added to
157
+ echodata : EchoData or str or pathlib.Path
158
+ An ``EchoData`` object or path to a file containing the ``EchoData``
159
+ object holding the raw data
149
160
nmea_sentence
150
161
NMEA sentence to select a subset of location data (optional)
151
162
@@ -174,6 +185,9 @@ def sel_interp(var, time_dim_name):
174
185
# Values may be nan if there are ping_time values outside the time_dim_name range
175
186
return position_var .interp (** {time_dim_name : ds ["ping_time" ]})
176
187
188
+ ds = open_source (ds , "dataset" , {})
189
+ echodata = open_source (echodata , "echodata" , {})
190
+
177
191
if "longitude" not in echodata ["Platform" ] or echodata ["Platform" ]["longitude" ].isnull ().all ():
178
192
raise ValueError ("Coordinate variables not present or all nan" )
179
193
@@ -198,12 +212,12 @@ def sel_interp(var, time_dim_name):
198
212
199
213
def add_splitbeam_angle (
200
214
source_Sv : Union [xr .Dataset , str , pathlib .Path ],
201
- echodata : EchoData ,
215
+ echodata : Union [ EchoData , str , pathlib . Path ] ,
202
216
waveform_mode : str ,
203
217
encode_mode : str ,
204
218
pulse_compression : bool = False ,
205
219
storage_options : dict = {},
206
- return_dataset : bool = True ,
220
+ to_disk : bool = True ,
207
221
) -> xr .Dataset :
208
222
"""
209
223
Add split-beam (alongship/athwartship) angles into the Sv dataset.
@@ -218,8 +232,9 @@ def add_splitbeam_angle(
218
232
source_Sv: xr.Dataset or str or pathlib.Path
219
233
The Sv Dataset or path to a file containing the Sv Dataset,
220
234
to which the split-beam angles will be added
221
- echodata: EchoData
222
- An ``EchoData`` object holding the raw data
235
+ echodata: EchoData or str or pathlib.Path
236
+ An ``EchoData`` object or path to a file containing the ``EchoData``
237
+ object holding the raw data
223
238
waveform_mode : {"CW", "BB"}
224
239
Type of transmit waveform
225
240
@@ -240,19 +255,20 @@ def add_splitbeam_angle(
240
255
storage_options: dict, default={}
241
256
Any additional parameters for the storage backend, corresponding to the
242
257
path provided for ``source_Sv``
243
- return_dataset : bool, default=True
244
- If ``True ``, ``source_Sv `` with split-beam angles added will be returned.
245
- ``return_dataset=False `` is useful when ``source_Sv`` is a path and
258
+ to_disk : bool, default=True
259
+ If ``False ``, ``to_disk `` with split-beam angles added will be returned.
260
+ ``to_disk=True `` is useful when ``source_Sv`` is a path and
246
261
users only want to write the split-beam angle data to this path.
247
262
248
263
Returns
249
264
-------
250
265
xr.Dataset or None
251
- If ``return_dataset =False``, nothing will be returned.
252
- If ``return_dataset =True``, either the input dataset ``source_Sv``
266
+ If ``to_disk =False``, nothing will be returned.
267
+ If ``to_disk =True``, either the input dataset ``source_Sv``
253
268
or a lazy-loaded Dataset (from the path ``source_Sv``)
254
269
with split-beam angles added will be returned.
255
270
271
+
256
272
Raises
257
273
------
258
274
ValueError
@@ -279,6 +295,19 @@ def add_splitbeam_angle(
279
295
`echodata`` will be identical. If this is not the case, only angle data corresponding
280
296
to channels existing in ``source_Sv`` will be added.
281
297
"""
298
+ # ensure that when source_Sv is a Dataset then to_disk should be False
299
+ if not isinstance (source_Sv , (str , Path )) and to_disk :
300
+ raise ValueError (
301
+ "The input source_Sv must be a path when to_disk=True, "
302
+ "so that the split-beam angles can be written to disk!"
303
+ )
304
+
305
+ # obtain the file format of source_Sv if it is a path
306
+ if isinstance (source_Sv , (str , Path )):
307
+ source_Sv_type = get_file_format (source_Sv )
308
+
309
+ source_Sv = open_source (source_Sv , "dataset" , storage_options )
310
+ echodata = open_source (echodata , "echodata" , storage_options )
282
311
283
312
# ensure that echodata was produced by EK60 or EK80-like sensors
284
313
if echodata .sonar_model not in ["EK60" , "ES70" , "EK80" , "ES80" , "EA640" ]:
@@ -287,22 +316,6 @@ def add_splitbeam_angle(
287
316
"transducers, split-beam angles cannot be added to source_Sv!"
288
317
)
289
318
290
- # validate the source_Sv type or path (if it is provided)
291
- source_Sv , file_type = validate_source_ds_da (source_Sv , storage_options )
292
-
293
- # initialize source_Sv_path
294
- source_Sv_path = None
295
-
296
- if isinstance (source_Sv , str ):
297
- # store source_Sv path so we can use it to write to later
298
- source_Sv_path = source_Sv
299
-
300
- # TODO: In the future we can improve this by obtaining the variable names, channels,
301
- # and dimension lengths directly from source_Sv using zarr or netcdf4. This would
302
- # prevent the unnecessary loading in of the coordinates, which the below statement does.
303
- # open up Dataset using source_Sv path
304
- source_Sv = xr .open_dataset (source_Sv , engine = file_type , chunks = {}, ** storage_options )
305
-
306
319
# raise not implemented error if source_Sv corresponds to MVBS
307
320
if source_Sv .attrs ["processing_function" ] == "commongrid.compute_MVBS" :
308
321
raise NotImplementedError ("Adding split-beam data to MVBS has not been implemented!" )
@@ -364,9 +377,18 @@ def add_splitbeam_angle(
364
377
theta , phi = get_angle_complex_samples (ds_beam , angle_params )
365
378
366
379
# add theta and phi to source_Sv input
367
- source_Sv = add_angle_to_ds (
368
- theta , phi , source_Sv , return_dataset , source_Sv_path , file_type , storage_options
369
- )
380
+ theta .attrs ["long_name" ] = "split-beam alongship angle"
381
+ phi .attrs ["long_name" ] = "split-beam athwartship angle"
382
+
383
+ # add the split-beam angles to the provided Dataset
384
+ source_Sv ["angle_alongship" ] = theta
385
+ source_Sv ["angle_athwartship" ] = phi
386
+ if to_disk :
387
+ if source_Sv_type == "netcdf4" :
388
+ source_Sv .to_netcdf (mode = "a" , ** storage_options )
389
+ else :
390
+ source_Sv .to_zarr (mode = "a" , ** storage_options )
391
+ source_Sv = open_source (source_Sv , "dataset" , storage_options )
370
392
371
393
# Add history attribute
372
394
history_attr = (
0 commit comments