def test_clip_array(self):
        from utilities.nctools import clip_array

        pixelwidth = 0.3
        pixelheight = 0.3

        x_left = 145.0
        y_upper = 13.0

        extent_list = [[145.40, -13.40, 146.40, -14.40],
                       [145.20, -13.20, 146.20, -14.20],
                       [145.25, -13.25, 146.20, -14.20]]

        expected = np.array([[8, 9, 10], [15, 16, 17], [22, 23, 24]])

        data = np.arange(49).reshape(7, 7)

        for extent in extent_list:

            data_clip = clip_array(data, x_left, y_upper, pixelwidth,
                                   pixelheight, extent)

            assert_almost_equal(expected, data_clip, decimal=2)
def terrain(temp_tile, tile_extents_nobuffer):
    """
    Performs core calculations to derive the terrain multiplier

    :param temp_tile: `file` the image file of the input tile of the land cover
    :param tile_extents_nobuffer: `tuple` the input tile extent without buffer

    """

    # open the tile
    temp_dataset = gdal.Open(temp_tile)

    # get image size, format, projection
    cols = temp_dataset.RasterXSize
    rows = temp_dataset.RasterYSize
    bands = temp_dataset.RasterCount
    log.info('Input raster format is %s' % temp_dataset.GetDriver().ShortName +
             '/ %s' % temp_dataset.GetDriver().LongName)
    log.info('Image size is %s' % cols + 'x %s' % rows + 'x %s' % bands)

    # get georeference info
    geotransform = temp_dataset.GetGeoTransform()
    x_left = geotransform[0]
    y_upper = -geotransform[3]
    pixelwidth = geotransform[1]
    pixelheight = -geotransform[5]

    # get the tile's longitude and latitude values used to save output in
    # netcdf
    lon, lat = get_lat_lon(tile_extents_nobuffer, pixelwidth, pixelheight)

    # get the average grid size in metre of the tile
    x_m_array, y_m_array = get_pixel_size_grids(temp_dataset)
    gridwidth = 0.5 * (np.mean(x_m_array) + np.mean(y_m_array))
    log.info('gridwidth is {0}'.format(gridwidth))

    # produce the original terrain multiplier from the input terrain map
    log.info(
        'Reclassify the terrain classes into initial terrain multipliers ...')
    band = temp_dataset.GetRasterBand(1)
    data = band.ReadAsArray(0, 0, cols, rows)

    nodata_value = band.GetNoDataValue()
    #if nodata_value is not None:
    #    data[np.where(data == nodata_value)] = np.nan
    #else:
    #    data[np.where(data is None)] = np.nan

    mz_init = get_terrain_table()
    reclassified_array = terrain_class2mz_orig(data, mz_init)

    # if the value is 0, it is nodata, if all 0s, empty tile
    if np.max(reclassified_array) == 0:
        log.info('Terrain dataset is all zeros. Terrain classification'
                 'will be skipped')
        return
    else:
        reclassified_array[reclassified_array == 0] = np.nan

    # assign nodata area as water with multiplier value 1
    mask = np.isnan(reclassified_array)
    reclassified_array[mask] = 1.0

    # convoulution of the original terrain multipler into different directions
    log.info('Moving average for each direction ...')
    dire = ['w', 'e', 'n', 's', 'nw', 'ne', 'se', 'sw']

    # set avearage and lag distance used for convolution as per
    # AS/NZ 1170.2 (2011) including amendments
    avg_dist = 500.
    lag_dist = 200.

    for one_dir in dire:
        log.info(one_dir)
        if one_dir in ['w', 'e', 'n', 's']:
            avg_width = int(np.around(avg_dist / gridwidth))
            lag_width = int(np.around(lag_dist / gridwidth))
        else:
            # for the diagonal directions, the avg_width is the same for x and
            # y component of the diagonal distance (avg_dist). lag_width is
            # the same principle as the avg_width
            avg_width = int(avg_dist / (gridwidth * 1.414))
            lag_width = int(lag_dist / (gridwidth * 1.414))

        # if the tile is smaller than the lag distance, no convolultion
        if lag_width > reclassified_array.shape[0]:
            outdata = reclassified_array
        else:
            # if the tile is smaller than the upwind buffer, all the tile is in
            # buffer
            if (avg_width + lag_width) > reclassified_array.shape[0]:
                avg_width = reclassified_array.shape[0] - lag_width

            log.info('convolution average width ' + str(avg_width))
            outdata = convo(one_dir, reclassified_array, avg_width, lag_width)
            outdata[mask] = np.nan

        # find output folder
        tile_folder = os.path.dirname(temp_tile)
        file_name = os.path.basename(temp_tile)

        # output format as netCDF4
        mz_folder = pjoin(tile_folder, 'terrain')

        tile_nc = pjoin(
            mz_folder,
            os.path.splitext(file_name)[0] +
            '_mz_' +
            one_dir +
            '.nc')
        log.info("Saving terrain multiplier in netCDF file")

        outdata_nobuffer = clip_array(outdata, x_left, y_upper, pixelwidth,
                                      pixelheight, tile_extents_nobuffer)

        save_multiplier('Mz', outdata_nobuffer, lat, lon, tile_nc)

        del outdata

    temp_dataset = None

    log.info(
        'finish terrain multiplier computation for this tile successfully')
def topomult(input_dem, tile_extents_nobuffer):
    """
    Executes core topographic multiplier functionality

    :param input_dem: `file` the input tile of the DEM
    :param tile_extents_nobuffer: `tuple` the input tile extent without buffer
    """

    # find output folder
    mh_folder = pjoin(os.path.dirname(input_dem), 'topographic')
    file_name = os.path.basename(input_dem)

    ds = gdal.Open(input_dem)
    nc = ds.RasterXSize
    nr = ds.RasterYSize

    geotransform = ds.GetGeoTransform()
    x_left = geotransform[0]
    y_upper = -geotransform[3]
    pixelwidth = geotransform[1]
    pixelheight = -geotransform[5]

    lon, lat = get_lat_lon(tile_extents_nobuffer, pixelwidth, pixelheight)

    band = ds.GetRasterBand(1)
    elevation_array = band.ReadAsArray(0, 0, nc, nr)

    nodata_value = band.GetNoDataValue()
    if nodata_value is not None:
        elevation_array[np.where(elevation_array == nodata_value)] = np.nan
    else:
        elevation_array[np.where(elevation_array is None)] = np.nan

    elevation_array_tran = np.transpose(elevation_array)
    data = elevation_array_tran.flatten()

    x_m_array, y_m_array = get_pixel_size_grids(ds)
    cellsize = 0.5 * (np.mean(x_m_array) + np.mean(y_m_array))

    # Compute the starting positions along the boundaries depending on dir
    # Together, the direction and the starting position determines a line.
    # Note that the starting positions are defined
    # in terms of the 1-d index of the array.

    directions = ['n', 's', 'e', 'w', 'ne', 'nw', 'se', 'sw']

    for direction in directions:
        log.info(direction)

        if len(direction) == 2:
            data_spacing = cellsize * math.sqrt(2)
        else:
            data_spacing = cellsize

        mhdata = np.ones(data.shape)

        strt_idx = []
        if direction.find('n') >= 0:
            strt_idx = np.append(strt_idx, list(range(0, nr * nc, nr)))
        if direction.find('s') >= 0:
            strt_idx = np.append(strt_idx, list(range(nr - 1, nr * nc, nr)))
        if direction.find('e') >= 0:
            strt_idx = np.append(strt_idx, list(range((nc - 1) * nr, nr * nc)))
        if direction.find('w') >= 0:
            strt_idx = np.append(strt_idx, list(range(0, nr)))

        # For the diagonal directions the corner will have been counted twice
        # so get rid of the duplicates then loop over the data lines
        # (i.e. over the starting positions)
        strt_idx = np.unique(strt_idx)

        for ctr, idx in enumerate(strt_idx):
            log.debug('Processing path %3i' % ctr + ' of %3i' % len(strt_idx) +
                      ', index %5i.' % idx)

            # Get a line of the data
            # path is a 1-d vector which gives the indices of the data
            path = make_path.make_path(nr, nc, idx, direction)
            line = data[path]
            line[np.isnan(line)] = 0.
            m = multiplier_calc.multiplier_calc(line, data_spacing)

            # write the line back to the data array
            m = np.transpose(m)
            mhdata[path] = m[0, ].flatten()

        # Reshape the result to matrix like
        mhdata = np.reshape(mhdata, (nc, nr))
        mhdata = np.transpose(mhdata)

        # Remove the conservatism as described in the Reference
        mhdata = remove_conservatism(mhdata)

        # consider the Tasmania factor
        if x_left > 143.0 and y_upper > 40.0:
            mhdata = tasmania(mhdata, elevation_array)

        # smooth
        g = np.ones((3, 3)) / 9.
        mhsmooth = signal.convolve(mhdata, g, mode='same')
        mhsmooth[np.isnan(elevation_array)] = np.nan
        del mhdata

        # output format as netCDF4
        tile_nc = pjoin(
            mh_folder,
            os.path.splitext(file_name)[0][:-4] + '_mt_' + direction + '.nc')

        mhsmooth_nobuffer = clip_array(mhsmooth, x_left, y_upper, pixelwidth,
                                       pixelheight, tile_extents_nobuffer)

        save_multiplier('Mt', mhsmooth_nobuffer, lat, lon, tile_nc)
        del mhsmooth

        log.info('Finished direction {0}'.format(direction))

    ds = None
Beispiel #4
0
def convo_combine(ms_orig, slope_array, aspect_array, tile_extents_nobuffer):
    """
    Apply convolution to the orginal shielding factor for each direction and
    call the :term:`combine` module to consider the slope and aspect and remove
    conservitism to get final shielding multiplier values

    :param ms_orig: `file` the original shidelding factor map
    :param slope_array: :class:`numpy.ndarray` the input slope values
    :param aspect_array: :class:`numpy.ndarray` the input aspect values
    :param tile_extents_nobuffer: `tuple` the input tile extent without buffer

    """

    ms_orig_ds = gdal.Open(ms_orig)
    if ms_orig_ds is None:
        log.info('Could not open ' + ms_orig)
        sys.exit(1)

    log.info('ms_orig is {0}'.format(ms_orig))

    # get image size, format, projection
    cols = ms_orig_ds.RasterXSize
    rows = ms_orig_ds.RasterYSize
    geotransform = ms_orig_ds.GetGeoTransform()
    x_left = geotransform[0]
    y_upper = -geotransform[3]
    pixelwidth = geotransform[1]
    pixelheight = -geotransform[5]

    lon, lat = get_lat_lon(tile_extents_nobuffer, pixelwidth, pixelheight)

    band = ms_orig_ds.GetRasterBand(1)
    data = band.ReadAsArray(0, 0, cols, rows)

    if ms_orig_ds is None:
        log.info('Could not open {0}'.format(ms_orig))
        sys.exit(1)

    x_m_array, y_m_array = get_pixel_size_grids(ms_orig_ds)
    gridwidth = 0.5 * (np.mean(x_m_array) + np.mean(y_m_array))

    log.info('gridwidth is {0}'.format(gridwidth))

    ms_folder = os.path.dirname(ms_orig)
    file_name = os.path.basename(ms_orig)

    dire = ['w', 'e', 'n', 's', 'nw', 'ne', 'se', 'sw']

    for one_dir in dire:

        log.info(one_dir)

        kernel_size = int(100.0 / gridwidth)

        log.info('convolution kernel size is {0}'.format(str(kernel_size)))

        # if the resolution size is bigger than 100 m, no covolution just copy
        # the initial shielding factor to each direction
        if kernel_size > 0:
            outdata = np.zeros((rows, cols), np.float32)

            kern_dir = globals()['kern_' + one_dir]
            mask = kern_dir(kernel_size)
            outdata = blur_image(data, mask)
        else:
            outdata = data

        result = combine(outdata, slope_array, aspect_array, one_dir)
        del outdata

        # output format as netCDF4
        tile_nc = pjoin(ms_folder,
                        os.path.splitext(file_name)[0] + '_' + one_dir + '.nc')
        log.info("Saving shielding multiplier in netCDF file")

        result_nobuffer = clip_array(result, x_left, y_upper, pixelwidth,
                                     pixelheight, tile_extents_nobuffer)

        save_multiplier('Ms', result_nobuffer, lat, lon, tile_nc)

        del result

    ms_orig_ds = None

    os.remove(ms_orig)
    os.chdir(ms_folder)
    filelist = glob.glob('*.xml')

    log.debug("useless xml files: {0}".format(repr(filelist)))

    if len(filelist) != 0:
        for f in filelist:
            try:
                os.remove(f)
            except OSError:
                pass
def convo_combine(ms_orig, slope_array, aspect_array, tile_extents_nobuffer):
    """
    Apply convolution to the orginal shielding factor for each direction and
    call the :term:`combine` module to consider the slope and aspect and remove
    conservitism to get final shielding multiplier values

    :param ms_orig: `file` the original shidelding factor map
    :param slope_array: :class:`numpy.ndarray` the input slope values
    :param aspect_array: :class:`numpy.ndarray` the input aspect values
    :param tile_extents_nobuffer: `tuple` the input tile extent without buffer

    """

    ms_orig_ds = gdal.Open(ms_orig)
    if ms_orig_ds is None:
        log.info('Could not open ' + ms_orig)
        sys.exit(1)

    log.info('ms_orig is {0}'.format(ms_orig))

    # get image size, format, projection
    cols = ms_orig_ds.RasterXSize
    rows = ms_orig_ds.RasterYSize
    geotransform = ms_orig_ds.GetGeoTransform()
    x_left = geotransform[0]
    y_upper = -geotransform[3]
    pixelwidth = geotransform[1]
    pixelheight = -geotransform[5]

    lon, lat = get_lat_lon(tile_extents_nobuffer, pixelwidth, pixelheight)

    band = ms_orig_ds.GetRasterBand(1)
    data = band.ReadAsArray(0, 0, cols, rows)

    if ms_orig_ds is None:
        log.info('Could not open {0}'.format(ms_orig))
        sys.exit(1)

    x_m_array, y_m_array = get_pixel_size_grids(ms_orig_ds)
    gridwidth = 0.5 * (np.mean(x_m_array) + np.mean(y_m_array))

    log.info('gridwidth is {0}'.format(gridwidth))

    ms_folder = os.path.dirname(ms_orig)
    file_name = os.path.basename(ms_orig)

    dire = ['w', 'e', 'n', 's', 'nw', 'ne', 'se', 'sw']

    for one_dir in dire:

        log.info(one_dir)

        kernel_size = int(100.0 / gridwidth)

        log.info('convolution kernel size is {0}'.format(str(kernel_size)))

        # if the resolution size is bigger than 100 m, no covolution just copy
        # the initial shielding factor to each direction
        if kernel_size > 0:
            outdata = np.zeros((rows, cols), np.float32)

            kern_dir = globals()['kern_' + one_dir]
            mask = kern_dir(kernel_size)
            outdata = blur_image(data, mask)
        else:
            outdata = data

        result = combine(outdata, slope_array, aspect_array, one_dir)
        log.debug('Maximum shielding value is {0}'.format(result.max()))
        log.debug('Minimum shielding value is {0}'.format(result.min()))
        del outdata

        # output format as netCDF4
        tile_nc = pjoin(
            ms_folder,
            os.path.splitext(file_name)[0] +
            '_' +
            one_dir +
            '.nc')
        log.info("Saving shielding multiplier in netCDF file")

        result_nobuffer = clip_array(result, x_left, y_upper, pixelwidth,
                                     pixelheight, tile_extents_nobuffer)

        save_multiplier('Ms', result_nobuffer, lat, lon, tile_nc)

        del result

    ms_orig_ds = None
    try:
        os.remove(ms_orig)
    except:
        pass
    os.chdir(ms_folder)
    filelist = glob.glob('*.xml')

    log.debug("useless xml files: {0}".format(repr(filelist)))

    if len(filelist) != 0:
        for f in filelist:
            try:
                os.remove(f)
            except OSError:
                pass