Tuesday, February 23, 2016

ArcGIS Erase without an ArcGIS advanced license

After creating code for converting Polygons to Lines and Vertices to Points, I decided to try my hand at creating an Erase function that didn't require ArcGIS's advance license. The code I came up with can erase polygons from lines, lines from lines, and polygons from polygons. The inputs are:


  • inFc: The input feature class
  • eraseFc: The feature class whose features will be erased from the input feature class
  • outLoc: The output file geodatabase
  • outName: The final name of the output feature class
  • workspace: A file geodatabase workspace
  • notices: Set to True for printed progress information


The code:

def Erase (inFc, eraseFc, outLoc = "", outName = "", workspace = "in_memory", notices = False):
    import os
    addToToc = arcpy.env.addOutputsToMap
    arcpy.env.addOutputsToMap = False
    if not outName:
        outName = os.path.basename (inFc) + "_Erase"
    if not outLoc:
        catPath = arcpy.Describe (inFc).catalogPath
        outLoc = os.path.dirname (catPath)
    outFc = UniqueFileName (outLoc, outName)
    outLoc, outName = os.path.split (outFc)

    garbage = []

    inType = arcpy.Describe (inFc).shapeType
    eraseType = arcpy.Describe (eraseFc).shapeType

    #lines and lines
    if inType == "Polyline" and eraseType == "Polyline":
        #buffer both lines: flat, left
        inBuffFc = UniqueFileName (workspace)
        arcpy.Buffer_analysis (inFc, inBuffFc, ".01 FEET")
        garbage += [inBuffFc]
        eraseBuffFc = UniqueFileName (workspace)
        arcpy.Buffer_analysis (eraseFc, eraseBuffFc, ".01 FEET")
        garbage += [eraseBuffFc]
        
        #union input buffer with erase buffer
        unionFc = UniqueFileName (workspace)
        arcpy.Union_analysis ([inBuffFc, eraseBuffFc], unionFc)
        garbage += [unionFc]
        #select union output by erase buffer
        unionLyr = UniqueLayerName (unionFc)
        garbage += [unionLyr]
        arcpy.SelectLayerByLocation_management (unionLyr, "WITHIN", eraseBuffFc)
        #switch union selection
        arcpy.SelectLayerByAttribute_management (unionLyr, "SWITCH_SELECTION")
        #check for selection
        if not arcpy.Describe (unionLyr).FIDSet:
            arcpy.CreateFeatureclass_management (outLoc, outName, "POLYLINE", inFc, "", "", inFc)
            for trash in garbage:
                arcpy.Delete_management (trash)
            return outFc
        #clip line
        arcpy.env.addOutputsToMap = addToToc
        arcpy.Clip_analysis (inFc, unionLyr, outFc)
        

    #lines and polygons
    elif inType == "Polyline" and eraseType == "Polygon":
        #buffer both lines: flat, left
        inBuffFc = UniqueFileName (workspace)
        arcpy.Buffer_analysis (inFc, inBuffFc, ".01 FEET")
        garbage += [inBuffFc]
        
        #union input buffer with erase buffer
        unionFc = UniqueFileName (workspace)
        arcpy.Union_analysis ([inBuffFc, eraseFc], unionFc)
        garbage += [unionFc]
        #select union output by erase buffer
        unionLyr = UniqueLayerName (unionFc)
        garbage += [unionLyr]
        arcpy.SelectLayerByLocation_management (unionLyr, "WITHIN", eraseFc)
        #switch union selection
        arcpy.SelectLayerByAttribute_management (unionLyr, "SWITCH_SELECTION")
        #check for selection
        if not arcpy.Describe (unionLyr).FIDSet:
            arcpy.CreateFeatureclass_management (outLoc, outName, "POLYLINE", inFc, "", "", inFc)
            for trash in garbage:
                arcpy.Delete_management (trash)
            return outFc
        #clip line
        arcpy.env.addOutputsToMap = addToToc
        arcpy.Clip_analysis (inFc, unionLyr, outFc)

    #polygons and polygons
    elif inType == "Polygon" and eraseType == "Polygon":
        unionFc = UniqueFileName (outLoc, outName)
        arcpy.env.addOutputsToMap = addToToc
        arcpy.Union_analysis ([inFc, eraseFc], unionFc, "NO_FID")
        arcpy.env.addOutputsToMap = False
        unionLyr = UniqueLayerName (unionFc)
        garbage += [unionLyr]
        arcpy.SelectLayerByLocation_management (unionLyr, "WITHIN", eraseFc)
        arcpy.DeleteFeatures_management (unionLyr)

        inFlds = [f.name for f in arcpy.ListFields (inFc)]
        delFlds = [f.name for f in arcpy.ListFields (unionLyr) if not f.name in inFlds and not
                   f.type in ("Geometry", "OID") and f.required == False]
        arcpy.DeleteField_management (unionLyr, delFlds)
        arcpy.env.addOutputsToMap = addToToc

    for trash in garbage:
        arcpy.Delete_management (trash)    
    
    return outFc

def UniqueFileName(Location = "in_memory", Name = "file", Extension = ""):
    """
    returns an unused file name
    'Location' will be the file folder
    'Name' will be the file name plus numeric extension if needed
    'Extension' is the file extension

    similar to arcpy.CreateUniqueName
    """
    import arcpy
    import os

    if Extension:

        outName = os.path.join (Location, Name + "." + Extension)
    else:
        outName = os.path.join (Location, Name)
    i = 0
    while arcpy.Exists (outName):
        i += 1
        if Extension:
            outName = os.path.join (Location, Name + "_" + str(i) + "." + Extension)
        else:
            outName = os.path.join (Location, Name + "_" + str(i))
        
    return outName

def UniqueLayerName(inFC = None, sql = None):
    """
    returns an unused feature layer name
    """
    import arcpy
    import os
    lyrName = os.path.join ("lyr0")
    i = 0
    while arcpy.Exists (lyrName):
        i += 1
        lyrName = "lyr" + str(i)
    if inFC:
        arcpy.MakeFeatureLayer_management (inFC, lyrName, sql) 
    return lyrName
profile for Emil Brundage at Geographic Information Systems Stack Exchange, Q&A for cartographers, geographers and GIS professionals

6 comments:

  1. This comment has been removed by a blog administrator.

    ReplyDelete
  2. Hi,
    Thank a lot for sharing the code! I think the variable "fcName" in Line 10 should be "outName". Cheers~~

    ReplyDelete
    Replies
    1. Thanks, you were right. I've fixed the code. Good catch.

      Delete
  3. This works well, however since I didn't have an advanced licence, I had to change the buffer parameters from "Left" to "Full" and from "Flat" to "Round". Since those with an advance licence would simply use the Erase tool, it would make no sense to keep the advanced licence parameters.
    It would be helpful if there were a script that would call the Erase Function, for those who are not arcpy proficient.

    ReplyDelete
  4. I thought you could use arcpy functions outside of arcmap without license?

    ReplyDelete