def workLinesMem(segment_info): """ New version of worklines. It uses memory workspace instead of shapefiles. The refactoring is to accelerate the processing speed. """ # input verification if segment_info is None or len(segment_info) <= 1: print("Input segment is corrupted, ignore") # Temporary files outWorkspace = flmc.GetWorkspace(workspaceName) # read params from text file f = open(outWorkspace + "\\params.txt") Forest_Line_Feature_Class = f.readline().strip() Cost_Raster = f.readline().strip() Line_Processing_Radius = float(f.readline().strip()) f.close() lineNo = segment_info[1] # second element is the line No. outWorkspaceMem = r"memory" arcpy.env.workspace = r"memory" fileSeg = os.path.join(outWorkspaceMem, "FLM_CL_Segment_" + str(lineNo)) fileOrigin = os.path.join(outWorkspaceMem, "FLM_CL_Origin_" + str(lineNo)) fileDestination = os.path.join(outWorkspaceMem, "FLM_CL_Destination_" + str(lineNo)) fileBuffer = os.path.join(outWorkspaceMem, "FLM_CL_Buffer_" + str(lineNo)) fileClip = os.path.join(outWorkspaceMem, "FLM_CL_Clip_" + str(lineNo) + ".tif") fileCostDist = os.path.join(outWorkspaceMem, "FLM_CL_CostDist_" + str(lineNo) + ".tif") fileCostBack = os.path.join(outWorkspaceMem, "FLM_CL_CostBack_" + str(lineNo) + ".tif") fileCenterline = os.path.join(outWorkspaceMem, "FLM_CL_Centerline_" + str(lineNo)) # Load segment list segment_list = [] for line in segment_info[0]: for point in line: # loops through every point in a line # loops through every vertex of every segment if point: # adds all the vertices to segment_list, which creates an array segment_list.append(point) # Find origin and destination coordinates x1 = segment_list[0].X y1 = segment_list[0].Y x2 = segment_list[-1].X y2 = segment_list[-1].Y # Create segment feature class try: arcpy.CreateFeatureclass_management(outWorkspaceMem, os.path.basename(fileSeg), "POLYLINE", Forest_Line_Feature_Class, "DISABLED", "DISABLED", Forest_Line_Feature_Class) cursor = arcpy.da.InsertCursor(fileSeg, ["SHAPE@"]) cursor.insertRow([segment_info[0]]) del cursor except Exception as e: print("Create feature class {} failed.".format(fileSeg)) print(e) return # Create origin feature class # TODO: not in use, delete later try: arcpy.CreateFeatureclass_management(outWorkspaceMem, os.path.basename(fileOrigin), "POINT", Forest_Line_Feature_Class, "DISABLED", "DISABLED", Forest_Line_Feature_Class) cursor = arcpy.da.InsertCursor(fileOrigin, ["SHAPE@XY"]) xy = (float(x1), float(y1)) cursor.insertRow([xy]) del cursor except Exception as e: print("Creating origin feature class failed: at X, Y" + str(xy) + ".") print(e) return # Create destination feature class # TODO: not in use, delete later try: arcpy.CreateFeatureclass_management(outWorkspaceMem, os.path.basename(fileDestination), "POINT", Forest_Line_Feature_Class, "DISABLED", "DISABLED", Forest_Line_Feature_Class) cursor = arcpy.da.InsertCursor(fileDestination, ["SHAPE@XY"]) xy = (float(x2), float(y2)) cursor.insertRow([xy]) del cursor except Exception as e: print("Creating destination feature class failed: at X, Y" + str(xy) + ".") print(e) return try: # Buffer around line arcpy.Buffer_analysis(fileSeg, fileBuffer, Line_Processing_Radius, "FULL", "ROUND", "NONE", "", "PLANAR") # Clip cost raster using buffer DescBuffer = arcpy.Describe(fileBuffer) SearchBox = str(DescBuffer.extent.XMin) + " " + str(DescBuffer.extent.YMin) + " " + \ str(DescBuffer.extent.XMax) + " " + str(DescBuffer.extent.YMax) arcpy.Clip_management(Cost_Raster, SearchBox, fileClip, fileBuffer, "", "ClippingGeometry", "NO_MAINTAIN_EXTENT") # Least cost path # arcpy.gp.CostDistance_sa(fileOrigin, fileClip, fileCostDist, "", fileCostBack, "", "", "", "", "TO_SOURCE") fileCostDist = CostDistance(arcpy.PointGeometry(arcpy.Point(x1, y1)), fileClip, "", fileCostBack) # print("Cost distance file path: {}".format(fileCostDist)) # arcpy.gp.CostPathAsPolyline_sa(fileDestination, fileCostDist, # fileCostBack, fileCenterline, "BEST_SINGLE", "") CostPathAsPolyline(arcpy.PointGeometry(arcpy.Point(x2, y2)), fileCostDist, fileCostBack, fileCenterline, "BEST_SINGLE", "") # get centerline polyline out of feature class file centerline = [] with arcpy.da.SearchCursor(fileCenterline, ["SHAPE@"]) as cursor: for row in cursor: centerline.append(row[0]) except Exception as e: print("Problem with line starting at X " + str(x1) + ", Y " + str(y1) + "; and ending at X " + str(x2) + ", Y " + str(y2) + ".") print(e) centerline = [] return centerline # Clean temporary files arcpy.Delete_management(fileSeg) arcpy.Delete_management(fileOrigin) arcpy.Delete_management(fileDestination) arcpy.Delete_management(fileBuffer) arcpy.Delete_management(fileClip) arcpy.Delete_management(fileCostDist) arcpy.Delete_management(fileCostBack) # Return centerline print("Processing line {} done".format(fileSeg)) return centerline, segment_info[2]
def main(argv=None): # Setup script path and workspace folder global workspaceName # workspaceName = "FLM_CL_output" global outWorkspace outWorkspace = flmc.SetupWorkspace(workspaceName) # outWorkspace = flmc.GetWorkspace(workspaceName) arcpy.env.workspace = outWorkspace arcpy.env.overwriteOutput = True # Load arguments from file if argv: args = argv else: args = flmc.GetArgs("FLM_CL_params.txt") # Tool arguments global Forest_Line_Feature_Class Forest_Line_Feature_Class = args[0].rstrip() global Cost_Raster Cost_Raster = args[1].rstrip() global Line_Processing_Radius Line_Processing_Radius = args[2].rstrip() ProcessSegments = args[3].rstrip() == "True" Out_Centerline = args[4].rstrip() # write params to text file f = open(outWorkspace + "\\params.txt", "w") f.write(Forest_Line_Feature_Class + "\n") f.write(Cost_Raster + "\n") f.write(Line_Processing_Radius + "\n") f.close() # Prepare input lines for multiprocessing fields = flmc.GetAllFieldsFromShp(Forest_Line_Feature_Class) segment_all = flmc.SplitLines(Forest_Line_Feature_Class, outWorkspace, "CL", ProcessSegments, fields) pool = multiprocessing.Pool(processes=flmc.GetCores()) flmc.log("Multiprocessing center lines...") flmc.log("Using {} CPU cores".format(flmc.GetCores())) centerlines = pool.map(workLinesMem, segment_all) pool.close() pool.join() flmc.logStep("Center line multiprocessing done.") # No line generated, exit if len(centerlines) <= 0: print("No lines generated, exit") return # Create output centerline shapefile flmc.log("Create centerline shapefile...") try: arcpy.CreateFeatureclass_management(os.path.dirname(Out_Centerline), os.path.basename(Out_Centerline), "POLYLINE", Forest_Line_Feature_Class, "DISABLED", "DISABLED", Forest_Line_Feature_Class) except Exception as e: print("Create feature class {} failed.".format(Out_Centerline)) print(e) return # Flatten centerlines which is a list of list flmc.log("Writing centerlines to shapefile...") cl_list = [] for sublist in centerlines: if len(sublist) > 0: for item in sublist[0]: cl_list.append([item, sublist[1]]) # arcpy.Merge_management(cl_list, Out_Centerline) with arcpy.da.InsertCursor(Out_Centerline, ["SHAPE@"] + fields) as cursor: for line in cl_list: row = [] for i in fields: row.append(line[1][i]) cursor.insertRow([line[0]] + row) # TODO: inspect CorridorTh # CorridorTh is added to footprint tool as new parameter # This can be removed after testing if arcpy.Exists(Out_Centerline): arcpy.AddField_management(Out_Centerline, "CorridorTh", "DOUBLE") arcpy.CalculateField_management(Out_Centerline, "CorridorTh", "3") flmc.log("Centerlines shapefile done")
def workLines(lineNo): # read params from text file outWorkspace = flmc.GetWorkspace(workspaceName) f = open(outWorkspace + "\\params.txt") outWorkspace = f.readline().strip() Centerline_Feature_Class = f.readline().strip() Canopy_Raster = f.readline().strip() Cost_Raster = f.readline().strip() Corridor_Threshold_Field = f.readline().strip() Maximum_distance_from_centerline = float(f.readline().strip()) Expand_And_Shrink_Cell_Range = f.readline().strip() f.close() # Temporary files fileSeg = outWorkspace + "\\FLM_LFP_Segment_" + str(lineNo) + ".shp" fileOrigin = outWorkspace + "\\FLM_LFP_Origin_" + str(lineNo) + ".shp" fileDestination = outWorkspace + "\\FLM_LFP_Destination_" + str( lineNo) + ".shp" fileBuffer = outWorkspace + "\\FLM_LFP_Buffer_" + str(lineNo) + ".shp" fileClip = outWorkspace + "\\FLM_LFP_Clip_" + str(lineNo) + ".tif" fileCostDa = outWorkspace + "\\FLM_LFP_CostDa_" + str(lineNo) + ".tif" fileCostDb = outWorkspace + "\\FLM_LFP_CostDb_" + str(lineNo) + ".tif" fileCorridor = outWorkspace + "\\FLM_LFP_Corridor_" + str(lineNo) + ".tif" fileCorridorMin = outWorkspace + "\\FLM_LFP_CorridorMin_" + str( lineNo) + ".tif" fileThreshold = outWorkspace + "\\FLM_LFP_Threshold_" + str( lineNo) + ".tif" fileExpand = outWorkspace + "\\FLM_LFP_Expand_" + str(lineNo) + ".tif" fileShrink = outWorkspace + "\\FLM_LFP_Shrink_" + str(lineNo) + ".tif" fileClean = outWorkspace + "\\FLM_LFP_Clean_" + str(lineNo) + ".tif" fileNull = outWorkspace + "\\FLM_LFP_Null_" + str(lineNo) + ".tif" fileFootprint = outWorkspace + "\\FLM_LFP_Footprint_" + str( lineNo) + ".shp" # When segment file is missing, just quit and log message # This segment will be recorded for later re-processing if not arcpy.Exists(fileSeg): print("{} doesn't exist, ignore.".format(fileSeg)) return # Load segment list segment_list = [] rows = arcpy.SearchCursor(fileSeg) shapeField = arcpy.Describe(fileSeg).ShapeFieldName for row in rows: feat = row.getValue(shapeField) # creates a geometry object Corridor_Threshold = float(row.getValue(Corridor_Threshold_Field)) segmentnum = 0 for segment in feat: # loops through every segment in a line # loops through every vertex of every segment for pnt in feat.getPart( segmentnum ): # get.PArt returns an array of points for a particular part in the geometry if pnt: # adds all the vertices to segment_list, which creates an array segment_list.append(arcpy.Point(float(pnt.X), float(pnt.Y))) segmentnum += 1 del rows # Find origin and destination coordinates x1 = segment_list[0].X y1 = segment_list[0].Y x2 = segment_list[-1].X y2 = segment_list[-1].Y # Create origin feature class try: arcpy.CreateFeatureclass_management(outWorkspace, PathFile(fileOrigin), "POINT", Centerline_Feature_Class, "DISABLED", "DISABLED", Centerline_Feature_Class) cursor = arcpy.da.InsertCursor(fileOrigin, ["SHAPE@XY"]) xy = (float(x1), float(y1)) cursor.insertRow([xy]) del cursor except Exception as e: print("Create feature class {} failed.".format(fileOrigin)) print(e) return # Create destination feature class try: arcpy.CreateFeatureclass_management(outWorkspace, PathFile(fileDestination), "POINT", Centerline_Feature_Class, "DISABLED", "DISABLED", Centerline_Feature_Class) cursor = arcpy.da.InsertCursor(fileDestination, ["SHAPE@XY"]) xy = (float(x2), float(y2)) cursor.insertRow([xy]) del cursor except Exception as e: print("Create feature class {} failed.".format(fileDestination)) print(e) return # Buffer around line try: arcpy.Buffer_analysis(fileSeg, fileBuffer, Maximum_distance_from_centerline, "FULL", "ROUND", "NONE", "", "PLANAR") except Exception as e: print("Create buffer for {} failed".format(fileSeg)) print(e) return # Clip cost raster using buffer DescBuffer = arcpy.Describe(fileBuffer) SearchBox = str(DescBuffer.extent.XMin) + " " + str( DescBuffer.extent.YMin) + " " + str( DescBuffer.extent.XMax) + " " + str(DescBuffer.extent.YMax) arcpy.Clip_management(Cost_Raster, SearchBox, fileClip, fileBuffer, "", "ClippingGeometry", "NO_MAINTAIN_EXTENT") # Process: Cost Distance arcpy.gp.CostDistance_sa(fileOrigin, fileClip, fileCostDa, "", "", "", "", "", "", "TO_SOURCE") arcpy.gp.CostDistance_sa(fileDestination, fileClip, fileCostDb, "", "", "", "", "", "", "TO_SOURCE") # Process: Corridor arcpy.gp.Corridor_sa(fileCostDa, fileCostDb, fileCorridor) # Calculate minimum value of corridor raster RasterCorridor = arcpy.Raster(fileCorridor) CorrMin = float(RasterCorridor.minimum) # Set minimum as zero and save minimum file RasterCorridor = ((RasterCorridor - CorrMin) > Corridor_Threshold) RasterCorridor.save(fileCorridorMin) # Process: Stamp CC and Max Line Width RasterClass = SetNull(IsNull(Raster(fileCorridorMin)), (Raster(fileCorridorMin) + (Raster(Canopy_Raster) >= 1)) > 0) RasterClass.save(fileThreshold) del RasterCorridor, RasterClass if (int(Expand_And_Shrink_Cell_Range) > 0): # Process: Expand arcpy.gp.Expand_sa(fileThreshold, fileExpand, Expand_And_Shrink_Cell_Range, "1") # Process: Shrink arcpy.gp.Shrink_sa(fileExpand, fileShrink, Expand_And_Shrink_Cell_Range, "1") else: fileShrink = fileThreshold # Process: Boundary Clean arcpy.gp.BoundaryClean_sa(fileShrink, fileClean, "ASCEND", "ONE_WAY") # arcpy.gp.BoundaryClean_sa(fileShrink, fileClean, "NO_SORT", "ONE_WAY") # This is original code # Process: Set Null arcpy.gp.SetNull_sa(fileClean, "1", fileNull, "VALUE > 0") # Process: Raster to Polygon arcpy.RasterToPolygon_conversion(fileNull, fileFootprint, "SIMPLIFY", "VALUE", "SINGLE_OUTER_PART", "") flmc.log("Processing line {} done".format(fileSeg)) # Clean temporary files try: arcpy.Delete_management(fileSeg) arcpy.Delete_management(fileOrigin) arcpy.Delete_management(fileDestination) arcpy.Delete_management(fileBuffer) arcpy.Delete_management(fileClip) arcpy.Delete_management(fileCostDa) arcpy.Delete_management(fileCostDb) arcpy.Delete_management(fileThreshold) arcpy.Delete_management(fileCorridor) arcpy.Delete_management(fileCorridorMin) arcpy.Delete_management(fileExpand) arcpy.Delete_management(fileShrink) arcpy.Delete_management(fileClean) arcpy.Delete_management(fileNull) except Exception as e: print("Line Footprint: Deleting temporary file failed. Inspect later.")
try: input("\n<Press any key to exit>") except Exception as e: print("") if __name__ != '__main__': # If script is one of the child processes (multiprocessing) load associated scripts # (otherwise parallel processing is avoided) import Scripts.FLM_CenterLine import Scripts.FLM_LineFootprint import Scripts.FLM_Corridor import Scripts.FLM_CorridorFootprint import Scripts.FLM_ZonalThreshold import Scripts.FLM_ForestLineAttributes else: # If script is main process, load and show GUI vfile = open(scriptPath + "\\Scripts\\FLM_VERSION", "r") args = vfile.readlines() vfile.close() version = args[0] flmc.newLog(version) print("-\nFLM Copyright (C) 2021 Applied Geospatial Research Group") print("This program comes with ABSOLUTELY NO WARRANTY;\n" "This is free software, and you are welcome to redistribute it under certain conditions;\n" "See the license file distributed along with this program for details.") main()
def workLinesMemory(segment_info): """ New version of worklines. It uses memory workspace instead of shapefiles. The refactoring is to accelerate the processing speed. """ # input verification if segment_info is None or len(segment_info) <= 1: print("Input segment is corrupted, ignore") # read params from text file outWorkspace = flmc.GetWorkspace(workspaceName) f = open(outWorkspace + "\\params.txt") outWorkspace = f.readline().strip() Centerline_Feature_Class = f.readline().strip() Canopy_Raster = f.readline().strip() Cost_Raster = f.readline().strip() Corridor_Threshold_Field = f.readline().strip() Corridor_Threshold = float(f.readline().strip()) Maximum_distance_from_centerline = float(f.readline().strip()) Expand_And_Shrink_Cell_Range = f.readline().strip() f.close() lineNo = segment_info[1] # second element is the line No. outWorkspaceMem = r"memory" # Temporary files fileSeg = os.path.join(outWorkspaceMem, "FLM_LFP_Segment_" + str(lineNo)) fileOrigin = os.path.join(outWorkspaceMem, "FLM_LFP_Origin_" + str(lineNo)) fileDestination = os.path.join(outWorkspaceMem, "FLM_LFP_Destination_" + str(lineNo)) fileBuffer = os.path.join(outWorkspaceMem, "FLM_LFP_Buffer_" + str(lineNo)) fileClip = os.path.join(outWorkspaceMem, "FLM_LFP_Clip_" + str(lineNo) + ".tif") fileCostDa = os.path.join(outWorkspaceMem, "FLM_LFP_CostDa_" + str(lineNo) + ".tif") fileCostDb = os.path.join(outWorkspaceMem, "FLM_LFP_CostDb_" + str(lineNo) + ".tif") fileCorridor = os.path.join(outWorkspaceMem, "FLM_LFP_Corridor_" + str(lineNo) + ".tif") fileCorridorMin = os.path.join( outWorkspaceMem, "FLM_LFP_CorridorMin_" + str(lineNo) + ".tif") fileThreshold = os.path.join(outWorkspaceMem, "FLM_LFP_Threshold_" + str(lineNo) + ".tif") fileExpand = os.path.join(outWorkspaceMem, "FLM_LFP_Expand_" + str(lineNo) + ".tif") fileShrink = os.path.join(outWorkspaceMem, "FLM_LFP_Shrink_" + str(lineNo) + ".tif") fileClean = os.path.join(outWorkspaceMem, "FLM_LFP_Clean_" + str(lineNo) + ".tif") fileNull = os.path.join(outWorkspaceMem, "FLM_LFP_Null_" + str(lineNo) + ".tif") # Load segment list segment_list = [] for line in segment_info[0]: for point in line: # loops through every point in a line # loops through every vertex of every segment if point: # adds all the vertices to segment_list, which creates an array segment_list.append(point) # Find origin and destination coordinates x1 = segment_list[0].X y1 = segment_list[0].Y x2 = segment_list[-1].X y2 = segment_list[-1].Y # Create segment feature class try: arcpy.CreateFeatureclass_management(outWorkspaceMem, os.path.basename(fileSeg), "POLYLINE", Centerline_Feature_Class, "DISABLED", "DISABLED", Centerline_Feature_Class) cursor = arcpy.da.InsertCursor(fileSeg, ["SHAPE@"]) cursor.insertRow([segment_info[0]]) del cursor except Exception as e: print("Create feature class {} failed.".format(fileSeg)) print(e) return # Create origin feature class try: arcpy.CreateFeatureclass_management(outWorkspaceMem, os.path.basename(fileOrigin), "POINT", Centerline_Feature_Class, "DISABLED", "DISABLED", Centerline_Feature_Class) cursor = arcpy.da.InsertCursor(fileOrigin, ["SHAPE@XY"]) xy = (float(x1), float(y1)) cursor.insertRow([xy]) del cursor except Exception as e: print("Create feature class {} failed.".format(fileOrigin)) print(e) return # Create destination feature class try: arcpy.CreateFeatureclass_management(outWorkspaceMem, os.path.basename(fileDestination), "POINT", Centerline_Feature_Class, "DISABLED", "DISABLED", Centerline_Feature_Class) cursor = arcpy.da.InsertCursor(fileDestination, ["SHAPE@XY"]) xy = (float(x2), float(y2)) cursor.insertRow([xy]) del cursor except Exception as e: print("Create feature class {} failed.".format(fileDestination)) print(e) return # Buffer around line try: arcpy.Buffer_analysis(fileSeg, fileBuffer, Maximum_distance_from_centerline, "FULL", "ROUND", "NONE", "", "PLANAR") except Exception as e: print("Create buffer for {} failed".format(fileSeg)) print(e) return # Clip cost raster using buffer DescBuffer = arcpy.Describe(fileBuffer) SearchBox = str(DescBuffer.extent.XMin) + " " + str( DescBuffer.extent.YMin) + " " + str( DescBuffer.extent.XMax) + " " + str(DescBuffer.extent.YMax) arcpy.Clip_management(Cost_Raster, SearchBox, fileClip, fileBuffer, "", "ClippingGeometry", "NO_MAINTAIN_EXTENT") try: # Process: Cost Distance arcpy.gp.CostDistance_sa(fileOrigin, fileClip, fileCostDa, "", "", "", "", "", "", "TO_SOURCE") arcpy.gp.CostDistance_sa(fileDestination, fileClip, fileCostDb, "", "", "", "", "", "", "TO_SOURCE") # Process: Corridor arcpy.gp.Corridor_sa(fileCostDa, fileCostDb, fileCorridor) except Exception as e: print(e) footprint = [] # Calculate minimum value of corridor raster try: RasterCorridor = arcpy.Raster(fileCorridor) if not RasterCorridor.minimum is None: CorrMin = float(RasterCorridor.minimum) else: print("Line segment {} error: RasterCorridor.minimum is None", lineNo) CorrMin = 0 # Set minimum as zero and save minimum file RasterCorridor = ((RasterCorridor - CorrMin) > Corridor_Threshold) RasterCorridor.save(fileCorridorMin) # Process: Stamp CC and Max Line Width RasterClass = SetNull(IsNull(Raster(fileCorridorMin)), (Raster(fileCorridorMin) + (Raster(Canopy_Raster) >= 1)) > 0) RasterClass.save(fileThreshold) del RasterCorridor, RasterClass if (int(Expand_And_Shrink_Cell_Range) > 0): # Process: Expand arcpy.gp.Expand_sa(fileThreshold, fileExpand, Expand_And_Shrink_Cell_Range, "1") # Process: Shrink arcpy.gp.Shrink_sa(fileExpand, fileShrink, Expand_And_Shrink_Cell_Range, "1") else: fileShrink = fileThreshold # Process: Boundary Clean arcpy.gp.BoundaryClean_sa(fileShrink, fileClean, "ASCEND", "ONE_WAY") # arcpy.gp.BoundaryClean_sa(fileShrink, fileClean, "NO_SORT", "ONE_WAY") # This is original code # Process: Set Null arcpy.gp.SetNull_sa(fileClean, "1", fileNull, "VALUE > 0") # Process: Raster to Polygon footprint = arcpy.RasterToPolygon_conversion(fileNull, arcpy.Geometry(), "SIMPLIFY", "VALUE", "MULTIPLE_OUTER_PART", "") except Exception as e: print(e) flmc.log("Processing line {} done".format(fileSeg)) # Clean temporary files try: arcpy.Delete_management(fileSeg) arcpy.Delete_management(fileOrigin) arcpy.Delete_management(fileDestination) arcpy.Delete_management(fileBuffer) arcpy.Delete_management(fileClip) arcpy.Delete_management(fileCostDa) arcpy.Delete_management(fileCostDb) arcpy.Delete_management(fileThreshold) arcpy.Delete_management(fileCorridor) arcpy.Delete_management(fileCorridorMin) arcpy.Delete_management(fileExpand) arcpy.Delete_management(fileShrink) arcpy.Delete_management(fileClean) arcpy.Delete_management(fileNull) except Exception as e: print("Line Footprint: Deleting temporary file failed. Inspect later.") return footprint # list of polygons
def main(argv=None): # Setup script path and workspace folder workspaceName = "FLM_LFP_output" global outWorkspace outWorkspace = flmc.GetWorkspace(workspaceName) arcpy.env.workspace = outWorkspace arcpy.env.overwriteOutput = True # Load arguments from file if argv: args = argv else: args = flmc.GetArgs("FLM_LFP_params.txt") # Tool arguments global Centerline_Feature_Class Centerline_Feature_Class = args[0].rstrip() global Canopy_Raster Canopy_Raster = args[1].rstrip() global Cost_Raster Cost_Raster = args[2].rstrip() global Corridor_Threshold_Field Corridor_Threshold_Field = args[3].rstrip() global Corridor_Threshold Corridor_Threshold = args[4].rstrip() global Maximum_distance_from_centerline Maximum_distance_from_centerline = float(args[5].rstrip()) / 2.0 global Expand_And_Shrink_Cell_Range Expand_And_Shrink_Cell_Range = args[6].rstrip() global ProcessSegments ProcessSegments = args[7].rstrip() == "True" global Output_Footprint Output_Footprint = args[8].rstrip() outWorkspace = flmc.SetupWorkspace(workspaceName) # write params to text file for use in function workLinesMemory f = open(outWorkspace + "\\params.txt", "w") f.write(outWorkspace + "\n") f.write(Centerline_Feature_Class + "\n") f.write(Canopy_Raster + "\n") f.write(Cost_Raster + "\n") f.write(Corridor_Threshold_Field + "\n") f.write(Corridor_Threshold + "\n") f.write(str(Maximum_distance_from_centerline) + "\n") f.write(Expand_And_Shrink_Cell_Range + "\n") f.close() # TODO: this code block is not necessary if not HasField(Centerline_Feature_Class, Corridor_Threshold_Field): flmc.log("ERROR: There is no field named " + Corridor_Threshold_Field + " in the input lines") return False # Prepare input lines for multiprocessing segment_all = flmc.SplitLines(Centerline_Feature_Class, outWorkspace, "LFP", ProcessSegments, Corridor_Threshold_Field) # TODO: inspect how GetCores works. Make sure it uses all the CPU cores pool = multiprocessing.Pool(processes=flmc.GetCores()) flmc.log("Multiprocessing line corridors...") flmc.log("Using {} CPU cores".format(flmc.GetCores())) footprints = pool.map( workLinesMemory, segment_all) # new version of memory based processing pool.close() pool.join() flmc.logStep("Corridor multiprocessing") flmc.log("Merging footprints...") try: # Flatten footprints which is a list of list ft_list = [item for sublist in footprints for item in sublist] fileMerge = outWorkspace + "\\FLM_LFP_Merge.shp" arcpy.Merge_management(ft_list, fileMerge) arcpy.Dissolve_management(fileMerge, Output_Footprint) # arcpy.Delete_management(fileMerge) except Exception as e: print("e") flmc.logStep("Footprints merged.")
def main(argv=None): # Setup script path and workspace folder workspaceName = "FLM_PT_output" global outWorkspace outWorkspace = flmc.GetWorkspace(workspaceName) arcpy.env.workspace = outWorkspace arcpy.env.overwriteOutput = True # Load arguments from file if argv: args = argv else: args = flmc.GetArgs("FLM_PT_params.txt") # Tool arguments global Centerline_Feature_Class Centerline_Feature_Class = args[0].rstrip() global In_CHM In_CHM = args[1].rstrip() global Canopy_Raster Canopy_Raster = args[2].rstrip() global Cost_Raster Cost_Raster = args[3].rstrip() global In_Lidar_Year In_Lidar_Year = args[4].rstrip() global Maximum_distance_from_centerline Maximum_distance_from_centerline = float(args[5].rstrip()) / 2.0 global Out_Tagged_Line Out_Tagged_Line = args[6].rstrip() outWorkspace = flmc.SetupWorkspace(workspaceName) # write params to text file f = open(outWorkspace + "\\params.txt", "w") f.write(outWorkspace + "\n") f.write(Centerline_Feature_Class + "\n") f.write(In_CHM + "\n") f.write(Canopy_Raster + "\n") f.write(Cost_Raster + "\n") f.write(In_Lidar_Year + "\n") f.write(str(Maximum_distance_from_centerline) + "\n") f.write(Out_Tagged_Line + "\n") f.close() # Remind if Status field is already in Shapefile if HasField(Centerline_Feature_Class, "Status"): print("{} has Status field, it will be overwritten.".format( Centerline_Feature_Class)) polygons = retrievePolygons(In_Lidar_Year) # Prepare input lines for multiprocessing fields = flmc.GetAllFieldsFromShp(Centerline_Feature_Class) global ProcessSegments segment_all = flmc.SplitLines(Centerline_Feature_Class, outWorkspace, "LFP", ProcessSegments, fields, polygons) # TODO: inspect how GetCores works. Make sure it uses all the CPU cores pool = multiprocessing.Pool(processes=flmc.GetCores()) flmc.log("Multiprocessing line corridors...") flmc.log("Using {} CPU cores".format(flmc.GetCores())) tagged_lines = pool.map( workLinesMem, segment_all) # new version of memory based processing pool.close() pool.join() flmc.logStep("Tagging lines multiprocessing") flmc.log("Write lines with existence and all statistics...") try: # create tagged line feature class with stats (root, ext) = os.path.splitext(Out_Tagged_Line) root = root + "_stats" out_tagged_line_stats = root + ext arcpy.CreateFeatureclass_management( os.path.dirname(out_tagged_line_stats), os.path.basename(out_tagged_line_stats), "Polyline", "", "DISABLED", "DISABLED", Centerline_Feature_Class) arcpy.AddField_management(out_tagged_line_stats, "Status", "TEXT") arcpy.AddField_management(out_tagged_line_stats, "Mean", "DOUBLE") arcpy.AddField_management(out_tagged_line_stats, "Median", "DOUBLE") arcpy.AddField_management(out_tagged_line_stats, "Variance", "DOUBLE") arcpy.AddField_management(out_tagged_line_stats, "Stdev", "DOUBLE") arcpy.AddField_management(out_tagged_line_stats, "Mean_B", "DOUBLE") arcpy.AddField_management(out_tagged_line_stats, "Median_B", "DOUBLE") arcpy.AddField_management(out_tagged_line_stats, "Variance_B", "DOUBLE") arcpy.AddField_management(out_tagged_line_stats, "Stdev_B", "DOUBLE") fields_stats = [ "SHAPE@", "Status", "Mean", "Median", "Variance", "Stdev", "Mean_B", "Median_B", "Variance_B", "Stdev_B" ] with arcpy.da.InsertCursor(out_tagged_line_stats, fields_stats) as cursor: for line in tagged_lines: cursor.insertRow([line[0]] + list(line[1])) # arcpy.Delete_management(fileMerge) except Exception as e: print(e) flmc.log("Writing lines with existence and all original attributes.") try: # create tagged line feature class with existence and all original attributes arcpy.CreateFeatureclass_management(os.path.dirname(Out_Tagged_Line), os.path.basename(Out_Tagged_Line), "Polyline", Centerline_Feature_Class, "DISABLED", "DISABLED", Centerline_Feature_Class) # Add Status field to indicate line existence type: confirmed, unconfirmed and invisible status_appended = False if not HasField(Centerline_Feature_Class, "Status"): arcpy.AddField_management(Out_Tagged_Line, "Status", "TEXT") fields.append("Status") status_appended = True with arcpy.da.InsertCursor(Out_Tagged_Line, ["Shape@"] + fields) as cursor: for line in tagged_lines: row = [] for i in fields: if i != "Status": row.append(line[2][i]) # Line status if status_appended: row.append(line[1][0]) else: # overwrite Status value row.insert(fields.index("Status"), line[1][0]) cursor.insertRow([line[0]] + row) except Exception as e: print(e) flmc.logStep("Tagged lines output done.")
def main(argv=None): # Setup script path and output folder outWorkspace = flmc.SetupWorkspace("FLM_CC_output") arcpy.env.workspace = outWorkspace arcpy.env.scratchWorkspace = outWorkspace arcpy.env.overwriteOutput = True # Load arguments from file if argv: args = argv else: args = flmc.GetArgs("FLM_CC_params.txt") # Tool arguments CHM_Raster = args[0].rstrip() Min_Canopy_Height = float(args[1].rstrip()) Tree_Search_Area = "Circle " + str(float(args[2].rstrip())) + " MAP" Max_Line_Distance = float(args[3].rstrip()) CanopyAvoidance = float(args[4].rstrip()) Cost_Raster_Exponent = float(args[5].rstrip()) Output_Canopy_Raster = args[6].rstrip() Output_Cost_Raster = args[7].rstrip() # Local variables: FLM_CC_EucRaster = outWorkspace + "\\FLM_CC_EucRaster.tif" FLM_CC_SmoothRaster = outWorkspace + "\\FLM_CC_SmoothRaster.tif" FLM_CC_Mean = outWorkspace + "\\FLM_CC_Mean.tif" FLM_CC_StDev = outWorkspace + "\\FLM_CC_StDev.tif" FLM_CC_CostRaster = outWorkspace + "\\FLM_CC_CostRaster.tif" # Process: Turn CHM into a Canopy Closure (CC) map flmc.log("Applying height threshold to CHM...") arcpy.gp.Con_sa(CHM_Raster, 1, Output_Canopy_Raster, 0, "VALUE > " + str(Min_Canopy_Height)) flmc.logStep("Height threshold") # Process: CC Mean flmc.log("Calculating Focal Mean...") arcpy.gp.FocalStatistics_sa(Output_Canopy_Raster, FLM_CC_Mean, Tree_Search_Area, "MEAN") flmc.logStep("Focal Mean") # Process: CC StDev flmc.log("Calculating Focal StDev..") arcpy.gp.FocalStatistics_sa(Output_Canopy_Raster, FLM_CC_StDev, Tree_Search_Area, "STD") flmc.logStep("Focal StDev") # Process: Euclidean Distance flmc.log("Calculating Euclidean Distance From Canopy...") EucAllocation(Con(arcpy.Raster(Output_Canopy_Raster) >= 1, 1, ""), "", "", "", "", FLM_CC_EucRaster, "") smoothCost = (float(Max_Line_Distance) - arcpy.Raster(FLM_CC_EucRaster)) smoothCost = Con(smoothCost > 0, smoothCost, 0) / float(Max_Line_Distance) smoothCost.save(FLM_CC_SmoothRaster) flmc.logStep("Euclidean Distance") # Process: Euclidean Distance flmc.log("Calculating Cost Raster...") arcpy.env.compression = "NONE" Raster_CC = arcpy.Raster(Output_Canopy_Raster) Raster_Mean = arcpy.Raster(FLM_CC_Mean) Raster_StDev = arcpy.Raster(FLM_CC_StDev) Raster_Smooth = arcpy.Raster(FLM_CC_SmoothRaster) avoidance = max(min(float(CanopyAvoidance), 1), 0) # TODO: shorten following sentence outRas = Power( Exp( Con((Raster_CC == 1), 1, Con((Raster_Mean + Raster_StDev <= 0), 0, (1 + (Raster_Mean - Raster_StDev) / (Raster_Mean + Raster_StDev)) / 2) * (1 - avoidance) + Raster_Smooth * avoidance)), float(Cost_Raster_Exponent)) outRas.save(FLM_CC_CostRaster) flmc.logStep("Cost Raster") flmc.log("Saving Outputs...") arcpy.CopyRaster_management(outRas, Output_Cost_Raster, "DEFAULTS") arcpy.ClearWorkspaceCache_management()
def main(argv=None): # Setup script path and workspace folder global outWorkspace outWorkspace = flmc.SetupWorkspace(workspaceName) arcpy.env.workspace = outWorkspace arcpy.env.overwriteOutput = True # Load arguments from file if argv: args = argv else: args = flmc.GetArgs("FLM_FLA_params.txt") # Tool arguments Input_Lines = args[0].rstrip() Input_Footprint = args[1].rstrip() Input_CHM = args[2].rstrip() SamplingType = args[3].rstrip() Segment_Length = float(args[4].rstrip()) Tolerance_Radius = float(args[5].rstrip()) LineSearchRadius = float(args[6].rstrip()) Attributed_Segments = args[7].rstrip() areaAnalysis = arcpy.Exists(Input_Footprint) heightAnalysis = arcpy.Exists(Input_CHM) # write params to text file f = open(outWorkspace + "\\params.txt", "w") f.write(outWorkspace + "\n") f.write(Input_Lines + "\n") f.write(Input_Footprint + "\n") f.write(Input_CHM + "\n") f.write(SamplingType + "\n") f.write(str(Segment_Length) + "\n") f.write(str(Tolerance_Radius) + "\n") f.write(str(LineSearchRadius) + "\n") f.write(Attributed_Segments + "\n") f.write(str(areaAnalysis) + "\n") f.write(str(heightAnalysis) + "\n") f.close() # Only process the following SampleingType if SamplingType not in [ "IN-FEATURES", "WHOLE-LINE", "LINE-CROSSINGS", "ARBITRARY" ]: print("SamplingType is not correct, please verify it.") return # Temporary layers fileBuffer = outWorkspace + "\\FLM_SLA_Buffer.shp" fileIdentity = outWorkspace + "\\FLM_SLA_Identity.shp" fileFootprints = outWorkspace + "\\FLM_SLA_Footprints.shp" footprintField = flmc.FileToField(fileBuffer) flmc.log("Preparing line segments...") # Segment lines flmc.log("FlmLineSplit: Input_Lines = " + Input_Lines) # Get all original fields keepFields = flmc.GetAllFieldsFromShp(Input_Lines) SLA_Segmented_Lines = flma.FlmLineSplit(outWorkspace, Input_Lines, SamplingType, Segment_Length, Tolerance_Radius) flmc.logStep("Line segmentation") # Linear attributes flmc.log("Adding attributes...") arcpy.AddGeometryAttributes_management(SLA_Segmented_Lines, "LENGTH;LINE_BEARING", "METERS") keepFields += ["LENGTH", "BEARING"] if areaAnalysis: arcpy.Buffer_analysis(SLA_Segmented_Lines, fileBuffer, LineSearchRadius, line_side="FULL", line_end_type="FLAT", dissolve_option="NONE", dissolve_field="", method="PLANAR") arcpy.Identity_analysis(Input_Footprint, fileBuffer, fileIdentity, join_attributes="ONLY_FID", cluster_tolerance="", relationship="NO_RELATIONSHIPS") arcpy.Dissolve_management(fileIdentity, fileFootprints, dissolve_field=footprintField, statistics_fields="", multi_part="MULTI_PART", unsplit_lines="DISSOLVE_LINES") arcpy.JoinField_management(fileFootprints, footprintField, fileBuffer, arcpy.Describe(fileBuffer).OIDFieldName, fields="ORIG_FID") fCursor = arcpy.UpdateCursor(fileFootprints) for row in fCursor: if float(row.getValue(footprintField)) < 0: fCursor.deleteRow(row) del fCursor arcpy.AddGeometryAttributes_management( fileFootprints, Geometry_Properties="AREA;PERIMETER_LENGTH", Length_Unit="METERS", Area_Unit="SQUARE_METERS", Coordinate_System="") arcpy.JoinField_management( SLA_Segmented_Lines, arcpy.Describe(SLA_Segmented_Lines).OIDFieldName, fileFootprints, "ORIG_FID", fields="POLY_AREA;PERIMETER") keepFields += ["POLY_AREA", "PERIMETER"] arcpy.Delete_management(fileBuffer) arcpy.Delete_management(fileIdentity) arcpy.Delete_management(fileFootprints) # Add other fields arcpy.AddField_management(SLA_Segmented_Lines, "Direction", "TEXT") arcpy.AddField_management(SLA_Segmented_Lines, "Sinuosity", "DOUBLE") keepFields += ["Direction", "Sinuosity"] if areaAnalysis: arcpy.AddField_management(SLA_Segmented_Lines, "AvgWidth", "DOUBLE") arcpy.AddField_management(SLA_Segmented_Lines, "Fragment", "DOUBLE") keepFields += ["AvgWidth", "Fragment"] if heightAnalysis: arcpy.AddField_management(SLA_Segmented_Lines, "AvgHeight", "DOUBLE") arcpy.AddField_management(SLA_Segmented_Lines, "Volume", "DOUBLE") arcpy.AddField_management(SLA_Segmented_Lines, "Roughness", "DOUBLE") keepFields += ["AvgHeight", "Volume", "Roughness"] # Prepare input lines for multiprocessing # ["Direction","Sinuosity","Area","AvgWidth","Perimeter","Fragment","SLA_Unity","AvgHeight","Volume","Roughness"]) segment_all = flmc.SplitLines(SLA_Segmented_Lines, outWorkspace, "SLA", False, keepFields) pool = multiprocessing.Pool(processes=flmc.GetCores()) flmc.log("Multiprocessing lines...") # pool.map(workLinesMem, range(1, numLines + 1)) line_with_attributes = pool.map(workLinesMem, segment_all) pool.close() pool.join() flmc.logStep("Line attributes multiprocessing") # Create output line attribute shapefile flmc.log("Create line attribute shapefile...") try: arcpy.CreateFeatureclass_management( os.path.dirname(Attributed_Segments), os.path.basename(Attributed_Segments), "POLYLINE", SLA_Segmented_Lines, "DISABLED", "DISABLED", SLA_Segmented_Lines) except Exception as e: print("Create feature class {} failed.".format(Attributed_Segments)) print(e) return # Flatten line attribute which is a list of list flmc.log("Writing line attributes to shapefile...") # TODO: is this necessary? Since we need list of single line next # la_list = [item for sublist in line_with_attributes for item in sublist] with arcpy.da.InsertCursor(Attributed_Segments, ["SHAPE@"] + keepFields) as cursor: for line in line_with_attributes: row = [] for fld in keepFields: row.append(line[1][fld]) cursor.insertRow(line[0] + row) flmc.logStep("Line attribute file: {} done".format(Attributed_Segments))
def workLines(lineNo): outWorkspace = flmc.GetWorkspace(workspaceName) f = open(outWorkspace + "\\params.txt") outWorkspace = f.readline().strip() Input_Lines = f.readline().strip() Input_Footprint = f.readline().strip() Input_CHM = f.readline().strip() SamplingType = f.readline().strip() Segment_Length = float(f.readline().strip()) Tolerance_Radius = float(f.readline().strip()) LineSearchRadius = float(f.readline().strip()) Attributed_Segments = f.readline().strip() areaAnalysis = True if f.readline().strip() == "True" else False heightAnalysis = True if f.readline().strip() == "True" else False f.close() # Temporary files lineSeg = outWorkspace + "\\FLM_SLA_Segment_" + str(lineNo) + ".shp" lineBuffer = outWorkspace + "\\FLM_SLA_Buffer_" + str(lineNo) + ".shp" lineClip = outWorkspace + "\\FLM_SLA_Clip_" + str(lineNo) + ".shp" lineStats = outWorkspace + "\\FLM_SLA_Stats_" + str(lineNo) + ".dbf" if areaAnalysis: arcpy.Buffer_analysis(lineSeg, lineBuffer, LineSearchRadius, line_side="FULL", line_end_type="FLAT", dissolve_option="NONE", dissolve_field="", method="PLANAR") arcpy.Clip_analysis(Input_Footprint, lineBuffer, lineClip) arcpy.Delete_management(lineBuffer) if heightAnalysis and arcpy.Exists(lineClip): try: arcpy.gp.ZonalStatisticsAsTable_sa( lineClip, arcpy.Describe(lineClip).OIDFieldName, Input_CHM, lineStats, "DATA", "ALL") except Exception as e: lineStats = "" print(e) rows = arcpy.UpdateCursor(lineSeg) shapeField = arcpy.Describe(lineSeg).ShapeFieldName row = rows.next() feat = row.getValue(shapeField) # creates a geometry object length = float(row.getValue("LENGTH")) # creates a geometry object try: bearing = float(row.getValue("BEARING")) # creates a geometry object except Exception as e: bearing = 0 print(e) segmentnum = 0 segment_list = [] for segment in feat: # loops through every segment in a line # loops through every vertex of every segment # get.PArt returns an array of points for a particular part in the geometry for pnt in feat.getPart(segmentnum): if pnt: # adds all the vertices to segment_list, which creates an array segment_list.append(arcpy.Point(float(pnt.X), float(pnt.Y))) # Sinuosity calculation eucDistance = arcpy.PointGeometry(feat.firstPoint).distanceTo( arcpy.PointGeometry(feat.lastPoint)) try: row.setValue("Sinuosity", length / eucDistance) except Exception as e: row.setValue("Sinuosity", float("inf")) print(e) # Direction based on bearing ori = "N-S" if 22.5 <= bearing < 67.5 or 202.5 <= bearing < 247.5: ori = "NE-SW" elif 67.5 <= bearing < 112.5 or 247.5 <= bearing < 292.5: ori = "E-W" elif 112.5 <= bearing < 157.5 or 292.5 <= bearing < 337.5: ori = "NW-SE" row.setValue("Direction", ori) # If footprint polygons are available, get area-based variables if areaAnalysis: totalArea = float(row.getValue("POLY_AREA")) totalPerim = float(row.getValue("PERIMETER")) row.setValue("AvgWidth", totalArea / length) try: row.setValue("Fragment", totalPerim / totalArea) except Exception as e: row.setValue("Fragment", float("inf")) if arcpy.Exists(lineStats): # Retrieve useful stats from table which are used to derive CHM attributes ChmFootprintCursor = arcpy.SearchCursor(lineStats) ChmFoot = ChmFootprintCursor.next() chm_count = float(ChmFoot.getValue("COUNT")) chm_area = float(ChmFoot.getValue("AREA")) chm_mean = float(ChmFoot.getValue("MEAN")) chm_std = float(ChmFoot.getValue("STD")) chm_sum = float(ChmFoot.getValue("SUM")) del ChmFootprintCursor # Average vegetation height directly obtained from CHM mean row.setValue("AvgHeight", chm_mean) # Cell area obtained via dividing the total area by the number of cells # (this assumes that the projection is UTM to obtain a measure in square meters) cellArea = chm_area / chm_count # CHM volume (3D) is obtained via multiplying the sum of height (1D) of all cells # of all cells within the footprint by the area of each cell (2D) row.setValue("Volume", chm_sum * cellArea) # The following math is performed to use available stats (fast) and avoid further # raster sampling procedures (slow). # RMSH is equal to the square root of the sum of the squared mean and # the squared standard deviation (population) # STD of population (n) is derived from the STD of sample (n-1). # This number is not useful by itself, only to derive RMSH. sqStdPop = math.pow(chm_std, 2) * (chm_count - 1) / chm_count # Obtain RMSH from mean and STD row.setValue("Roughness", math.sqrt(math.pow(chm_mean, 2) + sqStdPop)) rows.updateRow(row) del row, rows # Clean temporary files if arcpy.Exists(lineClip): arcpy.Delete_management(lineClip) if arcpy.Exists(lineStats): arcpy.Delete_management(lineStats)
def workLinesMem(segment_info): """ New version of worklines. It uses memory workspace instead of shapefiles. The refactoring is to accelerate the processing speed. """ # input verification if segment_info is None or len(segment_info) <= 1: print("Input segment is corrupted, ignore") outWorkspace = flmc.GetWorkspace(workspaceName) f = open(outWorkspace + "\\params.txt") outWorkspace = f.readline().strip() Input_Lines = f.readline().strip() Input_Footprint = f.readline().strip() Input_CHM = f.readline().strip() SamplingType = f.readline().strip() Segment_Length = float(f.readline().strip()) Tolerance_Radius = float(f.readline().strip()) LineSearchRadius = float(f.readline().strip()) Attributed_Segments = f.readline().strip() areaAnalysis = True if f.readline().strip() == "True" else False heightAnalysis = True if f.readline().strip() == "True" else False f.close() line = [segment_info[0]] lineNo = segment_info[1] # second element is the line No. attributes = segment_info[2] outWorkspaceMem = r"memory" arcpy.env.workspace = r"memory" # Temporary files lineSeg = os.path.join(outWorkspaceMem, "FLM_SLA_Segment_" + str(lineNo)) lineBuffer = os.path.join(outWorkspaceMem, "FLM_SLA_Buffer_" + str(lineNo)) lineClip = os.path.join(outWorkspaceMem, "FLM_SLA_Clip_" + str(lineNo)) lineStats = os.path.join(outWorkspaceMem, "FLM_SLA_Stats_" + str(lineNo)) # Create segment feature class try: arcpy.CreateFeatureclass_management(outWorkspaceMem, os.path.basename(lineSeg), "POLYLINE", Input_Lines, "DISABLED", "DISABLED", Input_Lines) cursor = arcpy.da.InsertCursor(lineSeg, ["SHAPE@"]) cursor.insertRow([segment_info[0]]) del cursor except Exception as e: print("Create feature class {} failed.".format(fileSeg)) print(e) return if areaAnalysis: arcpy.Buffer_analysis(line, lineBuffer, LineSearchRadius, line_side="FULL", line_end_type="FLAT", dissolve_option="NONE", dissolve_field="", method="PLANAR") arcpy.Clip_analysis(Input_Footprint, lineBuffer, lineClip) arcpy.Delete_management(lineBuffer) if heightAnalysis and arcpy.Exists(lineClip): try: arcpy.gp.ZonalStatisticsAsTable_sa( lineClip, arcpy.Describe(lineClip).OIDFieldName, Input_CHM, lineStats, "DATA", "ALL") except Exception as e: lineStats = "" print(e) rows = arcpy.UpdateCursor(lineSeg) shapeField = arcpy.Describe(lineSeg).ShapeFieldName row = rows.next() feat = row.getValue(shapeField) # creates a geometry object length = float(attributes["LENGTH"]) # creates a geometry object try: bearing = float(attributes["BEARING"]) # creates a geometry object except Exception as e: bearing = 0 print(e) # TODO: retrieve vertices from line directly and remove lineSeg segmentnum = 0 segment_list = [] for segment in feat: # loops through every segment in a line # loops through every vertex of every segment # get.PArt returns an array of points for a particular part in the geometry for pnt in feat.getPart(segmentnum): if pnt: # adds all the vertices to segment_list, which creates an array segment_list.append(arcpy.Point(float(pnt.X), float(pnt.Y))) # Sinuosity calculation eucDistance = arcpy.PointGeometry(feat.firstPoint).distanceTo( arcpy.PointGeometry(feat.lastPoint)) try: attributes["Sinuosity"] = length / eucDistance except Exception as e: attributes["Sinuosity"] = float("inf") print(e) # Direction based on bearing ori = "N-S" if 22.5 <= bearing < 67.5 or 202.5 <= bearing < 247.5: ori = "NE-SW" elif 67.5 <= bearing < 112.5 or 247.5 <= bearing < 292.5: ori = "E-W" elif 112.5 <= bearing < 157.5 or 292.5 <= bearing < 337.5: ori = "NW-SE" attributes["Direction"] = ori # If footprint polygons are available, get area-based variables if areaAnalysis: totalArea = float(attributes["POLY_AREA"]) totalPerim = float(attributes["PERIMETER"]) attributes["AvgWidth"] = totalArea / length try: attributes["Fragment"] = totalPerim / totalArea except Exception as e: attributes["Fragment"] = float("inf") if arcpy.Exists(lineStats): # Retrieve useful stats from table which are used to derive CHM attributes ChmFootprintCursor = arcpy.SearchCursor(lineStats) ChmFoot = ChmFootprintCursor.next() chm_count = float(ChmFoot.getValue("COUNT")) chm_area = float(ChmFoot.getValue("AREA")) chm_mean = float(ChmFoot.getValue("MEAN")) chm_std = float(ChmFoot.getValue("STD")) chm_sum = float(ChmFoot.getValue("SUM")) del ChmFootprintCursor # Average vegetation height directly obtained from CHM mean attributes["AvgHeight"] = chm_mean # Cell area obtained via dividing the total area by the number of cells # (this assumes that the projection is UTM to obtain a measure in square meters) cellArea = chm_area / chm_count # CHM volume (3D) is obtained via multiplying the sum of height (1D) of all cells # of all cells within the footprint by the area of each cell (2D) attributes["Volume"] = chm_sum * cellArea # The following math is performed to use available stats (fast) and avoid further # raster sampling procedures (slow). # RMSH is equal to the square root of the sum of the squared mean and # the squared standard deviation (population) # STD of population (n) is derived from the STD of sample (n-1). # This number is not useful by itself, only to derive RMSH. sqStdPop = math.pow(chm_std, 2) * (chm_count - 1) / chm_count # Obtain RMSH from mean and STD attributes["Roughness"] = math.sqrt( math.pow(chm_mean, 2) + sqStdPop) # Clean temporary files if arcpy.Exists(lineClip): arcpy.Delete_management(lineClip) if arcpy.Exists(lineStats): arcpy.Delete_management(lineStats) print("Line {} attributes calculation done.".format(lineNo)) return [line, attributes]
def main(): global outWorkspace outWorkspace = flmc.SetupWorkspace(workspaceName) # Prepare input lines for multiprocessing numLines = flmc.SplitLines(Input_Feature_Class, outWorkspace, "ZT", False, ThresholdField) pool = multiprocessing.Pool(processes=flmc.GetCores()) flmc.log("Multiprocessing line zonal thresholds...") pool.map(workLines, range(1, numLines + 1)) pool.close() pool.join() flmc.logStep("Line multiprocessing") flmc.log("Merging layers...") tempShapefiles = arcpy.ListFeatureClasses() arcpy.Merge_management(tempShapefiles, OutputLines) flmc.logStep("Merge") for shp in tempShapefiles: arcpy.Delete_management(shp)
# Webpage: https://github.com/appliedgrg/flm # # Purpose: Assigns corridor thresholds to the input lines based on their # surrounding canopy density # # --------------------------------------------------------------------------- import multiprocessing import arcpy from arcpy.sa import * arcpy.CheckOutExtension("Spatial") import FLM_Common as flmc # Setup script path and workspace folder workspaceName = "FLM_ZT_output" outWorkspace = flmc.GetWorkspace(workspaceName) arcpy.env.workspace = outWorkspace arcpy.env.overwriteOutput = True # Load arguments from file args = flmc.GetArgs("FLM_ZT_params.txt") # Tool arguments Input_Feature_Class = args[0].rstrip() #ID_Field = args[1].rstrip() ThresholdField = args[1].rstrip() Canopy_Raster = args[2].rstrip() Canopy_Search_Radius = float(args[3].rstrip()) MinValue = float(args[4].rstrip()) MaxValue = float(args[5].rstrip()) OutputLines = args[6].rstrip()
def workLines(lineNo): # Temporary files outWorkspace = flmc.GetWorkspace(workspaceName) # read params from text file f = open(outWorkspace + "\\params.txt") Forest_Line_Feature_Class = f.readline().strip() Cost_Raster = f.readline().strip() Line_Processing_Radius = f.readline().strip() f.close() fileSeg = outWorkspace + "\\FLM_CL_Segment_" + str(lineNo) + ".shp" fileOrigin = outWorkspace + "\\FLM_CL_Origin_" + str(lineNo) + ".shp" fileDestination = outWorkspace + "\\FLM_CL_Destination_" + str( lineNo) + ".shp" fileBuffer = outWorkspace + "\\FLM_CL_Buffer_" + str(lineNo) + ".shp" fileClip = outWorkspace + "\\FLM_CL_Clip_" + str(lineNo) + ".tif" fileCostDist = outWorkspace + "\\FLM_CL_CostDist_" + str(lineNo) + ".tif" fileCostBack = outWorkspace + "\\FLM_CL_CostBack_" + str(lineNo) + ".tif" fileCenterLine = outWorkspace + "\\FLM_CL_CenterLine_" + str( lineNo) + ".shp" # Find origin and destination coordinates x1 = segment_list[0].X y1 = segment_list[0].Y x2 = segment_list[-1].X y2 = segment_list[-1].Y # Create origin feature class try: arcpy.CreateFeatureclass_management(outWorkspace, PathFile(fileOrigin), "POINT", Forest_Line_Feature_Class, "DISABLED", "DISABLED", Forest_Line_Feature_Class) cursor = arcpy.da.InsertCursor(fileOrigin, ["SHAPE@XY"]) xy = (float(x1), float(y1)) cursor.insertRow([xy]) del cursor except Exception as e: print("Creating origin feature class failed: at X, Y" + str(xy) + ".") print(e) # Create destination feature class try: arcpy.CreateFeatureclass_management(outWorkspace, PathFile(fileDestination), "POINT", Forest_Line_Feature_Class, "DISABLED", "DISABLED", Forest_Line_Feature_Class) cursor = arcpy.da.InsertCursor(fileDestination, ["SHAPE@XY"]) xy = (float(x2), float(y2)) cursor.insertRow([xy]) del cursor except Exception as e: print("Creating destination feature class failed: at X, Y" + str(xy) + ".") print(e) try: # Buffer around line arcpy.Buffer_analysis(fileSeg, fileBuffer, Line_Processing_Radius, "FULL", "ROUND", "NONE", "", "PLANAR") # Clip cost raster using buffer DescBuffer = arcpy.Describe(fileBuffer) SearchBox = str(DescBuffer.extent.XMin) + " " + str( DescBuffer.extent.YMin) + " " + str( DescBuffer.extent.XMax) + " " + str(DescBuffer.extent.YMax) arcpy.Clip_management(Cost_Raster, SearchBox, fileClip, fileBuffer, "", "ClippingGeometry", "NO_MAINTAIN_EXTENT") # Least cost path arcpy.gp.CostDistance_sa(fileOrigin, fileClip, fileCostDist, "", fileCostBack, "", "", "", "", "TO_SOURCE") arcpy.gp.CostPathAsPolyline_sa(fileDestination, fileCostDist, fileCostBack, fileCenterLine, "BEST_SINGLE", "") except Exception as e: print("Problem with line starting at X " + str(x1) + ", Y " + str(y1) + "; and ending at X " + str(x1) + ", Y " + str(y1) + ".") print(e) # Clean temporary files arcpy.Delete_management(fileSeg) arcpy.Delete_management(fileOrigin) arcpy.Delete_management(fileDestination) arcpy.Delete_management(fileBuffer) arcpy.Delete_management(fileClip) arcpy.Delete_management(fileCostDist) arcpy.Delete_management(fileCostBack)
def main(): # Setup script path and output folder outWorkspace = flmc.SetupWorkspace("FLM_RLA_output") arcpy.env.workspace = outWorkspace arcpy.env.overwriteOutput = True # Load arguments from file args = flmc.GetArgs("FLM_RLA_params.txt") # Tool arguments Input_Lines = args[0].rstrip() Input_Raster = args[1].rstrip() SamplingType = args[2].rstrip() Measure_Interval = float(args[3].rstrip()) Segment_Length = float(args[4].rstrip()) Tolerance_Radius = float(args[5].rstrip()) Sampling_Method = args[6].rstrip() Attributed_Segments = args[7].rstrip() # Local variables: FLM_RLA_Measure_Points = outWorkspace + "\\FLM_RLA_Measure_Points.shp" FLM_RLA_Attributed_Points = outWorkspace + "\\FLM_RLA_Attributed_Points.shp" flmc.log("Generating sample points along lines...") arcpy.GeneratePointsAlongLines_management(Input_Lines, FLM_RLA_Measure_Points, "DISTANCE", Measure_Interval, "", "") flmc.logStep("Spawning sample points") flmc.log("Extracting raster values at sample points...") arcpy.gp.ExtractValuesToPoints_sa(FLM_RLA_Measure_Points, Input_Raster, FLM_RLA_Attributed_Points) flmc.logStep("Raster sampling") # Find RASTERVALU field and set user defined sampling (merge) method fieldmappings = arcpy.FieldMappings() fieldmappings.addTable(FLM_RLA_Attributed_Points) RastervaluIndex = fieldmappings.findFieldMapIndex("RASTERVALU") fieldmap = fieldmappings.getFieldMap(RastervaluIndex) fieldmap.mergeRule = Sampling_Method #Set sampling method (Mean, Minimum, Maximum, Standard Deviation, Etc..) fieldmappings = arcpy.FieldMappings() fieldmappings.addFieldMap(fieldmap) flmc.log("Splitting lines...") FLM_RLA_Segmented_Lines = flma.FlmLineSplit(outWorkspace, Input_Lines, SamplingType, Segment_Length, Tolerance_Radius) flmc.logStep("Line split") flmc.log("Generating raster statistics along line segments") arcpy.SpatialJoin_analysis(FLM_RLA_Segmented_Lines, FLM_RLA_Attributed_Points, Attributed_Segments, "JOIN_ONE_TO_ONE", "KEEP_COMMON", fieldmappings, "INTERSECT", Tolerance_Radius, "")
def main(version, tools, cols, rows, binSizes, binNames, binTips): """Creates tool selection screen and handles GUI loop""" master.title("Forest Line Mapper v." + str(version)) # FLM Header lab = tk.Label(toolSelection, text="Forest Line Mapper", font=fontHeader) lab.pack(side=tk.TOP, fill=tk.X, padx=5, pady=0) lab = tk.Label( toolSelection, text= "A toolset for enhanced delineation and attribution of linear disturbances " "in forests. Copyright (C) 2021 Applied Geospatial Research Group.", font=fontText, wraplength=minWidth, justify=tk.LEFT) lab.pack(side=tk.TOP, fill=tk.X, padx=5, pady=0) AddSpace(toolSelection) # Create tool bins inside toolbox toolBox = tk.Frame(toolSelection, highlightbackground="gray60", highlightthickness=1) bins = [] for i in range(0, cols): binCol = tk.Frame(toolBox) binCol.pack(side=tk.LEFT, fill=tk.BOTH, expand=tk.YES, padx=0, pady=0) c = [] for j in range(0, rows): bin = tk.Frame(binCol) bin.pack(side=tk.TOP, fill=tk.X, padx=0, pady=8) binId = i * rows + j if binId < 5: binName = tk.Label(bin, text=binNames[binId], font=fontBold) binName.pack(side=tk.TOP, pady=0) ttp.CreateToolTip(binName, binTips[binId], wraplength=minWidth) c.append(bin) bins.append(c) binId = 0 binLen = 0 for tool in tools: # Figue out the bin where to place the tool if binLen >= binSizes[binId]: binId += 1 binLen = 0 col = int(binId / rows) row = binId % rows currentBin = bins[col][row] binLen += 1 # Tool object currentTool = tool # Tool selection button row = tk.Frame(currentBin) but = tk.Button(row, text=tool.title, command=lambda tool=tool: tool.OpenTool()) ttp.CreateToolTip(but, tool.description, wraplength=minWidth) but.pack(padx=5) row.pack(side=tk.TOP, fill=tk.X, padx=5, pady=5) # Tool screen and inputs toolScreen = tk.Frame(master) toolScreens.append(toolScreen) tool.SetupTool(toolScreen) currentTool = None toolBox.pack(side=tk.TOP, fill=tk.BOTH, expand=1, padx=5, pady=0) # Multiprocessing cores row = tk.Frame(toolSelection) lab = tk.Label(row, text="Multiprocessing Cores") ttp.CreateToolTip( lab, "The number of CPU cores to be used in parallel processes. Not all FLM tools use " "multiprocessing.\nFor the most part a larger number of cores will decrease processing time. " "Small application \nareas (<100 hectares) may work best with a smaller number of cores." ) lab.pack(side=tk.LEFT, padx=5, pady=0) global mpc maxCores = multiprocessing.cpu_count() mpc = tk.Scale(row, from_=1, to=maxCores, orient="horizontal") mpc.set(flmc.GetCores()) mpc.pack(side=tk.TOP, fill=tk.X, padx=5, pady=0) row.pack(side=tk.TOP, fill=tk.X, padx=5, pady=10) # Footer Buttons row = tk.Frame(toolSelection) but = tk.Button(row, text='EXIT', command=lambda: Exit()) but.pack(side=tk.RIGHT, padx=5) but = tk.Button(row, text='HELP', command=lambda: webbrowser.open(help_url)) but.pack(side=tk.RIGHT, padx=5) row.pack(side=tk.BOTTOM, fill=tk.X, padx=5, pady=10) toolSelection.pack(side=tk.TOP, fill=tk.BOTH, expand=1, padx=5, pady=0) master.protocol("WM_DELETE_WINDOW", Exit) tk.mainloop() return userExit
def main(): global outWorkspace outWorkspace = flmc.SetupWorkspace(workspaceName) if(HasField(Centerline_Feature_Class, Corridor_Threshold_Field) == False): flmc.log("ERROR: There is no field named "+Corridor_Threshold_Field+" in the input lines") return False # Prepare input lines for multiprocessing numLines = flmc.SplitLines(Centerline_Feature_Class, outWorkspace, "CFP", False, Corridor_Threshold_Field) pool = multiprocessing.Pool(processes=flmc.GetCores()) flmc.log("Multiprocessing line corridors...") pool.map(workLines, range(1,numLines+1)) pool.close() pool.join() flmc.logStep("Corridor footprint multiprocessing") flmc.log("Merging footprint layers...") tempShapefiles = arcpy.ListFeatureClasses() fileMerge = outWorkspace +"\\FLM_CFP_Merge.shp" arcpy.Merge_management(tempShapefiles,fileMerge) arcpy.Dissolve_management(fileMerge,Output_Footprint) for shp in tempShapefiles: arcpy.Delete_management(shp) arcpy.Delete_management(fileMerge) flmc.logStep("Merging")