Skip to content

Commit

Permalink
chore: added docstrings and logging
Browse files Browse the repository at this point in the history
  • Loading branch information
rabii-chaarani committed Nov 27, 2024
1 parent 391ff45 commit 9ae4686
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 19 deletions.
1 change: 1 addition & 0 deletions LoopDataConverter/geometry_correction/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .fault_geometry import FaultConnector
60 changes: 48 additions & 12 deletions LoopDataConverter/geometry_correction/fault_geometry.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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])

Expand All @@ -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
Expand All @@ -63,5 +101,3 @@ def connect_faults(self):
j += 1 # Move to the next line

i += 1


44 changes: 37 additions & 7 deletions LoopDataConverter/utils/geometry.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,53 @@
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)))
return angle


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)))

Expand All @@ -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])

0 comments on commit 9ae4686

Please sign in to comment.