Skip to content

Commit

Permalink
Merge branch 'GlacioHack:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
rhugonnet authored Jun 17, 2021
2 parents f7f6108 + 2a389a0 commit 9c97f54
Show file tree
Hide file tree
Showing 37 changed files with 3,823 additions and 1,252 deletions.
55 changes: 55 additions & 0 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions

name: build

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build:
name: ${{ matrix.os }}, python ${{ matrix.python-version}}
runs-on: ${{ matrix.os }}

strategy:
matrix:
os: ["ubuntu-latest"]
python-version: ["3.7", "3.8", "3.9"]


# Run all shells using bash (including Windows)
defaults:
run:
shell: bash -l {0}

steps:
- uses: actions/checkout@v2

# Set up the conda-forge environment with mamba
- name: Set up Python ${{ matrix.python-version }} and dependencies
uses: conda-incubator/setup-miniconda@v2
with:
auto-update-conda: true
python-version: ${{ matrix.python-version }}
mamba-version: "*"
channels: conda-forge
channel-priority: strict
environment-file: dev-environment.yml
activate-environment: xdem-dev

- name: Install project
run: |
pip install . --no-dependencies
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
run: |
pytest -rA
13 changes: 10 additions & 3 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
# This workflow will upload a Python Package using Twine when a release is created
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries

name: Upload Python Package
name: pypi

on:
release:
types: [created]

workflow_dispatch:
inputs:
reason:
description: 'Reason for manual trigger'
required: true
default: 'testing'

jobs:
deploy:

Expand All @@ -21,11 +28,11 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
python -m pip install -U setuptools wheel build twine --user
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python setup.py sdist bdist_wheel
python -m build
twine upload dist/*
21 changes: 21 additions & 0 deletions .readthedocs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# .readthedocs.yml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details

# Required
version: 2

# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/source/conf.py
fail_on_warning: false

# Optionally build your docs in additional formats such as PDF and ePub
formats: []

python:
version: 3.7
install:
- requirements: dev-requirements.txt
- method: pip
path: .
47 changes: 36 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,36 @@ Set of tools to manipulate Digital Elevation Models (DEMs)
More documentation to come!

[![Documentation Status](https://readthedocs.org/projects/xdem/badge/?version=latest)](https://xdem.readthedocs.io/en/latest/?badge=latest)
[![build](https://github.com/GlacioHack/xdem/actions/workflows/python-package.yml/badge.svg)](https://github.com/GlacioHack/xdem/actions/workflows/python-package.yml)
[![Conda Version](https://img.shields.io/conda/vn/conda-forge/xdem.svg)](https://anaconda.org/conda-forge/xdem)
[![Conda Platforms](https://img.shields.io/conda/pn/conda-forge/xdem.svg)](https://anaconda.org/conda-forge/xdem)
[![Conda Downloads](https://img.shields.io/conda/dn/conda-forge/xdem.svg)](https://anaconda.org/conda-forge/xdem)

To cite this package: [![Zenodo](https://zenodo.org/badge/doi/10.5281/zenodo.4809697.svg)](https://zenodo.org/record/4809698)

## Installation ##
## Installation

Recommended: Use conda for depencency solving.
```
$ git clone https://github.com/GlacioHack/xdem.git
$ cd ./xdem
$ conda create -f environment.yml
$ conda activate glacio
$ conda env create -f environment.yml
$ conda activate xdem
$ pip install .
```
or
After installing, we recommend to check that everything is working by running the tests:

```
$ pytest -rA
```

### Installing with pip
**NOTE**: Setting up GDAL and PROJ may need some extra steps, depending on your operating system and configuration.
```bash
pip install git+https://github.com/GlacioHack/xdem.git
pip install xdem
```


## Structure

xdem are for now composed of three libraries:
Expand All @@ -31,6 +46,8 @@ xdem are for now composed of three libraries:
You can find ways to improve the libraries in the [issues](https://github.com/GlacioHack/xdem/issues) section. All contributions are welcome.
To avoid conflicts, it is suggested to use separate branches for each implementation. All changes must then be submitted to the dev branch using pull requests. Each PR must be reviewed by at least one other person.

Please see our [contribution page](CONTRIBUTING.md) for more detailed instructions.

### Documentation
See the documentation at https://xdem.readthedocs.io

Expand All @@ -46,16 +63,24 @@ https://github.com/corteva/rioxarray/blob/master/test/integration/test_integrati
```python
import xdem

reference_dem = "path/to/reference.tif"
dem_to_be_aligned = "path/to/dem.tif"
mask = "path/to/mask.shp" # This is optional. Could for example be glacier outlines.
reference_dem = xdem.DEM("path/to/reference.tif")
dem_to_be_aligned = xdem.DEM("path/to/dem.tif")

nuth_kaab = xdem.coreg.NuthKaab()

nuth_kaab.fit(reference_dem.data, dem_to_be_aligned.data, transform=reference_dem.transform)


aligned_dem, error = xdem.coreg.coregister(reference_dem, dem_to_be_aligned, mask=mask)
aligned_dem = xdem.DEM.from_array(
nuth_kaab.apply(dem_to_be_aligned.data, transform=dem_to_be_aligned.transform),
transform=dem_to_be_aligned.transform,
crs=dem_to_be_aligned.crs
)

aligned_dem.save("path/to/coreg.tif")
```
The default coregistration method is a [Nuth and Kääb (2011)](https://doi.org/10.5194/tc-5-271-2011) implementation, but this can be changed with the keyword argument `method=...`, e.g. to `"icp"`.
The currently supported methods are: `"nuth_kaab"`, `"icp"` and `"deramp"`.
This is an implementation of the [Nuth and Kääb (2011)](https://doi.org/10.5194/tc-5-271-2011) approach.
[Please see the documentation](https://xdem.readthedocs.io/en/latest/coregistration.html) for more approaches.

**Subtract one DEM with another**
```python
Expand Down
29 changes: 29 additions & 0 deletions dev-environment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: xdem-dev
channels:
- conda-forge
dependencies:
- python>=3.7
- proj>=7.2
- geopandas
- numba
- matplotlib
- opencv
- pyproj
- rasterio
- scipy
- tqdm
- scikit-image
- proj-data
- scikit-gstat
- pytransform3d
- geoutils

# Development-specific
- pytest
- pytest-xdist
- sphinx
- sphinx_rtd_theme
- numpydoc
- sphinxcontrib-programoutput
- flake8
- sphinx-autodoc-typehints
4 changes: 4 additions & 0 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
sphinx
numpydoc
sphinx_rtd_theme
sphinx_autodoc_typehints
sphinxcontrib-programoutput
numpy
scipy
rasterio
Expand All @@ -9,3 +11,5 @@ pyproj
tqdm
git+https://github.com/GlacioHack/GeoUtils.git
scikit-gstat
flake8
opencv-contrib-python
92 changes: 92 additions & 0 deletions docs/source/code/comparison.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
"""Example code for the DEM comparison chapter (it's made like this to test the syntax)."""
#######################
# SECTION: Example data
#######################
from datetime import datetime

import geoutils as gu
import numpy as np

import xdem

# Download the necessary data. This may take a few minutes.
xdem.examples.download_longyearbyen_examples(overwrite=False)

# Load a reference DEM from 2009
dem_2009 = xdem.DEM(xdem.examples.FILEPATHS["longyearbyen_ref_dem"], datetime=datetime(2009, 8, 1))
# Load a DEM from 1990
dem_1990 = xdem.DEM(xdem.examples.FILEPATHS["longyearbyen_tba_dem"], datetime=datetime(1990, 8, 1))
# Load glacier outlines from 1990.
glaciers_1990 = gu.Vector(xdem.examples.FILEPATHS["longyearbyen_glacier_outlines"])
glaciers_2010 = gu.Vector(xdem.examples.FILEPATHS["longyearbyen_glacier_outlines_2010"])

# Make a dictionary of glacier outlines where the key represents the associated date.
outlines = {
datetime(1990, 8, 1): glaciers_1990,
datetime(2009, 8, 1): glaciers_2010,
}

# Fake a future DEM to have a time-series of three DEMs
dem_2060 = dem_2009.copy()
# Assume that all glacier values will be 30 m lower than in 2009
dem_2060.data[glaciers_2010.create_mask(dem_2060)] -= 30
dem_2060.datetime = datetime(2060, 8, 1)

##############################
# SECTION: Subtracting rasters
##############################

dem1, dem2 = dem_2009, dem_1990

ddem_data = dem1.data - dem2.data
# If we want to inherit the georeferencing information:
ddem_raster = xdem.DEM.from_array(ddem_data, dem1.transform, dem2.crs)

# TEXT HERE

ddem_raster = xdem.spatial_tools.subtract_rasters(dem1, dem2)

#############################
# SECTION: dDEM interpolation
#############################

ddem = xdem.dDEM(
raster=xdem.spatial_tools.subtract_rasters(dem_2009, dem_1990),
start_time=dem_1990.datetime,
end_time=dem_2009.datetime
)

# The example DEMs are void-free, so let's make some random voids.
ddem.data.mask = np.zeros_like(ddem.data, dtype=bool) # Reset the mask
# Introduce 50000 nans randomly throughout the dDEM.
ddem.data.mask.ravel()[np.random.choice(ddem.data.size, 50000, replace=False)] = True

# SUBSECTION: Linear spatial interpolation

ddem.interpolate(method="linear")

# SUBSECTION: Local hypsometric interpolation

ddem.interpolate(method="local_hypsometric", reference_elevation=dem_2009, mask=glaciers_1990)

# SUBSECTION: Regional hypsometric interpolation

ddem.interpolate(method="regional_hypsometric", reference_elevation=dem_2009, mask=glaciers_1990)

###################################
# SECTION: The DEMCollection object
###################################

dems = xdem.DEMCollection(
[dem_1990, dem_2009, dem_2060],
outlines=outlines,
reference_dem=dem_2009
)

# TEXT HERE

dems.subtract_dems()
dems.get_cumulative_series(kind="dh", outlines_filter="NAME == 'Scott Turnerbreen'")

# Create an object that can be printed in the documentation.
scott_series = dems.get_cumulative_series(kind="dh", outlines_filter="NAME == 'Scott Turnerbreen'")
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""Plot an example of local hypsometric interpolation at Scott Turnerbreen, Svalbard."""
import geoutils as gu
import matplotlib.pyplot as plt
import numpy as np

import xdem

xdem.examples.download_longyearbyen_examples(overwrite=False)

dem_2009 = xdem.DEM(xdem.examples.FILEPATHS["longyearbyen_ref_dem"])
dem_1990 = xdem.DEM(xdem.examples.FILEPATHS["longyearbyen_tba_dem"])
outlines_1990 = gu.Vector(xdem.examples.FILEPATHS["longyearbyen_glacier_outlines"])

ddem = xdem.dDEM(
xdem.spatial_tools.subtract_rasters(dem_2009, dem_1990, resampling_method="nearest"),
start_time=np.datetime64("1990-08-01"),
end_time=np.datetime64("2009-08-01")
)

ddem.data /= (2009 - 1990)

scott_1990 = outlines_1990.query("NAME == 'Scott Turnerbreen'")
mask = scott_1990.create_mask(ddem)

ddem_bins = xdem.volume.hypsometric_binning(ddem.data[mask], dem_2009.data[mask])
stds = xdem.volume.hypsometric_binning(ddem.data[mask], dem_2009.data[mask], aggregation_function=np.std)

plt.figure(figsize=(8, 8))
plt.grid(zorder=0)
plt.plot(ddem_bins["value"], ddem_bins.index.mid, linestyle="--", zorder=1)

plt.barh(
y=ddem_bins.index.mid,
width=stds["value"],
left=ddem_bins["value"] - stds["value"] / 2,
height=(ddem_bins.index.left - ddem_bins.index.right) * 1,
zorder=2,
edgecolor="black",
)
for bin in ddem_bins.index:
plt.vlines(ddem_bins.loc[bin, "value"], bin.left, bin.right, color="black", zorder=3)

plt.xlabel("Elevation change (m / a)")
plt.twiny()
plt.barh(
y=ddem_bins.index.mid,
width=ddem_bins["count"] / ddem_bins["count"].sum(),
left=0,
height=(ddem_bins.index.left - ddem_bins.index.right) * 1,
zorder=2,
alpha=0.2,
)
plt.xlabel("Normalized area distribution (hypsometry)")

plt.ylabel("Elevation (m a.s.l.)")

plt.tight_layout()
plt.show()
Loading

0 comments on commit 9c97f54

Please sign in to comment.