├── README.md ├── __init__.py ├── centerlines_rgi.py ├── docs ├── Makefile ├── RGI11_comparison.rst ├── _build │ └── html │ │ ├── .buildinfo │ │ ├── RGI11_comparison.html │ │ ├── _images │ │ ├── RGI60-11.02209.png │ │ ├── RGI60-11.02242.png │ │ ├── RGI60-11.02245.png │ │ ├── RGI60-11.02337.png │ │ └── hef_flowline.jpg │ │ ├── _sources │ │ ├── RGI11_comparison.rst.txt │ │ ├── code.rst.txt │ │ ├── flowlines.rst.txt │ │ ├── index.rst.txt │ │ ├── introduction.rst.txt │ │ └── template.rst.txt │ │ ├── _static │ │ ├── RGI60-11.02209.png │ │ ├── RGI60-11.02242.png │ │ ├── RGI60-11.02245.png │ │ ├── RGI60-11.02337.png │ │ ├── alabaster.css │ │ ├── basic.css │ │ ├── custom.css │ │ ├── doctools.js │ │ ├── documentation_options.js │ │ ├── file.png │ │ ├── hef_flowline.jpg │ │ ├── jquery-3.5.1.js │ │ ├── jquery.js │ │ ├── language_data.js │ │ ├── minus.png │ │ ├── plus.png │ │ ├── pygments.css │ │ ├── searchtools.js │ │ ├── underscore-1.13.1.js │ │ ├── underscore-1.3.1.js │ │ └── underscore.js │ │ ├── code.html │ │ ├── flowlines.html │ │ ├── genindex.html │ │ ├── index.html │ │ ├── introduction.html │ │ ├── objects.inv │ │ ├── search.html │ │ └── searchindex.js ├── _static │ ├── RGI60-11.02209.png │ ├── RGI60-11.02242.png │ ├── RGI60-11.02245.png │ ├── RGI60-11.02337.png │ └── hef_flowline.jpg ├── code.rst ├── conf.py ├── flowlines.rst ├── index.rst ├── introduction.rst └── make.bat ├── functions.py ├── functions_rgi.py ├── main.py ├── params.py ├── setup.py ├── snippet_run_rgi_centerlines.py └── utils.py /README.md: -------------------------------------------------------------------------------- 1 | # Glacier Centerlines 2 | 3 | A re-implementation of the [Kienholz et al., 2014](https://tc.copernicus.org/articles/8/503/2014/) algorithm in python. 4 | 5 | This algorithm has been implemented succesfully [in OGGM](https://docs.oggm.org/en/stable/flowlines.html) since several years already. This code, however, has a few drawbacks: 6 | - it requires OGGM to be installed to run. 7 | - the OGGM developpers have made certain choices with respect to algorithm design and grid handling which made sense for the model, a bit less for the centerline tool 8 | - the agorithm code is a bit hidden in the large OGGM codebase, maybe impeding innovation and adaptations from the community. 9 | 10 | The main goal of this project is to adress these shortcomings and develop a single tool with a simple purpose: given glacier outlines and a digital elevation domain, compute the centerlines of glaciers. 11 | 12 | ### Getting started with the implementation 13 | 14 | Here are two files to test things on: 15 | - https://cluster.klima.uni-bremen.de/~oggm/tutorials/Norway_Inventory_sel.zip 16 | - https://cluster.klima.uni-bremen.de/~oggm/tutorials/Norway_DEM_sel.tif 17 | 18 | Workflow: 19 | - assume that the DEM and the outlines are in a cartesian projection (units: m) 20 | - open the geotiff with rioxarray 21 | - start with one outline, and crop the DEM to the outline + a few grid point 22 | - compute a mask of the glacier. 23 | - use the OGGM code to compute the heads, terminus, without simplifying geometries as done in OGGM. 24 | 25 | The tools you will need: 26 | - rioxarray to read geotiff 27 | - geopandas to read and write geometries 28 | - shapely to do the geometrical stuff (as OGGM does) 29 | - scipy for the routing algorithm (as OGGM does) 30 | 31 | # UPDATE, version V1.0.2: 32 | See documentation in `\docs` 33 | 34 | # UPDATE pip installation 35 | Now the tool is [pip installable](https://pypi.org/project/glacier-centerlines/). 36 | The tool has become an entity task that can be called using oggm already existing functions: 37 | 38 | (e.g. from `snippet_run_rgi_centerlines.py`) 39 | ``` 40 | #import general execution for oggm taks 41 | from oggm.workflow import execute_entity_task 42 | 43 | #new package where to take the task from: 44 | import glacier_centerlines as gc 45 | 46 | # run 47 | execute_entity_task(gc.centerlines_rgi.compute_centerlines_rgi, gdirs) 48 | 49 | ``` 50 | 51 | The new centerlines will be stored at the original oggm glacier directory as `centerlines_rgi.tar.gz 52 | ` 53 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | # This is a dummy init. 2 | __version__ = '1.0.1' 3 | 4 | # API 5 | # Some decorators used by many 6 | from oggm.utils import entity_task, global_task 7 | 8 | #add functions 9 | from glacier_centerlines import functions_rgi 10 | 11 | # Classes 12 | from glacier_centerlines.centerlines_rgi import compute_centerlines_rgi 13 | 14 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/RGI11_comparison.rst: -------------------------------------------------------------------------------- 1 | .. _RGI11_comparison: 2 | 3 | RGI11-check 4 | ======================================= 5 | 6 | (Corresponding to dev-RGI11 branch : https://github.com/GLIMS-RGI/glacier_centerlines/tree/dev-RGI11) 7 | 8 | Results: 9 | ~~~~~~~~~~~ 10 | **Highlights:** 11 | 12 | - 84% out of 3927 were able to be computed (627 failed). 13 | 14 | - 2 results have been issued: main flowline and "all" flowlines (main + tributaries) 15 | * Main flowline: most of the time new flowline matches OGGM. "Matches" means that it starts and ends at the same point, but with slightly different trajectories. 16 | * All flowlines: Agreement is worse than in the main flowline results. Normally we get some more tributaries in our new tool. 17 | 18 | **How has it been run?** 19 | 20 | * Data 21 | - DEM taken from each glacier from RGI6-11. See a sample in `/test_data `_ 22 | - Glacier outlines from RGI6-11 outlines. 23 | * Main script 24 | - `RGI11check.py `_ 25 | 26 | **Detailed results:** 27 | 28 | In purple: new results. 29 | 30 | Dashed: OGGM old flowlines. 31 | 32 | .. figure:: _static/RGI60-11.02209.png 33 | :width: 60% 34 | :align: left 35 | 36 | EX1 main flowline, differences determining main flowline: RGI60-11.02209 37 | 38 | 39 | .. figure:: _static/RGI60-11.02337.png 40 | :width: 60% 41 | :align: left 42 | 43 | EX2 main flowline, differences determining heads and tails: RGI60-11.02337 44 | 45 | 46 | 47 | .. figure:: _static/RGI60-11.02242.png 48 | :width: 60% 49 | :align: left 50 | 51 | EX3 multiple flowlines, differences in amount of tributaries: RGI60-11.02242 52 | 53 | .. figure:: _static/RGI60-11.02245.png 54 | :width: 60% 55 | :align: left 56 | 57 | EX3 multiple flowlines, differences in amount of tributaries: RGI60-11.02245 58 | 59 | 60 | 61 | Further info 62 | ------------ 63 | 64 | Check for more results yourselves in the data folder, main_flowlines.zip and multiple_flowlines.zip : https://github.com/GLIMS-RGI/glacier_centerlines/tree/dev-RGI11/test_data/ 65 | -------------------------------------------------------------------------------- /docs/_build/html/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: 6d50faa83b77c19fe2a867579ec0bc9c 4 | tags: 645f666f9bcd5a90fca523b33c5a78b7 5 | -------------------------------------------------------------------------------- /docs/_build/html/RGI11_comparison.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | RGI11-check — glacier_centerlines 23-03-2022 documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 |
31 | 32 | 33 |
34 | 35 |
36 |

RGI11-check

37 |

(Corresponding to dev-RGI11 branch : https://github.com/GLIMS-RGI/glacier_centerlines/tree/dev-RGI11)

38 |
39 |

Results:

40 |

Highlights:

41 |
42 |
    43 |
  • 84% out of 3927 were able to be computed (627 failed).

  • 44 |
  • 45 |
    2 results have been issued: main flowline and “all” flowlines (main + tributaries)
      46 |
    • Main flowline: most of the time new flowline matches OGGM. “Matches” means that it starts and ends at the same point, but with slightly different trajectories.

    • 47 |
    • All flowlines: Agreement is worse than in the main flowline results. Normally we get some more tributaries in our new tool.

    • 48 |
    49 |
    50 |
    51 |
  • 52 |
53 |
54 |

How has it been run?

55 |
    56 |
  • 57 |
    Data
      58 |
    • DEM taken from each glacier from RGI6-11. See a sample in /test_data

    • 59 |
    • Glacier outlines from RGI6-11 outlines.

    • 60 |
    61 |
    62 |
    63 |
  • 64 |
  • 65 |
    Main script
    68 |
    69 |
    70 |
  • 71 |
72 |

Detailed results:

73 |

In purple: new results.

74 |

Dashed: OGGM old flowlines.

75 |
76 | _images/RGI60-11.02209.png 77 |
78 |

EX1 main flowline, differences determining main flowline: RGI60-11.02209

79 |
80 |
81 |
82 | _images/RGI60-11.02337.png 83 |
84 |

EX2 main flowline, differences determining heads and tails: RGI60-11.02337

85 |
86 |
87 |
88 | _images/RGI60-11.02242.png 89 |
90 |

EX3 multiple flowlines, differences in amount of tributaries: RGI60-11.02242

91 |
92 |
93 |
94 | _images/RGI60-11.02245.png 95 |
96 |

EX3 multiple flowlines, differences in amount of tributaries: RGI60-11.02245

97 |
98 |
99 |
100 |

Further info

101 |

Check for more results yourselves in the data folder, main_flowlines.zip and multiple_flowlines.zip : https://github.com/GLIMS-RGI/glacier_centerlines/tree/dev-RGI11/test_data/

102 |
103 |
104 |
105 | 106 | 107 |
108 | 109 |
110 |
111 | 162 |
163 |
164 | 175 | 176 | 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /docs/_build/html/_images/RGI60-11.02209.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GLIMS-RGI/glacier_centerlines/edb361c6090837252d0f6fb13ed4b1b03956b73d/docs/_build/html/_images/RGI60-11.02209.png -------------------------------------------------------------------------------- /docs/_build/html/_images/RGI60-11.02242.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GLIMS-RGI/glacier_centerlines/edb361c6090837252d0f6fb13ed4b1b03956b73d/docs/_build/html/_images/RGI60-11.02242.png -------------------------------------------------------------------------------- /docs/_build/html/_images/RGI60-11.02245.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GLIMS-RGI/glacier_centerlines/edb361c6090837252d0f6fb13ed4b1b03956b73d/docs/_build/html/_images/RGI60-11.02245.png -------------------------------------------------------------------------------- /docs/_build/html/_images/RGI60-11.02337.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GLIMS-RGI/glacier_centerlines/edb361c6090837252d0f6fb13ed4b1b03956b73d/docs/_build/html/_images/RGI60-11.02337.png -------------------------------------------------------------------------------- /docs/_build/html/_images/hef_flowline.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GLIMS-RGI/glacier_centerlines/edb361c6090837252d0f6fb13ed4b1b03956b73d/docs/_build/html/_images/hef_flowline.jpg -------------------------------------------------------------------------------- /docs/_build/html/_sources/RGI11_comparison.rst.txt: -------------------------------------------------------------------------------- 1 | .. _RGI11_comparison: 2 | 3 | RGI11-check 4 | ======================================= 5 | 6 | (Corresponding to dev-RGI11 branch : https://github.com/GLIMS-RGI/glacier_centerlines/tree/dev-RGI11) 7 | 8 | Results: 9 | ~~~~~~~~~~~ 10 | **Highlights:** 11 | 12 | - 84% out of 3927 were able to be computed (627 failed). 13 | 14 | - 2 results have been issued: main flowline and "all" flowlines (main + tributaries) 15 | * Main flowline: most of the time new flowline matches OGGM. "Matches" means that it starts and ends at the same point, but with slightly different trajectories. 16 | * All flowlines: Agreement is worse than in the main flowline results. Normally we get some more tributaries in our new tool. 17 | 18 | **How has it been run?** 19 | 20 | * Data 21 | - DEM taken from each glacier from RGI6-11. See a sample in `/test_data `_ 22 | - Glacier outlines from RGI6-11 outlines. 23 | * Main script 24 | - `RGI11check.py `_ 25 | 26 | **Detailed results:** 27 | 28 | In purple: new results. 29 | 30 | Dashed: OGGM old flowlines. 31 | 32 | .. figure:: _static/RGI60-11.02209.png 33 | :width: 60% 34 | :align: left 35 | 36 | EX1 main flowline, differences determining main flowline: RGI60-11.02209 37 | 38 | 39 | .. figure:: _static/RGI60-11.02337.png 40 | :width: 60% 41 | :align: left 42 | 43 | EX2 main flowline, differences determining heads and tails: RGI60-11.02337 44 | 45 | 46 | 47 | .. figure:: _static/RGI60-11.02242.png 48 | :width: 60% 49 | :align: left 50 | 51 | EX3 multiple flowlines, differences in amount of tributaries: RGI60-11.02242 52 | 53 | .. figure:: _static/RGI60-11.02245.png 54 | :width: 60% 55 | :align: left 56 | 57 | EX3 multiple flowlines, differences in amount of tributaries: RGI60-11.02245 58 | 59 | 60 | 61 | Further info 62 | ------------ 63 | 64 | Check for more results yourselves in the data folder, main_flowlines.zip and multiple_flowlines.zip : https://github.com/GLIMS-RGI/glacier_centerlines/tree/dev-RGI11/test_data/ 65 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/code.rst.txt: -------------------------------------------------------------------------------- 1 | .. _code: 2 | 3 | Code 4 | ==== 5 | 6 | There are 4 main scripts in the repository, listed below: 7 | 8 | **Scripts** 9 | 10 | *main.py* 11 | The workflow of the project is executed in this script. It calls all the other scripts, loads DEM and glaciers outlines and computes the glacier heads, tails and ultimately the centerlines. 12 | 13 | *utils.py* 14 | It is a Toolbox for the whole project. It provides some small functions and classes. 15 | 16 | *functions.py* 17 | Functions used in main.py. Most of the functions come from OGGM/core and OGGM/utils. Some others are new. 18 | 19 | *params.py* 20 | Here the main parameters used in main.py are listed and initialized. 21 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/flowlines.rst.txt: -------------------------------------------------------------------------------- 1 | .. _Glacier flowlines: 2 | 3 | Glacier flowlines 4 | ================= 5 | 6 | OGGM's default model is a "flowline model", which means that the glacier ice flow is 7 | assumed to happen along a representative "1.5D" flowline, as in the image 8 | below. "1.5D" here is used to emphasize that although glacier ice can flow 9 | only in one direction along the flowline, each point of the glacier has 10 | a geometrical width. This width means that flowline glaciers are able to match 11 | the observed area-elevation distribution of true glaciers, and can parametrize 12 | the changes in glacier width with thickness changes. 13 | 14 | .. figure:: _static/hef_flowline.jpg 15 | :width: 80% 16 | :align: left 17 | 18 | Example of a glacier flowline. Background image from 19 | http://www.swisseduc.ch/glaciers/alps/hintereisferner/index-de.html 20 | 21 | 22 | Geometrical centerlines 23 | ----------------------- 24 | 25 | Centerline determination 26 | ~~~~~~~~~~~~~~~~~~~~~~~~ 27 | 28 | Our algorithm is an implementation of the procedure described by 29 | `Kienholz et al., (2014)`_. Apart from some minor changes (mostly the choice 30 | of some parameters), we stay close to the original algorithm. 31 | 32 | .. _Kienholz et al., (2014): http://www.the-cryosphere.net/8/503/2014/ 33 | 34 | 35 | The basic idea is to find the terminus of the glacier (its lowest point) and 36 | a series of centerline "heads" (local elevation maxima). The centerlines are then 37 | computed with a least cost routing algorithm minimizing both (i) the total 38 | elevation gain and (ii) the distance to the glacier terminus. 39 | 40 | The glacier has a major centerline (the longest one), and 41 | tributary branches (in this case: two). The Hintereisferner glacier is a 42 | good example of a wrongly outlined glacier: the two northern glacier sub-catchments 43 | should have been classified as independent entities since they do not flow 44 | to the main flowline (more on this below). 45 | 46 | At this stage, the centerlines are still not fully suitable 47 | for modelling. Therefore, a rather simple 48 | procedure converts them to "flowlines", which 49 | now have a regular grid spacing (which they will 50 | keep for the rest of the workflow). The tail of the tributaries are cut 51 | of before reaching the flowline they are tributing to: 52 | 53 | This step is needed to better represent glacier widths at flowline junctions. 54 | The empty circles on the main flowline indicate the location where the respective 55 | tributaries are connected (i.e. where the ice flux that is originating from the 56 | tributary will be added to the main flux when running the model dynamic). 57 | 58 | 59 | .. _flprocons: 60 | 61 | Pros and cons of both methods 62 | ----------------------------- 63 | 64 | Flowline representation of the glacier is **always** a simplification! 65 | 66 | Geometrical centerlines 67 | ~~~~~~~~~~~~~~~~~~~~~~~ 68 | 69 | - Pros: 70 | 71 | - Closer to the "true" length of the glacier. 72 | - Grid points along the centerlines preserve their geometrical information, 73 | i.e. one can compute the exact location of ice thickness change. 74 | - It is possible to have different model parameters for each flowline (e.g. 75 | different mass-balance models), although this is coming with its own 76 | challenges. 77 | - Arguably: better suitability for mass-balance parameterizations taking 78 | glacier geometry and exposition into account. 79 | - Arguably: better representation of the main glacier flow? 80 | 81 | - Cons: 82 | 83 | - Complex and error prone: considerably more code than the elevation band 84 | flowlines. 85 | - Less robust: more glaciers are failing in the preprocessing than with 86 | the simpler method. 87 | When glaciers are badly outlined (or worse, when ice caps are not 88 | properly divided), or with bad DEMs, the geometrical flowline 89 | can "look" very ugly. 90 | - Computationally expensive (more grid points on average, more prone 91 | to numerical instabilities). 92 | - Complex handling of mass-balance parameters for tributaries at the 93 | inversion (leading to multiple temperature sensitivity parameters 94 | for large glaciers). 95 | - Related: **all "new generation" mass-balance models in OGGM currently 96 | handle only a single flowline because of this complexity.** 97 | 98 | .. admonition:: **Summary** 99 | 100 | **When to use:** when geometry matters, and when length is a important variable. 101 | For mountain glaciers (e.g. Alps, Himalayas). With the old mass-balance 102 | model. 103 | 104 | **When not to use:** for ice caps, badly outlined glaciers, very large and 105 | flat glaciers, for global applications where geometrical details matters less. 106 | With the more fancy mass-balance models. 107 | 108 | 109 | References 110 | ---------- 111 | 112 | Kienholz, C., Rich, J. L., Arendt, A. A., and Hock, R.: A new method for deriving glacier centerlines applied to glaciers in Alaska and northwest Canada, The Cryosphere, 8, 503–519, https://doi.org/10.5194/tc-8-503-2014, 2014. 113 | 114 | 115 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/index.rst.txt: -------------------------------------------------------------------------------- 1 | .. glacier_centerlines documentation master file, created by 2 | sphinx-quickstart on Thu Mar 24 16:41:38 2022. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to glacier_centerlines's documentation! 7 | =============================================== 8 | 9 | Overview 10 | ^^^^^^^^ 11 | 12 | Core principles and structure of the OGGM modelling framework. 13 | 14 | * :doc:`introduction` 15 | * :doc:`flowlines` 16 | * :doc:`code` 17 | * :doc:`RGI11_comparison` **NEW! (14102022)** 18 | 19 | 20 | 21 | .. toctree:: 22 | :maxdepth: 2 23 | :hidden: 24 | :caption: GoTo: 25 | 26 | introduction.rst 27 | flowlines.rst 28 | code.rst 29 | RGI11_comparison.rst 30 | 31 | 32 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/introduction.rst.txt: -------------------------------------------------------------------------------- 1 | **ABOUT THIS VERSION** 2 | THE CURRENT VERSION V1.0.1 COMPUTES THE CENTERLINES FROM BALTORO GLACIER SUCCESSFULLY. SOME ERRORS AND BUGS FROM LAST VERSION HAVE BEEN SOLVED, SUCH AS THE INTERSECTION CENTERLINE-OUTLINE. THE CODE IS NOW ALSO A BIT NICER. 3 | F . Roura-Adserias, Jul 2022 4 | 5 | Introduction 6 | ============ 7 | 8 | The glacier_centerlines project is a spin-off that comes from OGGM glacier evolution model. We have taken most of the code from the original OGGM functions and workflow. Its main purpose is to compute the glacier central flowlines given a Digital Elevation Model (DEM) and a glacier outline in a georefferenced format. 9 | 10 | **Motivation** 11 | Due to the increasing complexity of OGGM, it has been convenient to separate the determination of the glacier centerlines from the main model. Therefore users can obtain the glacier flowlines using the same methodology as in OGGM, with a code that is thought to serve this only purpose. 12 | 13 | 14 | **External links** 15 | 16 | `OGGM `_ 17 | 18 | `OGGM git repository `_ 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/template.rst.txt: -------------------------------------------------------------------------------- 1 | 2 | A modular open source glacier model in Python 3 | --------------------------------------------- 4 | 5 | **OGGM is an open source modelling framework** able to simulate past and 6 | future mass-balance, volume and geometry of (almost) any glacier in the world, 7 | in a fully automated and extensible workflow. 8 | 9 | The model can account for glacier geometry (including contributory branches) and 10 | ships with several glacier evolution models, including 11 | an explicit ice dynamics module. We rely exclusively on publicly 12 | available data for calibration and validation. **OGGM is modular and 13 | supports novel modelling workflows**: it LOVES to be remixed and reused! 14 | 15 | 16 | **This webpage is for the software documentation: for general information about the 17 | OGGM project and related news, visit** `oggm.org `_. 18 | 19 | .. include:: _generated/version_text.txt 20 | 21 | 22 | IARPC presentation (April 2020) 23 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 24 | 25 | If you are new to OGGM would like a short introduction, here is a recent 26 | 15' presentation about the project: 27 | 28 | .. raw:: html 29 | 30 | 31 | 32 | *Slides available* `here `_ 33 | 34 | Overview 35 | ^^^^^^^^ 36 | 37 | Core principles and structure of the OGGM modelling framework. 38 | 39 | * :doc:`introduction` 40 | * :doc:`structure` 41 | * :doc:`flowlines` 42 | * :doc:`mass-balance` 43 | * :doc:`geometry-evolution` 44 | * :doc:`inversion` 45 | * :doc:`frontal-ablation` 46 | 47 | .. toctree:: 48 | :maxdepth: 1 49 | :hidden: 50 | :caption: Principles 51 | 52 | introduction.rst 53 | structure.rst 54 | flowlines.rst 55 | mass-balance.rst 56 | geometry-evolution.rst 57 | inversion.rst 58 | frontal-ablation.rst 59 | 60 | Using OGGM 61 | ^^^^^^^^^^ 62 | 63 | How to use the model, with concrete code examples and links to the tutorials. 64 | 65 | * :doc:`cloud` 66 | * :doc:`installing-oggm` 67 | * :doc:`getting-started` 68 | * :doc:`input-data` 69 | * :doc:`run` 70 | * :doc:`practicalities` 71 | * :doc:`api` 72 | * :doc:`faq` 73 | * :doc:`pitfalls` 74 | * :doc:`assets` 75 | * :doc:`hub` 76 | * :doc:`whats-new` 77 | 78 | .. toctree:: 79 | :maxdepth: 1 80 | :hidden: 81 | :caption: Using OGGM 82 | 83 | cloud.rst 84 | installing-oggm.rst 85 | getting-started.rst 86 | input-data.rst 87 | run.rst 88 | api.rst 89 | practicalities.rst 90 | faq.rst 91 | pitfalls.rst 92 | assets.rst 93 | hub.rst 94 | whats-new.rst 95 | 96 | Contributing 97 | ^^^^^^^^^^^^ 98 | 99 | Do you want to contribute to the model? You are more than welcome to do so and this is the right place to start. 100 | 101 | * :doc:`citing-oggm` 102 | * :doc:`add-module` 103 | * :doc:`oeps` 104 | * :doc:`contributing` 105 | 106 | .. toctree:: 107 | :maxdepth: 1 108 | :hidden: 109 | :caption: Contributing 110 | 111 | citing-oggm.rst 112 | add-module.rst 113 | oeps.rst 114 | contributing.rst 115 | 116 | .. _contact: 117 | 118 | Get in touch 119 | ------------ 120 | 121 | - View the source code `on GitHub`_. 122 | - Report bugs or share your ideas on the `issue tracker`_, and improve 123 | the model by submitting a `pull request`_. 124 | - Chat with us on `Slack`_! (just send us an `e-mail`_ so we can add you) 125 | - Follow us on `Twitter`_. 126 | - Participate to our regular `meeting`_. (`reach out`_ if you want to join in) 127 | - Or you can always send us an `e-mail`_ the good old way. 128 | 129 | .. _e-mail: info@oggm.org 130 | .. _Slack: https://slack.com 131 | .. _on GitHub: https://github.com/OGGM/oggm 132 | .. _issue tracker: https://github.com/OGGM/oggm/issues 133 | .. _pull request: https://github.com/OGGM/oggm/pulls 134 | .. _Twitter: https://twitter.com/OGGM1 135 | .. _meeting: https://oggm.org/meetings/ 136 | .. _reach out: info@oggm.org 137 | 138 | 139 | License and citation 140 | -------------------- 141 | 142 | OGGM is available under the open source `3-Clause BSD License`_. 143 | 144 | .. _3-Clause BSD License: https://opensource.org/licenses/BSD-3-Clause 145 | 146 | OGGM is a free software. This implies that you are free to use the model and 147 | copy, modify or redistribute its code at your wish, under certain conditions: 148 | 149 | 1. When using this software, please acknowledge the original authors of this 150 | contribution by using our logo, referring to our website or using an 151 | appropriate citation. See :ref:`citing-oggm` for how to do that. 152 | 153 | 2. Redistributions of any substantial portion of the OGGM source code must 154 | meet the conditions listed in the `OGGM license`_ 155 | 156 | 3. Neither OGGM e.V. nor the names of OGGM contributors may be used to endorse 157 | or promote products derived from this software without specific prior 158 | written permission. This does not mean that you need our written permission 159 | to work with OGGM or publish results based on OGGM: it simply means that 160 | the OGGM developers are not accountable for what you do with the tool 161 | (`more info `_). 162 | 163 | See the `OGGM license`_ for more information. 164 | 165 | .. _OGGM license: https://github.com/OGGM/oggm/blob/master/LICENSE.txt 166 | 167 | About 168 | ----- 169 | 170 | :Version: 171 | .. image:: https://img.shields.io/pypi/v/oggm.svg 172 | :target: https://pypi.python.org/pypi/oggm 173 | :alt: Pypi version 174 | 175 | .. image:: https://img.shields.io/pypi/pyversions/oggm.svg 176 | :target: https://pypi.python.org/pypi/oggm 177 | :alt: Supported python versions 178 | 179 | :Citation: 180 | .. image:: https://img.shields.io/badge/Citation-GMD%20paper-orange.svg 181 | :target: https://www.geosci-model-dev.net/12/909/2019/ 182 | :alt: GMD Paper 183 | 184 | .. image:: https://zenodo.org/badge/43965645.svg 185 | :target: https://zenodo.org/badge/latestdoi/43965645 186 | :alt: Zenodo 187 | 188 | :Tests: 189 | .. image:: https://coveralls.io/repos/github/OGGM/oggm/badge.svg?branch=master 190 | :target: https://coveralls.io/github/OGGM/oggm?branch=master 191 | :alt: Code coverage 192 | 193 | .. image:: https://github.com/OGGM/oggm/actions/workflows/run-tests.yml/badge.svg?branch=master 194 | :target: https://github.com/OGGM/oggm/actions/workflows/run-tests.yml 195 | :alt: Linux build status 196 | 197 | .. image:: https://img.shields.io/badge/Cross-validation-blue.svg 198 | :target: https://cluster.klima.uni-bremen.de/~oggm/ref_mb_params/oggm_v1.4/crossval.html 199 | :alt: Mass-balance cross validation 200 | 201 | .. image:: https://readthedocs.org/projects/oggm/badge/?version=latest 202 | :target: http://docs.oggm.org/en/latest 203 | :alt: Documentation status 204 | 205 | .. image:: https://img.shields.io/badge/benchmarked%20by-asv-green.svg?style=flat 206 | :target: https://cluster.klima.uni-bremen.de/~github/asv/ 207 | :alt: Benchmark status 208 | 209 | :License: 210 | .. image:: https://img.shields.io/pypi/l/oggm.svg 211 | :target: https://github.com/OGGM/oggm/blob/master/LICENSE.txt 212 | :alt: BSD-3-Clause License 213 | 214 | :Authors: 215 | 216 | See the `version history`_ for a list of all contributors. 217 | 218 | .. _version history: http://docs.oggm.org/en/latest/whats-new.html 219 | -------------------------------------------------------------------------------- /docs/_build/html/_static/RGI60-11.02209.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GLIMS-RGI/glacier_centerlines/edb361c6090837252d0f6fb13ed4b1b03956b73d/docs/_build/html/_static/RGI60-11.02209.png -------------------------------------------------------------------------------- /docs/_build/html/_static/RGI60-11.02242.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GLIMS-RGI/glacier_centerlines/edb361c6090837252d0f6fb13ed4b1b03956b73d/docs/_build/html/_static/RGI60-11.02242.png -------------------------------------------------------------------------------- /docs/_build/html/_static/RGI60-11.02245.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GLIMS-RGI/glacier_centerlines/edb361c6090837252d0f6fb13ed4b1b03956b73d/docs/_build/html/_static/RGI60-11.02245.png -------------------------------------------------------------------------------- /docs/_build/html/_static/RGI60-11.02337.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GLIMS-RGI/glacier_centerlines/edb361c6090837252d0f6fb13ed4b1b03956b73d/docs/_build/html/_static/RGI60-11.02337.png -------------------------------------------------------------------------------- /docs/_build/html/_static/alabaster.css: -------------------------------------------------------------------------------- 1 | @import url("basic.css"); 2 | 3 | /* -- page layout ----------------------------------------------------------- */ 4 | 5 | body { 6 | font-family: Georgia, serif; 7 | font-size: 17px; 8 | background-color: #fff; 9 | color: #000; 10 | margin: 0; 11 | padding: 0; 12 | } 13 | 14 | 15 | div.document { 16 | width: 940px; 17 | margin: 30px auto 0 auto; 18 | } 19 | 20 | div.documentwrapper { 21 | float: left; 22 | width: 100%; 23 | } 24 | 25 | div.bodywrapper { 26 | margin: 0 0 0 220px; 27 | } 28 | 29 | div.sphinxsidebar { 30 | width: 220px; 31 | font-size: 14px; 32 | line-height: 1.5; 33 | } 34 | 35 | hr { 36 | border: 1px solid #B1B4B6; 37 | } 38 | 39 | div.body { 40 | background-color: #fff; 41 | color: #3E4349; 42 | padding: 0 30px 0 30px; 43 | } 44 | 45 | div.body > .section { 46 | text-align: left; 47 | } 48 | 49 | div.footer { 50 | width: 940px; 51 | margin: 20px auto 30px auto; 52 | font-size: 14px; 53 | color: #888; 54 | text-align: right; 55 | } 56 | 57 | div.footer a { 58 | color: #888; 59 | } 60 | 61 | p.caption { 62 | font-family: inherit; 63 | font-size: inherit; 64 | } 65 | 66 | 67 | div.relations { 68 | display: none; 69 | } 70 | 71 | 72 | div.sphinxsidebar a { 73 | color: #444; 74 | text-decoration: none; 75 | border-bottom: 1px dotted #999; 76 | } 77 | 78 | div.sphinxsidebar a:hover { 79 | border-bottom: 1px solid #999; 80 | } 81 | 82 | div.sphinxsidebarwrapper { 83 | padding: 18px 10px; 84 | } 85 | 86 | div.sphinxsidebarwrapper p.logo { 87 | padding: 0; 88 | margin: -10px 0 0 0px; 89 | text-align: center; 90 | } 91 | 92 | div.sphinxsidebarwrapper h1.logo { 93 | margin-top: -10px; 94 | text-align: center; 95 | margin-bottom: 5px; 96 | text-align: left; 97 | } 98 | 99 | div.sphinxsidebarwrapper h1.logo-name { 100 | margin-top: 0px; 101 | } 102 | 103 | div.sphinxsidebarwrapper p.blurb { 104 | margin-top: 0; 105 | font-style: normal; 106 | } 107 | 108 | div.sphinxsidebar h3, 109 | div.sphinxsidebar h4 { 110 | font-family: Georgia, serif; 111 | color: #444; 112 | font-size: 24px; 113 | font-weight: normal; 114 | margin: 0 0 5px 0; 115 | padding: 0; 116 | } 117 | 118 | div.sphinxsidebar h4 { 119 | font-size: 20px; 120 | } 121 | 122 | div.sphinxsidebar h3 a { 123 | color: #444; 124 | } 125 | 126 | div.sphinxsidebar p.logo a, 127 | div.sphinxsidebar h3 a, 128 | div.sphinxsidebar p.logo a:hover, 129 | div.sphinxsidebar h3 a:hover { 130 | border: none; 131 | } 132 | 133 | div.sphinxsidebar p { 134 | color: #555; 135 | margin: 10px 0; 136 | } 137 | 138 | div.sphinxsidebar ul { 139 | margin: 10px 0; 140 | padding: 0; 141 | color: #000; 142 | } 143 | 144 | div.sphinxsidebar ul li.toctree-l1 > a { 145 | font-size: 120%; 146 | } 147 | 148 | div.sphinxsidebar ul li.toctree-l2 > a { 149 | font-size: 110%; 150 | } 151 | 152 | div.sphinxsidebar input { 153 | border: 1px solid #CCC; 154 | font-family: Georgia, serif; 155 | font-size: 1em; 156 | } 157 | 158 | div.sphinxsidebar hr { 159 | border: none; 160 | height: 1px; 161 | color: #AAA; 162 | background: #AAA; 163 | 164 | text-align: left; 165 | margin-left: 0; 166 | width: 50%; 167 | } 168 | 169 | div.sphinxsidebar .badge { 170 | border-bottom: none; 171 | } 172 | 173 | div.sphinxsidebar .badge:hover { 174 | border-bottom: none; 175 | } 176 | 177 | /* To address an issue with donation coming after search */ 178 | div.sphinxsidebar h3.donation { 179 | margin-top: 10px; 180 | } 181 | 182 | /* -- body styles ----------------------------------------------------------- */ 183 | 184 | a { 185 | color: #004B6B; 186 | text-decoration: underline; 187 | } 188 | 189 | a:hover { 190 | color: #6D4100; 191 | text-decoration: underline; 192 | } 193 | 194 | div.body h1, 195 | div.body h2, 196 | div.body h3, 197 | div.body h4, 198 | div.body h5, 199 | div.body h6 { 200 | font-family: Georgia, serif; 201 | font-weight: normal; 202 | margin: 30px 0px 10px 0px; 203 | padding: 0; 204 | } 205 | 206 | div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } 207 | div.body h2 { font-size: 180%; } 208 | div.body h3 { font-size: 150%; } 209 | div.body h4 { font-size: 130%; } 210 | div.body h5 { font-size: 100%; } 211 | div.body h6 { font-size: 100%; } 212 | 213 | a.headerlink { 214 | color: #DDD; 215 | padding: 0 4px; 216 | text-decoration: none; 217 | } 218 | 219 | a.headerlink:hover { 220 | color: #444; 221 | background: #EAEAEA; 222 | } 223 | 224 | div.body p, div.body dd, div.body li { 225 | line-height: 1.4em; 226 | } 227 | 228 | div.admonition { 229 | margin: 20px 0px; 230 | padding: 10px 30px; 231 | background-color: #EEE; 232 | border: 1px solid #CCC; 233 | } 234 | 235 | div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { 236 | background-color: #FBFBFB; 237 | border-bottom: 1px solid #fafafa; 238 | } 239 | 240 | div.admonition p.admonition-title { 241 | font-family: Georgia, serif; 242 | font-weight: normal; 243 | font-size: 24px; 244 | margin: 0 0 10px 0; 245 | padding: 0; 246 | line-height: 1; 247 | } 248 | 249 | div.admonition p.last { 250 | margin-bottom: 0; 251 | } 252 | 253 | div.highlight { 254 | background-color: #fff; 255 | } 256 | 257 | dt:target, .highlight { 258 | background: #FAF3E8; 259 | } 260 | 261 | div.warning { 262 | background-color: #FCC; 263 | border: 1px solid #FAA; 264 | } 265 | 266 | div.danger { 267 | background-color: #FCC; 268 | border: 1px solid #FAA; 269 | -moz-box-shadow: 2px 2px 4px #D52C2C; 270 | -webkit-box-shadow: 2px 2px 4px #D52C2C; 271 | box-shadow: 2px 2px 4px #D52C2C; 272 | } 273 | 274 | div.error { 275 | background-color: #FCC; 276 | border: 1px solid #FAA; 277 | -moz-box-shadow: 2px 2px 4px #D52C2C; 278 | -webkit-box-shadow: 2px 2px 4px #D52C2C; 279 | box-shadow: 2px 2px 4px #D52C2C; 280 | } 281 | 282 | div.caution { 283 | background-color: #FCC; 284 | border: 1px solid #FAA; 285 | } 286 | 287 | div.attention { 288 | background-color: #FCC; 289 | border: 1px solid #FAA; 290 | } 291 | 292 | div.important { 293 | background-color: #EEE; 294 | border: 1px solid #CCC; 295 | } 296 | 297 | div.note { 298 | background-color: #EEE; 299 | border: 1px solid #CCC; 300 | } 301 | 302 | div.tip { 303 | background-color: #EEE; 304 | border: 1px solid #CCC; 305 | } 306 | 307 | div.hint { 308 | background-color: #EEE; 309 | border: 1px solid #CCC; 310 | } 311 | 312 | div.seealso { 313 | background-color: #EEE; 314 | border: 1px solid #CCC; 315 | } 316 | 317 | div.topic { 318 | background-color: #EEE; 319 | } 320 | 321 | p.admonition-title { 322 | display: inline; 323 | } 324 | 325 | p.admonition-title:after { 326 | content: ":"; 327 | } 328 | 329 | pre, tt, code { 330 | font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; 331 | font-size: 0.9em; 332 | } 333 | 334 | .hll { 335 | background-color: #FFC; 336 | margin: 0 -12px; 337 | padding: 0 12px; 338 | display: block; 339 | } 340 | 341 | img.screenshot { 342 | } 343 | 344 | tt.descname, tt.descclassname, code.descname, code.descclassname { 345 | font-size: 0.95em; 346 | } 347 | 348 | tt.descname, code.descname { 349 | padding-right: 0.08em; 350 | } 351 | 352 | img.screenshot { 353 | -moz-box-shadow: 2px 2px 4px #EEE; 354 | -webkit-box-shadow: 2px 2px 4px #EEE; 355 | box-shadow: 2px 2px 4px #EEE; 356 | } 357 | 358 | table.docutils { 359 | border: 1px solid #888; 360 | -moz-box-shadow: 2px 2px 4px #EEE; 361 | -webkit-box-shadow: 2px 2px 4px #EEE; 362 | box-shadow: 2px 2px 4px #EEE; 363 | } 364 | 365 | table.docutils td, table.docutils th { 366 | border: 1px solid #888; 367 | padding: 0.25em 0.7em; 368 | } 369 | 370 | table.field-list, table.footnote { 371 | border: none; 372 | -moz-box-shadow: none; 373 | -webkit-box-shadow: none; 374 | box-shadow: none; 375 | } 376 | 377 | table.footnote { 378 | margin: 15px 0; 379 | width: 100%; 380 | border: 1px solid #EEE; 381 | background: #FDFDFD; 382 | font-size: 0.9em; 383 | } 384 | 385 | table.footnote + table.footnote { 386 | margin-top: -15px; 387 | border-top: none; 388 | } 389 | 390 | table.field-list th { 391 | padding: 0 0.8em 0 0; 392 | } 393 | 394 | table.field-list td { 395 | padding: 0; 396 | } 397 | 398 | table.field-list p { 399 | margin-bottom: 0.8em; 400 | } 401 | 402 | /* Cloned from 403 | * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 404 | */ 405 | .field-name { 406 | -moz-hyphens: manual; 407 | -ms-hyphens: manual; 408 | -webkit-hyphens: manual; 409 | hyphens: manual; 410 | } 411 | 412 | table.footnote td.label { 413 | width: .1px; 414 | padding: 0.3em 0 0.3em 0.5em; 415 | } 416 | 417 | table.footnote td { 418 | padding: 0.3em 0.5em; 419 | } 420 | 421 | dl { 422 | margin: 0; 423 | padding: 0; 424 | } 425 | 426 | dl dd { 427 | margin-left: 30px; 428 | } 429 | 430 | blockquote { 431 | margin: 0 0 0 30px; 432 | padding: 0; 433 | } 434 | 435 | ul, ol { 436 | /* Matches the 30px from the narrow-screen "li > ul" selector below */ 437 | margin: 10px 0 10px 30px; 438 | padding: 0; 439 | } 440 | 441 | pre { 442 | background: #EEE; 443 | padding: 7px 30px; 444 | margin: 15px 0px; 445 | line-height: 1.3em; 446 | } 447 | 448 | div.viewcode-block:target { 449 | background: #ffd; 450 | } 451 | 452 | dl pre, blockquote pre, li pre { 453 | margin-left: 0; 454 | padding-left: 30px; 455 | } 456 | 457 | tt, code { 458 | background-color: #ecf0f3; 459 | color: #222; 460 | /* padding: 1px 2px; */ 461 | } 462 | 463 | tt.xref, code.xref, a tt { 464 | background-color: #FBFBFB; 465 | border-bottom: 1px solid #fff; 466 | } 467 | 468 | a.reference { 469 | text-decoration: none; 470 | border-bottom: 1px dotted #004B6B; 471 | } 472 | 473 | /* Don't put an underline on images */ 474 | a.image-reference, a.image-reference:hover { 475 | border-bottom: none; 476 | } 477 | 478 | a.reference:hover { 479 | border-bottom: 1px solid #6D4100; 480 | } 481 | 482 | a.footnote-reference { 483 | text-decoration: none; 484 | font-size: 0.7em; 485 | vertical-align: top; 486 | border-bottom: 1px dotted #004B6B; 487 | } 488 | 489 | a.footnote-reference:hover { 490 | border-bottom: 1px solid #6D4100; 491 | } 492 | 493 | a:hover tt, a:hover code { 494 | background: #EEE; 495 | } 496 | 497 | 498 | @media screen and (max-width: 870px) { 499 | 500 | div.sphinxsidebar { 501 | display: none; 502 | } 503 | 504 | div.document { 505 | width: 100%; 506 | 507 | } 508 | 509 | div.documentwrapper { 510 | margin-left: 0; 511 | margin-top: 0; 512 | margin-right: 0; 513 | margin-bottom: 0; 514 | } 515 | 516 | div.bodywrapper { 517 | margin-top: 0; 518 | margin-right: 0; 519 | margin-bottom: 0; 520 | margin-left: 0; 521 | } 522 | 523 | ul { 524 | margin-left: 0; 525 | } 526 | 527 | li > ul { 528 | /* Matches the 30px from the "ul, ol" selector above */ 529 | margin-left: 30px; 530 | } 531 | 532 | .document { 533 | width: auto; 534 | } 535 | 536 | .footer { 537 | width: auto; 538 | } 539 | 540 | .bodywrapper { 541 | margin: 0; 542 | } 543 | 544 | .footer { 545 | width: auto; 546 | } 547 | 548 | .github { 549 | display: none; 550 | } 551 | 552 | 553 | 554 | } 555 | 556 | 557 | 558 | @media screen and (max-width: 875px) { 559 | 560 | body { 561 | margin: 0; 562 | padding: 20px 30px; 563 | } 564 | 565 | div.documentwrapper { 566 | float: none; 567 | background: #fff; 568 | } 569 | 570 | div.sphinxsidebar { 571 | display: block; 572 | float: none; 573 | width: 102.5%; 574 | margin: 50px -30px -20px -30px; 575 | padding: 10px 20px; 576 | background: #333; 577 | color: #FFF; 578 | } 579 | 580 | div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, 581 | div.sphinxsidebar h3 a { 582 | color: #fff; 583 | } 584 | 585 | div.sphinxsidebar a { 586 | color: #AAA; 587 | } 588 | 589 | div.sphinxsidebar p.logo { 590 | display: none; 591 | } 592 | 593 | div.document { 594 | width: 100%; 595 | margin: 0; 596 | } 597 | 598 | div.footer { 599 | display: none; 600 | } 601 | 602 | div.bodywrapper { 603 | margin: 0; 604 | } 605 | 606 | div.body { 607 | min-height: 0; 608 | padding: 0; 609 | } 610 | 611 | .rtd_doc_footer { 612 | display: none; 613 | } 614 | 615 | .document { 616 | width: auto; 617 | } 618 | 619 | .footer { 620 | width: auto; 621 | } 622 | 623 | .footer { 624 | width: auto; 625 | } 626 | 627 | .github { 628 | display: none; 629 | } 630 | } 631 | 632 | 633 | /* misc. */ 634 | 635 | .revsys-inline { 636 | display: none!important; 637 | } 638 | 639 | /* Make nested-list/multi-paragraph items look better in Releases changelog 640 | * pages. Without this, docutils' magical list fuckery causes inconsistent 641 | * formatting between different release sub-lists. 642 | */ 643 | div#changelog > div.section > ul > li > p:only-child { 644 | margin-bottom: 0; 645 | } 646 | 647 | /* Hide fugly table cell borders in ..bibliography:: directive output */ 648 | table.docutils.citation, table.docutils.citation td, table.docutils.citation th { 649 | border: none; 650 | /* Below needed in some edge cases; if not applied, bottom shadows appear */ 651 | -moz-box-shadow: none; 652 | -webkit-box-shadow: none; 653 | box-shadow: none; 654 | } 655 | 656 | 657 | /* relbar */ 658 | 659 | .related { 660 | line-height: 30px; 661 | width: 100%; 662 | font-size: 0.9rem; 663 | } 664 | 665 | .related.top { 666 | border-bottom: 1px solid #EEE; 667 | margin-bottom: 20px; 668 | } 669 | 670 | .related.bottom { 671 | border-top: 1px solid #EEE; 672 | } 673 | 674 | .related ul { 675 | padding: 0; 676 | margin: 0; 677 | list-style: none; 678 | } 679 | 680 | .related li { 681 | display: inline; 682 | } 683 | 684 | nav#rellinks { 685 | float: right; 686 | } 687 | 688 | nav#rellinks li+li:before { 689 | content: "|"; 690 | } 691 | 692 | nav#breadcrumbs li+li:before { 693 | content: "\00BB"; 694 | } 695 | 696 | /* Hide certain items when printing */ 697 | @media print { 698 | div.related { 699 | display: none; 700 | } 701 | } -------------------------------------------------------------------------------- /docs/_build/html/_static/custom.css: -------------------------------------------------------------------------------- 1 | /* This file intentionally left blank. */ 2 | -------------------------------------------------------------------------------- /docs/_build/html/_static/doctools.js: -------------------------------------------------------------------------------- 1 | /* 2 | * doctools.js 3 | * ~~~~~~~~~~~ 4 | * 5 | * Sphinx JavaScript utilities for all documentation. 6 | * 7 | * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | /** 13 | * select a different prefix for underscore 14 | */ 15 | $u = _.noConflict(); 16 | 17 | /** 18 | * make the code below compatible with browsers without 19 | * an installed firebug like debugger 20 | if (!window.console || !console.firebug) { 21 | var names = ["log", "debug", "info", "warn", "error", "assert", "dir", 22 | "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", 23 | "profile", "profileEnd"]; 24 | window.console = {}; 25 | for (var i = 0; i < names.length; ++i) 26 | window.console[names[i]] = function() {}; 27 | } 28 | */ 29 | 30 | /** 31 | * small helper function to urldecode strings 32 | * 33 | * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL 34 | */ 35 | jQuery.urldecode = function(x) { 36 | if (!x) { 37 | return x 38 | } 39 | return decodeURIComponent(x.replace(/\+/g, ' ')); 40 | }; 41 | 42 | /** 43 | * small helper function to urlencode strings 44 | */ 45 | jQuery.urlencode = encodeURIComponent; 46 | 47 | /** 48 | * This function returns the parsed url parameters of the 49 | * current request. Multiple values per key are supported, 50 | * it will always return arrays of strings for the value parts. 51 | */ 52 | jQuery.getQueryParameters = function(s) { 53 | if (typeof s === 'undefined') 54 | s = document.location.search; 55 | var parts = s.substr(s.indexOf('?') + 1).split('&'); 56 | var result = {}; 57 | for (var i = 0; i < parts.length; i++) { 58 | var tmp = parts[i].split('=', 2); 59 | var key = jQuery.urldecode(tmp[0]); 60 | var value = jQuery.urldecode(tmp[1]); 61 | if (key in result) 62 | result[key].push(value); 63 | else 64 | result[key] = [value]; 65 | } 66 | return result; 67 | }; 68 | 69 | /** 70 | * highlight a given string on a jquery object by wrapping it in 71 | * span elements with the given class name. 72 | */ 73 | jQuery.fn.highlightText = function(text, className) { 74 | function highlight(node, addItems) { 75 | if (node.nodeType === 3) { 76 | var val = node.nodeValue; 77 | var pos = val.toLowerCase().indexOf(text); 78 | if (pos >= 0 && 79 | !jQuery(node.parentNode).hasClass(className) && 80 | !jQuery(node.parentNode).hasClass("nohighlight")) { 81 | var span; 82 | var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); 83 | if (isInSVG) { 84 | span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); 85 | } else { 86 | span = document.createElement("span"); 87 | span.className = className; 88 | } 89 | span.appendChild(document.createTextNode(val.substr(pos, text.length))); 90 | node.parentNode.insertBefore(span, node.parentNode.insertBefore( 91 | document.createTextNode(val.substr(pos + text.length)), 92 | node.nextSibling)); 93 | node.nodeValue = val.substr(0, pos); 94 | if (isInSVG) { 95 | var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); 96 | var bbox = node.parentElement.getBBox(); 97 | rect.x.baseVal.value = bbox.x; 98 | rect.y.baseVal.value = bbox.y; 99 | rect.width.baseVal.value = bbox.width; 100 | rect.height.baseVal.value = bbox.height; 101 | rect.setAttribute('class', className); 102 | addItems.push({ 103 | "parent": node.parentNode, 104 | "target": rect}); 105 | } 106 | } 107 | } 108 | else if (!jQuery(node).is("button, select, textarea")) { 109 | jQuery.each(node.childNodes, function() { 110 | highlight(this, addItems); 111 | }); 112 | } 113 | } 114 | var addItems = []; 115 | var result = this.each(function() { 116 | highlight(this, addItems); 117 | }); 118 | for (var i = 0; i < addItems.length; ++i) { 119 | jQuery(addItems[i].parent).before(addItems[i].target); 120 | } 121 | return result; 122 | }; 123 | 124 | /* 125 | * backward compatibility for jQuery.browser 126 | * This will be supported until firefox bug is fixed. 127 | */ 128 | if (!jQuery.browser) { 129 | jQuery.uaMatch = function(ua) { 130 | ua = ua.toLowerCase(); 131 | 132 | var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || 133 | /(webkit)[ \/]([\w.]+)/.exec(ua) || 134 | /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || 135 | /(msie) ([\w.]+)/.exec(ua) || 136 | ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || 137 | []; 138 | 139 | return { 140 | browser: match[ 1 ] || "", 141 | version: match[ 2 ] || "0" 142 | }; 143 | }; 144 | jQuery.browser = {}; 145 | jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; 146 | } 147 | 148 | /** 149 | * Small JavaScript module for the documentation. 150 | */ 151 | var Documentation = { 152 | 153 | init : function() { 154 | this.fixFirefoxAnchorBug(); 155 | this.highlightSearchWords(); 156 | this.initIndexTable(); 157 | if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) { 158 | this.initOnKeyListeners(); 159 | } 160 | }, 161 | 162 | /** 163 | * i18n support 164 | */ 165 | TRANSLATIONS : {}, 166 | PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; }, 167 | LOCALE : 'unknown', 168 | 169 | // gettext and ngettext don't access this so that the functions 170 | // can safely bound to a different name (_ = Documentation.gettext) 171 | gettext : function(string) { 172 | var translated = Documentation.TRANSLATIONS[string]; 173 | if (typeof translated === 'undefined') 174 | return string; 175 | return (typeof translated === 'string') ? translated : translated[0]; 176 | }, 177 | 178 | ngettext : function(singular, plural, n) { 179 | var translated = Documentation.TRANSLATIONS[singular]; 180 | if (typeof translated === 'undefined') 181 | return (n == 1) ? singular : plural; 182 | return translated[Documentation.PLURALEXPR(n)]; 183 | }, 184 | 185 | addTranslations : function(catalog) { 186 | for (var key in catalog.messages) 187 | this.TRANSLATIONS[key] = catalog.messages[key]; 188 | this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); 189 | this.LOCALE = catalog.locale; 190 | }, 191 | 192 | /** 193 | * add context elements like header anchor links 194 | */ 195 | addContextElements : function() { 196 | $('div[id] > :header:first').each(function() { 197 | $('\u00B6'). 198 | attr('href', '#' + this.id). 199 | attr('title', _('Permalink to this headline')). 200 | appendTo(this); 201 | }); 202 | $('dt[id]').each(function() { 203 | $('\u00B6'). 204 | attr('href', '#' + this.id). 205 | attr('title', _('Permalink to this definition')). 206 | appendTo(this); 207 | }); 208 | }, 209 | 210 | /** 211 | * workaround a firefox stupidity 212 | * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 213 | */ 214 | fixFirefoxAnchorBug : function() { 215 | if (document.location.hash && $.browser.mozilla) 216 | window.setTimeout(function() { 217 | document.location.href += ''; 218 | }, 10); 219 | }, 220 | 221 | /** 222 | * highlight the search words provided in the url in the text 223 | */ 224 | highlightSearchWords : function() { 225 | var params = $.getQueryParameters(); 226 | var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; 227 | if (terms.length) { 228 | var body = $('div.body'); 229 | if (!body.length) { 230 | body = $('body'); 231 | } 232 | window.setTimeout(function() { 233 | $.each(terms, function() { 234 | body.highlightText(this.toLowerCase(), 'highlighted'); 235 | }); 236 | }, 10); 237 | $('') 239 | .appendTo($('#searchbox')); 240 | } 241 | }, 242 | 243 | /** 244 | * init the domain index toggle buttons 245 | */ 246 | initIndexTable : function() { 247 | var togglers = $('img.toggler').click(function() { 248 | var src = $(this).attr('src'); 249 | var idnum = $(this).attr('id').substr(7); 250 | $('tr.cg-' + idnum).toggle(); 251 | if (src.substr(-9) === 'minus.png') 252 | $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); 253 | else 254 | $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); 255 | }).css('display', ''); 256 | if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { 257 | togglers.click(); 258 | } 259 | }, 260 | 261 | /** 262 | * helper function to hide the search marks again 263 | */ 264 | hideSearchWords : function() { 265 | $('#searchbox .highlight-link').fadeOut(300); 266 | $('span.highlighted').removeClass('highlighted'); 267 | var url = new URL(window.location); 268 | url.searchParams.delete('highlight'); 269 | window.history.replaceState({}, '', url); 270 | }, 271 | 272 | /** 273 | * make the url absolute 274 | */ 275 | makeURL : function(relativeURL) { 276 | return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; 277 | }, 278 | 279 | /** 280 | * get the current relative url 281 | */ 282 | getCurrentURL : function() { 283 | var path = document.location.pathname; 284 | var parts = path.split(/\//); 285 | $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { 286 | if (this === '..') 287 | parts.pop(); 288 | }); 289 | var url = parts.join('/'); 290 | return path.substring(url.lastIndexOf('/') + 1, path.length - 1); 291 | }, 292 | 293 | initOnKeyListeners: function() { 294 | $(document).keydown(function(event) { 295 | var activeElementType = document.activeElement.tagName; 296 | // don't navigate when in search box, textarea, dropdown or button 297 | if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT' 298 | && activeElementType !== 'BUTTON' && !event.altKey && !event.ctrlKey && !event.metaKey 299 | && !event.shiftKey) { 300 | switch (event.keyCode) { 301 | case 37: // left 302 | var prevHref = $('link[rel="prev"]').prop('href'); 303 | if (prevHref) { 304 | window.location.href = prevHref; 305 | return false; 306 | } 307 | break; 308 | case 39: // right 309 | var nextHref = $('link[rel="next"]').prop('href'); 310 | if (nextHref) { 311 | window.location.href = nextHref; 312 | return false; 313 | } 314 | break; 315 | } 316 | } 317 | }); 318 | } 319 | }; 320 | 321 | // quick alias for translations 322 | _ = Documentation.gettext; 323 | 324 | $(document).ready(function() { 325 | Documentation.init(); 326 | }); 327 | -------------------------------------------------------------------------------- /docs/_build/html/_static/documentation_options.js: -------------------------------------------------------------------------------- 1 | var DOCUMENTATION_OPTIONS = { 2 | URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), 3 | VERSION: '23-03-2022', 4 | LANGUAGE: 'None', 5 | COLLAPSE_INDEX: false, 6 | BUILDER: 'html', 7 | FILE_SUFFIX: '.html', 8 | LINK_SUFFIX: '.html', 9 | HAS_SOURCE: true, 10 | SOURCELINK_SUFFIX: '.txt', 11 | NAVIGATION_WITH_KEYS: false 12 | }; -------------------------------------------------------------------------------- /docs/_build/html/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GLIMS-RGI/glacier_centerlines/edb361c6090837252d0f6fb13ed4b1b03956b73d/docs/_build/html/_static/file.png -------------------------------------------------------------------------------- /docs/_build/html/_static/hef_flowline.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GLIMS-RGI/glacier_centerlines/edb361c6090837252d0f6fb13ed4b1b03956b73d/docs/_build/html/_static/hef_flowline.jpg -------------------------------------------------------------------------------- /docs/_build/html/_static/language_data.js: -------------------------------------------------------------------------------- 1 | /* 2 | * language_data.js 3 | * ~~~~~~~~~~~~~~~~ 4 | * 5 | * This script contains the language-specific data used by searchtools.js, 6 | * namely the list of stopwords, stemmer, scorer and splitter. 7 | * 8 | * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. 9 | * :license: BSD, see LICENSE for details. 10 | * 11 | */ 12 | 13 | var stopwords = ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"]; 14 | 15 | 16 | /* Non-minified version is copied as a separate JS file, is available */ 17 | 18 | /** 19 | * Porter Stemmer 20 | */ 21 | var Stemmer = function() { 22 | 23 | var step2list = { 24 | ational: 'ate', 25 | tional: 'tion', 26 | enci: 'ence', 27 | anci: 'ance', 28 | izer: 'ize', 29 | bli: 'ble', 30 | alli: 'al', 31 | entli: 'ent', 32 | eli: 'e', 33 | ousli: 'ous', 34 | ization: 'ize', 35 | ation: 'ate', 36 | ator: 'ate', 37 | alism: 'al', 38 | iveness: 'ive', 39 | fulness: 'ful', 40 | ousness: 'ous', 41 | aliti: 'al', 42 | iviti: 'ive', 43 | biliti: 'ble', 44 | logi: 'log' 45 | }; 46 | 47 | var step3list = { 48 | icate: 'ic', 49 | ative: '', 50 | alize: 'al', 51 | iciti: 'ic', 52 | ical: 'ic', 53 | ful: '', 54 | ness: '' 55 | }; 56 | 57 | var c = "[^aeiou]"; // consonant 58 | var v = "[aeiouy]"; // vowel 59 | var C = c + "[^aeiouy]*"; // consonant sequence 60 | var V = v + "[aeiou]*"; // vowel sequence 61 | 62 | var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 63 | var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 64 | var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 65 | var s_v = "^(" + C + ")?" + v; // vowel in stem 66 | 67 | this.stemWord = function (w) { 68 | var stem; 69 | var suffix; 70 | var firstch; 71 | var origword = w; 72 | 73 | if (w.length < 3) 74 | return w; 75 | 76 | var re; 77 | var re2; 78 | var re3; 79 | var re4; 80 | 81 | firstch = w.substr(0,1); 82 | if (firstch == "y") 83 | w = firstch.toUpperCase() + w.substr(1); 84 | 85 | // Step 1a 86 | re = /^(.+?)(ss|i)es$/; 87 | re2 = /^(.+?)([^s])s$/; 88 | 89 | if (re.test(w)) 90 | w = w.replace(re,"$1$2"); 91 | else if (re2.test(w)) 92 | w = w.replace(re2,"$1$2"); 93 | 94 | // Step 1b 95 | re = /^(.+?)eed$/; 96 | re2 = /^(.+?)(ed|ing)$/; 97 | if (re.test(w)) { 98 | var fp = re.exec(w); 99 | re = new RegExp(mgr0); 100 | if (re.test(fp[1])) { 101 | re = /.$/; 102 | w = w.replace(re,""); 103 | } 104 | } 105 | else if (re2.test(w)) { 106 | var fp = re2.exec(w); 107 | stem = fp[1]; 108 | re2 = new RegExp(s_v); 109 | if (re2.test(stem)) { 110 | w = stem; 111 | re2 = /(at|bl|iz)$/; 112 | re3 = new RegExp("([^aeiouylsz])\\1$"); 113 | re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 114 | if (re2.test(w)) 115 | w = w + "e"; 116 | else if (re3.test(w)) { 117 | re = /.$/; 118 | w = w.replace(re,""); 119 | } 120 | else if (re4.test(w)) 121 | w = w + "e"; 122 | } 123 | } 124 | 125 | // Step 1c 126 | re = /^(.+?)y$/; 127 | if (re.test(w)) { 128 | var fp = re.exec(w); 129 | stem = fp[1]; 130 | re = new RegExp(s_v); 131 | if (re.test(stem)) 132 | w = stem + "i"; 133 | } 134 | 135 | // Step 2 136 | re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; 137 | if (re.test(w)) { 138 | var fp = re.exec(w); 139 | stem = fp[1]; 140 | suffix = fp[2]; 141 | re = new RegExp(mgr0); 142 | if (re.test(stem)) 143 | w = stem + step2list[suffix]; 144 | } 145 | 146 | // Step 3 147 | re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; 148 | if (re.test(w)) { 149 | var fp = re.exec(w); 150 | stem = fp[1]; 151 | suffix = fp[2]; 152 | re = new RegExp(mgr0); 153 | if (re.test(stem)) 154 | w = stem + step3list[suffix]; 155 | } 156 | 157 | // Step 4 158 | re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; 159 | re2 = /^(.+?)(s|t)(ion)$/; 160 | if (re.test(w)) { 161 | var fp = re.exec(w); 162 | stem = fp[1]; 163 | re = new RegExp(mgr1); 164 | if (re.test(stem)) 165 | w = stem; 166 | } 167 | else if (re2.test(w)) { 168 | var fp = re2.exec(w); 169 | stem = fp[1] + fp[2]; 170 | re2 = new RegExp(mgr1); 171 | if (re2.test(stem)) 172 | w = stem; 173 | } 174 | 175 | // Step 5 176 | re = /^(.+?)e$/; 177 | if (re.test(w)) { 178 | var fp = re.exec(w); 179 | stem = fp[1]; 180 | re = new RegExp(mgr1); 181 | re2 = new RegExp(meq1); 182 | re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 183 | if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) 184 | w = stem; 185 | } 186 | re = /ll$/; 187 | re2 = new RegExp(mgr1); 188 | if (re.test(w) && re2.test(w)) { 189 | re = /.$/; 190 | w = w.replace(re,""); 191 | } 192 | 193 | // and turn initial Y back to y 194 | if (firstch == "y") 195 | w = firstch.toLowerCase() + w.substr(1); 196 | return w; 197 | } 198 | } 199 | 200 | 201 | 202 | 203 | var splitChars = (function() { 204 | var result = {}; 205 | var singles = [96, 180, 187, 191, 215, 247, 749, 885, 903, 907, 909, 930, 1014, 1648, 206 | 1748, 1809, 2416, 2473, 2481, 2526, 2601, 2609, 2612, 2615, 2653, 2702, 207 | 2706, 2729, 2737, 2740, 2857, 2865, 2868, 2910, 2928, 2948, 2961, 2971, 208 | 2973, 3085, 3089, 3113, 3124, 3213, 3217, 3241, 3252, 3295, 3341, 3345, 209 | 3369, 3506, 3516, 3633, 3715, 3721, 3736, 3744, 3748, 3750, 3756, 3761, 210 | 3781, 3912, 4239, 4347, 4681, 4695, 4697, 4745, 4785, 4799, 4801, 4823, 211 | 4881, 5760, 5901, 5997, 6313, 7405, 8024, 8026, 8028, 8030, 8117, 8125, 212 | 8133, 8181, 8468, 8485, 8487, 8489, 8494, 8527, 11311, 11359, 11687, 11695, 213 | 11703, 11711, 11719, 11727, 11735, 12448, 12539, 43010, 43014, 43019, 43587, 214 | 43696, 43713, 64286, 64297, 64311, 64317, 64319, 64322, 64325, 65141]; 215 | var i, j, start, end; 216 | for (i = 0; i < singles.length; i++) { 217 | result[singles[i]] = true; 218 | } 219 | var ranges = [[0, 47], [58, 64], [91, 94], [123, 169], [171, 177], [182, 184], [706, 709], 220 | [722, 735], [741, 747], [751, 879], [888, 889], [894, 901], [1154, 1161], 221 | [1318, 1328], [1367, 1368], [1370, 1376], [1416, 1487], [1515, 1519], [1523, 1568], 222 | [1611, 1631], [1642, 1645], [1750, 1764], [1767, 1773], [1789, 1790], [1792, 1807], 223 | [1840, 1868], [1958, 1968], [1970, 1983], [2027, 2035], [2038, 2041], [2043, 2047], 224 | [2070, 2073], [2075, 2083], [2085, 2087], [2089, 2307], [2362, 2364], [2366, 2383], 225 | [2385, 2391], [2402, 2405], [2419, 2424], [2432, 2436], [2445, 2446], [2449, 2450], 226 | [2483, 2485], [2490, 2492], [2494, 2509], [2511, 2523], [2530, 2533], [2546, 2547], 227 | [2554, 2564], [2571, 2574], [2577, 2578], [2618, 2648], [2655, 2661], [2672, 2673], 228 | [2677, 2692], [2746, 2748], [2750, 2767], [2769, 2783], [2786, 2789], [2800, 2820], 229 | [2829, 2830], [2833, 2834], [2874, 2876], [2878, 2907], [2914, 2917], [2930, 2946], 230 | [2955, 2957], [2966, 2968], [2976, 2978], [2981, 2983], [2987, 2989], [3002, 3023], 231 | [3025, 3045], [3059, 3076], [3130, 3132], [3134, 3159], [3162, 3167], [3170, 3173], 232 | [3184, 3191], [3199, 3204], [3258, 3260], [3262, 3293], [3298, 3301], [3312, 3332], 233 | [3386, 3388], [3390, 3423], [3426, 3429], [3446, 3449], [3456, 3460], [3479, 3481], 234 | [3518, 3519], [3527, 3584], [3636, 3647], [3655, 3663], [3674, 3712], [3717, 3718], 235 | [3723, 3724], [3726, 3731], [3752, 3753], [3764, 3772], [3774, 3775], [3783, 3791], 236 | [3802, 3803], [3806, 3839], [3841, 3871], [3892, 3903], [3949, 3975], [3980, 4095], 237 | [4139, 4158], [4170, 4175], [4182, 4185], [4190, 4192], [4194, 4196], [4199, 4205], 238 | [4209, 4212], [4226, 4237], [4250, 4255], [4294, 4303], [4349, 4351], [4686, 4687], 239 | [4702, 4703], [4750, 4751], [4790, 4791], [4806, 4807], [4886, 4887], [4955, 4968], 240 | [4989, 4991], [5008, 5023], [5109, 5120], [5741, 5742], [5787, 5791], [5867, 5869], 241 | [5873, 5887], [5906, 5919], [5938, 5951], [5970, 5983], [6001, 6015], [6068, 6102], 242 | [6104, 6107], [6109, 6111], [6122, 6127], [6138, 6159], [6170, 6175], [6264, 6271], 243 | [6315, 6319], [6390, 6399], [6429, 6469], [6510, 6511], [6517, 6527], [6572, 6592], 244 | [6600, 6607], [6619, 6655], [6679, 6687], [6741, 6783], [6794, 6799], [6810, 6822], 245 | [6824, 6916], [6964, 6980], [6988, 6991], [7002, 7042], [7073, 7085], [7098, 7167], 246 | [7204, 7231], [7242, 7244], [7294, 7400], [7410, 7423], [7616, 7679], [7958, 7959], 247 | [7966, 7967], [8006, 8007], [8014, 8015], [8062, 8063], [8127, 8129], [8141, 8143], 248 | [8148, 8149], [8156, 8159], [8173, 8177], [8189, 8303], [8306, 8307], [8314, 8318], 249 | [8330, 8335], [8341, 8449], [8451, 8454], [8456, 8457], [8470, 8472], [8478, 8483], 250 | [8506, 8507], [8512, 8516], [8522, 8525], [8586, 9311], [9372, 9449], [9472, 10101], 251 | [10132, 11263], [11493, 11498], [11503, 11516], [11518, 11519], [11558, 11567], 252 | [11622, 11630], [11632, 11647], [11671, 11679], [11743, 11822], [11824, 12292], 253 | [12296, 12320], [12330, 12336], [12342, 12343], [12349, 12352], [12439, 12444], 254 | [12544, 12548], [12590, 12592], [12687, 12689], [12694, 12703], [12728, 12783], 255 | [12800, 12831], [12842, 12880], [12896, 12927], [12938, 12976], [12992, 13311], 256 | [19894, 19967], [40908, 40959], [42125, 42191], [42238, 42239], [42509, 42511], 257 | [42540, 42559], [42592, 42593], [42607, 42622], [42648, 42655], [42736, 42774], 258 | [42784, 42785], [42889, 42890], [42893, 43002], [43043, 43055], [43062, 43071], 259 | [43124, 43137], [43188, 43215], [43226, 43249], [43256, 43258], [43260, 43263], 260 | [43302, 43311], [43335, 43359], [43389, 43395], [43443, 43470], [43482, 43519], 261 | [43561, 43583], [43596, 43599], [43610, 43615], [43639, 43641], [43643, 43647], 262 | [43698, 43700], [43703, 43704], [43710, 43711], [43715, 43738], [43742, 43967], 263 | [44003, 44015], [44026, 44031], [55204, 55215], [55239, 55242], [55292, 55295], 264 | [57344, 63743], [64046, 64047], [64110, 64111], [64218, 64255], [64263, 64274], 265 | [64280, 64284], [64434, 64466], [64830, 64847], [64912, 64913], [64968, 65007], 266 | [65020, 65135], [65277, 65295], [65306, 65312], [65339, 65344], [65371, 65381], 267 | [65471, 65473], [65480, 65481], [65488, 65489], [65496, 65497]]; 268 | for (i = 0; i < ranges.length; i++) { 269 | start = ranges[i][0]; 270 | end = ranges[i][1]; 271 | for (j = start; j <= end; j++) { 272 | result[j] = true; 273 | } 274 | } 275 | return result; 276 | })(); 277 | 278 | function splitQuery(query) { 279 | var result = []; 280 | var start = -1; 281 | for (var i = 0; i < query.length; i++) { 282 | if (splitChars[query.charCodeAt(i)]) { 283 | if (start !== -1) { 284 | result.push(query.slice(start, i)); 285 | start = -1; 286 | } 287 | } else if (start === -1) { 288 | start = i; 289 | } 290 | } 291 | if (start !== -1) { 292 | result.push(query.slice(start)); 293 | } 294 | return result; 295 | } 296 | 297 | 298 | -------------------------------------------------------------------------------- /docs/_build/html/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GLIMS-RGI/glacier_centerlines/edb361c6090837252d0f6fb13ed4b1b03956b73d/docs/_build/html/_static/minus.png -------------------------------------------------------------------------------- /docs/_build/html/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GLIMS-RGI/glacier_centerlines/edb361c6090837252d0f6fb13ed4b1b03956b73d/docs/_build/html/_static/plus.png -------------------------------------------------------------------------------- /docs/_build/html/_static/pygments.css: -------------------------------------------------------------------------------- 1 | pre { line-height: 125%; } 2 | td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 3 | span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 4 | td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 5 | span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 6 | .highlight .hll { background-color: #ffffcc } 7 | .highlight { background: #f8f8f8; } 8 | .highlight .c { color: #8f5902; font-style: italic } /* Comment */ 9 | .highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */ 10 | .highlight .g { color: #000000 } /* Generic */ 11 | .highlight .k { color: #004461; font-weight: bold } /* Keyword */ 12 | .highlight .l { color: #000000 } /* Literal */ 13 | .highlight .n { color: #000000 } /* Name */ 14 | .highlight .o { color: #582800 } /* Operator */ 15 | .highlight .x { color: #000000 } /* Other */ 16 | .highlight .p { color: #000000; font-weight: bold } /* Punctuation */ 17 | .highlight .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */ 18 | .highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */ 19 | .highlight .cp { color: #8f5902 } /* Comment.Preproc */ 20 | .highlight .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */ 21 | .highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */ 22 | .highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */ 23 | .highlight .gd { color: #a40000 } /* Generic.Deleted */ 24 | .highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */ 25 | .highlight .gr { color: #ef2929 } /* Generic.Error */ 26 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 27 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 28 | .highlight .go { color: #888888 } /* Generic.Output */ 29 | .highlight .gp { color: #745334 } /* Generic.Prompt */ 30 | .highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */ 31 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 32 | .highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */ 33 | .highlight .kc { color: #004461; font-weight: bold } /* Keyword.Constant */ 34 | .highlight .kd { color: #004461; font-weight: bold } /* Keyword.Declaration */ 35 | .highlight .kn { color: #004461; font-weight: bold } /* Keyword.Namespace */ 36 | .highlight .kp { color: #004461; font-weight: bold } /* Keyword.Pseudo */ 37 | .highlight .kr { color: #004461; font-weight: bold } /* Keyword.Reserved */ 38 | .highlight .kt { color: #004461; font-weight: bold } /* Keyword.Type */ 39 | .highlight .ld { color: #000000 } /* Literal.Date */ 40 | .highlight .m { color: #990000 } /* Literal.Number */ 41 | .highlight .s { color: #4e9a06 } /* Literal.String */ 42 | .highlight .na { color: #c4a000 } /* Name.Attribute */ 43 | .highlight .nb { color: #004461 } /* Name.Builtin */ 44 | .highlight .nc { color: #000000 } /* Name.Class */ 45 | .highlight .no { color: #000000 } /* Name.Constant */ 46 | .highlight .nd { color: #888888 } /* Name.Decorator */ 47 | .highlight .ni { color: #ce5c00 } /* Name.Entity */ 48 | .highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */ 49 | .highlight .nf { color: #000000 } /* Name.Function */ 50 | .highlight .nl { color: #f57900 } /* Name.Label */ 51 | .highlight .nn { color: #000000 } /* Name.Namespace */ 52 | .highlight .nx { color: #000000 } /* Name.Other */ 53 | .highlight .py { color: #000000 } /* Name.Property */ 54 | .highlight .nt { color: #004461; font-weight: bold } /* Name.Tag */ 55 | .highlight .nv { color: #000000 } /* Name.Variable */ 56 | .highlight .ow { color: #004461; font-weight: bold } /* Operator.Word */ 57 | .highlight .w { color: #f8f8f8; text-decoration: underline } /* Text.Whitespace */ 58 | .highlight .mb { color: #990000 } /* Literal.Number.Bin */ 59 | .highlight .mf { color: #990000 } /* Literal.Number.Float */ 60 | .highlight .mh { color: #990000 } /* Literal.Number.Hex */ 61 | .highlight .mi { color: #990000 } /* Literal.Number.Integer */ 62 | .highlight .mo { color: #990000 } /* Literal.Number.Oct */ 63 | .highlight .sa { color: #4e9a06 } /* Literal.String.Affix */ 64 | .highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */ 65 | .highlight .sc { color: #4e9a06 } /* Literal.String.Char */ 66 | .highlight .dl { color: #4e9a06 } /* Literal.String.Delimiter */ 67 | .highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */ 68 | .highlight .s2 { color: #4e9a06 } /* Literal.String.Double */ 69 | .highlight .se { color: #4e9a06 } /* Literal.String.Escape */ 70 | .highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */ 71 | .highlight .si { color: #4e9a06 } /* Literal.String.Interpol */ 72 | .highlight .sx { color: #4e9a06 } /* Literal.String.Other */ 73 | .highlight .sr { color: #4e9a06 } /* Literal.String.Regex */ 74 | .highlight .s1 { color: #4e9a06 } /* Literal.String.Single */ 75 | .highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */ 76 | .highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */ 77 | .highlight .fm { color: #000000 } /* Name.Function.Magic */ 78 | .highlight .vc { color: #000000 } /* Name.Variable.Class */ 79 | .highlight .vg { color: #000000 } /* Name.Variable.Global */ 80 | .highlight .vi { color: #000000 } /* Name.Variable.Instance */ 81 | .highlight .vm { color: #000000 } /* Name.Variable.Magic */ 82 | .highlight .il { color: #990000 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /docs/_build/html/_static/underscore.js: -------------------------------------------------------------------------------- 1 | !function(n,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define("underscore",r):(n="undefined"!=typeof globalThis?globalThis:n||self,function(){var t=n._,e=n._=r();e.noConflict=function(){return n._=t,e}}())}(this,(function(){ 2 | // Underscore.js 1.13.1 3 | // https://underscorejs.org 4 | // (c) 2009-2021 Jeremy Ashkenas, Julian Gonggrijp, and DocumentCloud and Investigative Reporters & Editors 5 | // Underscore may be freely distributed under the MIT license. 6 | var n="1.13.1",r="object"==typeof self&&self.self===self&&self||"object"==typeof global&&global.global===global&&global||Function("return this")()||{},t=Array.prototype,e=Object.prototype,u="undefined"!=typeof Symbol?Symbol.prototype:null,o=t.push,i=t.slice,a=e.toString,f=e.hasOwnProperty,c="undefined"!=typeof ArrayBuffer,l="undefined"!=typeof DataView,s=Array.isArray,p=Object.keys,v=Object.create,h=c&&ArrayBuffer.isView,y=isNaN,d=isFinite,g=!{toString:null}.propertyIsEnumerable("toString"),b=["valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"],m=Math.pow(2,53)-1;function j(n,r){return r=null==r?n.length-1:+r,function(){for(var t=Math.max(arguments.length-r,0),e=Array(t),u=0;u=0&&t<=m}}function J(n){return function(r){return null==r?void 0:r[n]}}var G=J("byteLength"),H=K(G),Q=/\[object ((I|Ui)nt(8|16|32)|Float(32|64)|Uint8Clamped|Big(I|Ui)nt64)Array\]/;var X=c?function(n){return h?h(n)&&!q(n):H(n)&&Q.test(a.call(n))}:C(!1),Y=J("length");function Z(n,r){r=function(n){for(var r={},t=n.length,e=0;e":">",'"':""","'":"'","`":"`"},Cn=Ln($n),Kn=Ln(_n($n)),Jn=tn.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g},Gn=/(.)^/,Hn={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},Qn=/\\|'|\r|\n|\u2028|\u2029/g;function Xn(n){return"\\"+Hn[n]}var Yn=/^\s*(\w|\$)+\s*$/;var Zn=0;function nr(n,r,t,e,u){if(!(e instanceof r))return n.apply(t,u);var o=Mn(n.prototype),i=n.apply(o,u);return _(i)?i:o}var rr=j((function(n,r){var t=rr.placeholder,e=function(){for(var u=0,o=r.length,i=Array(o),a=0;a1)ur(a,r-1,t,e),u=e.length;else for(var f=0,c=a.length;f0&&(t=r.apply(this,arguments)),n<=1&&(r=null),t}}var lr=rr(cr,2);function sr(n,r,t){r=qn(r,t);for(var e,u=nn(n),o=0,i=u.length;o0?0:u-1;o>=0&&o0?a=o>=0?o:Math.max(o+f,a):f=o>=0?Math.min(o+1,f):o+f+1;else if(t&&o&&f)return e[o=t(e,u)]===u?o:-1;if(u!=u)return(o=r(i.call(e,a,f),$))>=0?o+a:-1;for(o=n>0?a:f-1;o>=0&&o0?0:i-1;for(u||(e=r[o?o[a]:a],a+=n);a>=0&&a=3;return r(n,Fn(t,u,4),e,o)}}var Ar=wr(1),xr=wr(-1);function Sr(n,r,t){var e=[];return r=qn(r,t),jr(n,(function(n,t,u){r(n,t,u)&&e.push(n)})),e}function Or(n,r,t){r=qn(r,t);for(var e=!er(n)&&nn(n),u=(e||n).length,o=0;o=0}var Br=j((function(n,r,t){var e,u;return D(r)?u=r:(r=Nn(r),e=r.slice(0,-1),r=r[r.length-1]),_r(n,(function(n){var o=u;if(!o){if(e&&e.length&&(n=In(n,e)),null==n)return;o=n[r]}return null==o?o:o.apply(n,t)}))}));function Nr(n,r){return _r(n,Rn(r))}function Ir(n,r,t){var e,u,o=-1/0,i=-1/0;if(null==r||"number"==typeof r&&"object"!=typeof n[0]&&null!=n)for(var a=0,f=(n=er(n)?n:jn(n)).length;ao&&(o=e);else r=qn(r,t),jr(n,(function(n,t,e){((u=r(n,t,e))>i||u===-1/0&&o===-1/0)&&(o=n,i=u)}));return o}function Tr(n,r,t){if(null==r||t)return er(n)||(n=jn(n)),n[Wn(n.length-1)];var e=er(n)?En(n):jn(n),u=Y(e);r=Math.max(Math.min(r,u),0);for(var o=u-1,i=0;i1&&(e=Fn(e,r[1])),r=an(n)):(e=qr,r=ur(r,!1,!1),n=Object(n));for(var u=0,o=r.length;u1&&(t=r[1])):(r=_r(ur(r,!1,!1),String),e=function(n,t){return!Er(r,t)}),Ur(n,e,t)}));function zr(n,r,t){return i.call(n,0,Math.max(0,n.length-(null==r||t?1:r)))}function Lr(n,r,t){return null==n||n.length<1?null==r||t?void 0:[]:null==r||t?n[0]:zr(n,n.length-r)}function $r(n,r,t){return i.call(n,null==r||t?1:r)}var Cr=j((function(n,r){return r=ur(r,!0,!0),Sr(n,(function(n){return!Er(r,n)}))})),Kr=j((function(n,r){return Cr(n,r)}));function Jr(n,r,t,e){A(r)||(e=t,t=r,r=!1),null!=t&&(t=qn(t,e));for(var u=[],o=[],i=0,a=Y(n);ir?(e&&(clearTimeout(e),e=null),a=c,i=n.apply(u,o),e||(u=o=null)):e||!1===t.trailing||(e=setTimeout(f,l)),i};return c.cancel=function(){clearTimeout(e),a=0,e=u=o=null},c},debounce:function(n,r,t){var e,u,o,i,a,f=function(){var c=zn()-u;r>c?e=setTimeout(f,r-c):(e=null,t||(i=n.apply(a,o)),e||(o=a=null))},c=j((function(c){return a=this,o=c,u=zn(),e||(e=setTimeout(f,r),t&&(i=n.apply(a,o))),i}));return c.cancel=function(){clearTimeout(e),e=o=a=null},c},wrap:function(n,r){return rr(r,n)},negate:fr,compose:function(){var n=arguments,r=n.length-1;return function(){for(var t=r,e=n[r].apply(this,arguments);t--;)e=n[t].call(this,e);return e}},after:function(n,r){return function(){if(--n<1)return r.apply(this,arguments)}},before:cr,once:lr,findKey:sr,findIndex:vr,findLastIndex:hr,sortedIndex:yr,indexOf:gr,lastIndexOf:br,find:mr,detect:mr,findWhere:function(n,r){return mr(n,Dn(r))},each:jr,forEach:jr,map:_r,collect:_r,reduce:Ar,foldl:Ar,inject:Ar,reduceRight:xr,foldr:xr,filter:Sr,select:Sr,reject:function(n,r,t){return Sr(n,fr(qn(r)),t)},every:Or,all:Or,some:Mr,any:Mr,contains:Er,includes:Er,include:Er,invoke:Br,pluck:Nr,where:function(n,r){return Sr(n,Dn(r))},max:Ir,min:function(n,r,t){var e,u,o=1/0,i=1/0;if(null==r||"number"==typeof r&&"object"!=typeof n[0]&&null!=n)for(var a=0,f=(n=er(n)?n:jn(n)).length;ae||void 0===t)return 1;if(t 3 | 4 | 5 | 6 | 7 | 8 | 9 | Code — glacier_centerlines 23-03-2022 documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
31 |
32 | 33 | 34 |
35 | 36 |
37 |

Code

38 |

There are 4 main scripts in the repository, listed below:

39 |

Scripts

40 |
41 |
main.py

The workflow of the project is executed in this script. It calls all the other scripts, loads DEM and glaciers outlines and computes the glacier heads, tails and ultimately the centerlines.

42 |
43 |
utils.py

It is a Toolbox for the whole project. It provides some small functions and classes.

44 |
45 |
functions.py

Functions used in main.py. Most of the functions come from OGGM/core and OGGM/utils. Some others are new.

46 |
47 |
params.py

Here the main parameters used in main.py are listed and initialized.

48 |
49 |
50 |
51 | 52 | 53 |
54 | 55 |
56 |
57 | 106 |
107 |
108 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /docs/_build/html/flowlines.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Glacier flowlines — glacier_centerlines 23-03-2022 documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
31 |
32 | 33 | 34 |
35 | 36 |
37 |

Glacier flowlines

38 |

OGGM’s default model is a “flowline model”, which means that the glacier ice flow is 39 | assumed to happen along a representative “1.5D” flowline, as in the image 40 | below. “1.5D” here is used to emphasize that although glacier ice can flow 41 | only in one direction along the flowline, each point of the glacier has 42 | a geometrical width. This width means that flowline glaciers are able to match 43 | the observed area-elevation distribution of true glaciers, and can parametrize 44 | the changes in glacier width with thickness changes.

45 |
46 | _images/hef_flowline.jpg 47 |
48 |

Example of a glacier flowline. Background image from 49 | http://www.swisseduc.ch/glaciers/alps/hintereisferner/index-de.html

50 |
51 |
52 |
53 |

Geometrical centerlines

54 |
55 |

Centerline determination

56 |

Our algorithm is an implementation of the procedure described by 57 | Kienholz et al., (2014). Apart from some minor changes (mostly the choice 58 | of some parameters), we stay close to the original algorithm.

59 |

The basic idea is to find the terminus of the glacier (its lowest point) and 60 | a series of centerline “heads” (local elevation maxima). The centerlines are then 61 | computed with a least cost routing algorithm minimizing both (i) the total 62 | elevation gain and (ii) the distance to the glacier terminus.

63 |

The glacier has a major centerline (the longest one), and 64 | tributary branches (in this case: two). The Hintereisferner glacier is a 65 | good example of a wrongly outlined glacier: the two northern glacier sub-catchments 66 | should have been classified as independent entities since they do not flow 67 | to the main flowline (more on this below).

68 |

At this stage, the centerlines are still not fully suitable 69 | for modelling. Therefore, a rather simple 70 | procedure converts them to “flowlines”, which 71 | now have a regular grid spacing (which they will 72 | keep for the rest of the workflow). The tail of the tributaries are cut 73 | of before reaching the flowline they are tributing to:

74 |

This step is needed to better represent glacier widths at flowline junctions. 75 | The empty circles on the main flowline indicate the location where the respective 76 | tributaries are connected (i.e. where the ice flux that is originating from the 77 | tributary will be added to the main flux when running the model dynamic).

78 |
79 |
80 |
81 |

Pros and cons of both methods

82 |

Flowline representation of the glacier is always a simplification!

83 |
84 |

Geometrical centerlines

85 |
    86 |
  • Pros:

    87 |
      88 |
    • Closer to the “true” length of the glacier.

    • 89 |
    • Grid points along the centerlines preserve their geometrical information, 90 | i.e. one can compute the exact location of ice thickness change.

    • 91 |
    • It is possible to have different model parameters for each flowline (e.g. 92 | different mass-balance models), although this is coming with its own 93 | challenges.

    • 94 |
    • Arguably: better suitability for mass-balance parameterizations taking 95 | glacier geometry and exposition into account.

    • 96 |
    • Arguably: better representation of the main glacier flow?

    • 97 |
    98 |
  • 99 |
  • Cons:

    100 |
      101 |
    • Complex and error prone: considerably more code than the elevation band 102 | flowlines.

    • 103 |
    • Less robust: more glaciers are failing in the preprocessing than with 104 | the simpler method. 105 | When glaciers are badly outlined (or worse, when ice caps are not 106 | properly divided), or with bad DEMs, the geometrical flowline 107 | can “look” very ugly.

    • 108 |
    • Computationally expensive (more grid points on average, more prone 109 | to numerical instabilities).

    • 110 |
    • Complex handling of mass-balance parameters for tributaries at the 111 | inversion (leading to multiple temperature sensitivity parameters 112 | for large glaciers).

    • 113 |
    • Related: all “new generation” mass-balance models in OGGM currently 114 | handle only a single flowline because of this complexity.

    • 115 |
    116 |
  • 117 |
118 |
119 |

Summary

120 |

When to use: when geometry matters, and when length is a important variable. 121 | For mountain glaciers (e.g. Alps, Himalayas). With the old mass-balance 122 | model.

123 |

When not to use: for ice caps, badly outlined glaciers, very large and 124 | flat glaciers, for global applications where geometrical details matters less. 125 | With the more fancy mass-balance models.

126 |
127 |
128 |
129 |
130 |

References

131 |

Kienholz, C., Rich, J. L., Arendt, A. A., and Hock, R.: A new method for deriving glacier centerlines applied to glaciers in Alaska and northwest Canada, The Cryosphere, 8, 503–519, https://doi.org/10.5194/tc-8-503-2014, 2014.

132 |
133 |
134 | 135 | 136 |
137 | 138 |
139 |
140 | 194 |
195 |
196 | 207 | 208 | 209 | 210 | 211 | 212 | -------------------------------------------------------------------------------- /docs/_build/html/genindex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Index — glacier_centerlines 23-03-2022 documentation 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 |
28 |
29 | 30 | 31 |
32 | 33 | 34 |

Index

35 | 36 |
37 | 38 |
39 | 40 | 41 |
42 | 43 |
44 |
45 | 92 |
93 |
94 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /docs/_build/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Welcome to glacier_centerlines’s documentation! — glacier_centerlines 23-03-2022 documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 |
31 | 32 | 33 |
34 | 35 |
36 |

Welcome to glacier_centerlines’s documentation!

37 |
38 |

Overview

39 |

Core principles and structure of the OGGM modelling framework.

40 | 46 |
47 |
48 |
49 |
50 | 51 | 52 |
53 | 54 |
55 |
56 | 104 |
105 |
106 | 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /docs/_build/html/introduction.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Introduction — glacier_centerlines 23-03-2022 documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
31 |
32 | 33 | 34 |
35 | 36 |

ABOUT THIS VERSION 37 | THE CURRENT VERSION V1.0.1 COMPUTES THE CENTERLINES FROM BALTORO GLACIER SUCCESSFULLY. SOME ERRORS AND BUGS FROM LAST VERSION HAVE BEEN SOLVED, SUCH AS THE INTERSECTION CENTERLINE-OUTLINE. THE CODE IS NOW ALSO A BIT NICER. 38 | F . Roura-Adserias, Jul 2022

39 |
40 |

Introduction

41 |

The glacier_centerlines project is a spin-off that comes from OGGM glacier evolution model. We have taken most of the code from the original OGGM functions and workflow. Its main purpose is to compute the glacier central flowlines given a Digital Elevation Model (DEM) and a glacier outline in a georefferenced format.

42 |

Motivation 43 | Due to the increasing complexity of OGGM, it has been convenient to separate the determination of the glacier centerlines from the main model. Therefore users can obtain the glacier flowlines using the same methodology as in OGGM, with a code that is thought to serve this only purpose.

44 |

External links

45 |

OGGM

46 |

OGGM git repository

47 |
48 | 49 | 50 |
51 | 52 |
53 |
54 | 102 |
103 |
104 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /docs/_build/html/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GLIMS-RGI/glacier_centerlines/edb361c6090837252d0f6fb13ed4b1b03956b73d/docs/_build/html/objects.inv -------------------------------------------------------------------------------- /docs/_build/html/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Search — glacier_centerlines 23-03-2022 documentation 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
33 |
34 |
35 | 36 | 37 |
38 | 39 |

Search

40 | 41 | 49 | 50 | 51 |

52 | Searching for multiple words only shows matches that contain 53 | all words. 54 |

55 | 56 | 57 |
58 | 59 | 60 | 61 |
62 | 63 | 64 | 65 |
66 | 67 |
68 | 69 | 70 |
71 | 72 |
73 |
74 | 111 |
112 |
113 | 121 | 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /docs/_build/html/searchindex.js: -------------------------------------------------------------------------------- 1 | Search.setIndex({docnames:["RGI11_comparison","code","flowlines","index","introduction"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":4,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":3,"sphinx.domains.rst":2,"sphinx.domains.std":2,sphinx:56},filenames:["RGI11_comparison.rst","code.rst","flowlines.rst","index.rst","introduction.rst"],objects:{},objnames:{},objtypes:{},terms:{"0":4,"02209":0,"02242":0,"02245":0,"02337":0,"1":[2,4],"10":2,"11":0,"14102022":3,"2":0,"2014":2,"2022":4,"3927":0,"4":1,"503":2,"519":2,"5194":2,"5d":2,"627":0,"8":2,"84":0,"case":2,"class":1,"default":2,"do":2,"function":[1,4],"import":2,"new":[0,1,2,3],"true":2,A:[2,4],AND:4,AS:4,At:2,For:2,IS:4,In:0,It:[1,2],Its:4,SUCH:4,THE:4,The:[1,2,4],There:1,With:2,abl:[0,2],about:4,account:2,ad:2,adseria:4,agreement:0,al:2,alaska:2,algorithm:2,all:[0,1,2],along:2,alp:2,also:4,although:2,alwai:2,amount:0,an:2,apart:2,appli:2,applic:2,ar:[1,2],area:2,arendt:2,arguabl:2,assum:2,averag:2,background:2,bad:2,badli:2,balanc:2,baltoro:4,band:2,basic:2,becaus:2,been:[0,2,4],befor:2,below:[1,2],better:2,bit:4,blob:[],branch:[0,2],bug:4,c:2,call:1,can:[2,4],canada:2,cap:2,catchment:2,centerlin:[1,4],central:4,ch:2,challeng:2,chang:2,check:3,choic:2,circl:2,classifi:2,close:2,closer:2,code:[2,3,4],com:0,come:[1,2,4],complex:[2,4],comput:[0,1,2,4],computation:2,connect:2,consider:2,conveni:4,convert:2,core:[1,3],correspond:0,cost:2,cryospher:2,current:[2,4],cut:2,dash:0,data:0,de:2,dem:[0,1,2,4],deriv:2,describ:2,detail:[0,2],determin:[0,4],dev:0,differ:[0,2],digit:4,direct:2,distanc:2,distribut:2,divid:2,doi:2,due:4,dynam:2,e:2,each:[0,2],elev:[2,4],emphas:2,empti:2,end:0,entiti:2,error:[2,4],et:2,evolut:4,ex1:0,ex2:0,ex3:0,exact:2,exampl:2,execut:1,expens:2,exposit:2,extern:4,f:4,fail:[0,2],fanci:2,find:2,flat:2,flow:2,flowlin:[0,3,4],flux:2,folder:0,format:4,framework:3,from:[0,1,2,4],fulli:2,g:2,gain:2,gener:2,geometri:2,georefferenc:4,get:0,git:4,github:0,given:4,glacier:[0,1,3,4],glacier_centerlin:[0,4],glim:0,global:2,good:2,grid:2,ha:[0,2,4],handl:2,happen:2,have:[0,2,4],head:[0,1,2],here:[1,2],highlight:0,himalaya:2,hintereisfern:2,hock:2,how:0,html:2,http:[0,2],i:2,ic:2,idea:2,ii:2,imag:2,implement:2,increas:4,independ:2,index:2,indic:2,inform:2,initi:1,instabl:2,intersect:4,introduct:3,invers:2,issu:0,its:2,j:2,jul:4,junction:2,keep:2,kienholz:2,l:2,larg:2,last:4,lead:2,least:2,length:2,less:2,link:4,list:1,load:1,local:2,locat:2,longest:2,look:2,lowest:2,main:[0,1,2,4],main_flowlin:0,major:2,mass:2,match:[0,2],matter:2,maxima:2,mean:[0,2],methodolog:4,minim:2,minor:2,model:[2,3,4],more:[0,2],most:[0,1,4],mostli:2,motiv:4,mountain:2,multipl:[0,2],multiple_flowlin:0,need:2,nicer:4,normal:0,northern:2,northwest:2,now:[2,4],numer:2,observ:2,obtain:4,off:4,oggm:[0,1,2,3,4],old:[0,2],one:2,onli:[2,4],org:2,origin:[2,4],other:1,our:[0,2],out:0,outlin:[0,1,2,4],own:2,param:1,paramet:[1,2],parameter:2,parametr:2,point:[0,2],possibl:2,preprocess:2,preserv:2,princip:[],principl:3,procedur:2,project:[1,4],prone:2,properli:2,provid:1,purpl:0,purpos:4,py:[0,1],r:2,rather:2,reach:2,regular:2,relat:2,repositori:[1,4],repres:2,represent:2,respect:2,rest:2,rgi11:3,rgi11_comparison:[],rgi11check:0,rgi60:0,rgi6:0,rgi:0,rich:2,robust:2,roura:4,rout:2,run:[0,2],s:2,same:[0,4],sampl:0,script:[0,1],see:0,sensit:2,separ:4,seri:2,serv:4,should:2,simpl:2,simpler:2,simplif:2,sinc:2,singl:2,slightli:0,small:1,solv:4,some:[0,1,2,4],space:2,spin:4,stage:2,stai:2,start:0,step:2,still:2,structur:3,sub:2,successfulli:4,suitabl:2,swisseduc:2,tail:[0,1,2],take:2,taken:[0,4],tc:2,temperatur:2,terminu:2,test_data:0,than:[0,2],thei:2,them:2,therefor:[2,4],thi:[1,2,4],thick:2,thought:4,time:0,tool:0,toolbox:1,total:2,trajectori:0,tree:0,tribut:2,tributari:[0,2],two:2,ugli:2,ultim:1,us:[1,2,4],user:4,util:1,v1:4,variabl:2,veri:2,version:4,we:[0,2,4],were:0,when:2,where:2,which:2,whole:1,width:2,workflow:[1,2,4],wors:[0,2],wrongli:2,www:2,yourselv:0,zip:0},titles:["RGI11-check","Code","Glacier flowlines","Welcome to glacier_centerlines\u2019s documentation!","Introduction"],titleterms:{both:2,centerlin:2,check:0,code:1,con:2,determin:2,document:3,flowlin:2,further:0,geometr:2,glacier:2,glacier_centerlin:3,info:0,introduct:4,method:2,overview:3,pro:2,refer:2,result:0,rgi11:0,s:3,summari:2,welcom:3}}) -------------------------------------------------------------------------------- /docs/_static/RGI60-11.02209.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GLIMS-RGI/glacier_centerlines/edb361c6090837252d0f6fb13ed4b1b03956b73d/docs/_static/RGI60-11.02209.png -------------------------------------------------------------------------------- /docs/_static/RGI60-11.02242.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GLIMS-RGI/glacier_centerlines/edb361c6090837252d0f6fb13ed4b1b03956b73d/docs/_static/RGI60-11.02242.png -------------------------------------------------------------------------------- /docs/_static/RGI60-11.02245.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GLIMS-RGI/glacier_centerlines/edb361c6090837252d0f6fb13ed4b1b03956b73d/docs/_static/RGI60-11.02245.png -------------------------------------------------------------------------------- /docs/_static/RGI60-11.02337.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GLIMS-RGI/glacier_centerlines/edb361c6090837252d0f6fb13ed4b1b03956b73d/docs/_static/RGI60-11.02337.png -------------------------------------------------------------------------------- /docs/_static/hef_flowline.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GLIMS-RGI/glacier_centerlines/edb361c6090837252d0f6fb13ed4b1b03956b73d/docs/_static/hef_flowline.jpg -------------------------------------------------------------------------------- /docs/code.rst: -------------------------------------------------------------------------------- 1 | Code 2 | ==== 3 | 4 | There are 4 main scripts in the repository, listed below: 5 | 6 | **Scripts** 7 | 8 | *main.py* 9 | The workflow of the project is executed in this script. It calls all the other scripts, loads DEM and glaciers outlines and computes the glacier heads, tails and ultimately the centerlines. 10 | 11 | *utils.py* 12 | It is a Toolbox for the whole project. It provides some small functions and classes. 13 | 14 | *functions.py* 15 | Functions used in main.py. Most of the functions come from OGGM/core and OGGM/utils. Some others are new. 16 | 17 | *params.py* 18 | Here the main parameters used in main.py are listed and initialized. 19 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | # import os 14 | # import sys 15 | # sys.path.insert(0, os.path.abspath('.')) 16 | 17 | 18 | # -- Project information ----------------------------------------------------- 19 | 20 | project = 'glacier_centerlines' 21 | copyright = '2022, FRoura' 22 | author = 'FRoura' 23 | 24 | # The full version, including alpha/beta/rc tags 25 | release = '23-03-2022' 26 | 27 | 28 | # -- General configuration --------------------------------------------------- 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = [ 34 | ] 35 | 36 | # Add any paths that contain templates here, relative to this directory. 37 | templates_path = ['_templates'] 38 | 39 | # List of patterns, relative to source directory, that match files and 40 | # directories to ignore when looking for source files. 41 | # This pattern also affects html_static_path and html_extra_path. 42 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 43 | 44 | 45 | # -- Options for HTML output ------------------------------------------------- 46 | 47 | # The theme to use for HTML and HTML Help pages. See the documentation for 48 | # a list of builtin themes. 49 | # 50 | html_theme = 'alabaster' 51 | 52 | # Add any paths that contain custom static files (such as style sheets) here, 53 | # relative to this directory. They are copied after the builtin static files, 54 | # so a file named "default.css" will overwrite the builtin "default.css". 55 | html_static_path = ['_static'] -------------------------------------------------------------------------------- /docs/flowlines.rst: -------------------------------------------------------------------------------- 1 | .. _flowlines: 2 | 3 | Glacier flowlines 4 | ================= 5 | 6 | OGGM's default model is a "flowline model", which means that the glacier ice flow is 7 | assumed to happen along a representative "1.5D" flowline, as in the image 8 | below. "1.5D" here is used to emphasize that although glacier ice can flow 9 | only in one direction along the flowline, each point of the glacier has 10 | a geometrical width. This width means that flowline glaciers are able to match 11 | the observed area-elevation distribution of true glaciers, and can parametrize 12 | the changes in glacier width with thickness changes. 13 | 14 | .. figure:: _static/hef_flowline.jpg 15 | :width: 80% 16 | :align: left 17 | 18 | Example of a glacier flowline. Background image from 19 | http://www.swisseduc.ch/glaciers/alps/hintereisferner/index-de.html 20 | 21 | 22 | Geometrical centerlines 23 | ----------------------- 24 | 25 | Centerline determination 26 | ~~~~~~~~~~~~~~~~~~~~~~~~ 27 | 28 | Our algorithm is an implementation of the procedure described by 29 | `Kienholz et al., (2014)`_. Apart from some minor changes (mostly the choice 30 | of some parameters), we stay close to the original algorithm. 31 | 32 | .. _Kienholz et al., (2014): http://www.the-cryosphere.net/8/503/2014/ 33 | 34 | 35 | The basic idea is to find the terminus of the glacier (its lowest point) and 36 | a series of centerline "heads" (local elevation maxima). The centerlines are then 37 | computed with a least cost routing algorithm minimizing both (i) the total 38 | elevation gain and (ii) the distance to the glacier terminus. 39 | 40 | The glacier has a major centerline (the longest one), and 41 | tributary branches (in this case: two). The Hintereisferner glacier is a 42 | good example of a wrongly outlined glacier: the two northern glacier sub-catchments 43 | should have been classified as independent entities since they do not flow 44 | to the main flowline (more on this below). 45 | 46 | At this stage, the centerlines are still not fully suitable 47 | for modelling. Therefore, a rather simple 48 | procedure converts them to "flowlines", which 49 | now have a regular grid spacing (which they will 50 | keep for the rest of the workflow). The tail of the tributaries are cut 51 | of before reaching the flowline they are tributing to: 52 | 53 | This step is needed to better represent glacier widths at flowline junctions. 54 | The empty circles on the main flowline indicate the location where the respective 55 | tributaries are connected (i.e. where the ice flux that is originating from the 56 | tributary will be added to the main flux when running the model dynamic). 57 | 58 | 59 | .. _flprocons: 60 | 61 | Pros and cons of both methods 62 | ----------------------------- 63 | 64 | Flowline representation of the glacier is **always** a simplification! 65 | 66 | Geometrical centerlines 67 | ~~~~~~~~~~~~~~~~~~~~~~~ 68 | 69 | - Pros: 70 | 71 | - Closer to the "true" length of the glacier. 72 | - Grid points along the centerlines preserve their geometrical information, 73 | i.e. one can compute the exact location of ice thickness change. 74 | - It is possible to have different model parameters for each flowline (e.g. 75 | different mass-balance models), although this is coming with its own 76 | challenges. 77 | - Arguably: better suitability for mass-balance parameterizations taking 78 | glacier geometry and exposition into account. 79 | - Arguably: better representation of the main glacier flow? 80 | 81 | - Cons: 82 | 83 | - Complex and error prone: considerably more code than the elevation band 84 | flowlines. 85 | - Less robust: more glaciers are failing in the preprocessing than with 86 | the simpler method. 87 | When glaciers are badly outlined (or worse, when ice caps are not 88 | properly divided), or with bad DEMs, the geometrical flowline 89 | can "look" very ugly. 90 | - Computationally expensive (more grid points on average, more prone 91 | to numerical instabilities). 92 | - Complex handling of mass-balance parameters for tributaries at the 93 | inversion (leading to multiple temperature sensitivity parameters 94 | for large glaciers). 95 | - Related: **all "new generation" mass-balance models in OGGM currently 96 | handle only a single flowline because of this complexity.** 97 | 98 | .. admonition:: **Summary** 99 | 100 | **When to use:** when geometry matters, and when length is a important variable. 101 | For mountain glaciers (e.g. Alps, Himalayas). With the old mass-balance 102 | model. 103 | 104 | **When not to use:** for ice caps, badly outlined glaciers, very large and 105 | flat glaciers, for global applications where geometrical details matters less. 106 | With the more fancy mass-balance models. 107 | 108 | 109 | References 110 | ---------- 111 | 112 | Kienholz, C., Rich, J. L., Arendt, A. A., and Hock, R.: A new method for deriving glacier centerlines applied to glaciers in Alaska and northwest Canada, The Cryosphere, 8, 503–519, https://doi.org/10.5194/tc-8-503-2014, 2014. 113 | 114 | 115 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. glacier_centerlines documentation master file, created by 2 | sphinx-quickstart on Thu Mar 24 16:41:38 2022. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to glacier_centerlines's documentation! 7 | =============================================== 8 | 9 | Overview 10 | ^^^^^^^^ 11 | 12 | Core principles and structure of the OGGM modelling framework. 13 | 14 | * :doc:`introduction` 15 | * :doc:`flowlines` 16 | * :doc:`code` 17 | * :doc:`RGI11_comparison` **NEW! (14102022)** 18 | 19 | 20 | 21 | .. toctree:: 22 | :maxdepth: 2 23 | :hidden: 24 | :caption: GoTo: 25 | 26 | introduction.rst 27 | flowlines.rst 28 | code.rst 29 | RGI11_comparison.rst 30 | 31 | 32 | -------------------------------------------------------------------------------- /docs/introduction.rst: -------------------------------------------------------------------------------- 1 | **ABOUT THIS VERSION** 2 | THE CURRENT VERSION V1.0.1 COMPUTES THE CENTERLINES FROM BALTORO GLACIER SUCCESSFULLY. SOME ERRORS AND BUGS FROM LAST VERSION HAVE BEEN SOLVED, SUCH AS THE INTERSECTION CENTERLINE-OUTLINE. THE CODE IS NOW ALSO A BIT NICER. 3 | F . Roura-Adserias, Jul 2022 4 | 5 | Introduction 6 | ============ 7 | 8 | The glacier_centerlines project is a spin-off that comes from OGGM glacier evolution model. We have taken most of the code from the original OGGM functions and workflow. Its main purpose is to compute the glacier central flowlines given a Digital Elevation Model (DEM) and a glacier outline in a georefferenced format. 9 | 10 | **Motivation** 11 | Due to the increasing complexity of OGGM, it has been convenient to separate the determination of the glacier centerlines from the main model. Therefore users can obtain the glacier flowlines using the same methodology as in OGGM, with a code that is thought to serve this only purpose. 12 | 13 | 14 | **External links** 15 | 16 | `OGGM `_ 17 | 18 | `OGGM git repository `_ 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.https://www.sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /functions.py: -------------------------------------------------------------------------------- 1 | """ 2 | Functions to be called in the main centerline script 3 | by froura, Mar 2022 4 | """ 5 | # import libraries -------------------- 6 | import shapely.geometry as shpg 7 | import shapely 8 | import numpy as np 9 | from osgeo import gdal 10 | import scipy 11 | from scipy.ndimage.morphology import distance_transform_edt 12 | from scipy.interpolate import RegularGridInterpolator 13 | import copy 14 | from scipy.ndimage.filters import gaussian_filter1d 15 | from functools import partial 16 | from params import (f1, f2, a, b, terminus_search_percentile,\ 17 | terminus_search_altitude_range) 18 | 19 | def coordinate_change(tif_path): 20 | """ 21 | Parameters 22 | ---------- 23 | tif_path :str 24 | path to raster file 25 | 26 | Returns 27 | ------- 28 | Raster values and raster parameters (xOrigin, yOrigin, pixelHeight, 29 | pixelWidth) 30 | """ 31 | #crop_extent.crs.to_epsg(4326) 32 | dataset = gdal.Open(tif_path) 33 | band = dataset.GetRasterBand(1) 34 | 35 | cols = dataset.RasterXSize 36 | rows = dataset.RasterYSize 37 | 38 | # map pixel/line coordinates into georeferenced space 39 | transform = dataset.GetGeoTransform() 40 | 41 | xOrigin = transform[0] 42 | yOrigin = transform[3] 43 | pixelWidth = transform[1] 44 | pixelHeight = -transform[5] 45 | 46 | data = band.ReadAsArray(0, 0, cols, rows) 47 | pix_params = [xOrigin,yOrigin,pixelHeight,pixelWidth] 48 | 49 | return data, pix_params 50 | 51 | 52 | def profile(points_xy, data, pix_params): 53 | """ 54 | Parameters 55 | ---------- 56 | points_list : 57 | list with lat, lon. 58 | data : 59 | np.ndarray, altitude (topography) of each pixel 60 | pix_params : 61 | list with (xorigin, yorigin, pixelH, pixelW) 62 | 63 | Returns 64 | ------- 65 | tuple: profile distance (arbitrary units) - altitude (m) 66 | """ 67 | 68 | # initialize vectors 69 | alt = np.zeros(1) 70 | dist = np.zeros(1) 71 | dumdist = 0 72 | 73 | xOrigin, yOrigin, pixelHeight, pixelWidth = pix_params 74 | 75 | # altitude 76 | for point in points_xy: 77 | col = int((point[0] - xOrigin) / pixelWidth) 78 | row = int((yOrigin - point[1]) / pixelHeight) 79 | 80 | alt = np.append(alt, data[row][col]) 81 | 82 | # remove dummy 0 in the beginning 83 | alt = alt[1:] 84 | 85 | # distance along line 86 | # Distance between 2 points 87 | 88 | # repeat the first point at the end 89 | # np.append(points_list, points_list[0]) 90 | 91 | for i in np.arange(len(points_xy)): 92 | i = int(i) 93 | a = shpg.Point(points_xy[i]) 94 | # last point 95 | if i == len(points_xy)-1: 96 | d = a.distance(shpg.Point(points_xy[0])) 97 | else: 98 | d = a.distance(shpg.Point(points_xy[i+1])) 99 | dumdist = dumdist + d 100 | dist = np.append(dist, dumdist) 101 | 102 | # remove the dummy 0 ini point 103 | dist = dist[1:] 104 | 105 | return dist, alt 106 | 107 | 108 | def get_terminus_coord(ext_yx, zoutline): 109 | """This finds the terminus coordinate of the glacier. 110 | There is a special case for marine terminating glaciers/ 111 | Parameters 112 | ---------- 113 | ext_yx : list 114 | list with the coordinates (y,x) from the points on the glacier outline 115 | zoutline : np.ndarray 116 | altitude of the outline points 117 | Returns 118 | ------- 119 | xy - coordinates (shapely.geometry.point.Point) of the glacier terminus. 120 | index: integer, index of the terminus in the input list. 121 | """ 122 | # NOTE: possible problems in tidewater because of not constant distance 123 | # between points 124 | 125 | #perc = 10 # from oggm #cfg.PARAMS['terminus_search_percentile'] 126 | perc = terminus_search_percentile 127 | #deltah = 20 # problem #50m(?)#cfg.PARAMS['terminus_search_altitude_range'] 128 | deltah = terminus_search_altitude_range 129 | 130 | # if gdir.is_tidewater and (perc > 0): 131 | if min(zoutline) == 0 and perc > 0: 132 | 133 | plow = np.percentile(zoutline, perc).astype(np.int64) 134 | 135 | # the minimum altitude in the glacier outline 136 | mini = np.min(zoutline) 137 | 138 | # indices of where in the outline the altitude is lower than the qth 139 | # percentile and lower than $delatah meters higher, 140 | # than the minimum altitude 141 | ind = np.where((zoutline < plow) & (zoutline < (mini + deltah)))[0] 142 | 143 | # We take the middle of this area 144 | try: 145 | ind_term = ind[np.round(len(ind) / 2.).astype(int)] 146 | except IndexError: 147 | # Sometimes the default perc is not large enough 148 | try: 149 | # Repeat 150 | perc *= 2 151 | plow = np.percentile(zoutline, perc).astype(np.int64) 152 | mini = np.min(zoutline) 153 | ind = np.where((zoutline < plow) & 154 | (zoutline < (mini + deltah)))[0] 155 | ind_term = ind[np.round(len(ind) / 2.).astype(int)] 156 | except IndexError: 157 | # Last resort 158 | ind_term = np.argmin(zoutline) 159 | else: 160 | # easy: just the minimum 161 | ind_term = np.argmin(zoutline) 162 | # find coordinated from ind_term 163 | xterm = ext_yx[ind_term][0] 164 | yterm = ext_yx[ind_term][1] 165 | 166 | xyterm = shpg.Point(xterm, yterm) 167 | return xyterm, ind_term 168 | 169 | 170 | def _make_costgrid(mask, ext, z): 171 | """Computes a costgrid following Kienholz et al. (2014) Eq. (2) 172 | Parameters 173 | ---------- 174 | mask : numpy.array 175 | The glacier mask. 176 | ext : numpy.array 177 | The glacier boundaries' mask. 178 | z : numpy.array 179 | The terrain height. 180 | Returns 181 | ------- 182 | numpy.array of the costgrid 183 | """ 184 | # # Kienholz et al eq (2) 185 | # f1 = 1000. 186 | # f2 = 3000. 187 | # a = 4.25 #literature 188 | # b = 3.7 189 | 190 | dis = np.where(mask, distance_transform_edt(mask), np.NaN) 191 | z = np.where(mask, z, np.NaN) 192 | 193 | dmax = np.nanmax(dis) 194 | zmax = np.nanmax(z) 195 | zmin = np.nanmin(z) 196 | cost = ((dmax - dis) / dmax * f1) ** a + \ 197 | ((z - zmin) / (zmax - zmin) * f2) ** b 198 | 199 | # This is new: we make the cost to go over boundaries 200 | # arbitrary high to avoid the lines to jump over adjacent boundaries 201 | cost[np.where(ext)] = np.nanmax(cost[np.where(ext)]) * 50 202 | # this works but makes the costgrid plot ugly 203 | return np.where(mask, cost, np.Inf) 204 | 205 | 206 | def _filter_lines(lines, heads, k, r): 207 | """Filter the centerline candidates by length. 208 | Kienholz et al. (2014), Ch. 4.3.1 209 | Parameters 210 | ---------- 211 | lines : list of shapely.geometry.LineString instances 212 | The lines to filter out (in raster coordinates). 213 | heads : list of shapely.geometry.Point instances 214 | The heads corresponding to the lines. (also in raster coordinates) 215 | k : float 216 | A buffer (in raster coordinates) to cut around the selected lines 217 | r : float 218 | The lines shorter than r will be filtered out. 219 | Returns 220 | ------- 221 | (lines, heads) a list of the new lines and corresponding heads 222 | """ 223 | 224 | olines = [] 225 | oheads = [] 226 | ilines = copy.copy(lines) 227 | 228 | lastline = None 229 | while len(ilines) > 0: # loop as long as we haven't filtered all lines 230 | if len(olines) > 0: # enter this after the first step only 231 | 232 | toremove = lastline.buffer(k) # buffer centerlines the last line 233 | tokeep = [] 234 | for l in ilines: 235 | # loop over all remaining lines and compute their diff 236 | # to the last longest line 237 | diff = l.difference(toremove) 238 | if diff.type == 'MultiLineString': 239 | # Remove the lines that have no head 240 | diff = list(diff.geoms) 241 | for il in diff: 242 | hashead = False 243 | for h in heads: 244 | #if il.intersects(h): # after the smoothing the lines may not finish at the same point "head". 245 | # hashead = True 246 | # diff = il 247 | break 248 | if hashead: 249 | break 250 | else: 251 | diff = None 252 | # keep this head line only if it's long enough 253 | if diff is not None and diff.length > r: 254 | # Fun fact. The heads can be cut by the buffer too 255 | diff = shpg.LineString(l.coords[0:2] + diff.coords[2:]) 256 | tokeep.append(diff) 257 | ilines = tokeep 258 | # it could happen that we're done at this point 259 | if len(ilines) == 0: 260 | break 261 | 262 | # Otherwise keep the longest one and continue 263 | lengths = np.array([]) 264 | for l in ilines: 265 | lengths = np.append(lengths, l.length) 266 | ll = ilines[np.argmax(lengths)] 267 | 268 | ilines.remove(ll) 269 | if len(olines) > 0: 270 | # the cut line's last point is not guaranteed 271 | # to on straight coordinates. Remove it 272 | olines.append(shpg.LineString(np.asarray(ll.xy)[:, 0:-1].T)) 273 | else: 274 | olines.append(ll) 275 | lastline = ll 276 | 277 | # add the corresponding head to each line 278 | for l in olines: 279 | # for h in heads: 280 | # if l.intersects(h): 281 | # oheads.append(h) 282 | # break 283 | oheads.append(l.coords[-1]) # assign first point as head of the centerline 284 | print(len(oheads)) # --> here is the problem!!! there are only 12 heads that remain 285 | # in the same position after the smoothing! I dont know how to fix it. --> recompute heads? 286 | print(len(olines)) 287 | 288 | return olines, oheads 289 | 290 | 291 | def _filter_lines_slope(lines, heads, topo, gdir, min_slope): 292 | """Filter the centerline candidates by slope: if they go up, remove 293 | Kienholz et al. (2014), Ch. 4.3.1 294 | Parameters 295 | ---------- 296 | lines : list of shapely.geometry.LineString instances 297 | The lines to filter out (in raster coordinates). 298 | topo : the glacier topography 299 | gdir : the glacier directory for simplicity 300 | min_slope: rad 301 | Returns 302 | ------- 303 | (lines, heads) a list of the new lines and corresponding heads 304 | """ 305 | import params 306 | dx_cls = params.flowline_dx 307 | lid = params.flowline_junction_pix 308 | sw = params.flowline_height_smooth 309 | 310 | # Bilinear interpolation 311 | # Geometries coordinates are in "pixel centered" convention, i.e 312 | # (0, 0) is also located in the center of the pixel 313 | xy = (np.arange(0, gdir.grid.ny-0.1, 1), 314 | np.arange(0, gdir.grid.nx-0.1, 1)) 315 | interpolator = RegularGridInterpolator(xy, topo) 316 | 317 | olines = [lines[0]] 318 | oheads = [heads[0]] 319 | for line, head in zip(lines[1:], heads[1:]): 320 | 321 | # The code below mimics what initialize_flowlines will do 322 | # this is a bit smelly but necessary 323 | points = line_interpol(line, dx_cls) 324 | 325 | # For tributaries, remove the tail 326 | points = points[0:-lid] 327 | 328 | new_line = shpg.LineString(points) 329 | 330 | # Interpolate heights 331 | x, y = new_line.xy 332 | hgts = interpolator((y, x)) 333 | 334 | # If smoothing, this is the moment 335 | hgts = gaussian_filter1d(hgts, sw) 336 | 337 | # Finally slope 338 | slope = np.arctan(-np.gradient(hgts, dx_cls*gdir.grid.dx)) 339 | 340 | # And altitude range 341 | z_range = np.max(hgts) - np.min(hgts) 342 | # arbitrary threshold with which we filter the lines, otherwise bye bye 343 | if np.sum(slope >= min_slope) >= 5 and z_range > 10: 344 | olines.append(line) 345 | oheads.append(head) 346 | 347 | return olines, oheads 348 | 349 | 350 | def _normalize(n): 351 | """Computes the normals of a vector n. 352 | Returns 353 | ------- 354 | the two normals (n1, n2) 355 | """ 356 | nn = n / np.sqrt(np.sum(n*n)) 357 | n1 = np.array([-nn[1], nn[0]]) 358 | n2 = np.array([nn[1], -nn[0]]) 359 | return n1, n2 360 | 361 | 362 | def _projection_point(centerline, point): 363 | """Projects a point on a line and returns the closest integer point 364 | guaranteed to be on the line, and guaranteed to be far enough from the 365 | head and tail. 366 | Parameters 367 | ---------- 368 | centerline : Centerline instance 369 | point : Shapely Point geometry 370 | Returns 371 | ------- 372 | (flow_point, ind_closest): Shapely Point and indice in the line 373 | """ 374 | prdis = centerline.line.project(point, normalized=False) 375 | ind_closest = np.argmin(np.abs(centerline.dis_on_line - prdis)).item() 376 | flow_point = shpg.Point(centerline.line.coords[int(ind_closest)]) 377 | return flow_point 378 | 379 | def line_interpol(line, dx): 380 | """Interpolates a shapely LineString to a regularly spaced one. 381 | Shapely's interpolate function does not guaranty equally 382 | spaced points in space. This is what this function is for. 383 | We construct new points on the line but at constant distance from the 384 | preceding one. 385 | Parameters 386 | ---------- 387 | line: a shapely.geometry.LineString instance 388 | dx: the spacing 389 | Returns 390 | ------- 391 | a list of equally distanced points 392 | """ 393 | 394 | # First point is easy 395 | points = [line.interpolate(dx / 2.)] 396 | 397 | # Continue as long as line is not finished 398 | while True: 399 | pref = points[-1] 400 | pbs = pref.buffer(dx).boundary.intersection(line) 401 | if pbs.type == 'Point': 402 | pbs = [pbs] 403 | elif pbs.type == 'LineString': 404 | # This is rare 405 | pbs = [shpg.Point(c) for c in pbs.coords] 406 | assert len(pbs) == 2 407 | elif pbs.type == 'GeometryCollection': 408 | # This is rare 409 | opbs = [] 410 | for p in pbs.geoms: 411 | if p.type == 'Point': 412 | opbs.append(p) 413 | elif p.type == 'LineString': 414 | opbs.extend([shpg.Point(c) for c in p.coords]) 415 | pbs = opbs 416 | else: 417 | if pbs.type != 'MultiPoint': 418 | raise RuntimeError('line_interpol: we expect a MultiPoint ' 419 | 'but got a {}.'.format(pbs.type)) 420 | 421 | try: 422 | # Shapely v2 compat 423 | pbs = pbs.geoms 424 | except AttributeError: 425 | pass 426 | 427 | # Out of the point(s) that we get, take the one farthest from the top 428 | refdis = line.project(pref) 429 | tdis = np.array([line.project(pb) for pb in pbs]) 430 | p = np.where(tdis > refdis)[0] 431 | if len(p) == 0: 432 | break 433 | points.append(pbs[int(p[0])]) 434 | 435 | return points 436 | 437 | def gaussian_blur(in_array, size): 438 | """Applies a Gaussian filter to a 2d array. 439 | Parameters 440 | ---------- 441 | in_array : numpy.array 442 | The array to smooth. 443 | size : int 444 | The half size of the smoothing window. 445 | Returns 446 | ------- 447 | a smoothed numpy.array 448 | """ 449 | 450 | # expand in_array to fit edge of kernel 451 | padded_array = np.pad(in_array, size, 'symmetric') 452 | 453 | # build kernel 454 | x, y = np.mgrid[-size:size + 1, -size:size + 1] 455 | g = np.exp(-(x**2 / float(size) + y**2 / float(size))) 456 | g = (g / g.sum()).astype(np.float) # I had to change that 457 | 458 | # do the Gaussian blur 459 | return scipy.signal.fftconvolve(padded_array, g, mode='valid') 460 | 461 | -------------------------------------------------------------------------------- /functions_rgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | Functions to be called in the main centerline script 3 | by froura, Mar 2022 4 | """ 5 | # import libraries -------------------- 6 | import shapely.geometry as shpg 7 | import shapely 8 | import numpy as np 9 | from osgeo import gdal 10 | import scipy 11 | from scipy.ndimage.morphology import distance_transform_edt 12 | from scipy.interpolate import RegularGridInterpolator 13 | import copy 14 | from scipy.ndimage.filters import gaussian_filter1d 15 | from functools import partial 16 | #from params import 17 | #import oggm.cfg as cfg 18 | 19 | #f1 = cfg.PARAMS['f1'] 20 | #f2 = cfg.PARAMS['f2'] 21 | #a = cfg.PARAMS['a'] 22 | #b = cfg.PARAMS['b'] 23 | f1 = 1000. 24 | f2 = 3000. 25 | a = 4.25 #4.25 in literature 26 | b = 3.7 27 | terminus_search_percentile = 10#cfg.PARAMS['terminus_search_percentile'] 28 | terminus_search_altitude_range = 20#cfg.PARAMS['terminus_search_altitude_range'] 29 | 30 | # class defined to be able to use some functions from OGGM "as they are". 31 | class glacier_dir(object): 32 | def __init__(self, grid): 33 | self.grid = grid 34 | 35 | 36 | ################## 37 | # move this to utils: 38 | 39 | 40 | def coordinate_change(tif_path): 41 | """ 42 | Parameters 43 | ---------- 44 | tif_path :str 45 | path to raster file 46 | 47 | Returns 48 | ------- 49 | Raster values and raster parameters (xOrigin, yOrigin, pixelHeight, 50 | pixelWidth) 51 | """ 52 | #crop_extent.crs.to_epsg(4326) 53 | dataset = gdal.Open(tif_path) 54 | band = dataset.GetRasterBand(1) 55 | 56 | cols = dataset.RasterXSize 57 | rows = dataset.RasterYSize 58 | 59 | # map pixel/line coordinates into georeferenced space 60 | transform = dataset.GetGeoTransform() 61 | 62 | xOrigin = transform[0] 63 | yOrigin = transform[3] 64 | pixelWidth = transform[1] 65 | pixelHeight = -transform[5] 66 | 67 | data = band.ReadAsArray(0, 0, cols, rows) 68 | pix_params = [xOrigin,yOrigin,pixelHeight,pixelWidth] 69 | 70 | return data, pix_params 71 | 72 | 73 | def profile(points_xy, data, pix_params): 74 | """ 75 | Parameters 76 | ---------- 77 | points_list : 78 | list with lat, lon. 79 | data : 80 | np.ndarray, altitude (topography) of each pixel 81 | pix_params : 82 | list with (xorigin, yorigin, pixelH, pixelW) 83 | 84 | Returns 85 | ------- 86 | tuple: profile distance (arbitrary units) - altitude (m) 87 | """ 88 | 89 | # initialize vectors 90 | alt = np.zeros(1) 91 | dist = np.zeros(1) 92 | dumdist = 0 93 | 94 | xOrigin, yOrigin, pixelHeight, pixelWidth = pix_params 95 | 96 | # altitude 97 | for point in points_xy: 98 | col = int((point[0] - xOrigin) / pixelWidth) 99 | row = int((yOrigin - point[1]) / pixelHeight) 100 | 101 | alt = np.append(alt, data[row][col]) 102 | 103 | # remove dummy 0 in the beginning 104 | alt = alt[1:] 105 | 106 | # distance along line 107 | # Distance between 2 points 108 | 109 | # repeat the first point at the end 110 | # np.append(points_list, points_list[0]) 111 | 112 | for i in np.arange(len(points_xy)): 113 | i = int(i) 114 | a = shpg.Point(points_xy[i]) 115 | # last point 116 | if i == len(points_xy)-1: 117 | d = a.distance(shpg.Point(points_xy[0])) 118 | else: 119 | d = a.distance(shpg.Point(points_xy[i+1])) 120 | dumdist = dumdist + d 121 | dist = np.append(dist, dumdist) 122 | 123 | # remove the dummy 0 ini point 124 | dist = dist[1:] 125 | 126 | return dist, alt 127 | 128 | 129 | def get_terminus_coord(ext_yx, zoutline): 130 | """This finds the terminus coordinate of the glacier. 131 | There is a special case for marine terminating glaciers/ 132 | Parameters 133 | ---------- 134 | ext_yx : list 135 | list with the coordinates (y,x) from the points on the glacier outline 136 | zoutline : np.ndarray 137 | altitude of the outline points 138 | Returns 139 | ------- 140 | xy - coordinates (shapely.geometry.point.Point) of the glacier terminus. 141 | index: integer, index of the terminus in the input list. 142 | """ 143 | # NOTE: possible problems in tidewater because of not constant distance 144 | # between points 145 | 146 | #perc = 10 # from oggm #cfg.PARAMS['terminus_search_percentile'] 147 | perc = terminus_search_percentile 148 | #deltah = 20 # problem #50m(?)#cfg.PARAMS['terminus_search_altitude_range'] 149 | deltah = terminus_search_altitude_range 150 | 151 | # if gdir.is_tidewater and (perc > 0): 152 | if min(zoutline) == 0 and perc > 0: 153 | 154 | plow = np.percentile(zoutline, perc).astype(np.int64) 155 | 156 | # the minimum altitude in the glacier outline 157 | mini = np.min(zoutline) 158 | 159 | # indices of where in the outline the altitude is lower than the qth 160 | # percentile and lower than $delatah meters higher, 161 | # than the minimum altitude 162 | ind = np.where((zoutline < plow) & (zoutline < (mini + deltah)))[0] 163 | 164 | # We take the middle of this area 165 | try: 166 | ind_term = ind[np.round(len(ind) / 2.).astype(int)] 167 | except IndexError: 168 | # Sometimes the default perc is not large enough 169 | try: 170 | # Repeat 171 | perc *= 2 172 | plow = np.percentile(zoutline, perc).astype(np.int64) 173 | mini = np.min(zoutline) 174 | ind = np.where((zoutline < plow) & 175 | (zoutline < (mini + deltah)))[0] 176 | ind_term = ind[np.round(len(ind) / 2.).astype(int)] 177 | except IndexError: 178 | # Last resort 179 | ind_term = np.argmin(zoutline) 180 | else: 181 | # easy: just the minimum 182 | ind_term = np.argmin(zoutline) 183 | # find coordinated from ind_term 184 | xterm = ext_yx[ind_term][0] 185 | yterm = ext_yx[ind_term][1] 186 | 187 | xyterm = shpg.Point(xterm, yterm) 188 | return xyterm, ind_term 189 | 190 | 191 | def _make_costgrid(mask, ext, z): 192 | """Computes a costgrid following Kienholz et al. (2014) Eq. (2) 193 | Parameters 194 | ---------- 195 | mask : numpy.array 196 | The glacier mask. 197 | ext : numpy.array 198 | The glacier boundaries' mask. 199 | z : numpy.array 200 | The terrain height. 201 | Returns 202 | ------- 203 | numpy.array of the costgrid 204 | """ 205 | # # Kienholz et al eq (2) 206 | # f1 = 1000. 207 | # f2 = 3000. 208 | # a = 4.25 #literature 209 | # b = 3.7 210 | 211 | dis = np.where(mask, distance_transform_edt(mask), np.NaN) 212 | z = np.where(mask, z, np.NaN) 213 | 214 | dmax = np.nanmax(dis) 215 | zmax = np.nanmax(z) 216 | zmin = np.nanmin(z) 217 | cost = ((dmax - dis) / dmax * f1) ** a + \ 218 | ((z - zmin) / (zmax - zmin) * f2) ** b 219 | 220 | # This is new: we make the cost to go over boundaries 221 | # arbitrary high to avoid the lines to jump over adjacent boundaries 222 | cost[np.where(ext)] = np.nanmax(cost[np.where(ext)]) * 50 223 | # this works but makes the costgrid plot ugly 224 | return np.where(mask, cost, np.Inf) 225 | 226 | 227 | def _filter_lines1(lines, heads, k, r): 228 | """Filter the centerline candidates by length. 229 | Kienholz et al. (2014), Ch. 4.3.1 230 | Parameters 231 | ---------- 232 | lines : list of shapely.geometry.LineString instances 233 | The lines to filter out (in raster coordinates). 234 | heads : list of shapely.geometry.Point instances 235 | The heads corresponding to the lines. (also in raster coordinates) 236 | k : float 237 | A buffer (in raster coordinates) to cut around the selected lines 238 | r : float 239 | The lines shorter than r will be filtered out. 240 | Returns 241 | ------- 242 | (lines, heads) a list of the new lines and corresponding heads 243 | """ 244 | 245 | olines = [] 246 | oheads = [] 247 | ilines = copy.copy(lines) 248 | 249 | lastline = None 250 | while len(ilines) > 0: # loop as long as we haven't filtered all lines 251 | if len(olines) > 0: # enter this after the first step only 252 | 253 | toremove = lastline.buffer(k) # buffer centerlines the last line 254 | tokeep = [] 255 | for l in ilines: 256 | # loop over all remaining lines and compute their diff 257 | # to the last longest line 258 | diff = l.difference(toremove) 259 | if diff.type == 'MultiLineString': 260 | # Remove the lines that have no head 261 | diff = list(diff.geoms) 262 | for il in diff: 263 | hashead = False 264 | for h in heads: 265 | #if il.intersects(h): # after the smoothing the lines may not finish at the same point "head". 266 | # hashead = True 267 | # diff = il 268 | break 269 | if hashead: 270 | break 271 | else: 272 | diff = None 273 | # keep this head line only if it's long enough 274 | if diff is not None and diff.length > r: 275 | # Fun fact. The heads can be cut by the buffer too 276 | diff = shpg.LineString(l.coords[0:2] + diff.coords[2:]) 277 | tokeep.append(diff) 278 | ilines = tokeep 279 | # it could happen that we're done at this point 280 | if len(ilines) == 0: 281 | break 282 | 283 | # Otherwise keep the longest one and continue 284 | lengths = np.array([]) 285 | for l in ilines: 286 | lengths = np.append(lengths, l.length) 287 | ll = ilines[np.argmax(lengths)] 288 | 289 | ilines.remove(ll) 290 | if len(olines) > 0: 291 | # the cut line's last point is not guaranteed 292 | # to on straight coordinates. Remove it 293 | olines.append(shpg.LineString(np.asarray(ll.xy)[:, 0:-1].T)) 294 | else: 295 | olines.append(ll) 296 | lastline = ll 297 | 298 | # add the corresponding head to each line 299 | for l in olines: 300 | # for h in heads: 301 | # if l.intersects(h): 302 | # oheads.append(h) 303 | # break 304 | oheads.append(l.coords[-1]) # assign first point as head of the centerline 305 | print(len(oheads)) # --> here is the problem!!! there are only 12 heads that remain 306 | # in the same position after the smoothing! I dont know how to fix it. --> recompute heads? 307 | print(len(olines)) 308 | 309 | return olines, oheads 310 | 311 | 312 | def _filter_lines_slope1(lines, heads, topo, gdir, min_slope): 313 | """Filter the centerline candidates by slope: if they go up, remove 314 | Kienholz et al. (2014), Ch. 4.3.1 315 | Parameters 316 | ---------- 317 | lines : list of shapely.geometry.LineString instances 318 | The lines to filter out (in raster coordinates). 319 | topo : the glacier topography 320 | gdir : the glacier directory for simplicity 321 | min_slope: rad 322 | Returns 323 | ------- 324 | (lines, heads) a list of the new lines and corresponding heads 325 | """ 326 | #import params 327 | dx_cls = 2#cfg.PARAMS['flowline_dx'] # probelm with the cfg imported here... idk how to do it 328 | lid = 3#cfg.PARAMS['flowline_junction_pix'] 329 | sw = 1#cfg.PARAMS['flowline_height_smooth'] 330 | 331 | # Bilinear interpolation 332 | # Geometries coordinates are in "pixel centered" convention, i.e 333 | # (0, 0) is also located in the center of the pixel 334 | xy = (np.arange(0, gdir.grid.ny-0.1, 1), 335 | np.arange(0, gdir.grid.nx-0.1, 1)) 336 | interpolator = RegularGridInterpolator(xy, topo) 337 | 338 | olines = [lines[0]] 339 | oheads = [heads[0]] 340 | for line, head in zip(lines[1:], heads[1:]): 341 | 342 | # The code below mimics what initialize_flowlines will do 343 | # this is a bit smelly but necessary 344 | points = line_interpol(line, dx_cls) 345 | 346 | # For tributaries, remove the tail 347 | points = points[0:-lid] 348 | 349 | new_line = shpg.LineString(points) 350 | 351 | # Interpolate heights 352 | x, y = new_line.xy 353 | hgts = interpolator((y, x)) 354 | 355 | # If smoothing, this is the moment 356 | hgts = gaussian_filter1d(hgts, sw) 357 | 358 | # Finally slope 359 | slope = np.arctan(-np.gradient(hgts, dx_cls*gdir.grid.dx)) 360 | 361 | # And altitude range 362 | z_range = np.max(hgts) - np.min(hgts) 363 | # arbitrary threshold with which we filter the lines, otherwise bye bye 364 | if np.sum(slope >= min_slope) >= 5 and z_range > 10: 365 | olines.append(line) 366 | oheads.append(head) 367 | 368 | return olines, oheads 369 | 370 | 371 | def _normalize(n): 372 | """Computes the normals of a vector n. 373 | Returns 374 | ------- 375 | the two normals (n1, n2) 376 | """ 377 | nn = n / np.sqrt(np.sum(n*n)) 378 | n1 = np.array([-nn[1], nn[0]]) 379 | n2 = np.array([nn[1], -nn[0]]) 380 | return n1, n2 381 | 382 | 383 | def _projection_point(centerline, point): 384 | """Projects a point on a line and returns the closest integer point 385 | guaranteed to be on the line, and guaranteed to be far enough from the 386 | head and tail. 387 | Parameters 388 | ---------- 389 | centerline : Centerline instance 390 | point : Shapely Point geometry 391 | Returns 392 | ------- 393 | (flow_point, ind_closest): Shapely Point and indice in the line 394 | """ 395 | prdis = centerline.line.project(point, normalized=False) 396 | ind_closest = np.argmin(np.abs(centerline.dis_on_line - prdis)).item() 397 | flow_point = shpg.Point(centerline.line.coords[int(ind_closest)]) 398 | return flow_point 399 | 400 | def line_interpol(line, dx): 401 | """Interpolates a shapely LineString to a regularly spaced one. 402 | Shapely's interpolate function does not guaranty equally 403 | spaced points in space. This is what this function is for. 404 | We construct new points on the line but at constant distance from the 405 | preceding one. 406 | Parameters 407 | ---------- 408 | line: a shapely.geometry.LineString instance 409 | dx: the spacing 410 | Returns 411 | ------- 412 | a list of equally distanced points 413 | """ 414 | 415 | # First point is easy 416 | points = [line.interpolate(dx / 2.)] 417 | 418 | # Continue as long as line is not finished 419 | while True: 420 | pref = points[-1] 421 | pbs = pref.buffer(dx).boundary.intersection(line) 422 | if pbs.type == 'Point': 423 | pbs = [pbs] 424 | elif pbs.type == 'LineString': 425 | # This is rare 426 | pbs = [shpg.Point(c) for c in pbs.coords] 427 | assert len(pbs) == 2 428 | elif pbs.type == 'GeometryCollection': 429 | # This is rare 430 | opbs = [] 431 | for p in pbs.geoms: 432 | if p.type == 'Point': 433 | opbs.append(p) 434 | elif p.type == 'LineString': 435 | opbs.extend([shpg.Point(c) for c in p.coords]) 436 | pbs = opbs 437 | else: 438 | if pbs.type != 'MultiPoint': 439 | raise RuntimeError('line_interpol: we expect a MultiPoint ' 440 | 'but got a {}.'.format(pbs.type)) 441 | 442 | try: 443 | # Shapely v2 compat 444 | pbs = pbs.geoms 445 | except AttributeError: 446 | pass 447 | 448 | # Out of the point(s) that we get, take the one farthest from the top 449 | refdis = line.project(pref) 450 | tdis = np.array([line.project(pb) for pb in pbs]) 451 | p = np.where(tdis > refdis)[0] 452 | if len(p) == 0: 453 | break 454 | points.append(pbs[int(p[0])]) 455 | 456 | return points 457 | 458 | def gaussian_blur(in_array, size): 459 | """Applies a Gaussian filter to a 2d array. 460 | Parameters 461 | ---------- 462 | in_array : numpy.array 463 | The array to smooth. 464 | size : int 465 | The half size of the smoothing window. 466 | Returns 467 | ------- 468 | a smoothed numpy.array 469 | """ 470 | 471 | # expand in_array to fit edge of kernel 472 | padded_array = np.pad(in_array, size, 'symmetric') 473 | 474 | # build kernel 475 | x, y = np.mgrid[-size:size + 1, -size:size + 1] 476 | g = np.exp(-(x**2 / float(size) + y**2 / float(size))) 477 | g = (g / g.sum()).astype(np.float) # I had to change that 478 | 479 | # do the Gaussian blur 480 | return scipy.signal.fftconvolve(padded_array, g, mode='valid') 481 | 482 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | """ 2 | Francesc Roura Adserias @franra9 from OGGM/core/centerlines.py 3 | ## https://github.com/OGGM/oggm/blob/master/oggm/core/centerlines.py 4 | 5 | Compute glacier centerlines (flow lines) as in here: 6 | https://tc.copernicus.org/articles/8/503/2014/ 7 | """ 8 | import numpy as np 9 | import scipy 10 | import utils as utils 11 | import salem 12 | import shapely.geometry as shpg 13 | import os 14 | import rioxarray as rio 15 | import geopandas as gpd 16 | from shapely.ops import transform as shp_trafo 17 | import shapely.affinity as shpa 18 | 19 | try: 20 | import skimage.draw as skdraw 21 | except ImportError: 22 | pass 23 | from functools import partial 24 | try: 25 | from skimage.graph import route_through_array 26 | except ImportError: 27 | pass 28 | try: 29 | from scipy.signal.windows import gaussian 30 | except AttributeError: 31 | # Old scipy 32 | from scipy.signal import gaussian 33 | # ------------------------ Import functions --------- 34 | from functions import (get_terminus_coord, profile, coordinate_change, 35 | _make_costgrid,_filter_lines,_filter_lines_slope, 36 | gaussian_blur) # TODO: make costgrid has some issue when importing it from OGGM because I dont have the same params.py file\ 37 | #idem for _filter_lines_slope 38 | 39 | from oggm.core.gis import (_polygon_to_pix)#,gaussian_blur) --> problem with oggm blur 40 | from oggm.core.centerlines import (line_order) 41 | from oggm.utils._workflow import _chaikins_corner_cutting 42 | 43 | # load parameters to be used (more info in params.py) 44 | import params 45 | 46 | ############################################################ 47 | # TODO: ? parse params as in OGGM cfg.PARAMS('parameter-name') 48 | import oggm.cfg as cfg 49 | 50 | q1 = params.q1 #cfg.PARAMS['q1'] 51 | q2 = params.q2 # cfg.PARAMS['q2'] 52 | rmax = params.rmax # cfg.PARAMS['rmax'] 53 | localmax_window = params.localmax_window #cfg.PARAMS['localmax_window'] # In units of dx 54 | flowline_dx = params.flowline_dx #cfg.PARAMS['flowline_dx'] 55 | flowline_junction_pix = params.flowline_junction_pix = 1 #cfg.PARAMS['flowline_junction_pix'] 56 | kbuffer = params.kbuffer #cfg.PARAMS['kbuffer'] 57 | min_slope_flowline_filter = params.min_slope_flowline_filter #cfg.PARAMS['slope_flowline_filter'] 58 | filter_min_slope = params.filter_min_slope #cfg.PARAMS['filter_min_slope'] 59 | flowline_height_smooth = params.flowline_height_smooth #cfg.PARAMS['flowline_height_smooth'] 60 | is_first_call = params.is_first_call #cfg.PARAMS['is_first_call'] 61 | plot = params.plot #cfg.PARAMS['plot'] 62 | data_path = params.data_path #cfg.PARAMS['data_path'] 63 | out_path = params.out_path #cfg.PARAMS['out_path'] 64 | dem_file = params.dem_file #cfg.PARAMS['dem_file'] 65 | shape_file = params.shape_file #cfg.PARAMS['shape_file'] 66 | smooth_window = params.smooth_window #cfg.PARAMS['smooth_window'] 67 | single_fl = params.single_fl #cfg.PARAMS['single_fl'] 68 | 69 | ############################################################ 70 | 71 | # # this is a variable used in the centerline class. 72 | # GAUSSIAN_KERNEL = dict() 73 | # for ks in [5, 7, 9]: 74 | # kernel = gaussian(ks, 1) 75 | # GAUSSIAN_KERNEL[ks] = kernel / kernel.sum() 76 | 77 | 78 | # class defined to be able to use some functions from OGGM "as they are". 79 | class glacier_dir(object): 80 | def __init__(self, grid): 81 | self.grid = grid 82 | self.sourceDem = os.path.join( f'{data_path}', f'{dem_file}') 83 | self.sourceOutline = os.path.join( f'{data_path}', f'{shape_file}') 84 | 85 | ################## 86 | # move this to utils: 87 | 88 | def geoline_to_cls(lines): 89 | """ 90 | list of shapely.geometry.lines to be converted to Centerline list 91 | 92 | Parameters 93 | ---------- 94 | lines : list of shapely.geometry.lines 95 | list of shapely.geometry.lines 96 | 97 | Returns 98 | ------- 99 | list of centerlines instances. 100 | 101 | """ 102 | clss = [] 103 | for li in lines: 104 | clss.append(utils.Centerline(li)) 105 | cls = clss 106 | return cls 107 | 108 | 109 | ################################################################ 110 | # read shapefile (glacier outlines) 111 | 112 | def get_centerlines_rgi(dem_path, outline_path): 113 | """ 114 | Given outline and DEM get glacier centerlines. 115 | 116 | Parameters 117 | ---------- 118 | dem_path : str 119 | Gopography. Path to .tif file 120 | outline_path : str 121 | Glacier outlines. Path to shapefile. 122 | 123 | Returns 124 | ------- 125 | None. 126 | 127 | """ 128 | 129 | dem = rio.open_rasterio(dem_path) 130 | 131 | # smooth DEM using gaussian smoothing. 132 | dx = abs(dem.x[0] - dem.x[1]) 133 | gsize = int(np.rint(smooth_window / dx)) 134 | smoothed_dem = gaussian_blur(np.array(dem.values[0]), int(gsize)) 135 | dem.values[0] = smoothed_dem 136 | 137 | crop_extent = gpd.read_file(os.path.join(data_path, shape_file)) 138 | 139 | crop_extent = crop_extent.to_crs(dem.rio.crs) 140 | 141 | # Check that projection is in metre 142 | try: 143 | assert crop_extent.crs.axis_info[0].unit_name == 'metre' 144 | 145 | except Exception: 146 | raise Exception('Projection from input shapefile data is not in meters.') 147 | 148 | # # Check that projection is in metre 149 | # try: 150 | # assert dem.rio.crs.data['units'] == 'm' 151 | 152 | # except Exception: 153 | # raise Exception('Projection from input DEM data is not in meters.') 154 | 155 | # view all glaciers at once: 156 | #if plot: 157 | # crop_extent.plot() 158 | 159 | # Get altitude and pixel info: 160 | # altitude, (xorigin, yorigin, pixelH, pixelW) 161 | data, pix_params = coordinate_change(dem_path) 162 | 163 | # select i-th glacier 164 | crp1 = crop_extent.iloc[[0]] 165 | 166 | # crop the DEM to the outline + a few buffer points. Buffer in meters 167 | dem_clipped = dem.rio.clip(crp1.buffer(20).apply(shpg.mapping), 168 | crop_extent.crs, drop=False) 169 | 170 | #dem=dem_clipped 171 | # get pix paramaters and topography of the new clipped DEM 172 | #dem_clipped.rio.to_raster('.tmp.tif') 173 | #data, pix_params = coordinate_change('.tmp.tif') 174 | 175 | #del dum_data 176 | 177 | # assign some value to outside crop: e.g. -1 (default number is too large) 178 | dem_clipped.values[0][dem_clipped.values[0] < 0 ] = -1 179 | dem_clipped.values[0][dem_clipped.values[0] > 8848 ] = -1 180 | 181 | # Determine heads and tails # 182 | area = crop_extent.geometry[0].area 183 | 184 | 185 | # list of outline X,Y coordinates 186 | points_xy = crop_extent.geometry.exterior[0].coords 187 | 188 | 189 | # Circular outline: if the first element is repeated at the end, 190 | # delete the last one 191 | if points_xy[0] == points_xy[-1]: 192 | points_xy = points_xy[:-1] 193 | 194 | # get profile under glacier outline: distance 195 | # (arbitrary units (?)) - altitude (m) 196 | prof = profile(points_xy, data, pix_params) 197 | 198 | # get terminus coordinates and position (index) in our data 199 | xyterm, ind_term = get_terminus_coord(points_xy, prof[1]) 200 | 201 | zoutline = prof[1] 202 | 203 | # here heads start 204 | # create grid 205 | nx, ny = len(dem_clipped.x), len(dem_clipped.y) 206 | #nx, ny = len(dem.x), len(dem.y) 207 | 208 | ulx, uly, dx, dy = pix_params 209 | 210 | # Initial topmost-leftmost point 211 | # To pixel center coordinates 212 | x0y0 = (ulx + dx/2, uly - dy/2) 213 | 214 | # try the shapefile curent crs for the raster grid 215 | utm_proj = salem.check_crs(crop_extent.crs) 216 | 217 | # build raster grid properties 218 | grid = salem.Grid(proj=utm_proj, nxny=(nx, ny), dxdy=(dx, -dy), x0y0=x0y0) 219 | 220 | # fill gdir class with grid data 221 | gdir = glacier_dir(grid) 222 | 223 | # Size of the half window to use to look for local maxima 224 | maxorder = np.rint(localmax_window/dx) 225 | 226 | # Order: number of points to take at each side. 227 | # minumum 5, maximum = maxorder 228 | maxorder = utils.clip_scalar(maxorder, 5., np.rint((len(zoutline) / 5.))) 229 | heads_idx = scipy.signal.argrelmax(zoutline, mode='wrap', 230 | order=int(maxorder)) 231 | if single_fl or len(heads_idx[0]) <= 1: 232 | # small glaciers with one or less heads: take the absolute max 233 | heads_idx = (np.atleast_1d(np.argmax(zoutline)),) 234 | 235 | # TODO: if is tidewater: 236 | # do something 237 | 238 | # Remove the heads that are too low 239 | zglacier = dem_clipped.values[dem_clipped.values != -1] 240 | head_threshold = np.percentile(zglacier, (1./3.)*100) 241 | _heads_idx = heads_idx[0][np.where(zoutline[heads_idx] > head_threshold)] 242 | 243 | if len(_heads_idx) == 0: 244 | # this is for bad ice caps where the outline is far off in altitude 245 | _heads_idx = [heads_idx[0][np.argmax(zoutline[heads_idx])]] 246 | heads_idx = _heads_idx 247 | 248 | # TODO: undo the loop, do something like this 249 | # headsxx = gpd.GeoDataFrame(points_xy)[0] 250 | # headsyy = gpd.GeoDataFrame(points_x)[1] 251 | 252 | headsx = headsy = np.array(0) 253 | 254 | for j in heads_idx: 255 | headsy = np.append(headsy, points_xy[int(j)][1]) 256 | headsx = np.append(headsx, points_xy[int(j)][0]) 257 | 258 | headsx, headsy = headsx[1:], headsy[1:] 259 | heads_z = zoutline[heads_idx] 260 | 261 | # careful, the coords are in y, x order! 262 | heads = [shpg.Point(x, y) for y, x in zip(headsy, 263 | headsx)] 264 | 265 | # radius for descarting heads 266 | radius = q1 * area + q2 267 | radius = utils.clip_scalar(radius, 0, rmax) 268 | radius /= grid.dx # radius in raster coordinates 269 | # (warning: this is assuming |xd| = |dy|) 270 | 271 | # parameters taken from default OGGM values 272 | # Plus our criteria, quite useful to remove short lines: 273 | radius += flowline_junction_pix * flowline_dx 274 | 275 | # OK. Filter and see. 276 | glacier_poly_hr = crop_extent.geometry[0] 277 | 278 | 279 | 280 | # heads' coordinates and altitude 281 | heads, heads_z = utils._filter_heads(heads, list(heads_z), float(radius), 282 | glacier_poly_hr) 283 | 284 | # after head filtering, refill heads xy coordinates: 285 | headsx = headsy = np.zeros(1) 286 | 287 | for khead in heads: 288 | headsy = np.append(headsy, khead.y) 289 | headsx = np.append(headsx, khead.x) 290 | 291 | headsx, headsy = headsx[1:], headsy[1:] 292 | 293 | # topography on glacier 294 | z = dem_clipped.values[0] 295 | 296 | # Rounded nearest pix 297 | glacier_poly_pix = _polygon_to_pix(glacier_poly_hr) 298 | 299 | tuple2int = partial(np.array, dtype=np.int64) 300 | 301 | # Compute the glacier mask (currently: center pixels + touched) 302 | glacier_mask = np.zeros((ny, nx), dtype=np.uint8) 303 | glacier_ext = np.zeros((ny, nx), dtype=np.uint8) 304 | (x, y) = glacier_poly_pix.exterior.xy 305 | 306 | # transform coordinates to pixels and assign to 1 inside, 0 otherwise 307 | xx, yy = grid.transform(x, y, crs=utm_proj) 308 | 309 | # I have added a np.clip because some errors when regridding data 310 | xx = np.clip(xx, 0, glacier_mask.shape[1] - 1) 311 | yy = np.clip(yy, 0, glacier_mask.shape[0] - 1) 312 | glacier_mask[skdraw.polygon(np.array(yy), np.array(xx))] = 1 #1 inside plygon 313 | 314 | for gint in glacier_poly_pix.interiors: 315 | x, y = tuple2int(gint.xy) 316 | xx, yy = grid.transform(x, y, crs=utm_proj) 317 | xx, yy = np.round(xx), np.round(yy) 318 | xx, yy = xx.astype(int), yy.astype(int) 319 | glacier_mask[skdraw.polygon(yy, xx)] = 0 # inside the nunataks 320 | glacier_mask[yy, xx] = 0 # onto nunatacks boundaries 321 | 322 | x, y = tuple2int(glacier_poly_pix.exterior.xy) 323 | 324 | # project xy to our local (raster) grid 325 | xx, yy = grid.transform(x, y, crs=utm_proj) 326 | xx, yy = np.round(xx), np.round(yy) 327 | xx, yy = xx.astype(int), yy.astype(int) 328 | 329 | # I have added a np.clip because some errors when regridding data 330 | xx = np.clip(xx, 0, glacier_mask.shape[1]-1) 331 | yy = np.clip(yy, 0, glacier_mask.shape[0]-1) 332 | 333 | glacier_mask[yy, xx] = 1 # glacier bundaries belong to the glacier 334 | glacier_ext[yy, xx] = 1 335 | 336 | ############################################################ 337 | # Compute centerlines 338 | # Cost array 339 | costgrid = _make_costgrid(glacier_mask, glacier_ext, z) 340 | 341 | # Terminus 342 | t_coord = xyterm 343 | 344 | # Compute the least cost routes for all possible head-terminus trajectories 345 | lines = [] 346 | heads_pix = [] 347 | count = 0 348 | for h in heads: 349 | try: 350 | h_coord_pix = np.array(grid.transform(h.x, h.y, crs=utm_proj)) 351 | h_coord_pix = np.round(h_coord_pix).astype(int) 352 | t_coord_pix = np.array(grid.transform(t_coord.x, 353 | t_coord.y, crs=utm_proj)) 354 | t_coord_pix = np.round(t_coord_pix).astype(int) 355 | heads_pix.append(shpg.Point(h_coord_pix)) 356 | 357 | indices, _ = route_through_array(costgrid, np.roll(h_coord_pix, 1), 358 | np.roll(t_coord_pix, 1)) 359 | lines.append(shpg.LineString(np.array(indices)[:, [1, 0]])) 360 | except: 361 | print("There is a problem when computing the least-cost route") 362 | # raise Exception("There is a problem when computing 363 | # the least-cost route") 364 | count += 1 365 | #finally: 366 | # print('') 367 | print(str(count) + " centerlines out of " + str(len(heads)) + 368 | " were not able to be computed") 369 | 370 | ############################### 371 | 372 | # Filter the shortest lines out 373 | dx_cls = flowline_dx 374 | radius = flowline_junction_pix * dx_cls 375 | radius += 6 * dx_cls 376 | 377 | ############################### 378 | 379 | # Smooth centerlines 380 | tra_func = partial(gdir.grid.transform, crs=crop_extent.crs) 381 | exterior = shpg.Polygon(shp_trafo(tra_func, crop_extent.geometry[0].exterior)) 382 | #exterior = shpg.Polygon(crop_extent.geometry[0].exterior) 383 | 384 | liness = [] 385 | for j, line in enumerate(lines): 386 | mm = 1 if j == (len(lines)-1) else 0 387 | 388 | ensure_exterior_match = True 389 | if ensure_exterior_match: 390 | # Extend line at the start by 10 391 | fs = shpg.LineString(line.coords[:2]) 392 | # First check if this is necessary - this segment should 393 | # be within the geometry or it's already good to go 394 | if fs.within(exterior): 395 | fs = shpa.scale(fs, xfact=3, yfact=3, origin=fs.boundary.geoms[1]) 396 | line = shpg.LineString([*fs.coords, *line.coords[2:]]) 397 | # If last also extend at the end 398 | if mm == 1: # mm means main 399 | ls = shpg.LineString(line.coords[-2:]) 400 | if ls.within(exterior): 401 | ls = shpa.scale(ls, xfact=3, yfact=3, origin=ls.boundary.geoms[0]) 402 | line = shpg.LineString([*line.coords[:-2], *ls.coords]) 403 | 404 | # Simplify and smooth? 405 | simplify_line = True 406 | if simplify_line: 407 | line = line.simplify(simplify_line) 408 | corner_cutting = True 409 | if corner_cutting: 410 | line = _chaikins_corner_cutting(line, refinements=5) 411 | 412 | # Intersect with exterior geom 413 | line = line.intersection(exterior) 414 | if line.type == 'MultiLineString': 415 | # Take the longest 416 | lens = [il.length for il in line.geoms] 417 | line = line.geoms[np.argmax(lens)] 418 | liness.append(line) 419 | 420 | lines = liness 421 | 422 | # heads in raster coordinates: 423 | olines, oheads = _filter_lines(lines, heads_pix, kbuffer, radius) 424 | 425 | 426 | # Filter the lines which are going up instead of down 427 | min_slope = np.deg2rad(min_slope_flowline_filter) 428 | 429 | if filter_min_slope: # this kills most of the branches! --> too many killed! 430 | topo = z 431 | olines, oheads = _filter_lines_slope(olines, oheads, topo, 432 | gdir, min_slope) 433 | # tmp: convert to cls 434 | olines = geoline_to_cls(olines) 435 | # 436 | # Adds the line level 437 | for cl in olines: 438 | cl.order = line_order(cl) 439 | 440 | # And sort them per order !!! several downstream tasks rely on this 441 | cls = [] 442 | for k in np.argsort([cl.order for cl in olines]): 443 | cls.append(olines[k]) 444 | 445 | ##### 446 | cls_xy = [] 447 | for li in cls: 448 | # ij_to_xy 449 | ii = np.array(li.line.xy[0]) 450 | jj = np.array(li.line.xy[1]) 451 | x = ulx + ii * dx + 0.5 * dx 452 | y = uly - jj * dy - 0.5 * dy 453 | 454 | xy = np.zeros((len(x), 2)) 455 | 456 | xy[:, 0] = x 457 | xy[:, 1] = y 458 | 459 | lxy = shpg.LineString(xy) 460 | 461 | cls_xy.append(lxy) 462 | 463 | #Convert to multiline 464 | if len(cls_xy) > 1: 465 | cls_xy = shpg.MultiLineString(cls_xy) 466 | 467 | # convert to geopandas datagrame to save afterwards 468 | cls_xy_gpd = gpd.GeoDataFrame(cls_xy) 469 | cls_xy_gpd['geometry'] = cls_xy_gpd[0]; del cls_xy_gpd[0] 470 | 471 | # add some metadata 472 | cls_xy_gpd['src_files'] = os.path.join(f'{gdir.sourceDem}',f'{gdir.sourceOutline}') 473 | 474 | if single_fl: 475 | fileout = 'main_flowline' 476 | else: 477 | fileout = 'flowlines' 478 | 479 | # save 480 | cls_xy_gpd.to_file(os.path.join(f"{out_path}", f"{fileout}.shp")) 481 | 482 | return None 483 | ############################################################## 484 | # END # 485 | 486 | dem_path = os.path.join(data_path, dem_file) 487 | outline_path = os.path.join(data_path, shape_file) 488 | 489 | get_centerlines_rgi(dem_path, outline_path) -------------------------------------------------------------------------------- /params.py: -------------------------------------------------------------------------------- 1 | # Configuration file for OGGM parameters 2 | 3 | ### not sure: add description 4 | is_first_call = False 5 | 6 | # declare general paths 7 | out_path = "/home/francesc/results/glacier_centerlines/" 8 | 9 | # Baltoro test data 10 | data_path = "./test_data/" 11 | dem_file = "dem_balto_tm.tif" 12 | shape_file = "outlines.shp" 13 | 14 | # Norway test data 15 | #dem_file = "Norway_DEM_sel.tif" 16 | #data_path = "/home/francesc/data/glacier_centerlines/" 17 | #shape_file = "Norway_Inventory_sel/Norway_Inventory_sel.shp" 18 | 19 | # RGI11 20 | #data_path = "/home/francesc/data/OGGM/rgi/RGIV60/11_rgi60_CentralEurope/" 21 | #dem_file = 'dummy'#'RGI60-11.00/RGI60-11.00002/NASADEM/dem.tif' 22 | #shape_file = "11_rgi60_CentralEurope.shp" 23 | 24 | #data_path = "/home/francesc/repositories/glacier_centerlines/test_data/" 25 | #dem_file = 'RGI60-11.00/RGI60-11.00002/NASADEM/dem.tif' 26 | #shape_file = "11_rgi60_CentralEurope.shp" 27 | 28 | 29 | ### Input/Output paths. Set to ~ to default to home directory 30 | 31 | # Where OGGM will write its output 32 | #working_dir = 33 | 34 | # Users can specify their own topography file if they want to. 35 | # This is useful for testing, or if you 36 | # are simulating a single region with better data. 37 | # the empty default is what most users should do 38 | #dem_file = 39 | 40 | # Use compression for the intermediate pickles? (might slow down I/O a bit) 41 | # Both the performance loss (0% ?) and the space gain (-10%) seem to be low 42 | use_compression = True 43 | 44 | ### CENTERLINE determination 45 | 46 | # Decision on grid spatial resolution for each glacier 47 | # 'fixed': dx (meters) = fixed_dx 48 | # 'linear': dx (meters) = d1 * AREA (km) + d2 ; clipped to dmax (e.g.: 5, 10, 200) 49 | # 'square': dx (meters) = d1 * sqrt(AREA) (km) + d2 ; clipped to dmax (e.g.: 20, 10, 200) 50 | 51 | # Was default for a long time 52 | # grid_dx_method = 'linear' 53 | # d1 = 5. 54 | # d2 = 10. 55 | # dmax = 100. 56 | 57 | # New default? 58 | grid_dx_method = 'square' 59 | d1 = 14. 60 | d2 = 10. 61 | dmax = 200. 62 | 63 | # Ignored if grid_dx_method != 'fixed' 64 | fixed_dx = 50. 65 | 66 | # Which algorithm to use for interpolating the topography to the local grid 67 | # 'bilinear' or 'cubic' 68 | topo_interp = 'cubic' 69 | 70 | # Grid border buffer around the glacier (in pixels) 71 | # Make it large if you want to do past simulations. 72 | border = 40 73 | 74 | # For tidewater glaciers it doesn't make sense to have large maps 75 | # if for some reason you still want this, set to false 76 | clip_tidewater_border = True 77 | 78 | # The glacier area, CenLon and CenLat are usually taken from the RGI 79 | # shapefile, which is a good thing for default RGI files. If you use your 80 | # own inventory, however, it might be a good idea to let OGGM compute these 81 | # attributes at runtime: set to `False` in this case. 82 | use_rgi_area = True 83 | 84 | # Head determination: (approx) size in meters of the half-size window 85 | # where to look for maxima 86 | localmax_window = 500. #In units of dx 87 | 88 | # DEM smoothing: (approx) size in meters of the smoothing window. 89 | # Set to 0 for no smoothing 90 | smooth_window = 251. 91 | 92 | 93 | # Kienholz et al eq (1) 94 | q1 = 2/10**6 # 1/m 95 | q2 = 500 #m 96 | rmax = 1000 #m 97 | 98 | q1 = 2e-6 99 | q2 = 500. 100 | rmax = 1000. 101 | 102 | # Kienholz et al eq (2) --> it is hardcoded in _make_costgrid() function! 103 | f1 = 1000. 104 | f2 = 3000. 105 | a = 4.25 #4.25 in literature 106 | b = 3.7 107 | 108 | # Kienholz et al eq (8) but modified here 109 | # Buffer in pixels where to cut the incoming centerlines 110 | kbuffer = 1 111 | 112 | 113 | # For water-terminating glaciers, use the percentile instead of minimum h? 114 | # Set to zero if no special treatment for water terminating glaciers should be 115 | # used, and to an integer > 0 to specify the percentile 116 | terminus_search_percentile = 10 117 | terminus_search_altitude_range = 20 118 | 119 | ### FLOWLINES definition parameters 120 | # Whether the model should use the glacier intersects information 121 | # given by the user 122 | use_intersects = True 123 | # Grid spacing of a flowline in pixel coordinates 124 | flowline_dx = 2 125 | 126 | # Number of pixels to arbitrarily remove at junctions 127 | flowline_junction_pix = 3 128 | # Gaussian smooth of the altitude along a flowline 129 | # sigma, in pixel coordinates (sigma=1 -> smooth around a -4:+4 window) 130 | flowline_height_smooth = 1 131 | 132 | # Prevent too small slopes? (see also min_slope param below) 133 | filter_min_slope = True 134 | 135 | ### Elevation band flowlines (or "collapsed flowlines") parameters 136 | # Only used if using the alternative flowline definition 137 | # The elevation binsize in m - it was 10m in Huss&Farinotti2012, 30m in Werder 2019 138 | elevation_band_flowline_binsize = 30 139 | 140 | ### CATCHMENT WIDTHS computation parameters 141 | # altitude range threshold for filtering 142 | # This stuff has not been really optimized, it's also not very critical 143 | width_alt_range_thres = 250. 144 | # Minimum number of elements per bin for altitude-binsize definition 145 | min_n_per_bin = 2 146 | # Baseline binsize for the altitude-area distribution 147 | base_binsize = 50. 148 | # Smoothing of the widths after altitude-area matching? 0 means no smoothing, 149 | # 1 means default (i.e. kernel size 9). 150 | smooth_widths_window_size = 1 151 | 152 | ### INVERSION params 153 | # Clip the flowline slope, in degrees 154 | # This will crop the slope during the ice thickness inversion. 155 | # This is quite a sensitive parameter! 156 | min_slope = 1.5 157 | min_slope_ice_caps = 1.5 158 | # When converting the centerlines to flowlines, we prevent negative slopes. 159 | # Ideally, this value should be set to `min_slope` for physical consistency, 160 | # but it turns that many flat glaciers will have weird flowlines with this 161 | # setting. Using zero works ok, and was the default in OGGM for long 162 | min_slope_flowline_filter = 0 163 | 164 | ### File output options 165 | 166 | # Whether to store the model geometry files during operational runs 167 | # This can be useful for advanced applications needing the retrieval 168 | # of glacier geometries after the run, but is not necessary if you 169 | # are interested in diagnostics only (volume, length, etc.) 170 | store_model_geometry = False 171 | 172 | # Single flow: if True, a single, principal flowline is computed. 173 | # If False, tributaries are also computed. 174 | single_fl = False 175 | 176 | ### Plots 177 | # Produce plots in the terminal? True = y; False = n 178 | plot = False -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name='glacier_centerlines', 5 | version='0.5.3', 6 | description='Compute glacier centerlines using RGI data and a well known algorithm (see documentetion)', 7 | author='Francesc Roura Adserias @OGGM developpers', 8 | author_email='info@oggm.org', 9 | packages=['glacier_centerlines'], 10 | install_requires=[ 11 | 'numpy==1.22.0', 12 | 'scipy=1.7.3', 13 | 'salem', 14 | 'shapely==1.8.0', 15 | 'rioxarray==0.9.1', 16 | 'geopandas==0.10.2', 17 | # 'skimage', 18 | 'scikit-image', 19 | 'oggm==1.5.3', 20 | 'gdal'#osgeo is not accepted' 21 | # 'functools' 22 | ], 23 | ) 24 | -------------------------------------------------------------------------------- /snippet_run_rgi_centerlines.py: -------------------------------------------------------------------------------- 1 | #code snippet to try glaciercenterlines_rgi centerlines, that is now pip installable (pip install glacier_centelrines) 2 | 3 | #code available in https://github.com/GLIMS-RGI/glacier_centerlines/tree/dev-entity_task 4 | 5 | #load libraries and oggm dependencies 6 | import os 7 | from oggm import cfg, utils 8 | cfg.initialize(logging_level='WARNING') 9 | from oggm import workflow 10 | 11 | #set tmpdir 12 | cfg.PATHS['working_dir'] = utils.gettempdir(dirname='OGGM-new-cls', reset=False) 13 | 14 | # try hintereisferner, our beloved refference glacier in Tirol: 15 | rgi_ids = ['RGI60-11.00897'] 16 | 17 | # bese url: 18 | base_url = ('https://cluster.klima.uni-bremen.de/~oggm/gdirs/oggm_v1.6/L3-L5_files/centerlines/w5e5/qc0/pcpwin/match_geod_pergla/') 19 | 20 | # Initialize glacier directories 21 | gdirs = workflow.init_glacier_directories(rgi_ids, from_prepro_level=3, prepro_border=80, prepro_base_url=base_url) 22 | 23 | #import general execution for oggm taks 24 | from oggm.workflow import execute_entity_task 25 | 26 | #new package where to take the task from: 27 | import glacier_centerlines as gc 28 | 29 | # run 30 | execute_entity_task(gc.centerlines_rgi.compute_centerlines_rgi, gdirs) 31 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | """Some useful functions that did not fit into the other modules. 2 | """ 3 | import shapely 4 | import shapely.geometry as shpg 5 | import numpy as np 6 | import copy 7 | from functools import (wraps) 8 | 9 | 10 | def nicenumber(number, binsize, lower=False): 11 | """Returns the next higher or lower "nice number", given by binsize. 12 | Examples: 13 | --------- 14 | >>> nicenumber(12, 10) 15 | 20 16 | >>> nicenumber(19, 50) 17 | 50 18 | >>> nicenumber(51, 50) 19 | 100 20 | >>> nicenumber(51, 50, lower=True) 21 | 50 22 | """ 23 | 24 | e, _ = divmod(number, binsize) 25 | if lower: 26 | return e * binsize 27 | else: 28 | return (e + 1) * binsize 29 | 30 | 31 | def clip_scalar(value, vmin, vmax): 32 | """A faster numpy.clip ON SCALARS ONLY. 33 | See https://github.com/numpy/numpy/issues/14281 34 | """ 35 | return vmin if value < vmin else vmax if value > vmax else value 36 | 37 | 38 | def _filter_heads(heads, heads_height, radius, polygon): 39 | """Filter the head candidates following Kienholz et al. (2014), Ch. 4.1.2 40 | Parameters 41 | ---------- 42 | heads : list of shapely.geometry.Point instances 43 | The heads to filter out (in raster coordinates). 44 | heads_height : list 45 | The heads altitudes. 46 | radius : float 47 | The radius around each head to search for potential challengers 48 | polygon : shapely.geometry.Polygon class instance 49 | The glacier geometry (in raster coordinates). 50 | Returns 51 | ------- 52 | a list of shapely.geometry.Point instances with the "bad ones" removed 53 | """ 54 | 55 | heads = copy.copy(heads) 56 | heads_height = copy.copy(heads_height) 57 | 58 | i = 0 59 | # I think a "while" here is ok: we remove the heads forwards only 60 | while i < len(heads): 61 | head = heads[i] 62 | pbuffer = head.buffer(radius) 63 | inter_poly = pbuffer.intersection(polygon.exterior) 64 | if inter_poly.type in ['MultiPolygon', 65 | 'GeometryCollection', 66 | 'MultiLineString']: 67 | # In the case of a junction point, we have to do a check 68 | # http://lists.gispython.org/pipermail/community/ 69 | # 2015-January/003357.html 70 | if inter_poly.type == 'MultiLineString': 71 | inter_poly = shapely.ops.linemerge(inter_poly) 72 | 73 | if inter_poly.type != 'LineString': 74 | # keep the local polygon only 75 | for sub_poly in inter_poly.geoms: 76 | if sub_poly.intersects(head): 77 | inter_poly = sub_poly 78 | break 79 | elif inter_poly.type == 'LineString': # i have in treoduced "tuple()" 80 | inter_poly = shpg.Polygon(tuple(np.asarray(inter_poly.xy).T)) 81 | elif inter_poly.type == 'Polygon': 82 | pass 83 | else: 84 | extext = ('Geometry collection not expected: ' 85 | '{}'.format(inter_poly.type)) 86 | # raise InvalidGeometryError(extext) 87 | 88 | # Find other points in radius and in polygon 89 | _heads = [head] 90 | _z = [heads_height[i]] 91 | for op, z in zip(heads[i+1:], heads_height[i+1:]): 92 | if inter_poly.intersects(op): 93 | _heads.append(op) 94 | _z.append(z) 95 | 96 | # If alone, go to the next point 97 | if len(_heads) == 1: 98 | i += 1 99 | continue 100 | 101 | # If not, keep the highest 102 | _w = np.argmax(_z) 103 | 104 | for head in _heads: 105 | if not (head is _heads[_w]): 106 | heads_height = np.delete(heads_height, heads.index(head)) 107 | heads.remove(head) 108 | 109 | return heads, heads_height 110 | 111 | 112 | def find_nearest(array, value): 113 | array = np. asarray(array) 114 | idx = (np. abs(array - value)).argmin() 115 | return array[idx] 116 | 117 | 118 | class glacier_dir(object): 119 | def __init__(self, grid): 120 | self.grid = grid 121 | 122 | 123 | class grid_inf(object): 124 | def __init__(self, grid): 125 | self.grid = grid 126 | 127 | 128 | def lazy_property(fn): 129 | """Decorator that makes a property lazy-evaluated.""" 130 | 131 | attr_name = '_lazy_' + fn.__name__ 132 | 133 | @property 134 | @wraps(fn) 135 | def _lazy_property(self): 136 | if not hasattr(self, attr_name): 137 | setattr(self, attr_name, fn(self)) 138 | return getattr(self, attr_name) 139 | 140 | return _lazy_property 141 | 142 | 143 | class SuperclassMeta(type): 144 | """Metaclass for abstract base classes. 145 | http://stackoverflow.com/questions/40508492/python-sphinx-inherit- 146 | method-documentation-from-superclass 147 | """ 148 | def __new__(mcls, classname, bases, cls_dict): 149 | cls = super().__new__(mcls, classname, bases, cls_dict) 150 | for name, member in cls_dict.items(): 151 | if not getattr(member, '__doc__'): 152 | try: 153 | member.__doc__ = getattr(bases[-1], name).__doc__ 154 | except AttributeError: 155 | pass 156 | return cls 157 | 158 | ################################################################## 159 | # Light version of Centerline class from OGGM 160 | class Centerline(object, metaclass=SuperclassMeta): 161 | """Geometry (line and widths) and flow rooting properties, but no thickness 162 | """ 163 | 164 | def __init__(self, line, dx=None, surface_h=None, orig_head=None, 165 | rgi_id=None, map_dx=None): 166 | """ Initialize a Centerline 167 | Parameters 168 | ---------- 169 | line : :py:class:`shapely.geometry.LineString` 170 | The geometrically calculated centerline 171 | dx : float 172 | Grid spacing of the initialised flowline in pixel coordinates 173 | surface_h : :py:class:`numpy.ndarray` 174 | elevation [m] of the points on ``line`` 175 | orig_head : :py:class:`shapely.geometry.Point` 176 | geometric point of the lines head 177 | rgi_id : str 178 | The glacier's RGI identifier 179 | map_dx : float 180 | the map's grid resolution. Centerline.dx_meter = dx * map_dx 181 | """ 182 | 183 | self.line = None # Shapely LineString 184 | self.head = None # Shapely Point 185 | self.tail = None # Shapely Point 186 | self.dis_on_line = None 187 | self.nx = None 188 | if line is not None: 189 | self.set_line(line) # Init all previous properties 190 | else: 191 | self.nx = len(surface_h) 192 | self.dis_on_line = np.arange(self.nx) * dx 193 | 194 | self.order = None # Hydrological flow level (~ Strahler number) 195 | 196 | # These are computed at run time by compute_centerlines 197 | self.flows_to = None # pointer to a Centerline object (when available) 198 | self.flows_to_point = None # point of the junction in flows_to 199 | self.inflows = [] # list of Centerline instances (when available) 200 | self.inflow_points = [] # junction points 201 | 202 | # Optional attrs 203 | self.dx = dx # dx in pixels (assumes the line is on constant dx 204 | self.map_dx = map_dx # the pixel spacing 205 | try: 206 | self.dx_meter = self.dx * self.map_dx 207 | except TypeError: 208 | # For backwards compatibility we allow this for now 209 | self.dx_meter = None 210 | self._surface_h = surface_h 211 | self._widths = None 212 | self.is_rectangular = None 213 | self.is_trapezoid = None 214 | self.orig_head = orig_head # Useful for debugging and for filtering 215 | self.geometrical_widths = None # these are kept for plotting and such 216 | self.apparent_mb = None # Apparent MB, NOT weighted by width. 217 | self.mu_star = None # the mu* associated with the apparent mb 218 | self.mu_star_is_valid = False # if mu* leeds to good flux, keep it 219 | self.flux = None # Flux (kg m-2) 220 | self.flux_needs_correction = False # whether this branch was baaad 221 | self.rgi_id = rgi_id # Useful if line is used with another glacier 222 | 223 | 224 | def set_line(self, line): 225 | """Update the Shapely LineString coordinate. 226 | Parameters 227 | ---------- 228 | line : :py:class`shapely.geometry.LineString` 229 | """ 230 | 231 | self.nx = len(line.coords) 232 | self.line = line 233 | dis = [line.project(shpg.Point(co)) for co in line.coords] 234 | self.dis_on_line = np.array(dis) 235 | xx, yy = line.xy 236 | self.head = shpg.Point(xx[0], yy[0]) 237 | self.tail = shpg.Point(xx[-1], yy[-1]) 238 | 239 | 240 | # A faster numpy.clip when only one value is clipped (here: min). 241 | clip_min = np.core.umath.maximum 242 | 243 | # A faster numpy.clip when only one value is clipped (here: max). 244 | clip_max = np.core.umath.minimum 245 | 246 | def cls_to_geoline(cls): 247 | """ 248 | list of Centerline object to list of shapely.geometry.lines 249 | 250 | Parameters 251 | ---------- 252 | cls : list of centerlines 253 | list of centerlines instances. 254 | 255 | Returns 256 | ------- 257 | list of shapely.geometry.lines 258 | 259 | """ 260 | liness = [] 261 | for cl in cls: 262 | liness.append(cl.line) 263 | lines = liness 264 | return lines 265 | 266 | def geoline_to_cls(lines): 267 | """ 268 | list of shapely.geometry.lines to be converted to Centerline list 269 | 270 | Parameters 271 | ---------- 272 | lines : list of shapely.geometry.lines 273 | list of shapely.geometry.lines 274 | 275 | Returns 276 | ------- 277 | list of centerlines instances. 278 | 279 | """ 280 | clss = [] 281 | for li in lines: 282 | clss.append(Centerline(li)) 283 | cls = clss 284 | return cls --------------------------------------------------------------------------------