Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
658ae0b
fix: stratigraphic alpha sorter was inverted, reverse sorter
lachlangrose Nov 1, 2024
71fb2e3
ignore units without contact for max contact length sorter
lachlangrose Nov 1, 2024
b3cbb0a
updated orientation sorter to use correct trigonometry for strike/dip…
lachlangrose Nov 4, 2024
768d0c7
revert back to original orientation sorter
lachlangrose Nov 6, 2024
7769a27
style: black
lachlangrose Nov 6, 2024
cd8e046
updating WA json to work
lachlangrose Nov 6, 2024
d973151
remove unused argument documentation
lachlangrose Nov 7, 2024
46f296f
only run doc build on master
lachlangrose Nov 7, 2024
11746c4
use geopandas to produce topology graphs instead of map2model.
lachlangrose Nov 7, 2024
60a9c2e
update sorter to not use stratigraphic order hint and deprecate hint …
lachlangrose Nov 7, 2024
f5e9da4
update deformation history to use eventId not name for merging
lachlangrose Nov 8, 2024
d21b2c8
bipass old map2model run
lachlangrose Nov 8, 2024
b80722e
use property accessor to make sure that variables are up to date
lachlangrose Nov 8, 2024
8c21386
remove sorted units from sorter call
lachlangrose Nov 8, 2024
c1defdb
removing comment and condaforge channel
lachlangrose Nov 8, 2024
6a006fe
Merge branch 'master' into fix/remove-m2m
lachlangrose Nov 8, 2024
a2dbbfc
fix: remove map2model call from map2loop
lachlangrose Nov 8, 2024
b080860
update networkx sorter to have a name2index map
lachlangrose Nov 8, 2024
fd30a85
Merge branch 'v3.2' into fix/remove-m2m
lachlangrose Nov 25, 2024
ac4de17
fix: add mode to choose betweeen m2m and geopandas
lachlangrose Nov 26, 2024
d71c527
remove gdal fix
lachlangrose Nov 27, 2024
ea43d4a
Merge branch 'v3.2' into fix/remove-m2m
lachlangrose Nov 27, 2024
ad6c5fe
linting
lachlangrose Nov 27, 2024
8856796
Merge branch 'fix/remove-m2m' of github.com:Loop3D/map2loop into fix/…
lachlangrose Nov 27, 2024
ab049dd
style: black formatting
lachlangrose Nov 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dependencies.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ owslib
map2model
loopprojectfile==0.1.4
beartype
gdal==3.8.4
gdal
pytest
scikit-learn
3 changes: 1 addition & 2 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# all on conda forge
sphinx
sphinx-gallery
pydata-sphinx-theme
myst-parser
sphinxcontrib-bibtex
conda-forge::poppler
poppler
LoopStructural
43 changes: 28 additions & 15 deletions map2loop/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

import logging

loggers = {}
ch = logging.StreamHandler()
formatter = logging.Formatter("%(levelname)s: %(asctime)s: %(filename)s:%(lineno)d -- %(message)s")
Expand All @@ -9,22 +9,25 @@
from .version import __version__

import warnings # TODO: convert warnings to logging
from packaging import version as pkg_version # used instead of importlib.version because adheres to PEP 440 using pkg_version.parse
from packaging import (
version as pkg_version,
) # used instead of importlib.version because adheres to PEP 440 using pkg_version.parse
import pathlib
import re
import pkg_resources #import get_distribution, DistributionNotFound # Use pkg_resources for version checking
import pkg_resources # import get_distribution, DistributionNotFound # Use pkg_resources for version checking


class DependencyChecker:
'''
A class to check installation and version compatibility of each package in dependencies.txt

Attributes:
package_name (str): Name of the package
dependency_file (str): path to dependencies.txt
required_version (str or None): required version of the package as in dependencies.txt
installed_version (str or None): installed version of the package in the current environment
'''

def __init__(self, package_name, dependency_file="dependencies.txt"):

self.package_name = package_name
Expand All @@ -35,7 +38,7 @@ def __init__(self, package_name, dependency_file="dependencies.txt"):
## 1) get required version of each dependency from dependencies.txt
def get_required_version(self):
'''
Get the required package version for each package from dependencies.txt;
Get the required package version for each package from dependencies.txt;

Returns:
str or None: The required version of the package (if specified), otherwise None.
Expand All @@ -56,15 +59,15 @@ def get_required_version(self):
except FileNotFoundError: # in case dependencies.txt is not found (shouldn't happen)
warnings.warn(
f"{self.dependency_file} not found. Unable to check {self.package_name} version compatibility.",
UserWarning
UserWarning,
)
return None

## 2) Check the installed version of the dependency and compare with required version
def get_installed_version(self):
'''
Get the installed version of the package.

Returns:
str: The installed version of the package.
'''
Expand All @@ -73,31 +76,38 @@ def get_installed_version(self):
return pkg_resources.get_distribution(self.package_name).version
except pkg_resources.DistributionNotFound:
# Raise ImportError if package is not installed
raise ImportError(f"{self.package_name} is not installed. Please install {self.package_name}.")
raise ImportError(
f"{self.package_name} is not installed. Please install {self.package_name}."
)

## 3) Compare the installed version of the dependency with the required version, if applicable
def check_version(self):
'''
Checks if the installed version of the package matches the required version uin the dependencies.txt file.

'''
if self.required_version is None:
# No required version specified, only check if installed
if self.installed_version is None:
raise ImportError(f"{self.package_name} is not installed. Please install {self.package_name} before using map2loop.")
raise ImportError(
f"{self.package_name} is not installed. Please install {self.package_name} before using map2loop."
)
# else:
# print(f"{self.package_name} is installed.")
else:
# Compare versions if required version is specified
if self.installed_version is None or pkg_version.parse(self.installed_version) != pkg_version.parse(self.required_version):
if self.installed_version is None or pkg_version.parse(
self.installed_version
) != pkg_version.parse(self.required_version):
raise ImportError(
f"Installed version of {self.package_name}=={self.installed_version} does not match required version=={self.required_version}. "
f"Please install the correct version of {self.package_name}."
)

# else:
# print(f"{self.package_name} version is compatible.")


# check all dependencies & versions in dependencies.txt
def check_all_dependencies(dependency_file="dependencies.txt"):
dependencies_path = pathlib.Path(__file__).parent.parent / dependency_file
Expand All @@ -114,12 +124,15 @@ def check_all_dependencies(dependency_file="dependencies.txt"):
package_name, _ = line.split("==")
else:
package_name = line

# check version for each package
checker = DependencyChecker(package_name, dependency_file=dependency_file)
checker.check_version()
except FileNotFoundError:
ImportError(f"{dependency_file} not found. No dependencies checked for map2loop", UserWarning)
ImportError(
f"{dependency_file} not found. No dependencies checked for map2loop", UserWarning
)


# Run check for all dependencies listed in dependencies.txt
check_all_dependencies()
16 changes: 12 additions & 4 deletions map2loop/_datasets/geodata_files/load_map2loop_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import pkg_resources
from osgeo import gdal


def load_hamersley_geology():
"""
Loads Hamersley geology data from a shapefile
Expand All @@ -13,7 +14,9 @@ def load_hamersley_geology():
Returns:
geopandas.GeoDataFrame: The geology data
"""
stream = pkg_resources.resource_filename("map2loop", "/_datasets/geodata_files/hamersley/geology.geojson")
stream = pkg_resources.resource_filename(
"map2loop", "/_datasets/geodata_files/hamersley/geology.geojson"
)
return geopandas.read_file(stream)


Expand All @@ -28,16 +31,21 @@ def load_hamersley_structure():
Returns:
geopandas.GeoDataFrame: The structure data
"""

path = pkg_resources.resource_filename("map2loop", "/_datasets/geodata_files/hamersley/structure.geojson")

path = pkg_resources.resource_filename(
"map2loop", "/_datasets/geodata_files/hamersley/structure.geojson"
)
return geopandas.read_file(path)


def load_hamersley_dtm():
"""
Load DTM data from a raster file

Returns:
gdal.Dataset: The DTM data
"""
path = pkg_resources.resource_filename("map2loop", "/_datasets/geodata_files/hamersley/dtm_rp.tif")
path = pkg_resources.resource_filename(
"map2loop", "/_datasets/geodata_files/hamersley/dtm_rp.tif"
)
return gdal.Open(path)
38 changes: 26 additions & 12 deletions map2loop/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

logger = getLogger(__name__)


class Config:
"""
A data structure containing column name mappings for files and keywords
Expand Down Expand Up @@ -65,7 +66,7 @@ def __init__(self):
"dipestimate_text": "'NORTH_EAST','NORTH',<rest of cardinals>,'NOT ACCESSED'",
"name_column": "NAME",
"objectid_column": "ID",
"minimum_fault_length": -1., #negative -1 means not set
"minimum_fault_length": -1.0, # negative -1 means not set
"ignore_fault_codes": [None],
}
self.fold_config = {
Expand Down Expand Up @@ -103,25 +104,33 @@ def update_from_dictionary(self, dictionary: dict, lower: bool = False):
self.structure_config.update(dictionary["structure"])
for key in dictionary["structure"].keys():
if key not in self.structure_config:
logger.warning(f"Config dictionary structure segment contained {key} which is not used")
logger.warning(
f"Config dictionary structure segment contained {key} which is not used"
)
dictionary.pop("structure")
if "geology" in dictionary:
self.geology_config.update(dictionary["geology"])
for key in dictionary["geology"].keys():
if key not in self.geology_config:
logger.warning(f"Config dictionary geology segment contained {key} which is not used")
logger.warning(
f"Config dictionary geology segment contained {key} which is not used"
)
dictionary.pop("geology")
if "fault" in dictionary:
self.fault_config.update(dictionary["fault"])
for key in dictionary["fault"].keys():
if key not in self.fault_config:
logger.warning(f"Config dictionary fault segment contained {key} which is not used")
logger.warning(
f"Config dictionary fault segment contained {key} which is not used"
)
dictionary.pop("fault")
if "fold" in dictionary:
self.fold_config.update(dictionary["fold"])
for key in dictionary["fold"].keys():
if key not in self.fold_config:
logger.warning(f"Config dictionary fold segment contained {key} which is not used")
logger.warning(
f"Config dictionary fold segment contained {key} which is not used"
)
dictionary.pop("fold")
if len(dictionary):
logger.warning(f"Unused keys from config format {list(dictionary.keys())}")
Expand Down Expand Up @@ -206,7 +215,7 @@ def update_from_file(
try:
filename = str(filename)

#if url, open the url
# if url, open the url
if filename.startswith("http") or filename.startswith("ftp"):
try_count = 5
success = False
Expand All @@ -218,36 +227,41 @@ def update_from_file(
func(data, lower)
success = True

#case 1. handle url error
# case 1. handle url error
except urllib.error.URLError as e:
# wait 0.25 seconds before trying again
time.sleep(0.25)
# decrease the number of tries by 1
try_count -= 1
# if no more tries left, raise the error
if try_count <= 0:
raise urllib.error.URLError(f"Failed to access URL after multiple attempts: {filename}") from e
raise urllib.error.URLError(
f"Failed to access URL after multiple attempts: {filename}"
) from e

# case 2. handle json error
except json.JSONDecodeError as e:
raise json.JSONDecodeError(
f"Error decoding JSON data from URL: {filename}") from e
f"Error decoding JSON data from URL: {filename}"
) from e
else:
try:
with open(filename) as file_data:
data = json.load(file_data)
func(data, lower)
except FileNotFoundError as e:
err_string = f"The specified config file does not exist ({filename}).\n"
err_string += "Please check the file exists and is accessible, then try again.\n"
err_string += (
"Please check the file exists and is accessible, then try again.\n"
)
raise FileNotFoundError(err_string) from e
except json.JSONDecodeError as e:
raise json.JSONDecodeError(
f"Error decoding JSON data from file: {filename}"
) from e

except FileNotFoundError:
raise
raise

except Exception:
err_string = f"There is a problem parsing the config file ({filename}).\n"
Expand All @@ -258,4 +272,4 @@ def update_from_file(
if not legacy_format:
err_string += "Also check if this is a legacy config file and add clut_file_legacy=True to the Project function\n"
err_string += "Check the contents for mismatched quotes or brackets!"
raise Exception(err_string)
raise Exception(err_string)
3 changes: 0 additions & 3 deletions map2loop/deformation_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,9 +284,6 @@ def get_fault_relationships_with_ids(self, fault_fault_relationships: pandas.Dat

faultIds = self.get_faults_for_export()[["eventId", "name"]].copy()
rel = fault_fault_relationships.copy()
rel['Fault1'] = rel['Fault1'].astype(str)
rel['Fault2'] = rel['Fault2'].astype(str)
faultIds['eventId'] = faultIds['eventId'].astype(str)
rel = rel.merge(faultIds, left_on="Fault1", right_on="eventId")
rel.rename(columns={"eventId": "eventId1"}, inplace=True)
rel.drop(columns=["name"], inplace=True)
Expand Down
4 changes: 3 additions & 1 deletion map2loop/fault_orientation.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from .logging import getLogger

logger = getLogger(__name__)


class FaultOrientation(ABC):
"""
Base Class of Fault Orientation assigner to force structure of FaultOrientation
Expand Down Expand Up @@ -85,7 +87,7 @@ def calculate(
logger.info("Assigning fault orientations to fault traces from nearest orientation")
orientations = fault_orientations.copy()
logger.info(f'There are {len(orientations)} fault orientations to assign')

orientations["ID"] = -1

for i in orientations.index:
Expand Down
6 changes: 4 additions & 2 deletions map2loop/interpolators.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,10 +345,12 @@ def setup_interpolation(self, structure_data: pandas.DataFrame):
# Check if there are any clusters with more than one point (indicating collocated points)
collocated_clusters = structure_data['cluster'].value_counts()
collocated_clusters = collocated_clusters[collocated_clusters > 1]

if not collocated_clusters.empty:
# Log a warning if collocated points are detected
logging.warning(f"Detected {len(collocated_clusters)} collocated point clusters. Aggregating these points.")
logging.warning(
f"Detected {len(collocated_clusters)} collocated point clusters. Aggregating these points."
)

# Aggregate data for collocated points by taking the mean of X, Y, DIP, and DIPDIR within each cluster
aggregated_data = (
Expand Down
15 changes: 11 additions & 4 deletions map2loop/logging.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import logging
import map2loop


def get_levels():
"""dict for converting to logger levels from string

Expand All @@ -16,7 +18,8 @@ def get_levels():
"debug": logging.DEBUG,
}

def getLogger(name:str):

def getLogger(name: str):
"""Get a logger object with a specific name


Expand All @@ -32,13 +35,17 @@ def getLogger(name:str):
"""
if name in map2loop.loggers:
return map2loop.loggers[name]
logger = logging.getLogger(name)
logger = logging.getLogger(name)
logger.addHandler(map2loop.ch)
logger.propagate = False
map2loop.loggers[name] = logger
return logger


logger = getLogger(__name__)
def set_level(level:str):


def set_level(level: str):
"""Set the level of the logging object


Expand All @@ -54,4 +61,4 @@ def set_level(level:str):
for name in map2loop.loggers:
logger = logging.getLogger(name)
logger.setLevel(level)
logger.info(f"Logging level set to {level}")
logger.info(f"Logging level set to {level}")
Loading
Loading