Skip to content

Commit

Permalink
feat: add FaultConnector
Browse files Browse the repository at this point in the history
  • Loading branch information
rabii-chaarani committed Nov 27, 2024
1 parent 840f135 commit 391ff45
Show file tree
Hide file tree
Showing 7 changed files with 3,922 additions and 390 deletions.
31 changes: 22 additions & 9 deletions LoopDataConverter/converters/ntgs_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,29 @@ def convert_fold_map(self):
to degrees.
'''
# # rename columns
# if "AxialPlaneDipDir" in self.raw_data[Datatype.FOLD].columns:
# self.raw_data[Datatype.FOLD] = self.raw_data[Datatype.FOLD].rename(columns={'AxialPlaneDipDir': 'AxPlDipDir'})
# if "AxialPlaneDip" in self.raw_data[Datatype.FOLD].columns:
# self.raw_data[Datatype.FOLD] = self.raw_data[Datatype.FOLD].rename(columns={'AxialPlaneDip': 'AxPlaneDip'})
# if "AxialPlane" in self.raw_data[Datatype.FOLD].columns:
# self.raw_data[Datatype.FOLD] = self.raw_data[Datatype.FOLD].rename(columns={'AxialPlane': 'AxPlDipDir'})
# if "AxialPla_1" in self.raw_data[Datatype.FOLD].columns:
# self.raw_data[Datatype.FOLD] = self.raw_data[Datatype.FOLD].rename(columns={'AxialPla_1': 'AxPlaneDip'})
# if "InterlimbA" in self.raw_data[Datatype.FOLD].columns:
# self.raw_data[Datatype.FOLD] = self.raw_data[Datatype.FOLD].rename(columns={'InterlimbA': 'Interlimb'})

# convert dip direction terms to degrees
self.raw_data[Datatype.FOLD]["AxPlaneDD"] = self.raw_data[Datatype.FOLD]["AxPlaneDD"].apply(
self.raw_data[Datatype.FOLD]["AxPlDipDir"] = self.raw_data[Datatype.FOLD]["AxPlDipDir"].apply(
lambda x: convert_dipdir_terms(x)
)

# convert dip terms to degrees
self.raw_data[Datatype.FOLD]["AxPlaneDip"] = self.raw_data[Datatype.FOLD][
"AxPlaneDip"
self.raw_data[Datatype.FOLD]["AxPlDip"] = self.raw_data[Datatype.FOLD][
"AxPlDip"
].apply(lambda x: convert_dip_terms(x, type="fold"))
# convert tightness terms to degrees
self.raw_data[Datatype.FOLD]["Interlimb"] = self.raw_data[Datatype.FOLD]["Interlimb"].apply(
self.raw_data[Datatype.FOLD]["IntlimbAng"] = self.raw_data[Datatype.FOLD]["IntlimbAng"].apply(
lambda x: convert_tightness_terms(x)
)

Expand All @@ -80,8 +93,8 @@ def convert_fault_map(self):

# convert dip direction terms to degrees

self.raw_data[Datatype.FAULT]["DipDirectn"] = self.raw_data[Datatype.FAULT][
"DipDirectn"
self.raw_data[Datatype.FAULT]["DipDir"] = self.raw_data[Datatype.FAULT][
"DipDir"
].apply(lambda x: convert_dipdir_terms(x))
# convert dip terms to degrees
self.raw_data[Datatype.FAULT]["Dip"] = self.raw_data[Datatype.FAULT]["Dip"].apply(
Expand All @@ -100,19 +113,19 @@ def convert_structure_map(self):
'''
# select any rows that has a dip value of -99 and have any estimated dip value
condition = (self.raw_data[Datatype.STRUCTURE]["Dip"] == -99) & (
self.raw_data[Datatype.STRUCTURE]["DipEstimte"] != "NaN"
self.raw_data[Datatype.STRUCTURE]["DipEst"] != "NaN"
)

# convert dip estimate to float (average of the range)
self.raw_data[Datatype.STRUCTURE].loc[condition, "Dip"] = (
self.raw_data[Datatype.STRUCTURE]
.loc[condition, "DipEstimte"]
.loc[condition, "DipEst"]
.apply(lambda x: convert_dip_terms(x, type="structure"))
)

# discard any rows that has a dip value of -99 and does not have any estimated dip value
condition = (self.raw_data[Datatype.STRUCTURE]["Dip"] == -99) & (
self.raw_data[Datatype.STRUCTURE]["DipEstimte"] == "NaN"
self.raw_data[Datatype.STRUCTURE]["DipEst"] == "NaN"
)
self.raw_data[Datatype.STRUCTURE] = self.raw_data[Datatype.STRUCTURE][~condition]

Expand Down
14 changes: 7 additions & 7 deletions LoopDataConverter/fields/_ntgs/_ntgs_config.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
class NtgsConfig:
def __init__(self):
self.fold_config = {
"structtype_column": "FoldEvent",
"fold_text": "FeatDesc",
"structtype_column": "FoldType",
"fold_text": "'Anticline','Syncline','Antiform','Synform','Monocline','Monoform','Neutral','Fold axis','Overturned syncline'",
"description_column": "Desc",
"synform_text": "FoldType",
"foldname_column": "FoldName",
Expand All @@ -15,7 +15,7 @@ def __init__(self):

self.fault_config = {
"structtype_column": "FaultType",
"fault_text": "'Normal', 'Reverse', 'Shear zone', 'Strike-slip', 'Thrust', 'Unknown'",
"fault_text": "Thrust", #, Reverse,Normal,Shear zone,Strike-slip,Thrust,Unknown",
"dip_null_value": "-999",
"dipdir_flag": "num",
"dipdir_column": "DipDir",
Expand All @@ -26,7 +26,7 @@ def __init__(self):
"displacement_column": "Displace",
"displacement_text": "'1m-100m', '100m-1km', '1km-5km', '>5km'",
"fault_length_column": "FaultLen",
"fault_length_text": "'Small (0-5km)', 'Medium (5-30km)', 'Large (30-100km)', 'Regional (>100km)', 'Unclassified'",
"fault_length_text": "Small (0-5km),Medium (5-30km),Large (30-100km),Regional (>100km),Unclassified",
"name_column": "FaultName",
"objectid_column": "OBJECTID",
"interp_source_column": "InterpSrce",
Expand All @@ -50,12 +50,12 @@ def __init__(self):
}

self.structure_config = {
"orientation_type": "DipDirectn",
"dipdir_column": "DipDirectn",
"orientation_type": "dip direction",
"dipdir_column": "DipDir",
"dip_column": "Dip",
"description_column": "FeatDesc",
"bedding_text": "ObsType",
"overturned_column": "FeatDesc",
"overturned_column": "Desc",
"overturned_text": "overturned",
"objectid_column": "ObjectID",
"interp_source_column": "InterpSrce",
Expand Down
Empty file.
67 changes: 67 additions & 0 deletions LoopDataConverter/geometry_correction/fault_geometry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# internal imports
from ..utils import calculate_vector_along_line, calculate_angle

#external imports
import numpy
import geopandas
import shapely


class FaultConnector:


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."""

self.processed_data = self._data.copy()
i = 0

while i < len(self.processed_data):
j = i + 1
while j < len(self.processed_data):
line1 = self.processed_data.iloc[i].geometry
line2 = self.processed_data.iloc[j].geometry

# Find the intersection
intersection = line1.intersection(line2)
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])

# Compute vectors aligned with each LineString
vector1 = calculate_vector_along_line(line1, intersection_point)
vector2 = calculate_vector_along_line(line2, intersection_point)

# Ensure non-zero vectors before proceeding
if numpy.linalg.norm(vector1) == 0 or numpy.linalg.norm(vector2) == 0:
j += 1
continue

# Calculate the angle between the vectors
angle = calculate_angle(vector1, vector2)

# If the angle is below 20 degrees, merge the lines
if angle < 20:
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)

# Restart processing for safety (to avoid index shifts)
i = 0
j = 0
else:
j += 1 # Move to the next line

i += 1


6 changes: 6 additions & 0 deletions LoopDataConverter/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@
convert_tightness_terms,
convert_displacement_terms,
)

from .geometry import (
calculate_angle,
calculate_vector_along_line,
unit_vector
)
38 changes: 38 additions & 0 deletions LoopDataConverter/utils/geometry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import numpy
import shapely


def unit_vector(vector):
"""Returns the unit vector of the vector."""
return vector / numpy.linalg.norm(vector)


def calculate_angle(v1, v2):
"""Returns the angle in degrees between 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 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)))

# Get the two closest segments of the line around the intersection point
coords = list(line.coords)
for i in range(len(coords) - 1):
start, end = numpy.array(coords[i], dtype=object), numpy.array(coords[i + 1], dtype=object)
if (
shapely.geometry.Point(start).distance(proj_point)
+ shapely.geometry.Point(end).distance(proj_point)
) == shapely.geometry.Point(start).distance(shapely.geometry.Point(end)):
# 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])
Loading

0 comments on commit 391ff45

Please sign in to comment.