diff --git a/.github/workflows/conda.yml b/.github/workflows/conda.yml index 1a9949f1..d60fc74e 100644 --- a/.github/workflows/conda.yml +++ b/.github/workflows/conda.yml @@ -11,7 +11,7 @@ jobs: fail-fast: false matrix: os: ${{ fromJSON(vars.BUILD_OS)}} - python-version: ${{ fromJSON(vars.PYTHON_VERSIONS)}} + python-version: ${{ fromJSON(vars.PYTHON_VERSIONS) }} steps: - uses: conda-incubator/setup-miniconda@v3 with: @@ -27,10 +27,14 @@ jobs: ANACONDA_API_TOKEN: ${{ secrets.ANACONDA_TOKEN }} shell: bash -l {0} run: | + conda config --env --add channels conda-forge + conda config --env --add channels loop3d + conda config --env --set channel_priority strict conda install -c conda-forge conda-build scikit-build-core numpy anaconda-client conda-libmamba-solver -y - conda build -c conda-forge -c loop3d --output-folder conda conda --python ${{matrix.python-version}} + conda config --set solver libmamba + conda build --output-folder conda conda --python ${{matrix.python-version}} anaconda upload --label main conda/*/*.tar.bz2 - + - name: upload artifacts uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/linting_and_testing.yml b/.github/workflows/linting_and_testing.yml index a7ea3a0c..482fcf86 100644 --- a/.github/workflows/linting_and_testing.yml +++ b/.github/workflows/linting_and_testing.yml @@ -1,7 +1,20 @@ name: Linting and Testing on: - [push] + push: + branches: + - master + paths: + - '**.py' + - .github/workflows/linting_and_testing.yml + + pull_request: + branches: + - master + paths: + - '**.py' + - .github/workflows/linting_and_testing.yml + workflow_dispatch: jobs: linting: @@ -18,17 +31,23 @@ jobs: - uses: stefanzweifel/git-auto-commit-action@v5 with: commit_message: "style: style fixes by ruff and autoformatting by black" - + + testing: - name: Testing - runs-on: ubuntu-24.04 + name: Testing${{ matrix.os }} python ${{ matrix.python-version }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: ${{ fromJSON(vars.BUILD_OS)}} + python-version: ${{ fromJSON(vars.PYTHON_VERSIONS)}} steps: - uses: actions/checkout@v4 - - name: Install GDAL - run: | - sudo add-apt-repository ppa:ubuntugis/ubuntugis-unstable - sudo apt-get update - sudo apt-get install -y libgdal-dev gdal-bin + - uses: conda-incubator/setup-miniconda@v3 + with: + python-version: ${{ matrix.python }} + conda-remove-defaults: "true" + - name: Install dependencies run: | diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index 50e8d505..a2f47d5c 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -6,7 +6,7 @@ jobs: sdist: name: Build sdist - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 @@ -23,7 +23,7 @@ jobs: publish: name: Publish wheels to pypi - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest permissions: # IMPORTANT: this permission is mandatory for trusted publishing id-token: write diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 1f73031b..144739cd 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "3.2.0" + ".": "3.2.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 016d06e0..a78587db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## [3.2.2](https://github.com/Loop3D/map2loop/compare/v3.2.1...v3.2.2) (2025-01-13) + + +### Bug Fixes + +* add featureId when parsing fault_orientations ([#177](https://github.com/Loop3D/map2loop/issues/177)) ([924c2cf](https://github.com/Loop3D/map2loop/commit/924c2cf696688abe3dc1c8579753daeb2f1b45e4)) + +## [3.2.1](https://github.com/Loop3D/map2loop/compare/v3.2.0...v3.2.1) (2025-01-12) + + +### Bug Fixes + +* include dependencies in site-packages - issue [#169](https://github.com/Loop3D/map2loop/issues/169) ([#170](https://github.com/Loop3D/map2loop/issues/170)) ([b33532b](https://github.com/Loop3D/map2loop/commit/b33532b56473148433fd192e182aadee028dc875)) + ## [3.2.0](https://github.com/Loop3D/map2loop/compare/v3.1.13...v3.2.0) (2024-12-16) diff --git a/MANIFEST.in b/MANIFEST.in index 67577e81..02b24a35 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,8 +1,3 @@ -include LICENSE -include README.md - include map2loop/_datasets/clut_files/*.csv include map2loop/_datasets/config_files/*.json -include map2loop/_datasets/geodata_files/hamersley/* - -recursive-include tests *.py \ No newline at end of file +include map2loop/_datasets/geodata_files/hamersley/* \ No newline at end of file diff --git a/README.md b/README.md index 144bc856..35a6909e 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@ ![GitHub Release](https://img.shields.io/github/v/release/loop3d/map2loop) +[![DOI](https://img.shields.io/static/v1?label=DOI&message=10.5194/gmd-14-5063-2021&color=blue)](https://doi.org/10.5194/gmd-14-5063-2021) ![License](https://img.shields.io/github/license/loop3d/map2loop) ![PyPI - Downloads](https://img.shields.io/pypi/dm/map2loop?label=pip%20downloads) ![Conda Downloads](https://img.shields.io/conda/dn/loop3d/map2loop?label=Conda%20downloads) [![Testing](https://github.com/Loop3D/map2loop/actions/workflows/linting_and_testing.yml/badge.svg)](https://github.com/Loop3D/map2loop/actions/workflows/linting_and_testing.yml) [![Build and Deploy Documentation](https://github.com/Loop3D/map2loop/actions/workflows/documentation.yml/badge.svg)](https://github.com/Loop3D/map2loop/actions/workflows/documentation.yml) -# Map2Loop 3.1 + +# Map2Loop 3.2 Generate 3D geological model inputs from geological maps — a high-level implementation and extension of the original map2loop code developed by Prof. Mark Jessell at UWA. To see an example interactive model built with map2loop and LoopStructural, follow this link: @@ -13,88 +15,56 @@ Generate 3D geological model inputs from geological maps — a high-level implem ## Install -You will need some flavour of conda (a Python package manager, [see here](https://docs.anaconda.com/anaconda/install/index.html)), as well as Python ≥ 3.8. +#### Option 1: Install with Anaconda -### Adding ```conda-forge``` to Anaconda channels -map2loop installation may run smoother if ```conda-forge``` is added to the channels. -To check for that, run +This is the simplest and recommended installation process, with: ```bash -conda config --show channels +conda install -c loop3d -c conda-forge map2loop ``` -if conda-forge is not in the output, the channel can be added with: +#### Option 2: Install with pip +Installation with pip will require that GDAL is installed on your system prior to map2loop installation. +This is because GDAL cannot be installed via pip (at least not with one line of code), and the GDAL installation process will vary depending on your OS. + +For more information on installing gdal, see GDAL's Pypi page. + +Once GDAL is available on your system, map2loop can be installed with: ```bash -conda config --add channels conda-forge +pip install map2loop ``` -### Run - -To just use map2loop, issue the following. * +#### Option 3: From source ```bash git clone https://github.com/Loop3D/map2loop.git cd map2loop -conda install -c loop3d --file dependencies.txt +conda install gdal + +conda install -c loop3d -c conda-forge --file dependencies.txt pip install . ``` -

* We're actively working towards a better approach - stay tuned!

- -### Documentation - -If you can call it that, is available here - -### Development - -If you want to tinker yourself/contribute, clone the source code with - +#### Option 4: From source & developer mode: ```bash git clone https://github.com/Loop3D/map2loop.git -``` -Or get the source + example notebooks with +cd map2loop -```bash -git clone https://github.com/Loop3D/map2loop.git -git clone https://github.com/Loop3D/map2loop-3-notebooks -``` +conda install gdal -Navigate into map2loop, and issue the following to install map2loop and its dependencies. _Note_: The 'develop' flag makes your source changes take effect on saving, so you only need to run this once +conda install -c loop3d -c conda-forge --file dependencies.txt -```bash -conda install -c loop3d --file dependencies.txt pip install -e . ``` -## Building with Docker - -Fair warning, we recommend conda to almost everyone. With great software development power comes great environment setup inconvenience. You'll need to download and install the [docker containerisation software](https://docs.docker.com/get-docker/), and the docker and docker-compose CLI. - -### Development - -1. Clone this repo and navigate inside as per above -2. Run the following and click on the Jupyter server forwarded link to access and edit the notebooks - - ```bash - docker-compose up --build - ``` - -3. To hop into a bash shell in a running container, open a terminal and issue - - ```bash - docker ps - ``` +### Documentation - Find the container name or ID and then run +Map2loop's documentation is available here - ```bash - docker exec -it bash - # Probably -> docker exec -it map2loop_dev_1 bash - ``` ## Usage @@ -118,8 +88,6 @@ bbox_3d = { } ``` -![sa example](docs/Untitled.png?raw=true) - Then, specify: the state, directory for the output, the bounding box and projection from above - and hit go! That's it. ```python diff --git a/conda/bld.bat b/conda/bld.bat new file mode 100644 index 00000000..79ddb243 --- /dev/null +++ b/conda/bld.bat @@ -0,0 +1,5 @@ +mkdir %SP_DIR%\map2loop +copy %RECIPE_DIR%\..\LICENSE %SP_DIR%\map2loop\ +copy %RECIPE_DIR%\..\README.md %SP_DIR%\map2loop\ +copy %RECIPE_DIR%\..\dependencies.txt %SP_DIR%\map2loop\ +%PYTHON% -m pip install . \ No newline at end of file diff --git a/conda/build.sh b/conda/build.sh new file mode 100644 index 00000000..ca06369b --- /dev/null +++ b/conda/build.sh @@ -0,0 +1,6 @@ +#!/bin/bash +mkdir -p $SP_DIR/map2loop +cp $RECIPE_DIR/../dependencies.txt $SP_DIR/map2loop/ +cp $RECIPE_DIR/../LICENSE $SP_DIR/map2loop/ +cp $RECIPE_DIR/../README.md $SP_DIR/map2loop/ +$PYTHON -m pip install . \ No newline at end of file diff --git a/conda/conda-build.py b/conda/conda-build.py deleted file mode 100644 index e56347e3..00000000 --- a/conda/conda-build.py +++ /dev/null @@ -1,85 +0,0 @@ -from subprocess import Popen, PIPE -from concurrent.futures import ThreadPoolExecutor -import tqdm -import sys -import os - - -def process(python_version): - try: - - command = 'conda build --py {} .'.format( - python_version) - p = Popen( - command.split(), stdin=PIPE, stdout=PIPE, stderr=PIPE) - except Exception as e: - return e - - output, err = p.communicate() - return str(err.decode('ascii')) - - -def build(packages): - for dirname in packages: - if not os.path.exists(dirname): - sys.exit( - "Make sure each package directory is at the same level as this script.") - - python_versions = [3.9, 3.10, 3.11, 3.12] - print("Building " + " ".join(packages), - "for python versions {}".format(python_versions)) - - with ThreadPoolExecutor(15) as executor: - output = list( - tqdm.tqdm(executor.map(process, python_versions), total=len(packages))) - - for message in output: - lines = message.split('\\n') - for line in lines: - print(line) - - -def upload(root_buildpath): - - # Create OSX packages from linux versions - output = '' - command = "conda convert -p osx-64 {}*tar.bz2 -o {} -f".format( - root_buildpath + 'linux-64/', root_buildpath) - p = Popen( - command, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE) - output += p.communicate()[0].decode('ascii') - output += p.communicate()[1].decode('ascii') - command = "conda convert -p win-64 {}*tar.bz2 -o {} -f".format( - root_buildpath + 'linux-64/', root_buildpath) - p = Popen( - command, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE) - output += p.communicate()[0].decode('ascii') - output += p.communicate()[1].decode('ascii') - - command = "anaconda upload {} {} {} --force".format( - root_buildpath + 'linux-64/*.tar.bz2', - root_buildpath + 'osx-64/*.tar.bz2', - root_buildpath + 'win-64/*tar.bz2', - ) - p = Popen( - command, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE) - output += p.communicate()[0].decode('ascii') - output += p.communicate()[1].decode('ascii') - print(output) - - -if __name__ == "__main__": - - packages = [ - '.' - ] - - command = 'which conda' - p = Popen( - command.split(), stdin=PIPE, stdout=PIPE, stderr=PIPE) - std, err = p.communicate() - root_buildpath = "/".join(std.decode('ascii').split('/') - [:-2]) + '/conda-bld/' - - build(packages) - # upload(root_buildpath) diff --git a/conda/conda_build_config.yaml b/conda/conda_build_config.yaml deleted file mode 100644 index d7d85008..00000000 --- a/conda/conda_build_config.yaml +++ /dev/null @@ -1,6 +0,0 @@ -python: - - 3.8 - - 3.9 - - 3.10 - - 3.11 - - 3.12 diff --git a/conda/meta.yaml b/conda/meta.yaml index 4da8e118..4abf63a8 100644 --- a/conda/meta.yaml +++ b/conda/meta.yaml @@ -7,10 +7,8 @@ package: source: git_url: https://github.com/Loop3D/map2loop - build: number: 0 - script: "{{ PYTHON }} -m pip install . --no-deps" requirements: host: @@ -20,17 +18,17 @@ requirements: run: - loopprojectfile ==0.2.2 - gdal - - map2model - - beartype - python - numpy - - pandas + - scipy - geopandas - shapely - - tqdm - networkx - owslib - + - map2model + - beartype + - pytest + - scikit-learn about: home: "https://github.com/Loop3D/map2loop" diff --git a/dependencies.txt b/dependencies.txt index 57a2f246..7a1d95e7 100644 --- a/dependencies.txt +++ b/dependencies.txt @@ -1,4 +1,3 @@ - numpy scipy geopandas diff --git a/map2loop/__init__.py b/map2loop/__init__.py index 17428522..d7ccac11 100644 --- a/map2loop/__init__.py +++ b/map2loop/__init__.py @@ -30,7 +30,7 @@ class DependencyChecker: def __init__(self, package_name, dependency_file="dependencies.txt"): self.package_name = package_name - self.dependency_file = pathlib.Path(__file__).parent.parent / dependency_file + self.dependency_file = pathlib.Path(__file__).parent / dependency_file self.required_version = self.get_required_version() self.installed_version = self.get_installed_version() @@ -93,7 +93,7 @@ def check_version(self): def check_all_dependencies(dependency_file="dependencies.txt"): - dependencies_path = pathlib.Path(__file__).parent.parent / dependency_file + dependencies_path = pathlib.Path(__file__).parent / dependency_file try: with dependencies_path.open("r") as file: for line in file: @@ -108,6 +108,7 @@ def check_all_dependencies(dependency_file="dependencies.txt"): checker = DependencyChecker(package_name, dependency_file=dependency_file) checker.check_version() + except FileNotFoundError: warnings.warn( f"{dependency_file} not found. No dependencies checked for map2loop.", diff --git a/map2loop/mapdata.py b/map2loop/mapdata.py index 4ef0be89..6948d3c3 100644 --- a/map2loop/mapdata.py +++ b/map2loop/mapdata.py @@ -567,7 +567,7 @@ def __retrieve_tif(self, filename: str): if filename.lower() == "aus" or filename.lower() == "au": logger.info('Using Geoscience Australia DEM') - url = "http://services.ga.gov.au/gis/services/DEM_SRTM_1Second_over_Bathymetry_Topography/MapServer/WCSServer?" + url = "https://services.ga.gov.au/gis/services/Bathymetry_Topography/MapServer/WCSServer?" wcs = WebCoverageService(url, version="1.0.0") coverage = wcs.getCoverage( @@ -730,7 +730,7 @@ def parse_fault_orientations(self) -> tuple: # Parse dip direction and dip columns if config["dipdir_column"] in self.raw_data[Datatype.FAULT_ORIENTATION]: if config["orientation_type"] == "strike": - fault_orientations["DIPDIR"] = self.raw_data[Datatype.STRUCTURE].apply( + fault_orientations["DIPDIR"] = self.raw_data[Datatype.FAULT_ORIENTATION].apply( lambda row: (row[config["dipdir_column"]] + 90.0) % 360.0, axis=1 ) else: @@ -763,6 +763,14 @@ def parse_fault_orientations(self) -> tuple: else: fault_orientations["ID"] = numpy.arange(len(fault_orientations)) self.data[Datatype.FAULT_ORIENTATION] = fault_orientations + + if config["featureid_column"] in self.raw_data[Datatype.FAULT_ORIENTATION]: + fault_orientations["featureId"] = self.raw_data[Datatype.FAULT_ORIENTATION][ + config["featureid_column"] + ] + else: + fault_orientations["featureId"] = numpy.arange(len(fault_orientations)) + return (False, "") @beartype.beartype diff --git a/map2loop/version.py b/map2loop/version.py index 11731085..1e3bed4c 100644 --- a/map2loop/version.py +++ b/map2loop/version.py @@ -1 +1 @@ -__version__ = "3.2.0" +__version__ = "3.2.2" diff --git a/pyproject.toml b/pyproject.toml index c5d1b68a..4ecb4207 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,11 @@ version = { attr = "map2loop.version.__version__" } [tool.setuptools.packages.find] include = ['map2loop', 'map2loop.*'] - +[tool.setuptools.package-data] +map2loop = [ + "dependencies.txt", + "_datasets/geodata_files/hamersley/*" +] [tool.black] line-length = 100 diff --git a/setup.py b/setup.py index 7cea5025..21383418 100644 --- a/setup.py +++ b/setup.py @@ -1,28 +1,49 @@ -"""See pyproject.toml for project metadata.""" +from setuptools.command.sdist import sdist as _sdist +import shutil +from setuptools import setup, find_packages +from pathlib import Path -import os -from setuptools import setup - -package_root = os.path.abspath(os.path.dirname(__file__)) +# Resolve the absolute path to the directory containing this file: +package_root = Path(__file__).resolve().parent # Get the version from the version.py file version = {} -with open(os.path.join(package_root, "map2loop/version.py")) as fp: +version_file = package_root / "map2loop" / "version.py" +with version_file.open() as fp: exec(fp.read(), version) -version = version["__version__"] # Read dependencies from dependencies.txt -requirements_file = os.path.join(package_root, "dependencies.txt") -with open(requirements_file, 'r') as f: +requirements_file = package_root / "dependencies.txt" +with requirements_file.open("r") as f: install_requires = [line.strip() for line in f if line.strip()] + +class CustomSDist(_sdist): + + def make_release_tree(self, base_dir, files): + # 1) Let the normal sdist process run first. + super().make_release_tree(base_dir, files) + map2loop_dir = Path(base_dir) / "map2loop" + + # 2) Specify which files to move from the root to map2loop/. + top_level_files = ["dependencies.txt", "LICENSE", "README.md"] + + for filename in top_level_files: + src = Path(base_dir) / filename + dst = map2loop_dir / filename + + # If the source file exists in base_dir, move it to map2loop/. + if src.exists(): + shutil.copy(str(src), str(dst)) + setup( name="map2loop", - install_requires=install_requires, - version=version, + install_requires=install_requires, + packages=find_packages(exclude=["tests", "tests.*"]), + package_data={"": ['dependencies.txt']}, + include_package_data=True, license="MIT", - package_data={ - # Include test files: - '': ['tests/*.py'], + cmdclass={ + "sdist": CustomSDist, }, -) \ No newline at end of file +)