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.
@@ -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, MultiLineString
17
+ from shapely.geometry import LineString, MultiLineString # Added MultiLineString import
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,312 +31,279 @@ 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 plotting maximum water surface elevations,
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 Features:
23
- - Plotting maximum water surface elevations and their timing
24
- - Extracting and visualizing 2D flow area cell polygons
25
- - Calculating and visualizing fluvial-pluvial boundaries
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
- 1. For plotting maximum water surface:
29
- - Use HdfResultsMesh.mesh_max_ws(hdf_path) to get max_ws_df
30
- - Use HdfResultsMesh.mesh_timeseries_output(hdf_path, mesh_name, 'water_surface')
31
- to get time series data
32
-
33
- 2. For extracting cell polygons:
34
- - Use HdfMesh.mesh_cell_polygons(geom_hdf_path) to get cell_polygons_df
35
- - Use HdfUtils.projection(hdf_path) to get the projection
36
-
37
- 3. For calculating fluvial-pluvial boundary:
38
- - Requires cell_polygons_gdf (from step 2)
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
-
70
- @staticmethod
71
- def plot_max_water_surface(max_ws_df):
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
-
56
+ def __init__(self):
57
+ self.logger = get_logger(__name__) # Initialize logger with module name
113
58
 
114
-
115
-
116
59
  @staticmethod
117
- def plot_max_wsel_time(max_ws_df: pd.DataFrame) -> None:
60
+ @standardize_input(file_type='plan_hdf')
61
+ def calculate_fluvial_pluvial_boundary(hdf_path: Path, delta_t: float = 12) -> gpd.GeoDataFrame:
118
62
  """
119
- Plots the time of the maximum water surface elevation (WSEL) per cell.
63
+ Calculate the fluvial-pluvial boundary based on cell polygons and maximum water surface elevation times.
120
64
 
121
- Parameters:
122
- - max_ws_df: DataFrame containing merged data with coordinates and max water surface.
65
+ Args:
66
+ hdf_path (Path): Path to the HEC-RAS plan HDF file
67
+ delta_t (float): Threshold time difference in hours. Cells with time differences
68
+ greater than this value are considered boundaries. Default is 12 hours.
123
69
 
124
70
  Returns:
125
- - None
71
+ gpd.GeoDataFrame: GeoDataFrame containing the fluvial-pluvial boundaries with:
72
+ - geometry: LineString features representing boundaries
73
+ - CRS: Coordinate reference system matching the input HDF file
74
+
75
+ Raises:
76
+ ValueError: If no cell polygons or maximum water surface data found in HDF file
77
+ Exception: If there are errors during boundary calculation
78
+
79
+ Note:
80
+ The returned boundaries represent locations where the timing of maximum water surface
81
+ elevation changes significantly (> delta_t), indicating potential transitions between
82
+ fluvial and pluvial flooding mechanisms.
126
83
  """
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
-
84
+ try:
85
+ # Get cell polygons from HdfMesh
86
+ logger.info("Getting cell polygons from HDF file...")
87
+ cell_polygons_gdf = HdfMesh.get_mesh_cell_polygons(hdf_path)
88
+ if cell_polygons_gdf.empty:
89
+ raise ValueError("No cell polygons found in HDF file")
90
+
91
+ # Get max water surface data from HdfResultsMesh
92
+ logger.info("Getting maximum water surface data from HDF file...")
93
+ max_ws_df = HdfResultsMesh.get_mesh_max_ws(hdf_path)
94
+ if max_ws_df.empty:
95
+ raise ValueError("No maximum water surface data found in HDF file")
96
+
97
+ # Convert timestamps using the renamed utility function
98
+ logger.info("Converting maximum water surface timestamps...")
99
+ if 'maximum_water_surface_time' in max_ws_df.columns:
100
+ max_ws_df['maximum_water_surface_time'] = max_ws_df['maximum_water_surface_time'].apply(
101
+ lambda x: HdfUtils.parse_ras_datetime(x) if isinstance(x, str) else x
102
+ )
103
+
104
+ # Process cell adjacencies
105
+ logger.info("Processing cell adjacencies...")
106
+ cell_adjacency, common_edges = HdfFluvialPluvial._process_cell_adjacencies(cell_polygons_gdf)
107
+
108
+ # Get cell times from max_ws_df
109
+ logger.info("Extracting cell times from maximum water surface data...")
110
+ cell_times = max_ws_df.set_index('cell_id')['maximum_water_surface_time'].to_dict()
111
+
112
+ # Identify boundary edges
113
+ logger.info("Identifying boundary edges...")
114
+ boundary_edges = HdfFluvialPluvial._identify_boundary_edges(
115
+ cell_adjacency, common_edges, cell_times, delta_t
116
+ )
117
+
118
+ # Join adjacent LineStrings into simple LineStrings
119
+ logger.info("Joining adjacent LineStrings into simple LineStrings...")
120
+ joined_lines = []
121
+
122
+ def get_coords(geom):
123
+ """Helper function to get coordinates from either LineString or MultiLineString"""
124
+ if isinstance(geom, LineString):
125
+ return list(geom.coords)
126
+ elif isinstance(geom, MultiLineString):
127
+ return list(geom.geoms[0].coords)
128
+ return None
129
+
130
+ # Create a dictionary to store start and end points for each line
131
+ line_endpoints = {}
132
+ for i, edge in enumerate(boundary_edges):
133
+ coords = get_coords(edge)
134
+ if coords:
135
+ line_endpoints[i] = (coords[0], coords[-1])
136
+
137
+ # Process lines in order
138
+ used_indices = set()
139
+ while len(used_indices) < len(boundary_edges):
140
+ current_line = []
141
+ current_points = []
142
+
143
+ # Find a new starting line if needed
144
+ for i in range(len(boundary_edges)):
145
+ if i not in used_indices:
146
+ current_line.append(boundary_edges[i])
147
+ coords = get_coords(boundary_edges[i])
148
+ if coords:
149
+ current_points.extend(coords)
150
+ used_indices.add(i)
151
+ break
152
+
153
+ # Continue adding connected lines
154
+ while True:
155
+ found_next = False
156
+ current_end = current_points[-1] if current_points else None
157
+
158
+ # Look for the next connected line
159
+ for i, (start, end) in line_endpoints.items():
160
+ if i not in used_indices and current_end:
161
+ if start == current_end:
162
+ # Add line in forward direction
163
+ coords = get_coords(boundary_edges[i])
164
+ if coords:
165
+ current_points.extend(coords[1:]) # Skip first point to avoid duplication
166
+ current_line.append(boundary_edges[i])
167
+ used_indices.add(i)
168
+ found_next = True
169
+ break
170
+ elif end == current_end:
171
+ # Add line in reverse direction
172
+ coords = get_coords(boundary_edges[i])
173
+ if coords:
174
+ current_points.extend(reversed(coords[:-1])) # Skip last point to avoid duplication
175
+ current_line.append(boundary_edges[i])
176
+ used_indices.add(i)
177
+ found_next = True
178
+ break
179
+
180
+ if not found_next:
181
+ break
182
+
183
+ # Create a single LineString from the collected points
184
+ if current_points:
185
+ joined_lines.append(LineString(current_points))
186
+
187
+ # Create final GeoDataFrame with CRS from cell_polygons_gdf
188
+ logger.info("Creating final GeoDataFrame for boundaries...")
189
+ boundary_gdf = gpd.GeoDataFrame(
190
+ geometry=joined_lines,
191
+ crs=cell_polygons_gdf.crs
192
+ )
193
+
194
+ # Clean up intermediate dataframes
195
+ logger.info("Cleaning up intermediate dataframes...")
196
+ del cell_polygons_gdf
197
+ del max_ws_df
198
+
199
+ logger.info("Fluvial-pluvial boundary calculation completed successfully.")
200
+ return boundary_gdf
201
+
202
+ except Exception as e:
203
+ self.logger.error(f"Error calculating fluvial-pluvial boundary: {str(e)}")
204
+ return None
205
+
206
+
156
207
  @staticmethod
157
- def plot_cell_polygons(cell_polygons_df: pd.DataFrame, projection: str) -> gpd.GeoDataFrame:
208
+ def _process_cell_adjacencies(cell_polygons_gdf: gpd.GeoDataFrame) -> Tuple[Dict[int, List[int]], Dict[int, Dict[int, LineString]]]:
158
209
  """
159
- Plots the cell polygons from the provided DataFrame and returns the GeoDataFrame.
160
-
210
+ Optimized method to process cell adjacencies by extracting shared edges directly.
211
+
161
212
  Args:
162
- cell_polygons_df (pd.DataFrame): DataFrame containing cell polygons.
163
- projection (str): The coordinate reference system to assign to the GeoDataFrame.
213
+ cell_polygons_gdf (gpd.GeoDataFrame): GeoDataFrame containing 2D mesh cell polygons
214
+ with 'cell_id' and 'geometry' columns.
164
215
 
165
216
  Returns:
166
- gpd.GeoDataFrame: GeoDataFrame containing the cell polygons.
217
+ Tuple containing:
218
+ - Dict[int, List[int]]: Dictionary mapping cell IDs to lists of adjacent cell IDs.
219
+ - Dict[int, Dict[int, LineString]]: Nested dictionary storing common edges between cells,
220
+ where common_edges[cell1][cell2] gives the shared boundary.
167
221
  """
168
- if cell_polygons_df.empty:
169
- print("No Cell Polygons found.")
170
- return None
171
-
172
- cell_polygons_gdf = HdfFluvialPluvial._convert_to_geodataframe(cell_polygons_df, projection)
173
-
174
- print("Cell Polygons CRS:", cell_polygons_gdf.crs)
175
- display(cell_polygons_gdf.head())
176
-
177
- fig, ax = plt.subplots(figsize=(12, 8))
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()
222
+ cell_adjacency = defaultdict(list)
223
+ common_edges = defaultdict(dict)
185
224
 
186
- return cell_polygons_gdf
225
+ # Build an edge to cells mapping
226
+ edge_to_cells = defaultdict(set)
227
+
228
+ # Function to generate edge keys
229
+ def edge_key(coords1, coords2, precision=8):
230
+ # Round coordinates
231
+ coords1 = tuple(round(coord, precision) for coord in coords1)
232
+ coords2 = tuple(round(coord, precision) for coord in coords2)
233
+ # Create sorted key to handle edge direction
234
+ return tuple(sorted([coords1, coords2]))
235
+
236
+ # For each polygon, extract edges
237
+ for idx, row in cell_polygons_gdf.iterrows():
238
+ cell_id = row['cell_id']
239
+ geom = row['geometry']
240
+ if geom.is_empty or not geom.is_valid:
241
+ continue
242
+ # Get exterior coordinates
243
+ coords = list(geom.exterior.coords)
244
+ num_coords = len(coords)
245
+ for i in range(num_coords - 1):
246
+ coord1 = coords[i]
247
+ coord2 = coords[i + 1]
248
+ key = edge_key(coord1, coord2)
249
+ edge_to_cells[key].add(cell_id)
250
+
251
+ # Now, process edge_to_cells to build adjacency
252
+ for edge, cells in edge_to_cells.items():
253
+ cells = list(cells)
254
+ if len(cells) >= 2:
255
+ # For all pairs of cells sharing this edge
256
+ for i in range(len(cells)):
257
+ for j in range(i + 1, len(cells)):
258
+ cell1 = cells[i]
259
+ cell2 = cells[j]
260
+ # Update adjacency
261
+ if cell2 not in cell_adjacency[cell1]:
262
+ cell_adjacency[cell1].append(cell2)
263
+ if cell1 not in cell_adjacency[cell2]:
264
+ cell_adjacency[cell2].append(cell1)
265
+ # Store common edge
266
+ common_edge = LineString([edge[0], edge[1]])
267
+ common_edges[cell1][cell2] = common_edge
268
+ common_edges[cell2][cell1] = common_edge
269
+
270
+ logger.info("Cell adjacencies processed successfully.")
271
+ return cell_adjacency, common_edges
187
272
 
188
273
  @staticmethod
189
- def calculate_fluvial_pluvial_boundary(cell_polygons_gdf: gpd.GeoDataFrame, max_ws_df: pd.DataFrame, delta_t: float = 12) -> gpd.GeoDataFrame:
274
+ def _identify_boundary_edges(cell_adjacency: Dict[int, List[int]],
275
+ common_edges: Dict[int, Dict[int, LineString]],
276
+ cell_times: Dict[int, pd.Timestamp],
277
+ delta_t: float) -> List[LineString]:
190
278
  """
191
- Calculate the fluvial-pluvial boundary based on cell polygons and maximum water surface elevation times.
279
+ Identify boundary edges between cells with significant time differences.
192
280
 
193
281
  Args:
194
- cell_polygons_gdf (gpd.GeoDataFrame): GeoDataFrame containing cell polygons with 'cell_id' and 'geometry' columns.
195
- max_ws_df (pd.DataFrame): DataFrame containing 'cell_id' and 'max_wsel_time' columns.
196
- delta_t (float): Threshold time difference in hours. Default is 12 hours.
282
+ cell_adjacency (Dict[int, List[int]]): Dictionary of cell adjacencies
283
+ common_edges (Dict[int, Dict[int, LineString]]): Dictionary of shared edges between cells
284
+ cell_times (Dict[int, pd.Timestamp]): Dictionary mapping cell IDs to their max WSE times
285
+ delta_t (float): Time threshold in hours
197
286
 
198
287
  Returns:
199
- gpd.GeoDataFrame: GeoDataFrame containing the fluvial-pluvial boundary as simple LineStrings.
200
- """
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']}")
288
+ List[LineString]: List of LineString geometries representing boundaries where
289
+ adjacent cells have time differences greater than delta_t
235
290
 
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.
291
+ Note:
292
+ Boundaries are identified where the absolute time difference between adjacent
293
+ cells exceeds the specified delta_t threshold.
240
294
  """
241
- # Install rtree using pip install rtree if not already installed
242
- from rtree import index
243
- cell_adjacency = defaultdict(list)
244
- common_edges = defaultdict(dict)
245
- idx = index.Index()
246
- for i, geom in enumerate(cell_polygons_gdf.geometry):
247
- idx.insert(i, geom.bounds)
248
-
249
- with tqdm(total=len(cell_polygons_gdf), desc="Processing cell adjacencies") as pbar:
250
- for idx1, row1 in cell_polygons_gdf.iterrows():
251
- cell_id1 = row1['cell_id']
252
- poly1 = row1['geometry']
253
- potential_neighbors = list(idx.intersection(poly1.bounds))
254
-
255
- for idx2 in potential_neighbors:
256
- if idx1 >= idx2:
257
- continue
258
-
259
- row2 = cell_polygons_gdf.iloc[idx2]
260
- cell_id2 = row2['cell_id']
261
- poly2 = row2['geometry']
262
-
263
- if poly1.touches(poly2):
264
- intersection = poly1.intersection(poly2)
265
- if isinstance(intersection, LineString):
266
- cell_adjacency[cell_id1].append(cell_id2)
267
- cell_adjacency[cell_id2].append(cell_id1)
268
- common_edges[cell_id1][cell_id2] = intersection
269
- common_edges[cell_id2][cell_id1] = intersection
270
-
271
- pbar.update(1)
272
-
273
- return cell_adjacency, common_edges
274
-
275
- @staticmethod
276
- def _identify_boundary_edges(cell_adjacency: Dict[int, List[int]], common_edges: Dict[int, Dict[int, LineString]], cell_times: Dict[int, pd.Timestamp], delta_t: float) -> List[LineString]:
277
295
  boundary_edges = []
278
296
  with tqdm(total=len(cell_adjacency), desc="Processing cell adjacencies") as pbar:
279
297
  for cell_id, neighbors in cell_adjacency.items():
280
298
  cell_time = cell_times[cell_id]
281
-
299
+
282
300
  for neighbor_id in neighbors:
283
301
  neighbor_time = cell_times[neighbor_id]
284
302
  time_diff = abs((cell_time - neighbor_time).total_seconds() / 3600)
285
-
303
+
286
304
  if time_diff >= delta_t:
287
305
  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
306
 
307
+ pbar.update(1)
326
308
 
309
+ return boundary_edges