From 9ae4686f45c8100580598affffcf0e330d3a5b89 Mon Sep 17 00:00:00 2001 From: rabii-chaarani Date: Wed, 27 Nov 2024 12:47:05 +0930 Subject: [PATCH] chore: added docstrings and logging --- .../geometry_correction/__init__.py | 1 + .../geometry_correction/fault_geometry.py | 60 +++++++++++++++---- LoopDataConverter/utils/geometry.py | 44 +++++++++++--- 3 files changed, 86 insertions(+), 19 deletions(-) diff --git a/LoopDataConverter/geometry_correction/__init__.py b/LoopDataConverter/geometry_correction/__init__.py index e69de29..644fc3c 100644 --- a/LoopDataConverter/geometry_correction/__init__.py +++ b/LoopDataConverter/geometry_correction/__init__.py @@ -0,0 +1 @@ +from .fault_geometry import FaultConnector \ No newline at end of file diff --git a/LoopDataConverter/geometry_correction/fault_geometry.py b/LoopDataConverter/geometry_correction/fault_geometry.py index c7aab3e..4a0563b 100644 --- a/LoopDataConverter/geometry_correction/fault_geometry.py +++ b/LoopDataConverter/geometry_correction/fault_geometry.py @@ -1,23 +1,57 @@ # internal imports from ..utils import calculate_vector_along_line, calculate_angle -#external imports +# external imports import numpy import geopandas import shapely class FaultConnector: - - + """ + A class to connect and merge faults in a GeoDataFrame based on an angle criterion. + + _data (geopandas.GeoDataFrame): The original GeoDataFrame containing fault data. + processed_data (geopandas.GeoDataFrame or None): A copy of the original data that is processed + and modified within the connect_faults method. + + Methods: + connect_faults() + + """ + def __init__(self, data: geopandas.GeoDataFrame): self._data = data.copy() self.processed_data = None - + def connect_faults(self): - - """Process the GeoDataFrame to merge faults based on the angle criterion.""" - + """ + Connects and merges faults in the GeoDataFrame based on an angle criterion. + This method processes the GeoDataFrame to merge faults if the angle between + their vectors at the intersection point is below a specified threshold (20 degrees). + The method iterates through the GeoDataFrame, checks for intersections between + LineStrings, calculates the angle between the vectors at the intersection point, + and merges the lines if the angle criterion is met. + Attributes: + processed_data (GeoDataFrame): A copy of the original data that is processed + and modified within the method. + Steps: + 1. Iterate through each pair of LineStrings in the GeoDataFrame. + 2. Check for intersections between the LineStrings. + 3. If an intersection is found and it is a point, calculate the vectors + aligned with each LineString at the intersection point. + 4. Compute the angle between the vectors. + 5. If the angle is below 20 degrees, merge the LineStrings into a single + LineString and update the GeoDataFrame. + 6. Restart the process to ensure all possible merges are considered. + Note: + The method ensures non-zero vectors before proceeding with angle calculation + to avoid division by zero errors. + Raises: + ValueError: If the input data is not a GeoDataFrame or if the geometries + are not LineStrings. + """ + self.processed_data = self._data.copy() i = 0 @@ -32,7 +66,7 @@ def connect_faults(self): if intersection.is_empty or not intersection.geom_type == "Point": j += 1 continue # Skip if no intersection or if it's not a point - + # Get the intersection point intersection_point = numpy.array(intersection.coords[0]) @@ -50,11 +84,15 @@ def connect_faults(self): # If the angle is below 20 degrees, merge the lines if angle < 20: - merged_line = shapely.geometry.LineString(list(line1.coords) + list(line2.coords)) + merged_line = shapely.geometry.LineString( + list(line1.coords) + list(line2.coords) + ) # Add the merged line and remove the old ones self.processed_data = self.processed_data.drop([i, j]).reset_index(drop=True) - self.processed_data = self.processed_data.append({'geometry': merged_line}, ignore_index=True) + self.processed_data = self.processed_data.append( + {"geometry": merged_line}, ignore_index=True + ) # Restart processing for safety (to avoid index shifts) i = 0 @@ -63,5 +101,3 @@ def connect_faults(self): j += 1 # Move to the next line i += 1 - - \ No newline at end of file diff --git a/LoopDataConverter/utils/geometry.py b/LoopDataConverter/utils/geometry.py index 272affb..6c5d1f5 100644 --- a/LoopDataConverter/utils/geometry.py +++ b/LoopDataConverter/utils/geometry.py @@ -1,14 +1,35 @@ import numpy import shapely +import logging + +logging.basicConfig(level=logging.WARNING) def unit_vector(vector): - """Returns the unit vector of the vector.""" + """ + Returns the unit vector of the given vector. + + Parameters: + vector (numpy.ndarray): A numpy array representing the vector. + + Returns: + numpy.ndarray: The unit vector of the input vector. + """ return vector / numpy.linalg.norm(vector) def calculate_angle(v1, v2): - """Returns the angle in degrees between two vectors.""" + """ + Returns the angle in degrees between two vectors. + + Parameters: + v1 (array-like): The first vector. + v2 (array-like): The second vector. + + Returns: + float: The angle in degrees between the two vectors. + """ + v1_u = unit_vector(v1) v2_u = unit_vector(v2) angle = numpy.degrees(numpy.arccos(numpy.clip(numpy.dot(v1_u, v2_u), -1.0, 1.0))) @@ -16,9 +37,17 @@ def calculate_angle(v1, v2): def calculate_vector_along_line(line, intersection_point): + """Computes a unit vector along the provided LineString, aligned with its direction. + + Parameters: + line (shapely.geometry.LineString): The LineString object representing the line. + intersection_point (tuple or list): Coordinates (x, y) of the intersection point. + + Returns: + numpy.ndarray: A unit vector (array of floats) in the direction of the line. + Returns [0, 0, 0] if no valid segment is found. """ - Computes a unit vector along the LineString that is aligned with its direction. - """ + # Project the intersection point onto the line to find its position along the line proj_point = line.interpolate(line.project(shapely.geometry.Point(intersection_point))) @@ -33,6 +62,7 @@ def calculate_vector_along_line(line, intersection_point): # Found the segment containing the projection point segment_vector = end - start return unit_vector(segment_vector) - - # Fallback: Return zero vector if no segment is found (shouldn't happen) - return numpy.array([0, 0, 0]) + else: + logging.warning("FaultConnector: No segment found for the intersection point.") + # Fallback: Return zero vector if no segment is found (shouldn't happen) + return numpy.array([0, 0, 0])