Esempio n. 1
0
def test_add_halo_where_halo_value_is_zero():
    values = np.ones((3, 4), dtype=int)
    with_halo = add_halo(values, halo_value=0)
    assert np.all(with_halo == [
        [0, 0, 0, 0, 0, 0],
        [0, 1, 1, 1, 1, 0],
        [0, 1, 1, 1, 1, 0],
        [0, 1, 1, 1, 1, 0],
        [0, 0, 0, 0, 0, 0],
    ])
Esempio n. 2
0
def test_add_halo_where_halo_value_is_nan():
    values = np.ones((2, 3), dtype=float)
    with_halo = add_halo(values, halo=1, halo_value=np.nan)
    assert np.all(with_halo[1:-1, 1:-1] == approx(values))
    assert np.all(
        np.isnan(with_halo) == [
            [True, True, True, True, True],
            [True, False, False, False, True],
            [True, False, False, False, True],
            [True, True, True, True, True],
        ])
Esempio n. 3
0
def test_add_halo_where_halo_value_is_nan():
    values = np.ones((2, 3), dtype=float)
    with_halo = add_halo(values, halo=1, halo_value=np.nan)
    assert np.all(with_halo[1:-1, 1:-1] == approx(values))
    assert np.all(
        np.isnan(with_halo)
        == [
            [True, True, True, True, True],
            [True, False, False, False, True],
            [True, False, False, False, True],
            [True, True, True, True, True],
        ]
    )
Esempio n. 4
0
def test_add_halo_where_halo_value_is_zero():
    values = np.ones((3, 4), dtype=int)
    with_halo = add_halo(values, halo_value=0)
    assert np.all(
        with_halo
        == [
            [0, 0, 0, 0, 0, 0],
            [0, 1, 1, 1, 1, 0],
            [0, 1, 1, 1, 1, 0],
            [0, 1, 1, 1, 1, 0],
            [0, 0, 0, 0, 0, 0],
        ]
    )
Esempio n. 5
0
def test_add_halo_with_bigger_halo():
    values = np.ones((2, 4), dtype=int)
    with_halo = add_halo(values, halo=3, halo_value=0)
    assert np.all(with_halo == [
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 1, 1, 1, 1, 0, 0, 0],
        [0, 0, 0, 1, 1, 1, 1, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    ])
Esempio n. 6
0
def test_add_halo_with_bigger_halo():
    values = np.ones((2, 4), dtype=int)
    with_halo = add_halo(values, halo=3, halo_value=0)
    assert np.all(
        with_halo
        == [
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 1, 1, 1, 1, 0, 0, 0],
            [0, 0, 0, 1, 1, 1, 1, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        ]
    )
Esempio n. 7
0
def read_netcdf(
    nc_file, grid=None, name=None, just_grid=False, halo=0, nodata_value=-9999.0
):
    """Create a :class:`~.RasterModelGrid` from a netcdf file.

    Create a new :class:`~.RasterModelGrid` from the netcdf file, *nc_file*.
    If the netcdf file also contains data, add that data to the grid's fields.
    To create a new grid without any associated data from the netcdf file,
    set the *just_grid* keyword to ``True``.

    A halo can be added with the keyword *halo*.

    If you want the fields to be added to an existing grid, it can be passed
    to the keyword argument *grid*.

    Parameters
    ----------
    nc_file : str
        Name of a netcdf file.
    grid : *grid* , optional
        Adds data to an existing *grid* instead of creating a new one.
    name : str, optional
        Add only fields with NetCDF variable name to the grid. Default is to
        add all NetCDF varibles to the grid.
    just_grid : boolean, optional
        Create a new grid but don't add value data.
    halo : integer, optional
        Adds outer border of depth halo to the *grid*.
    nodata_value : float, optional
        Value that indicates an invalid value. Default is -9999.

    Returns
    -------
    :class:`~.RasterModelGrid`
        A newly-created :class:`~.RasterModelGrid`.

    Examples
    --------
    Import :func:`read_netcdf` and the path to an example netcdf file included
    with landlab.

    >>> from landlab.io.netcdf import read_netcdf
    >>> from landlab.io.netcdf import NETCDF4_EXAMPLE_FILE

    Create a new grid from the netcdf file. The example grid is a uniform
    rectilinear grid with 4 rows and 3 columns of nodes with unit spacing.
    The data file also contains data defined at the nodes for the grid for
    a variable called, *surface__elevation*.

    >>> grid = read_netcdf(NETCDF4_EXAMPLE_FILE)
    >>> grid.shape == (4, 3)
    True
    >>> grid.dy, grid.dx
    (1.0, 1.0)
    >>> list(grid.at_node.keys())
    ['surface__elevation']
    >>> grid.at_node['surface__elevation']
    array([  0.,   1.,   2.,   3.,   4.,   5.,   6.,   7.,   8.,   9.,  10.,
            11.])

    :func:`read_netcdf` will try to determine the format of the netcdf file.
    For example, the same call will also work for *netcdf3*-formatted files.

    >>> from landlab.io.netcdf import NETCDF3_64BIT_EXAMPLE_FILE
    >>> grid = read_netcdf(NETCDF3_64BIT_EXAMPLE_FILE)
    >>> grid.shape == (4, 3)
    True
    >>> grid.dy, grid.dx
    (1.0, 1.0)

    A more complicated example might add data with a halo to an existing grid.
    Note that the lower left corner must be specified correctly for the data
    and the grid to align correctly.

    >>> from landlab import RasterModelGrid
    >>> grid = RasterModelGrid((6, 5), xy_of_lower_left=(-1., -1.))
    >>> grid = read_netcdf(
    ...     NETCDF4_EXAMPLE_FILE,
    ...     grid=grid,
    ...     halo=1,
    ...     nodata_value=-1)
    >>> grid.at_node['surface__elevation'].reshape(grid.shape)
    array([[ -1.,  -1.,  -1.,  -1.,  -1.],
           [ -1.,   0.,   1.,   2.,  -1.],
           [ -1.,   3.,   4.,   5.,  -1.],
           [ -1.,   6.,   7.,   8.,  -1.],
           [ -1.,   9.,  10.,  11.,  -1.],
           [ -1.,  -1.,  -1.,  -1.,  -1.]])
    """
    from landlab import RasterModelGrid

    try:
        root = nc.netcdf_file(nc_file, "r", version=2)
    except TypeError:
        root = nc4.Dataset(nc_file, "r", format="NETCDF4")

    try:
        node_coords = _read_netcdf_structured_grid(root)
    except ValueError:
        if (len(root.variables["x"].dimensions) == 1) and (
            len(root.variables["y"].dimensions) == 1
        ):

            node_coords = _read_netcdf_raster_structured_grid(root)
        else:
            assert ValueError(
                "x and y dimensions must both either be 2D "
                "(nj, ni) or 1D (ni,) and (nj)."
            )

    assert len(node_coords) == 2

    dx = _get_raster_spacing(node_coords)
    xy_spacing = (dx, dx)
    shape = node_coords[0].shape
    xy_of_lower_left = (
        node_coords[0].min() - halo * dx,
        node_coords[1].min() - halo * dx,
    )

    if grid is not None:
        if (grid.number_of_node_rows != shape[0] + 2 * halo) or (
            grid.number_of_node_columns != shape[1] + 2 * halo
        ):
            raise MismatchGridDataSizeError(
                shape[0] + 2 * halo * shape[1] + 2 * halo,
                grid.number_of_node_rows * grid.number_of_node_columns,
            )
        if (grid.dx, grid.dy) != xy_spacing:
            raise MismatchGridXYSpacing((grid.dx, grid.dy), xy_spacing)

        if grid.xy_of_lower_left != xy_of_lower_left:
            raise MismatchGridXYLowerLeft(grid.xy_of_lower_left, xy_of_lower_left)

    if grid is None:
        grid = RasterModelGrid(
            shape, xy_spacing=xy_spacing, xy_of_lower_left=xy_of_lower_left
        )

    if not just_grid:
        fields, grid_mapping_dict = _read_netcdf_structured_data(root)
        for (field_name, values) in fields.items():

            # add halo if necessary
            if halo > 0:
                values = add_halo(
                    values.reshape(shape), halo=halo, halo_value=nodata_value
                ).reshape((-1,))

            # add only the requested fields.
            if (name is None) or (field_name == name):
                add_field = True
            else:
                add_field = False

            if add_field:
                grid.add_field(field_name, values, at="node", noclobber=False)

        if (name is not None) and (name not in grid.at_node):
            raise ValueError(
                "Specified field {name} was not in provided NetCDF.".format(name=name)
            )

    # save grid mapping
    if grid_mapping_dict is not None:
        grid.grid_mapping = grid_mapping_dict

    root.close()

    return grid
Esempio n. 8
0
def test_add_halo_with_defaults():
    values = np.arange(6, dtype=int).reshape((2, 3))
    with_halo = add_halo(values)
    assert np.all(with_halo[1:-1, 1:-1] == values)
    assert values.dtype == with_halo.dtype
Esempio n. 9
0
def test_add_halo_to_bool_array():
    values = np.full((2, 3), True, dtype=bool)
    with_halo = add_halo(values)
    assert np.all(with_halo[1:-1, 1:-1] == values)
    assert values.dtype == with_halo.dtype
Esempio n. 10
0
def read_esri_ascii(asc_file, grid=None, reshape=False, name=None, halo=0):
    """Read :py:class:`~landlab.RasterModelGrid` from an ESRI ASCII file.

    Read data from *asc_file*, an ESRI_ ASCII file, into a
    :py:class:`~landlab.RasterModelGrid`.  *asc_file* is either the name of
    the data file or is a file-like object.

    The grid and data read from the file are returned as a tuple
    (*grid*, *data*) where *grid* is an instance of
    :py:class:`~landlab.RasterModelGrid` and *data* is a numpy
    array of doubles with that has been reshaped to have the number of rows
    and columns given in the header.

    .. _ESRI: http://resources.esri.com/help/9.3/arcgisengine/java/GP_ToolRef/spatial_analyst_tools/esri_ascii_raster_format.htm

    Parameters
    ----------
    asc_file : str of file-like
        Data file to read.
    reshape : boolean, optional
        Reshape the returned array, otherwise return a flattened array.
    name : str, optional
        Add data to the grid as a named field.
    grid : *grid* , optional
        Adds data to an existing *grid* instead of creating a new one.
    halo : integer, optional
        Adds outer border of depth halo to the *grid*.

    Returns
    -------
    (grid, data) : tuple
        A newly-created RasterModel grid and the associated node data.

    Raises
    ------
    DataSizeError
        Data are not the same size as indicated by the header file.
    MismatchGridDataSizeError
        If a grid is passed, and the size of the grid does not agree with the
        size of the data.
    MismatchGridXYSpacing
        If a grid is passed, and the cellsize listed in the heading does not
        match the grid dx and dy.
    MismatchGridXYLowerLeft
        If a grid is passed and the xllcorner and yllcorner do not match that
        of the grid.

    Examples
    --------
    Assume that fop is the name of a file that contains text below
    (make sure you have your path correct):
    ncols         3
    nrows         4
    xllcorner     1.
    yllcorner     2.
    cellsize      10.
    NODATA_value  -9999
    0. 1. 2.
    3. 4. 5.
    6. 7. 8.
    9. 10. 11.
    --------
    >>> from landlab.io import read_esri_ascii
    >>> (grid, data) = read_esri_ascii('fop') # doctest: +SKIP
    >>> #grid is an object of type RasterModelGrid with 4 rows and 3 cols
    >>> #data contains an array of length 4*3 that is equal to
    >>> # [9., 10., 11., 6., 7., 8., 3., 4., 5., 0., 1., 2.]
    >>> (grid, data) = read_esri_ascii('fop', halo=1) # doctest: +SKIP
    >>> #now the data has a nodata_value ring of -9999 around it. So array is
    >>> # [-9999, -9999, -9999, -9999, -9999, -9999,
    >>> #  -9999, 9., 10., 11., -9999,
    >>> #  -9999, 6., 7., 8., -9999,
    >>> #  -9999, 3., 4., 5., -9999,
    >>> #  -9999, 0., 1., 2. -9999,
    >>> #  -9999, -9999, -9999, -9999, -9999, -9999]
    """
    from ..grid import RasterModelGrid

    # if the asc_file is provided as a string, open it and pass the pointer to
    # _read_asc_header, and _read_asc_data
    if isinstance(asc_file, six.string_types):
        with open(asc_file, "r") as f:
            header = read_asc_header(f)
            data = _read_asc_data(f)

    # otherwise, pass asc_file directly.
    else:
        header = read_asc_header(asc_file)
        data = _read_asc_data(asc_file)

    # There is no reason for halo to be negative.
    # Assume that if a negative value is given it should be 0.
    if halo <= 0:
        shape = (header["nrows"], header["ncols"])
        if data.size != shape[0] * shape[1]:
            raise DataSizeError(shape[0] * shape[1], data.size)
    else:
        shape = (header["nrows"] + 2 * halo, header["ncols"] + 2 * halo)
        # check to see if a nodata_value was given.  If not, assign -9999.
        if "nodata_value" in header.keys():
            nodata_value = header["nodata_value"]
        else:
            header["nodata_value"] = -9999.0
            nodata_value = header["nodata_value"]
        if data.size != (shape[0] - 2 * halo) * (shape[1] - 2 * halo):
            raise DataSizeError(shape[0] * shape[1], data.size)
    xy_spacing = (header["cellsize"], header["cellsize"])
    xy_of_lower_left = (
        header["xllcorner"] - halo * header["cellsize"],
        header["yllcorner"] - halo * header["cellsize"],
    )

    data = np.flipud(data)

    if halo > 0:
        data = add_halo(
            data.reshape(header["nrows"], header["ncols"]),
            halo=halo,
            halo_value=nodata_value,
        ).reshape((-1,))

    if not reshape:
        data = data.flatten()

    if grid is not None:
        if (grid.number_of_node_rows != shape[0]) or (
            grid.number_of_node_columns != shape[1]
        ):
            raise MismatchGridDataSizeError(
                shape[0] * shape[1],
                grid.number_of_node_rows * grid.number_of_node_columns,
            )
        if (grid.dx, grid.dy) != xy_spacing:
            raise MismatchGridXYSpacing((grid.dx, grid.dy), xy_spacing)

        if grid.xy_of_lower_left != xy_of_lower_left:
            raise MismatchGridXYLowerLeft(grid.xy_of_lower_left, xy_of_lower_left)

    if grid is None:
        grid = RasterModelGrid(
            shape, xy_spacing=xy_spacing, xy_of_lower_left=xy_of_lower_left
        )
    if name:
        grid.add_field("node", name, data)

    return (grid, data)
Esempio n. 11
0
def test_add_halo_to_float_array():
    values = np.arange(6, dtype=float).reshape((2, 3))
    with_halo = add_halo(values)
    assert np.all(with_halo[1:-1, 1:-1] == approx(values))
    assert values.dtype == with_halo.dtype
Esempio n. 12
0
def test_add_halo_with_defaults():
    values = np.arange(6, dtype=int).reshape((2, 3))
    with_halo = add_halo(values)
    assert np.all(with_halo[1:-1, 1:-1] == values)
    assert values.dtype == with_halo.dtype
Esempio n. 13
0
def test_add_halo_with_zero_halo():
    values = np.ones((2, 3), dtype=int)
    with_halo = add_halo(values, halo=0)
    assert np.all(with_halo == values)
Esempio n. 14
0
def read_esri_ascii(asc_file, grid=None, reshape=False, name=None, halo=0):
    """Read :py:class:`~landlab.RasterModelGrid` from an ESRI ASCII file.

    Read data from *asc_file*, an ESRI_ ASCII file, into a
    :py:class:`~landlab.RasterModelGrid`.  *asc_file* is either the name of
    the data file or is a file-like object.

    The grid and data read from the file are returned as a tuple
    (*grid*, *data*) where *grid* is an instance of
    :py:class:`~landlab.RasterModelGrid` and *data* is a numpy
    array of doubles with that has been reshaped to have the number of rows
    and columns given in the header.

    .. _ESRI: http://resources.esri.com/help/9.3/arcgisengine/java/GP_ToolRef/spatial_analyst_tools/esri_ascii_raster_format.htm

    Parameters
    ----------
    asc_file : str of file-like
        Data file to read.
    reshape : boolean, optional
        Reshape the returned array, otherwise return a flattened array.
    name : str, optional
        Add data to the grid as a named field.
    grid : *grid* , optional
        Adds data to an existing *grid* instead of creating a new one.
    halo : integer, optional
        Adds outer border of depth halo to the *grid*.

    Returns
    -------
    (grid, data) : tuple
        A newly-created RasterModel grid and the associated node data.

    Raises
    ------
    DataSizeError
        Data are not the same size as indicated by the header file.
    MismatchGridDataSizeError
        If a grid is passed, and the size of the grid does not agree with the
        size of the data.
    MismatchGridXYSpacing
        If a grid is passed, and the cellsize listed in the heading does not
        match the grid dx and dy.
    MismatchGridXYLowerLeft
        If a grid is passed and the xllcorner and yllcorner do not match that
        of the grid.

    Examples
    --------
    Assume that fop is the name of a file that contains text below
    (make sure you have your path correct):
    ncols         3
    nrows         4
    xllcorner     1.
    yllcorner     2.
    cellsize      10.
    NODATA_value  -9999
    0. 1. 2.
    3. 4. 5.
    6. 7. 8.
    9. 10. 11.
    --------
    >>> from landlab.io import read_esri_ascii
    >>> (grid, data) = read_esri_ascii('fop') # doctest: +SKIP
    >>> #grid is an object of type RasterModelGrid with 4 rows and 3 cols
    >>> #data contains an array of length 4*3 that is equal to
    >>> # [9., 10., 11., 6., 7., 8., 3., 4., 5., 0., 1., 2.]
    >>> (grid, data) = read_esri_ascii('fop', halo=1) # doctest: +SKIP
    >>> #now the data has a nodata_value ring of -9999 around it. So array is
    >>> # [-9999, -9999, -9999, -9999, -9999, -9999,
    >>> #  -9999, 9., 10., 11., -9999,
    >>> #  -9999, 6., 7., 8., -9999,
    >>> #  -9999, 3., 4., 5., -9999,
    >>> #  -9999, 0., 1., 2. -9999,
    >>> #  -9999, -9999, -9999, -9999, -9999, -9999]
    """
    from ..grid import RasterModelGrid

    # if the asc_file is provided as a string, open it and pass the pointer to
    # _read_asc_header, and _read_asc_data
    if isinstance(asc_file, (str, pathlib.Path)):
        with open(asc_file, "r") as f:
            header = read_asc_header(f)
            data = _read_asc_data(f)

    # otherwise, pass asc_file directly.
    else:
        header = read_asc_header(asc_file)
        data = _read_asc_data(asc_file)

    # There is no reason for halo to be negative.
    # Assume that if a negative value is given it should be 0.
    if halo <= 0:
        shape = (header["nrows"], header["ncols"])
        if data.size != shape[0] * shape[1]:
            raise DataSizeError(shape[0] * shape[1], data.size)
    else:
        shape = (header["nrows"] + 2 * halo, header["ncols"] + 2 * halo)
        # check to see if a nodata_value was given.  If not, assign -9999.
        if "nodata_value" in header.keys():
            nodata_value = header["nodata_value"]
        else:
            header["nodata_value"] = -9999.0
            nodata_value = header["nodata_value"]
        if data.size != (shape[0] - 2 * halo) * (shape[1] - 2 * halo):
            raise DataSizeError(shape[0] * shape[1], data.size)
    xy_spacing = (header["cellsize"], header["cellsize"])
    xy_of_lower_left = (
        header["xllcorner"] - halo * header["cellsize"],
        header["yllcorner"] - halo * header["cellsize"],
    )

    data = np.flipud(data)

    if halo > 0:
        data = add_halo(
            data.reshape(header["nrows"], header["ncols"]),
            halo=halo,
            halo_value=nodata_value,
        ).reshape((-1, ))

    if not reshape:
        data = data.flatten()

    if grid is not None:
        if (grid.number_of_node_rows !=
                shape[0]) or (grid.number_of_node_columns != shape[1]):
            raise MismatchGridDataSizeError(
                shape[0] * shape[1],
                grid.number_of_node_rows * grid.number_of_node_columns,
            )
        if (grid.dx, grid.dy) != xy_spacing:
            raise MismatchGridXYSpacing((grid.dx, grid.dy), xy_spacing)

        if grid.xy_of_lower_left != xy_of_lower_left:
            raise MismatchGridXYLowerLeft(grid.xy_of_lower_left,
                                          xy_of_lower_left)

    if grid is None:
        grid = RasterModelGrid(shape,
                               xy_spacing=xy_spacing,
                               xy_of_lower_left=xy_of_lower_left)
    if name:
        grid.add_field(name, data, at="node")

    return (grid, data)
Esempio n. 15
0
def test_add_halo_to_bool_array():
    values = np.full((2, 3), True, dtype=bool)
    with_halo = add_halo(values)
    assert np.all(with_halo[1:-1, 1:-1] == values)
    assert values.dtype == with_halo.dtype
Esempio n. 16
0
File: read.py Progetto: stgl/landlab
def read_netcdf(
    nc_file,
    grid=None,
    name=None,
    just_grid=False,
    halo=0,
    nodata_value=-9999.0,
):
    """Create a :class:`~.RasterModelGrid` from a netcdf file.

    Create a new :class:`~.RasterModelGrid` from the netcdf file, *nc_file*.
    If the netcdf file also contains data, add that data to the grid's fields.
    To create a new grid without any associated data from the netcdf file,
    set the *just_grid* keyword to ``True``.

    A halo can be added with the keyword *halo*.

    If you want the fields to be added to an existing grid, it can be passed
    to the keyword argument *grid*.

    Parameters
    ----------
    nc_file : str
        Name of a netcdf file.
    grid : *grid* , optional
        Adds data to an existing *grid* instead of creating a new one.
    name : str, optional
        Add only fields with NetCDF variable name to the grid. Default is to
        add all NetCDF varibles to the grid.
    just_grid : boolean, optional
        Create a new grid but don't add value data.
    halo : integer, optional
        Adds outer border of depth halo to the *grid*.
    nodata_value : float, optional
        Value that indicates an invalid value. Default is -9999.

    Returns
    -------
    :class:`~.RasterModelGrid`
        A newly-created :class:`~.RasterModelGrid`.

    Examples
    --------
    Import :func:`read_netcdf` and the path to an example netcdf file included
    with landlab.

    >>> from landlab.io.netcdf import read_netcdf

    Create a new grid from the netcdf file. The example grid is a uniform
    rectilinear grid with 4 rows and 3 columns of nodes with unit spacing.
    The data file also contains data defined at the nodes for the grid for
    a variable called, *surface__elevation*.

    >>> grid = read_netcdf("test-netcdf4.nc") # doctest: +SKIP
    >>> grid.shape == (4, 3) # doctest: +SKIP
    True
    >>> grid.dy, grid.dx # doctest: +SKIP
    (1.0, 1.0)
    >>> list(grid.at_node.keys()) # doctest: +SKIP
    ['surface__elevation']
    >>> grid.at_node['surface__elevation'] # doctest: +SKIP
    array([  0.,   1.,   2.,   3.,   4.,   5.,   6.,   7.,   8.,   9.,  10.,
            11.])

    :func:`read_netcdf` will try to determine the format of the netcdf file.
    For example, the same call will also work for *netcdf3*-formatted files.

    >>> grid = read_netcdf("test-netcdf3-64bit.nc") # doctest: +SKIP
    >>> grid.shape == (4, 3) # doctest: +SKIP
    True
    >>> grid.dy, grid.dx # doctest: +SKIP
    (1.0, 1.0)

    A more complicated example might add data with a halo to an existing grid.
    Note that the lower left corner must be specified correctly for the data
    and the grid to align correctly.

    >>> from landlab import RasterModelGrid
    >>> grid = RasterModelGrid((6, 5), xy_of_lower_left=(-1., -1.)) # doctest: +SKIP
    >>> grid = read_netcdf(
    ...     "test-netcdf4.nc",
    ...     grid=grid,
    ...     halo=1,
    ...     nodata_value=-1,
    ... ) # doctest: +SKIP
    >>> grid.at_node['surface__elevation'].reshape(grid.shape) # doctest: +SKIP
    array([[ -1.,  -1.,  -1.,  -1.,  -1.],
           [ -1.,   0.,   1.,   2.,  -1.],
           [ -1.,   3.,   4.,   5.,  -1.],
           [ -1.,   6.,   7.,   8.,  -1.],
           [ -1.,   9.,  10.,  11.,  -1.],
           [ -1.,  -1.,  -1.,  -1.,  -1.]])
    """
    from landlab import RasterModelGrid

    dataset = xr.open_dataset(nc_file)

    if isinstance(name, str):
        names = {name}
    elif name is None:
        names = set(dataset.variables)
    else:
        names = set(name)

    # test if the input is a raster (x and y) are only 1-D instead of 2D.
    if len(dataset["x"].shape) == 1:
        y, x = np.meshgrid(dataset["y"], dataset["x"], indexing="ij")
    else:
        x = dataset["x"]
        y = dataset["y"]

    dx = np.diff(x, axis=1)
    dy = np.diff(y, axis=0)

    if np.all(dx == dx[0, 0]) and np.all(dy == dy[0, 0]):
        xy_spacing = (dx[0, 0], dy[0, 0])
    else:
        raise NotRasterGridError()

    shape = x.shape
    xy_of_lower_left = (
        x[0, 0] - halo * xy_spacing[0],
        y[0, 0] - halo * xy_spacing[1],
    )

    if grid is None:
        grid = RasterModelGrid(shape,
                               xy_spacing=xy_spacing,
                               xy_of_lower_left=xy_of_lower_left)
    else:
        if grid.shape != (shape[0] + 2 * halo, shape[1] + 2 * halo):
            raise MismatchGridDataSizeError(
                shape[0] + 2 * halo * shape[1] + 2 * halo,
                grid.number_of_node_rows * grid.number_of_node_columns,
            )
        if (grid.dx, grid.dy) != xy_spacing:
            raise MismatchGridXYSpacing((grid.dx, grid.dy), xy_spacing)

        if grid.xy_of_lower_left != xy_of_lower_left:
            raise MismatchGridXYLowerLeft(grid.xy_of_lower_left,
                                          xy_of_lower_left)

    if not just_grid:
        fields, grid_mapping_dict = _read_netcdf_structured_data(dataset)
        for (field_name, values) in fields.items():

            # add halo if necessary
            if halo > 0:
                values = add_halo(values.reshape(shape),
                                  halo=halo,
                                  halo_value=nodata_value).reshape((-1, ))

            # add only the requested fields.
            if (name is None) or (field_name == name):
                add_field = True
            else:
                add_field = False

            if add_field:
                grid.add_field(field_name, values, at="node", clobber=True)

        if (name is not None) and (name not in grid.at_node):
            raise ValueError(
                "Specified field {name} was not in provided NetCDF.".format(
                    name=name))

    ignore = {"x", "y"}
    for name in names - ignore:
        values = dataset.variables[name].values
        if halo > 0:
            values = add_halo(values.reshape(shape),
                              halo=halo,
                              halo_value=nodata_value).reshape((-1, ))
        grid.add_field(name, values, at="node", clobber=True)

    return grid
Esempio n. 17
0
def test_add_halo_with_zero_halo():
    values = np.ones((2, 3), dtype=int)
    with_halo = add_halo(values, halo=0)
    assert np.all(with_halo == values)
Esempio n. 18
0
def read_netcdf(nc_file,
                grid=None,
                name=None,
                just_grid=False,
                halo=0,
                nodata_value=-9999.0):
    """Create a :class:`~.RasterModelGrid` from a netcdf file.

    Create a new :class:`~.RasterModelGrid` from the netcdf file, *nc_file*.
    If the netcdf file also contains data, add that data to the grid's fields.
    To create a new grid without any associated data from the netcdf file,
    set the *just_grid* keyword to ``True``.

    A halo can be added with the keyword *halo*.

    If you want the fields to be added to an existing grid, it can be passed
    to the keyword argument *grid*.

    Parameters
    ----------
    nc_file : str
        Name of a netcdf file.
    grid : *grid* , optional
        Adds data to an existing *grid* instead of creating a new one.
    name : str, optional
        Add only fields with NetCDF variable name to the grid. Default is to
        add all NetCDF varibles to the grid.
    just_grid : boolean, optional
        Create a new grid but don't add value data.
    halo : integer, optional
        Adds outer border of depth halo to the *grid*.
    nodata_value : float, optional
        Value that indicates an invalid value. Default is -9999.

    Returns
    -------
    :class:`~.RasterModelGrid`
        A newly-created :class:`~.RasterModelGrid`.

    Examples
    --------
    Import :func:`read_netcdf` and the path to an example netcdf file included
    with landlab.

    >>> from landlab.io.netcdf import read_netcdf

    Create a new grid from the netcdf file. The example grid is a uniform
    rectilinear grid with 4 rows and 3 columns of nodes with unit spacing.
    The data file also contains data defined at the nodes for the grid for
    a variable called, *surface__elevation*.

    >>> grid = read_netcdf("test-netcdf4.nc") # doctest: +SKIP
    >>> grid.shape == (4, 3) # doctest: +SKIP
    True
    >>> grid.dy, grid.dx # doctest: +SKIP
    (1.0, 1.0)
    >>> list(grid.at_node.keys()) # doctest: +SKIP
    ['surface__elevation']
    >>> grid.at_node['surface__elevation'] # doctest: +SKIP
    array([  0.,   1.,   2.,   3.,   4.,   5.,   6.,   7.,   8.,   9.,  10.,
            11.])

    :func:`read_netcdf` will try to determine the format of the netcdf file.
    For example, the same call will also work for *netcdf3*-formatted files.

    >>> grid = read_netcdf("test-netcdf3-64bit.nc") # doctest: +SKIP
    >>> grid.shape == (4, 3) # doctest: +SKIP
    True
    >>> grid.dy, grid.dx # doctest: +SKIP
    (1.0, 1.0)

    A more complicated example might add data with a halo to an existing grid.
    Note that the lower left corner must be specified correctly for the data
    and the grid to align correctly.

    >>> from landlab import RasterModelGrid
    >>> grid = RasterModelGrid((6, 5), xy_of_lower_left=(-1., -1.)) # doctest: +SKIP
    >>> grid = read_netcdf(
    ...     "test-netcdf4.nc",
    ...     grid=grid,
    ...     halo=1,
    ...     nodata_value=-1,
    ... ) # doctest: +SKIP
    >>> grid.at_node['surface__elevation'].reshape(grid.shape) # doctest: +SKIP
    array([[ -1.,  -1.,  -1.,  -1.,  -1.],
           [ -1.,   0.,   1.,   2.,  -1.],
           [ -1.,   3.,   4.,   5.,  -1.],
           [ -1.,   6.,   7.,   8.,  -1.],
           [ -1.,   9.,  10.,  11.,  -1.],
           [ -1.,  -1.,  -1.,  -1.,  -1.]])
    """
    from landlab import RasterModelGrid

    try:
        root = nc.netcdf_file(nc_file, "r", version=2)
    except TypeError:
        root = nc4.Dataset(nc_file, "r", format="NETCDF4")

    try:
        node_coords = _read_netcdf_structured_grid(root)
    except ValueError:
        if (len(root.variables["x"].dimensions) == 1) and (len(
                root.variables["y"].dimensions) == 1):

            node_coords = _read_netcdf_raster_structured_grid(root)
        else:
            assert ValueError("x and y dimensions must both either be 2D "
                              "(nj, ni) or 1D (ni,) and (nj).")

    assert len(node_coords) == 2

    dx = _get_raster_spacing(node_coords)
    xy_spacing = (dx, dx)
    shape = node_coords[0].shape
    xy_of_lower_left = (
        node_coords[0].min() - halo * dx,
        node_coords[1].min() - halo * dx,
    )

    if grid is not None:
        if (grid.number_of_node_rows != shape[0] + 2 * halo) or (
                grid.number_of_node_columns != shape[1] + 2 * halo):
            raise MismatchGridDataSizeError(
                shape[0] + 2 * halo * shape[1] + 2 * halo,
                grid.number_of_node_rows * grid.number_of_node_columns,
            )
        if (grid.dx, grid.dy) != xy_spacing:
            raise MismatchGridXYSpacing((grid.dx, grid.dy), xy_spacing)

        if grid.xy_of_lower_left != xy_of_lower_left:
            raise MismatchGridXYLowerLeft(grid.xy_of_lower_left,
                                          xy_of_lower_left)

    if grid is None:
        grid = RasterModelGrid(shape,
                               xy_spacing=xy_spacing,
                               xy_of_lower_left=xy_of_lower_left)

    if not just_grid:
        fields, grid_mapping_dict = _read_netcdf_structured_data(root)
        for (field_name, values) in fields.items():

            # add halo if necessary
            if halo > 0:
                values = add_halo(values.reshape(shape),
                                  halo=halo,
                                  halo_value=nodata_value).reshape((-1, ))

            # add only the requested fields.
            if (name is None) or (field_name == name):
                add_field = True
            else:
                add_field = False

            if add_field:
                grid.add_field(field_name, values, at="node", clobber=True)

        if (name is not None) and (name not in grid.at_node):
            raise ValueError(
                "Specified field {name} was not in provided NetCDF.".format(
                    name=name))

    # save grid mapping
    if grid_mapping_dict is not None:
        grid.grid_mapping = grid_mapping_dict

    root.close()

    return grid
Esempio n. 19
0
def test_add_halo_to_float_array():
    values = np.arange(6, dtype=float).reshape((2, 3))
    with_halo = add_halo(values)
    assert np.all(with_halo[1:-1, 1:-1] == approx(values))
    assert values.dtype == with_halo.dtype