rashdf 0.4.0__py3-none-any.whl → 0.6.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.
- cli.py +49 -6
- rashdf/base.py +4 -1
- rashdf/geom.py +216 -112
- rashdf/plan.py +445 -7
- rashdf/utils.py +32 -2
- {rashdf-0.4.0.dist-info → rashdf-0.6.0.dist-info}/METADATA +12 -6
- rashdf-0.6.0.dist-info/RECORD +12 -0
- {rashdf-0.4.0.dist-info → rashdf-0.6.0.dist-info}/WHEEL +1 -1
- rashdf-0.4.0.dist-info/RECORD +0 -12
- {rashdf-0.4.0.dist-info → rashdf-0.6.0.dist-info}/LICENSE +0 -0
- {rashdf-0.4.0.dist-info → rashdf-0.6.0.dist-info}/entry_points.txt +0 -0
- {rashdf-0.4.0.dist-info → rashdf-0.6.0.dist-info}/top_level.txt +0 -0
cli.py
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
from rashdf import RasGeomHdf, RasPlanHdf
|
|
4
4
|
from rashdf.utils import df_datetimes_to_str
|
|
5
5
|
|
|
6
|
-
import fiona
|
|
7
6
|
from geopandas import GeoDataFrame
|
|
8
7
|
|
|
9
8
|
import argparse
|
|
@@ -23,6 +22,8 @@ COMMANDS = [
|
|
|
23
22
|
"refinement_regions",
|
|
24
23
|
"bc_lines",
|
|
25
24
|
"breaklines",
|
|
25
|
+
"reference_lines",
|
|
26
|
+
"reference_points",
|
|
26
27
|
"structures",
|
|
27
28
|
]
|
|
28
29
|
|
|
@@ -50,6 +51,20 @@ def docstring_to_help(docstring: Optional[str]) -> str:
|
|
|
50
51
|
return help_text
|
|
51
52
|
|
|
52
53
|
|
|
54
|
+
def pyogrio_supported_drivers() -> List[str]:
|
|
55
|
+
"""Return a list of drivers supported by pyogrio for writing output files.
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
list
|
|
60
|
+
A list of drivers supported by pyogrio for writing output files.
|
|
61
|
+
"""
|
|
62
|
+
import pyogrio
|
|
63
|
+
|
|
64
|
+
drivers = pyogrio.list_drivers(write=True)
|
|
65
|
+
return sorted(drivers)
|
|
66
|
+
|
|
67
|
+
|
|
53
68
|
def fiona_supported_drivers() -> List[str]:
|
|
54
69
|
"""Return a list of drivers supported by Fiona for writing output files.
|
|
55
70
|
|
|
@@ -58,18 +73,34 @@ def fiona_supported_drivers() -> List[str]:
|
|
|
58
73
|
list
|
|
59
74
|
A list of drivers supported by Fiona for writing output files.
|
|
60
75
|
"""
|
|
76
|
+
import fiona
|
|
77
|
+
|
|
61
78
|
drivers = [d for d, s in fiona.supported_drivers.items() if "w" in s]
|
|
62
|
-
return drivers
|
|
79
|
+
return sorted(drivers)
|
|
63
80
|
|
|
64
81
|
|
|
65
82
|
def parse_args(args: str) -> argparse.Namespace:
|
|
66
83
|
"""Parse command-line arguments."""
|
|
67
84
|
parser = argparse.ArgumentParser(description="Extract data from HEC-RAS HDF files.")
|
|
68
85
|
parser.add_argument(
|
|
69
|
-
"--
|
|
86
|
+
"--pyogrio-drivers",
|
|
70
87
|
action="store_true",
|
|
71
|
-
help="List the drivers supported by
|
|
88
|
+
help="List the drivers supported by pyogrio for writing output files.",
|
|
72
89
|
)
|
|
90
|
+
fiona_installed = False
|
|
91
|
+
engines = ["pyogrio"]
|
|
92
|
+
try:
|
|
93
|
+
import fiona
|
|
94
|
+
|
|
95
|
+
fiona_installed = True
|
|
96
|
+
engines.append("fiona")
|
|
97
|
+
parser.add_argument(
|
|
98
|
+
"--fiona-drivers",
|
|
99
|
+
action="store_true",
|
|
100
|
+
help="List the drivers supported by Fiona for writing output files.",
|
|
101
|
+
)
|
|
102
|
+
except ImportError:
|
|
103
|
+
pass
|
|
73
104
|
subparsers = parser.add_subparsers(help="Sub-command help")
|
|
74
105
|
for command in COMMANDS:
|
|
75
106
|
f = getattr(RasGeomHdf, command)
|
|
@@ -91,6 +122,13 @@ def parse_args(args: str) -> argparse.Namespace:
|
|
|
91
122
|
output_group.add_argument(
|
|
92
123
|
"--feather", action="store_true", help="Output as Feather."
|
|
93
124
|
)
|
|
125
|
+
output_group.add_argument(
|
|
126
|
+
"--engine",
|
|
127
|
+
type=str,
|
|
128
|
+
choices=engines,
|
|
129
|
+
default="pyogrio",
|
|
130
|
+
help="Engine for writing output data.",
|
|
131
|
+
)
|
|
94
132
|
subparser.add_argument(
|
|
95
133
|
"--kwargs",
|
|
96
134
|
type=str,
|
|
@@ -105,7 +143,11 @@ def parse_args(args: str) -> argparse.Namespace:
|
|
|
105
143
|
|
|
106
144
|
def export(args: argparse.Namespace) -> Optional[str]:
|
|
107
145
|
"""Act on parsed arguments to extract data from HEC-RAS HDF files."""
|
|
108
|
-
if args.
|
|
146
|
+
if args.pyogrio_drivers:
|
|
147
|
+
for driver in pyogrio_supported_drivers():
|
|
148
|
+
print(driver)
|
|
149
|
+
return
|
|
150
|
+
if hasattr(args, "fiona_drivers") and args.fiona_drivers:
|
|
109
151
|
for driver in fiona_supported_drivers():
|
|
110
152
|
print(driver)
|
|
111
153
|
return
|
|
@@ -138,6 +180,7 @@ def export(args: argparse.Namespace) -> Optional[str]:
|
|
|
138
180
|
),
|
|
139
181
|
)
|
|
140
182
|
result = gdf.to_json(**kwargs)
|
|
183
|
+
print("No output file!")
|
|
141
184
|
print(result)
|
|
142
185
|
return result
|
|
143
186
|
elif args.parquet:
|
|
@@ -153,7 +196,7 @@ def export(args: argparse.Namespace) -> Optional[str]:
|
|
|
153
196
|
# convert any datetime columns to string.
|
|
154
197
|
# TODO: besides Geopackage, which of the standard Fiona drivers allow datetime?
|
|
155
198
|
gdf = df_datetimes_to_str(gdf)
|
|
156
|
-
gdf.to_file(args.output_file, **kwargs)
|
|
199
|
+
gdf.to_file(args.output_file, engine=args.engine, **kwargs)
|
|
157
200
|
|
|
158
201
|
|
|
159
202
|
def main():
|
rashdf/base.py
CHANGED
|
@@ -19,6 +19,7 @@ class RasHdf(h5py.File):
|
|
|
19
19
|
Additional keyword arguments to pass to h5py.File
|
|
20
20
|
"""
|
|
21
21
|
super().__init__(name, mode="r", **kwargs)
|
|
22
|
+
self._loc = name
|
|
22
23
|
|
|
23
24
|
@classmethod
|
|
24
25
|
def open_uri(
|
|
@@ -49,7 +50,9 @@ class RasHdf(h5py.File):
|
|
|
49
50
|
import fsspec
|
|
50
51
|
|
|
51
52
|
remote_file = fsspec.open(uri, mode="rb", **fsspec_kwargs)
|
|
52
|
-
|
|
53
|
+
result = cls(remote_file.open(), **h5py_kwargs)
|
|
54
|
+
result._loc = uri
|
|
55
|
+
return result
|
|
53
56
|
|
|
54
57
|
def get_attrs(self, attr_path: str) -> Dict:
|
|
55
58
|
"""Convert attributes from a HEC-RAS HDF file into a Python dictionary for a given attribute path.
|
rashdf/geom.py
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"""HEC-RAS Geometry HDF class."""
|
|
2
2
|
|
|
3
|
-
from typing import Dict, List, Optional
|
|
4
|
-
|
|
5
3
|
import numpy as np
|
|
6
4
|
import pandas as pd
|
|
7
5
|
from geopandas import GeoDataFrame
|
|
8
6
|
from pyproj import CRS
|
|
9
7
|
from shapely import (
|
|
8
|
+
Geometry,
|
|
9
|
+
Polygon,
|
|
10
|
+
Point,
|
|
10
11
|
LineString,
|
|
11
12
|
MultiLineString,
|
|
12
13
|
MultiPolygon,
|
|
@@ -15,6 +16,9 @@ from shapely import (
|
|
|
15
16
|
polygonize_full,
|
|
16
17
|
)
|
|
17
18
|
|
|
19
|
+
from typing import Dict, List, Optional, Union
|
|
20
|
+
|
|
21
|
+
|
|
18
22
|
from .base import RasHdf
|
|
19
23
|
from .utils import (
|
|
20
24
|
convert_ras_hdf_string,
|
|
@@ -24,12 +28,24 @@ from .utils import (
|
|
|
24
28
|
)
|
|
25
29
|
|
|
26
30
|
|
|
31
|
+
class RasGeomHdfError(Exception):
|
|
32
|
+
"""HEC-RAS Plan HDF error class."""
|
|
33
|
+
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
|
|
27
37
|
class RasGeomHdf(RasHdf):
|
|
28
38
|
"""HEC-RAS Geometry HDF class."""
|
|
29
39
|
|
|
30
40
|
GEOM_PATH = "Geometry"
|
|
31
41
|
GEOM_STRUCTURES_PATH = f"{GEOM_PATH}/Structures"
|
|
32
42
|
FLOW_AREA_2D_PATH = f"{GEOM_PATH}/2D Flow Areas"
|
|
43
|
+
BC_LINES_PATH = f"{GEOM_PATH}/Boundary Condition Lines"
|
|
44
|
+
BREAKLINES_PATH = f"{GEOM_PATH}/2D Flow Area Break Lines"
|
|
45
|
+
REFERENCE_LINES_PATH = f"{GEOM_PATH}/Reference Lines"
|
|
46
|
+
REFERENCE_POINTS_PATH = f"{GEOM_PATH}/Reference Points"
|
|
47
|
+
CROSS_SECTIONS = f"{GEOM_PATH}/Cross Sections"
|
|
48
|
+
RIVER_CENTERLINES = f"{GEOM_PATH}/River Centerlines"
|
|
33
49
|
|
|
34
50
|
def __init__(self, name: str, **kwargs):
|
|
35
51
|
"""Open a HEC-RAS Geometry HDF file.
|
|
@@ -262,6 +278,38 @@ class RasGeomHdf(RasHdf):
|
|
|
262
278
|
|
|
263
279
|
return d2_flow_area_attrs
|
|
264
280
|
|
|
281
|
+
def _get_polylines(
|
|
282
|
+
self,
|
|
283
|
+
path: str,
|
|
284
|
+
info_name: str = "Polyline Info",
|
|
285
|
+
parts_name: str = "Polyline Parts",
|
|
286
|
+
points_name: str = "Polyline Points",
|
|
287
|
+
) -> List[Geometry]:
|
|
288
|
+
polyline_info_path = f"{path}/{info_name}"
|
|
289
|
+
polyline_parts_path = f"{path}/{parts_name}"
|
|
290
|
+
polyline_points_path = f"{path}/{points_name}"
|
|
291
|
+
|
|
292
|
+
polyline_info = self[polyline_info_path][()]
|
|
293
|
+
polyline_parts = self[polyline_parts_path][()]
|
|
294
|
+
polyline_points = self[polyline_points_path][()]
|
|
295
|
+
|
|
296
|
+
geoms = []
|
|
297
|
+
for pnt_start, pnt_cnt, part_start, part_cnt in polyline_info:
|
|
298
|
+
points = polyline_points[pnt_start : pnt_start + pnt_cnt]
|
|
299
|
+
if part_cnt == 1:
|
|
300
|
+
geoms.append(LineString(points))
|
|
301
|
+
else:
|
|
302
|
+
parts = polyline_parts[part_start : part_start + part_cnt]
|
|
303
|
+
geoms.append(
|
|
304
|
+
MultiLineString(
|
|
305
|
+
list(
|
|
306
|
+
points[part_pnt_start : part_pnt_start + part_pnt_cnt]
|
|
307
|
+
for part_pnt_start, part_pnt_cnt in parts
|
|
308
|
+
)
|
|
309
|
+
)
|
|
310
|
+
)
|
|
311
|
+
return geoms
|
|
312
|
+
|
|
265
313
|
def bc_lines(self) -> GeoDataFrame:
|
|
266
314
|
"""Return 2D mesh area boundary condition lines.
|
|
267
315
|
|
|
@@ -270,35 +318,15 @@ class RasGeomHdf(RasHdf):
|
|
|
270
318
|
GeoDataFrame
|
|
271
319
|
A GeoDataFrame containing the 2D mesh area boundary condition lines if they exist.
|
|
272
320
|
"""
|
|
273
|
-
if
|
|
321
|
+
if self.BC_LINES_PATH not in self:
|
|
274
322
|
return GeoDataFrame()
|
|
275
|
-
bc_line_data = self[
|
|
323
|
+
bc_line_data = self[self.BC_LINES_PATH]
|
|
276
324
|
bc_line_ids = range(bc_line_data["Attributes"][()].shape[0])
|
|
277
325
|
v_conv_str = np.vectorize(convert_ras_hdf_string)
|
|
278
326
|
names = v_conv_str(bc_line_data["Attributes"][()]["Name"])
|
|
279
327
|
mesh_names = v_conv_str(bc_line_data["Attributes"][()]["SA-2D"])
|
|
280
328
|
types = v_conv_str(bc_line_data["Attributes"][()]["Type"])
|
|
281
|
-
geoms =
|
|
282
|
-
for pnt_start, pnt_cnt, part_start, part_cnt in bc_line_data["Polyline Info"][
|
|
283
|
-
()
|
|
284
|
-
]:
|
|
285
|
-
points = bc_line_data["Polyline Points"][()][
|
|
286
|
-
pnt_start : pnt_start + pnt_cnt
|
|
287
|
-
]
|
|
288
|
-
if part_cnt == 1:
|
|
289
|
-
geoms.append(LineString(points))
|
|
290
|
-
else:
|
|
291
|
-
parts = bc_line_data["Polyline Parts"][()][
|
|
292
|
-
part_start : part_start + part_cnt
|
|
293
|
-
]
|
|
294
|
-
geoms.append(
|
|
295
|
-
MultiLineString(
|
|
296
|
-
list(
|
|
297
|
-
points[part_pnt_start : part_pnt_start + part_pnt_cnt]
|
|
298
|
-
for part_pnt_start, part_pnt_cnt in parts
|
|
299
|
-
)
|
|
300
|
-
)
|
|
301
|
-
)
|
|
329
|
+
geoms = self._get_polylines(self.BC_LINES_PATH)
|
|
302
330
|
return GeoDataFrame(
|
|
303
331
|
{
|
|
304
332
|
"bc_line_id": bc_line_ids,
|
|
@@ -319,34 +347,14 @@ class RasGeomHdf(RasHdf):
|
|
|
319
347
|
GeoDataFrame
|
|
320
348
|
A GeoDataFrame containing the 2D mesh area breaklines if they exist.
|
|
321
349
|
"""
|
|
322
|
-
if
|
|
350
|
+
if self.BREAKLINES_PATH not in self:
|
|
323
351
|
return GeoDataFrame()
|
|
324
|
-
bl_line_data = self[
|
|
352
|
+
bl_line_data = self[self.BREAKLINES_PATH]
|
|
325
353
|
bl_line_ids = range(bl_line_data["Attributes"][()].shape[0])
|
|
326
354
|
names = np.vectorize(convert_ras_hdf_string)(
|
|
327
355
|
bl_line_data["Attributes"][()]["Name"]
|
|
328
356
|
)
|
|
329
|
-
geoms =
|
|
330
|
-
for pnt_start, pnt_cnt, part_start, part_cnt in bl_line_data["Polyline Info"][
|
|
331
|
-
()
|
|
332
|
-
]:
|
|
333
|
-
points = bl_line_data["Polyline Points"][()][
|
|
334
|
-
pnt_start : pnt_start + pnt_cnt
|
|
335
|
-
]
|
|
336
|
-
if part_cnt == 1:
|
|
337
|
-
geoms.append(LineString(points))
|
|
338
|
-
else:
|
|
339
|
-
parts = bl_line_data["Polyline Parts"][()][
|
|
340
|
-
part_start : part_start + part_cnt
|
|
341
|
-
]
|
|
342
|
-
geoms.append(
|
|
343
|
-
MultiLineString(
|
|
344
|
-
list(
|
|
345
|
-
points[part_pnt_start : part_pnt_start + part_pnt_cnt]
|
|
346
|
-
for part_pnt_start, part_pnt_cnt in parts
|
|
347
|
-
)
|
|
348
|
-
)
|
|
349
|
-
)
|
|
357
|
+
geoms = self._get_polylines(self.BREAKLINES_PATH)
|
|
350
358
|
return GeoDataFrame(
|
|
351
359
|
{"bl_id": bl_line_ids, "name": names, "geometry": geoms},
|
|
352
360
|
geometry="geometry",
|
|
@@ -400,36 +408,21 @@ class RasGeomHdf(RasHdf):
|
|
|
400
408
|
GeoDataFrame
|
|
401
409
|
A GeoDataFrame containing the model structures if they exist.
|
|
402
410
|
"""
|
|
403
|
-
if
|
|
411
|
+
if self.GEOM_STRUCTURES_PATH not in self:
|
|
404
412
|
return GeoDataFrame()
|
|
405
|
-
struct_data = self[
|
|
413
|
+
struct_data = self[self.GEOM_STRUCTURES_PATH]
|
|
406
414
|
v_conv_val = np.vectorize(convert_ras_hdf_value)
|
|
407
415
|
sd_attrs = struct_data["Attributes"][()]
|
|
408
416
|
struct_dict = {"struct_id": range(sd_attrs.shape[0])}
|
|
409
417
|
struct_dict.update(
|
|
410
418
|
{name: v_conv_val(sd_attrs[name]) for name in sd_attrs.dtype.names}
|
|
411
419
|
)
|
|
412
|
-
geoms =
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
]
|
|
419
|
-
if part_cnt == 1:
|
|
420
|
-
geoms.append(LineString(points))
|
|
421
|
-
else:
|
|
422
|
-
parts = struct_data["Centerline Parts"][()][
|
|
423
|
-
part_start : part_start + part_cnt
|
|
424
|
-
]
|
|
425
|
-
geoms.append(
|
|
426
|
-
MultiLineString(
|
|
427
|
-
list(
|
|
428
|
-
points[part_pnt_start : part_pnt_start + part_pnt_cnt]
|
|
429
|
-
for part_pnt_start, part_pnt_cnt in parts
|
|
430
|
-
)
|
|
431
|
-
)
|
|
432
|
-
)
|
|
420
|
+
geoms = self._get_polylines(
|
|
421
|
+
self.GEOM_STRUCTURES_PATH,
|
|
422
|
+
info_name="Centerline Info",
|
|
423
|
+
parts_name="Centerline Parts",
|
|
424
|
+
points_name="Centerline Points",
|
|
425
|
+
)
|
|
433
426
|
struct_gdf = GeoDataFrame(
|
|
434
427
|
struct_dict,
|
|
435
428
|
geometry=geoms,
|
|
@@ -447,11 +440,153 @@ class RasGeomHdf(RasHdf):
|
|
|
447
440
|
def ic_points(self) -> GeoDataFrame: # noqa D102
|
|
448
441
|
raise NotImplementedError
|
|
449
442
|
|
|
450
|
-
def
|
|
451
|
-
|
|
443
|
+
def _reference_lines_points_names(
|
|
444
|
+
self, reftype: str = "lines", mesh_name: Optional[str] = None
|
|
445
|
+
) -> Union[Dict[str, List[str]], List[str]]:
|
|
446
|
+
"""Return reference line names.
|
|
452
447
|
|
|
453
|
-
|
|
454
|
-
|
|
448
|
+
If a mesh name is provided, return a list of the reference line names for that mesh area.
|
|
449
|
+
If no mesh name is provided, return a dictionary of mesh names and their reference line names.
|
|
450
|
+
|
|
451
|
+
Parameters
|
|
452
|
+
----------
|
|
453
|
+
mesh_name : str, optional
|
|
454
|
+
The name of the mesh area for which to return reference line names.
|
|
455
|
+
|
|
456
|
+
Returns
|
|
457
|
+
-------
|
|
458
|
+
Union[Dict[str, List[str]], List[str]]
|
|
459
|
+
A dictionary of mesh names and their reference line names if mesh_name is None.
|
|
460
|
+
A list of reference line names for the specified mesh area if mesh_name is not None.
|
|
461
|
+
"""
|
|
462
|
+
if reftype == "lines":
|
|
463
|
+
path = self.REFERENCE_LINES_PATH
|
|
464
|
+
sa_2d_field = "SA-2D"
|
|
465
|
+
elif reftype == "points":
|
|
466
|
+
path = self.REFERENCE_POINTS_PATH
|
|
467
|
+
sa_2d_field = "SA/2D"
|
|
468
|
+
else:
|
|
469
|
+
raise RasGeomHdfError(
|
|
470
|
+
f"Invalid reference type: {reftype} -- must be 'lines' or 'points'."
|
|
471
|
+
)
|
|
472
|
+
attributes_path = f"{path}/Attributes"
|
|
473
|
+
if mesh_name is None and attributes_path not in self:
|
|
474
|
+
return {m: [] for m in self.mesh_area_names()}
|
|
475
|
+
if mesh_name is not None and attributes_path not in self:
|
|
476
|
+
return []
|
|
477
|
+
attributes = self[attributes_path][()]
|
|
478
|
+
v_conv_str = np.vectorize(convert_ras_hdf_string)
|
|
479
|
+
names = np.vectorize(convert_ras_hdf_string)(attributes["Name"])
|
|
480
|
+
if mesh_name is not None:
|
|
481
|
+
return names[v_conv_str(attributes[sa_2d_field]) == mesh_name].tolist()
|
|
482
|
+
mesh_names = np.vectorize(convert_ras_hdf_string)(attributes[sa_2d_field])
|
|
483
|
+
return {m: names[mesh_names == m].tolist() for m in np.unique(mesh_names)}
|
|
484
|
+
|
|
485
|
+
def reference_lines_names(
|
|
486
|
+
self, mesh_name: Optional[str] = None
|
|
487
|
+
) -> Union[Dict[str, List[str]], List[str]]:
|
|
488
|
+
"""Return reference line names.
|
|
489
|
+
|
|
490
|
+
If a mesh name is provided, return a list of the reference line names for that mesh area.
|
|
491
|
+
If no mesh name is provided, return a dictionary of mesh names and their reference line names.
|
|
492
|
+
|
|
493
|
+
Parameters
|
|
494
|
+
----------
|
|
495
|
+
mesh_name : str, optional
|
|
496
|
+
The name of the mesh area for which to return reference line names.
|
|
497
|
+
|
|
498
|
+
Returns
|
|
499
|
+
-------
|
|
500
|
+
Union[Dict[str, List[str]], List[str]]
|
|
501
|
+
A dictionary of mesh names and their reference line names if mesh_name is None.
|
|
502
|
+
A list of reference line names for the specified mesh area if mesh_name is not None.
|
|
503
|
+
"""
|
|
504
|
+
return self._reference_lines_points_names("lines", mesh_name)
|
|
505
|
+
|
|
506
|
+
def reference_points_names(
|
|
507
|
+
self, mesh_name: Optional[str] = None
|
|
508
|
+
) -> Union[Dict[str, List[str]], List[str]]:
|
|
509
|
+
"""Return reference point names.
|
|
510
|
+
|
|
511
|
+
If a mesh name is provided, return a list of the reference point names for that mesh area.
|
|
512
|
+
If no mesh name is provided, return a dictionary of mesh names and their reference point names.
|
|
513
|
+
|
|
514
|
+
Parameters
|
|
515
|
+
----------
|
|
516
|
+
mesh_name : str, optional
|
|
517
|
+
The name of the mesh area for which to return reference point names.
|
|
518
|
+
|
|
519
|
+
Returns
|
|
520
|
+
-------
|
|
521
|
+
Union[Dict[str, List[str]], List[str]]
|
|
522
|
+
A dictionary of mesh names and their reference point names if mesh_name is None.
|
|
523
|
+
A list of reference point names for the specified mesh area if mesh_name is not None.
|
|
524
|
+
"""
|
|
525
|
+
return self._reference_lines_points_names("points", mesh_name)
|
|
526
|
+
|
|
527
|
+
def reference_lines(self) -> GeoDataFrame:
|
|
528
|
+
"""Return the reference lines geometry and attributes.
|
|
529
|
+
|
|
530
|
+
Returns
|
|
531
|
+
-------
|
|
532
|
+
GeoDataFrame
|
|
533
|
+
A GeoDataFrame containing the reference lines if they exist.
|
|
534
|
+
"""
|
|
535
|
+
attributes_path = f"{self.REFERENCE_LINES_PATH}/Attributes"
|
|
536
|
+
if attributes_path not in self:
|
|
537
|
+
return GeoDataFrame()
|
|
538
|
+
attributes = self[attributes_path][()]
|
|
539
|
+
refline_ids = range(attributes.shape[0])
|
|
540
|
+
v_conv_str = np.vectorize(convert_ras_hdf_string)
|
|
541
|
+
names = v_conv_str(attributes["Name"])
|
|
542
|
+
mesh_names = v_conv_str(attributes["SA-2D"])
|
|
543
|
+
try:
|
|
544
|
+
types = v_conv_str(attributes["Type"])
|
|
545
|
+
except ValueError:
|
|
546
|
+
# "Type" field doesn't exist -- observed in some RAS HDF files
|
|
547
|
+
types = np.array([""] * attributes.shape[0])
|
|
548
|
+
geoms = self._get_polylines(self.REFERENCE_LINES_PATH)
|
|
549
|
+
return GeoDataFrame(
|
|
550
|
+
{
|
|
551
|
+
"refln_id": refline_ids,
|
|
552
|
+
"refln_name": names,
|
|
553
|
+
"mesh_name": mesh_names,
|
|
554
|
+
"type": types,
|
|
555
|
+
"geometry": geoms,
|
|
556
|
+
},
|
|
557
|
+
geometry="geometry",
|
|
558
|
+
crs=self.projection(),
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
def reference_points(self) -> GeoDataFrame:
|
|
562
|
+
"""Return the reference points geometry and attributes.
|
|
563
|
+
|
|
564
|
+
Returns
|
|
565
|
+
-------
|
|
566
|
+
GeoDataFrame
|
|
567
|
+
A GeoDataFrame containing the reference points if they exist.
|
|
568
|
+
"""
|
|
569
|
+
attributes_path = f"{self.REFERENCE_POINTS_PATH}/Attributes"
|
|
570
|
+
if attributes_path not in self:
|
|
571
|
+
return GeoDataFrame()
|
|
572
|
+
ref_points_group = self[self.REFERENCE_POINTS_PATH]
|
|
573
|
+
attributes = ref_points_group["Attributes"][:]
|
|
574
|
+
v_conv_str = np.vectorize(convert_ras_hdf_string)
|
|
575
|
+
names = v_conv_str(attributes["Name"])
|
|
576
|
+
mesh_names = v_conv_str(attributes["SA/2D"])
|
|
577
|
+
cell_id = attributes["Cell Index"]
|
|
578
|
+
points = ref_points_group["Points"][()]
|
|
579
|
+
return GeoDataFrame(
|
|
580
|
+
{
|
|
581
|
+
"refpt_id": range(attributes.shape[0]),
|
|
582
|
+
"refpt_name": names,
|
|
583
|
+
"mesh_name": mesh_names,
|
|
584
|
+
"cell_id": cell_id,
|
|
585
|
+
"geometry": list(map(Point, points)),
|
|
586
|
+
},
|
|
587
|
+
geometry="geometry",
|
|
588
|
+
crs=self.projection(),
|
|
589
|
+
)
|
|
455
590
|
|
|
456
591
|
def pump_stations(self) -> GeoDataFrame: # noqa D102
|
|
457
592
|
raise NotImplementedError
|
|
@@ -473,33 +608,17 @@ class RasGeomHdf(RasHdf):
|
|
|
473
608
|
GeoDataFrame
|
|
474
609
|
A GeoDataFrame containing the model 1D cross sections if they exist.
|
|
475
610
|
"""
|
|
476
|
-
if
|
|
611
|
+
if self.CROSS_SECTIONS not in self:
|
|
477
612
|
return GeoDataFrame()
|
|
478
613
|
|
|
479
|
-
xs_data = self[
|
|
614
|
+
xs_data = self[self.CROSS_SECTIONS]
|
|
480
615
|
v_conv_val = np.vectorize(convert_ras_hdf_value)
|
|
481
616
|
xs_attrs = xs_data["Attributes"][()]
|
|
482
617
|
xs_dict = {"xs_id": range(xs_attrs.shape[0])}
|
|
483
618
|
xs_dict.update(
|
|
484
619
|
{name: v_conv_val(xs_attrs[name]) for name in xs_attrs.dtype.names}
|
|
485
620
|
)
|
|
486
|
-
geoms =
|
|
487
|
-
for pnt_start, pnt_cnt, part_start, part_cnt in xs_data["Polyline Info"][()]:
|
|
488
|
-
points = xs_data["Polyline Points"][()][pnt_start : pnt_start + pnt_cnt]
|
|
489
|
-
if part_cnt == 1:
|
|
490
|
-
geoms.append(LineString(points))
|
|
491
|
-
else:
|
|
492
|
-
parts = xs_data["Polyline Parts"][()][
|
|
493
|
-
part_start : part_start + part_cnt
|
|
494
|
-
]
|
|
495
|
-
geoms.append(
|
|
496
|
-
MultiLineString(
|
|
497
|
-
list(
|
|
498
|
-
points[part_pnt_start : part_pnt_start + part_pnt_cnt]
|
|
499
|
-
for part_pnt_start, part_pnt_cnt in parts
|
|
500
|
-
)
|
|
501
|
-
)
|
|
502
|
-
)
|
|
621
|
+
geoms = self._get_polylines(self.CROSS_SECTIONS)
|
|
503
622
|
xs_gdf = GeoDataFrame(
|
|
504
623
|
xs_dict,
|
|
505
624
|
geometry=geoms,
|
|
@@ -519,10 +638,10 @@ class RasGeomHdf(RasHdf):
|
|
|
519
638
|
GeoDataFrame
|
|
520
639
|
A GeoDataFrame containing the model 1D river reach lines if they exist.
|
|
521
640
|
"""
|
|
522
|
-
if
|
|
641
|
+
if self.RIVER_CENTERLINES not in self:
|
|
523
642
|
return GeoDataFrame()
|
|
524
643
|
|
|
525
|
-
river_data = self[
|
|
644
|
+
river_data = self[self.RIVER_CENTERLINES]
|
|
526
645
|
v_conv_val = np.vectorize(convert_ras_hdf_value)
|
|
527
646
|
river_attrs = river_data["Attributes"][()]
|
|
528
647
|
river_dict = {"river_id": range(river_attrs.shape[0])}
|
|
@@ -530,22 +649,7 @@ class RasGeomHdf(RasHdf):
|
|
|
530
649
|
{name: v_conv_val(river_attrs[name]) for name in river_attrs.dtype.names}
|
|
531
650
|
)
|
|
532
651
|
geoms = list()
|
|
533
|
-
|
|
534
|
-
points = river_data["Polyline Points"][()][pnt_start : pnt_start + pnt_cnt]
|
|
535
|
-
if part_cnt == 1:
|
|
536
|
-
geoms.append(LineString(points))
|
|
537
|
-
else:
|
|
538
|
-
parts = river_data["Polyline Parts"][()][
|
|
539
|
-
part_start : part_start + part_cnt
|
|
540
|
-
]
|
|
541
|
-
geoms.append(
|
|
542
|
-
MultiLineString(
|
|
543
|
-
list(
|
|
544
|
-
points[part_pnt_start : part_pnt_start + part_pnt_cnt]
|
|
545
|
-
for part_pnt_start, part_pnt_cnt in parts
|
|
546
|
-
)
|
|
547
|
-
)
|
|
548
|
-
)
|
|
652
|
+
geoms = self._get_polylines(self.RIVER_CENTERLINES)
|
|
549
653
|
river_gdf = GeoDataFrame(
|
|
550
654
|
river_dict,
|
|
551
655
|
geometry=geoms,
|
rashdf/plan.py
CHANGED
|
@@ -5,6 +5,7 @@ from .utils import (
|
|
|
5
5
|
df_datetimes_to_str,
|
|
6
6
|
ras_timesteps_to_datetimes,
|
|
7
7
|
parse_ras_datetime_ms,
|
|
8
|
+
deprecated,
|
|
8
9
|
)
|
|
9
10
|
|
|
10
11
|
from geopandas import GeoDataFrame
|
|
@@ -163,6 +164,8 @@ class RasPlanHdf(RasGeomHdf):
|
|
|
163
164
|
f"{BASE_OUTPUT_PATH}/Summary Output/2D Flow Areas"
|
|
164
165
|
)
|
|
165
166
|
UNSTEADY_TIME_SERIES_PATH = f"{BASE_OUTPUT_PATH}/Unsteady Time Series"
|
|
167
|
+
REFERENCE_LINES_OUTPUT_PATH = f"{UNSTEADY_TIME_SERIES_PATH}/Reference Lines"
|
|
168
|
+
REFERENCE_POINTS_OUTPUT_PATH = f"{UNSTEADY_TIME_SERIES_PATH}/Reference Points"
|
|
166
169
|
|
|
167
170
|
RESULTS_STEADY_PATH = "Results/Steady"
|
|
168
171
|
BASE_STEADY_PATH = f"{RESULTS_STEADY_PATH}/Output/Output Blocks/Base Output"
|
|
@@ -583,7 +586,8 @@ class RasPlanHdf(RasGeomHdf):
|
|
|
583
586
|
Returns
|
|
584
587
|
-------
|
|
585
588
|
DataFrame
|
|
586
|
-
A DataFrame with columns 'mesh_name', 'cell_id' or 'face_id', a value column,
|
|
589
|
+
A DataFrame with columns 'mesh_name', 'cell_id' or 'face_id', a value column,
|
|
590
|
+
and a time column if the value corresponds to a specific time.
|
|
587
591
|
"""
|
|
588
592
|
methods_with_times = {
|
|
589
593
|
SummaryOutputVar.MAXIMUM_WATER_SURFACE: self.mesh_max_ws,
|
|
@@ -602,6 +606,76 @@ class RasPlanHdf(RasGeomHdf):
|
|
|
602
606
|
df = other_methods[var]()
|
|
603
607
|
return df
|
|
604
608
|
|
|
609
|
+
def _mesh_summary_outputs_df(
|
|
610
|
+
self,
|
|
611
|
+
cells_or_faces: str,
|
|
612
|
+
output_vars: Optional[List[SummaryOutputVar]] = None,
|
|
613
|
+
round_to: str = "0.1 s",
|
|
614
|
+
) -> DataFrame:
|
|
615
|
+
if cells_or_faces == "cells":
|
|
616
|
+
feature_id_field = "cell_id"
|
|
617
|
+
elif cells_or_faces == "faces":
|
|
618
|
+
feature_id_field = "face_id"
|
|
619
|
+
else:
|
|
620
|
+
raise ValueError('cells_or_faces must be either "cells" or "faces".')
|
|
621
|
+
if output_vars is None:
|
|
622
|
+
summary_output_vars = self._summary_output_vars(
|
|
623
|
+
cells_or_faces=cells_or_faces
|
|
624
|
+
)
|
|
625
|
+
elif isinstance(output_vars, list):
|
|
626
|
+
summary_output_vars = []
|
|
627
|
+
for var in output_vars:
|
|
628
|
+
if not isinstance(var, SummaryOutputVar):
|
|
629
|
+
var = SummaryOutputVar(var)
|
|
630
|
+
summary_output_vars.append(var)
|
|
631
|
+
else:
|
|
632
|
+
raise ValueError(
|
|
633
|
+
"include_output must be a boolean or a list of SummaryOutputVar values."
|
|
634
|
+
)
|
|
635
|
+
df = self.mesh_summary_output(summary_output_vars[0], round_to=round_to)
|
|
636
|
+
for var in summary_output_vars[1:]:
|
|
637
|
+
df_var = self.mesh_summary_output(var, round_to=round_to)
|
|
638
|
+
df = df.merge(df_var, on=["mesh_name", feature_id_field], how="left")
|
|
639
|
+
return df
|
|
640
|
+
|
|
641
|
+
def mesh_cells_summary_output(self, round_to: str = "0.1 s") -> DataFrame:
|
|
642
|
+
"""
|
|
643
|
+
Return a DataFrame with summary output data for each mesh cell in the model.
|
|
644
|
+
|
|
645
|
+
Parameters
|
|
646
|
+
----------
|
|
647
|
+
round_to : str, optional
|
|
648
|
+
The time unit to round the datetimes to. Default: "0.1 s" (seconds).
|
|
649
|
+
See Pandas documentation for valid time units:
|
|
650
|
+
https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html
|
|
651
|
+
|
|
652
|
+
Returns
|
|
653
|
+
-------
|
|
654
|
+
DataFrame
|
|
655
|
+
A DataFrame with columns 'mesh_name', 'cell_id', and columns for each
|
|
656
|
+
summary output variable.
|
|
657
|
+
"""
|
|
658
|
+
return self._mesh_summary_outputs_df("cells", round_to=round_to)
|
|
659
|
+
|
|
660
|
+
def mesh_faces_summary_output(self, round_to: str = "0.1 s") -> DataFrame:
|
|
661
|
+
"""
|
|
662
|
+
Return a DataFrame with summary output data for each mesh face in the model.
|
|
663
|
+
|
|
664
|
+
Parameters
|
|
665
|
+
----------
|
|
666
|
+
round_to : str, optional
|
|
667
|
+
The time unit to round the datetimes to. Default: "0.1 s" (seconds).
|
|
668
|
+
See Pandas documentation for valid time units:
|
|
669
|
+
https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html
|
|
670
|
+
|
|
671
|
+
Returns
|
|
672
|
+
-------
|
|
673
|
+
DataFrame
|
|
674
|
+
A DataFrame with columns 'mesh_name', 'face_id', and columns for each
|
|
675
|
+
summary output variable.
|
|
676
|
+
"""
|
|
677
|
+
return self._mesh_summary_outputs_df("faces", round_to=round_to)
|
|
678
|
+
|
|
605
679
|
def _summary_output_vars(
|
|
606
680
|
self, cells_or_faces: Optional[str] = None
|
|
607
681
|
) -> List[SummaryOutputVar]:
|
|
@@ -810,7 +884,7 @@ class RasPlanHdf(RasGeomHdf):
|
|
|
810
884
|
mesh_name: str,
|
|
811
885
|
var: TimeSeriesOutputVar,
|
|
812
886
|
) -> Tuple[np.ndarray, str]:
|
|
813
|
-
path =
|
|
887
|
+
path = self._mesh_timeseries_output_path(mesh_name, var.value)
|
|
814
888
|
group = self.get(path)
|
|
815
889
|
try:
|
|
816
890
|
import dask.array as da
|
|
@@ -828,6 +902,7 @@ class RasPlanHdf(RasGeomHdf):
|
|
|
828
902
|
self,
|
|
829
903
|
mesh_name: str,
|
|
830
904
|
var: Union[str, TimeSeriesOutputVar],
|
|
905
|
+
truncate: bool = True,
|
|
831
906
|
) -> xr.DataArray:
|
|
832
907
|
"""Return the time series output data for a given variable.
|
|
833
908
|
|
|
@@ -837,6 +912,8 @@ class RasPlanHdf(RasGeomHdf):
|
|
|
837
912
|
The name of the 2D flow area mesh.
|
|
838
913
|
var : TimeSeriesOutputVar
|
|
839
914
|
The time series output variable to retrieve.
|
|
915
|
+
truncate : bool, optional
|
|
916
|
+
If True, truncate the number of cells to the listed cell count.
|
|
840
917
|
|
|
841
918
|
Returns
|
|
842
919
|
-------
|
|
@@ -854,7 +931,10 @@ class RasPlanHdf(RasGeomHdf):
|
|
|
854
931
|
values, units = self._mesh_timeseries_output_values_units(mesh_name, var)
|
|
855
932
|
if var in TIME_SERIES_OUTPUT_VARS_CELLS:
|
|
856
933
|
cell_count = mesh_names_counts[mesh_name]
|
|
857
|
-
|
|
934
|
+
if truncate:
|
|
935
|
+
values = values[:, :cell_count]
|
|
936
|
+
else:
|
|
937
|
+
values = values[:, :]
|
|
858
938
|
id_coord = "cell_id"
|
|
859
939
|
elif var in TIME_SERIES_OUTPUT_VARS_FACES:
|
|
860
940
|
id_coord = "face_id"
|
|
@@ -872,24 +952,28 @@ class RasPlanHdf(RasGeomHdf):
|
|
|
872
952
|
"mesh_name": mesh_name,
|
|
873
953
|
"variable": var.value,
|
|
874
954
|
"units": units,
|
|
955
|
+
"hdf_path": self._mesh_timeseries_output_path(mesh_name, var.value),
|
|
875
956
|
},
|
|
876
957
|
)
|
|
877
958
|
return da
|
|
878
959
|
|
|
960
|
+
def _mesh_timeseries_output_path(self, mesh_name: str, var_name: str) -> str:
|
|
961
|
+
return f"{self.UNSTEADY_TIME_SERIES_PATH}/2D Flow Areas/{mesh_name}/{var_name}"
|
|
962
|
+
|
|
879
963
|
def _mesh_timeseries_outputs(
|
|
880
|
-
self, mesh_name: str, vars: List[TimeSeriesOutputVar]
|
|
964
|
+
self, mesh_name: str, vars: List[TimeSeriesOutputVar], truncate: bool = True
|
|
881
965
|
) -> xr.Dataset:
|
|
882
966
|
datasets = {}
|
|
883
967
|
for var in vars:
|
|
884
968
|
var_path = f"{self.UNSTEADY_TIME_SERIES_PATH}/2D Flow Areas/{mesh_name}/{var.value}"
|
|
885
969
|
if self.get(var_path) is None:
|
|
886
970
|
continue
|
|
887
|
-
da = self.mesh_timeseries_output(mesh_name, var)
|
|
971
|
+
da = self.mesh_timeseries_output(mesh_name, var, truncate=truncate)
|
|
888
972
|
datasets[var.value] = da
|
|
889
973
|
ds = xr.Dataset(datasets, attrs={"mesh_name": mesh_name})
|
|
890
974
|
return ds
|
|
891
975
|
|
|
892
|
-
def
|
|
976
|
+
def mesh_cells_timeseries_output(self, mesh_name: str) -> xr.Dataset:
|
|
893
977
|
"""Return the time series output data for cells in a 2D flow area mesh.
|
|
894
978
|
|
|
895
979
|
Parameters
|
|
@@ -905,7 +989,25 @@ class RasPlanHdf(RasGeomHdf):
|
|
|
905
989
|
ds = self._mesh_timeseries_outputs(mesh_name, TIME_SERIES_OUTPUT_VARS_CELLS)
|
|
906
990
|
return ds
|
|
907
991
|
|
|
908
|
-
|
|
992
|
+
@deprecated
|
|
993
|
+
def mesh_timeseries_output_cells(self, mesh_name: str) -> xr.Dataset:
|
|
994
|
+
"""Return the time series output data for cells in a 2D flow area mesh.
|
|
995
|
+
|
|
996
|
+
Deprecated: use mesh_cells_timeseries_output instead.
|
|
997
|
+
|
|
998
|
+
Parameters
|
|
999
|
+
----------
|
|
1000
|
+
mesh_name : str
|
|
1001
|
+
The name of the 2D flow area mesh.
|
|
1002
|
+
|
|
1003
|
+
Returns
|
|
1004
|
+
-------
|
|
1005
|
+
xr.Dataset
|
|
1006
|
+
An xarray Dataset with DataArrays for each time series output variable.
|
|
1007
|
+
"""
|
|
1008
|
+
return self.mesh_cells_timeseries_output(mesh_name)
|
|
1009
|
+
|
|
1010
|
+
def mesh_faces_timeseries_output(self, mesh_name: str) -> xr.Dataset:
|
|
909
1011
|
"""Return the time series output data for faces in a 2D flow area mesh.
|
|
910
1012
|
|
|
911
1013
|
Parameters
|
|
@@ -921,6 +1023,232 @@ class RasPlanHdf(RasGeomHdf):
|
|
|
921
1023
|
ds = self._mesh_timeseries_outputs(mesh_name, TIME_SERIES_OUTPUT_VARS_FACES)
|
|
922
1024
|
return ds
|
|
923
1025
|
|
|
1026
|
+
@deprecated
|
|
1027
|
+
def mesh_timeseries_output_faces(self, mesh_name: str) -> xr.Dataset:
|
|
1028
|
+
"""Return the time series output data for faces in a 2D flow area mesh.
|
|
1029
|
+
|
|
1030
|
+
Deprecated: use mesh_faces_timeseries_output instead.
|
|
1031
|
+
|
|
1032
|
+
Parameters
|
|
1033
|
+
----------
|
|
1034
|
+
mesh_name : str
|
|
1035
|
+
The name of the 2D flow area mesh.
|
|
1036
|
+
|
|
1037
|
+
Returns
|
|
1038
|
+
-------
|
|
1039
|
+
xr.Dataset
|
|
1040
|
+
An xarray Dataset with DataArrays for each time series output variable.
|
|
1041
|
+
"""
|
|
1042
|
+
return self.mesh_faces_timeseries_output(mesh_name)
|
|
1043
|
+
|
|
1044
|
+
def reference_timeseries_output(self, reftype: str = "lines") -> xr.Dataset:
|
|
1045
|
+
"""Return timeseries output data for reference lines or points from a HEC-RAS HDF plan file.
|
|
1046
|
+
|
|
1047
|
+
Parameters
|
|
1048
|
+
----------
|
|
1049
|
+
reftype : str, optional
|
|
1050
|
+
The type of reference data to retrieve. Must be either "lines" or "points".
|
|
1051
|
+
(default: "lines")
|
|
1052
|
+
|
|
1053
|
+
Returns
|
|
1054
|
+
-------
|
|
1055
|
+
xr.Dataset
|
|
1056
|
+
An xarray Dataset with reference line timeseries data.
|
|
1057
|
+
"""
|
|
1058
|
+
if reftype == "lines":
|
|
1059
|
+
output_path = self.REFERENCE_LINES_OUTPUT_PATH
|
|
1060
|
+
abbrev = "refln"
|
|
1061
|
+
elif reftype == "points":
|
|
1062
|
+
output_path = self.REFERENCE_POINTS_OUTPUT_PATH
|
|
1063
|
+
abbrev = "refpt"
|
|
1064
|
+
else:
|
|
1065
|
+
raise ValueError('reftype must be either "lines" or "points".')
|
|
1066
|
+
reference_group = self.get(output_path)
|
|
1067
|
+
if reference_group is None:
|
|
1068
|
+
raise RasPlanHdfError(
|
|
1069
|
+
f"Could not find HDF group at path '{output_path}'."
|
|
1070
|
+
f" Does the Plan HDF file contain reference {reftype[:-1]} output data?"
|
|
1071
|
+
)
|
|
1072
|
+
reference_names = reference_group["Name"][:]
|
|
1073
|
+
names = []
|
|
1074
|
+
mesh_areas = []
|
|
1075
|
+
for s in reference_names:
|
|
1076
|
+
name, mesh_area = s.decode("utf-8").split("|")
|
|
1077
|
+
names.append(name)
|
|
1078
|
+
mesh_areas.append(mesh_area)
|
|
1079
|
+
|
|
1080
|
+
times = self.unsteady_datetimes()
|
|
1081
|
+
|
|
1082
|
+
das = {}
|
|
1083
|
+
for var in ["Flow", "Velocity", "Water Surface"]:
|
|
1084
|
+
group = reference_group.get(var)
|
|
1085
|
+
if group is None:
|
|
1086
|
+
continue
|
|
1087
|
+
try:
|
|
1088
|
+
import dask.array as da
|
|
1089
|
+
|
|
1090
|
+
# TODO: user-specified chunks?
|
|
1091
|
+
values = da.from_array(group, chunks=group.chunks)
|
|
1092
|
+
except ImportError:
|
|
1093
|
+
values = group[:]
|
|
1094
|
+
units = group.attrs["Units"].decode("utf-8")
|
|
1095
|
+
da = xr.DataArray(
|
|
1096
|
+
values,
|
|
1097
|
+
name=var,
|
|
1098
|
+
dims=["time", f"{abbrev}_id"],
|
|
1099
|
+
coords={
|
|
1100
|
+
"time": times,
|
|
1101
|
+
f"{abbrev}_id": range(values.shape[1]),
|
|
1102
|
+
f"{abbrev}_name": (f"{abbrev}_id", names),
|
|
1103
|
+
"mesh_name": (f"{abbrev}_id", mesh_areas),
|
|
1104
|
+
},
|
|
1105
|
+
attrs={"units": units, "hdf_path": f"{output_path}/{var}"},
|
|
1106
|
+
)
|
|
1107
|
+
das[var] = da
|
|
1108
|
+
return xr.Dataset(das)
|
|
1109
|
+
|
|
1110
|
+
def reference_lines_timeseries_output(self) -> xr.Dataset:
|
|
1111
|
+
"""Return timeseries output data for reference lines from a HEC-RAS HDF plan file.
|
|
1112
|
+
|
|
1113
|
+
Returns
|
|
1114
|
+
-------
|
|
1115
|
+
xr.Dataset
|
|
1116
|
+
An xarray Dataset with timeseries output data for reference lines.
|
|
1117
|
+
"""
|
|
1118
|
+
return self.reference_timeseries_output(reftype="lines")
|
|
1119
|
+
|
|
1120
|
+
def reference_points_timeseries_output(self) -> xr.Dataset:
|
|
1121
|
+
"""Return timeseries output data for reference points from a HEC-RAS HDF plan file.
|
|
1122
|
+
|
|
1123
|
+
Returns
|
|
1124
|
+
-------
|
|
1125
|
+
xr.Dataset
|
|
1126
|
+
An xarray Dataset with timeseries output data for reference points.
|
|
1127
|
+
"""
|
|
1128
|
+
return self.reference_timeseries_output(reftype="points")
|
|
1129
|
+
|
|
1130
|
+
def reference_summary_output(self, reftype: str = "lines") -> DataFrame:
|
|
1131
|
+
"""Return summary output data for reference lines or points from a HEC-RAS HDF plan file.
|
|
1132
|
+
|
|
1133
|
+
Returns
|
|
1134
|
+
-------
|
|
1135
|
+
DataFrame
|
|
1136
|
+
A DataFrame with reference line summary output data.
|
|
1137
|
+
"""
|
|
1138
|
+
if reftype == "lines":
|
|
1139
|
+
abbrev = "refln"
|
|
1140
|
+
elif reftype == "points":
|
|
1141
|
+
abbrev = "refpt"
|
|
1142
|
+
else:
|
|
1143
|
+
raise ValueError('reftype must be either "lines" or "points".')
|
|
1144
|
+
ds = self.reference_timeseries_output(reftype=reftype)
|
|
1145
|
+
result = {
|
|
1146
|
+
f"{abbrev}_id": ds[f"{abbrev}_id"],
|
|
1147
|
+
f"{abbrev}_name": ds[f"{abbrev}_name"],
|
|
1148
|
+
"mesh_name": ds.mesh_name,
|
|
1149
|
+
}
|
|
1150
|
+
vars = {
|
|
1151
|
+
"Flow": "q",
|
|
1152
|
+
"Water Surface": "ws",
|
|
1153
|
+
"Velocity": "v",
|
|
1154
|
+
}
|
|
1155
|
+
for var, abbrev in vars.items():
|
|
1156
|
+
if var not in ds:
|
|
1157
|
+
continue
|
|
1158
|
+
max_var = ds[var].max(dim="time")
|
|
1159
|
+
max_time = ds[var].time[ds[var].argmax(dim="time")]
|
|
1160
|
+
min_var = ds[var].min(dim="time")
|
|
1161
|
+
min_time = ds[var].time[ds[var].argmin(dim="time")]
|
|
1162
|
+
result[f"max_{abbrev}"] = max_var
|
|
1163
|
+
result[f"max_{abbrev}_time"] = max_time
|
|
1164
|
+
result[f"min_{abbrev}"] = min_var
|
|
1165
|
+
result[f"min_{abbrev}_time"] = min_time
|
|
1166
|
+
return DataFrame(result)
|
|
1167
|
+
|
|
1168
|
+
def _reference_lines_points(
|
|
1169
|
+
self,
|
|
1170
|
+
reftype: str = "lines",
|
|
1171
|
+
include_output: bool = True,
|
|
1172
|
+
datetime_to_str: bool = False,
|
|
1173
|
+
) -> GeoDataFrame:
|
|
1174
|
+
if reftype == "lines":
|
|
1175
|
+
abbrev = "refln"
|
|
1176
|
+
gdf = super().reference_lines()
|
|
1177
|
+
elif reftype == "points":
|
|
1178
|
+
abbrev = "refpt"
|
|
1179
|
+
gdf = super().reference_points()
|
|
1180
|
+
else:
|
|
1181
|
+
raise ValueError('reftype must be either "lines" or "points".')
|
|
1182
|
+
if include_output is False:
|
|
1183
|
+
return gdf
|
|
1184
|
+
summary_output = self.reference_summary_output(reftype=reftype)
|
|
1185
|
+
gdf = gdf.merge(
|
|
1186
|
+
summary_output,
|
|
1187
|
+
on=[f"{abbrev}_id", f"{abbrev}_name", "mesh_name"],
|
|
1188
|
+
how="left",
|
|
1189
|
+
)
|
|
1190
|
+
if datetime_to_str:
|
|
1191
|
+
gdf = df_datetimes_to_str(gdf)
|
|
1192
|
+
return gdf
|
|
1193
|
+
|
|
1194
|
+
def reference_lines(
|
|
1195
|
+
self, include_output: bool = True, datetime_to_str: bool = False
|
|
1196
|
+
) -> GeoDataFrame:
|
|
1197
|
+
"""Return the reference lines from a HEC-RAS HDF plan file.
|
|
1198
|
+
|
|
1199
|
+
Includes summary output data for each reference line:
|
|
1200
|
+
- Maximum flow & time (max_q, max_q_time)
|
|
1201
|
+
- Minimum flow & time (min_q, min_q_time)
|
|
1202
|
+
- Maximum water surface elevation & time (max_ws, max_ws_time)
|
|
1203
|
+
- Minimum water surface elevation & time (min_ws, min_ws_time)
|
|
1204
|
+
|
|
1205
|
+
Parameters
|
|
1206
|
+
----------
|
|
1207
|
+
include_output : bool, optional
|
|
1208
|
+
If True, include summary output data in the GeoDataFrame. (default: True)
|
|
1209
|
+
datetime_to_str : bool, optional
|
|
1210
|
+
If True, convert datetime columns to strings. (default: False)
|
|
1211
|
+
|
|
1212
|
+
Returns
|
|
1213
|
+
-------
|
|
1214
|
+
GeoDataFrame
|
|
1215
|
+
A GeoDataFrame with reference line geometry and summary output data.
|
|
1216
|
+
"""
|
|
1217
|
+
return self._reference_lines_points(
|
|
1218
|
+
reftype="lines",
|
|
1219
|
+
include_output=include_output,
|
|
1220
|
+
datetime_to_str=datetime_to_str,
|
|
1221
|
+
)
|
|
1222
|
+
|
|
1223
|
+
def reference_points(
|
|
1224
|
+
self, include_output: bool = True, datetime_to_str: bool = False
|
|
1225
|
+
) -> GeoDataFrame:
|
|
1226
|
+
"""Return the reference points from a HEC-RAS HDF plan file.
|
|
1227
|
+
|
|
1228
|
+
Parameters
|
|
1229
|
+
----------
|
|
1230
|
+
include_output : bool, optional
|
|
1231
|
+
If True, include summary output data in the GeoDataFrame. (default: True)
|
|
1232
|
+
datetime_to_str : bool, optional
|
|
1233
|
+
If True, convert datetime columns to strings. (default: False)
|
|
1234
|
+
|
|
1235
|
+
Includes summary output data for each reference point:
|
|
1236
|
+
- Maximum flow & time (max_q, max_q_time)
|
|
1237
|
+
- Minimum flow & time (min_q, min_q_time)
|
|
1238
|
+
- Maximum water surface elevation & time (max_ws, max_ws_time)
|
|
1239
|
+
- Minimum water surface elevation & time (min_ws, min_ws_time)
|
|
1240
|
+
|
|
1241
|
+
Returns
|
|
1242
|
+
-------
|
|
1243
|
+
GeoDataFrame
|
|
1244
|
+
A GeoDataFrame with reference point geometry and summary output data.
|
|
1245
|
+
"""
|
|
1246
|
+
return self._reference_lines_points(
|
|
1247
|
+
reftype="points",
|
|
1248
|
+
include_output=include_output,
|
|
1249
|
+
datetime_to_str=datetime_to_str,
|
|
1250
|
+
)
|
|
1251
|
+
|
|
924
1252
|
def get_plan_info_attrs(self) -> Dict:
|
|
925
1253
|
"""Return plan information attributes from a HEC-RAS HDF plan file.
|
|
926
1254
|
|
|
@@ -1107,3 +1435,113 @@ class RasPlanHdf(RasGeomHdf):
|
|
|
1107
1435
|
A DataFrame containing the velocity inside the cross sections
|
|
1108
1436
|
"""
|
|
1109
1437
|
return self.steady_profile_xs_output(XsSteadyOutputVar.VELOCITY_TOTAL)
|
|
1438
|
+
|
|
1439
|
+
def _zmeta(self, ds: xr.Dataset) -> Dict:
|
|
1440
|
+
"""Given a xarray Dataset, return kerchunk-style zarr reference metadata."""
|
|
1441
|
+
from kerchunk.hdf import SingleHdf5ToZarr
|
|
1442
|
+
import zarr
|
|
1443
|
+
import base64
|
|
1444
|
+
|
|
1445
|
+
encoding = {}
|
|
1446
|
+
chunk_meta = {}
|
|
1447
|
+
|
|
1448
|
+
# Loop through each variable / DataArray in the Dataset
|
|
1449
|
+
for var, da in ds.data_vars.items():
|
|
1450
|
+
# The "hdf_path" attribute is the path within the HDF5 file
|
|
1451
|
+
# that the DataArray was read from. This is attribute is inserted
|
|
1452
|
+
# by rashdf (see "mesh_timeseries_output" method).
|
|
1453
|
+
hdf_ds_path = da.attrs["hdf_path"]
|
|
1454
|
+
hdf_ds = self.get(hdf_ds_path)
|
|
1455
|
+
if hdf_ds is None:
|
|
1456
|
+
# If we don't know where in the HDF5 the data came from, we
|
|
1457
|
+
# have to skip it, because we won't be able to generate the
|
|
1458
|
+
# correct metadata for it.
|
|
1459
|
+
continue
|
|
1460
|
+
# Get the filters and storage info for the HDF5 dataset.
|
|
1461
|
+
# Calling private methods from Kerchunk here because
|
|
1462
|
+
# there's not a nice public API for this part. This is hacky
|
|
1463
|
+
# and a bit risky because these private methods are more likely
|
|
1464
|
+
# to change, but short of reimplementing these functions ourselves
|
|
1465
|
+
# it's the best way to get the metadata we need.
|
|
1466
|
+
# TODO: raise an issue in Kerchunk to expose this functionality?
|
|
1467
|
+
filters = SingleHdf5ToZarr._decode_filters(None, hdf_ds)
|
|
1468
|
+
encoding[var] = {"compressor": None, "filters": filters}
|
|
1469
|
+
storage_info = SingleHdf5ToZarr._storage_info(None, hdf_ds)
|
|
1470
|
+
# Generate chunk metadata for the DataArray
|
|
1471
|
+
for key, value in storage_info.items():
|
|
1472
|
+
chunk_number = ".".join([str(k) for k in key])
|
|
1473
|
+
chunk_key = f"{var}/{chunk_number}"
|
|
1474
|
+
chunk_meta[chunk_key] = [str(self._loc), value["offset"], value["size"]]
|
|
1475
|
+
# "Write" the Dataset to a temporary in-memory zarr store (which
|
|
1476
|
+
# is the same a Python dictionary)
|
|
1477
|
+
zarr_tmp = zarr.MemoryStore()
|
|
1478
|
+
# Use compute=False here because we don't _actually_ want to write
|
|
1479
|
+
# the data to the zarr store, we just want to generate the metadata.
|
|
1480
|
+
ds.to_zarr(zarr_tmp, mode="w", compute=False, encoding=encoding)
|
|
1481
|
+
zarr_meta = {"version": 1, "refs": {}}
|
|
1482
|
+
# Loop through the in-memory Zarr store, decode the data to strings,
|
|
1483
|
+
# and add it to the final metadata dictionary.
|
|
1484
|
+
for key, value in zarr_tmp.items():
|
|
1485
|
+
try:
|
|
1486
|
+
value_str = value.decode("utf-8")
|
|
1487
|
+
except UnicodeDecodeError:
|
|
1488
|
+
value_str = "base64:" + base64.b64encode(value).decode("utf-8")
|
|
1489
|
+
zarr_meta["refs"][key] = value_str
|
|
1490
|
+
zarr_meta["refs"].update(chunk_meta)
|
|
1491
|
+
return zarr_meta
|
|
1492
|
+
|
|
1493
|
+
def zmeta_mesh_cells_timeseries_output(self, mesh_name: str) -> Dict:
|
|
1494
|
+
"""Return kerchunk-style zarr reference metadata.
|
|
1495
|
+
|
|
1496
|
+
Requires the 'zarr' and 'kerchunk' packages.
|
|
1497
|
+
|
|
1498
|
+
Returns
|
|
1499
|
+
-------
|
|
1500
|
+
dict
|
|
1501
|
+
Dictionary of kerchunk-style zarr reference metadata.
|
|
1502
|
+
"""
|
|
1503
|
+
ds = self._mesh_timeseries_outputs(
|
|
1504
|
+
mesh_name, TIME_SERIES_OUTPUT_VARS_CELLS, truncate=False
|
|
1505
|
+
)
|
|
1506
|
+
return self._zmeta(ds)
|
|
1507
|
+
|
|
1508
|
+
def zmeta_mesh_faces_timeseries_output(self, mesh_name: str) -> Dict:
|
|
1509
|
+
"""Return kerchunk-style zarr reference metadata.
|
|
1510
|
+
|
|
1511
|
+
Requires the 'zarr' and 'kerchunk' packages.
|
|
1512
|
+
|
|
1513
|
+
Returns
|
|
1514
|
+
-------
|
|
1515
|
+
dict
|
|
1516
|
+
Dictionary of kerchunk-style zarr reference metadata.
|
|
1517
|
+
"""
|
|
1518
|
+
ds = self._mesh_timeseries_outputs(
|
|
1519
|
+
mesh_name, TIME_SERIES_OUTPUT_VARS_FACES, truncate=False
|
|
1520
|
+
)
|
|
1521
|
+
return self._zmeta(ds)
|
|
1522
|
+
|
|
1523
|
+
def zmeta_reference_lines_timeseries_output(self) -> Dict:
|
|
1524
|
+
"""Return kerchunk-style zarr reference metadata.
|
|
1525
|
+
|
|
1526
|
+
Requires the 'zarr' and 'kerchunk' packages.
|
|
1527
|
+
|
|
1528
|
+
Returns
|
|
1529
|
+
-------
|
|
1530
|
+
dict
|
|
1531
|
+
Dictionary of kerchunk-style zarr reference metadata.
|
|
1532
|
+
"""
|
|
1533
|
+
ds = self.reference_lines_timeseries_output()
|
|
1534
|
+
return self._zmeta(ds)
|
|
1535
|
+
|
|
1536
|
+
def zmeta_reference_points_timeseries_output(self) -> Dict:
|
|
1537
|
+
"""Return kerchunk-style zarr reference metadata.
|
|
1538
|
+
|
|
1539
|
+
Requires the 'zarr' and 'kerchunk' packages.
|
|
1540
|
+
|
|
1541
|
+
Returns
|
|
1542
|
+
-------
|
|
1543
|
+
dict
|
|
1544
|
+
Dictionary of kerchunk-style zarr reference metadata.
|
|
1545
|
+
"""
|
|
1546
|
+
ds = self.reference_points_timeseries_output()
|
|
1547
|
+
return self._zmeta(ds)
|
rashdf/utils.py
CHANGED
|
@@ -6,8 +6,8 @@ import pandas as pd
|
|
|
6
6
|
|
|
7
7
|
from datetime import datetime, timedelta
|
|
8
8
|
import re
|
|
9
|
-
from typing import Any, List, Tuple, Union, Optional
|
|
10
|
-
|
|
9
|
+
from typing import Any, Callable, List, Tuple, Union, Optional
|
|
10
|
+
import warnings
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def parse_ras_datetime_ms(datetime_str: str) -> datetime:
|
|
@@ -308,3 +308,33 @@ def ras_timesteps_to_datetimes(
|
|
|
308
308
|
start_time + pd.Timedelta(timestep, unit=time_unit).round(round_to)
|
|
309
309
|
for timestep in timesteps.astype(np.float64)
|
|
310
310
|
]
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def deprecated(func) -> Callable:
|
|
314
|
+
"""
|
|
315
|
+
Deprecate a function.
|
|
316
|
+
|
|
317
|
+
This is a decorator which can be used to mark functions as deprecated.
|
|
318
|
+
It will result in a warning being emitted when the function is used.
|
|
319
|
+
|
|
320
|
+
Parameters
|
|
321
|
+
----------
|
|
322
|
+
func: The function to be deprecated.
|
|
323
|
+
|
|
324
|
+
Returns
|
|
325
|
+
-------
|
|
326
|
+
The decorated function.
|
|
327
|
+
"""
|
|
328
|
+
|
|
329
|
+
def new_func(*args, **kwargs):
|
|
330
|
+
warnings.warn(
|
|
331
|
+
f"{func.__name__} is deprecated and will be removed in a future version.",
|
|
332
|
+
category=DeprecationWarning,
|
|
333
|
+
stacklevel=2,
|
|
334
|
+
)
|
|
335
|
+
return func(*args, **kwargs)
|
|
336
|
+
|
|
337
|
+
new_func.__name__ = func.__name__
|
|
338
|
+
new_func.__doc__ = func.__doc__
|
|
339
|
+
new_func.__dict__.update(func.__dict__)
|
|
340
|
+
return new_func
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: rashdf
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
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
|
|
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
14
14
|
Description-Content-Type: text/markdown
|
|
15
15
|
License-File: LICENSE
|
|
16
16
|
Requires-Dist: h5py
|
|
17
|
-
Requires-Dist: geopandas <0.
|
|
17
|
+
Requires-Dist: geopandas <2.0,>=1.0
|
|
18
18
|
Requires-Dist: pyarrow
|
|
19
19
|
Requires-Dist: xarray
|
|
20
20
|
Provides-Extra: dev
|
|
@@ -22,6 +22,12 @@ Requires-Dist: pre-commit ; extra == 'dev'
|
|
|
22
22
|
Requires-Dist: ruff ; extra == 'dev'
|
|
23
23
|
Requires-Dist: pytest ; extra == 'dev'
|
|
24
24
|
Requires-Dist: pytest-cov ; extra == 'dev'
|
|
25
|
+
Requires-Dist: fiona ; extra == 'dev'
|
|
26
|
+
Requires-Dist: kerchunk ; extra == 'dev'
|
|
27
|
+
Requires-Dist: zarr ; extra == 'dev'
|
|
28
|
+
Requires-Dist: dask ; extra == 'dev'
|
|
29
|
+
Requires-Dist: fsspec ; extra == 'dev'
|
|
30
|
+
Requires-Dist: s3fs ; extra == 'dev'
|
|
25
31
|
Provides-Extra: docs
|
|
26
32
|
Requires-Dist: sphinx ; extra == 'docs'
|
|
27
33
|
Requires-Dist: numpydoc ; extra == 'docs'
|
|
@@ -76,8 +82,8 @@ Also, methods to extract certain HDF group attributes as dictionaries:
|
|
|
76
82
|
```python
|
|
77
83
|
>>> from rashdf import RasPlanHdf
|
|
78
84
|
>>> with RasPlanHdf("path/to/rasmodel/Muncie.p04.hdf") as plan_hdf:
|
|
79
|
-
>>>
|
|
80
|
-
>>>
|
|
85
|
+
>>> results_unsteady_summary_attrs = plan_hdf.get_results_unsteady_summary_attrs()
|
|
86
|
+
>>> results_unsteady_summary_attrs
|
|
81
87
|
{'Computation Time DSS': datetime.timedelta(0),
|
|
82
88
|
'Computation Time Total': datetime.timedelta(seconds=23),
|
|
83
89
|
'Maximum WSEL Error': 0.0099277812987566,
|
|
@@ -101,9 +107,9 @@ CLI help:
|
|
|
101
107
|
$ rashdf --help
|
|
102
108
|
```
|
|
103
109
|
|
|
104
|
-
Print the output formats supported by
|
|
110
|
+
Print the output formats supported by pyorgio:
|
|
105
111
|
```
|
|
106
|
-
$ rashdf --
|
|
112
|
+
$ rashdf --pyogrio-drivers
|
|
107
113
|
```
|
|
108
114
|
|
|
109
115
|
Help for a specific subcommand:
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
cli.py,sha256=yItWmCxnYLcuOpJVRpUsfv_NLS9IxLjojZB9GrxfKAU,6571
|
|
2
|
+
rashdf/__init__.py,sha256=XXFtJDgLPCimqAhfsFz_pTWYECJiRT0i-Kb1uflXmVU,156
|
|
3
|
+
rashdf/base.py,sha256=cAQJX1aeBJKb3MJ06ltpbRTUaZX5NkuxpR1J4f7FyTU,2507
|
|
4
|
+
rashdf/geom.py,sha256=2aTfj6mqZGP6rysflQ5L8FeItlYJsknO00sKHo-yaTw,26090
|
|
5
|
+
rashdf/plan.py,sha256=4kftqnZedhSWPl-5Yn3vz9Z4VifTXcUokti0s5lX1lU,56479
|
|
6
|
+
rashdf/utils.py,sha256=Cba6sULF0m0jg6CQass4bPm2oxTd_avoe1pRQxq082c,10896
|
|
7
|
+
rashdf-0.6.0.dist-info/LICENSE,sha256=L_0QaLpQVHPcglVjiaJPnOocwzP8uXevDRjUPr9DL1Y,1065
|
|
8
|
+
rashdf-0.6.0.dist-info/METADATA,sha256=0MarTKZArGaOTTROyz4PENscSiVy7cYwvatftl89y_Q,5920
|
|
9
|
+
rashdf-0.6.0.dist-info/WHEEL,sha256=Wyh-_nZ0DJYolHNn1_hMa4lM7uDedD_RGVwbmTjyItk,91
|
|
10
|
+
rashdf-0.6.0.dist-info/entry_points.txt,sha256=LHHMR1lLy4wRyscMuW1RlYDXemtPgqQhNcILz0DtStY,36
|
|
11
|
+
rashdf-0.6.0.dist-info/top_level.txt,sha256=SrmLb6FFTJtM_t6O1v0M0JePshiQJMHr0yYVkHL7ztk,11
|
|
12
|
+
rashdf-0.6.0.dist-info/RECORD,,
|
rashdf-0.4.0.dist-info/RECORD
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
cli.py,sha256=dnTMEBid99xqorBFKZnUwOTDyTmIg08D83bSCkJ6104,5389
|
|
2
|
-
rashdf/__init__.py,sha256=XXFtJDgLPCimqAhfsFz_pTWYECJiRT0i-Kb1uflXmVU,156
|
|
3
|
-
rashdf/base.py,sha256=lHYVDwFTA1qFI34QYZ55QKcp7b8CeZsmDfESdkYISbg,2432
|
|
4
|
-
rashdf/geom.py,sha256=z3ak4TYjYo8-jrIQNSU96S7ulX5xk67rFZ5J0Y9yKbI,22048
|
|
5
|
-
rashdf/plan.py,sha256=YfJdjzZmGA9X8QnUflkc-8DMzMGoW5Gda3lRgMfxeQc,39669
|
|
6
|
-
rashdf/utils.py,sha256=93arHtIT-iL9dIpbYr7esjrxv1uJabTRJSruyjvr8mw,10168
|
|
7
|
-
rashdf-0.4.0.dist-info/LICENSE,sha256=L_0QaLpQVHPcglVjiaJPnOocwzP8uXevDRjUPr9DL1Y,1065
|
|
8
|
-
rashdf-0.4.0.dist-info/METADATA,sha256=x7h2Pvw_xTQmzOhJKWkj6-R1lzCp_gDBt1wIkBikTZQ,5671
|
|
9
|
-
rashdf-0.4.0.dist-info/WHEEL,sha256=cpQTJ5IWu9CdaPViMhC9YzF8gZuS5-vlfoFihTBC86A,91
|
|
10
|
-
rashdf-0.4.0.dist-info/entry_points.txt,sha256=LHHMR1lLy4wRyscMuW1RlYDXemtPgqQhNcILz0DtStY,36
|
|
11
|
-
rashdf-0.4.0.dist-info/top_level.txt,sha256=SrmLb6FFTJtM_t6O1v0M0JePshiQJMHr0yYVkHL7ztk,11
|
|
12
|
-
rashdf-0.4.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|