ras-commander 0.52.0__py3-none-any.whl → 0.53.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/Decorators.py +137 -127
- ras_commander/HdfBase.py +21 -6
- ras_commander/HdfFluvialPluvial.py +553 -553
- ras_commander/HdfResultsPlan.py +192 -84
- ras_commander/HdfStruc.py +1 -1
- ras_commander/HdfXsec.py +2 -2
- ras_commander/LoggingConfig.py +2 -1
- ras_commander/RasCmdr.py +45 -20
- ras_commander/RasPlan.py +74 -65
- ras_commander/RasPrj.py +934 -917
- ras_commander/RasUnsteady.py +38 -19
- {ras_commander-0.52.0.dist-info → ras_commander-0.53.0.dist-info}/METADATA +92 -49
- {ras_commander-0.52.0.dist-info → ras_commander-0.53.0.dist-info}/RECORD +16 -16
- {ras_commander-0.52.0.dist-info → ras_commander-0.53.0.dist-info}/WHEEL +1 -1
- {ras_commander-0.52.0.dist-info → ras_commander-0.53.0.dist-info}/LICENSE +0 -0
- {ras_commander-0.52.0.dist-info → ras_commander-0.53.0.dist-info}/top_level.txt +0 -0
ras_commander/Decorators.py
CHANGED
@@ -1,128 +1,138 @@
|
|
1
|
-
from functools import wraps
|
2
|
-
from pathlib import Path
|
3
|
-
from typing import Union
|
4
|
-
import logging
|
5
|
-
import h5py
|
6
|
-
import inspect
|
7
|
-
|
8
|
-
|
9
|
-
def log_call(func):
|
10
|
-
@wraps(func)
|
11
|
-
def wrapper(*args, **kwargs):
|
12
|
-
logger = logging.getLogger(func.__module__)
|
13
|
-
logger.
|
14
|
-
result = func(*args, **kwargs)
|
15
|
-
logger.
|
16
|
-
return result
|
17
|
-
return wrapper
|
18
|
-
|
19
|
-
def standardize_input(file_type: str = 'plan_hdf'):
|
20
|
-
"""
|
21
|
-
Decorator to standardize input for HDF file operations.
|
22
|
-
|
23
|
-
This decorator processes various input types and converts them to a Path object
|
24
|
-
pointing to the correct HDF file. It handles the following input types:
|
25
|
-
- h5py.File objects
|
26
|
-
- pathlib.Path objects
|
27
|
-
- Strings (file paths or plan/geom numbers)
|
28
|
-
- Integers (interpreted as plan/geom numbers)
|
29
|
-
|
30
|
-
The decorator also manages RAS object references and logging.
|
31
|
-
|
32
|
-
Args:
|
33
|
-
file_type (str): Specifies whether to look for 'plan_hdf' or 'geom_hdf' files.
|
34
|
-
|
35
|
-
Returns:
|
36
|
-
A decorator that wraps the function to standardize its input to a Path object.
|
37
|
-
"""
|
38
|
-
def decorator(func):
|
39
|
-
@wraps(func)
|
40
|
-
def wrapper(*args, **kwargs):
|
41
|
-
logger = logging.getLogger(func.__module__)
|
42
|
-
|
43
|
-
# Check if the function expects an hdf_path parameter
|
44
|
-
sig = inspect.signature(func)
|
45
|
-
param_names = list(sig.parameters.keys())
|
46
|
-
|
47
|
-
# If first parameter is 'hdf_file', skip path processing
|
48
|
-
if param_names and param_names[0] == 'hdf_file':
|
49
|
-
return func(*args, **kwargs)
|
50
|
-
|
51
|
-
# Handle both static method calls and regular function calls
|
52
|
-
if args and isinstance(args[0], type):
|
53
|
-
# Static method call, remove the class argument
|
54
|
-
args = args[1:]
|
55
|
-
|
56
|
-
hdf_input = kwargs.pop('hdf_path', None) or kwargs.pop('hdf_input', None) or (args[0] if args else None)
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
if
|
61
|
-
|
62
|
-
|
63
|
-
#
|
64
|
-
if
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
# Handle
|
79
|
-
elif isinstance(hdf_input,
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
1
|
+
from functools import wraps
|
2
|
+
from pathlib import Path
|
3
|
+
from typing import Union
|
4
|
+
import logging
|
5
|
+
import h5py
|
6
|
+
import inspect
|
7
|
+
|
8
|
+
|
9
|
+
def log_call(func):
|
10
|
+
@wraps(func)
|
11
|
+
def wrapper(*args, **kwargs):
|
12
|
+
logger = logging.getLogger(func.__module__)
|
13
|
+
logger.debug(f"Calling {func.__name__}")
|
14
|
+
result = func(*args, **kwargs)
|
15
|
+
logger.debug(f"Finished {func.__name__}")
|
16
|
+
return result
|
17
|
+
return wrapper
|
18
|
+
|
19
|
+
def standardize_input(file_type: str = 'plan_hdf'):
|
20
|
+
"""
|
21
|
+
Decorator to standardize input for HDF file operations.
|
22
|
+
|
23
|
+
This decorator processes various input types and converts them to a Path object
|
24
|
+
pointing to the correct HDF file. It handles the following input types:
|
25
|
+
- h5py.File objects
|
26
|
+
- pathlib.Path objects
|
27
|
+
- Strings (file paths or plan/geom numbers)
|
28
|
+
- Integers (interpreted as plan/geom numbers)
|
29
|
+
|
30
|
+
The decorator also manages RAS object references and logging.
|
31
|
+
|
32
|
+
Args:
|
33
|
+
file_type (str): Specifies whether to look for 'plan_hdf' or 'geom_hdf' files.
|
34
|
+
|
35
|
+
Returns:
|
36
|
+
A decorator that wraps the function to standardize its input to a Path object.
|
37
|
+
"""
|
38
|
+
def decorator(func):
|
39
|
+
@wraps(func)
|
40
|
+
def wrapper(*args, **kwargs):
|
41
|
+
logger = logging.getLogger(func.__module__)
|
42
|
+
|
43
|
+
# Check if the function expects an hdf_path parameter
|
44
|
+
sig = inspect.signature(func)
|
45
|
+
param_names = list(sig.parameters.keys())
|
46
|
+
|
47
|
+
# If first parameter is 'hdf_file', skip path processing
|
48
|
+
if param_names and param_names[0] == 'hdf_file':
|
49
|
+
return func(*args, **kwargs)
|
50
|
+
|
51
|
+
# Handle both static method calls and regular function calls
|
52
|
+
if args and isinstance(args[0], type):
|
53
|
+
# Static method call, remove the class argument
|
54
|
+
args = args[1:]
|
55
|
+
|
56
|
+
hdf_input = kwargs.pop('hdf_path', None) or kwargs.pop('hdf_input', None) or (args[0] if args else None)
|
57
|
+
|
58
|
+
# Import ras here to ensure we get the most current instance
|
59
|
+
from .RasPrj import ras as ras
|
60
|
+
ras_object = kwargs.pop('ras_object', None) or (args[1] if len(args) > 1 else None)
|
61
|
+
ras_obj = ras_object or ras
|
62
|
+
|
63
|
+
# If no hdf_input provided, return the function unmodified
|
64
|
+
if hdf_input is None:
|
65
|
+
return func(*args, **kwargs)
|
66
|
+
|
67
|
+
# NEW: If input is already a Path and exists, use it directly regardless of file_type
|
68
|
+
if isinstance(hdf_input, Path) and hdf_input.is_file():
|
69
|
+
logger.info(f"Using existing HDF file: {hdf_input}")
|
70
|
+
new_args = (hdf_input,) + args[1:]
|
71
|
+
return func(*new_args, **kwargs)
|
72
|
+
|
73
|
+
hdf_path = None
|
74
|
+
|
75
|
+
# If hdf_input is already an h5py.File object, use its filename
|
76
|
+
if isinstance(hdf_input, h5py.File):
|
77
|
+
hdf_path = Path(hdf_input.filename)
|
78
|
+
# Handle Path objects
|
79
|
+
elif isinstance(hdf_input, Path):
|
80
|
+
if hdf_input.is_file():
|
81
|
+
hdf_path = hdf_input
|
82
|
+
# Handle string inputs
|
83
|
+
elif isinstance(hdf_input, str):
|
84
|
+
# Check if it's a file path
|
85
|
+
if Path(hdf_input).is_file():
|
86
|
+
hdf_path = Path(hdf_input)
|
87
|
+
# Check if it's a number (with or without 'p' prefix)
|
88
|
+
elif hdf_input.isdigit() or (len(hdf_input) == 3 and hdf_input[0] == 'p' and hdf_input[1:].isdigit()):
|
89
|
+
try:
|
90
|
+
ras_obj.check_initialized()
|
91
|
+
except Exception as e:
|
92
|
+
raise ValueError(f"RAS object is not initialized: {str(e)}")
|
93
|
+
|
94
|
+
number = hdf_input if hdf_input.isdigit() else hdf_input[1:]
|
95
|
+
|
96
|
+
if file_type == 'plan_hdf':
|
97
|
+
plan_info = ras_obj.plan_df[ras_obj.plan_df['plan_number'] == number]
|
98
|
+
if not plan_info.empty:
|
99
|
+
hdf_path = Path(plan_info.iloc[0]['HDF_Results_Path'])
|
100
|
+
elif file_type == 'geom_hdf':
|
101
|
+
geom_info = ras_obj.geom_df[ras_obj.geom_df['geom_number'] == number]
|
102
|
+
if not geom_info.empty:
|
103
|
+
hdf_path = Path(geom_info.iloc[0]['HDF_Path'])
|
104
|
+
else:
|
105
|
+
raise ValueError(f"Invalid file type: {file_type}")
|
106
|
+
# Handle integer inputs (assuming they're plan or geom numbers)
|
107
|
+
elif isinstance(hdf_input, int):
|
108
|
+
try:
|
109
|
+
ras_obj.check_initialized()
|
110
|
+
except Exception as e:
|
111
|
+
raise ValueError(f"RAS object is not initialized: {str(e)}")
|
112
|
+
|
113
|
+
number = f"{hdf_input:02d}"
|
114
|
+
|
115
|
+
if file_type == 'plan_hdf':
|
116
|
+
plan_info = ras_obj.plan_df[ras_obj.plan_df['plan_number'] == number]
|
117
|
+
if not plan_info.empty:
|
118
|
+
hdf_path = Path(plan_info.iloc[0]['HDF_Results_Path'])
|
119
|
+
elif file_type == 'geom_hdf':
|
120
|
+
geom_info = ras_obj.geom_df[ras_obj.geom_df['geom_number'] == number]
|
121
|
+
if not geom_info.empty:
|
122
|
+
hdf_path = Path(geom_info.iloc[0]['HDF_Path'])
|
123
|
+
else:
|
124
|
+
raise ValueError(f"Invalid file type: {file_type}")
|
125
|
+
|
126
|
+
if hdf_path is None or not hdf_path.is_file():
|
127
|
+
error_msg = f"HDF file not found: {hdf_input}"
|
128
|
+
logger.error(error_msg)
|
129
|
+
raise FileNotFoundError(error_msg)
|
130
|
+
|
131
|
+
logger.info(f"Using HDF file: {hdf_path}")
|
132
|
+
|
133
|
+
# Pass all original arguments and keywords, replacing hdf_input with standardized hdf_path
|
134
|
+
new_args = (hdf_path,) + args[1:]
|
135
|
+
return func(*new_args, **kwargs)
|
136
|
+
|
137
|
+
return wrapper
|
128
138
|
return decorator
|
ras_commander/HdfBase.py
CHANGED
@@ -158,6 +158,7 @@ class HdfBase:
|
|
158
158
|
|
159
159
|
project_folder = hdf_path.parent
|
160
160
|
wkt = None
|
161
|
+
proj_file = None # Initialize proj_file variable
|
161
162
|
|
162
163
|
# Try HDF file
|
163
164
|
try:
|
@@ -170,6 +171,7 @@ class HdfBase:
|
|
170
171
|
return wkt
|
171
172
|
except Exception as e:
|
172
173
|
logger.error(f"Error reading projection from HDF file {hdf_path}: {str(e)}")
|
174
|
+
|
173
175
|
# Try RASMapper file if no HDF projection
|
174
176
|
if not wkt:
|
175
177
|
try:
|
@@ -189,16 +191,29 @@ class HdfBase:
|
|
189
191
|
except Exception as e:
|
190
192
|
logger.error(f"Error reading RASMapper projection file: {str(e)}")
|
191
193
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
194
|
+
# Customize error message based on whether proj_file was found
|
195
|
+
if proj_file:
|
196
|
+
error_msg = (
|
197
|
+
"No valid projection found. Checked:\n"
|
198
|
+
f"1. HDF file projection attribute: {hdf_path}\n"
|
199
|
+
f"2. RASMapper projection file {proj_file} found in RASMapper file, but was invalid"
|
200
|
+
)
|
201
|
+
else:
|
202
|
+
error_msg = (
|
203
|
+
"No valid projection found. Checked:\n"
|
204
|
+
f"1. HDF file projection attribute: {hdf_path}\n was checked and no projection attribute found"
|
205
|
+
"2. No RASMapper projection file found"
|
206
|
+
)
|
207
|
+
|
208
|
+
error_msg += (
|
209
|
+
"\nTo fix this:\n"
|
197
210
|
"1. Open RASMapper\n"
|
198
211
|
"2. Click Map > Set Projection\n"
|
199
212
|
"3. Select an appropriate projection file or coordinate system\n"
|
200
213
|
"4. Save the RASMapper project"
|
201
214
|
)
|
215
|
+
|
216
|
+
logger.critical(error_msg)
|
202
217
|
return None
|
203
218
|
|
204
219
|
@staticmethod
|
@@ -219,7 +234,7 @@ class HdfBase:
|
|
219
234
|
logger.warning(f"Path {attr_path} not found in HDF file")
|
220
235
|
return {}
|
221
236
|
|
222
|
-
return HdfUtils.
|
237
|
+
return HdfUtils.convert_hdf5_attrs_to_dict(hdf_file[attr_path].attrs)
|
223
238
|
except Exception as e:
|
224
239
|
logger.error(f"Error getting attributes from {attr_path}: {str(e)}")
|
225
240
|
return {}
|