<jupyter_start><jupyter_text>Part 3: Other options for inputUmami is a package for calculating metrics for use with for Earth surface dynamics models. This notebook is the final notebook in a three-part introduction to using umami.Umami was designed to work well with the [terrainbento](https://terrainbento.readthedocs.io/en/latest/) model package, as well as other models built using the [Landlab Toolkit](https://github.com/landlab/landlab). However, umami can be used with models built with other modeling tools and data in a variety of formats. This notebook is meant to demonstrate this capability.  Scope of this tutorialIn this tutorial you will learn how to use other input options along with umami. Specifically we will use square gridded terrain stored in [ESRI ASCII](http://resources.esri.com/help/9.3/arcgisengine/java/GP_ToolRef/spatial_analyst_tools/esri_ascii_raster_format.htm) format. We will read this in as a numpy array. We will also interpolate it to an irregular grid.If you have comments or questions about the notebooks, the best place to get help is through [GitHub Issues](https://github.com/TerrainBento/umami/issues).To begin this example, we will import the required python packages.<jupyter_code>import warnings
warnings.filterwarnings('ignore')

from io import BytesIO, StringIO

import numpy as np
from scipy.interpolate import RegularGridInterpolator
import matplotlib.pylab as plt
from urllib.request import urlopen
from urllib.error import URLError

import rasterio

from landlab import imshow_grid, RasterModelGrid, HexModelGrid

from umami import Metric<jupyter_output><empty_output><jupyter_text>Umami does not make any requirements regarding where terrain data comes from or what model or modeling package is used to construct modeled terrain. However, umami does require that modeled or observed terrain is provided to it as a Landlab grid with an at-node field called `topographic__elevation`. Using the Landlab model grid datastructure means that umami knows how large each grid cell is, and how they are connected. Landlab has five model grid classes. One of them will probably suit your needs. - [`RasterModelGrid`](https://landlab.readthedocs.io/en/release/landlab.grid.raster.htmlraster)- [`HexModelGrid`](https://landlab.readthedocs.io/en/release/landlab.grid.hex.htmlhex)- [`RadialModelGrid`](https://landlab.readthedocs.io/en/release/landlab.grid.radial.htmlradial)- [`VoronoiDelaunayGrid`](https://landlab.readthedocs.io/en/release/landlab.grid.voronoi.htmlvoronoi)- [`NetworkModelGrid`](https://landlab.readthedocs.io/en/release/landlab.grid.network.htmlnetwork) In this example we will use the `RasterModelGrid` for regularly spaced square grid cells and the `VoronoiDelaunayGrid` for irregularly spaced observations. If you can read your topography into python as a numpy array, you can put it on a Landlab grid called `topographic__elevation` and use it with umami. While umami is strict about use of the Landlab grid, this quality of the grid makes it very flexible. You can use a Landlab function such as [`read_esri_ascii`](https://landlab.readthedocs.io/en/release/landlab.io.esri_ascii.htmllandlab.io.esri_ascii.read_esri_ascii) or [`read_netcdf`](https://landlab.readthedocs.io/en/release/landlab.io.netcdf.htmllandlab.io.netcdf.read.read_netcdf) to read your data into a numpy array. You can also create a synthetic one, or use some other package to read a file into python. The world is your oyster. In this case we will use the [rasterio](https://rasterio.readthedocs.io/en/stable/) package to read an [ESRI ASCII](http://resources.esri.com/help/9.3/arcgisengine/java/GP_ToolRef/spatial_analyst_tools/esri_ascii_raster_format.htm) format file that we will download from the [OpenTopography rest server](https://opentopography.org/developers). Step 1: Read in a numpy arrayFirst, we download a small patch of land near Boulder, CO. You can change the values of `north`, `south`, `east` and `west` to change the location. Its not hard to download a very large file, so increase values carefully. These data are provided with horizontal units of degrees. For this example we will not convert from degrees to meters, or address the issue of changing from a geographic coordinate system (WGS84) to a projected one (e.g, UTM Zone 13 N, for Colorado). These are things you should address if you are using this sort of data in an application or research project. The code is wrapped in a `try`-`except` block because if it takes a very long time to get a response from OpenTopography (which sometimes happens on Binder), or if you don't have internet, we want you to still be able to do the tutorial. In this case, you will use some data pre-loaded into the file "topo_data.asc".<jupyter_code>try:
    west = -105.4  # longitude (degrees)
    east = -105.15  # longitude (degrees)
    north = 40.1  # latitude (degrees)
    south = 39.9  # latitue (degrees)

    URL = "http://opentopo.sdsc.edu/otr/getdem?demtype=SRTMGL3&"
    url = (URL + "west=" + str(west) + "&" + "south=" + str(south) + "&"
           "east=" + str(east) + "&"
           "north=" + str(north) + "&"
           "outputFormat=AAIGrid")

    f = urlopen(url)
    file_like = BytesIO(f.read())
    print("URL Sucess: Using data from OpenTopography.")
except URLError:
    print("URL Timed out, using pre-saved file.")
    file_like = "topo_data.asc"<jupyter_output><empty_output><jupyter_text>We now have a variable in our python workspace called `file_like`. We can think of it like a python object that will behave like like an ESRI ASCII file on disk.Next we read `file_like` in with the rasterio package and grab important characteristics like the number of rows (`nrows`), number of columns (`ncols`), the resolution of each pixel (`dx, dy`), the coordinates of the lower left corner (`xy_lower_left`), and the actual elevation data (`elevations`).<jupyter_code>with rasterio.open(file_like) as dataset:
    nrows = dataset.height
    ncols = dataset.width
    dx, dy = dataset.res
    xy_lower_left = (dataset.bounds.left, dataset.bounds.bottom)
    elevations = dataset.read(1)<jupyter_output><empty_output><jupyter_text>We can plot it. As expected, it looks like the topography near Boulder, CO.<jupyter_code>plt.imshow(elevations, cmap="terrain", origin="lower")<jupyter_output><empty_output><jupyter_text>Step 2: Create a `RasterModelGrid` to give to UmamiNext we create a Landlab model grid by passing the information we got from the rasterio dataset to `RasterModelGrid`. We add the field using the name umami requires, `topographic__elevation`. One tricky step here is that the variable elevations is of datatype `int32`.<jupyter_code>elevations.dtype<jupyter_output><empty_output><jupyter_text>Some of the underlying tools that umami uses assume that this field is of type `float`. So when we provide the field `topographic__elevation` to the grid, we will specify the that it should be as type `float`.<jupyter_code>rmg = RasterModelGrid((nrows, ncols),
                       xy_spacing=(dx, dy),
                       xy_of_lower_left=xy_lower_left)

z = rmg.add_field("topographic__elevation", elevations.astype(float))<jupyter_output><empty_output><jupyter_text>If we use the Landlab function [`imshow_grid`](https://landlab.readthedocs.io/en/release/landlab.plot.htmllandlab.plot.imshow.imshow_grid) we see that the topography is correctly represented by the grid.<jupyter_code>imshow_grid(rmg, "topographic__elevation", cmap="terrain")<jupyter_output><empty_output><jupyter_text>Now we make our Metric using the same settings we used in [Part 1](IntroductionToMetric.ipynb).<jupyter_code>metrics = {
    "me": {
        "_func": "aggregate",
        "method": "mean",
        "field": "topographic__elevation"
    },
    "ep10": {
        "_func": "aggregate",
        "method": "percentile",
        "field": "topographic__elevation",
        "q": 10
    }
}

rmg_metric = Metric(rmg, metrics=metrics)
rmg_metric.calculate()
rmg_metric.names
rmg_metric.values<jupyter_output><empty_output><jupyter_text>Step 3: Use irregular data and a `HexModelGrid`As a final example, we will look at specifying umami with an irregular grid. We won't import any standard format of irregular data but will create some by interpolating the regular data using the scipy tool [RegularGridInterpolator](https://docs.scipy.org/doc/scipy-0.16.0/reference/generated/scipy.interpolate.RegularGridInterpolator.html).We use a smaller number of nodes as we had in the prior example. This is just for speed, feel free to adjust the value for `factor` to change this. We start by creating a set of grid node locations in x and y.<jupyter_code>factor = 5
dx = rmg.spacing[0] * factor

hmg = HexModelGrid((int(rmg.shape[0]/factor*1.2), int(rmg.shape[1]/factor)+1), 
                   dx, 
                   node_layout="rect", 
                   xy_of_lower_left=rmg.xy_of_lower_left)<jupyter_output><empty_output><jupyter_text>We can plot them in comparison with our regular grid nodes. There are a lot of nodes, so we will zoom into a corner of the plot.<jupyter_code>plt.plot(rmg.x_of_node, rmg.y_of_node, 'k.',  markersize=2, label="Raster Points")
plt.plot(hmg.x_of_node, hmg.y_of_node, 'm.', label="Irregular Points")
plt.xlim(-105.40, -105.375)
plt.ylim(40.00, 40.025)<jupyter_output><empty_output><jupyter_text>Next we create an interpolation object and interpolate to find the elevation values at our new randomly located set of model grid nodes based on the regular grid.<jupyter_code>interp_obj = RegularGridInterpolator((rmg.y_of_node.reshape(rmg.shape)[:, 0], 
                                      rmg.x_of_node.reshape(rmg.shape)[0, :]),
                                     z.reshape(rmg.shape), bounds_error=False, fill_value=None)

interp_z = interp_obj((hmg.y_of_node, hmg.x_of_node))<jupyter_output><empty_output><jupyter_text>Next we create a `HexModelGrid` and add `topographic__elevation` to it. One nice feature of the `imshow_grid` function is that it works for both regular and irregular grids.<jupyter_code>z = hmg.add_field("topographic__elevation", interp_z, at="node")

imshow_grid(hmg, z, cmap="terrain")<jupyter_output><empty_output><jupyter_text>As expected we see a slightly smoothed version of our original topography. This is expected because we decreased the number of model grid nodes by a factor of 10. The final step is to create a `Metric` and calculate values.<jupyter_code>metrics = {
    "me": {
        "_func": "aggregate",
        "method": "mean",
        "field": "topographic__elevation"
    },
    "ep10": {
        "_func": "aggregate",
        "method": "percentile",
        "field": "topographic__elevation",
        "q": 10
    }
}

hmg_metric = Metric(hmg, metrics=metrics)
hmg_metric.calculate()
hmg_metric.names
hmg_metric.values<jupyter_output><empty_output><jupyter_text>Comparing the metric values for the two grids, we can see that the mean is slightly different in absolute value but very close based on percent change and the 10th percentile is identical.<jupyter_code>for n in hmg_metric.names:
    abs_change = np.abs(hmg_metric.value(n) - rmg_metric.value(n))
    pct_change =  abs_change /( (hmg_metric.value(n) + rmg_metric.value(n))/2)
    print(n, "\n  abs_change: ", abs_change, "\n  pct_change: ", pct_change)<jupyter_output><empty_output>
StarcoderdataJupyterStructuredCleanDedup