diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 2e0c8f4..01538b1 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -27,7 +27,7 @@ jobs: fail-fast: false matrix: os: ${{ fromJSON(vars.BUILD_OS)}} - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 - uses: conda-incubator/setup-miniconda@v3 @@ -37,7 +37,7 @@ jobs: - name: Installing dependencies shell: bash -l {0} run: | - conda install -c conda-forge numpy scipy pytest dill beartype pandas geopandas shapely validators sqlite -y + conda install -c conda-forge 'numpy>=2.1.0' scipy pytest dill beartype pandas geopandas shapely validators sqlite -y - name: Building and install shell: bash -l {0} run: | @@ -49,32 +49,27 @@ jobs: release-please: runs-on: ubuntu-latest - if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/dev' + if: github.ref == 'refs/heads/master' steps: - uses: GoogleCloudPlatform/release-please-action@v4 id: release with: release-type: python - package-name: LoopDataConverter - version-file: LoopDataConverter/version.py + - name: Debug release_created output + run: | + echo "Release created: ${{ steps.release.outputs.release_created }}" outputs: release_created: ${{ steps.release.outputs.release_created }} # if a release is created then run the deploy scripts for github.io, conda, pypi and docker - conda-deploy: - name: Building conda package for python ${{ matrix.os }}) + conda-build: + name: Building conda package for python needs: "continuous-integration" - runs-on: ${{matrix.os}} - strategy: - fail-fast: false - matrix: - os: ["ubuntu-latest"] - python-version: ["3.9", "3.10", "3.11", "3.12"] + runs-on: ubuntu-latest steps: - uses: conda-incubator/setup-miniconda@v3 with: auto-update-conda: true - python-version: ${{ matrix.python-version }} - uses: actions/checkout@v4 - name: update submodules @@ -86,16 +81,15 @@ jobs: ANACONDA_API_TOKEN: ${{ secrets.ANACONDA_TOKEN }} shell: bash -l {0} run: | - conda install -c conda-forge conda-build pytest numpy scipy pandas geopandas sqlite shapely validators anaconda-client -y - conda build -c anaconda -c conda-forge --output-folder conda conda - conda convert -p all conda/linux-64/*.tar.bz2 -f -o conda - conda install anaconda-client -y + conda config --set solver libmamba + conda install -c conda-forge conda-build pytest 'numpy>=2.1.0' scipy pandas geopandas sqlite shapely validators anaconda-client conda-libmamba-solver -y + conda build -c conda-forge --output-folder conda conda + - name: upload artifacts uses: actions/upload-artifact@v4 with: - name: conda-${{ matrix.os }}-${{ matrix.python-version }} + name: conda path: conda - make_sdist: needs: "continuous-integration" name: Make SDist @@ -114,18 +108,13 @@ jobs: path: dist/ upload_to_conda: - needs: ["release-please", "conda-deploy"] - runs-on: ${{matrix.os}} - strategy: - fail-fast: false - matrix: - os: ["ubuntu-latest"] - python-version: ["3.9", "3.10", "3.11", "3.12"] + needs: ["release-please", "conda-build"] + runs-on: ubuntu-latest if: ${{ needs.release-please.outputs.release_created }} steps: - uses: actions/download-artifact@v4 with: - name: conda-${{ matrix.os }}-${{ matrix.python-version }} + name: conda path: conda - uses: conda-incubator/setup-miniconda@v3 - name: upload all files to conda-forge @@ -134,10 +123,10 @@ jobs: ANACONDA_API_TOKEN: ${{ secrets.ANACONDA_TOKEN }} run: | conda install -c anaconda anaconda-client -y - anaconda upload --label main conda/*/*.tar.bz2 + anaconda upload --label main conda/*/*.conda upload_to_pypi: - needs: ["release-please", "conda-deploy"] + needs: ["release-please", "conda-build"] runs-on: "ubuntu-latest" if: ${{ needs.release-please.outputs.release_created }} diff --git a/.gitignore b/.gitignore index 4801bb1..1f90af5 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ LoopDataConverter.egg-info/requires.txt LoopDataConverter.egg-info/SOURCES.txt LoopDataConverter.egg-info/PKG-INFO LoopDataConverter.egg-info/dependency_links.txt +examples/.ipynb_checkpoints/ntgs_example_1-checkpoint.ipynb diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..cda9cbd --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.1.2" +} \ No newline at end of file diff --git a/LoopDataConverter/__init__.py b/LoopDataConverter/__init__.py index e69de29..8bde8af 100644 --- a/LoopDataConverter/__init__.py +++ b/LoopDataConverter/__init__.py @@ -0,0 +1,5 @@ +from .converters import LoopConverter +from .input import InputData +from .datatypes import SurveyName, Datatype +from .fields import NtgsConfig +from .geometry_correction import FaultConnector diff --git a/LoopDataConverter/__pycache__/__init__.cpython-311.pyc b/LoopDataConverter/__pycache__/__init__.cpython-311.pyc index 7d5e42c..3b2692f 100644 Binary files a/LoopDataConverter/__pycache__/__init__.cpython-311.pyc and b/LoopDataConverter/__pycache__/__init__.cpython-311.pyc differ diff --git a/LoopDataConverter/converters/__init__.py b/LoopDataConverter/converters/__init__.py index 8a97d5a..48e4c0f 100644 --- a/LoopDataConverter/converters/__init__.py +++ b/LoopDataConverter/converters/__init__.py @@ -1,14 +1,4 @@ from .base_converter import BaseConverter from .ntgs_converter import NTGSConverter +from .loop_converter import LoopConverter from ..datatypes import SurveyName - -converter_map = { - SurveyName.NTGS: NTGSConverter, - SurveyName.GA: "", - SurveyName.GSQ: "", - SurveyName.GSWA: "", - SurveyName.GSSA: "", - SurveyName.GSV: "", - SurveyName.MRT: "", - SurveyName.GSNSW: "", -} diff --git a/LoopDataConverter/converters/__pycache__/__init__.cpython-311.pyc b/LoopDataConverter/converters/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..fa27d10 Binary files /dev/null and b/LoopDataConverter/converters/__pycache__/__init__.cpython-311.pyc differ diff --git a/LoopDataConverter/converters/__pycache__/base_converter.cpython-311.pyc b/LoopDataConverter/converters/__pycache__/base_converter.cpython-311.pyc new file mode 100644 index 0000000..e2b0962 Binary files /dev/null and b/LoopDataConverter/converters/__pycache__/base_converter.cpython-311.pyc differ diff --git a/LoopDataConverter/converters/__pycache__/conversion_manager.cpython-311.pyc b/LoopDataConverter/converters/__pycache__/conversion_manager.cpython-311.pyc new file mode 100644 index 0000000..c6696f4 Binary files /dev/null and b/LoopDataConverter/converters/__pycache__/conversion_manager.cpython-311.pyc differ diff --git a/LoopDataConverter/converters/__pycache__/loop_converter.cpython-311.pyc b/LoopDataConverter/converters/__pycache__/loop_converter.cpython-311.pyc new file mode 100644 index 0000000..7439cc2 Binary files /dev/null and b/LoopDataConverter/converters/__pycache__/loop_converter.cpython-311.pyc differ diff --git a/LoopDataConverter/converters/__pycache__/ntgs_converter.cpython-311.pyc b/LoopDataConverter/converters/__pycache__/ntgs_converter.cpython-311.pyc new file mode 100644 index 0000000..6b311b6 Binary files /dev/null and b/LoopDataConverter/converters/__pycache__/ntgs_converter.cpython-311.pyc differ diff --git a/LoopDataConverter/converters/conversion_manager.py b/LoopDataConverter/converters/conversion_manager.py deleted file mode 100644 index 1e6193b..0000000 --- a/LoopDataConverter/converters/conversion_manager.py +++ /dev/null @@ -1,44 +0,0 @@ -from .ntgs_converter import NTGSConverter -from ..datatypes import SurveyName -from ..file_readers import LoopGisReader -from ..input import InputData - - -class LoopConverter: - """ - LoopConverter class use the LoopGisReader to look up the correct file - reader for the input file type and then converting the data to - Map2Loop or LoopStrucural formats using the adequate converter - """ - - def __init__(self, survey_name: SurveyName, data: InputData, layer: str = None): - self._fileData = data - self._layer = layer - self._survey_name = survey_name - self._converters = { - SurveyName.NTGS: NTGSConverter, - SurveyName.GA: "", - SurveyName.GSQ: "", - SurveyName.GSWA: "", - SurveyName.GSSA: "", - SurveyName.GSV: "", - SurveyName.GSNSW: "", - SurveyName.MRT: "", - } - - def read_file(self): - """ - read the file using the correct file reader - """ - file_reader = LoopGisReader(self._fileData) - file_reader.read(self._fileData, self._layer) - return file_reader.data - - def get_converter(self): - return self._converters[self._survey_name] - - def convert(self): - data = self.read_file() - converter = self.get_converter() - converter(data) - self.data = converter._data diff --git a/LoopDataConverter/converters/loop_converter.py b/LoopDataConverter/converters/loop_converter.py new file mode 100644 index 0000000..9d13f3c --- /dev/null +++ b/LoopDataConverter/converters/loop_converter.py @@ -0,0 +1,91 @@ +from .ntgs_converter import NTGSConverter +from ..datatypes import SurveyName, Datatype +from ..file_readers import LoopGisReader +from ..input import InputData + + +class LoopConverter: + """ + LoopConverter class use the LoopGisReader to look up the correct file + reader for the input file type and then converting the data to + Map2Loop format using the adequate converter + """ + + def __init__(self, survey_name: SurveyName, data: InputData, layer: str = None): + ''' + This function initializes an object with survey name, input data, and optional layer + information, along with converters for different survey names. + + Parameters + ---------- + survey_name : SurveyName + `survey_name` is a parameter that represents the name of a survey. It is expected to be of type + `SurveyName`. + data : InputData + The `data` parameter in the `__init__` method is of type `InputData`. It seems to represent the + data that will be used in the survey. + layer : str + The `layer` parameter is a string that represents a specific layer within a .GPKG file. + It is an optional parameter with a default value of `None`, which means it can be omitted + when creating an instance of the class. If provided, it specifies the layer to + + ''' + self._fileData = data + self._layer = layer + self._survey_name = survey_name + self._converters = { + SurveyName.NTGS: NTGSConverter, + SurveyName.GA: "", + SurveyName.GSQ: "", + SurveyName.GSWA: "", + SurveyName.GSSA: "", + SurveyName.GSV: "", + SurveyName.GSNSW: "", + SurveyName.MRT: "", + } + self._used_converter = None + + def read_file(self): + """ + read the file using the correct file reader + """ + self.file_reader = LoopGisReader(self._fileData) + self.file_reader() + return self.file_reader._data + + def get_converter(self): + ''' + This function returns a converter based on the survey name. + + Returns + ------- + The `get_converter` method is returning the converter associated with the survey name stored in + the `_survey_name` attribute. + + ''' + return self._converters[self._survey_name] + + def convert(self): + ''' + This function reads data from a file, uses a converter to process the data, and stores the + converted data in the object's data attribute. + + ''' + data = self.read_file() + self._used_converter = self.get_converter() + self._used_converter = self._used_converter(data) + self._used_converter.convert() + self.data = self._used_converter._data + + def save(self, datatype: Datatype, file_path: str, file_extension: str = None): + if file_extension == "geojson": + self.data[datatype].to_file(file_path, driver="GeoJSON") + + elif file_extension == "gpkg": + self.data.to_file(file_path, driver="GPKG") + + elif file_extension == "shp": + self.data[datatype].to_file(file_path) + + else: + raise ValueError(f"Unsupported file format: {file_extension}") diff --git a/LoopDataConverter/converters/ntgs_converter.py b/LoopDataConverter/converters/ntgs_converter.py index 49ca4e5..e4ec51e 100644 --- a/LoopDataConverter/converters/ntgs_converter.py +++ b/LoopDataConverter/converters/ntgs_converter.py @@ -1,5 +1,8 @@ # internal imports +from ..datatypes import Datatype from .base_converter import BaseConverter + +# from ..geometry_correction import FaultConnector from ..utils import ( convert_dipdir_terms, convert_dip_terms, @@ -9,60 +12,181 @@ # external imports import pandas -import numpy +import geopandas class NTGSConverter(BaseConverter): # TODO: modify class to take fold, fault, and structure layers as arguments def __init__(self, data: pandas.DataFrame): self.raw_data = data.copy() + self.update_empty_rows() self._type_label = "NTGSConverter" + self.crs = self.raw_data[Datatype.GEOLOGY].crs self._data = None def type(self): + """ + The function `type` returns the `_type_label` attribute of the object. + + Returns + ------- + The `type` method is returning the value of the `_type_label` attribute of the object. + + """ return self._type_label + def update_empty_rows(self): + """ + The function `update_empty_rows` updates empty rows in the DataFrame with NaN values. + + Parameters + ---------- + None + + This method operates on the DataFrames stored in the class and replaces all empty values + (e.g., empty strings, None, NaN) with NaN across the specified tables. + """ + + # List of tables (DataFrames) to update + tables_to_update = [Datatype.FOLD, Datatype.FAULT, Datatype.STRUCTURE] + + for table in tables_to_update: + # Replace empty strings, None, or NaN with np.nan in the entire table + self.raw_data[table] = self.raw_data[table].map( + lambda x: "NaN" if pandas.isna(x) or x == "" or x is None else x + ) + def convert_fold_map(self): + """ + The function `convert_fold_map` converts dip direction, dip, and tightness terms in the raw data + 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["AxialPlaneDipDir"] = self.raw_data["AxialPlaneDipDir"].apply( - lambda x: convert_dipdir_terms(x) - ) + 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["AxialPlaneDip"] = self.raw_data["AxialPlaneDip"].apply( + 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["InterlimbAngle"] = self.raw_data["InterlimbAngle"].apply( - lambda x: convert_tightness_terms(x) - ) + self.raw_data[Datatype.FOLD]["IntlimbAng"] = self.raw_data[Datatype.FOLD][ + "IntlimbAng" + ].apply(lambda x: convert_tightness_terms(x)) def convert_fault_map(self): + """ + The function `convert_fault_map` converts dip direction, dip, and displacement terms to degrees + in a DataFrame. + + """ + # convert dip direction terms to degrees - self.raw_data["DipDirection"] = self.raw_data["DipDirection"].apply( + + 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["Dip"] = self.raw_data["Dip"].apply( + self.raw_data[Datatype.FAULT]["Dip"] = self.raw_data[Datatype.FAULT]["Dip"].apply( lambda x: convert_dip_terms(x, type="fault") ) - self.raw_data["Displacement"] = self.raw_data["Displacement"].apply( + # convert displacement terms to meters + self.raw_data[Datatype.FAULT]["Displace"] = self.raw_data[Datatype.FAULT]["Displace"].apply( lambda x: convert_displacement_terms(x) ) + self.raw_data[Datatype.FAULT]["centroid"] = self.raw_data[Datatype.FAULT].geometry.centroid + self.raw_data[Datatype.FAULT]["centroid_x"] = self.raw_data[Datatype.FAULT].centroid.x + self.raw_data[Datatype.FAULT]["centroid_y"] = self.raw_data[Datatype.FAULT].centroid.y + # self.raw_data[Datatype.FAULT]["Strike"] = self.raw_data[Datatype.FAULT]["DipDir"] + 90 + self.raw_data[Datatype.FAULT]["X"] = self.raw_data[Datatype.FAULT].centroid_x + self.raw_data[Datatype.FAULT]["Y"] = self.raw_data[Datatype.FAULT].centroid_y + self.raw_data[Datatype.FAULT]["Z"] = 0.0 def convert_structure_map(self): - # discard any rows that has a dip value of -99 and does not have any esimated dip value - condition = (self.raw_data["Dip"] != -99) & (self.raw_data["DipEstimate"] != numpy.nan) - self.raw_data = self.raw_data[condition] + """ + This function filters out rows with a dip value of -99 and no estimated dip value, then converts + dip estimates to floats by averaging the range. + + """ + # 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]["DipEst"] != "NaN" + ) + # convert dip estimate to float (average of the range) - condition = self.raw_data["Dip"] == -99 - self.raw_data.loc[condition, "DipEstimate"] = self.raw_data.loc[ - condition, "DipEstimate" - ].apply(lambda x: sum(map(float, x.split("-"))) / 2) - self.raw_data[condition, "Dip"] = self.raw_data[condition, "DipEstimate"] + self.raw_data[Datatype.STRUCTURE].loc[condition, "Dip"] = ( + self.raw_data[Datatype.STRUCTURE] + .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]["DipEst"] == "NaN" + ) + self.raw_data[Datatype.STRUCTURE] = self.raw_data[Datatype.STRUCTURE][~condition] + self.raw_data[Datatype.STRUCTURE]["Strike"] = ( + self.raw_data[Datatype.STRUCTURE]["DipDir"] + 90 + ) % 360 + self.raw_data[Datatype.STRUCTURE]["X"] = self.raw_data[Datatype.STRUCTURE].geometry.x + self.raw_data[Datatype.STRUCTURE]["Y"] = self.raw_data[Datatype.STRUCTURE].geometry.y + self.raw_data[Datatype.STRUCTURE]["Z"] = 0.0 + + def fault_map_postprocessing(self): + """ + This function performs post-processing on the different datatypes. + + """ + + # Add strike and dip data from the Fault dataset where STRIKE and DIP rows contain a value and not a NaN value + valid_faults = self.raw_data[Datatype.FAULT].dropna( + subset=["MSID", "X", "Y", "Z", "DipDir", "Strike", "Dip", "centroid"] + ) + valid_faults = valid_faults.rename(columns={"MSID": "featureId"}) + valid_faults["geometry"] = valid_faults["centroid"] + self.raw_data[Datatype.FAULT_ORIENTATION] = geopandas.GeoDataFrame( + valid_faults[["featureId", "X", "Y", "Z", "DipDir", "Dip", "geometry"]].copy(), + crs=self.crs, + ) + # Remove the string part of "featureId" + self.raw_data[Datatype.FAULT_ORIENTATION]["featureId"] = self.raw_data[ + Datatype.FAULT_ORIENTATION + ]["featureId"].apply(lambda x: ''.join(filter(str.isdigit, str(x)))) + self.raw_data[Datatype.FAULT] = self.raw_data[Datatype.FAULT].drop(columns="centroid") + + # fault_data = self.raw_data[Datatype.FAULT].copy() + # fault_connector = FaultConnector(fault_data) + # fault_connector.connect_faults() + # processed_fault_data = fault_connector.processed_data + + # return processed_fault_data def convert(self): - self.convert_fold_map() - self.convert_fault_map() - self.convert_structure_map() + """ + The function `convert` performs various conversions and copies the raw data in a Python class. + + """ + + if self.raw_data[Datatype.FOLD] is not None: + self.convert_fold_map() + if self.raw_data[Datatype.FAULT] is not None: + self.convert_fault_map() + if self.raw_data[Datatype.STRUCTURE] is not None: + self.convert_structure_map() + self.fault_map_postprocessing() self._data = self.raw_data.copy() diff --git a/LoopDataConverter/fields/__init__.py b/LoopDataConverter/fields/__init__.py index e69de29..d6a9250 100644 --- a/LoopDataConverter/fields/__init__.py +++ b/LoopDataConverter/fields/__init__.py @@ -0,0 +1 @@ +from ._ntgs import NtgsConfig diff --git a/LoopDataConverter/fields/__pycache__/__init__.cpython-311.pyc b/LoopDataConverter/fields/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..6b5bd51 Binary files /dev/null and b/LoopDataConverter/fields/__pycache__/__init__.cpython-311.pyc differ diff --git a/LoopDataConverter/fields/_ntgs/__init__.py b/LoopDataConverter/fields/_ntgs/__init__.py index d3cb28f..b209719 100644 --- a/LoopDataConverter/fields/_ntgs/__init__.py +++ b/LoopDataConverter/fields/_ntgs/__init__.py @@ -1 +1 @@ -from ._ntgs_config import NtgsM2lConfig +from ._ntgs_config import NtgsConfig diff --git a/LoopDataConverter/fields/_ntgs/__pycache__/__init__.cpython-311.pyc b/LoopDataConverter/fields/_ntgs/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..c6ab173 Binary files /dev/null and b/LoopDataConverter/fields/_ntgs/__pycache__/__init__.cpython-311.pyc differ diff --git a/LoopDataConverter/fields/_ntgs/__pycache__/_ntgs_config.cpython-311.pyc b/LoopDataConverter/fields/_ntgs/__pycache__/_ntgs_config.cpython-311.pyc new file mode 100644 index 0000000..0bc2c4a Binary files /dev/null and b/LoopDataConverter/fields/_ntgs/__pycache__/_ntgs_config.cpython-311.pyc differ diff --git a/LoopDataConverter/fields/_ntgs/_ntgs_config.py b/LoopDataConverter/fields/_ntgs/_ntgs_config.py index e2a72a4..58e3d55 100644 --- a/LoopDataConverter/fields/_ntgs/_ntgs_config.py +++ b/LoopDataConverter/fields/_ntgs/_ntgs_config.py @@ -1,76 +1,68 @@ -from ...datatypes.enums import Datatype - - class NtgsConfig: def __init__(self): - self.fold_config = ( - { - "structtype_column": "FoldEvent", - "fold_text": "FeatureCodeDesc", - "description_column": "Description", - "synform_text": "FoldType", - "foldname_column": "FoldName", - "objectid_column": "OBJECTID", - "tightness_column": "InterlimbAngle", - "axial_plane_dipdir_column": "AxialPlaneDipDir", - "axial_plane_dip_column": "AxialPlaneDip", - "interp_source_column": "InterpSource", - }, - ) + self.fold_config = { + "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", + "objectid_column": "OBJECTID", + "tightness_column": "IntlimbAng", + "axial_plane_dipdir_column": "AxPlDipDir", + "axial_plane_dip_column": "AxPlDip", + } self.fault_config = { + "orientation_type": "dip direction", "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": "DipDirection", + "dipdir_column": "DipDir", "dip_column": "Dip", - "orientation_type": "dip direction", "dipestimate_column": "DipEstimate", "dipestimate_text": "'NORTH_EAST','NORTH',,'NOT ACCESSED'", - "displacement_column": "Displacement", + "displacement_column": "Displace", "displacement_text": "'1m-100m', '100m-1km', '1km-5km', '>5km'", - "fault_length_column": "FaultLength", - "fault_length_text": "'Small (0-5km)', 'Medium (5-30km)', 'Large (30-100km)', 'Regional (>100km)', 'Unclassified'", + "fault_length_column": "FaultLen", + "fault_length_text": "Small (0-5km),Medium (5-30km),Large (30-100km),Regional (>100km),Unclassified", "name_column": "FaultName", "objectid_column": "OBJECTID", - "interp_source_column": "InterpSource", } self.geology_config = { "unitname_column": "Formation", - "alt_unitname_column": "CODE", - "group_column": "GroupSuite", + "alt_unitname_column": "Formation", + "group_column": "Group", "supergroup_column": "Supergroup", - "description_column": "LithDescription", + "description_column": "LithDescn1", "minage_column": "AgeMin", "maxage_column": "AgeMax", "rocktype_column": "LithClass", - "alt_rocktype_column": "RockCategory", - "sill_text": "RockCategory", - "intrusive_text": "RockCategory", - "volcanic_text": "RockCategory", + "alt_rocktype_column": "RockCat", + "sill_text": "RockCat", + "intrusive_text": "RockCat", + "volcanic_text": "RockCat", "objectid_column": "OBJECTID", - "ignore_codes": ["cover"], + "ignore_lithology_codes": ["cover", "Unknown"], } self.structure_config = { "orientation_type": "dip direction", - "dipdir_column": "DipDirection", + "dipdir_column": "DipDir", "dip_column": "Dip", - "description_column": "FeatureCodeDesc", - "bedding_text": "'Bedding', 'Cleavage', 'Faulting', 'Folding', 'Foliation', 'Geophysical', 'Igneous banding', 'Lineation'", - "overturned_column": "FeatureCodeDesc", + "description_column": "FeatDesc", + "bedding_text": "ObsType", + "overturned_column": "Desc", "overturned_text": "overturned", - "objectid_column": "ID", - "interp_source_column": "InterpSource", + "objectid_column": "OBJECTID", } self.config_map = { - Datatype.GEOLOGY: self.geology_config, - Datatype.STRUCTURE: self.structure_config, - Datatype.FAULT: self.fault_config, - Datatype.FOLD: self.fold_config, + "geology": self.geology_config, + "structure": self.structure_config, + "fault": self.fault_config, + "fold": self.fold_config, } def __getitem__(self, datatype): diff --git a/LoopDataConverter/file_readers/__pycache__/_file_readers.cpython-311.pyc b/LoopDataConverter/file_readers/__pycache__/_file_readers.cpython-311.pyc index 5badfd1..0522225 100644 Binary files a/LoopDataConverter/file_readers/__pycache__/_file_readers.cpython-311.pyc and b/LoopDataConverter/file_readers/__pycache__/_file_readers.cpython-311.pyc differ diff --git a/LoopDataConverter/file_readers/_file_readers.py b/LoopDataConverter/file_readers/_file_readers.py index c446c3d..48be961 100644 --- a/LoopDataConverter/file_readers/_file_readers.py +++ b/LoopDataConverter/file_readers/_file_readers.py @@ -6,6 +6,8 @@ import os import validators +# TODO: add a metaclass for methods that are repetitive + class BaseFileReader(ABC): def __init__(self): @@ -21,7 +23,6 @@ def check_source_type(self, file_source: str): file_source ), "Invalid file source, must be a valid URL or file path" - @beartype.beartype @abstractmethod def get_file(self, file_source: str, layer: str = None): pass @@ -49,7 +50,6 @@ def type(self): def check_source_type(self, file_source: str): super().check_source_type(file_source) - @beartype.beartype def get_file(self, file_source: str, layer: str = None): return pandas.read_csv(file_source) @@ -67,7 +67,6 @@ def read(self, file_source: str): class GeoDataFileReader(BaseFileReader): def __init__(self): self.file_reader_label = "GeoDataFileReader" - self.file = None self.data = None def type(self): @@ -77,7 +76,7 @@ def type(self): def check_source_type(self, file_source: str): super().check_source_type(file_source) - @beartype.beartype + # TODO: add general check for variable types def get_file(self, file_source: str, layer: str = None): file_extension = os.path.splitext(file_source)[1] @@ -85,7 +84,7 @@ def get_file(self, file_source: str, layer: str = None): return geopandas.read_file(file_source) elif file_extension == ".gpkg": - assert layer is not None, "Layer name must be provided for GeoPackage files" + assert layer is False, "Layer name must be provided for GeoPackage files" return geopandas.read_file(file_source, layer=layer) @@ -107,14 +106,14 @@ def save(self, file_path: str, file_extension: str = None): raise ValueError(f"Unsupported file format: {file_extension}") @beartype.beartype - def read(self, file_source: str): + def read(self, file_source: str, layer: str = None): self.check_source_type(file_source) - self.file = self.get_file(file_source) + self.file = self.get_file(file_source, layer) self.data = geopandas.GeoDataFrame(self.file) class LoopGisReader: - def __init__(self, fileData, layer=None): + def __init__(self, fileData, layer=""): self._layer = layer self._fileData = fileData self._reader = [None] * len(Datatype) @@ -137,9 +136,11 @@ def assign_reader(self, file_source): raise ValueError(f"Unsupported file format: {file_extension}") def read(self, datatype: Datatype): - self._reader.read(self._fileData[Datatype.GEOLOGY], self._layer) + self._reader[datatype] = self.assign_reader(self._fileData[datatype]) + self.file_reader_label[datatype] = self._reader[datatype].type() + self._reader[datatype].read(self._fileData[datatype], self._layer) - return self._reader.data + return self._reader[datatype].data def __call__(self): """ @@ -147,26 +148,16 @@ def __call__(self): """ if self._fileData[Datatype.GEOLOGY] is not None: - self._reader[Datatype.GEOLOGY] = self.assign_reader(self._fileData[Datatype.GEOLOGY]) - self.file_reader_label[Datatype.GEOLOGY] = self._reader[Datatype.GEOLOGY].type() self._data[Datatype.GEOLOGY] = self.read(Datatype.GEOLOGY) if self._fileData[Datatype.STRUCTURE] is not None: - self._reader[Datatype.STRUCTURE] = self.assign_reader( - self._fileData[Datatype.STRUCTURE] - ) - self.file_reader_label[Datatype.STRUCTURE] = self._reader[Datatype.STRUCTURE].type() self._data[Datatype.STRUCTURE] = self.read(Datatype.STRUCTURE) if self._fileData[Datatype.FAULT] is not None: - self._reader[Datatype.FAULT] = self.assign_reader(self._fileData[Datatype.FAULT]) - self.file_reader_label[Datatype.FAULT] = self._reader[Datatype.FAULT].type() self._data[Datatype.FAULT] = self.read(Datatype.FAULT) if self._fileData[Datatype.FOLD] is not None: - self._reader[Datatype.FOLD] = self.assign_reader(self._fileData[Datatype.FOLD]) - self.file_reader_label[Datatype.FOLD] = self._reader[Datatype.FOLD].type() self._data[Datatype.FOLD] = self.read(Datatype.FOLD) - def save(self, file_path, file_extension=None): - self._reader.save(file_path, file_extension) + def save(self, datatype, file_path, file_extension=None): + self._reader[datatype].save(file_path, file_extension) diff --git a/LoopDataConverter/geometry_correction/__init__.py b/LoopDataConverter/geometry_correction/__init__.py new file mode 100644 index 0000000..ba8e573 --- /dev/null +++ b/LoopDataConverter/geometry_correction/__init__.py @@ -0,0 +1 @@ +from .fault_geometry import FaultConnector diff --git a/LoopDataConverter/geometry_correction/fault_geometry.py b/LoopDataConverter/geometry_correction/fault_geometry.py new file mode 100644 index 0000000..8b7c02a --- /dev/null +++ b/LoopDataConverter/geometry_correction/fault_geometry.py @@ -0,0 +1,110 @@ +# internal imports +from ..utils import calculate_vector_along_line, calculate_angle + +# external imports +import numpy +import pandas +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.crs = self._data.crs + self.processed_data = None + + def connect_faults(self): + """ + 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 + + 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) + new_row = geopandas.GeoDataFrame({"geometry": [merged_line]}, crs=self.crs) + self.processed_data = pandas.concat( + [self.processed_data, new_row], ignore_index=True + ) + self.processed_data = geopandas.GeoDataFrame(self.processed_data, crs=self.crs) + # self.processed_data = self.processed_data.concat( + # {"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 diff --git a/LoopDataConverter/input/__init__.py b/LoopDataConverter/input/__init__.py index e69de29..0e1cb26 100644 --- a/LoopDataConverter/input/__init__.py +++ b/LoopDataConverter/input/__init__.py @@ -0,0 +1 @@ +from .input_data import InputData diff --git a/LoopDataConverter/input/__pycache__/__init__.cpython-311.pyc b/LoopDataConverter/input/__pycache__/__init__.cpython-311.pyc index a2ac4c6..b933b05 100644 Binary files a/LoopDataConverter/input/__pycache__/__init__.cpython-311.pyc and b/LoopDataConverter/input/__pycache__/__init__.cpython-311.pyc differ diff --git a/LoopDataConverter/input/__pycache__/input_data.cpython-311.pyc b/LoopDataConverter/input/__pycache__/input_data.cpython-311.pyc index c8853bd..7db50fe 100644 Binary files a/LoopDataConverter/input/__pycache__/input_data.cpython-311.pyc and b/LoopDataConverter/input/__pycache__/input_data.cpython-311.pyc differ diff --git a/LoopDataConverter/input/input_data.py b/LoopDataConverter/input/input_data.py index 214d3bb..22f00b2 100644 --- a/LoopDataConverter/input/input_data.py +++ b/LoopDataConverter/input/input_data.py @@ -7,29 +7,33 @@ class InputData: """Class to store input data for the loop data converter Attributes: - geology: Datatype.GEOLOGY = None - structure: Datatype.STRUCTURE = None - fault: Datatype.FAULT = None - fold: Datatype.FOLD = None - + geology: Optional[Datatype] = None + structure: Optional[Datatype] = None + fault: Optional[Datatype] = None + fold: Optional[Datatype] = None """ - geology: Datatype.GEOLOGY = None - structure: Datatype.STRUCTURE = None - fault: Datatype.FAULT = None - fold: Datatype.FOLD = None + GEOLOGY: Datatype.GEOLOGY = None + STRUCTURE: Datatype.STRUCTURE = None + FAULT: Datatype.FAULT = None + FOLD: Datatype.FOLD = None - def __getitem__(self, datatype: Datatype): - """Method to get the the file directory of a datatype + def __getitem__(self, datatype: str): + """Method to get the file directory of a datatype Parameters: - datatype (Datatype): The datatype to get the file directory of + datatype (str): The datatype to get the file directory of Returns: The file directory of the datatype - """ - return self.__dict__[datatype] + Raises: + KeyError: If the datatype is not found in InputData + """ + try: + return getattr(self, datatype.name) + except AttributeError: + raise KeyError(f"Datatype {datatype.name} not found in InputData") @dataclass diff --git a/LoopDataConverter/utils/__init__.py b/LoopDataConverter/utils/__init__.py index 90e18d2..198a17c 100644 --- a/LoopDataConverter/utils/__init__.py +++ b/LoopDataConverter/utils/__init__.py @@ -1,6 +1,8 @@ from .conversion import ( convert_dip_terms, - convert_dipdir_cardinals, + convert_dipdir_terms, convert_tightness_terms, convert_displacement_terms, ) + +from .geometry import calculate_angle, calculate_vector_along_line, unit_vector diff --git a/LoopDataConverter/utils/__pycache__/__init__.cpython-311.pyc b/LoopDataConverter/utils/__pycache__/__init__.cpython-311.pyc index a5417c7..bd9e6ac 100644 Binary files a/LoopDataConverter/utils/__pycache__/__init__.cpython-311.pyc and b/LoopDataConverter/utils/__pycache__/__init__.cpython-311.pyc differ diff --git a/LoopDataConverter/utils/__pycache__/conversion.cpython-311.pyc b/LoopDataConverter/utils/__pycache__/conversion.cpython-311.pyc index 4b7ee70..0cd1796 100644 Binary files a/LoopDataConverter/utils/__pycache__/conversion.cpython-311.pyc and b/LoopDataConverter/utils/__pycache__/conversion.cpython-311.pyc differ diff --git a/LoopDataConverter/utils/conversion.py b/LoopDataConverter/utils/conversion.py index 21a70c6..0163390 100644 --- a/LoopDataConverter/utils/conversion.py +++ b/LoopDataConverter/utils/conversion.py @@ -1,9 +1,12 @@ +import logging import numpy -import beartype +import re +logging.basicConfig(level=logging.ERROR) +logger = logging.getLogger(__name__) -@beartype.beartype -def convert_dipdir_cardinals(cardinal: str): + +def convert_dipdir_terms(cardinal: str): """ Convert cardinal directions to degrees. @@ -12,40 +15,45 @@ def convert_dipdir_cardinals(cardinal: str): return (float): The cardinal direction in degrees. """ - if cardinal == "N": - return 0.0 + logger.info(f"convert_dipdir_terms called with cardinal: {cardinal}") + if cardinal == "NaN": + result = numpy.nan + elif cardinal == "N": + result = 0.0 elif cardinal == "NNE": - return 22.5 + result = 22.5 elif cardinal == "NE": - return 45.0 + result = 45.0 elif cardinal == "ENE": - return 67.5 + result = 67.5 elif cardinal == "E": - return 90.0 + result = 90.0 elif cardinal == "ESE": - return 112.5 + result = 112.5 elif cardinal == "SE": - return 135.0 + result = 135.0 elif cardinal == "SSE": - return 157.5 + result = 157.5 elif cardinal == "S": - return 180.0 + result = 180.0 elif cardinal == "SSW": - return 202.5 + result = 202.5 elif cardinal == "SW": - return 225.0 + result = 225.0 elif cardinal == "WSW": - return 247.5 + result = 247.5 elif cardinal == "W": - return 270.0 + result = 270.0 elif cardinal == "WNW": - return 292.5 + result = 292.5 elif cardinal == "NW": - return 315.0 + result = 315.0 elif cardinal == "NNW": - return 337.5 + result = 337.5 else: - return numpy.nan + result = numpy.nan + logger.info(f"convert_dipdir_terms returning: {result}") + return result def convert_dip_terms(dip_term: str, type: str): @@ -57,29 +65,44 @@ def convert_dip_terms(dip_term: str, type: str): return (float): The dip term in degrees. """ + logger.info(f"convert_dip_terms called with dip_term: {dip_term}, type: {type}") if type == "fault": - if dip_term == "Vertical": - return 90.0 - elif dip_term == "Horizontal": - return 0.0 - elif dip_term == "Moderate": - return 45.0 - elif dip_term == "Steep": - return 75.0 + dip_text = split_string(dip_term)[0] + if dip_text == "Vertical": + result = 90.0 + elif dip_text == "Horizontal": + result = 0.0 + elif dip_text == "Moderate": + result = 45.0 + elif dip_text == "Steep": + result = 75.0 else: - return numpy.nan + result = numpy.nan elif type == "fold": if dip_term == "Upright": - return 90.0 + result = 90.0 elif dip_term == "Recumbent": - return 0.0 + result = 0.0 elif dip_term == "Inclined": - return 45.0 + result = 45.0 elif dip_term == "Reclined": - return 75.0 + result = 75.0 + else: + result = numpy.nan + elif type == "structure": + if dip_term == "0-5": + result = 2.5 + elif dip_term == "5-15": + result = 10.0 + elif dip_term == "15-45": + result = 45.0 + elif dip_term == ">45": + result = 75.0 else: - return numpy.nan + result = numpy.nan + logger.info(f"convert_dip_terms returning: {result}") + return result def convert_tightness_terms(tightness_term: str): @@ -92,36 +115,75 @@ def convert_tightness_terms(tightness_term: str): return (float): The tightness term in degrees, which is the average of the interlimb angle range. """ + logger.info(f"convert_tightness_terms called with tightness_term: {tightness_term}") if tightness_term == "gentle": - return 150.0 + result = 150.0 elif tightness_term == "open": - return 95.0 + result = 95.0 elif tightness_term == "close": - return 50.0 + result = 50.0 elif tightness_term == "tight": - return 15.0 + result = 15.0 elif tightness_term == "isoclinal": - return 0.0 + result = 0.0 else: - return numpy.nan + result = numpy.nan + logger.info(f"convert_tightness_terms returning: {result}") + return result def convert_displacement_terms(displacement_term: str): + """Convert displacement terms expressed as ranges or inequalities. + + Parameters + ---------- + displacement_term : str + Term describing fault displacement. Examples include ``"1m-100m"`` or + ``">5km"``. + + Returns + ------- + float + Displacement in metres, using the mean value of a range. Returns + ``numpy.nan`` if the term cannot be parsed. """ - Convert displacement terms to meters. + + logger.info(f"convert_displacement_terms called with displacement_term: {displacement_term}") + + def _value(text: str) -> float: + """Convert a textual value with units to metres.""" + text = text.strip().lower() + if text.endswith("km"): + return float(text[:-2]) * 1000 + if text.endswith("m"): + return float(text[:-1]) + return float(text) + + try: + if displacement_term.startswith(">"): + result = _value(displacement_term[1:]) + elif "-" in displacement_term: + start, end = displacement_term.split("-", 1) + result = (_value(start) + _value(end)) / 2 + else: + result = numpy.nan + except Exception: + result = numpy.nan + + logger.info(f"convert_displacement_terms returning: {result}") + return result + + +def split_string(input_string): + """ + Split a string into components. Parameters: - displacement_term (str): The displacement term to convert. + input_string (str): The string to split. - return (float): The displacement term in meters. + return (list): The components of the string. """ - if displacement_term == "1m-100m": - return 50.5 - elif displacement_term == "100m-1km": - return 550.0 - elif displacement_term == "1km-5km": - return 3000.0 - elif displacement_term == ">5km": - return 5000.0 - else: - return numpy.nan + logger.info(f"split_string called with input_string: {input_string}") + result = re.split(r'\s+', input_string) + logger.info(f"split_string returning: {result}") + return result diff --git a/LoopDataConverter/utils/geometry.py b/LoopDataConverter/utils/geometry.py new file mode 100644 index 0000000..6c5d1f5 --- /dev/null +++ b/LoopDataConverter/utils/geometry.py @@ -0,0 +1,68 @@ +import numpy +import shapely +import logging + +logging.basicConfig(level=logging.WARNING) + + +def unit_vector(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. + + 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. + """ + + # 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) + 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]) diff --git a/conda/conda_build_config.yaml b/conda/conda_build_config.yaml index 8b13789..1826a5c 100644 --- a/conda/conda_build_config.yaml +++ b/conda/conda_build_config.yaml @@ -1 +1,2 @@ - +numpy: +- 2.1.0 diff --git a/conda/meta.yaml b/conda/meta.yaml index bef4916..a575ae0 100644 --- a/conda/meta.yaml +++ b/conda/meta.yaml @@ -14,7 +14,7 @@ build: requirements: build: - {{ compiler('cxx') }} - - numpy + - numpy {{ numpy }} - pandas - geopandas - shapely @@ -26,10 +26,10 @@ requirements: - pip - python - setuptools - - numpy + - numpy {{ numpy }} run: - - numpy - - python>=3.9 + - numpy {{ numpy }} + - python>=3.10 test: import: - numpy @@ -47,4 +47,4 @@ about: extra: recipe-maintainers: - - rabii-chaarani \ No newline at end of file + - rabii-chaarani diff --git a/examples/ntgs_example_1.ipynb b/examples/ntgs_example_1.ipynb new file mode 100644 index 0000000..1da61ce --- /dev/null +++ b/examples/ntgs_example_1.ipynb @@ -0,0 +1,6534 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "initial_id", + "metadata": { + "ExecuteTime": { + "end_time": "2024-09-16T05:43:40.858722Z", + "start_time": "2024-09-16T05:43:40.302437Z" + }, + "collapsed": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/rabii/Git_Repos/map2loop/map2loop/__init__.py:113: UserWarning: dependencies.txt not found. No dependencies checked for map2loop.\n", + " warnings.warn(\n" + ] + } + ], + "source": [ + "from LoopDataConverter import LoopConverter, InputData, SurveyName, Datatype, NtgsConfig\n", + "from osgeo import gdal, osr\n", + "import os \n", + "from map2loop import Project#, logging\n", + "import map2loop\n", + "import tempfile\n", + "# logging.set_level('debug')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "eec0e1145cd2b0c6", + "metadata": { + "ExecuteTime": { + "end_time": "2024-08-26T04:27:18.004378Z", + "start_time": "2024-08-26T04:27:18.000708Z" + }, + "collapsed": false + }, + "outputs": [], + "source": [ + "def create_raster(output_path, bbox, epsg, pixel_size, value=100):\n", + " minx, miny, maxx, maxy = bbox\n", + " cols = int((maxx - minx) / pixel_size)\n", + " rows = int((maxy - miny) / pixel_size)\n", + " driver = gdal.GetDriverByName('GTiff')\n", + " out_raster = driver.Create(output_path, cols, rows, 1, gdal.GDT_Byte)\n", + " out_raster.SetGeoTransform([minx, pixel_size, 0, maxy, 0, -pixel_size])\n", + " srs = osr.SpatialReference()\n", + " srs.ImportFromEPSG(epsg)\n", + " out_raster.SetProjection(srs.ExportToWkt())\n", + " out_band = out_raster.GetRasterBand(1)\n", + " out_band.Fill(value)\n", + " out_band.FlushCache()\n", + " out_raster = None" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "c100ae76", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'minx': np.float64(194915.04765542696),\n", + " 'miny': np.float64(7314111.534384862),\n", + " 'maxx': np.float64(269996.050907918),\n", + " 'maxy': np.float64(7343389.948186358),\n", + " 'base': -3000,\n", + " 'top': 3000}" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import geopandas\n", + "\n", + "def get_bounding_box(shapefile_path, zmax=3000, zmin=-3000):\n", + " \"\"\"\n", + " Returns the bounding box of the given shapefile.\n", + " \n", + " Parameters:\n", + " shapefile_path (str): Path to the shapefile.\n", + " \n", + " Returns:\n", + " dict: Bounding box with minx, miny, maxx, maxy.\n", + " \"\"\"\n", + " gdf = geopandas.read_file(shapefile_path)\n", + " bounds = gdf.total_bounds # [minx, miny, maxx, maxy]\n", + " bounding_box = {\n", + " \"minx\": bounds[0],\n", + " \"miny\": bounds[1],\n", + " \"maxx\": bounds[2],\n", + " \"maxy\": bounds[3],\n", + " \"base\": zmin,\n", + " \"top\": zmax,\n", + " }\n", + " return bounding_box\n", + "\n", + "bounding_box = get_bounding_box(\"../test_data/NTGS_data/Henbury/bounding_box.shp\")\n", + "\n", + "# xmin, ymax = 194809, 7342124\n", + "# xmax, ymin = 269919, 7314400\n", + "# zmin, zmax = -3000, 3000\n", + "# bounding_box = {\n", + "# \"minx\": xmin,\n", + "# \"miny\": ymin,\n", + "# \"maxx\": xmax,\n", + "# \"maxy\": ymax,\n", + "# \"base\": zmin,\n", + "# \"top\": zmax,\n", + "# }\n", + "f_path = \"/home/rabii/Git_Repos/LoopDataConverter/test_data/NTGS_data/Henbury\"\n", + "create_raster(\n", + " os.path.join(f_path, \"DEM.tif\"),\n", + " (bounding_box['minx'], bounding_box['miny'], bounding_box['maxx'], bounding_box['maxy']),\n", + " 7854,\n", + " 1000,\n", + ")\n", + "bounding_box" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "2cefe141abea8187", + "metadata": { + "ExecuteTime": { + "end_time": "2024-09-16T05:43:43.673760Z", + "start_time": "2024-09-16T05:43:43.562794Z" + }, + "collapsed": false + }, + "outputs": [], + "source": [ + "input_data = InputData(\n", + " GEOLOGY=\"../test_data/NTGS_data/Henbury/NT_LithOutcrop_250k_shp.shp\", \n", + " STRUCTURE=\"../test_data/NTGS_data/Henbury/NT_StructData_250k_shp.shp\",\n", + " FAULT=\"../test_data/NTGS_data/Henbury/NT_Fault_250k_shp.shp\",\n", + " FOLD=\"../test_data/NTGS_data/Henbury/NT_Fold_250k_shp.shp\"\n", + ")\n", + "\n", + "loop_converter = LoopConverter(survey_name=SurveyName.NTGS,\n", + " data=input_data)\n", + "loop_converter.convert()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "9730d1e1", + "metadata": {}, + "outputs": [], + "source": [ + "loop_converter.data[Datatype.FAULT_ORIENTATION].loc[loop_converter.data[Datatype.FAULT_ORIENTATION].index == 31, 'Strike'] = 270\n", + "loop_converter.data[Datatype.FAULT_ORIENTATION].loc[loop_converter.data[Datatype.FAULT_ORIENTATION].index == 28, 'Strike'] = 235\n", + "loop_converter.data[Datatype.FAULT_ORIENTATION].loc[loop_converter.data[Datatype.FAULT_ORIENTATION].index == 30, 'Strike'] = 135\n", + "\n", + "# rows_to_delete = list(range(18, 32))\n", + "# rows_to_delete.remove(20)\n", + "# rows_to_delete.remove(31)\n", + "# loop_converter.data[Datatype.FAULT_ORIENTATION].drop(rows_to_delete, inplace=True)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "cae9f5c2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[18, 19, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "rows_to_delete" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "2793efcf", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
featureIdXYZDipDirDipgeometry
12205221419.1144357.334860e+060.0225.045.0POINT (221419.114 7334860.227)
13248235303.4107657.327076e+060.00.075.0POINT (235303.411 7327075.503)
14329221402.9897137.334874e+060.0180.045.0POINT (221402.99 7334873.574)
15420198101.7862897.317804e+060.045.045.0POINT (198101.786 7317804.047)
16494235948.9841847.329999e+060.0180.075.0POINT (235948.984 7329998.593)
17551241465.8987317.331696e+060.0180.075.0POINT (241465.899 7331696.005)
18554223313.6190697.334932e+060.0180.075.0POINT (223313.619 7334931.516)
\n", + "
" + ], + "text/plain": [ + " featureId X Y Z DipDir Dip \\\n", + "12 205 221419.114435 7.334860e+06 0.0 225.0 45.0 \n", + "13 248 235303.410765 7.327076e+06 0.0 0.0 75.0 \n", + "14 329 221402.989713 7.334874e+06 0.0 180.0 45.0 \n", + "15 420 198101.786289 7.317804e+06 0.0 45.0 45.0 \n", + "16 494 235948.984184 7.329999e+06 0.0 180.0 75.0 \n", + "17 551 241465.898731 7.331696e+06 0.0 180.0 75.0 \n", + "18 554 223313.619069 7.334932e+06 0.0 180.0 75.0 \n", + "\n", + " geometry \n", + "12 POINT (221419.114 7334860.227) \n", + "13 POINT (235303.411 7327075.503) \n", + "14 POINT (221402.99 7334873.574) \n", + "15 POINT (198101.786 7317804.047) \n", + "16 POINT (235948.984 7329998.593) \n", + "17 POINT (241465.899 7331696.005) \n", + "18 POINT (223313.619 7334931.516) " + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "loop_converter.data[Datatype.FAULT_ORIENTATION]" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "826effa7a96f801f", + "metadata": { + "ExecuteTime": { + "end_time": "2024-09-16T05:43:50.453376Z", + "start_time": "2024-09-16T05:43:50.400403Z" + } + }, + "outputs": [], + "source": [ + "path = tempfile.mkdtemp()\n", + "loop_converter.save(Datatype.GEOLOGY, os.path.join(path, \"geology.shp\"), file_extension=\"shp\")\n", + "loop_converter.save(Datatype.STRUCTURE, os.path.join(path, \"structures.shp\"), file_extension=\"shp\")\n", + "loop_converter.save(Datatype.FOLD, os.path.join(path, \"folds.shp\"), file_extension=\"shp\")\n", + "loop_converter.save(Datatype.FAULT, os.path.join(path, \"faults.shp\"), file_extension=\"shp\")\n", + "loop_converter.save(Datatype.FAULT_ORIENTATION, os.path.join(path, \"fault_orientations.shp\"), file_extension=\"shp\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "bf332c206256fae6", + "metadata": { + "ExecuteTime": { + "end_time": "2024-09-16T05:49:13.925502Z", + "start_time": "2024-09-16T05:49:12.424661Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/rabii/Git_Repos/m2l_pypi/lib/python3.11/site-packages/pandas/core/series.py:1031: RuntimeWarning: invalid value encountered in cast\n", + " arr = np.asarray(values, dtype=dtype)\n" + ] + } + ], + "source": [ + "import os\n", + "from map2loop.project import Project\n", + "from map2loop.m2l_enums import VerboseLevel\n", + "from map2loop.m2l_enums import Datatype\n", + "from map2loop.sampler import SamplerSpacing, SamplerDecimator\n", + "from map2loop.sorter import SorterUseHint, SorterUseNetworkX, SorterAgeBased, SorterAlpha, SorterObservationProjections\n", + "from map2loop.thickness_calculator import InterpolatedStructure, StructuralPoint, ThicknessCalculatorAlpha\n", + "import time\n", + "from datetime import datetime\n", + "# nowtime=datetime.now().isoformat(timespec='minutes')\n", + "# model_name=nowtime.replace(\":\",\"-\").replace(\"T\",\"-\")\n", + "loop_project_filename = os.path.join(\"processed_data/HB_map2.loop3d\")\n", + "project_path = tempfile.mkdtemp()\n", + "t0 = time.time()\n", + "structure_config = {\n", + " \"orientation_type\": \"strike\",\n", + " \"dipdir_column\": \"DIPDIR\",\n", + " \"dip_column\": \"DIP\",\n", + " \"description_column\": \"\",\n", + " \"bedding_text\": \"\",\n", + " \"overturned_column\": \"\",\n", + " \"overturned_text\": \"\",\n", + " \"objectid_column\": \"OBJECTID\",\n", + " \"interp_source_column\": \"\",\n", + " }\n", + "ntgs_config = NtgsConfig()\n", + "config = ntgs_config.config_map\n", + "config['structure'] = structure_config\n", + "\n", + "# Initialise the project with the shapefiles, dtm, config file\n", + "# output locations and projection to work in\n", + "proj = Project(\n", + " geology_filename = os.path.join(path, \"geology.shp\"),\n", + " fault_filename = os.path.join(path, \"faults.shp\"),\n", + " fault_orientation_filename = os.path.join(path, \"fault_orientations.shp\"),\n", + " structure_filename = '../test_data/NTGS_data/Henbury/site_HenburyStructure.shp', #os.path.join(path, \"structures.shp\"),\n", + " fold_filename= os.path.join(path, \"folds.shp\"),\n", + " dtm_filename = \"../test_data/NTGS_data/Henbury/DEM.tif\",\n", + " config_dictionary= config,\n", + " clut_filename = '../test_data/NTGS_data/500kibg_colours.csv',\n", + " clut_file_legacy = True,\n", + " verbose_level = VerboseLevel.ALL,\n", + " tmp_path = project_path,\n", + " working_projection = \"EPSG:28353\",#\"EPSG:4283\",\n", + " bounding_box = bounding_box,\n", + " loop_project_filename = loop_project_filename,\n", + " overwrite_loopprojectfile = True\n", + ")\n", + "\n", + "# Remove faults less than 5km\n", + "proj.set_minimum_fault_length(0.0)\n", + "\n", + "# Set sampling distance for geology and fault maps to 200m\n", + "proj.set_sampler(Datatype.GEOLOGY, SamplerSpacing(50.0))\n", + "proj.set_sampler(Datatype.FAULT, SamplerSpacing(200.0))\n", + "\n", + "# Set to only take every second orientation observation (0 or 1 means take all observations)\n", + "proj.set_sampler(Datatype.STRUCTURE, SamplerDecimator(2))\n", + "\n", + "# Set what text is expected for intrusions (contained within the description field)\n", + "# proj.map_data.config.geology_config[\"intrusive_text\"] = \"mafic intrusive\"\n", + "\n", + "# Set specific layers from the geology map to be ignored (commonly \"cover\" or \"water\")\n", + "proj.set_ignore_lithology_codes([\"None\", \"No_formal_name\", \"Julie_Formation\", \"Stokes_Siltstone\",])\n", + "\n", + "proj.set_thickness_calculator(InterpolatedStructure())\n", + "# Specify which stratigraphic columns sorter to use, other options are\n", + "# (SorterAlpha, SorterAgeBased, SorterUseHint, SorterUseNetworkX, SorterMaximiseContacts, SorterObservationProjections)\n", + "proj.set_sorter(SorterObservationProjections())\n", + "\n", + "# Or you can run map2loop and pre-specify the stratigraphic column\n", + "# column = [\n", + "# # youngest\n", + "# 'No_formal_name',\n", + "# 'Chandler_Limestone',\n", + "# 'Arumbera_Sandstone',\n", + "# 'Pertatataka_Formation',\n", + "# 'Waldo_Pedlar_Formation',\n", + "# 'Aralka_Formation',\n", + "# 'Areyonga_Formation',\n", + "# 'Bitter_Springs_Formation'\n", + "# # oldest\n", + "# ]\n", + "\n", + "full_column = [\n", + " # youngest\n", + " 'No_formal_name',\n", + " \"Brewer_Conglomerate\",\n", + " \"Hermannsburg_Sandstone\",\n", + " \"Parke_Siltstone\",\n", + " \"Mereenie_Sandstone\",\n", + " \"Carmichael_Sandstone\",\n", + " \"Stokes_Siltstone\",\n", + " \"Stairway_Sandstone\",\n", + " \"Horn_Valley_Siltstone\",\n", + " \"Pacoota_Sandstone\",\n", + " \"Goyder_Formation\",\n", + " \"Jay_Creek_Limestone\",\n", + " \"Petermann_Sandstone\",\n", + " \"Deception_Formation\",\n", + " \"Illara_Sandstone\",\n", + " \"Tempe_Formation\",\n", + " \"Chandler_Formation\",\n", + " \"Arumbera_Sandstone\",\n", + " \"Namatjira_Formation\",\n", + " \"Puna_Kura_Kura_Formation\",\n", + " \"Julie_Formation\",\n", + " \"Liddle_Formation\",\n", + " \"Pertatataka_Formation\",\n", + " \"Froud_Formation\",\n", + " \"Gloaming_Formation\",\n", + " \"Quandong_Conglomerate\",\n", + " \"Breaden_Formation\",\n", + " \"Pioneer_Sandstone\",\n", + " \"Aralka_Formation\",\n", + " \"Areyonga_Formation\",\n", + " \"Wallara_Formation\",\n", + " \"Johnnys_Creek_Formation\",\n", + " \"Loves_Creek_Formation\",\n", + " \"Gillen_Formation\",\n", + " \n", + " # oldest\n", + "]\n", + "\n", + "column = [\n", + " # 'No_formal_name',\n", + " 'Hermannsburg_Sandstone',\n", + " 'Parke_Siltstone',\n", + " 'Mereenie_Sandstone',\n", + " 'Carmichael_Sandstone',\n", + " 'Stokes_Siltstone',\n", + " 'Stairway_Sandstone',\n", + " 'Horn_Valley_Siltstone',\n", + " 'Pacoota_Sandstone',\n", + " 'Goyder_Formation',\n", + " 'Petermann_Sandstone',\n", + " 'Deception_Formation',\n", + " 'Illara_Sandstone',\n", + " 'Tempe_Formation',\n", + " 'Chandler_Formation',\n", + " 'Arumbera_Sandstone',\n", + " 'Namatjira_Formation',\n", + " 'Julie_Formation',\n", + " 'Pertatataka_Formation',\n", + " 'Areyonga_Formation',\n", + " 'Wallara_Formation',\n", + " 'Johnnys_Creek_Formation',\n", + " 'Loves_Creek_Formation'\n", + " ]\n", + "\n", + "proj.run_all(user_defined_stratigraphic_column=column)\n", + "# Or you can get map2loop to run all column sorting algorithms it has and takes the one\n", + "# that has the longest total basal contact length\n", + "# proj.run_all(take_best=False)\n", + "# proj.run_all()\n", + "\n", + "t1 = time.time()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "696f2d90", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
layerIdnameminAgemaxAgegroupsupergroupstratigraphic_OrderInterpolatedStructure_meanInterpolatedStructure_medianInterpolatedStructure_stddevcodecolour
06Hermannsburg_Sandstone-9999.0-9999.0None0-1.000000-1.000000-1.000000NaN#74fbf4
114Parke_Siltstone-9999.0-9999.0None110109.1777294980.2365267821.054591NaN#e7f2f3
211Mereenie_Sandstone-9999.0-9999.0None25597.3821715595.41104218.757256SD__me_st#F7E8FE
32Carmichael_Sandstone-9999.0-9999.0None3-1.000000-1.000000-1.000000NaN#fb0fda
417Stairway_Sandstone-9999.0-9999.0None5-1.000000-1.000000-1.000000NaN#7c2e17
57Horn_Valley_Siltstone-9999.0-9999.0None6199.423948215.83321065.848605NaN#a2f290
613Pacoota_Sandstone-9999.0-9999.0None72369.0200782072.8278061482.080209NaN#d0d47c
75Goyder_Formation-9999.0-9999.0None810558.04484410494.7278296200.960491NaN#1932e2
816Petermann_Sandstone-9999.0-9999.0None9271.962422294.38739552.004392NaN#5d7e60
94Deception_Formation-9999.0-9999.0None10495.081250582.041223214.459496NaN#8e9aef
108Illara_Sandstone-9999.0-9999.0None111787.6410591583.7122411305.479204NaN#628304
1118Tempe_Formation-9999.0-9999.0None124576.8471174960.2775432581.731438NaN#0c2562
123Chandler_Formation-9999.0-9999.0None1314770.06499215581.4666573177.989877NaN#47d10a
131Arumbera_Sandstone-9999.0-9999.0None147358.9258858668.4907602854.051268NaN#6710c2
1412Namatjira_Formation-9999.0-9999.0None15-1.000000-1.000000-1.000000NaN#a2daa5
1515Pertatataka_Formation-9999.0-9999.0None17-1.000000-1.000000-1.000000NaN#16c432
160Areyonga_Formation-9999.0-9999.0None181287.492015608.8564991259.425613NaN#387866
1719Wallara_Formation-9999.0-9999.0None19144.800069154.25412218.145799NaN#f48b70
189Johnnys_Creek_Formation-9999.0-9999.0None20167.564935177.47702215.589652NaN#106e8a
1910Loves_Creek_Formation-9999.0-9999.0None21-1.000000-1.000000-1.000000NaN#5fb3c5
\n", + "
" + ], + "text/plain": [ + " layerId name minAge maxAge group supergroup \\\n", + "0 6 Hermannsburg_Sandstone -9999.0 -9999.0 None \n", + "1 14 Parke_Siltstone -9999.0 -9999.0 None \n", + "2 11 Mereenie_Sandstone -9999.0 -9999.0 None \n", + "3 2 Carmichael_Sandstone -9999.0 -9999.0 None \n", + "4 17 Stairway_Sandstone -9999.0 -9999.0 None \n", + "5 7 Horn_Valley_Siltstone -9999.0 -9999.0 None \n", + "6 13 Pacoota_Sandstone -9999.0 -9999.0 None \n", + "7 5 Goyder_Formation -9999.0 -9999.0 None \n", + "8 16 Petermann_Sandstone -9999.0 -9999.0 None \n", + "9 4 Deception_Formation -9999.0 -9999.0 None \n", + "10 8 Illara_Sandstone -9999.0 -9999.0 None \n", + "11 18 Tempe_Formation -9999.0 -9999.0 None \n", + "12 3 Chandler_Formation -9999.0 -9999.0 None \n", + "13 1 Arumbera_Sandstone -9999.0 -9999.0 None \n", + "14 12 Namatjira_Formation -9999.0 -9999.0 None \n", + "15 15 Pertatataka_Formation -9999.0 -9999.0 None \n", + "16 0 Areyonga_Formation -9999.0 -9999.0 None \n", + "17 19 Wallara_Formation -9999.0 -9999.0 None \n", + "18 9 Johnnys_Creek_Formation -9999.0 -9999.0 None \n", + "19 10 Loves_Creek_Formation -9999.0 -9999.0 None \n", + "\n", + " stratigraphic_Order InterpolatedStructure_mean \\\n", + "0 0 -1.000000 \n", + "1 1 10109.177729 \n", + "2 2 5597.382171 \n", + "3 3 -1.000000 \n", + "4 5 -1.000000 \n", + "5 6 199.423948 \n", + "6 7 2369.020078 \n", + "7 8 10558.044844 \n", + "8 9 271.962422 \n", + "9 10 495.081250 \n", + "10 11 1787.641059 \n", + "11 12 4576.847117 \n", + "12 13 14770.064992 \n", + "13 14 7358.925885 \n", + "14 15 -1.000000 \n", + "15 17 -1.000000 \n", + "16 18 1287.492015 \n", + "17 19 144.800069 \n", + "18 20 167.564935 \n", + "19 21 -1.000000 \n", + "\n", + " InterpolatedStructure_median InterpolatedStructure_stddev code \\\n", + "0 -1.000000 -1.000000 NaN \n", + "1 4980.236526 7821.054591 NaN \n", + "2 5595.411042 18.757256 SD__me_st \n", + "3 -1.000000 -1.000000 NaN \n", + "4 -1.000000 -1.000000 NaN \n", + "5 215.833210 65.848605 NaN \n", + "6 2072.827806 1482.080209 NaN \n", + "7 10494.727829 6200.960491 NaN \n", + "8 294.387395 52.004392 NaN \n", + "9 582.041223 214.459496 NaN \n", + "10 1583.712241 1305.479204 NaN \n", + "11 4960.277543 2581.731438 NaN \n", + "12 15581.466657 3177.989877 NaN \n", + "13 8668.490760 2854.051268 NaN \n", + "14 -1.000000 -1.000000 NaN \n", + "15 -1.000000 -1.000000 NaN \n", + "16 608.856499 1259.425613 NaN \n", + "17 154.254122 18.145799 NaN \n", + "18 177.477022 15.589652 NaN \n", + "19 -1.000000 -1.000000 NaN \n", + "\n", + " colour \n", + "0 #74fbf4 \n", + "1 #e7f2f3 \n", + "2 #F7E8FE \n", + "3 #fb0fda \n", + "4 #7c2e17 \n", + "5 #a2f290 \n", + "6 #d0d47c \n", + "7 #1932e2 \n", + "8 #5d7e60 \n", + "9 #8e9aef \n", + "10 #628304 \n", + "11 #0c2562 \n", + "12 #47d10a \n", + "13 #6710c2 \n", + "14 #a2daa5 \n", + "15 #16c432 \n", + "16 #387866 \n", + "17 #f48b70 \n", + "18 #106e8a \n", + "19 #5fb3c5 " + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "proj.stratigraphic_column.stratigraphicUnits" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "b8afe13f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
p1_xp1_yp1_zp2_xp2_yp2_zthicknessunit
0195258.2218617.323483e+060.0219768.4817127.338227e+060.0[14761.694488962836, 14486.46185159159, 14813....Hermannsburg_Sandstone
1195257.3043157.323533e+060.0219768.4817127.338227e+060.0[14748.818069292922, 14800.575047250039, 14251...Hermannsburg_Sandstone
2195256.3867697.323583e+060.0219768.4817127.338227e+060.0[14735.975584247035, 14787.68749498505, 14239....Hermannsburg_Sandstone
3195255.4692227.323633e+060.0219768.4817127.338227e+060.0[14774.834085539378, 14226.914841126985, 14827...Hermannsburg_Sandstone
4195254.5516767.323683e+060.0219768.4817127.338227e+060.0[14762.014908098608, 14814.729916642531, 14542...Hermannsburg_Sandstone
...........................
4468219799.4995147.336458e+060.0219580.1088657.337437e+060.0[]Wallara_Formation
4469219819.3681557.336412e+060.0219580.1098177.337437e+060.0[]Wallara_Formation
4470219839.2367967.336366e+060.0219580.1098177.337437e+060.0[]Wallara_Formation
4471219859.1054377.336320e+060.0219580.1098177.337437e+060.0[]Wallara_Formation
4472219878.9740787.336274e+060.0219580.1098177.337437e+060.0[]Wallara_Formation
\n", + "

4473 rows × 8 columns

\n", + "
" + ], + "text/plain": [ + " p1_x p1_y p1_z p2_x p2_y p2_z \\\n", + "0 195258.221861 7.323483e+06 0.0 219768.481712 7.338227e+06 0.0 \n", + "1 195257.304315 7.323533e+06 0.0 219768.481712 7.338227e+06 0.0 \n", + "2 195256.386769 7.323583e+06 0.0 219768.481712 7.338227e+06 0.0 \n", + "3 195255.469222 7.323633e+06 0.0 219768.481712 7.338227e+06 0.0 \n", + "4 195254.551676 7.323683e+06 0.0 219768.481712 7.338227e+06 0.0 \n", + "... ... ... ... ... ... ... \n", + "4468 219799.499514 7.336458e+06 0.0 219580.108865 7.337437e+06 0.0 \n", + "4469 219819.368155 7.336412e+06 0.0 219580.109817 7.337437e+06 0.0 \n", + "4470 219839.236796 7.336366e+06 0.0 219580.109817 7.337437e+06 0.0 \n", + "4471 219859.105437 7.336320e+06 0.0 219580.109817 7.337437e+06 0.0 \n", + "4472 219878.974078 7.336274e+06 0.0 219580.109817 7.337437e+06 0.0 \n", + "\n", + " thickness \\\n", + "0 [14761.694488962836, 14486.46185159159, 14813.... \n", + "1 [14748.818069292922, 14800.575047250039, 14251... \n", + "2 [14735.975584247035, 14787.68749498505, 14239.... \n", + "3 [14774.834085539378, 14226.914841126985, 14827... \n", + "4 [14762.014908098608, 14814.729916642531, 14542... \n", + "... ... \n", + "4468 [] \n", + "4469 [] \n", + "4470 [] \n", + "4471 [] \n", + "4472 [] \n", + "\n", + " unit \n", + "0 Hermannsburg_Sandstone \n", + "1 Hermannsburg_Sandstone \n", + "2 Hermannsburg_Sandstone \n", + "3 Hermannsburg_Sandstone \n", + "4 Hermannsburg_Sandstone \n", + "... ... \n", + "4468 Wallara_Formation \n", + "4469 Wallara_Formation \n", + "4470 Wallara_Formation \n", + "4471 Wallara_Formation \n", + "4472 Wallara_Formation \n", + "\n", + "[4473 rows x 8 columns]" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "proj.thickness_calculator[0].location_tracking" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b7ec7237", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
geometryDIPDIRDIPIDfeatureId
3POINT (198101.786 7317804.047)45.045.00420
1POINT (235303.411 7327075.503)0.075.01248
4POINT (235948.984 7329998.593)180.075.02494
5POINT (241465.899 7331696.005)180.075.03551
0POINT (221419.114 7334860.227)225.045.04205
2POINT (221402.99 7334873.574)180.045.05329
6POINT (223313.619 7334931.516)225.075.06554
\n", + "
" + ], + "text/plain": [ + " geometry DIPDIR DIP ID featureId\n", + "3 POINT (198101.786 7317804.047) 45.0 45.0 0 420\n", + "1 POINT (235303.411 7327075.503) 0.0 75.0 1 248\n", + "4 POINT (235948.984 7329998.593) 180.0 75.0 2 494\n", + "5 POINT (241465.899 7331696.005) 180.0 75.0 3 551\n", + "0 POINT (221419.114 7334860.227) 225.0 45.0 4 205\n", + "2 POINT (221402.99 7334873.574) 180.0 45.0 5 329\n", + "6 POINT (223313.619 7334931.516) 225.0 75.0 6 554" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "proj.map_data.get_map_data(Datatype.FAULT_ORIENTATION)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "a8bb2116", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'column_2' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[7], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m# Create a set from column_2 for faster lookup\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m column_2_set \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mset\u001b[39m(\u001b[43mcolumn_2\u001b[49m)\n\u001b[1;32m 4\u001b[0m \u001b[38;5;66;03m# Reorder column_2 to match the order in column_1\u001b[39;00m\n\u001b[1;32m 5\u001b[0m reordered_column_2 \u001b[38;5;241m=\u001b[39m [formation \u001b[38;5;28;01mfor\u001b[39;00m formation \u001b[38;5;129;01min\u001b[39;00m column_1 \u001b[38;5;28;01mif\u001b[39;00m formation \u001b[38;5;129;01min\u001b[39;00m column_2_set]\n", + "\u001b[0;31mNameError\u001b[0m: name 'column_2' is not defined" + ] + } + ], + "source": [ + "# Create a set from column_2 for faster lookup\n", + "column_2_set = set(column_2)\n", + "\n", + "# Reorder column_2 to match the order in column_1\n", + "reordered_column_2 = [formation for formation in column_1 if formation in column_2_set]\n", + "reordered_column_2" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "e4c0f4fc", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "ERROR: 2025-04-16 12:23:55,401: process_data.py:460 -- Thickness for Pertatataka_Formation is less than or equal to 0\n", + " Update the thickness value for Pertatataka_Formation before continuing\n", + "ERROR: 2025-04-16 12:23:55,402: process_data.py:460 -- Thickness for Namatjira_Formation is less than or equal to 0\n", + " Update the thickness value for Namatjira_Formation before continuing\n", + "ERROR: 2025-04-16 12:23:55,402: process_data.py:460 -- Thickness for Stairway_Sandstone is less than or equal to 0\n", + " Update the thickness value for Stairway_Sandstone before continuing\n", + "ERROR: 2025-04-16 12:23:55,402: process_data.py:460 -- Thickness for Carmichael_Sandstone is less than or equal to 0\n", + " Update the thickness value for Carmichael_Sandstone before continuing\n" + ] + } + ], + "source": [ + "import LoopProjectFile as LPF\n", + "from LoopStructural.modelling.input.project_file import LoopProjectfileProcessor as LPFProcessor\n", + "\n", + "\n", + "LPFilename = loop_project_filename\n", + "projFile = LPF.ProjectFile(LPFilename)\n", + "processedData = LPFProcessor(projFile)\n", + "processedData.thicknesses[\"Hermannsburg_Sandstone\"] = 5e4\n", + "processedData.thicknesses[\"Loves_Creek_Formation\"] = 200\n", + "processedData.thicknesses[\"Namatjira_Formation\"] = 1000\n", + "processedData.thicknesses[\"Pertatataka_Formation\"] = 1000\n", + "processedData.thicknesses[\"Stairway_Sandstone\"] = 1000\n", + "processedData.thicknesses[\"Carmichael_Sandstone\"] = 1000\n", + "processedData.thicknesses[\"Parke_Siltstone\"] = 3000\n", + "processedData.thicknesses[\"Illara_Sandstone\"] = 3000\n", + "processedData.thicknesses[\"Wallara_Formation\"] = 3000\n", + "processedData.thicknesses[\"Areyonga_Formation\"] = 3000" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "82d654f1", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy\n", + "def strike_dip_vector(strike, dip) -> numpy.ndarray:\n", + " \"\"\"\n", + " Calculates the strike-dip vector from the given strike and dip angles.\n", + "\n", + " Args:\n", + " strike (Union[float, list, numpy.ndarray]): The strike angle(s) in degrees.\n", + " dip (Union[float, list, numpy.ndarray]): The dip angle(s) in degrees.\n", + "\n", + " Returns:\n", + " numpy.ndarray: The calculated strike-dip vector(s). Each row corresponds to a vector,\n", + " and the columns correspond to the x, y, and z components of the vector.\n", + " \"\"\"\n", + "\n", + " # If strike or dip is a single value, wrap them into a list\n", + " if not isinstance(strike, (list, numpy.ndarray)):\n", + " strike = [strike]\n", + " if not isinstance(dip, (list, numpy.ndarray)):\n", + " dip = [dip]\n", + "\n", + " # Convert to numpy arrays\n", + " strike = numpy.array(strike)\n", + " dip = numpy.array(dip)\n", + "\n", + " # Initialize a zero matrix with shape (n, 3)\n", + " vec = numpy.zeros((len(strike), 3))\n", + "\n", + " # Convert strike and dip from degrees to radians\n", + " s_r = numpy.deg2rad(strike)\n", + " d_r = numpy.deg2rad(dip)\n", + "\n", + " # Compute the x, y, and z components of the strike-dip vector\n", + " vec[:, 0] = numpy.sin(d_r) * numpy.cos(s_r)\n", + " vec[:, 1] = -numpy.sin(d_r) * numpy.sin(s_r)\n", + " vec[:, 2] = numpy.cos(d_r)\n", + "\n", + " # Normalize the vectors\n", + " norms = numpy.linalg.norm(vec, axis=1)[:, None]\n", + " vec = vec / norms\n", + "\n", + " return vec" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "fabe4ed7", + "metadata": {}, + "outputs": [], + "source": [ + "slip_vec = strike_dip_vector(90, -45)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "c7d546b5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
XYZgxgygzcoordfeature_name
0235948.9841847.329999e+060.0-0.965926-1.182918e-160.2588190Fault_28
1230722.9815707.334040e+060.00.965926-0.000000e+000.2588190Fault_30
2231139.5362827.333190e+060.0-0.707107-8.659561e-170.7071070Gardiner Thrust Fault
\n", + "
" + ], + "text/plain": [ + " X Y Z gx gy gz coord \\\n", + "0 235948.984184 7.329999e+06 0.0 -0.965926 -1.182918e-16 0.258819 0 \n", + "1 230722.981570 7.334040e+06 0.0 0.965926 -0.000000e+00 0.258819 0 \n", + "2 231139.536282 7.333190e+06 0.0 -0.707107 -8.659561e-17 0.707107 0 \n", + "\n", + " feature_name \n", + "0 Fault_28 \n", + "1 Fault_30 \n", + "2 Gardiner Thrust Fault " + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "processedData.fault_orientations" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "id": "62ba67a1", + "metadata": {}, + "outputs": [], + "source": [ + "processedData.fault_properties.displacement = -200" + ] + }, + { + "cell_type": "code", + "execution_count": 97, + "id": "48fddaa6", + "metadata": {}, + "outputs": [], + "source": [ + "# processedData.fault_properties.avgSlipDirEasting = slip_vec[0][0]\n", + "# processedData.fault_properties.avgSlipDirNorthing = slip_vec[0][1]\n", + "# processedData.fault_properties.avgSlipDirAltitude = slip_vec[0][2]\n", + "processedData.fault_properties.avgSlipDirEasting = 1\n", + "processedData.fault_properties.avgSlipDirNorthing = 1\n", + "processedData.fault_properties.avgSlipDirAltitude = 0\n", + "processedData.fault_properties.avgDownthrowDir = 0" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "id": "ca5f247d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
eventIdminAgemaxAgegroupsupergroupenableddisplacementavgDownthrowDirminor_axisintermediate_axis...centreNorthingcentreAltitudeavgSlipDirEastingavgSlipDirNorthingavgSlipDirAltitudeavgNormalEastingavgNormalNorthingavgNormalAltitudeinterpolatortypenelements
fault_name
Fault_1313-1.0-1.00-300NaN16950.96907967803.876316...7.327114e+060.0000.707107NaNNaNNaNFDI1000.0
Fault_1616-1.0-1.00-300NaN3553.29950014213.198002...7.330006e+060.0000.707107NaNNaNNaNFDI1000.0
Fault_1717-1.0-1.00-300NaN14522.97144058091.885760...7.331663e+060.0000.707107NaNNaNNaNFDI1000.0
Fault_1818-1.0-1.00-300NaN5672.68000622690.720023...7.334821e+060.0000.707107NaNNaNNaNFDI1000.0
\n", + "

4 rows × 23 columns

\n", + "
" + ], + "text/plain": [ + " eventId minAge maxAge group supergroup enabled displacement \\\n", + "fault_name \n", + "Fault_13 13 -1.0 -1.0 0 -300 \n", + "Fault_16 16 -1.0 -1.0 0 -300 \n", + "Fault_17 17 -1.0 -1.0 0 -300 \n", + "Fault_18 18 -1.0 -1.0 0 -300 \n", + "\n", + " avgDownthrowDir minor_axis intermediate_axis ... \\\n", + "fault_name ... \n", + "Fault_13 NaN 16950.969079 67803.876316 ... \n", + "Fault_16 NaN 3553.299500 14213.198002 ... \n", + "Fault_17 NaN 14522.971440 58091.885760 ... \n", + "Fault_18 NaN 5672.680006 22690.720023 ... \n", + "\n", + " centreNorthing centreAltitude avgSlipDirEasting \\\n", + "fault_name \n", + "Fault_13 7.327114e+06 0.0 0 \n", + "Fault_16 7.330006e+06 0.0 0 \n", + "Fault_17 7.331663e+06 0.0 0 \n", + "Fault_18 7.334821e+06 0.0 0 \n", + "\n", + " avgSlipDirNorthing avgSlipDirAltitude avgNormalEasting \\\n", + "fault_name \n", + "Fault_13 0 0.707107 NaN \n", + "Fault_16 0 0.707107 NaN \n", + "Fault_17 0 0.707107 NaN \n", + "Fault_18 0 0.707107 NaN \n", + "\n", + " avgNormalNorthing avgNormalAltitude interpolatortype nelements \n", + "fault_name \n", + "Fault_13 NaN NaN FDI 1000.0 \n", + "Fault_16 NaN NaN FDI 1000.0 \n", + "Fault_17 NaN NaN FDI 1000.0 \n", + "Fault_18 NaN NaN FDI 1000.0 \n", + "\n", + "[4 rows x 23 columns]" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "processedData.fault_properties" + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "id": "16138f2adab35552", + "metadata": { + "ExecuteTime": { + "end_time": "2024-09-16T05:55:52.266719Z", + "start_time": "2024-09-16T05:55:52.042873Z" + } + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "6e9e339140b645caa60ed732ce7426f8", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + " 0%| | 0/13 [00:00\n", + "\t \t\n", + "\t \t\n", + "\t \t\n", + ">\n", + "\t4 faults.\n", + "\tFault enabled True\n", + "\t \t\n", + "\t \t\n", + "\t \t\n", + "\t \t" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.features[4].interpolator.add_norm_constraints" + ] + }, + { + "cell_type": "code", + "execution_count": 99, + "id": "f71e73ab080121a8", + "metadata": { + "ExecuteTime": { + "end_time": "2024-08-28T05:37:13.131517Z", + "start_time": "2024-08-28T05:37:13.126426Z" + } + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "5eaea14a829b4ee9a6c6d100dec9fa64", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Widget(value='