Skip to content

Commit 6152497

Browse files
committed
Fix readers and convertOG1 based on review
1 parent a6f8255 commit 6152497

2 files changed

Lines changed: 72 additions & 23 deletions

File tree

seagliderOG1/convertOG1.py

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818
_log = logging.getLogger(__name__)
1919

2020

21-
def convert_to_OG1(list_of_datasets: list[xr.Dataset] | xr.Dataset, contrib_to_append: dict[str, str] | None = None,) -> tuple[xr.Dataset, list[str]]:
21+
def convert_to_OG1(
22+
list_of_datasets: list[xr.Dataset] | xr.Dataset,
23+
contrib_to_append: dict[str, str] | None = None,
24+
) -> tuple[xr.Dataset, list[str]]:
2225
"""Convert Seaglider basestation datasets to OG1 format.
2326
Processes a list of xarray datasets or a single xarray dataset, converts them to OG1 format,
2427
concatenates the datasets, sorts by time, and applies attributes. Main conversion function that
@@ -64,6 +67,8 @@ def convert_to_OG1(list_of_datasets: list[xr.Dataset] | xr.Dataset, contrib_to_a
6467

6568
ds_og1 = xr.concat(processed_datasets, dim="N_MEASUREMENTS")
6669
ds_og1 = ds_og1.sortby("TIME")
70+
# Change format of time into datetime64[ns] to avoid problems with attributes and writing to netcdf
71+
ds_og1["TIME"] = (ds_og1["TIME"].astype("float64") * 1e9).astype("datetime64[ns]")
6772

6873
# Apply attributes
6974
ordered_attributes = update_dataset_attributes(
@@ -104,12 +109,18 @@ def convert_to_OG1(list_of_datasets: list[xr.Dataset] | xr.Dataset, contrib_to_a
104109
ds_og1["TRAJECTORY"].attrs["long_name"] = "trajectory name"
105110
ds_og1["TRAJECTORY"].attrs["cf_role"] = "trajectory_id"
106111

107-
ds_og1["DEPLOYMENT_LATITUDE"] = xr.DataArray(ds_og1.LATITUDE.values[~np.isnan(ds_og1.LATITUDE)][0],
108-
attrs = {"long_name": "latitude of deployment"})
109-
ds_og1["DEPLOYMENT_LONGITUDE"] = xr.DataArray(ds_og1.LONGITUDE.values[~np.isnan(ds_og1.LONGITUDE)][0],
110-
attrs = {"long_name": "longitude of deployment"})
111-
ds_og1["DEPLOYMENT_TIME"] = xr.DataArray(ds_og1.TIME.values[~np.isnan(ds_og1.TIME)][0],
112-
attrs = {"long_name": "time of deployment"})
112+
ds_og1["DEPLOYMENT_LATITUDE"] = xr.DataArray(
113+
ds_og1.LATITUDE.values[~np.isnan(ds_og1.LATITUDE)][0],
114+
attrs={"long_name": "latitude of deployment"},
115+
)
116+
ds_og1["DEPLOYMENT_LONGITUDE"] = xr.DataArray(
117+
ds_og1.LONGITUDE.values[~np.isnan(ds_og1.LONGITUDE)][0],
118+
attrs={"long_name": "longitude of deployment"},
119+
)
120+
ds_og1["DEPLOYMENT_TIME"] = xr.DataArray(
121+
ds_og1.TIME.values[~np.isnan(ds_og1.TIME)][0],
122+
attrs={"long_name": "time of deployment"},
123+
)
113124

114125
# Remove attributes from TIME_GPS
115126
if "TIME_GPS" in ds_og1.variables:
@@ -274,8 +285,10 @@ def process_dataset(ds1_base: xr.Dataset, firstrun: bool = False) -> tuple[
274285
ds_new = tools.add_sensor_to_dataset(ds_new, ds_sensor, ds_sgcal, firstrun)
275286

276287
# To avoid problems, reset the dtype of TIME_GPS
277-
ds_new['TIME_GPS'] = ds_new['TIME_GPS'].astype('datetime64[ns]')
278-
vars_to_remove = vocabularies.vars_to_remove #+ ["TIME_GPS"]
288+
ds_new["TIME_GPS"] = (ds_new["TIME_GPS"].astype("float64") * 1e9).astype(
289+
"datetime64[ns]"
290+
)
291+
vars_to_remove = vocabularies.vars_to_remove # + ["TIME_GPS"]
279292
vars_present_to_remove = [var for var in vars_to_remove if var in ds_new.variables]
280293

281294
# Drop them
@@ -288,6 +301,7 @@ def process_dataset(ds1_base: xr.Dataset, firstrun: bool = False) -> tuple[
288301
attr_warnings = ""
289302
return ds_new, attr_warnings, ds_sgcal, ds_other, ds_log
290303

304+
291305
def standardise_OG10(
292306
ds: xr.Dataset,
293307
firstrun: bool = False,
@@ -367,7 +381,10 @@ def standardise_OG10(
367381
)
368382
### Only log a warning for variables that aren't in the vocabularies and aren't in the list of variables to keep or remove
369383
### Removed varaiables will be printed in the log as being removed, so no need to log a warning for them here.
370-
if orig_varname not in (*vocabularies.vars_as_is, *vocabularies.vars_to_remove):
384+
if orig_varname not in (
385+
*vocabularies.vars_as_is,
386+
*vocabularies.vars_to_remove,
387+
):
371388
vars_not_in_vocab.append(orig_varname)
372389

373390
if firstrun and vars_not_in_vocab:
@@ -505,7 +522,9 @@ def add_gps_info_to_dataset(ds: xr.Dataset, gps_ds: xr.Dataset) -> xr.Dataset:
505522
##-----------------------------------------------------------------------------------------
506523
## Editing attributes
507524
##-----------------------------------------------------------------------------------------
508-
def update_dataset_attributes(ds: xr.Dataset, contrib_to_append: dict[str, str] | None) -> dict[str, str]:
525+
def update_dataset_attributes(
526+
ds: xr.Dataset, contrib_to_append: dict[str, str] | None
527+
) -> dict[str, str]:
509528
"""Update the attributes of the dataset based on the provided attribute input.
510529
511530
Processes contributor information, time attributes, and applies OG1
@@ -570,7 +589,9 @@ def update_dataset_attributes(ds: xr.Dataset, contrib_to_append: dict[str, str]
570589
return ordered_attributes
571590

572591

573-
def get_contributors(ds: xr.Dataset, values_to_append: dict[str, str] | None = None) -> dict[str, str]:
592+
def get_contributors(
593+
ds: xr.Dataset, values_to_append: dict[str, str] | None = None
594+
) -> dict[str, str]:
574595
"""Extract and format contributor information for OG1 attributes.
575596
576597
Processes creator and contributor information from dataset attributes,
@@ -589,6 +610,7 @@ def get_contributors(ds: xr.Dataset, values_to_append: dict[str, str] | None = N
589610
Dictionary with formatted contributor attribute strings.
590611
591612
"""
613+
592614
# Function to create or append to a list
593615
def create_or_append_list(existing_list, new_item):
594616
if new_item not in existing_list:
@@ -808,7 +830,9 @@ def get_time_attributes(ds: xr.Dataset) -> dict[str, str]:
808830
return time_attrs
809831

810832

811-
def extract_attr_to_keep(ds1: xr.Dataset, attr_as_is: list[str] = vocabularies.global_attrs["attr_as_is"]) -> dict[str, str]:
833+
def extract_attr_to_keep(
834+
ds1: xr.Dataset, attr_as_is: list[str] = vocabularies.global_attrs["attr_as_is"]
835+
) -> dict[str, str]:
812836
"""Extract attributes to retain unchanged.
813837
814838
Parameters
@@ -835,7 +859,8 @@ def extract_attr_to_keep(ds1: xr.Dataset, attr_as_is: list[str] = vocabularies.g
835859

836860

837861
def extract_attr_to_rename(
838-
ds1: xr.Dataset, attr_to_rename: dict[str, str] = vocabularies.global_attrs["attr_to_rename"]
862+
ds1: xr.Dataset,
863+
attr_to_rename: dict[str, str] = vocabularies.global_attrs["attr_to_rename"],
839864
) -> dict[str, str]:
840865
"""Extract and rename attributes according to OG1 vocabulary.
841866
@@ -861,7 +886,12 @@ def extract_attr_to_rename(
861886
return renamed_attrs
862887

863888

864-
def process_and_save_data(input_location: str, save: bool = False, output_dir: str = ".", run_quietly: bool = True) -> xr.Dataset:
889+
def process_and_save_data(
890+
input_location: str,
891+
save: bool = False,
892+
output_dir: str = ".",
893+
run_quietly: bool = True,
894+
) -> xr.Dataset:
865895
"""Process and save data from the specified input location.
866896
867897
This function loads and concatenates datasets from the server, converts them to OG1 format,

seagliderOG1/readers.py

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import re
44
import sys
55

6-
import pooch
76
import requests
87
import xarray as xr
98
from bs4 import BeautifulSoup
@@ -54,10 +53,13 @@
5453
},
5554
)"""
5655

56+
5757
# Instead of loading from the server, we will load from a local directory for testing and development purposes.
5858
# The local directory will contain the same files as the server, but we will not use pooch to manage them.
5959
# Instead, we will just read them directly from the filesystem.
60-
def load_sample_dataset(file_path: str = str(parent_dir / "data/demo_sg005/p0050001_20080606.nc")) -> xr.Dataset:
60+
def load_sample_dataset(
61+
file_path: str = str(parent_dir / "data/demo_sg005/p0050001_20080606.nc"),
62+
) -> xr.Dataset:
6163
"""Download sample datasets for use with seagliderOG1.
6264
6365
Parameters
@@ -83,6 +85,7 @@ def load_sample_dataset(file_path: str = str(parent_dir / "data/demo_sg005/p0050
8385
msg = f"Requested sample dataset {file_path} not known. Available datasets are: {os.listdir(str(parent_dir / 'data/demo_sg005'))}"
8486
raise KeyError(msg)
8587

88+
8689
def _validate_filename(filename: str) -> bool:
8790
"""Validate if filename matches expected Seaglider basestation patterns.
8891
@@ -158,7 +161,11 @@ def _glider_sn_from_filename(filename: str) -> int:
158161
return int(filename[1:4])
159162

160163

161-
def filter_files_by_profile(file_list: list[str], start_profile: int | None = None, end_profile: int | None = None) -> list[str]:
164+
def filter_files_by_profile(
165+
file_list: list[str],
166+
start_profile: int | None = None,
167+
end_profile: int | None = None,
168+
) -> list[str]:
162169
"""Filter files by profile/dive number range.
163170
164171
Filters Seaglider basestation files based on profile number range.
@@ -233,7 +240,9 @@ def load_first_basestation_file(source: str) -> xr.Dataset:
233240
return datasets[0]
234241

235242

236-
def load_basestation_files(source: str, start_profile: int | None = None, end_profile: int | None = None) -> list[xr.Dataset]:
243+
def load_basestation_files(
244+
source: str, start_profile: int | None = None, end_profile: int | None = None
245+
) -> list[xr.Dataset]:
237246
"""Load multiple Seaglider basestation files with optional profile filtering.
238247
239248
Main function for loading Seaglider data from either online repositories
@@ -264,15 +273,17 @@ def load_basestation_files(source: str, start_profile: int | None = None, end_pr
264273

265274
### Include a tqdm progress bar
266275
for file in tqdm(filtered_files, desc="Loading datasets", unit="file"):
267-
ds = xr.open_dataset(os.path.join(source, file), decode_timedelta=False)
276+
ds = xr.open_dataset(os.path.join(source, file))
268277

269278
datasets.append(ds)
270279

271280
return datasets
272281

273282

274283
def list_files(
275-
source: str, registry_loc: str = "seagliderOG1", registry_name: str = "seaglider_registry.txt"
284+
source: str,
285+
registry_loc: str = "seagliderOG1",
286+
registry_name: str = "seaglider_registry.txt",
276287
) -> list[str]:
277288
"""List NetCDF files from a source (URL or local directory).
278289
@@ -322,6 +333,7 @@ def list_files(
322333

323334
return file_list
324335

336+
325337
def scan_and_repair_files(
326338
source: str,
327339
start_profile: int | None = None,
@@ -380,14 +392,21 @@ def _repair_folder(source: str | pathlib.Path) -> pathlib.Path:
380392
return repair_dir
381393

382394

383-
def _backup_path(path: str | pathlib.Path, repair_dir: str | pathlib.Path) -> pathlib.Path:
395+
def _backup_path(
396+
path: str | pathlib.Path, repair_dir: str | pathlib.Path
397+
) -> pathlib.Path:
384398
"""Return a backup filename stored in metadata_fixes/."""
385399
path = pathlib.Path(path)
386400
repair_dir = pathlib.Path(repair_dir)
387401
return repair_dir / f"{path.stem}_original{path.suffix}"
388402

389403

390-
def log_repair(log_path: str | pathlib.Path, filename: str, fixed_vars: list[str], error: Exception | str):
404+
def log_repair(
405+
log_path: str | pathlib.Path,
406+
filename: str,
407+
fixed_vars: list[str],
408+
error: Exception | str,
409+
):
391410
"""Append one repair entry to the log file."""
392411
with open(log_path, "a") as f:
393412
f.write(f"{datetime.now().isoformat()} | {filename}\n")

0 commit comments

Comments
 (0)