ArcGIS – Achieve Buffer Wizard-like Speed in Python Using MultipleRingBuffer_analysis

arcgis-10.1arcgis-desktoparcpybuffer

I've got a script that takes a single input polygon feature, throws 10 buffers around it at tenths of a specified input distance, and then symbolises the output based on an existing layer file. Pretty simple.

However, the arcpy.MultipleRingBuffer_analysis operation is amazingly slow. It takes upwards of two minutes to generate the buffers, even for very basic polygon inputs – the same result can be had in about two seconds by using the buffer wizard tool. Problem is, the buffer wizard can't be accessed through arcpy.

So obviously it's possible to quickly generate multiple ring buffers – does anyone have any insight as to how the buffer wizard tool is doing it, and how that might be replicated in Python?

Best Answer

While the buffer wizard is not exposed through ArcPy, geometries do expose the buffer method so the following works to create multiple ring buffers:

import arcpy

def MultiRingBuffer(ringDistance, ringCount, inputLayer, outputLayer):
    buffers = []

    cursor = arcpy.SearchCursor(inputLayer)
    for inputFeature in cursor:
        sourceOid = inputFeature.getValue("OBJECTID")
        currentBuffers = dict()
        buffers.append(currentBuffers)
        prevBuffer = inputFeature.Shape

        for multiple in range(1, ringCount + 1):
            distance = multiple * ringDistance
            bufferedGeom = inputFeature.Shape.buffer(distance)
            bufGeom = bufferedGeom.difference(prevBuffer)
            prevBuffer = bufferedGeom
            row = dict()
            row["sourceOid"] = sourceOid
            row["distance"] = distance
            row["SHAPE"] = bufGeom
            currentBuffers[distance] = row
    del cursor

    cursor = arcpy.InsertCursor(outputLayer)
    for ringBuffers in buffers:
        for feature in ringBuffers.values():
            row = cursor.newRow()
            for k in feature.keys():
                if k == "SHAPE":
                    row.Shape = feature[k]
                else:
                    row.setValue(k, feature[k])
            cursor.insertRow(row)
    del cursor

if __name__ == '__main__':
    MultiRingBuffer(10, 10, "c:\\temp\\test.gdb\\buffertest", "c:\\temp\\test.gdb\\bufferout")
    print("Complete")

For each source feature, we create a dictionary to store each ring buffer. Each of these buffers is a dictionary with a few values - the source OID, the distance, and the buffer geometry. We then create an insert cursor and create features in the output table for each ring buffer of each input feature.

Edit: I have tested this and with 3 simple features, where the gp tool takes over a minute, this script takes ~1s once arcpy has finished importing.

Edit 2: Fixed a couple of bugs - firstly, the difference call was removing the previous ring rather than all previous rings. Secondly, I wasn't adding the ring to the currentBuffer so it wasn't getting added later...

Edit 3: Handled the shape field not being 'SHAPE' in the output featureclass. Added cleanup of cursor objects.