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?
2 Answers 2
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.
-
Thanks for this, it looks promising. I couldn't get any output though. I can get things to a point where it looks like the script is running (it runs very quickly, as you said), but nothing appears to be altered in the input or output feature classes. How exactly did you set up your test, so that I can try to replicate your results?Nathaniel– Nathaniel2015年01月23日 06:12:32 +00:00Commented Jan 23, 2015 at 6:12
-
Thank you, there were a couple of bugs in there. In the loop that actually calls Shape.buffer I've made a couple of changes - first, to separate the buffer and difference, so the plain buffered geometry can be used for subsequent differences, and secondly to actually add the row dictionary to the currentBuffers dictionaryMartin Wilkerson– Martin Wilkerson2015年01月23日 10:15:43 +00:00Commented Jan 23, 2015 at 10:15
-
It's still falling over for me at the last row.setvalue line. I can see what it's supposed to do, but I don't know enough Python to fix it. Any ideas?Nathaniel– Nathaniel2015年01月28日 07:05:38 +00:00Commented Jan 28, 2015 at 7:05
-
Could be that your shape field isn't called 'SHAPE'? It certainly doesn't have to be, I only used that as a shortcut. Could you post the error it's giving please? And I'll edit the above to use the row's shape property just in case that's it (and it's more correct anyway).Martin Wilkerson– Martin Wilkerson2015年01月29日 09:03:20 +00:00Commented Jan 29, 2015 at 9:03
-
It could also be missing fields in the output feature class - it needs to have be a polygon feature class with two long integer fields called 'sourceOid' and 'distance'. Change the row dictionary entries in the first loop to change what attributes are set in the output feature class.Martin Wilkerson– Martin Wilkerson2015年01月29日 09:11:21 +00:00Commented Jan 29, 2015 at 9:11
Unfortunately, the Buffer wizard (and its options) are not exposed in ArcObjects or ArcPy.
I'm trying to think of ways around this. One cool thing about the Buffer wizard is that you can use an optimized coordinate system at either a selected feature set / entire dataset or feature level. If you're using the former (not the feature-optimized), and your data is using a geographic coordinate reference system, you might try projecting your data into a projected coordinate system, then running the Buffer Tool.
Note: I work for Esri.
-
1Hi @mkennedy - thanks for your comment. I don't know what an optimized coordinate system is, but I am using features that have been projected into a proj. coord. system. If you have any sway in this at ESRI, can you ask why the Buffer Wizard a) is so buried in tools so that only an exhaustive Google search eventually leads to an Esri blog post that reveals its existence? and b) why can't it just retain the other fields in the outputs from the input features like almost every other tool?SharonB– SharonB2013年09月09日 16:11:53 +00:00Commented Sep 9, 2013 at 16:11
-
I know your response is from a couple of years ago @SharonB but I found this article that might help shed some light on what an "optimized coordinate system" is. It's worth adding for any users that may be in the same boat coming across this question.Sethdd– Sethdd2017年03月27日 17:28:13 +00:00Commented Mar 27, 2017 at 17:28
Explore related questions
See similar questions with these tags.
import arcpy
take 10s of seconds on its own.