def LCP(outputWeightCodedShp): """ Source dir: costRasters Output dir: LCPs """ weightCode = outputWeightCodedShp.split(".")[0] source = paths.join(paths.costRasters, weightCode + ".asc") output = paths.join(paths.LCPs, outputWeightCodedShp) print "Finding least cost paths for weighting {}".format(weightCode) if arcpy.Exists(output) and not redoExistingOutput: print "{} already exists, leaving it as is.".format(output) return output paths.setEnv(env) if arcpy.Raster(source).minimum is None: print " Calculating statistics for {}...".format(source) arcpy.CalculateStatistics_management(source, x_skip_factor=30, y_skip_factor=30) print " Calculating cost distance and backlink..." costDist = sa.CostDistance(paths.destination, source, out_backlink_raster="backlink") print " Finding least-cost path..." costPath = sa.CostPath(paths.sources, costDist, "backlink", "EACH_ZONE") print " Vectorizing..." arcpy.RasterToPolyline_conversion(costPath, output, simplify="SIMPLIFY") arcpy.Delete_management("backlink") return output
row = rows.next() while row: patchIDs.append(row.VALUE) row = rows.next() del row, rows # Loop through patches and and calculate least cost paths streamFC = "in_memory/LCPlines" first = True for patchID in patchIDs: msg("Working on patch %s of %s" % (patchID, len(patchIDs))) # Idenfity the cost and back link rasters cdRaster = os.path.join(CostDistWS, "CD_%s.img" % patchID) blRaster = os.path.join(CostDistWS, "BL_%s.img" % patchID) # Calculate least cost paths from all patches to the current patch lcpRaster = sa.CostPath(patchFix, cdRaster, blRaster, "EACH_ZONE") if first: lcpOutput = sa.Con(sa.IsNull(lcpRaster), 0, 1) first = False else: lcpTemp = sa.Con(sa.IsNull(lcpRaster), 0, 1) + lcpOutput lcpOutput = sa.Con(lcpTemp, 1, 0, "VALUE > 0") ''' # Convert the backlink to a flow direction raster #fdRaster = sa.Int(sa.Exp2(blRaster) / 2) # Convert the LCP raster to a vector if first: # If the first patch, save the streamsFC to the output FC file sa.StreamToFeature(lcpRaster,fdRaster,lcpFC,"NO_SIMPLIFY") first = False else: # Otherwise, create it and append it to the original sa.StreamToFeature(lcpRaster,fdRaster,streamFC,"NO_SIMPLIFY")
def get_centerline (feature, dem, workspace, power = 5, eu_cell_size = 10): """Returns a center line feature of the given polygon feature based on cost over an euclidean distance raster and cost path. points are seeded using minimum and maximum elevation.""" centerline = workspace + '\\centerline.shp' center_length = 0 center_slope = 0 smoothing = 4 trim_distance = "100 Meters" try: # Setup extents / environments for the current feature ARCPY.env.extent = feature.shape.extent desc = ARCPY.Describe(feature) XMin_new = desc.extent.XMin - 200 YMin_new = desc.extent.YMin - 200 XMax_new = desc.extent.XMax + 200 YMax_new = desc.extent.YMax + 200 ARCPY.env.extent = ARCPY.Extent(XMin_new, YMin_new, XMax_new, YMax_new) ARCPY.env.overwriteOutput = True ARCPY.env.cellSize = eu_cell_size ARCPY.env.snapRaster = dem # Get minimum and maximum points resample = ARCPY.Resample_management (dem, 'in_memory\\sample', eu_cell_size) masked_dem = spatial.ExtractByMask (resample, feature.shape) # Find the maximum elevation value in the feature, convert them to # points and then remove all but one. maximum = get_properties (masked_dem, 'MAXIMUM') maximum_raster = spatial.SetNull(masked_dem, masked_dem, 'VALUE <> ' + maximum) maximum_point = ARCPY.RasterToPoint_conversion(maximum_raster, 'in_memory\\max_point') rows = ARCPY.UpdateCursor (maximum_point) for row in rows: if row.pointid <> 1: rows.deleteRow(row) del row, rows # Find the minimum elevation value in the feature, convert them to # points and then remove all but one. minimum = get_properties (masked_dem, 'MINIMUM') minimum_raster = spatial.SetNull(masked_dem, masked_dem, 'VALUE <> ' + minimum) minimum_point = ARCPY.RasterToPoint_conversion(minimum_raster, 'in_memory\\min_point') rows = ARCPY.UpdateCursor (minimum_point) for row in rows: if row.pointid <> 1: rows.deleteRow(row) del row, rows # Calculate euclidean Distance to boundary line for input DEM cells. polyline = ARCPY.PolygonToLine_management(feature.shape, 'in_memory\\polyline') eucdist =spatial.EucDistance(polyline, "", eu_cell_size, '') masked_eucdist = spatial.ExtractByMask (eucdist, feature.shape) # Calculate the cost raster by inverting the euclidean distance results, # and raising it to the power of x to exaggerate the least expensive route. cost_raster = (-1 * masked_eucdist + float(maximum))**power # Run the cost distance and cost path function to find the path of least # resistance between the minimum and maximum values. The results are set # so all values equal 1 (different path segments have different values) # and convert the raster line to a poly-line. backlink = 'in_memory\\backlink' cost_distance = spatial.CostDistance(minimum_point, cost_raster, '', backlink) cost_path = spatial.CostPath(maximum_point, cost_distance, backlink, 'EACH_CELL', '') cost_path_ones = spatial.Con(cost_path, 1, '', 'VALUE > ' + str(-1)) # Set all resulting pixels to 1 r_to_p = ARCPY.RasterToPolyline_conversion (cost_path_ones, 'in_memory\\raster_to_polygon') del ARCPY.env.extent # Delete current extents (need here but do not know why) # Removes small line segments from the centerline shape. These segments are # a byproduct of cost analysis. lines = str(ARCPY.GetCount_management(r_to_p)) #check whether we have more than one line segment if float(lines) > 1: # If there is more then one line rows = ARCPY.UpdateCursor(r_to_p) for row in rows: if row.shape.length == eu_cell_size: # delete all the short 10 m lines rows.deleteRow(row) del row, rows lines = str(ARCPY.GetCount_management(r_to_p)) if float(lines) > 1: ARCPY.Snap_edit(r_to_p, [[r_to_p, "END", "50 Meters"]]) # make sure that the ends of the lines are connected r_to_p = ARCPY.Dissolve_management(r_to_p, 'in_memory\\raster_to_polygon_dissolve') # Smooth the resulting line. Currently smoothing is determined by minimum # and maximum distance. The greater change the greater the smoothing. smooth_tolerance = (float(maximum) - float(minimum)) / smoothing ARCPY.SmoothLine_cartography(r_to_p, centerline, 'PAEK', smooth_tolerance, 'FIXED_CLOSED_ENDPOINT', 'NO_CHECK') field_names = [] # List of field names in the file that will be deleted. fields_list = ARCPY.ListFields(centerline) for field in fields_list: # Loop through the field names if not field.required: # If they are not required append them to the list of field names. field_names.append(field.name) # Add new fields to the center line feature ARCPY.AddField_management(centerline, 'GLIMSID', 'TEXT', '', '', '25') ARCPY.AddField_management(centerline, 'LENGTH', 'FLOAT') ARCPY.AddField_management(centerline, 'SLOPE', 'FLOAT') ARCPY.DeleteField_management(centerline, field_names) # Remove the old fields. # Calculate the length of the line segment and populate segment data. ARCPY.CalculateField_management(centerline, 'LENGTH', 'float(!shape.length@meters!)', 'PYTHON') rows = ARCPY.UpdateCursor (centerline) for row in rows: row.GLIMSID = feature.GLIMSID # Get GLIMS ID and add it to segment center_length = row.LENGTH # Get the length of the center line # Calculate slope of the line based on change in elevation over length of line center_slope = round(math.degrees(math.atan((float(maximum) - float(minimum)) / row.LENGTH)), 2) row.SLOPE = center_slope # Write slope to Segment rows.updateRow(row) # Update the new entry del row, rows #Delete cursors and remove locks # Flip Line if needed - Turn min point and end point into a line segment if # the length of this line is greater then the threshold set, flip the line. end_point = ARCPY.FeatureVerticesToPoints_management(centerline, 'in_memory\\end_point', 'END') merged_points = ARCPY.Merge_management ([end_point, minimum_point], 'in_memory\\merged_points') merged_line = ARCPY.PointsToLine_management (merged_points, 'in_memory\\merged_line') merged_line_length = 0 # Get the line Length rows = ARCPY.SearchCursor (merged_line) for row in rows: merged_line_length += row.shape.length del row, rows # if the line length is greater then a quarter the entire feature length, flip if merged_line_length > (center_length/4): ARCPY.FlipLine_edit(centerline) # This function attempts to extend the line and clip it back to the # feature extents in order to create a line that runs from edge to edge #trimmed_line = ARCPY.Merge_management([polyline, centerline], 'in_memory\\line_merge') trimmed_line = ARCPY.Append_management (polyline, centerline, 'NO_TEST') ARCPY.TrimLine_edit (trimmed_line, trim_distance, "DELETE_SHORT") ARCPY.ExtendLine_edit(trimmed_line, trim_distance, "EXTENSION") rows = ARCPY.UpdateCursor (trimmed_line) for row in rows: if row.LENGTH == 0.0: rows.deleteRow(row) del row, rows # Recalculate length. Must be after 0.0 lengths are deleted or they will # not be removed above. ARCPY.CalculateField_management(centerline, 'LENGTH', 'float(!shape.length@meters!)', 'PYTHON') ARCPY.env.overwriteOutput = False return centerline, center_length, center_slope, False except: ARCPY.env.overwriteOutput = False return centerline, '', '', True
cdInt.save(os.path.join(saveRasterLocation,"CD_%s.img" %patchID)) # - Tabulate zonal stats for the other patches on the cost distance zStatTable = sa.ZonalStatisticsAsTable(patchRaster,"Value",costDist,"in_memory\zstattbl","DATA","MINIMUM") # - Write out edges to an edge list; setting to VALUE > patchID writes only the lower half of the matrix recs = arcpy.SearchCursor(zStatTable,"VALUE > %d" %patchID) rec = recs.next() while rec: outFile.write("%d,%d,%s\n" %(patchID, rec.VALUE, rec.MIN)) # - If asked to write LCPs, here we go if computeLCPs == 'true': ToPatchID = rec.VALUE if rec.MIN > 0: # Isolate the to-patch ToPatch = sa.SetNull(patchRaster,patchRaster,"VALUE <> %d" %ToPatchID) # Calculate the least cost path to the to_patch lcpRaster = sa.CostPath(ToPatch,costDist,backLink,"BEST_SINGLE") # Convert the raster to a feature lcpFeature = "in_memory/LCPfeature" result = arcpy.RasterToPolyline_conversion(lcpRaster,lcpFeature) # Dissolve the feature lcpDissolve = "in_memory/LCPdissolve" result = arcpy.Dissolve_management(lcpFeature,lcpDissolve) # Copy the features over to the LCP feature class cur = arcpy.InsertCursor(lcpFC) feat = cur.newRow() feat.shape = arcpy.SearchCursor(lcpDissolve).next().shape feat.FromID = patchID feat.ToID = ToPatchID feat.Cost = rec.MIN cur.insertRow(feat) del feat, cur
first = True for to_patch in patchIDs: # Idenfity the cost and back link rasters cdRaster = os.path.join(CostDistWS,"CD_%s.img" %to_patch) blRaster = os.path.join(CostDistWS,"BL_%s.img" %to_patch) # Loop through each from patch (skipping ones already processed...) for from_patch in patchIDs: if from_patch <= to_patch: continue msg("Creating least cost path from %s to %s" %(to_patch, from_patch)) # Extract the cost cost = edgeDict[(to_patch,from_patch)] # Isolate the to patch fromPatch = sa.SetNull(patchRaster,patchRaster,"VALUE <> %s" %from_patch) # Calculate least cost paths from all patches to the current patch lcpRaster = sa.CostPath(fromPatch,cdRaster,blRaster,"BEST_SINGLE") # Convert the backlink to a flow direction raster #fdRaster = sa.Int(sa.Exp2(blRaster) / 2) # Convert the LCP raster to a vector arcpy.RasterToPolyline_conversion(lcpRaster,streamFC,'ZERO',0,"NO_SIMPLIFY") #sa.StreamToFeature(lcpRaster,fdRaster,streamFC,"NO_SIMPLIFY") if first: # If the first patch, dissolve to the output FC file arcpy.Dissolve_management(streamFC,lcpFC) arcpy.AddField_management(lcpFC,"FromID","LONG",10) arcpy.AddField_management(lcpFC,"ToID","LONG",10) arcpy.AddField_management(lcpFC,"Cost","DOUBLE",10,2) arcpy.CalculateField_management(lcpFC,"FromID",from_patch) arcpy.CalculateField_management(lcpFC,"ToID",to_patch) arcpy.CalculateField_management(lcpFC,"Cost",cost) first = False else: # Otherwise, dissolve it and append it to the original