ras-commander 0.48.0__py3-none-any.whl → 0.49.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 +18 -1
- ras_commander/HdfBase.py +307 -197
- ras_commander/HdfBndry.py +94 -287
- ras_commander/HdfFluvialPluvial.py +155 -247
- ras_commander/HdfInfiltration.py +410 -0
- ras_commander/HdfMesh.py +117 -36
- ras_commander/HdfPipe.py +127 -175
- ras_commander/HdfPlan.py +144 -58
- ras_commander/HdfPlot.py +104 -0
- ras_commander/HdfPump.py +76 -28
- ras_commander/HdfResultsMesh.py +186 -167
- ras_commander/HdfResultsPlan.py +76 -220
- ras_commander/HdfResultsPlot.py +182 -0
- ras_commander/HdfResultsXsec.py +185 -145
- ras_commander/HdfStruc.py +65 -35
- ras_commander/HdfUtils.py +435 -518
- ras_commander/HdfXsec.py +137 -127
- ras_commander/RasCmdr.py +13 -0
- ras_commander/RasExamples.py +14 -0
- ras_commander/RasGeo.py +11 -0
- ras_commander/RasGpt.py +8 -0
- ras_commander/RasMapper.py +105 -0
- ras_commander/RasPlan.py +30 -0
- ras_commander/RasPrj.py +34 -0
- ras_commander/RasToGo.py +16 -0
- ras_commander/RasUnsteady.py +15 -0
- ras_commander/RasUtils.py +31 -0
- ras_commander/__init__.py +10 -0
- {ras_commander-0.48.0.dist-info → ras_commander-0.49.0.dist-info}/METADATA +73 -8
- ras_commander-0.49.0.dist-info/RECORD +34 -0
- ras_commander-0.48.0.dist-info/RECORD +0 -30
- {ras_commander-0.48.0.dist-info → ras_commander-0.49.0.dist-info}/LICENSE +0 -0
- {ras_commander-0.48.0.dist-info → ras_commander-0.49.0.dist-info}/WHEEL +0 -0
- {ras_commander-0.48.0.dist-info → ras_commander-0.49.0.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,29 @@
|
|
1
|
+
"""
|
2
|
+
Class: HdfFluvialPluvial
|
3
|
+
|
4
|
+
All of the methods in this class are static and are designed to be used without instantiation.
|
5
|
+
|
6
|
+
List of Functions in HdfFluvialPluvial:
|
7
|
+
- calculate_fluvial_pluvial_boundary()
|
8
|
+
- _process_cell_adjacencies()
|
9
|
+
- _identify_boundary_edges()
|
10
|
+
|
11
|
+
"""
|
12
|
+
|
1
13
|
from typing import Dict, List, Tuple
|
2
14
|
import pandas as pd
|
3
15
|
import geopandas as gpd
|
4
|
-
import matplotlib.pyplot as plt
|
5
16
|
from collections import defaultdict
|
6
|
-
from shapely.geometry import LineString
|
17
|
+
from shapely.geometry import LineString
|
7
18
|
from tqdm import tqdm
|
8
|
-
|
9
19
|
from .HdfMesh import HdfMesh
|
10
20
|
from .HdfUtils import HdfUtils
|
21
|
+
from .Decorators import standardize_input
|
22
|
+
from .HdfResultsMesh import HdfResultsMesh
|
23
|
+
from .LoggingConfig import get_logger
|
24
|
+
from pathlib import Path
|
25
|
+
|
26
|
+
logger = get_logger(__name__)
|
11
27
|
|
12
28
|
class HdfFluvialPluvial:
|
13
29
|
"""
|
@@ -15,234 +31,141 @@ class HdfFluvialPluvial:
|
|
15
31
|
|
16
32
|
This class provides methods to process and visualize HEC-RAS 2D model outputs,
|
17
33
|
specifically focusing on the delineation of fluvial and pluvial flood areas.
|
18
|
-
It includes functionality for
|
19
|
-
extracting cell polygons, and calculating fluvial-pluvial boundaries based on
|
34
|
+
It includes functionality for calculating fluvial-pluvial boundaries based on
|
20
35
|
the timing of maximum water surface elevations.
|
21
36
|
|
22
|
-
Key
|
23
|
-
-
|
24
|
-
-
|
25
|
-
-
|
37
|
+
Key Concepts:
|
38
|
+
- Fluvial flooding: Flooding from rivers/streams
|
39
|
+
- Pluvial flooding: Flooding from rainfall/surface water
|
40
|
+
- Delta_t: Time threshold (in hours) used to distinguish between fluvial and pluvial cells.
|
41
|
+
Cells with max WSE time differences greater than delta_t are considered boundaries.
|
26
42
|
|
27
43
|
Data Requirements:
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
- Requires max_ws_df with 'cell_id' and 'max_wsel_time' columns
|
40
|
-
(can be derived from HdfResultsMesh.mesh_max_ws(hdf_path))
|
41
|
-
|
42
|
-
Usage:
|
43
|
-
To use this class effectively, first initialize a RasPrj object and load the
|
44
|
-
necessary HDF files. Then, use the methods provided to analyze and visualize
|
45
|
-
the fluvial-pluvial characteristics of your 2D model results.
|
46
|
-
|
47
|
-
Example:
|
48
|
-
ras = RasPrj()
|
49
|
-
ras = init_ras_project(project_path, ras_version)
|
50
|
-
hdf_path = ras.get_plan_value(plan_number, 'Results_Output')
|
51
|
-
|
52
|
-
# Get maximum water surface data
|
53
|
-
max_ws_df = HdfResultsMesh.mesh_max_ws(hdf_path)
|
54
|
-
|
55
|
-
# Plot maximum water surface
|
56
|
-
HdfFluvialPluvial.plot_max_water_surface(max_ws_df)
|
57
|
-
|
58
|
-
# Extract cell polygons
|
59
|
-
cell_polygons_df = HdfMesh.mesh_cell_polygons(hdf_path)
|
60
|
-
projection = HdfUtils.projection(hdf_path)
|
61
|
-
cell_polygons_gdf = HdfFluvialPluvial.plot_cell_polygons(cell_polygons_df, projection)
|
62
|
-
|
63
|
-
# Calculate fluvial-pluvial boundary
|
64
|
-
boundary_gdf = HdfFluvialPluvial.calculate_fluvial_pluvial_boundary(cell_polygons_gdf, max_ws_df)
|
65
|
-
|
66
|
-
Note: Ensure that you have the necessary permissions and have initialized
|
67
|
-
the RAS project correctly before attempting to access HDF files.
|
44
|
+
- HEC-RAS plan HDF file containing:
|
45
|
+
- 2D mesh cell geometry (accessed via HdfMesh)
|
46
|
+
- Maximum water surface elevation times (accessed via HdfResultsMesh)
|
47
|
+
|
48
|
+
Usage Example:
|
49
|
+
>>> ras = init_ras_project(project_path, ras_version)
|
50
|
+
>>> hdf_path = Path("path/to/plan.hdf")
|
51
|
+
>>> boundary_gdf = HdfFluvialPluvial.calculate_fluvial_pluvial_boundary(
|
52
|
+
... hdf_path,
|
53
|
+
... delta_t=12
|
54
|
+
... )
|
68
55
|
"""
|
69
56
|
|
70
57
|
@staticmethod
|
71
|
-
|
72
|
-
|
73
|
-
Plots the maximum water surface elevation per cell.
|
74
|
-
|
75
|
-
Parameters:
|
76
|
-
- max_ws_df: DataFrame containing merged data with coordinates and max water surface.
|
77
|
-
|
78
|
-
Returns:
|
79
|
-
- None
|
80
|
-
"""
|
81
|
-
# Extract x and y coordinates from the geometry column
|
82
|
-
max_ws_df['x'] = max_ws_df['geometry'].apply(lambda geom: geom.x if geom is not None else None)
|
83
|
-
max_ws_df['y'] = max_ws_df['geometry'].apply(lambda geom: geom.y if geom is not None else None)
|
84
|
-
|
85
|
-
# Check if 'x' and 'y' columns exist in max_ws_df
|
86
|
-
if 'x' not in max_ws_df.columns or 'y' not in max_ws_df.columns:
|
87
|
-
print("Error: 'x' or 'y' columns not found in the merged dataframe.")
|
88
|
-
print("Available columns:", max_ws_df.columns.tolist())
|
89
|
-
return
|
90
|
-
|
91
|
-
# Create the plot
|
92
|
-
fig, ax = plt.subplots(figsize=(12, 8))
|
93
|
-
scatter = ax.scatter(max_ws_df['x'], max_ws_df['y'], c=max_ws_df['maximum_water_surface'], cmap='viridis', s=10)
|
94
|
-
|
95
|
-
# Customize the plot
|
96
|
-
ax.set_title('Max Water Surface per Cell')
|
97
|
-
ax.set_xlabel('X Coordinate')
|
98
|
-
ax.set_ylabel('Y Coordinate')
|
99
|
-
plt.colorbar(scatter, label='Max Water Surface (ft)')
|
100
|
-
|
101
|
-
# Add grid lines
|
102
|
-
ax.grid(True, linestyle='--', alpha=0.7)
|
103
|
-
|
104
|
-
# Increase font size for better readability
|
105
|
-
plt.rcParams.update({'font.size': 12})
|
106
|
-
|
107
|
-
# Adjust layout to prevent cutting off labels
|
108
|
-
plt.tight_layout()
|
109
|
-
|
110
|
-
# Show the plot
|
111
|
-
plt.show()
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
@staticmethod
|
117
|
-
def plot_max_wsel_time(max_ws_df: pd.DataFrame) -> None:
|
118
|
-
"""
|
119
|
-
Plots the time of the maximum water surface elevation (WSEL) per cell.
|
120
|
-
|
121
|
-
Parameters:
|
122
|
-
- max_ws_df: DataFrame containing merged data with coordinates and max water surface.
|
123
|
-
|
124
|
-
Returns:
|
125
|
-
- None
|
126
|
-
"""
|
127
|
-
max_ws_df['max_wsel_time'] = pd.to_datetime(max_ws_df['maximum_water_surface_time'])
|
128
|
-
HdfFluvialPluvial._extract_coordinates(max_ws_df)
|
129
|
-
|
130
|
-
if 'x' not in max_ws_df.columns or 'y' not in max_ws_df.columns:
|
131
|
-
raise ValueError("x and y coordinates are missing from the DataFrame. Make sure the 'face_point' column exists and contains valid coordinate data.")
|
132
|
-
|
133
|
-
fig, ax = plt.subplots(figsize=(12, 8))
|
134
|
-
|
135
|
-
min_time = max_ws_df['max_wsel_time'].min()
|
136
|
-
color_values = (max_ws_df['max_wsel_time'] - min_time).dt.total_seconds() / 3600
|
137
|
-
|
138
|
-
scatter = ax.scatter(max_ws_df['x'], max_ws_df['y'], c=color_values, cmap='viridis', s=10)
|
139
|
-
|
140
|
-
ax.set_title('Time of Maximum Water Surface Elevation per Cell')
|
141
|
-
ax.set_xlabel('X Coordinate')
|
142
|
-
ax.set_ylabel('Y Coordinate')
|
143
|
-
|
144
|
-
cbar = plt.colorbar(scatter)
|
145
|
-
cbar.set_label('Hours since simulation start')
|
146
|
-
cbar.set_ticks(range(0, int(color_values.max()) + 1, 6))
|
147
|
-
cbar.set_ticklabels([f'{h}h' for h in range(0, int(color_values.max()) + 1, 6)])
|
148
|
-
|
149
|
-
ax.grid(True, linestyle='--', alpha=0.7)
|
150
|
-
plt.rcParams.update({'font.size': 12})
|
151
|
-
plt.tight_layout()
|
152
|
-
plt.show()
|
153
|
-
|
154
|
-
HdfFluvialPluvial._print_max_wsel_info(max_ws_df, min_time)
|
155
|
-
|
156
|
-
@staticmethod
|
157
|
-
def plot_cell_polygons(cell_polygons_df: pd.DataFrame, projection: str) -> gpd.GeoDataFrame:
|
58
|
+
@standardize_input(file_type='plan_hdf')
|
59
|
+
def calculate_fluvial_pluvial_boundary(hdf_path: Path, delta_t: float = 12) -> gpd.GeoDataFrame:
|
158
60
|
"""
|
159
|
-
|
61
|
+
Calculate the fluvial-pluvial boundary based on cell polygons and maximum water surface elevation times.
|
160
62
|
|
161
63
|
Args:
|
162
|
-
|
163
|
-
|
64
|
+
hdf_path (Path): Path to the HEC-RAS plan HDF file
|
65
|
+
delta_t (float): Threshold time difference in hours. Cells with time differences
|
66
|
+
greater than this value are considered boundaries. Default is 12 hours.
|
164
67
|
|
165
68
|
Returns:
|
166
|
-
gpd.GeoDataFrame: GeoDataFrame containing the
|
69
|
+
gpd.GeoDataFrame: GeoDataFrame containing the fluvial-pluvial boundaries with:
|
70
|
+
- geometry: LineString features representing boundaries
|
71
|
+
- CRS: Coordinate reference system matching the input HDF file
|
72
|
+
|
73
|
+
Raises:
|
74
|
+
ValueError: If no cell polygons or maximum water surface data found in HDF file
|
75
|
+
Exception: If there are errors during boundary calculation
|
76
|
+
|
77
|
+
Note:
|
78
|
+
The returned boundaries represent locations where the timing of maximum water surface
|
79
|
+
elevation changes significantly (> delta_t), indicating potential transitions between
|
80
|
+
fluvial and pluvial flooding mechanisms.
|
167
81
|
"""
|
168
|
-
|
169
|
-
|
170
|
-
|
82
|
+
try:
|
83
|
+
# Get cell polygons from HdfMesh
|
84
|
+
logger.info("Getting cell polygons from HDF file...")
|
85
|
+
cell_polygons_gdf = HdfMesh.get_mesh_cell_polygons(hdf_path)
|
86
|
+
if cell_polygons_gdf.empty:
|
87
|
+
raise ValueError("No cell polygons found in HDF file")
|
88
|
+
|
89
|
+
# Get max water surface data from HdfResultsMesh
|
90
|
+
logger.info("Getting maximum water surface data from HDF file...")
|
91
|
+
max_ws_df = HdfResultsMesh.get_mesh_max_ws(hdf_path)
|
92
|
+
if max_ws_df.empty:
|
93
|
+
raise ValueError("No maximum water surface data found in HDF file")
|
94
|
+
|
95
|
+
# Convert timestamps using the renamed utility function
|
96
|
+
if 'maximum_water_surface_time' in max_ws_df.columns:
|
97
|
+
max_ws_df['maximum_water_surface_time'] = max_ws_df['maximum_water_surface_time'].apply(
|
98
|
+
lambda x: HdfUtils.parse_ras_datetime(x) if isinstance(x, str) else x
|
99
|
+
)
|
100
|
+
|
101
|
+
# Process cell adjacencies
|
102
|
+
cell_adjacency, common_edges = HdfFluvialPluvial._process_cell_adjacencies(cell_polygons_gdf)
|
103
|
+
|
104
|
+
# Get cell times from max_ws_df
|
105
|
+
cell_times = max_ws_df.set_index('cell_id')['maximum_water_surface_time'].to_dict()
|
106
|
+
|
107
|
+
# Identify boundary edges
|
108
|
+
boundary_edges = HdfFluvialPluvial._identify_boundary_edges(
|
109
|
+
cell_adjacency, common_edges, cell_times, delta_t
|
110
|
+
)
|
111
|
+
|
112
|
+
# Join adjacent LineStrings into simple LineStrings
|
113
|
+
joined_lines = []
|
114
|
+
current_line = []
|
115
|
+
|
116
|
+
for edge in boundary_edges:
|
117
|
+
if not current_line:
|
118
|
+
current_line.append(edge)
|
119
|
+
else:
|
120
|
+
if current_line[-1].coords[-1] == edge.coords[0]:
|
121
|
+
current_line.append(edge)
|
122
|
+
else:
|
123
|
+
joined_lines.append(LineString([point for line in current_line for point in line.coords]))
|
124
|
+
current_line = [edge]
|
125
|
+
|
126
|
+
if current_line:
|
127
|
+
joined_lines.append(LineString([point for line in current_line for point in line.coords]))
|
171
128
|
|
172
|
-
|
129
|
+
# Create final GeoDataFrame with CRS from cell_polygons_gdf
|
130
|
+
boundary_gdf = gpd.GeoDataFrame(
|
131
|
+
geometry=joined_lines,
|
132
|
+
crs=cell_polygons_gdf.crs
|
133
|
+
)
|
173
134
|
|
174
|
-
|
175
|
-
|
135
|
+
# Clean up intermediate dataframes
|
136
|
+
del cell_polygons_gdf
|
137
|
+
del max_ws_df
|
176
138
|
|
177
|
-
|
178
|
-
cell_polygons_gdf.plot(ax=ax, edgecolor='blue', facecolor='none')
|
179
|
-
ax.set_xlabel('X Coordinate')
|
180
|
-
ax.set_ylabel('Y Coordinate')
|
181
|
-
ax.set_title('2D Flow Area Cell Polygons')
|
182
|
-
ax.grid(True)
|
183
|
-
plt.tight_layout()
|
184
|
-
plt.show()
|
139
|
+
return boundary_gdf
|
185
140
|
|
186
|
-
|
141
|
+
except Exception as e:
|
142
|
+
logger.error(f"Error calculating fluvial-pluvial boundary: {str(e)}")
|
143
|
+
raise
|
187
144
|
|
188
145
|
@staticmethod
|
189
|
-
def
|
146
|
+
def _process_cell_adjacencies(cell_polygons_gdf: gpd.GeoDataFrame) -> Tuple[Dict[int, List[int]], Dict[int, Dict[int, LineString]]]:
|
190
147
|
"""
|
191
|
-
|
148
|
+
Process cell adjacencies and common edges using R-tree spatial indexing for efficiency.
|
192
149
|
|
193
150
|
Args:
|
194
|
-
cell_polygons_gdf (gpd.GeoDataFrame): GeoDataFrame containing cell polygons
|
195
|
-
|
196
|
-
delta_t (float): Threshold time difference in hours. Default is 12 hours.
|
151
|
+
cell_polygons_gdf (gpd.GeoDataFrame): GeoDataFrame containing 2D mesh cell polygons
|
152
|
+
with 'cell_id' and 'geometry' columns
|
197
153
|
|
198
154
|
Returns:
|
199
|
-
|
155
|
+
Tuple containing:
|
156
|
+
- Dict[int, List[int]]: Dictionary mapping cell IDs to lists of adjacent cell IDs
|
157
|
+
- Dict[int, Dict[int, LineString]]: Nested dictionary storing common edges between cells,
|
158
|
+
where common_edges[cell1][cell2] gives the shared boundary
|
159
|
+
|
160
|
+
Note:
|
161
|
+
Uses R-tree spatial indexing to efficiently identify potential neighboring cells
|
162
|
+
before performing more detailed geometric operations.
|
200
163
|
"""
|
201
|
-
cell_adjacency, common_edges = HdfFluvialPluvial._process_cell_adjacencies(cell_polygons_gdf)
|
202
|
-
cell_times = max_ws_df.set_index('cell_id')['max_wsel_time'].to_dict()
|
203
|
-
boundary_edges = HdfFluvialPluvial._identify_boundary_edges(cell_adjacency, common_edges, cell_times, delta_t)
|
204
|
-
|
205
|
-
# Join adjacent LineStrings into simple LineStrings
|
206
|
-
joined_lines = []
|
207
|
-
current_line = []
|
208
|
-
|
209
|
-
for edge in boundary_edges:
|
210
|
-
if not current_line:
|
211
|
-
current_line.append(edge)
|
212
|
-
else:
|
213
|
-
if current_line[-1].coords[-1] == edge.coords[0]: # Check if the last point of the current line matches the first point of the new edge
|
214
|
-
current_line.append(edge)
|
215
|
-
else:
|
216
|
-
# Create a simple LineString from the current line and reset
|
217
|
-
joined_lines.append(LineString([point for line in current_line for point in line.coords]))
|
218
|
-
current_line = [edge]
|
219
|
-
|
220
|
-
# Add the last collected line if exists
|
221
|
-
if current_line:
|
222
|
-
joined_lines.append(LineString([point for line in current_line for point in line.coords]))
|
223
|
-
|
224
|
-
boundary_gdf = gpd.GeoDataFrame(geometry=joined_lines, crs=cell_polygons_gdf.crs)
|
225
|
-
return boundary_gdf
|
226
|
-
|
227
|
-
@staticmethod
|
228
|
-
def _print_max_wsel_info(max_ws_df: pd.DataFrame, min_time: pd.Timestamp) -> None:
|
229
|
-
max_wsel_row = max_ws_df.loc[max_ws_df['maximum_water_surface'].idxmax()]
|
230
|
-
hours_since_start = (max_wsel_row['max_wsel_time'] - min_time).total_seconds() / 3600
|
231
|
-
print(f"\nOverall Maximum WSEL: {max_wsel_row['maximum_water_surface']:.2f} ft")
|
232
|
-
print(f"Time of Overall Maximum WSEL: {max_wsel_row['max_wsel_time']}")
|
233
|
-
print(f"Hours since simulation start: {hours_since_start:.2f} hours")
|
234
|
-
print(f"Location of Overall Maximum WSEL: X={max_wsel_row['x']}, Y={max_wsel_row['y']}")
|
235
|
-
|
236
|
-
@staticmethod
|
237
|
-
def _process_cell_adjacencies(cell_polygons_gdf: gpd.GeoDataFrame) -> Tuple[Dict[int, List[int]], Dict[int, Dict[int, LineString]]]:
|
238
|
-
"""
|
239
|
-
Process cell adjacencies and common edges using R-tree indexing.
|
240
|
-
"""
|
241
|
-
# Install rtree using pip install rtree if not already installed
|
242
164
|
from rtree import index
|
243
165
|
cell_adjacency = defaultdict(list)
|
244
166
|
common_edges = defaultdict(dict)
|
245
167
|
idx = index.Index()
|
168
|
+
|
246
169
|
for i, geom in enumerate(cell_polygons_gdf.geometry):
|
247
170
|
idx.insert(i, geom.bounds)
|
248
171
|
|
@@ -251,15 +174,15 @@ class HdfFluvialPluvial:
|
|
251
174
|
cell_id1 = row1['cell_id']
|
252
175
|
poly1 = row1['geometry']
|
253
176
|
potential_neighbors = list(idx.intersection(poly1.bounds))
|
254
|
-
|
177
|
+
|
255
178
|
for idx2 in potential_neighbors:
|
256
179
|
if idx1 >= idx2:
|
257
180
|
continue
|
258
|
-
|
181
|
+
|
259
182
|
row2 = cell_polygons_gdf.iloc[idx2]
|
260
183
|
cell_id2 = row2['cell_id']
|
261
184
|
poly2 = row2['geometry']
|
262
|
-
|
185
|
+
|
263
186
|
if poly1.touches(poly2):
|
264
187
|
intersection = poly1.intersection(poly2)
|
265
188
|
if isinstance(intersection, LineString):
|
@@ -267,60 +190,45 @@ class HdfFluvialPluvial:
|
|
267
190
|
cell_adjacency[cell_id2].append(cell_id1)
|
268
191
|
common_edges[cell_id1][cell_id2] = intersection
|
269
192
|
common_edges[cell_id2][cell_id1] = intersection
|
270
|
-
|
193
|
+
|
271
194
|
pbar.update(1)
|
272
|
-
|
195
|
+
|
273
196
|
return cell_adjacency, common_edges
|
274
197
|
|
275
198
|
@staticmethod
|
276
|
-
def _identify_boundary_edges(cell_adjacency: Dict[int, List[int]],
|
199
|
+
def _identify_boundary_edges(cell_adjacency: Dict[int, List[int]],
|
200
|
+
common_edges: Dict[int, Dict[int, LineString]],
|
201
|
+
cell_times: Dict[int, pd.Timestamp],
|
202
|
+
delta_t: float) -> List[LineString]:
|
203
|
+
"""
|
204
|
+
Identify boundary edges between cells with significant time differences.
|
205
|
+
|
206
|
+
Args:
|
207
|
+
cell_adjacency (Dict[int, List[int]]): Dictionary of cell adjacencies
|
208
|
+
common_edges (Dict[int, Dict[int, LineString]]): Dictionary of shared edges between cells
|
209
|
+
cell_times (Dict[int, pd.Timestamp]): Dictionary mapping cell IDs to their max WSE times
|
210
|
+
delta_t (float): Time threshold in hours
|
211
|
+
|
212
|
+
Returns:
|
213
|
+
List[LineString]: List of LineString geometries representing boundaries where
|
214
|
+
adjacent cells have time differences greater than delta_t
|
215
|
+
|
216
|
+
Note:
|
217
|
+
Boundaries are identified where the absolute time difference between adjacent
|
218
|
+
cells exceeds the specified delta_t threshold.
|
219
|
+
"""
|
277
220
|
boundary_edges = []
|
278
221
|
with tqdm(total=len(cell_adjacency), desc="Processing cell adjacencies") as pbar:
|
279
222
|
for cell_id, neighbors in cell_adjacency.items():
|
280
223
|
cell_time = cell_times[cell_id]
|
281
|
-
|
224
|
+
|
282
225
|
for neighbor_id in neighbors:
|
283
226
|
neighbor_time = cell_times[neighbor_id]
|
284
227
|
time_diff = abs((cell_time - neighbor_time).total_seconds() / 3600)
|
285
|
-
|
228
|
+
|
286
229
|
if time_diff >= delta_t:
|
287
230
|
boundary_edges.append(common_edges[cell_id][neighbor_id])
|
288
|
-
|
289
|
-
pbar.update(1)
|
290
|
-
return boundary_edges
|
291
|
-
|
292
|
-
@staticmethod
|
293
|
-
def _extract_coordinates(df: pd.DataFrame) -> None:
|
294
|
-
"""
|
295
|
-
Extract x and y coordinates from the 'face_point' column.
|
296
|
-
|
297
|
-
Parameters:
|
298
|
-
- df: DataFrame containing the 'face_point' column.
|
299
|
-
|
300
|
-
Returns:
|
301
|
-
- None (modifies the DataFrame in-place)
|
302
|
-
"""
|
303
|
-
if 'face_point' in df.columns:
|
304
|
-
df[['x', 'y']] = df['face_point'].str.strip('()').str.split(',', expand=True).astype(float)
|
305
|
-
else:
|
306
|
-
print("Warning: 'face_point' column not found in the DataFrame.")
|
307
|
-
|
308
|
-
@staticmethod
|
309
|
-
def _convert_to_geodataframe(df: pd.DataFrame, projection: str) -> gpd.GeoDataFrame:
|
310
|
-
"""
|
311
|
-
Convert a DataFrame to a GeoDataFrame.
|
312
|
-
|
313
|
-
Parameters:
|
314
|
-
- df: DataFrame containing 'geometry' column.
|
315
|
-
- projection: The coordinate reference system to assign to the GeoDataFrame.
|
316
|
-
|
317
|
-
Returns:
|
318
|
-
- GeoDataFrame with the specified projection.
|
319
|
-
"""
|
320
|
-
gdf = gpd.GeoDataFrame(df, geometry='geometry', crs=projection)
|
321
|
-
|
322
|
-
return gdf
|
323
|
-
|
324
|
-
|
325
231
|
|
232
|
+
pbar.update(1)
|
326
233
|
|
234
|
+
return boundary_edges
|