rashdf 0.8.0__tar.gz → 0.8.1__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.4
2
2
  Name: rashdf
3
- Version: 0.8.0
3
+ Version: 0.8.1
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
@@ -16,7 +16,7 @@ License-File: LICENSE
16
16
  Requires-Dist: h5py
17
17
  Requires-Dist: geopandas<2.0,>=1.0
18
18
  Requires-Dist: pyarrow
19
- Requires-Dist: xarray
19
+ Requires-Dist: xarray<=2025.4.0
20
20
  Provides-Extra: dev
21
21
  Requires-Dist: pre-commit; extra == "dev"
22
22
  Requires-Dist: ruff; extra == "dev"
@@ -12,8 +12,8 @@ classifiers = [
12
12
  "Programming Language :: Python :: 3.11",
13
13
  "Programming Language :: Python :: 3.12",
14
14
  ]
15
- version = "0.8.0"
16
- dependencies = ["h5py", "geopandas>=1.0,<2.0", "pyarrow", "xarray"]
15
+ version = "0.8.1"
16
+ dependencies = ["h5py", "geopandas>=1.0,<2.0", "pyarrow", "xarray<=2025.4.0"]
17
17
 
18
18
  [project.optional-dependencies]
19
19
  dev = [
@@ -17,6 +17,8 @@ from shapely import (
17
17
  )
18
18
 
19
19
  from typing import Dict, List, Optional, Union
20
+ from warnings import warn
21
+ from pathlib import Path
20
22
 
21
23
 
22
24
  from .base import RasHdf
@@ -29,7 +31,7 @@ from .utils import (
29
31
 
30
32
 
31
33
  class RasGeomHdfError(Exception):
32
- """HEC-RAS Plan HDF error class."""
34
+ """HEC-RAS Geometry HDF error class."""
33
35
 
34
36
  pass
35
37
 
@@ -43,6 +45,7 @@ class RasGeomHdf(RasHdf):
43
45
  BC_LINES_PATH = f"{GEOM_PATH}/Boundary Condition Lines"
44
46
  IC_POINTS_PATH = f"{GEOM_PATH}/IC Points"
45
47
  BREAKLINES_PATH = f"{GEOM_PATH}/2D Flow Area Break Lines"
48
+ REFINEMENT_REGIONS_PATH = f"{GEOM_PATH}/2D Flow Area Refinement Regions"
46
49
  REFERENCE_LINES_PATH = f"{GEOM_PATH}/Reference Lines"
47
50
  REFERENCE_POINTS_PATH = f"{GEOM_PATH}/Reference Points"
48
51
  CROSS_SECTIONS = f"{GEOM_PATH}/Cross Sections"
@@ -295,19 +298,28 @@ class RasGeomHdf(RasHdf):
295
298
  polyline_points = self[polyline_points_path][()]
296
299
 
297
300
  geoms = []
298
- for pnt_start, pnt_cnt, part_start, part_cnt in polyline_info:
299
- points = polyline_points[pnt_start : pnt_start + pnt_cnt]
300
- if part_cnt == 1:
301
- geoms.append(LineString(points))
302
- else:
303
- parts = polyline_parts[part_start : part_start + part_cnt]
304
- geoms.append(
305
- MultiLineString(
306
- list(
307
- points[part_pnt_start : part_pnt_start + part_pnt_cnt]
308
- for part_pnt_start, part_pnt_cnt in parts
301
+ for i, (pnt_start, pnt_cnt, part_start, part_cnt) in enumerate(polyline_info):
302
+ try:
303
+ points = polyline_points[pnt_start : pnt_start + pnt_cnt]
304
+ if part_cnt == 1:
305
+ geoms.append(LineString(points))
306
+ else: # pragma: no cover | TODO: add test coverage for this
307
+ parts = polyline_parts[part_start : part_start + part_cnt]
308
+ geoms.append(
309
+ MultiLineString(
310
+ list(
311
+ points[part_pnt_start : part_pnt_start + part_pnt_cnt]
312
+ for part_pnt_start, part_pnt_cnt in parts
313
+ )
309
314
  )
310
315
  )
316
+ except (
317
+ Exception
318
+ ) as e: # pragma: no cover | TODO: add test coverage for this
319
+ geoms.append(None)
320
+ warn(
321
+ f"Feature ID {i} within '{Path(path).name}' layer set to null due to invalid geometry. {e}",
322
+ UserWarning,
311
323
  )
312
324
  return geoms
313
325
 
@@ -370,25 +382,38 @@ class RasGeomHdf(RasHdf):
370
382
  GeoDataFrame
371
383
  A GeoDataFrame containing the 2D mesh area refinement regions if they exist.
372
384
  """
373
- if "/Geometry/2D Flow Area Refinement Regions" not in self:
385
+ if self.REFINEMENT_REGIONS_PATH not in self:
374
386
  return GeoDataFrame()
375
- rr_data = self["/Geometry/2D Flow Area Refinement Regions"]
387
+ rr_data = self[self.REFINEMENT_REGIONS_PATH]
376
388
  rr_ids = range(rr_data["Attributes"][()].shape[0])
377
389
  names = np.vectorize(convert_ras_hdf_string)(rr_data["Attributes"][()]["Name"])
378
390
  geoms = list()
379
- for pnt_start, pnt_cnt, part_start, part_cnt in rr_data["Polygon Info"][()]:
380
- points = rr_data["Polygon Points"][()][pnt_start : pnt_start + pnt_cnt]
381
- if part_cnt == 1:
382
- geoms.append(Polygon(points))
383
- else:
384
- parts = rr_data["Polygon Parts"][()][part_start : part_start + part_cnt]
385
- geoms.append(
386
- MultiPolygon(
387
- list(
388
- points[part_pnt_start : part_pnt_start + part_pnt_cnt]
389
- for part_pnt_start, part_pnt_cnt in parts
391
+ for i, (pnt_start, pnt_cnt, part_start, part_cnt) in enumerate(
392
+ rr_data["Polygon Info"][()]
393
+ ):
394
+ try:
395
+ points = rr_data["Polygon Points"][()][pnt_start : pnt_start + pnt_cnt]
396
+ if part_cnt == 1:
397
+ geoms.append(Polygon(points))
398
+ else: # pragma: no cover | TODO: add test coverage for this
399
+ parts = rr_data["Polygon Parts"][()][
400
+ part_start : part_start + part_cnt
401
+ ]
402
+ geoms.append(
403
+ MultiPolygon(
404
+ list(
405
+ points[part_pnt_start : part_pnt_start + part_pnt_cnt]
406
+ for part_pnt_start, part_pnt_cnt in parts
407
+ )
390
408
  )
391
409
  )
410
+ except (
411
+ Exception
412
+ ) as e: # pragma: no cover | TODO: add test coverage for this
413
+ geoms.append(None)
414
+ warn(
415
+ f"Feature ID {i} within '{Path(self.REFINEMENT_REGIONS_PATH).name}' layer set to null due to invalid geometry. {e}",
416
+ UserWarning,
392
417
  )
393
418
  return GeoDataFrame(
394
419
  {"rr_id": rr_ids, "name": names, "geometry": geoms},
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rashdf
3
- Version: 0.8.0
3
+ Version: 0.8.1
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
@@ -16,7 +16,7 @@ License-File: LICENSE
16
16
  Requires-Dist: h5py
17
17
  Requires-Dist: geopandas<2.0,>=1.0
18
18
  Requires-Dist: pyarrow
19
- Requires-Dist: xarray
19
+ Requires-Dist: xarray<=2025.4.0
20
20
  Provides-Extra: dev
21
21
  Requires-Dist: pre-commit; extra == "dev"
22
22
  Requires-Dist: ruff; extra == "dev"
@@ -1,7 +1,7 @@
1
1
  h5py
2
2
  geopandas<2.0,>=1.0
3
3
  pyarrow
4
- xarray
4
+ xarray<=2025.4.0
5
5
 
6
6
  [dev]
7
7
  pre-commit
@@ -4,6 +4,7 @@ import h5py
4
4
  from pyproj import CRS
5
5
  from src.rashdf import RasGeomHdf
6
6
  from pandas.testing import assert_frame_equal
7
+ import pytest
7
8
 
8
9
  from . import _create_hdf_with_group_attrs, _gdf_matches_json, _gdf_matches_json_alt
9
10
 
@@ -35,30 +36,71 @@ def test_mesh_area_names():
35
36
  assert ghdf.mesh_area_names() == ["2D Interior Area", "Perimeter_NW"]
36
37
 
37
38
 
39
+ def test_invalid_mesh_area_names(tmp_path):
40
+ test_hdf = tmp_path / "test.hdf"
41
+ _create_hdf_with_group_attrs(test_hdf, RasGeomHdf.GEOM_PATH, TEST_ATTRS)
42
+ # Test the empty Mesh Area names
43
+ with RasGeomHdf(test_hdf) as ghdf:
44
+ assert ghdf.mesh_area_names() == []
45
+
46
+
38
47
  def test_mesh_areas():
39
48
  mesh_areas_json = TEST_JSON / "mesh_areas.json"
40
49
  with RasGeomHdf(MUNCIE_G05) as ghdf:
41
50
  assert _gdf_matches_json(ghdf.mesh_areas(), mesh_areas_json)
42
51
 
43
52
 
53
+ def test_invalid_mesh_areas(tmp_path):
54
+ test_hdf = tmp_path / "test.hdf"
55
+ _create_hdf_with_group_attrs(test_hdf, RasGeomHdf.GEOM_PATH, TEST_ATTRS)
56
+ # Test the empty Mesh Areas
57
+ with RasGeomHdf(test_hdf) as ghdf:
58
+ assert ghdf.mesh_areas().empty
59
+
60
+
44
61
  def test_mesh_cell_faces():
45
62
  mesh_cell_faces_json = TEST_JSON / "mesh_cell_faces.json"
46
63
  with RasGeomHdf(MUNCIE_G05) as ghdf:
47
64
  assert _gdf_matches_json(ghdf.mesh_cell_faces(), mesh_cell_faces_json)
48
65
 
49
66
 
67
+ def test_invalid_mesh_faces(tmp_path):
68
+ test_hdf = tmp_path / "test.hdf"
69
+ _create_hdf_with_group_attrs(test_hdf, RasGeomHdf.GEOM_PATH, TEST_ATTRS)
70
+ # Test the empty Mesh Faces
71
+ with RasGeomHdf(test_hdf) as ghdf:
72
+ assert ghdf.mesh_cell_faces().empty
73
+
74
+
50
75
  def test_mesh_cell_points():
51
76
  mesh_cell_points_json = TEST_JSON / "mesh_cell_points.json"
52
77
  with RasGeomHdf(MUNCIE_G05) as ghdf:
53
78
  assert _gdf_matches_json(ghdf.mesh_cell_points(), mesh_cell_points_json)
54
79
 
55
80
 
81
+ def test_invalid_mesh_cell_points(tmp_path):
82
+ test_hdf = tmp_path / "test.hdf"
83
+ _create_hdf_with_group_attrs(test_hdf, RasGeomHdf.GEOM_PATH, TEST_ATTRS)
84
+ # Test the empty Mesh Cell Points
85
+ with RasGeomHdf(test_hdf) as ghdf:
86
+ assert ghdf.mesh_cell_points().empty
87
+
88
+
56
89
  def test_mesh_cell_polygons():
57
90
  mesh_cell_polygons_json = TEST_JSON / "mesh_cell_polygons.json"
58
91
  with RasGeomHdf(MUNCIE_G05) as ghdf:
59
92
  assert _gdf_matches_json(ghdf.mesh_cell_polygons(), mesh_cell_polygons_json)
60
93
 
61
94
 
95
+ def test_invalid_mesh_cell_polygons(tmp_path):
96
+ # Create a dummy HDF file
97
+ test_hdf = tmp_path / "test.hdf"
98
+ _create_hdf_with_group_attrs(test_hdf, RasGeomHdf.GEOM_PATH, TEST_ATTRS)
99
+ # Test the empty Mesh Cell Polygons
100
+ with RasGeomHdf(test_hdf) as ghdf:
101
+ assert ghdf.mesh_cell_polygons().empty
102
+
103
+
62
104
  def test_mesh_cell_polygons_coal():
63
105
  """Test with the mesh from the Coal River model.
64
106
 
@@ -118,6 +160,18 @@ def test_get_geom_2d_flow_area_attrs(tmp_path):
118
160
  assert ras_hdf.get_geom_2d_flow_area_attrs() == TEST_ATTRS
119
161
 
120
162
 
163
+ def test_invalid_get_geom_2d_flow_area_attrs(tmp_path):
164
+ test_hdf = tmp_path / "test.hdf"
165
+ _create_hdf_with_group_attrs(test_hdf, RasGeomHdf.GEOM_PATH, TEST_ATTRS)
166
+ ras_hdf = RasGeomHdf(test_hdf)
167
+
168
+ with pytest.raises(
169
+ AttributeError,
170
+ match=f"Unable to get 2D Flow Area; {RasGeomHdf.FLOW_AREA_2D_PATH} group not found in HDF5 file.",
171
+ ):
172
+ ras_hdf.get_geom_2d_flow_area_attrs()
173
+
174
+
121
175
  def test_structs():
122
176
  structs_json = TEST_JSON / "structures.json"
123
177
  with RasGeomHdf(MUNCIE_G05) as ghdf:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes