landlab

Source code for landlab.grid.raster_aspect

#! /usr/bin/env python
"""Calculate slope aspects on a :any:`RasterModelGrid`."""
import numpy as np
from landlab.utils.decorators import deprecated


def _one_line_slopes(input_array, grid, vals):
    node = input_array[0]
    diagonals = input_array[5:]
    neighbors = input_array[1:5]

    if not grid.status_at_node[node] == 0:
        raise IndexError('One or more of the provided nodes was closed!')

    try:
        slope_we = (
            (vals[diagonals[1]] + 2. * vals[neighbors[2]] +
             vals[diagonals[2]]) - (vals[diagonals[0]] +
                                    2. * vals[neighbors[0]] +
                                    vals[diagonals[3]])
        ) / (8. * grid.dx)
        slope_sn = (
            (vals[diagonals[2]] + 2. * vals[neighbors[3]] +
             vals[diagonals[3]]) - (vals[diagonals[1]] +
                                    2. * vals[neighbors[:, 1]] +
                                    vals[diagonals[0]])
        ) / (8. * grid.dy)
        return slope_we, slope_sn
    except IndexError:
        C = vals[node]
        weighting_verticals = 4.
        weighting_horizontals = 4.
        try:
            vertical_grad = (vals[neighbors[3]] -
                             vals[neighbors[1]]) / (2. * grid.dy)
        except IndexError:
            try:
                vertical_grad = (C - vals[neighbors[1]]) / grid.dy
            except IndexError:
                try:
                    vertical_grad = (vals[neighbors[3]] - C) / grid.dy
                except IndexError:
                    vertical_grad = 0.
                    weighting_verticals -= 2.
        try:
            horizontal_grad = (
                (vals[neighbors[2]] - vals[neighbors[0]]) /
                (2. * grid.dx))
        except IndexError:
            try:
                horizontal_grad = (C - vals[neighbors[0]]) / grid.dx
            except IndexError:
                try:
                    horizontal_grad = (
                        vals[neighbors[2]] - C) / grid.dx
                except IndexError:
                    horizontal_grad = 0.
                    weighting_horizontals -= 2.
        try:
            left_grad = (vals[diagonals[2]] -
                         vals[diagonals[1]]) / (2. * grid.dx)
        except IndexError:
            try:
                C = vals[neighbors[2]]
            except:
                left_grad = 0.
                weighting_verticals -= 1.
            else:
                try:
                    left_grad = (C - vals[diagonals[1]]) / grid.dx
                except IndexError:
                    left_grad = (vals[diagonals[2]] - C) / grid.dx
        try:
            right_grad = (vals[diagonals[3]] -
                          vals[diagonals[0]]) / (2. * grid.dx)
        except IndexError:
            try:
                C = vals[neighbors[0]]
            except:
                right_grad = 0.
                weighting_verticals -= 1.
            else:
                try:
                    right_grad = (C - vals[diagonals[0]]) / grid.dx
                except IndexError:
                    right_grad = (vals[diagonals[3]] - C) / grid.dx
        try:
            top_grad = (vals[diagonals[1]] -
                        vals[diagonals[0]]) / (2. * grid.dy)
        except IndexError:
            try:
                C = vals[neighbors[1]]
            except:
                top_grad = 0.
                weighting_horizontals -= 1.
            else:
                try:
                    top_grad = (C - vals[diagonals[0]]) / grid.dy
                except IndexError:
                    top_grad = (vals[diagonals[1]] - C) / grid.dy
        try:
            bottom_grad = (vals[diagonals[2]] -
                           vals[diagonals[3]]) / (2. * grid.dy)
        except IndexError:
            try:
                C = vals[neighbors[3]]
            except:
                bottom_grad = 0.
                weighting_horizontals -= 1.
            else:
                try:
                    bottom_grad = (C - vals[diagonals[3]]) / grid.dy
                except IndexError:
                    bottom_grad = (vals[diagonals[2]] - C) / grid.dy

        slope_we = (top_grad + 2. * horizontal_grad +
                    bottom_grad) / weighting_horizontals
        slope_sn = (left_grad + 2. * vertical_grad +
                    right_grad) / weighting_verticals

        return slope_we, slope_sn


@deprecated(use='grid.calc_slope_at_node', version=1.0)
[docs]def calc_slope_aspect_of_nodes_horn(grid, ids=None, vals='topographic__elevation'): r"""Calculate slope and aspect. .. note:: THIS CODE HAS ISSUES (SN 25-Sept-14): This code didn't perform well on a NS facing elevation profile. Please check slope_aspect_routines_comparison.py under landlab\examples before using this. Suggested alternative: calculate_slope_aspect_at_nodes_burrough Calculates the local topographic slope (i.e., the down-dip slope, and presented as positive), and the aspect (dip direction in radians clockwise from north), at the given nodes, *ids*. All *ids* must be of core nodes. This method uses the Horn 1981 algorithm, the one employed by many GIS packages. It should be significantly faster than alternative slope methods. If *ids* is not provided, the slope will be returned for all core nodes. *vals* is either the name of an existing grid field from which to draw topographic data, or an array of values to use. If an array of values is passed, it must be nnodes long. If *vals* is not provided, this method will default to trying to use the field "topographic__elevation". Parameters ---------- grid : RasterModelGrid Grid on which to calculate slopes and aspects. ids : array_like of int, optional Nodes on which to calculate slope and aspect. vals : str or ndarray, optional Node values used to measure slope and aspect. Returns ------- (slope, aspect) : tuple of float *slope*: a len(ids) array of slopes at each node provided. *aspect*: a len(ids) array of aspects at each node provided. Examples -------- >>> from landlab import RasterModelGrid >>> import numpy as np >>> grid = RasterModelGrid((4, 5)) Create a south-facing slope. >>> elevation = np.array([ ... 0., 0., 0., 0., 0, ... 1., 1., 1., 1., 1, ... 2., 2., 2., 2., 2, ... 3., 3., 3., 3., 3]) >>> (slope, aspect) = calc_slope_aspect_of_nodes_horn(grid, ... vals=elevation) >>> len(slope) == grid.number_of_core_nodes True >>> len(aspect) == grid.number_of_core_nodes True >>> slope array([ 1., 1., 1., 1., 1., 1.]) >>> aspect * 180. / np.pi array([ 180., 180., 180., 180., 180., 180.]) Make the slope north-facing by multiplying elevations by -1. The slopes will still be positive but the aspects will change. >>> elevation *= -1 >>> (slope, aspect) = calc_slope_aspect_of_nodes_horn(grid, ... vals=elevation) >>> slope array([ 1., 1., 1., 1., 1., 1.]) >>> aspect * 180. / np.pi array([ 0., 0., 0., 0., 0., 0.]) Double the slope and make it west-facing. >>> elevation = np.array([ ... 0., 1., 2., 3., 4., ... 0., 1., 2., 3., 4., ... 0., 1., 2., 3., 4., ... 0., 1., 2., 3., 4.]) >>> elevation *= 2. >>> (slope, aspect) = calc_slope_aspect_of_nodes_horn(grid, ... vals=elevation) >>> slope array([ 2., 2., 2., 2., 2., 2.]) >>> aspect * 180. / np.pi array([ 270., 270., 270., 270., 270., 270.]) Make the slope east-facing by multiplying elevations by -1. The slopes will still be positive but the aspects will change. >>> elevation *= -1. >>> (slope, aspect) = calc_slope_aspect_of_nodes_horn(grid, ... vals=elevation) >>> slope array([ 2., 2., 2., 2., 2., 2.]) >>> aspect * 180. / np.pi array([ 90., 90., 90., 90., 90., 90.]) LLCATS: DEPR NINF SURF """ if ids is None: ids = grid.core_nodes if isinstance(vals, str): vals = grid.at_node[vals] else: if len(vals) != grid.number_of_nodes: raise IndexError('*vals* was not of a compatible length!') # [right, top, left, bottom] neighbors = grid.active_neighbors_at_node[ids] # [topright, topleft, bottomleft, bottomright] diagonals = grid._get_diagonal_list(ids) input_array = np.empty((len(ids), 9), dtype=int) input_array[:, 0] = ids input_array[:, 1:5] = neighbors input_array[:, 5:] = diagonals slopes_array = np.apply_along_axis(_one_line_slopes, 1, input_array, grid, vals) slope_we = slopes_array[:, 0] slope_sn = slopes_array[:, 1] slope = np.sqrt(slope_we * slope_we + slope_sn * slope_sn) # aspect = np.empty_like(slope) aspect = np.ones(slope.size, dtype=float) simple_cases = slope_we != 0. complex_cases = np.logical_not(simple_cases) complex_aspects = aspect[complex_cases] complex_aspects[slope_sn[complex_cases] < 0.] = np.pi complex_aspects[slope_sn[complex_cases] >= 0.] = 0. aspect[complex_cases] = complex_aspects # +ve is CCW rotation from x axis angle_to_xaxis = np.arctan(slope_sn[simple_cases] / slope_we[simple_cases]) aspect[simple_cases] = ( (1. - np.sign(slope_we[simple_cases])) * 0.5) * np.pi + ( 0.5 * np.pi - angle_to_xaxis) return slope.ravel(), aspect.ravel()