rashdf 0.2.0__tar.gz → 0.2.2__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: rashdf
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Read data from HEC-RAS HDF files.
5
5
  Project-URL: repository, https://github.com/fema-ffrd/rashdf
6
6
  Classifier: Development Status :: 4 - Beta
@@ -20,16 +20,26 @@ Provides-Extra: dev
20
20
  Requires-Dist: pre-commit; extra == "dev"
21
21
  Requires-Dist: ruff; extra == "dev"
22
22
  Requires-Dist: pytest; extra == "dev"
23
+ Requires-Dist: pytest-cov; extra == "dev"
24
+ Provides-Extra: docs
25
+ Requires-Dist: sphinx; extra == "docs"
26
+ Requires-Dist: numpydoc; extra == "docs"
27
+ Requires-Dist: sphinx_rtd_theme; extra == "docs"
23
28
 
24
29
  # rashdf
25
30
  [![CI](https://github.com/fema-ffrd/rashdf/actions/workflows/continuous-integration.yml/badge.svg?branch=main)](https://github.com/fema-ffrd/rashdf/actions/workflows/continuous-integration.yml)
26
31
  [![Release](https://github.com/fema-ffrd/rashdf/actions/workflows/release.yml/badge.svg)](https://github.com/fema-ffrd/rashdf/actions/workflows/release.yml)
27
32
  [![PyPI version](https://badge.fury.io/py/rashdf.svg)](https://badge.fury.io/py/rashdf)
33
+ [![codecov](https://codecov.io/gh/fema-ffrd/rashdf/graph/badge.svg?token=CTIIONEHV1)](https://codecov.io/gh/fema-ffrd/rashdf)
34
+ [![Documentation Status](https://readthedocs.org/projects/rashdf/badge/?version=latest)](https://rashdf.readthedocs.io/en/latest/?badge=latest)
28
35
 
29
36
  Read data from [HEC-RAS](https://www.hec.usace.army.mil/software/hec-ras/) [HDF](https://github.com/HDFGroup/hdf5) files.
30
37
 
31
38
  *Pronunciation: `raz·aitch·dee·eff`*
32
39
 
40
+ ## Documentation
41
+ [rashdf on ReadTheDocs](http://rashdf.readthedocs.io/)
42
+
33
43
  ## Install
34
44
  ```bash
35
45
  $ pip install rashdf
@@ -120,9 +130,6 @@ Example: write structures GeoJSON to `stdout`:
120
130
  $ rashdf structures Potomac.p01.hdf
121
131
  ```
122
132
 
123
- ## Documentation
124
- Coming soon.
125
-
126
133
  ## Developer Setup
127
134
  Create a virtual environment in the project directory:
128
135
  ```
@@ -2,11 +2,16 @@
2
2
  [![CI](https://github.com/fema-ffrd/rashdf/actions/workflows/continuous-integration.yml/badge.svg?branch=main)](https://github.com/fema-ffrd/rashdf/actions/workflows/continuous-integration.yml)
3
3
  [![Release](https://github.com/fema-ffrd/rashdf/actions/workflows/release.yml/badge.svg)](https://github.com/fema-ffrd/rashdf/actions/workflows/release.yml)
4
4
  [![PyPI version](https://badge.fury.io/py/rashdf.svg)](https://badge.fury.io/py/rashdf)
5
+ [![codecov](https://codecov.io/gh/fema-ffrd/rashdf/graph/badge.svg?token=CTIIONEHV1)](https://codecov.io/gh/fema-ffrd/rashdf)
6
+ [![Documentation Status](https://readthedocs.org/projects/rashdf/badge/?version=latest)](https://rashdf.readthedocs.io/en/latest/?badge=latest)
5
7
 
6
8
  Read data from [HEC-RAS](https://www.hec.usace.army.mil/software/hec-ras/) [HDF](https://github.com/HDFGroup/hdf5) files.
7
9
 
8
10
  *Pronunciation: `raz·aitch·dee·eff`*
9
11
 
12
+ ## Documentation
13
+ [rashdf on ReadTheDocs](http://rashdf.readthedocs.io/)
14
+
10
15
  ## Install
11
16
  ```bash
12
17
  $ pip install rashdf
@@ -97,9 +102,6 @@ Example: write structures GeoJSON to `stdout`:
97
102
  $ rashdf structures Potomac.p01.hdf
98
103
  ```
99
104
 
100
- ## Documentation
101
- Coming soon.
102
-
103
105
  ## Developer Setup
104
106
  Create a virtual environment in the project directory:
105
107
  ```
@@ -12,11 +12,12 @@ classifiers = [
12
12
  "Programming Language :: Python :: 3.11",
13
13
  "Programming Language :: Python :: 3.12",
14
14
  ]
15
- version = "0.2.0"
15
+ version = "0.2.2"
16
16
  dependencies = ["h5py", "geopandas", "pyarrow"]
17
17
 
18
18
  [project.optional-dependencies]
19
- dev = ["pre-commit", "ruff", "pytest"]
19
+ dev = ["pre-commit", "ruff", "pytest", "pytest-cov"]
20
+ docs = ["sphinx", "numpydoc", "sphinx_rtd_theme"]
20
21
 
21
22
  [project.urls]
22
23
  repository = "https://github.com/fema-ffrd/rashdf"
@@ -28,10 +29,12 @@ rashdf = "cli:main"
28
29
  pythonpath = "src"
29
30
  testpaths = "tests"
30
31
 
31
- # TODO: linting for docstrings.
32
- # https://github.com/fema-ffrd/rashdf/issues/15
33
- # [tool.ruff.lint]
34
- # select = ["D"]
32
+ [tool.ruff.lint]
33
+ select = ["D"]
35
34
 
36
- # [tool.ruff.lint.pydocstyle]
37
- # convention = "numpy"
35
+ [tool.ruff.lint.pydocstyle]
36
+ convention = "numpy"
37
+
38
+ [tool.ruff.lint.per-file-ignores]
39
+ "tests/**" = ["D"]
40
+ "docs/**" = ["D"]
@@ -1,3 +1,5 @@
1
+ """rashdf CLI."""
2
+
1
3
  from rashdf import RasGeomHdf
2
4
  from rashdf.utils import df_datetimes_to_str
3
5
 
@@ -60,6 +62,7 @@ def fiona_supported_drivers() -> List[str]:
60
62
 
61
63
 
62
64
  def parse_args(args: str) -> argparse.Namespace:
65
+ """Parse command-line arguments."""
63
66
  parser = argparse.ArgumentParser(description="Extract data from HEC-RAS HDF files.")
64
67
  parser.add_argument(
65
68
  "--fiona-drivers",
@@ -100,6 +103,7 @@ def parse_args(args: str) -> argparse.Namespace:
100
103
 
101
104
 
102
105
  def export(args: argparse.Namespace) -> Optional[str]:
106
+ """Act on parsed arguments to extract data from HEC-RAS HDF files."""
103
107
  if args.fiona_drivers:
104
108
  for driver in fiona_supported_drivers():
105
109
  print(driver)
@@ -147,6 +151,7 @@ def export(args: argparse.Namespace) -> Optional[str]:
147
151
 
148
152
 
149
153
  def main():
154
+ """Entry point for the rashdf CLI."""
150
155
  args = parse_args(sys.argv[1:])
151
156
  export(args)
152
157
 
@@ -1,3 +1,5 @@
1
+ """rashdf package."""
2
+
1
3
  from .base import RasHdf
2
4
  from .geom import RasGeomHdf
3
5
  from .plan import RasPlanHdf
@@ -1,3 +1,5 @@
1
+ """Base class for reading HEC-RAS HDF files."""
2
+
1
3
  import h5py
2
4
  from .utils import hdf5_attrs_to_dict
3
5
  from typing import Dict
@@ -68,7 +70,7 @@ class RasHdf(h5py.File):
68
70
  return {}
69
71
 
70
72
  def get_root_attrs(self):
71
- """Returns attributes at root level of HEC-RAS HDF file.
73
+ """Return attributes at root level of HEC-RAS HDF file.
72
74
 
73
75
  Returns
74
76
  -------
@@ -1,3 +1,5 @@
1
+ """HEC-RAS Geometry HDF class."""
2
+
1
3
  from .base import RasHdf
2
4
  from .utils import (
3
5
  convert_ras_hdf_string,
@@ -16,27 +18,37 @@ from shapely import (
16
18
  LineString,
17
19
  MultiLineString,
18
20
  MultiPolygon,
19
- polygonize,
21
+ polygonize_full,
20
22
  )
21
23
 
22
- from typing import List, Optional
24
+ from typing import Dict, List, Optional
23
25
 
24
26
 
25
27
  class RasGeomHdf(RasHdf):
28
+ """HEC-RAS Geometry HDF class."""
29
+
26
30
  GEOM_PATH = "Geometry"
27
31
  GEOM_STRUCTURES_PATH = f"{GEOM_PATH}/Structures"
28
32
  FLOW_AREA_2D_PATH = f"{GEOM_PATH}/2D Flow Areas"
29
33
 
30
34
  def __init__(self, name: str, **kwargs):
35
+ """Open a HEC-RAS Geometry HDF file.
36
+
37
+ Parameters
38
+ ----------
39
+ name : str
40
+ The path to the RAS Geometry HDF file.
41
+ kwargs : dict
42
+ Additional keyword arguments to pass to h5py.File
43
+ """
31
44
  super().__init__(name, **kwargs)
32
45
 
33
46
  def projection(self) -> Optional[CRS]:
34
- """Return the projection of the RAS geometry as a
35
- pyproj.CRS object.
47
+ """Return the projection of the RAS geometry as a pyproj.CRS object.
36
48
 
37
49
  Returns
38
50
  -------
39
- CRS
51
+ pyproj.CRS or None
40
52
  The projection of the RAS geometry.
41
53
  """
42
54
  proj_wkt = self.attrs.get("Projection")
@@ -47,12 +59,11 @@ class RasGeomHdf(RasHdf):
47
59
  return CRS.from_wkt(proj_wkt)
48
60
 
49
61
  def mesh_area_names(self) -> List[str]:
50
- """Return a list of the 2D mesh area names of
51
- the RAS geometry.
62
+ """Return a list of the 2D mesh area names of the RAS geometry.
52
63
 
53
64
  Returns
54
65
  -------
55
- list
66
+ List[str]
56
67
  A list of the 2D mesh area names (str) within the RAS geometry if 2D areas exist.
57
68
  """
58
69
  if "/Geometry/2D Flow Areas" not in self:
@@ -128,13 +139,19 @@ class RasGeomHdf(RasHdf):
128
139
  cell_dict["cell_id"] += cell_ids
129
140
  cell_dict["geometry"] += list(
130
141
  np.vectorize(
131
- lambda face_id_list: polygonize(
132
- np.ravel(
133
- mesh_faces[
134
- np.array(face_id_list.strip("[]").split()).astype(int)
135
- ]
142
+ lambda face_id_list: (
143
+ lambda geom_col: Polygon((geom_col[0] or geom_col[3]).geoms[0])
144
+ )(
145
+ polygonize_full(
146
+ np.ravel(
147
+ mesh_faces[
148
+ np.array(face_id_list.strip("[]").split()).astype(
149
+ int
150
+ )
151
+ ]
152
+ )
136
153
  )
137
- ).geoms[0]
154
+ )
138
155
  )(face_id_lists)
139
156
  )
140
157
  return GeoDataFrame(cell_dict, geometry="geometry", crs=self.projection())
@@ -206,8 +223,8 @@ class RasGeomHdf(RasHdf):
206
223
  face_dict["geometry"].append(LineString(coordinates))
207
224
  return GeoDataFrame(face_dict, geometry="geometry", crs=self.projection())
208
225
 
209
- def get_geom_attrs(self):
210
- """Returns base geometry attributes from a HEC-RAS HDF file.
226
+ def get_geom_attrs(self) -> Dict:
227
+ """Return base geometry attributes from a HEC-RAS HDF file.
211
228
 
212
229
  Returns
213
230
  -------
@@ -216,8 +233,8 @@ class RasGeomHdf(RasHdf):
216
233
  """
217
234
  return self.get_attrs(self.GEOM_PATH)
218
235
 
219
- def get_geom_structures_attrs(self):
220
- """Returns geometry structures attributes from a HEC-RAS HDF file.
236
+ def get_geom_structures_attrs(self) -> Dict:
237
+ """Return geometry structures attributes from a HEC-RAS HDF file.
221
238
 
222
239
  Returns
223
240
  -------
@@ -226,8 +243,8 @@ class RasGeomHdf(RasHdf):
226
243
  """
227
244
  return self.get_attrs(self.GEOM_STRUCTURES_PATH)
228
245
 
229
- def get_geom_2d_flow_area_attrs(self):
230
- """Returns geometry 2d flow area attributes from a HEC-RAS HDF file.
246
+ def get_geom_2d_flow_area_attrs(self) -> Dict:
247
+ """Return geometry 2d flow area attributes from a HEC-RAS HDF file.
231
248
 
232
249
  Returns
233
250
  -------
@@ -373,6 +390,11 @@ class RasGeomHdf(RasHdf):
373
390
  def structures(self, datetime_to_str: bool = False) -> GeoDataFrame:
374
391
  """Return the model structures.
375
392
 
393
+ Parameters
394
+ ----------
395
+ datetime_to_str : bool, optional
396
+ If True, convert datetime values to string format (default: False).
397
+
376
398
  Returns
377
399
  -------
378
400
  GeoDataFrame
@@ -419,50 +441,50 @@ class RasGeomHdf(RasHdf):
419
441
  )
420
442
  return struct_gdf
421
443
 
422
- def connections(self) -> GeoDataFrame:
444
+ def connections(self) -> GeoDataFrame: # noqa D102
423
445
  raise NotImplementedError
424
446
 
425
- def ic_points(self) -> GeoDataFrame:
447
+ def ic_points(self) -> GeoDataFrame: # noqa D102
426
448
  raise NotImplementedError
427
449
 
428
- def reference_lines(self) -> GeoDataFrame:
450
+ def reference_lines(self) -> GeoDataFrame: # noqa D102
429
451
  raise NotImplementedError
430
452
 
431
- def reference_points(self) -> GeoDataFrame:
453
+ def reference_points(self) -> GeoDataFrame: # noqa D102
432
454
  raise NotImplementedError
433
455
 
434
- def pump_stations(self) -> GeoDataFrame:
456
+ def pump_stations(self) -> GeoDataFrame: # noqa D102
435
457
  raise NotImplementedError
436
458
 
437
- def mannings_calibration_regions(self) -> GeoDataFrame:
459
+ def mannings_calibration_regions(self) -> GeoDataFrame: # noqa D102
438
460
  raise NotImplementedError
439
461
 
440
- def classification_polygons(self) -> GeoDataFrame:
462
+ def classification_polygons(self) -> GeoDataFrame: # noqa D102
441
463
  raise NotImplementedError
442
464
 
443
- def terrain_modifications(self) -> GeoDataFrame:
465
+ def terrain_modifications(self) -> GeoDataFrame: # noqa D102
444
466
  raise NotImplementedError
445
467
 
446
- def cross_sections(self) -> GeoDataFrame:
468
+ def cross_sections(self) -> GeoDataFrame: # noqa D102
447
469
  raise NotImplementedError
448
470
 
449
- def river_reaches(self) -> GeoDataFrame:
471
+ def river_reaches(self) -> GeoDataFrame: # noqa D102
450
472
  raise NotImplementedError
451
473
 
452
- def flowpaths(self) -> GeoDataFrame:
474
+ def flowpaths(self) -> GeoDataFrame: # noqa D102
453
475
  raise NotImplementedError
454
476
 
455
- def bank_points(self) -> GeoDataFrame:
477
+ def bank_points(self) -> GeoDataFrame: # noqa D102
456
478
  raise NotImplementedError
457
479
 
458
- def bank_lines(self) -> GeoDataFrame:
480
+ def bank_lines(self) -> GeoDataFrame: # noqa D102
459
481
  raise NotImplementedError
460
482
 
461
- def ineffective_areas(self) -> GeoDataFrame:
483
+ def ineffective_areas(self) -> GeoDataFrame: # noqa D102
462
484
  raise NotImplementedError
463
485
 
464
- def ineffective_points(self) -> GeoDataFrame:
486
+ def ineffective_points(self) -> GeoDataFrame: # noqa D102
465
487
  raise NotImplementedError
466
488
 
467
- def blocked_obstructions(self) -> GeoDataFrame:
489
+ def blocked_obstructions(self) -> GeoDataFrame: # noqa D102
468
490
  raise NotImplementedError
@@ -1,9 +1,13 @@
1
+ """HEC-RAS Plan HDF class."""
2
+
1
3
  from .geom import RasGeomHdf
2
4
  from typing import Dict
3
5
  from geopandas import GeoDataFrame
4
6
 
5
7
 
6
8
  class RasPlanHdf(RasGeomHdf):
9
+ """HEC-RAS Plan HDF class."""
10
+
7
11
  PLAN_INFO_PATH = "Plan Data/Plan Information"
8
12
  PLAN_PARAMS_PATH = "Plan Data/Plan Parameters"
9
13
  PRECIP_PATH = "Event Conditions/Meteorology/Precipitation"
@@ -12,67 +16,76 @@ class RasPlanHdf(RasGeomHdf):
12
16
  VOLUME_ACCOUNTING_PATH = f"{RESULTS_UNSTEADY_PATH}/Volume Accounting"
13
17
 
14
18
  def __init__(self, name: str, **kwargs):
19
+ """Open a HEC-RAS Plan HDF file.
20
+
21
+ Parameters
22
+ ----------
23
+ name : str
24
+ The path to the RAS Plan HDF file.
25
+ kwargs : dict
26
+ Additional keyword arguments to pass to h5py.File
27
+ """
15
28
  super().__init__(name, **kwargs)
16
29
 
17
30
  def get_plan_info_attrs(self) -> Dict:
18
- """Returns plan information attributes from a HEC-RAS HDF plan file.
31
+ """Return plan information attributes from a HEC-RAS HDF plan file.
19
32
 
20
33
  Returns
21
34
  -------
22
35
  dict
23
- Dictionary filled with plan information attributes.
36
+ Dictionary of plan information attributes.
24
37
  """
25
38
  return self.get_attrs(self.PLAN_INFO_PATH)
26
39
 
27
40
  def get_plan_param_attrs(self) -> Dict:
28
- """Returns plan parameter attributes from a HEC-RAS HDF plan file.
41
+ """Return plan parameter attributes from a HEC-RAS HDF plan file.
29
42
 
30
43
  Returns
31
44
  -------
32
45
  dict
33
- Dictionary filled with plan parameter attributes.
46
+ Dictionary of plan parameter attributes.
34
47
  """
35
48
  return self.get_attrs(self.PLAN_PARAMS_PATH)
36
49
 
37
50
  def get_meteorology_precip_attrs(self) -> Dict:
38
- """Returns precipitation attributes from a HEC-RAS HDF plan file.
51
+ """Return precipitation attributes from a HEC-RAS HDF plan file.
39
52
 
40
53
  Returns
41
54
  -------
42
55
  dict
43
- Dictionary filled with precipitation attributes.
56
+ Dictionary of precipitation attributes.
44
57
  """
45
58
  return self.get_attrs(self.PRECIP_PATH)
46
59
 
47
60
  def get_results_unsteady_attrs(self) -> Dict:
48
- """Returns unsteady attributes from a HEC-RAS HDF plan file.
61
+ """Return unsteady attributes from a HEC-RAS HDF plan file.
49
62
 
50
63
  Returns
51
64
  -------
52
65
  dict
53
- Dictionary filled with unsteady attributes.
66
+ Dictionary of unsteady attributes.
54
67
  """
55
68
  return self.get_attrs(self.RESULTS_UNSTEADY_PATH)
56
69
 
57
70
  def get_results_unsteady_summary_attrs(self) -> Dict:
58
- """Returns results unsteady summary attributes from a HEC-RAS HDF plan file.
71
+ """Return results unsteady summary attributes from a HEC-RAS HDF plan file.
59
72
 
60
73
  Returns
61
74
  -------
62
75
  dict
63
- Dictionary filled with results summary attributes.
76
+ Dictionary of results summary attributes.
64
77
  """
65
78
  return self.get_attrs(self.RESULTS_UNSTEADY_SUMMARY_PATH)
66
79
 
67
80
  def get_results_volume_accounting_attrs(self) -> Dict:
68
- """Returns volume accounting attributes from a HEC-RAS HDF plan file.
81
+ """Return volume accounting attributes from a HEC-RAS HDF plan file.
69
82
 
70
83
  Returns
71
84
  -------
72
85
  dict
73
- Dictionary filled with volume accounting attributes.
86
+ Dictionary of volume accounting attributes.
74
87
  """
75
88
  return self.get_attrs(self.VOLUME_ACCOUNTING_PATH)
76
89
 
77
- def enroachment_points(self) -> GeoDataFrame:
90
+ def enroachment_points(self) -> GeoDataFrame: # noqa: D102
78
91
  raise NotImplementedError
@@ -1,3 +1,5 @@
1
+ """Utility functions for reading HEC-RAS HDF data."""
2
+
1
3
  import h5py
2
4
  import numpy as np
3
5
  import pandas as pd
@@ -5,27 +7,40 @@ import pandas as pd
5
7
  from datetime import datetime, timedelta
6
8
  import re
7
9
  from typing import Any, List, Tuple, Union, Optional
10
+ from shapely import LineString, Polygon, polygonize_full
8
11
 
9
12
 
10
13
  def parse_ras_datetime(datetime_str: str) -> datetime:
11
14
  """Parse a datetime string from a RAS file into a datetime object.
12
15
 
16
+ If the datetime has a time of 2400, then it is converted to midnight of the next day.
17
+
13
18
  Parameters
14
19
  ----------
15
- datetime_str (str): The datetime string to be parsed. The string should be in the format "ddMMMyyyy HHmm".
20
+ datetime_str (str): The datetime string to be parsed. The string should be in the format "ddMMMyyyy HH:mm:ss".
16
21
 
17
22
  Returns
18
23
  -------
19
24
  datetime: A datetime object representing the parsed datetime.
20
25
  """
21
26
  format = "%d%b%Y %H:%M:%S"
22
- return datetime.strptime(datetime_str, format)
27
+
28
+ if datetime_str.endswith("24:00:00"):
29
+ datetime_str = datetime_str.replace("24:00:00", "00:00:00")
30
+ parsed_dt = datetime.strptime(datetime_str, format)
31
+ parsed_dt += timedelta(days=1)
32
+ else:
33
+ parsed_dt = datetime.strptime(datetime_str, format)
34
+
35
+ return parsed_dt
23
36
 
24
37
 
25
38
  def parse_ras_simulation_window_datetime(datetime_str) -> datetime:
26
39
  """
27
40
  Parse a datetime string from a RAS simulation window into a datetime object.
28
41
 
42
+ If the datetime has a time of 2400, then it is converted to midnight of the next day.
43
+
29
44
  Parameters
30
45
  ----------
31
46
  datetime_str: The datetime string to be parsed.
@@ -35,7 +50,15 @@ def parse_ras_simulation_window_datetime(datetime_str) -> datetime:
35
50
  datetime: A datetime object representing the parsed datetime.
36
51
  """
37
52
  format = "%d%b%Y %H%M"
38
- return datetime.strptime(datetime_str, format)
53
+
54
+ if datetime_str.endswith("2400"):
55
+ datetime_str = datetime_str.replace("2400", "0000")
56
+ parsed_dt = datetime.strptime(datetime_str, format)
57
+ parsed_dt += timedelta(days=1)
58
+ else:
59
+ parsed_dt = datetime.strptime(datetime_str, format)
60
+
61
+ return parsed_dt
39
62
 
40
63
 
41
64
  def parse_run_time_window(window: str) -> Tuple[datetime, datetime]:
@@ -183,13 +206,13 @@ def hdf5_attrs_to_dict(attrs: dict, prefix: str = None) -> dict:
183
206
  """
184
207
  Convert a dictionary of attributes from an HDF5 file into a Python dictionary.
185
208
 
186
- Parameters:
209
+ Parameters
187
210
  ----------
188
211
  attrs (dict): The attributes to be converted.
189
212
  prefix (str, optional): An optional prefix to prepend to the keys.
190
213
 
191
- Returns:
192
- ----------
214
+ Returns
215
+ -------
193
216
  dict: A dictionary with the converted attributes.
194
217
  """
195
218
  results = {}
@@ -210,12 +233,12 @@ def get_first_hdf_group(parent_group: h5py.Group) -> Optional[h5py.Group]:
210
233
  This function iterates over the items in the parent group and returns the first item that is an instance of
211
234
  h5py.Group. If no such item is found, it returns None.
212
235
 
213
- Parameters:
236
+ Parameters
214
237
  ----------
215
238
  parent_group (h5py.Group): The parent group to search in.
216
239
 
217
- Returns:
218
- ----------
240
+ Returns
241
+ -------
219
242
  Optional[h5py.Group]: The first HDF5 group in the parent group, or None if no group is found.
220
243
  """
221
244
  for _, item in parent_group.items():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: rashdf
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Read data from HEC-RAS HDF files.
5
5
  Project-URL: repository, https://github.com/fema-ffrd/rashdf
6
6
  Classifier: Development Status :: 4 - Beta
@@ -20,16 +20,26 @@ Provides-Extra: dev
20
20
  Requires-Dist: pre-commit; extra == "dev"
21
21
  Requires-Dist: ruff; extra == "dev"
22
22
  Requires-Dist: pytest; extra == "dev"
23
+ Requires-Dist: pytest-cov; extra == "dev"
24
+ Provides-Extra: docs
25
+ Requires-Dist: sphinx; extra == "docs"
26
+ Requires-Dist: numpydoc; extra == "docs"
27
+ Requires-Dist: sphinx_rtd_theme; extra == "docs"
23
28
 
24
29
  # rashdf
25
30
  [![CI](https://github.com/fema-ffrd/rashdf/actions/workflows/continuous-integration.yml/badge.svg?branch=main)](https://github.com/fema-ffrd/rashdf/actions/workflows/continuous-integration.yml)
26
31
  [![Release](https://github.com/fema-ffrd/rashdf/actions/workflows/release.yml/badge.svg)](https://github.com/fema-ffrd/rashdf/actions/workflows/release.yml)
27
32
  [![PyPI version](https://badge.fury.io/py/rashdf.svg)](https://badge.fury.io/py/rashdf)
33
+ [![codecov](https://codecov.io/gh/fema-ffrd/rashdf/graph/badge.svg?token=CTIIONEHV1)](https://codecov.io/gh/fema-ffrd/rashdf)
34
+ [![Documentation Status](https://readthedocs.org/projects/rashdf/badge/?version=latest)](https://rashdf.readthedocs.io/en/latest/?badge=latest)
28
35
 
29
36
  Read data from [HEC-RAS](https://www.hec.usace.army.mil/software/hec-ras/) [HDF](https://github.com/HDFGroup/hdf5) files.
30
37
 
31
38
  *Pronunciation: `raz·aitch·dee·eff`*
32
39
 
40
+ ## Documentation
41
+ [rashdf on ReadTheDocs](http://rashdf.readthedocs.io/)
42
+
33
43
  ## Install
34
44
  ```bash
35
45
  $ pip install rashdf
@@ -120,9 +130,6 @@ Example: write structures GeoJSON to `stdout`:
120
130
  $ rashdf structures Potomac.p01.hdf
121
131
  ```
122
132
 
123
- ## Documentation
124
- Coming soon.
125
-
126
133
  ## Developer Setup
127
134
  Create a virtual environment in the project directory:
128
135
  ```
@@ -6,3 +6,9 @@ pyarrow
6
6
  pre-commit
7
7
  ruff
8
8
  pytest
9
+ pytest-cov
10
+
11
+ [docs]
12
+ sphinx
13
+ numpydoc
14
+ sphinx_rtd_theme
@@ -9,6 +9,7 @@ from . import _create_hdf_with_group_attrs
9
9
 
10
10
  TEST_DATA = Path("./tests/data")
11
11
  MUNCIE_G05 = TEST_DATA / "ras/Muncie.g05.hdf"
12
+ COAL_G01 = TEST_DATA / "ras/Coal.g01.hdf"
12
13
  TEST_JSON = TEST_DATA / "json"
13
14
 
14
15
  TEST_ATTRS = {"test_attribute1": "test_str1", "test_attribute2": 500}
@@ -59,6 +60,24 @@ def test_mesh_cell_polygons():
59
60
  assert _gdf_matches_json(ghdf.mesh_cell_polygons(), mesh_cell_polygons_json)
60
61
 
61
62
 
63
+ def test_mesh_cell_polygons_coal():
64
+ """Test with the mesh from the Coal River model.
65
+
66
+ The Jan 2024 Coal River model from the Kanawha FFRD pilot project
67
+ contains some topologically incorrect polygons in the 2D mesh;
68
+ some of the mesh cell faces overlap.
69
+
70
+ See: https://github.com/fema-ffrd/rashdf/issues/31
71
+ """
72
+ coal_bad_polygons_json = TEST_JSON / "coal-bad-mesh-cell-polygons.json"
73
+ with RasGeomHdf(COAL_G01) as geom_hdf:
74
+ gdf = geom_hdf.mesh_cell_polygons()
75
+ gdf_bad_polygons = gdf[gdf["cell_id"].isin([8561, 11791, 17529])].to_crs(
76
+ "EPSG:4326"
77
+ ) # reproject because the Kanawha FFRD pilot project uses a custom CRS
78
+ assert _gdf_matches_json(gdf_bad_polygons, coal_bad_polygons_json)
79
+
80
+
62
81
  def test_bc_lines():
63
82
  bc_lines_json = TEST_JSON / "bc_lines.json"
64
83
  with RasGeomHdf(MUNCIE_G05) as ghdf:
@@ -15,13 +15,35 @@ def test_convert_ras_hdf_value():
15
15
  assert utils.convert_ras_hdf_value(b"15Mar2024 16:39:01") == datetime(
16
16
  2024, 3, 15, 16, 39, 1
17
17
  )
18
+ assert utils.convert_ras_hdf_value(b"15Mar2024 24:00:00") == datetime(
19
+ 2024, 3, 16, 0, 0, 0
20
+ )
18
21
  assert utils.convert_ras_hdf_value(b"15Mar2024 16:39:01 to 16Mar2024 16:39:01") == [
19
22
  datetime(2024, 3, 15, 16, 39, 1),
20
23
  datetime(2024, 3, 16, 16, 39, 1),
21
24
  ]
25
+ assert utils.convert_ras_hdf_value(b"18Mar2024 24:00:00 to 19Mar2024 24:00:00") == [
26
+ datetime(2024, 3, 19, 0, 0, 0),
27
+ datetime(2024, 3, 20, 0, 0, 0),
28
+ ]
22
29
  assert utils.convert_ras_hdf_value(b"01:23:45") == timedelta(
23
30
  hours=1, minutes=23, seconds=45
24
31
  )
32
+ assert utils.convert_ras_hdf_value(b"15Mar2024 2400") == datetime(
33
+ 2024, 3, 16, 0, 0, 0
34
+ )
35
+ assert utils.convert_ras_hdf_value(b"15Mar2024 2315") == datetime(
36
+ 2024, 3, 15, 23, 15, 0
37
+ )
38
+
39
+ assert utils.convert_ras_hdf_value(b"15Mar2024 1639 to 16Mar2024 1639") == [
40
+ datetime(2024, 3, 15, 16, 39, 0),
41
+ datetime(2024, 3, 16, 16, 39, 0),
42
+ ]
43
+ assert utils.convert_ras_hdf_value(b"18Mar2024 2400 to 19Mar2024 2400") == [
44
+ datetime(2024, 3, 19, 0, 0, 0),
45
+ datetime(2024, 3, 20, 0, 0, 0),
46
+ ]
25
47
 
26
48
 
27
49
  def test_df_datetimes_to_str():
File without changes
File without changes
File without changes
File without changes