ras-commander 0.48.0__py3-none-any.whl → 0.50.0__py3-none-any.whl

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.
ras_commander/HdfBndry.py CHANGED
@@ -1,11 +1,28 @@
1
1
  """
2
2
  Class: HdfBndry
3
3
 
4
+ A utility class for extracting and processing boundary-related features from HEC-RAS HDF files,
5
+ including boundary conditions, breaklines, refinement regions, and reference features.
6
+
4
7
  Attribution: A substantial amount of code in this file is sourced or derived
5
8
  from the https://github.com/fema-ffrd/rashdf library,
6
9
  released under MIT license and Copyright (c) 2024 fema-ffrd
7
10
 
8
11
  The file has been forked and modified for use in RAS Commander.
12
+
13
+ -----
14
+
15
+ All of the methods in this class are static and are designed to be used without instantiation.
16
+
17
+ List of Functions in HdfBndry:
18
+ - get_bc_lines() # Returns boundary condition lines as a GeoDataFrame.
19
+ - get_breaklines() # Returns 2D mesh area breaklines as a GeoDataFrame.
20
+ - get_refinement_regions() # Returns refinement regions as a GeoDataFrame.
21
+ - get_reference_lines() # Returns reference lines as a GeoDataFrame.
22
+ - get_reference_points() # Returns reference points as a GeoDataFrame.
23
+
24
+
25
+
9
26
  """
10
27
  from pathlib import Path
11
28
  from typing import Dict, List, Optional, Union, Any
@@ -38,10 +55,9 @@ class HdfBndry:
38
55
  This class relies on the HdfBase and HdfUtils classes for some of its
39
56
  functionality. Ensure these classes are available in the same package.
40
57
  """
41
-
42
58
  @staticmethod
43
59
  @standardize_input(file_type='plan_hdf')
44
- def bc_lines(hdf_path: Path) -> gpd.GeoDataFrame:
60
+ def get_bc_lines(hdf_path: Path) -> gpd.GeoDataFrame:
45
61
  """
46
62
  Return 2D mesh area boundary condition lines.
47
63
 
@@ -53,38 +69,47 @@ class HdfBndry:
53
69
  Returns
54
70
  -------
55
71
  gpd.GeoDataFrame
56
- A GeoDataFrame containing the boundary condition lines.
72
+ A GeoDataFrame containing the boundary condition lines and their attributes.
57
73
  """
58
74
  try:
59
75
  with h5py.File(hdf_path, 'r') as hdf_file:
60
76
  bc_lines_path = "Geometry/Boundary Condition Lines"
61
77
  if bc_lines_path not in hdf_file:
62
78
  return gpd.GeoDataFrame()
79
+
80
+ # Get geometries
63
81
  bc_line_data = hdf_file[bc_lines_path]
64
- bc_line_ids = range(bc_line_data["Attributes"][()].shape[0])
65
- v_conv_str = np.vectorize(HdfUtils.convert_ras_hdf_string)
66
- names = v_conv_str(bc_line_data["Attributes"][()]["Name"])
67
- mesh_names = v_conv_str(bc_line_data["Attributes"][()]["SA-2D"])
68
- types = v_conv_str(bc_line_data["Attributes"][()]["Type"])
69
- geoms = HdfBndry._get_polylines(hdf_file, bc_lines_path)
70
- return gpd.GeoDataFrame(
71
- {
72
- "bc_line_id": bc_line_ids,
73
- "Name": names,
74
- "mesh_name": mesh_names,
75
- "Type": types,
76
- "geometry": geoms,
77
- },
78
- geometry="geometry",
79
- crs=HdfUtils.projection(hdf_file),
82
+ geoms = HdfUtils.get_polylines_from_parts(hdf_path, bc_lines_path)
83
+
84
+ # Get attributes
85
+ attributes = pd.DataFrame(bc_line_data["Attributes"][()])
86
+
87
+ # Convert string columns
88
+ str_columns = ['Name', 'SA-2D', 'Type']
89
+ for col in str_columns:
90
+ if col in attributes.columns:
91
+ attributes[col] = attributes[col].apply(HdfUtils.convert_ras_string)
92
+
93
+ # Create GeoDataFrame with all attributes
94
+ gdf = gpd.GeoDataFrame(
95
+ attributes,
96
+ geometry=geoms,
97
+ crs=HdfUtils.get_projection(hdf_file)
80
98
  )
99
+
100
+ # Add ID column if not present
101
+ if 'bc_line_id' not in gdf.columns:
102
+ gdf['bc_line_id'] = range(len(gdf))
103
+
104
+ return gdf
105
+
81
106
  except Exception as e:
82
- print(f"Error reading boundary condition lines: {str(e)}")
107
+ logger.error(f"Error reading boundary condition lines: {str(e)}")
83
108
  return gpd.GeoDataFrame()
84
109
 
85
110
  @staticmethod
86
111
  @standardize_input(file_type='plan_hdf')
87
- def breaklines(hdf_path: Path) -> gpd.GeoDataFrame:
112
+ def get_breaklines(hdf_path: Path) -> gpd.GeoDataFrame:
88
113
  """
89
114
  Return 2D mesh area breaklines.
90
115
 
@@ -102,25 +127,26 @@ class HdfBndry:
102
127
  with h5py.File(hdf_path, 'r') as hdf_file:
103
128
  breaklines_path = "Geometry/2D Flow Area Break Lines"
104
129
  if breaklines_path not in hdf_file:
130
+ logger.warning(f"Breaklines path '{breaklines_path}' not found in HDF file.")
105
131
  return gpd.GeoDataFrame()
106
132
  bl_line_data = hdf_file[breaklines_path]
107
133
  bl_line_ids = range(bl_line_data["Attributes"][()].shape[0])
108
- names = np.vectorize(HdfUtils.convert_ras_hdf_string)(
134
+ names = np.vectorize(HdfUtils.convert_ras_string)(
109
135
  bl_line_data["Attributes"][()]["Name"]
110
136
  )
111
- geoms = HdfBndry._get_polylines(hdf_file, breaklines_path)
137
+ geoms = HdfUtils.get_polylines_from_parts(hdf_path, breaklines_path)
112
138
  return gpd.GeoDataFrame(
113
139
  {"bl_id": bl_line_ids, "Name": names, "geometry": geoms},
114
140
  geometry="geometry",
115
- crs=HdfUtils.projection(hdf_file),
141
+ crs=HdfUtils.get_projection(hdf_file),
116
142
  )
117
143
  except Exception as e:
118
- print(f"Error reading breaklines: {str(e)}")
144
+ logger.error(f"Error reading breaklines: {str(e)}")
119
145
  return gpd.GeoDataFrame()
120
146
 
121
147
  @staticmethod
122
148
  @standardize_input(file_type='plan_hdf')
123
- def refinement_regions(hdf_path: Path) -> gpd.GeoDataFrame:
149
+ def get_refinement_regions(hdf_path: Path) -> gpd.GeoDataFrame:
124
150
  """
125
151
  Return 2D mesh area refinement regions.
126
152
 
@@ -141,7 +167,7 @@ class HdfBndry:
141
167
  return gpd.GeoDataFrame()
142
168
  rr_data = hdf_file[refinement_regions_path]
143
169
  rr_ids = range(rr_data["Attributes"][()].shape[0])
144
- names = np.vectorize(HdfUtils.convert_ras_hdf_string)(rr_data["Attributes"][()]["Name"])
170
+ names = np.vectorize(HdfUtils.convert_ras_string)(rr_data["Attributes"][()]["Name"])
145
171
  geoms = list()
146
172
  for pnt_start, pnt_cnt, part_start, part_cnt in rr_data["Polygon Info"][()]:
147
173
  points = rr_data["Polygon Points"][()][pnt_start : pnt_start + pnt_cnt]
@@ -160,37 +186,17 @@ class HdfBndry:
160
186
  return gpd.GeoDataFrame(
161
187
  {"rr_id": rr_ids, "Name": names, "geometry": geoms},
162
188
  geometry="geometry",
163
- crs=HdfUtils.projection(hdf_file),
189
+ crs=HdfUtils.get_projection(hdf_file),
164
190
  )
165
191
  except Exception as e:
166
- print(f"Error reading refinement regions: {str(e)}")
192
+ logger.error(f"Error reading refinement regions: {str(e)}")
167
193
  return gpd.GeoDataFrame()
168
194
 
169
195
  @staticmethod
170
196
  @standardize_input(file_type='plan_hdf')
171
- def reference_lines_names(hdf_path: Path, mesh_name: Optional[str] = None) -> Union[Dict[str, List[str]], List[str]]:
197
+ def get_reference_lines(hdf_path: Path, mesh_name: Optional[str] = None) -> gpd.GeoDataFrame:
172
198
  """
173
- Return reference line names.
174
-
175
- Parameters
176
- ----------
177
- hdf_path : Path
178
- Path to the HEC-RAS geometry HDF file.
179
- mesh_name : Optional[str], optional
180
- Name of the mesh to filter by. Default is None.
181
-
182
- Returns
183
- -------
184
- Union[Dict[str, List[str]], List[str]]
185
- A dictionary of mesh names to reference line names, or a list of reference line names if mesh_name is provided.
186
- """
187
- return HdfBndry._get_reference_lines_points_names(hdf_path, "lines", mesh_name)
188
-
189
- @staticmethod
190
- @standardize_input(file_type='plan_hdf')
191
- def reference_points_names(hdf_path: Path, mesh_name: Optional[str] = None) -> Union[Dict[str, List[str]], List[str]]:
192
- """
193
- Return reference point names.
199
+ Return the reference lines geometry and attributes.
194
200
 
195
201
  Parameters
196
202
  ----------
@@ -199,28 +205,11 @@ class HdfBndry:
199
205
  mesh_name : Optional[str], optional
200
206
  Name of the mesh to filter by. Default is None.
201
207
 
202
- Returns
203
- -------
204
- Union[Dict[str, List[str]], List[str]]
205
- A dictionary of mesh names to reference point names, or a list of reference point names if mesh_name is provided.
206
- """
207
- return HdfBndry._get_reference_lines_points_names(hdf_path, "points", mesh_name)
208
-
209
- @staticmethod
210
- @standardize_input(file_type='plan_hdf')
211
- def reference_lines(hdf_path: Path) -> gpd.GeoDataFrame:
212
- """
213
- Return the reference lines geometry and attributes.
214
-
215
- Parameters
216
- ----------
217
- hdf_path : Path
218
- Path to the HEC-RAS geometry HDF file.
219
-
220
208
  Returns
221
209
  -------
222
210
  gpd.GeoDataFrame
223
- A GeoDataFrame containing the reference lines.
211
+ A GeoDataFrame containing the reference lines. If mesh_name is provided,
212
+ returns only lines for that mesh.
224
213
  """
225
214
  try:
226
215
  with h5py.File(hdf_path, 'r') as hdf_file:
@@ -228,35 +217,45 @@ class HdfBndry:
228
217
  attributes_path = f"{reference_lines_path}/Attributes"
229
218
  if attributes_path not in hdf_file:
230
219
  return gpd.GeoDataFrame()
220
+
231
221
  attributes = hdf_file[attributes_path][()]
232
222
  refline_ids = range(attributes.shape[0])
233
- v_conv_str = np.vectorize(HdfUtils.convert_ras_hdf_string)
223
+ v_conv_str = np.vectorize(HdfUtils.convert_ras_string)
234
224
  names = v_conv_str(attributes["Name"])
235
225
  mesh_names = v_conv_str(attributes["SA-2D"])
226
+
236
227
  try:
237
228
  types = v_conv_str(attributes["Type"])
238
229
  except ValueError:
239
- # "Type" field doesn't exist -- observed in some RAS HDF files
240
230
  types = np.array([""] * attributes.shape[0])
241
- geoms = HdfBndry._get_polylines(hdf_file, reference_lines_path)
242
- return gpd.GeoDataFrame(
231
+
232
+ geoms = HdfUtils.get_polylines_from_parts(hdf_path, reference_lines_path)
233
+
234
+ gdf = gpd.GeoDataFrame(
243
235
  {
244
236
  "refln_id": refline_ids,
245
237
  "Name": names,
246
- "mesh-name": mesh_names,
238
+ "mesh_name": mesh_names,
247
239
  "Type": types,
248
240
  "geometry": geoms,
249
241
  },
250
242
  geometry="geometry",
251
- crs=HdfUtils.projection(hdf_file),
243
+ crs=HdfUtils.get_projection(hdf_file),
252
244
  )
245
+
246
+ # Filter by mesh_name if provided
247
+ if mesh_name is not None:
248
+ gdf = gdf[gdf['mesh_name'] == mesh_name]
249
+
250
+ return gdf
251
+
253
252
  except Exception as e:
254
- print(f"Error reading reference lines: {str(e)}")
253
+ logger.error(f"Error reading reference lines: {str(e)}")
255
254
  return gpd.GeoDataFrame()
256
255
 
257
256
  @staticmethod
258
257
  @standardize_input(file_type='plan_hdf')
259
- def reference_points(hdf_path: Path) -> gpd.GeoDataFrame:
258
+ def get_reference_points(hdf_path: Path, mesh_name: Optional[str] = None) -> gpd.GeoDataFrame:
260
259
  """
261
260
  Return the reference points geometry and attributes.
262
261
 
@@ -264,11 +263,14 @@ class HdfBndry:
264
263
  ----------
265
264
  hdf_path : Path
266
265
  Path to the HEC-RAS geometry HDF file.
266
+ mesh_name : Optional[str], optional
267
+ Name of the mesh to filter by. Default is None.
267
268
 
268
269
  Returns
269
270
  -------
270
271
  gpd.GeoDataFrame
271
- A GeoDataFrame containing the reference points.
272
+ A GeoDataFrame containing the reference points. If mesh_name is provided,
273
+ returns only points for that mesh.
272
274
  """
273
275
  try:
274
276
  with h5py.File(hdf_path, 'r') as hdf_file:
@@ -276,14 +278,16 @@ class HdfBndry:
276
278
  attributes_path = f"{reference_points_path}/Attributes"
277
279
  if attributes_path not in hdf_file:
278
280
  return gpd.GeoDataFrame()
281
+
279
282
  ref_points_group = hdf_file[reference_points_path]
280
283
  attributes = ref_points_group["Attributes"][:]
281
- v_conv_str = np.vectorize(HdfUtils.convert_ras_hdf_string)
284
+ v_conv_str = np.vectorize(HdfUtils.convert_ras_string)
282
285
  names = v_conv_str(attributes["Name"])
283
286
  mesh_names = v_conv_str(attributes["SA/2D"])
284
287
  cell_id = attributes["Cell Index"]
285
288
  points = ref_points_group["Points"][()]
286
- return gpd.GeoDataFrame(
289
+
290
+ gdf = gpd.GeoDataFrame(
287
291
  {
288
292
  "refpt_id": range(attributes.shape[0]),
289
293
  "Name": names,
@@ -292,214 +296,17 @@ class HdfBndry:
292
296
  "geometry": list(map(Point, points)),
293
297
  },
294
298
  geometry="geometry",
295
- crs=HdfUtils.projection(hdf_file),
299
+ crs=HdfUtils.get_projection(hdf_file),
296
300
  )
297
- except Exception as e:
298
- print(f"Error reading reference points: {str(e)}")
299
- return gpd.GeoDataFrame()
300
-
301
- @staticmethod
302
- def _get_reference_lines_points_names(hdf_path: Path, reftype: str = "lines", mesh_name: Optional[str] = None) -> Union[Dict[str, List[str]], List[str]]:
303
- """
304
- Get the names of reference lines or points.
305
-
306
- Parameters
307
- ----------
308
- hdf_path : Path
309
- Path to the HEC-RAS geometry HDF file.
310
- reftype : str, optional
311
- Type of reference, either "lines" or "points" (default "lines").
312
- mesh_name : Optional[str], optional
313
- Name of the mesh to filter by. Default is None.
314
-
315
- Returns
316
- -------
317
- Union[Dict[str, List[str]], List[str]]
318
- A dictionary of mesh names to reference names, or a list of reference names if mesh_name is provided.
319
- """
320
- try:
321
- with h5py.File(hdf_path, 'r') as hdf_file:
322
- if reftype == "lines":
323
- path = "Geometry/Reference Lines"
324
- sa_2d_field = "SA-2D"
325
- elif reftype == "points":
326
- path = "Geometry/Reference Points"
327
- sa_2d_field = "SA/2D"
328
- else:
329
- raise ValueError(
330
- f"Invalid reference type: {reftype} -- must be 'lines' or 'points'."
331
- )
332
- attributes_path = f"{path}/Attributes"
333
- if mesh_name is None and attributes_path not in hdf_file:
334
- return {m: [] for m in HdfMesh.mesh_area_names(hdf_file)}
335
- if mesh_name is not None and attributes_path not in hdf_file:
336
- return []
337
- attributes = hdf_file[attributes_path][()]
338
- v_conv_str = np.vectorize(HdfUtils.convert_ras_hdf_string)
339
- names = v_conv_str(attributes["Name"])
301
+
302
+ # Filter by mesh_name if provided
340
303
  if mesh_name is not None:
341
- return names[v_conv_str(attributes[sa_2d_field]) == mesh_name].tolist()
342
- mesh_names = v_conv_str(attributes[sa_2d_field])
343
- return {m: names[mesh_names == m].tolist() for m in np.unique(mesh_names)}
304
+ gdf = gdf[gdf['mesh_name'] == mesh_name]
305
+
306
+ return gdf
307
+
344
308
  except Exception as e:
345
- print(f"Error reading reference lines/points names: {str(e)}")
346
- return {} if mesh_name is None else []
347
-
348
- @staticmethod
349
- def _get_polylines(hdf_file: h5py.File, path: str, info_name: str = "Polyline Info", parts_name: str = "Polyline Parts", points_name: str = "Polyline Points") -> List[Union[LineString, MultiLineString]]:
350
- """
351
- Get polyline geometries from HDF file.
352
-
353
- Parameters
354
- ----------
355
- hdf_file : h5py.File
356
- Open HDF file object.
357
- path : str
358
- Path to the polyline data in the HDF file.
359
- info_name : str, optional
360
- Name of the info dataset (default "Polyline Info").
361
- parts_name : str, optional
362
- Name of the parts dataset (default "Polyline Parts").
363
- points_name : str, optional
364
- Name of the points dataset (default "Polyline Points").
365
-
366
- Returns
367
- -------
368
- List[Union[LineString, MultiLineString]]
369
- A list of polyline geometries.
370
- """
371
- polyline_info_path = f"{path}/{info_name}"
372
- polyline_parts_path = f"{path}/{parts_name}"
373
- polyline_points_path = f"{path}/{points_name}"
374
-
375
- polyline_info = hdf_file[polyline_info_path][()]
376
- polyline_parts = hdf_file[polyline_parts_path][()]
377
- polyline_points = hdf_file[polyline_points_path][()]
309
+ logger.error(f"Error reading reference points: {str(e)}")
310
+ return gpd.GeoDataFrame()
378
311
 
379
- geoms = []
380
- for pnt_start, pnt_cnt, part_start, part_cnt in polyline_info:
381
- points = polyline_points[pnt_start : pnt_start + pnt_cnt]
382
- if part_cnt == 1:
383
- geoms.append(LineString(points))
384
- else:
385
- parts = polyline_parts[part_start : part_start + part_cnt]
386
- geoms.append(
387
- MultiLineString(
388
- list(
389
- points[part_pnt_start : part_pnt_start + part_pnt_cnt]
390
- for part_pnt_start, part_pnt_cnt in parts
391
- )
392
- )
393
- )
394
- return geoms
395
312
 
396
- @staticmethod
397
- @standardize_input(file_type='plan_hdf')
398
- def get_boundary_attributes(hdf_path: Path, boundary_type: str) -> pd.DataFrame:
399
- """
400
- Get attributes of boundary elements.
401
-
402
- Parameters
403
- ----------
404
- hdf_path : Path
405
- Path to the HEC-RAS geometry HDF file.
406
- boundary_type : str
407
- Type of boundary element ('bc_lines', 'breaklines', 'refinement_regions', 'reference_lines', 'reference_points').
408
-
409
- Returns
410
- -------
411
- pd.DataFrame
412
- A DataFrame containing the attributes of the specified boundary element.
413
- """
414
- try:
415
- with h5py.File(hdf_path, 'r') as hdf_file:
416
- if boundary_type == 'bc_lines':
417
- path = "Geometry/Boundary Condition Lines/Attributes"
418
- elif boundary_type == 'breaklines':
419
- path = "Geometry/2D Flow Area Break Lines/Attributes"
420
- elif boundary_type == 'refinement_regions':
421
- path = "Geometry/2D Flow Area Refinement Regions/Attributes"
422
- elif boundary_type == 'reference_lines':
423
- path = "Geometry/Reference Lines/Attributes"
424
- elif boundary_type == 'reference_points':
425
- path = "Geometry/Reference Points/Attributes"
426
- else:
427
- raise ValueError(f"Invalid boundary type: {boundary_type}")
428
-
429
- if path not in hdf_file:
430
- return pd.DataFrame()
431
-
432
- attributes = hdf_file[path][()]
433
- return pd.DataFrame(attributes)
434
- except Exception as e:
435
- print(f"Error reading {boundary_type} attributes: {str(e)}")
436
- return pd.DataFrame()
437
-
438
- @staticmethod
439
- @standardize_input(file_type='plan_hdf')
440
- def get_boundary_count(hdf_path: Path, boundary_type: str) -> int:
441
- """
442
- Get the count of boundary elements.
443
-
444
- Parameters
445
- ----------
446
- hdf_path : Path
447
- Path to the HEC-RAS geometry HDF file.
448
- boundary_type : str
449
- Type of boundary element ('bc_lines', 'breaklines', 'refinement_regions', 'reference_lines', 'reference_points').
450
-
451
- Returns
452
- -------
453
- int
454
- The count of the specified boundary element.
455
- """
456
- try:
457
- with h5py.File(hdf_path, 'r') as hdf_file:
458
- if boundary_type == 'bc_lines':
459
- path = "Geometry/Boundary Condition Lines/Attributes"
460
- elif boundary_type == 'breaklines':
461
- path = "Geometry/2D Flow Area Break Lines/Attributes"
462
- elif boundary_type == 'refinement_regions':
463
- path = "Geometry/2D Flow Area Refinement Regions/Attributes"
464
- elif boundary_type == 'reference_lines':
465
- path = "Geometry/Reference Lines/Attributes"
466
- elif boundary_type == 'reference_points':
467
- path = "Geometry/Reference Points/Attributes"
468
- else:
469
- raise ValueError(f"Invalid boundary type: {boundary_type}")
470
-
471
- if path not in hdf_file:
472
- return 0
473
-
474
- return hdf_file[path].shape[0]
475
- except Exception as e:
476
- print(f"Error getting {boundary_type} count: {str(e)}")
477
- return 0
478
-
479
- @staticmethod
480
- @standardize_input(file_type='plan_hdf')
481
- def get_boundary_names(hdf_path: Path, boundary_type: str) -> List[str]:
482
- """
483
- Get the names of boundary elements.
484
-
485
- Parameters
486
- ----------
487
- hdf_path : Path
488
- Path to the HEC-RAS geometry HDF file.
489
- boundary_type : str
490
- Type of boundary element ('bc_lines', 'breaklines', 'refinement_regions', 'reference_lines', 'reference_points').
491
-
492
- Returns
493
- -------
494
- List[str]
495
- A list of names for the specified boundary element.
496
- """
497
- try:
498
- df = HdfBndry.get_boundary_attributes(hdf_path, boundary_type)
499
- if 'Name' in df.columns:
500
- return df['Name'].tolist()
501
- else:
502
- return []
503
- except Exception as e:
504
- print(f"Error getting {boundary_type} names: {str(e)}")
505
- return []