def main(bathy=None, out_sin_raster=None, out_cos_raster=None): """ Calculate the statistical aspect of a raster, which computes the sin(aspect) and cos(aspect). By using these two variables, aspect can be accounted for as a continuous circular variable. Because aspect is circular (0 and 359.9 are immediately adjacent), this trigonometric transformation preserves distances between elements and is the simplest transformation mechanism. """ try: arcpy.env.rasterStatistics = "STATISTICS" # Calculate the aspect of the bathymetric raster. "Aspect is expressed # in positive degrees from 0 to 359.9, measured clockwise from north." utils.msg("Calculating aspect...") aspect = Aspect(bathy) # Both the sin and cos functions here expect radians, not degrees. # convert our Aspect raster into radians, check that the values # are in range. aspect_rad = aspect * (math.pi / 180) aspect_sin = Sin(aspect_rad) aspect_cos = Cos(aspect_rad) out_sin_raster = utils.validate_path(out_sin_raster) out_cos_raster = utils.validate_path(out_cos_raster) aspect_sin.save(out_sin_raster) aspect_cos.save(out_cos_raster) except Exception as e: utils.msg(e, mtype='error')
def topographic_radiation(raw_aspect, radiation_output): """ Description: calculates 32-bit float topographic radiation Inputs: 'raw_aspect' -- an input raw aspect raster 'radiation_output' -- an output topographic radiation raster Returned Value: Returns a raster dataset on disk Preconditions: requires an input raw aspect raster """ # Import packages import arcpy from arcpy.sa import Con from arcpy.sa import Cos from arcpy.sa import Raster # Set overwrite option arcpy.env.overwriteOutput = True # Calculate topographic radiation aspect index print('\t\tCalculating topographic radiation aspect index...') numerator = 1 - Cos((3.142 / 180) * (Raster(raw_aspect) - 30)) radiation_index = numerator / 2 # Convert negative aspect values print('\t\tConverting negative aspect values...') out_raster = Con(Raster(raw_aspect) < 0, 0.5, radiation_index) out_raster.save(radiation_output)
def site_exposure(raw_aspect, raw_slope, exposure_output): """ Description: calculates 32-bit float site exposure Inputs: 'raw_aspect' -- an input raw aspect raster 'raw_slope' -- an input raster digital elevation model 'exposure_output' -- an output exposure raster Returned Value: Returns a raster dataset on disk Preconditions: requires an input aspect and slope raster """ # Import packages import arcpy from arcpy.sa import Cos from arcpy.sa import Divide from arcpy.sa import Minus from arcpy.sa import Raster from arcpy.sa import Times # Set overwrite option arcpy.env.overwriteOutput = True # Calculate cosine of modified aspect print('\t\tCalculating cosine of modified aspect...') cosine = Cos(Divide(Times(3.142, Minus(Raster(raw_aspect), 180)), 180)) # Calculate site exposure index and save output print('\t\tCalculating site exposure index...') out_raster = Times(Raster(raw_slope), cosine) out_raster.save(exposure_output)
def main(bathy=None, out_sin_raster=None, out_cos_raster=None): """ Calculate the statistical aspect of a raster, which computes the sin(aspect) and cos(aspect). By using these two variables, aspect can be accounted for as a continuous circular variable. Because aspect is circular (0 and 359.9 are immediately adjacent), this trigonometric transformation preserves distances between elements and is the simplest transformation mechanism. """ try: arcpy.env.compression = "LZW" arcpy.env.rasterStatistics = "STATISTICS" # Calculate the aspect of the bathymetric raster. "Aspect is expressed # in positive degrees from 0 to 359.9, measured clockwise from north." utils.msg("Calculating aspect...") aspect = Aspect(bathy) # Both the sin and cos functions here expect radians, not degrees. # convert our Aspect raster into radians, check that the values # are in range. aspect_rad = aspect * (math.pi / 180) aspect_sin = Sin(aspect_rad) aspect_cos = Cos(aspect_rad) out_sin_raster = utils.validate_path(out_sin_raster) out_cos_raster = utils.validate_path(out_cos_raster) arcpy.CopyRaster_management(aspect_sin, out_sin_raster) arcpy.CopyRaster_management(aspect_cos, out_cos_raster) except Exception as e: utils.msg(e, mtype='error')
def main(bathy=None, out_sin_raster=None, out_cos_raster=None): try: arcpy.env.rasterStatistics = "STATISTICS" # Calculate the aspect of the bathymetric raster. "Aspect is expressed in # positive degrees from 0 to 359.9, measured clockwise from north." utils.msg("Calculating aspect...") aspect = Aspect(bathy) # both the sin and cos functions here expect radians, not degrees. # convert our Aspect raster into radians, check that the values are in range. aspect_rad = aspect * (math.pi / 180) # because this statistic is circular (0 and 359.9 are immediately adjacent), # we need to transform this into a form which preserves distances between items. # trig is the simplest mechanism. aspect_sin = Sin(aspect_rad) aspect_cos = Cos(aspect_rad) out_sin_raster = utils.validate_path(out_sin_raster) out_cos_raster = utils.validate_path(out_cos_raster) aspect_sin.save(out_sin_raster) aspect_cos.save(out_cos_raster) except Exception as e: utils.msg(e, mtype='error')
def linear_aspect(raw_aspect, aspect_output): """ Description: calculates 32-bit float linear aspect Inputs: 'raw_aspect' -- an input raw aspect raster 'aspect_output' -- an output linear aspect raster Returned Value: Returns a raster dataset on disk Preconditions: requires an input DEM """ # Import packages import arcpy from arcpy.sa import ATan2 from arcpy.sa import Con from arcpy.sa import Cos from arcpy.sa import FocalStatistics from arcpy.sa import Mod from arcpy.sa import NbrRectangle from arcpy.sa import Raster from arcpy.sa import SetNull from arcpy.sa import Sin # Set overwrite option arcpy.env.overwriteOutput = True # Define a neighborhood variable neighborhood = NbrRectangle(3, 3, "CELL") # Calculate aspect transformations print('\t\tTransforming raw aspect to linear aspect...') setNull_aspect = SetNull( Raster(raw_aspect) < 0, (450.0 - Raster(raw_aspect)) / 57.296) sin_aspect = Sin(setNull_aspect) cos_aspect = Cos(setNull_aspect) sum_sin = FocalStatistics(sin_aspect, neighborhood, "SUM", "DATA") sum_cos = FocalStatistics(cos_aspect, neighborhood, "SUM", "DATA") mod_aspect = Mod( ((450 - (ATan2(sum_sin, sum_cos) * 57.296)) * 100), 36000 ) / 100 # The *100 and 36000(360*100) / 100 allow for two decimal points since Fmod appears to be gone out_raster = Con((sum_sin == 0) & (sum_cos == 0), -1, mod_aspect) # Save output raster file out_raster.save(aspect_output)
def surface_area(raw_slope, area_output): """ Description: calculates 32-bit float surface area ratio Inputs: 'raw_slope' -- an input raw slope raster 'roughness_output' -- an output roughness raster Returned Value: Returns a raster dataset on disk Preconditions: requires an input elevation raster """ # Import packages import arcpy from arcpy.sa import Cos from arcpy.sa import Float from arcpy.sa import Raster import math # Set overwrite option arcpy.env.overwriteOutput = True # Getting info on raster description = arcpy.Describe(raw_slope) cell_size = description.meanCellHeight # Set the cell size environment arcpy.env.cellSize = cell_size # Calculate cell area cell_area = cell_size * cell_size # Modify raw slope print('\t\tModifying raw slope...') modifier = math.pi / 180 modified_slope = Raster(raw_slope) * modifier # Calculate surface area ratio print('\t\tCalculating surface area ratio...') out_raster = Float(cell_area) / Cos(modified_slope) out_raster.save(area_output)
def main(in_raster=None, neighborhood_size=None, out_raster=None): """ Compute terrain ruggedness, using the vector ruggedness measure (VRM), as described in: Sappington et al., 2007. Quantifying Landscape Ruggedness for Animal Habitat Analysis: A Case Study Using Bighorn Sheep in the Mojave Desert. Journal of Wildlife Management. 71(5): 1419 -1426. """ hood_size = int(neighborhood_size) # FIXME: expose this as an option per #18 w = utils.Workspace() if w.exists: out_workspace = w.path else: out_workspace = os.path.dirname(out_raster) utils.workspace_exists(out_workspace) # force temporary stats to be computed in our output workspace arcpy.env.scratchWorkspace = out_workspace arcpy.env.workspace = out_workspace # TODO expose as config pyramid_orig = arcpy.env.pyramid arcpy.env.pyramid = "NONE" # TODO: currently set to automatically overwrite, expose this as option arcpy.env.overwriteOutput = True arcpy.env.compression = 'LZW' try: # Create Slope and Aspect rasters utils.msg("Calculating aspect...") out_aspect = Aspect(in_raster) utils.msg("Calculating slope...") out_slope = Slope(in_raster, "DEGREE") # Convert Slope and Aspect rasters to radians utils.msg("Converting slope and aspect to radians...") slope_rad = out_slope * (math.pi / 180) aspect_rad = out_aspect * (math.pi / 180) # Calculate x, y, and z rasters utils.msg("Calculating x, y, and z rasters...") xy_raster_calc = Sin(slope_rad) z_raster_calc = Cos(slope_rad) x_raster_calc = Con(out_aspect == -1, 0, Sin(aspect_rad)) * xy_raster_calc y_raster_calc = Con(out_aspect == -1, 0, Cos(aspect_rad)) * xy_raster_calc # Calculate sums of x, y, and z rasters for selected neighborhood size utils.msg("Calculating sums of x, y, and z rasters in neighborhood...") hood = NbrRectangle(hood_size, hood_size, "CELL") x_sum_calc = FocalStatistics(x_raster_calc, hood, "SUM", "NODATA") y_sum_calc = FocalStatistics(y_raster_calc, hood, "SUM", "NODATA") z_sum_calc = FocalStatistics(z_raster_calc, hood, "SUM", "NODATA") # Calculate the resultant vector utils.msg("Calculating the resultant vector...") result_vect = (x_sum_calc**2 + y_sum_calc**2 + z_sum_calc**2)**0.5 arcpy.env.rasterStatistics = "STATISTICS" arcpy.env.pyramid = pyramid_orig # Calculate the Ruggedness raster utils.msg("Calculating the final ruggedness raster...") ruggedness = 1 - (result_vect / hood_size**2) out_raster = utils.validate_path(out_raster) utils.msg("Saving ruggedness raster to to {}.".format(out_raster)) arcpy.CopyRaster_management(ruggedness, out_raster) except Exception as e: utils.msg(e, mtype='error')
def main(in_raster=None, out_raster=None, acr_correction=True, area_raster=None): """ A calculation of rugosity, based on the difference between surface area and planar area, as described in Jenness, J. 2002. Surface Areas and Ratios from Elevation Grid (surfgrids.avx) extension for ArcView 3.x, v. 1.2. Jenness Enterprises. NOTE: the VRM method implemented in ruggeddness is generally considered superior to this method. """ # sanitize acr input if isinstance(acr_correction, str) and acr_correction.lower() == 'false': acr_correction = False w = utils.Workspace() if w.exists: out_workspace = w.path else: out_workspace = os.path.dirname(out_raster) # make sure workspace exists utils.workspace_exists(out_workspace) utils.msg("Set scratch workspace to {}...".format(out_workspace)) # force temporary stats to be computed in our output workspace arcpy.env.scratchWorkspace = out_workspace arcpy.env.workspace = out_workspace pyramid_orig = arcpy.env.pyramid arcpy.env.pyramid = "NONE" # TODO: currently set to automatically overwrite, expose this as option arcpy.env.overwriteOutput = True bathy = Raster(in_raster) # get the cell size of the input raster; use same calculation as was # performed in BTM v1: (mean_x + mean_y) / 2 cell_size = (bathy.meanCellWidth + bathy.meanCellHeight) / 2.0 corner_dist = math.sqrt(2 * cell_size ** 2) flat_area = cell_size ** 2 utils.msg("Cell size: {}\nFlat area: {}".format(cell_size, flat_area)) try: # Create a set of shifted grids, with offets n from the origin X: # 8 | 7 | 6 # --|---|--- # 5 | X | 4 # --|---|--- # 3 | 2 | 1 positions = [(1, -1), (0, -1), (-1, -1), (1, 0), (-1, 0), (1, 1), (0, 1), (-1, 1)] corners = (1, 3, 6, 8) # dist * sqrt(2), as set in corner_dist orthogonals = (2, 4, 5, 7) # von Neumann neighbors, straight dist shift_rasts = [None] # offset to align numbers temp_rasts = [] for (n, pos) in enumerate(positions, start=1): utils.msg("Creating Shift Grid {} of 8...".format(n)) # scale shift grid by cell size (x_shift, y_shift) = map(lambda n: n * cell_size, pos) # set explicit path on shift rasters, otherwise suffer # inexplicable 999999 errors. shift_out = os.path.join(out_workspace, "shift_{}.tif".format(n)) shift_out = utils.validate_path(shift_out) temp_rasts.append(shift_out) arcpy.Shift_management(bathy, shift_out, x_shift, y_shift) shift_rasts.append(arcpy.sa.Raster(shift_out)) edge_rasts = [None] # calculate triangle length grids # edges 1-8: pairs of bathy:shift[n] for (n, shift) in enumerate(shift_rasts[1:], start=1): utils.msg("Calculating Triangle Edge {} of 16...".format(n)) # adjust for corners being sqrt(2) from center if n in corners: dist = corner_dist else: dist = cell_size edge_out = os.path.join(out_workspace, "edge_{}.tif".format(n)) edge_out = utils.validate_path(edge_out) temp_rasts.append(edge_out) edge = compute_edge(bathy, shift, dist) edge.save(edge_out) edge_rasts.append(arcpy.sa.Raster(edge_out)) # edges 9-16: pairs of adjacent shift grids [see layout above] # in BTM_v1, these are labeled A-H adjacent_shift = [(1, 2), (2, 3), (1, 4), (3, 5), (6, 4), (5, 8), (6, 7), (7, 8)] for (n, pair) in enumerate(adjacent_shift, start=9): utils.msg("Calculating Triangle Edge {} of 16...".format(n)) # the two shift rasters for this iteration (i, j) = pair edge_out = os.path.join(out_workspace, "edge_{}.tif".format(n)) edge_out = utils.validate_path(edge_out) temp_rasts.append(edge_out) edge = compute_edge(shift_rasts[i], shift_rasts[j], cell_size) edge.save(edge_out) edge_rasts.append(arcpy.sa.Raster(edge_out)) # areas of each triangle areas = [] for (n, pair) in enumerate(adjacent_shift, start=1): utils.msg("Calculating Triangle Area {} of 8...".format(n)) # the two shift rasters; n has the third side (i, j) = pair area_out = os.path.join(out_workspace, "area_{}.tif".format(n)) area_out = utils.validate_path(area_out) temp_rasts.append(area_out) area = triangle_area(edge_rasts[i], edge_rasts[j], edge_rasts[n+8]) area.save(area_out) areas.append(arcpy.sa.Raster(area_out)) utils.msg("Summing Triangle Area...") arcpy.env.pyramid = pyramid_orig arcpy.env.rasterStatistics = "STATISTICS" arcpy.env.compression = "LZW" total_area = (areas[0] + areas[1] + areas[2] + areas[3] + areas[4] + areas[5] + areas[6] + areas[7]) if area_raster: save_msg = "Saving Surface Area Raster to " + \ "{}.".format(area_raster) utils.msg(save_msg) arcpy.CopyRaster_management(total_area, area_raster) if not acr_correction: utils.msg("Calculating ratio with uncorrected planar area.") area_ratio = total_area / cell_size**2 else: utils.msg("Calculating ratio with slope-corrected planar area.") slope_raster = arcpy.sa.Slope(in_raster, "DEGREE", "1") planar_area = Divide(float(cell_size**2), Cos(Times(slope_raster, 0.01745))) area_ratio = Divide(total_area, planar_area) out_raster = utils.validate_path(out_raster) save_msg = "Saving Surface Area to Planar Area ratio to " + \ "{}.".format(out_raster) utils.msg(save_msg) arcpy.CopyRaster_management(area_ratio, out_raster) except Exception as e: utils.msg(e, mtype='error') try: # Delete all intermediate raster data sets utils.msg("Deleting intermediate data...") for path in temp_rasts: arcpy.Delete_management(path) except Exception as e: utils.msg(e, mtype='error')