Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add functionality for saving to .evl and .evr files #199

Merged
merged 9 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/source/Lines_functionality.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -956,8 +956,9 @@
"outputs": [],
"source": [
"# Use the built in mask function\n",
"bottom_mask_da, bottom_points = lines.mask(\n",
"bottom_mask_da, bottom_points = lines.bottom_mask(\n",
" ds_Sv[\"Sv\"].isel(channel=1).drop_vars(\"channel\").compute(),\n",
" operation=\"above_below\",\n",
" method=\"slinear\",\n",
" limit_area=None,\n",
" limit_direction=\"both\"\n",
Expand Down Expand Up @@ -1752,7 +1753,7 @@
],
"source": [
"# Test ds_Sv\n",
"lines.mask(\n",
"lines.bottom_mask(\n",
" ds_Sv,\n",
" method=\"slinear\",\n",
" limit_area=None,\n",
Expand Down
6 changes: 3 additions & 3 deletions docs/source/Regions2D_functionality.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -1085,7 +1085,7 @@
"outputs": [],
"source": [
"# Use the built in mask function to create a mask\n",
"region_mask_ds, region_points = regions2d.mask(\n",
"region_mask_ds, region_points = regions2d.region_mask(\n",
" ds_Sv[\"Sv\"].isel(channel=1).drop_vars(\"channel\").compute(),\n",
" region_class=\"Trawl\",\n",
" #collapse_to_2d= False, mask defaults to this so we can comment it out\n",
Expand Down Expand Up @@ -1752,7 +1752,7 @@
"outputs": [],
"source": [
"# Set collapse_to_2d = True\n",
"region_mask_2d_ds, region_points_2d = regions2d.mask(\n",
"region_mask_2d_ds, region_points_2d = regions2d.region_mask(\n",
" ds_Sv[\"Sv\"].isel(channel=1).drop_vars(\"channel\").compute(),\n",
" region_class=\"Trawl\",\n",
" collapse_to_2d=True,\n",
Expand Down Expand Up @@ -2246,7 +2246,7 @@
],
"source": [
"# Test ds_Sv\n",
"regions2d.mask(\n",
"regions2d.region_mask(\n",
" ds_Sv,\n",
" region_class=\"Trawl\",\n",
")"
Expand Down
52 changes: 48 additions & 4 deletions echoregions/lines/lines.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
from pathlib import Path
from typing import Dict, Iterable, List, Union

import matplotlib
Expand Down Expand Up @@ -69,22 +70,65 @@ def replace_nan_depth(self, inplace: bool = False) -> Union[DataFrame, None]:
if not inplace:
return regions

def to_csv(self, save_path: bool = None, mode="w", **kwaargs) -> None:
def to_csv(self, save_path: Union[str, Path], mode: str = "w", **kwaargs) -> None:
"""Save a Dataframe to a .csv file

Parameters
----------
save_path : str
save_path : Union[str, Path]
Path to save the CSV file to.
mode : str
Write mode arg for to_csv.
Write mode arg for to_csv. Defaults to 'w'.
"""
# Check if the save directory is safe
save_path = validate_path(save_path=save_path, input_file=self.input_file, ext=".csv")
# Reorder columns and export to csv
self.data.to_csv(save_path, mode=mode, **kwaargs)
self.output_file.append(save_path)

def to_evl(self, save_path: Union[str, Path], mode: str = "w") -> None:
"""Save a Dataframe to a .evl file

Parameters
----------
save_path : Union[str, Path]
Path to save the `evl` file to.
mode : str
Write mode arg for IO open. Defaults to 'w'.
"""
# Check if the save directory is safe
save_path = validate_path(save_path=save_path, input_file=self.input_file, ext=".evl")

# Grab header information
echoview_version = (
f"EVBD 3 {self.data.iloc[0]['echoview_version']}"
if len(self.data) > 0
else "EVBD 3 12.0.341.42620"
)
number_of_regions = str(len(self.data))

with open(save_path, mode=mode) as f:
# Write header to `.evl`
f.write(echoview_version + "\n")
f.write(number_of_regions + "\n")

# Write each bottom point to `.evl`
for _, row in self.data.iterrows():
f.write(
str(row["time"].strftime("%Y%m%d"))
+ " "
+ str(row["time"].strftime("%H%M%S%f"))[:-2]
+ " "
+ str(row["depth"])
+ " "
+ str(row["status"])
+ "\n"
)
f.close()

# Append save_path
self.output_file.append(save_path)

def to_json(self, save_path: str = None, pretty: bool = True, **kwargs) -> None:
# TODO Currently only EVL files can be exported to JSON
"""Convert supported formats to .json file.
Expand Down Expand Up @@ -174,7 +218,7 @@ def _filter_bottom(self, bottom, start_date, end_date, operation):
filtered_bottom = filtered_bottom.set_index("time")
return filtered_bottom

def mask(self, da_Sv: DataArray, operation: str = "regionmask", **kwargs):
def bottom_mask(self, da_Sv: DataArray, operation: str = "regionmask", **kwargs):
"""
Subsets a bottom dataset to the range of an Sv dataset. Create a mask of
the same shape as data found in the Echogram object:
Expand Down
116 changes: 112 additions & 4 deletions echoregions/regions2d/regions2d.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,15 +112,15 @@ def __iter__(self) -> Iterable:
def __getitem__(self, val: int) -> Series:
return self.data.iloc[val]

def to_csv(self, save_path: bool = None, mode="w", **kwaargs) -> None:
def to_csv(self, save_path: Union[str, Path], mode: str = "w", **kwaargs) -> None:
"""Save a Dataframe to a .csv file

Parameters
----------
save_path : str
save_path : Union[str, Path]
Path to save the CSV file to.
mode : str
Write mode arg for to_csv.
Write mode arg for to_csv. Defaults to 'w'.
"""
# Check if the save directory is safe
save_path = validate_path(save_path=save_path, input_file=self.input_file, ext=".csv")
Expand All @@ -131,6 +131,114 @@ def to_csv(self, save_path: bool = None, mode="w", **kwaargs) -> None:
# Append save_path
self.output_file.append(save_path)

def _write_region(self, row, save_path):
"""Helper function to write individual regions to `.evr` file."""
# Set region variables
datetimes = pd.to_datetime(row["time"])
depths = row["depth"]
region_structure_version = row["region_structure_version"]
point_count = str(len(datetimes))
selected = "0"
region_creation_type = row["region_creation_type"]
dummy = "-1"
bounding_rectangle_calculated = "1"
region_notes = row["region_notes"]
number_of_lines_of_notes = len(region_notes)
number_of_lines_of_detection_settings = "0"
region_class = row["region_class"]
region_type = row["region_type"]
region_name = row["region_name"]
region_id = row["region_id"]

# Append to existing `.evr` file
with open(save_path, "a") as f:
# Calculate bounding box
left_x = (
str(min(datetimes).strftime("%Y%m%d"))
+ " "
+ str(min(datetimes).strftime("%H%M%S%f"))
)
top_y = str(min(depths))
right_x = (
str(max(datetimes).strftime("%Y%m%d"))
+ " "
+ str(max(datetimes).strftime("%H%M%S%f"))
)
bottom_y = str(max(depths))
bbox = left_x + " " + top_y + " " + right_x + " " + bottom_y

# Write first line
f.write(
"\n"
+ region_structure_version
+ " "
+ point_count
+ " "
+ str(region_id)
+ " "
+ selected
+ " "
+ region_creation_type
+ " "
+ dummy
+ " "
+ bounding_rectangle_calculated
+ " "
+ bbox
+ "\n"
)
f.write(str(number_of_lines_of_notes) + "\n")
if number_of_lines_of_notes > 0:
for region_note in region_notes:
f.write(region_note + "\n")
f.write(number_of_lines_of_detection_settings + "\n")
f.write(region_class + "\n")

# Write points
for datetime, depth in zip(datetimes, depths):
date = str(datetime.strftime("%Y%m%d"))
time = str(datetime.strftime("%H%M%S%f"))[:-2]
depth = str(depth)
point = date + " " + time + " " + depth
f.write("%s " % point)
f.write(region_type + "\n")
f.write(region_name + "\n")
f.close()

def to_evr(self, save_path: Union[str, Path], mode: str = "w") -> None:
"""Save a Dataframe to a .evr file

Parameters
----------
save_path : Union[str, Path]
Path to save the `evr` file to.
mode : str
Write mode arg for IO open. Defaults to 'w'.
"""
# Check if the save directory is safe
save_path = validate_path(save_path=save_path, input_file=self.input_file, ext=".evr")

# Grab header information
echoview_version = (
f"EVRG 7 {self.data.iloc[0]['echoview_version']}"
if len(self.data) > 0
else "EVRG 7 12.0.341.42620"
)
number_of_regions = str(len(self.data))

# Write header to `.evr`
with open(save_path, mode=mode) as f:
f.write(echoview_version + "\n")
f.write(number_of_regions + "\n")
f.close()

# Write each region to `.evr`
for _, row in self.data.iterrows():
self._write_region(row, save_path=save_path)

# Append save_path
self.output_file.append(save_path)

def to_json(self, save_path: str = None) -> None:
# TODO: Implement this function
"""Convert EVR to a JSON file. Currently Deprecated.
Expand Down Expand Up @@ -439,7 +547,7 @@ def plot(
for _, row in region.iterrows():
plt.plot(row["time"], row["depth"], **kwargs)

def mask(
def region_mask(
self,
da_Sv: DataArray,
region_id: Union[float, int, str, List[Union[float, int, str]]] = None,
Expand Down
6 changes: 3 additions & 3 deletions echoregions/test_data/transect.evr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
EVRG 7 13.0.378.44817
19
20

13 4 1 0 6 -1 1 20190702 0350546295 -9999.99 20190702 0810094255 9999.99
1
Expand Down Expand Up @@ -135,14 +135,14 @@ Log
20190702 2024556635 -9999.9900000000 20190702 2024556635 9999.9900000000 20190702 2024570135 9999.9900000000 20190702 2024570135 -9999.9900000000 2
COM

13 4 18 0 4 -1 1 20190702 1840518097 9.2447583998 20190702 1952215310 758.9732173069
13 4 18 0 4 -1 1 20190702 1840518097 200 20190702 1900215310 300 20190702
0
0
Trawl
20190702 1840518097 200 20190702 1840518097 300 20190702 1900215310 300 20190702 1900215310 200 0
AWT20

13 4 19 0 4 -1 1 20190702 1840518097 9.2447583998 20190702 1952215310 758.9732173069
13 4 19 0 4 -1 1 20190702 1922215310 220 20190702 1952215310 350
0
0
Trawl
Expand Down
Loading