def _get_raster_spacing(coords): """Get the row and column spacing of a raster grid. Parameters ---------- root : netcdf_file A NetCDF object. Returns ------- (dy, dx) Spacing of grid rows and columns. """ spacing = np.empty(len(coords), dtype=np.float64) for (axis, coord) in enumerate(coords): coord_spacing = np.diff(coord, axis=axis) if not np.all(coord_spacing == coord_spacing.flat[0]): raise NotRasterGridError() spacing[axis] = coord_spacing.flat[0] if not np.all(spacing == spacing[0]): raise NotRasterGridError() return spacing[0]
def read_netcdf(nc_file, reshape=False, just_grid=False): """ Reads the NetCDF file *nc_file*, and writes it to the fields of a new RasterModelGrid, which it then returns. Check the names of the fields in the returned grid with grid.at_nodes.keys(). """ try: root = nc.netcdf_file(nc_file, 'r', version=2) except TypeError: root = nc4.Dataset(nc_file, 'r', format='NETCDF4') shape = _read_netcdf_grid_shape(root) spacing = _read_netcdf_grid_spacing(root) assert (len(shape) == 2) assert (len(spacing) == 2) if spacing[0] != spacing[1]: raise NotRasterGridError() grid = RasterModelGrid(num_rows=shape[0], num_cols=shape[1], dx=spacing[0]) if not just_grid: fields = _read_netcdf_structured_data(root) for (name, values) in fields.items(): grid.add_field('node', name, values) root.close() return grid
def _get_raster_spacing(coords): spacing = np.empty(len(coords), dtype=np.float64) for (axis, coord) in enumerate(coords): coord_spacing = np.diff(coord, axis=axis) try: assert (np.all(coord_spacing == coord_spacing.flat[0])) except AssertionError: raise NotRasterGridError() spacing[axis] = coord_spacing.flat[0] try: assert (np.all(spacing == spacing[0])) except AssertionError: raise NotRasterGridError() else: return spacing[0]
def read_netcdf(nc_file, just_grid=False): """Read a GEBCO-formatted NetCDF file. Reads the NetCDF file *nc_file*, and writes it to the fields of a new RasterModelGrid, which it then returns. Check the names of the fields in the returned grid with grid.at_nodes.keys(). Parameters ---------- nc_file : str Name of the NetCDF file. just_grid : bool, optional If ``True``, just read the grid information and forget the data. Otherwise add the data as fields. Returns ------- RasterModelGrid A newly-created :any:`RasterModelGrid`. """ try: root = nc.netcdf_file(nc_file, 'r', version=2) except TypeError: root = nc4.Dataset(nc_file, 'r', format='NETCDF4') shape = _read_netcdf_grid_shape(root) spacing = _read_netcdf_grid_spacing(root) assert len(shape) == 2 assert len(spacing) == 2 if spacing[0] != spacing[1]: raise NotRasterGridError() grid = RasterModelGrid(shape, spacing=spacing) if not just_grid: fields = _read_netcdf_structured_data(root) for (name, values) in fields.items(): grid.add_field('node', name, values) root.close() return grid
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