{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# topo-descriptors\n",
    "\n",
    "A python library to compute DEM-based topographical descriptors.\n",
    "\n",
    "## Usage\n",
    "\n",
    "Let's install `topo-descriptors` with few additional packages that will help us\n",
    "to run a simple example (remember to use a virtual environment):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%pip install topo-descriptors elevation rioxarray matplotlib --quiet"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The [elevation](https://github.com/bopen/elevation) package is an python library that\n",
    "provides an easy access to global elevation data. Here we are going to clip the SRTM 30m\n",
    "DEM around the Basodino region in southern Switzerland, around 46.4N 8.5E:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!eio clip -o Basodino-30m-DEM.tif --bounds 8.2 46.30 8.6 46.55"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import logging\n",
    "\n",
    "logger = logging.getLogger()\n",
    "handler = logging.StreamHandler()\n",
    "formatter = logging.Formatter(\"%(asctime)s %(name)-12s %(levelname)-8s %(message)s\")\n",
    "handler.setFormatter(formatter)\n",
    "logger.addHandler(handler)\n",
    "logger.setLevel(logging.INFO)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now in python we can use the xarray interface to rasterio to easily import the\n",
    "`Basodino-30m-DEM.tif` file generated above:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import xarray as xr\n",
    "\n",
    "dem = xr.open_rasterio(\"Basodino-30m-DEM.tif\")\n",
    "dem = dem.isel(band=0, drop=True)\n",
    "dem.plot(robust=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from topo_descriptors import topo, helpers\n",
    "\n",
    "scale_meters = 500\n",
    "scale_pixel, __ = helpers.scale_to_pixel(scale_meters, dem)\n",
    "topo.tpi(dem, scale_pixel).plot(vmin=-100, vmax=100, cmap=\"bwr\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The Sx is used to describe the horizon in a given direction and spatial scale.\n",
    "In the example below we compute the Sx for a 0° azimuth (i.e., looking North)\n",
    "and a radius of 500 meters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sx_500m = topo.sx(dem, azimuth=0, radius=500)\n",
    "xr.DataArray(sx_500m, coords=dem.coords).plot.imshow()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Other topographical descriptors are available, such as slope, aspect, derivatives,\n",
    "and more. As an example, below we show how to compute a range of descriptors for two\n",
    "distinc spatial scales (200 and 2000 meters)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "output_dir = Path(\"out/\")\n",
    "output_dir.mkdir(exist_ok=True)\n",
    "\n",
    "scales_meters = [200, 2000]\n",
    "domain = {\"x\": slice(8.25, 8.55), \"y\": slice(46.50, 46.35)}\n",
    "\n",
    "topo.compute_gradient(dem, scales_meters, sig_ratios=1, crop=domain, outdir=output_dir)\n",
    "topo.compute_std(dem, scales_meters, crop=domain, outdir=output_dir)\n",
    "topo.compute_tpi(dem, scales_meters, crop=domain, outdir=output_dir)\n",
    "topo.compute_sx(dem, azimuth=0, radius=scales_meters[0], crop=domain, outdir=output_dir)\n",
    "topo.compute_sx(dem, azimuth=0, radius=scales_meters[1], crop=domain, outdir=output_dir)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Above, the output was written directly to disk, while in the cell below we show how \n",
    "to easly import the results and visualize them using xarray."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ds = xr.open_mfdataset(str(output_dir / \"topo_*.nc\"))\n",
    "min_max = ds.quantile(q=[0.05, 0.95])\n",
    "ds = (ds - min_max.isel(quantile=0)) / (\n",
    "    min_max.isel(quantile=1) - min_max.isel(quantile=0)\n",
    ")\n",
    "ds.to_array().plot.imshow(\n",
    "    col=\"variable\",\n",
    "    col_wrap=len(scales_meters),\n",
    "    robust=True,\n",
    "    add_colorbar=False,\n",
    "    vmin=0,\n",
    "    vmax=1,\n",
    ")\n",
    "ds.close()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Build the README\n",
    "\n",
    "To use this Jupyter Notebook to compile the markdown's version for GitHub, first install\n",
    "the conda environment using the `environment.yaml` file:\n",
    "\n",
    "```shell\n",
    "conda env create -f environment.yaml\n",
    "conda activate topo\n",
    "```\n",
    "\n",
    "Then generate the `README.md` by running:\n",
    "\n",
    "```shell\n",
    "jupyter nbconvert --execute --to markdown README.ipynb\n",
    "```\n",
    "\n",
    "The associated figures are saved in the `README_files/` folder."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.10.8 ('topo')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.8 | packaged by conda-forge | (main, Nov 22 2022, 08:25:29) [Clang 14.0.6 ]"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "deb7fae88368ab96eb09766f06009a343f9a7d51b6b1f227ac5998e9f38709ff"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
