Esempio n. 1
0
    def get_pixelsize_metres(self, xy=None):
        """
        Compute the size (in metres) of the pixel at the specified xy position.

        :param xy:
            A tuple containing an (x, y) grid co-ordinates of a pixel. Defaults \
            to the central pixel in the grid.

        :return:
            A tuple (x_size, y_size) gives the size of the pixel in metres
        """
        if xy is None:
            xy = (self.shape[1] / 2, self.shape[0] / 2)

        (x, y) = xy

        spheroid, _ = setup_spheroid(self.crs.ExportToWkt())

        (lon1, lat1) = self.transform * (x, y + 0.5)
        (lon2, lat2) = self.transform * (x + 1, y + 0.5)
        x_size, _, _ = vinc_dist(spheroid[1], spheroid[0], radians(lat1),
                                 radians(lon1), radians(lat2), radians(lon2))

        (lon1, lat1) = self.transform * (x + 0.5, y)
        (lon2, lat2) = self.transform * (x + 0.5, y + 1)
        y_size, _, _ = vinc_dist(spheroid[1], spheroid[0], radians(lat1),
                                 radians(lon1), radians(lat2), radians(lon2))

        return (x_size, y_size)
Esempio n. 2
0
def calculate_cast_shadow(acquisition,
                          dsm_group,
                          satellite_solar_group,
                          buffer_distance,
                          out_group=None,
                          compression=H5CompressionFilter.LZF,
                          filter_opts=None,
                          solar_source=True):
    """
    This code is an interface to the fortran code
    cast_shadow_main.f90 written by Fuqin (and modified to
    work with F2py).

    The following was taken from the top of the Fotran program:
    "cast_shadow_main.f90":

    Creates a shadow mask for a standard Landsat scene
    the program was originally written by DLB Jupp in Oct. 2010
    for a small sub_matrix and was modified by Fuqin Li in Oct.
    2010 so that the program can be used for large landsat scene.

    Basically, a sub-matrix A is embedded in a larger DEM image
    and the borders must be large enough to find the shaded pixels.
    If we assume the solar azimuth and zenith angles change very
    little within the sub-matrix A, then the Landsat scene can be
    divided into several sub_matrix.
    For Australian region, with 0 .00025 degree resolution, the
    sub-marix A is set to 500x500

    we also need to set extra DEM lines/columns to run the Landsat
    scene. This will change with elevation
    difference within the scene and solar zenith angle. For
    Australian region and Landsat scene with 0.00025 degree
    resolution, the maximum extra lines are set to 250 pixels/lines
    for each direction. This figure shold be sufficient for everywhere
    and anytime in Australia. Thus the DEM image will be larger than
    landsat image for 500 lines x 500 columns

    :param acquisition:
        An instance of an acquisition object.

    :param dsm_group:
        The root HDF5 `Group` that contains the Digital Surface Model
        data.
        The dataset pathnames are given by:

        * DatasetName.DSM_SMOOTHED

        The dataset must have the same dimensions as `acquisition`
        plus a margin of widths specified by margin.

    :param satellite_solar_group:
        The root HDF5 `Group` that contains the satellite and solar
        datasets specified by the pathnames given by:

        * DatasetName.SOLAR_ZENITH
        * DatasetName.SOLAR_AZIMUTH
        * DatasetName.SATELLITE_VIEW
        * DatasetName.SATELLITE_AZIMUTH

    :param buffer_distance:
        A number representing the desired distance (in the same
        units as the acquisition) in which to calculate the extra
        number of pixels required to buffer an image.
        Default is 8000.

    :param out_group:
        If set to None (default) then the results will be returned
        as an in-memory hdf5 file, i.e. the `core` driver. Otherwise,
        a writeable HDF5 `Group` object.

        The dataset names will be given by the format string detailed
        by:

        * DatasetName.CAST_SHADOW_FMT

    :param compression:
        The compression filter to use.
        Default is H5CompressionFilter.LZF 

    :filter_opts:
        A dict of key value pairs available to the given configuration
        instance of H5CompressionFilter. For example
        H5CompressionFilter.LZF has the keywords *chunks* and *shuffle*
        available.
        Default is None, which will use the default settings for the
        chosen H5CompressionFilter instance.

    :param solar_source:
        A `bool` indicating whether or not the source for the line
        of sight comes from the sun (True; Default), or False
        indicating the satellite.

    :return:
        An opened `h5py.File` object, that is either in-memory using the
        `core` driver, or on disk.

    :warning:
        The Fortran code cannot be compiled with ``-O3`` as it
        produces incorrect results if it is.
    """
    # Setup the geobox
    geobox = acquisition.gridded_geo_box()
    x_res, y_res = geobox.pixelsize
    x_origin, y_origin = geobox.origin

    # Are we in UTM or geographics?
    is_utm = not geobox.crs.IsGeographic()

    # Retrive the spheroid parameters
    # (used in calculating pixel size in metres per lat/lon)
    spheroid, _ = setup_spheroid(geobox.crs.ExportToWkt())

    # Define Top, Bottom, Left, Right pixel buffer margins
    margins = pixel_buffer(acquisition, buffer_distance)

    if solar_source:
        zenith_name = DatasetName.SOLAR_ZENITH.value
        azimuth_name = DatasetName.SOLAR_AZIMUTH.value
    else:
        zenith_name = DatasetName.SATELLITE_VIEW.value
        azimuth_name = DatasetName.SATELLITE_AZIMUTH.value

    zenith_angle = satellite_solar_group[zenith_name][:]
    azimuth_angle = satellite_solar_group[azimuth_name][:]
    elevation = dsm_group[DatasetName.DSM_SMOOTHED.value][:]

    # block height and width of the window/submatrix used in the cast
    # shadow algorithm
    block_width = margins.left + margins.right
    block_height = margins.top + margins.bottom

    # Compute the cast shadow mask
    ierr, mask = cast_shadow_main(elevation, zenith_angle, azimuth_angle,
                                  x_res, y_res, spheroid, y_origin, x_origin,
                                  margins.left, margins.right, margins.top,
                                  margins.bottom, block_height, block_width,
                                  is_utm)

    if ierr:
        raise CastShadowError(ierr)

    source_dir = 'SUN' if solar_source else 'SATELLITE'

    # Initialise the output file
    if out_group is None:
        fid = h5py.File('cast-shadow-{}.h5'.format(source_dir),
                        driver='core',
                        backing_store=False)
    else:
        fid = out_group

    if GroupName.SHADOW_GROUP.value not in fid:
        fid.create_group(GroupName.SHADOW_GROUP.value)

    if filter_opts is None:
        filter_opts = {}
    else:
        filter_opts = filter_opts.copy()

    grp = fid[GroupName.SHADOW_GROUP.value]
    tile_size = satellite_solar_group[zenith_name].chunks
    filter_opts['chunks'] = tile_size
    kwargs = compression.config(**filter_opts).dataset_compression_kwargs()
    kwargs['dtype'] = 'bool'

    dname_fmt = DatasetName.CAST_SHADOW_FMT.value
    out_dset = grp.create_dataset(dname_fmt.format(source=source_dir),
                                  data=mask,
                                  **kwargs)

    # attach some attributes to the image datasets
    attrs = {
        'crs_wkt': geobox.crs.ExportToWkt(),
        'geotransform': geobox.transform.to_gdal()
    }
    desc = ("The cast shadow mask determined using the {} "
            "as the source direction.").format(source_dir)
    attrs['description'] = desc
    attrs['alias'] = 'cast-shadow-{}'.format(source_dir).lower()
    attach_image_attributes(out_dset, attrs)

    if out_group is None:
        return fid
Esempio n. 3
0
def slope_aspect_arrays(acquisition, dsm_group, buffer_distance,
                        out_group=None, compression=H5CompressionFilter.LZF,
                        filter_opts=None):
    """
    Calculates slope and aspect.

    :param acquisition:
        An instance of an acquisition object.

    :param dsm_group:
        The root HDF5 `Group` that contains the Digital Surface Model
        data.
        The dataset pathname is given by:

        * DatasetName.DSM_SMOOTHED

        The dataset must have the same dimensions as `acquisition`
        plus a margin of widths specified by margin.

    :param buffer_distance:
        A number representing the desired distance (in the same
        units as the acquisition) in which to calculate the extra
        number of pixels required to buffer an image.
        Default is 8000.

    :param out_group:
        If set to None (default) then the results will be returned
        as an in-memory hdf5 file, i.e. the `core` driver. Otherwise,
        a writeable HDF5 `Group` object.

        The dataset names will be given by the format string detailed
        by:

        * DatasetName.SLOPE
        * DatasetName.ASPECT

    :param compression:
        The compression filter to use.
        Default is H5CompressionFilter.LZF 

    :filter_opts:
        A dict of key value pairs available to the given configuration
        instance of H5CompressionFilter. For example
        H5CompressionFilter.LZF has the keywords *chunks* and *shuffle*
        available.
        Default is None, which will use the default settings for the
        chosen H5CompressionFilter instance.

    :return:
        An opened `h5py.File` object, that is either in-memory using the
        `core` driver, or on disk.
    """

    # Setup the geobox
    geobox = acquisition.gridded_geo_box()

    # Retrive the spheroid parameters
    # (used in calculating pixel size in metres per lat/lon)
    spheroid, _ = setup_spheroid(geobox.crs.ExportToWkt())

    # Are we in projected or geographic space
    is_utm = not geobox.crs.IsGeographic()

    # Define Top, Bottom, Left, Right pixel margins
    margins = pixel_buffer(acquisition, buffer_distance)

    # Get the x and y pixel sizes
    _, y_origin = geobox.origin
    x_res, y_res = geobox.pixelsize

    # Get acquisition dimensions and add 1 pixel top, bottom, left & right
    cols, rows = geobox.get_shape_xy()
    ncol = cols + 2
    nrow = rows + 2

    # elevation dataset
    elevation = dsm_group[DatasetName.DSM_SMOOTHED.value]
    ele_rows, ele_cols  = elevation.shape

    # TODO: check that the index is correct
    # Define the index to read the DEM subset
    ystart, ystop = (margins.top - 1, ele_rows - (margins.bottom - 1))
    xstart, xstop = (margins.left - 1, ele_cols - (margins.right - 1))
    idx = (slice(ystart, ystop), slice(xstart, xstop))

    subset = as_array(elevation[idx], dtype=numpy.float32, transpose=True)

    # Define an array of latitudes
    # This will be ignored if is_utm == True
    alat = numpy.array([y_origin - i * y_res for i in range(-1, nrow - 1)],
                       dtype=numpy.float64)  # yes, I did mean float64.

    # Output the reprojected result
    # Initialise the output files
    if out_group is None:
        fid = h5py.File('slope-aspect.h5', driver='core',
                        backing_store=False)
    else:
        fid = out_group

    if GroupName.SLP_ASP_GROUP.value not in fid:
        fid.create_group(GroupName.SLP_ASP_GROUP.value)

    if filter_opts is None:
        filter_opts = {}
    else:
        filter_opts = filter_opts.copy()
    filter_opts['chunks'] = acquisition.tile_size

    group = fid[GroupName.SLP_ASP_GROUP.value]

    # metadata for calculation
    param_group = group.create_group('PARAMETERS')
    param_group.attrs['dsm_index'] = ((ystart, ystop), (xstart, xstop))
    param_group.attrs['pixel_buffer'] = '1 pixel'

    kwargs = compression.config(**filter_opts).dataset_compression_kwargs()
    no_data = -999
    kwargs['fillvalue'] = no_data

    # Define the output arrays. These will be transposed upon input
    slope = numpy.zeros((rows, cols), dtype='float32')
    aspect = numpy.zeros((rows, cols), dtype='float32')

    slope_aspect(ncol, nrow, cols, rows, x_res, y_res, spheroid, alat, is_utm,
                 subset, slope.transpose(), aspect.transpose())

    # output datasets
    dname = DatasetName.SLOPE.value
    slope_dset = group.create_dataset(dname, data=slope, **kwargs)
    dname = DatasetName.ASPECT.value
    aspect_dset = group.create_dataset(dname, data=aspect, **kwargs)

    # attach some attributes to the image datasets
    attrs = {'crs_wkt': geobox.crs.ExportToWkt(),
             'geotransform': geobox.transform.to_gdal(),
             'no_data_value': no_data}
    desc = "The slope derived from the input elevation model."
    attrs['description'] = desc
    attach_image_attributes(slope_dset, attrs)

    desc = "The aspect derived from the input elevation model."
    attrs['description'] = desc
    attach_image_attributes(aspect_dset, attrs)

    if out_group is None:
        return fid