# Multiple buffers to both sides of a line using ArcGIS

I am aiming to create multiple buffer zones to both sides of a line that have flat ends where the line ends (unlike 'rounded' edges of multiple ring buffers) and are not connected (each buffer zone to each side an individual feature) as shown for the blue line.

I am wondering if it is possible to do this without having to create each single buffer and then to clip and merge.

• You could clip the multiple buffers with a single buffer (flat ends) as wide as the widest multiple buffer – BERA Dec 13 '17 at 10:33
• I tried to do this and it seemed to work once, but I cannot replicate it now in such a way that I will get the 4 buffers as 4 separate features and without the end 'caps'. Which tool do you use for the clipping? – Kristina Dec 13 '17 at 10:58
• – BERA Dec 13 '17 at 11:25
• The Clip tool gives me no individual features for each buffer to each side of the line but keeps the original features of the multiple ring buffer, e.g. if it has 5 buffer zones, the clipped one will have 5 too, but I need 10 (5 to each side). – Kristina Dec 13 '17 at 11:33
• Then you need to split the clipped buffers with center line. For example like this:Split Polygons With Lines arcgis.com/home/item.html?id=cd6b2d45df654245b7806a896670a431 . Or when you create the single buffer you can create one for each side and then clip the multiring with these – BERA Dec 13 '17 at 11:57

By merging a "little" python and some manual processing, the desired results can be obtained like this:

``````import arcpy
import math
from math import floor, ceil
import os

inLine = r'C:\Temp\Scratch.gdb\test_line' #original line feature
inBufferDistances = [5,10,15] #buffer distances

arcpy.env.overwriteOutput = True

def normalize(num, lower=0, upper=360):
res = num
total_length = abs(lower) + abs(upper)
if num < -total_length:
num += ceil(num / (-2 * total_length)) * 2 * total_length
if num > total_length:
num -= floor(num / (2 * total_length)) * 2 * total_length
if num > upper:
num = total_length - num
if num < lower:
num = -total_length - num
res = num
res *= 1.0
return res

#Get list of vertices from inLine
lsVertices = []
lsArray = []
for row in arcpy.da.SearchCursor(inLine, ["OID@", "SHAPE@"]):
for part in row[1]:
for pnt in part:
lsVertices.append([pnt.X, pnt.Y])
lsArray.append(arcpy.Point(pnt.X, pnt.Y))

#Calculate direction of endpoints using first 2 and last 2 items in lsVertices
azimuth1 = abs(normalize(math.degrees(math.atan2((lsVertices[0][0] - lsVertices[1][0]),(lsVertices[0][1] - lsVertices[1][1])))))
azimuth2 = abs(normalize(math.degrees(math.atan2((lsVertices[-1][0] - lsVertices[-2][0]),(lsVertices[-1][1] - lsVertices[-2][1])))))

#find xy coordinates to use in clipping polygon geometry
length = max(inBufferDistances) + 50
#West End Point
end1x = lsVertices[0][0]
end1y = lsVertices[0][1]
bearing = abs(normalize(azimuth1 + 90))
angle = 90 - bearing
new1x_1, new1y_1 = (end1x + (length * math.cos(angle)), end1y + (length * math.cos(bearing)))
bearing = abs(normalize(azimuth1 - 90))
angle = 90 - bearing
new1x_2, new1y_2 = (end1x + (length * math.cos(angle)), end1y + (length * math.cos(bearing)))
#East End Point
end2x = lsVertices[-1][0]
end2y = lsVertices[-1][1]
bearing = abs(normalize(azimuth2 + 90))
angle = 90 - bearing
new2x_1, new2y_1 = (end2x + (length * math.cos(angle)), end2y + (length * math.cos(bearing)))
bearing = abs(normalize(azimuth2 - 90))
angle = 90 - bearing
new2x_2, new2y_2 = (end2x + (length * math.cos(angle)), end2y + (length * math.cos(bearing)))

#Create clipping polygon
arcpy.CreateFeatureclass_management(r'c:/temp/scratch.gdb', 'temp_poly', "POLYGON", spatial_reference = arcpy.SpatialReference(26912))
cursor = arcpy.da.InsertCursor(r'c:/temp/scratch.gdb/temp_poly', ["SHAPE@"])
lsTemp = []
lsTemp.append(arcpy.Point(new1x_1,new1y_1))
for item in lsArray:
lsTemp.append(arcpy.Point(item.X, item.Y))
lsTemp.append(arcpy.Point(new2x_2, new2y_2))
array = arcpy.Array(lsTemp)
multipartFeature = arcpy.Polygon(array)
cursor.insertRow([multipartFeature])
lsTemp2 = []
lsTemp2.append(arcpy.Point(new1x_2,new1y_2))
for item in lsArray:
lsTemp2.append(arcpy.Point(item.X, item.Y))
lsTemp2.append(arcpy.Point(new2x_1, new2y_1))
array = arcpy.Array(lsTemp2)
multipartFeature2 = arcpy.Polygon(array)
cursor.insertRow([multipartFeature2])
del cursor

#Run multiring buffer
arcpy.MultipleRingBuffer_analysis(inLine, r'c:/temp/scratch.gdb/test_buffer', ";".join([str(i) for i in inBufferDistances]), "Default", "distance", "ALL", "FULL")
``````

This outputs a multi ring buffer feature class and a "clipping polygon" feature class:

From there, you can select each part of the clipping polygon and run the clip function:

Then use the merge tool to merge the two feature classes together:

Note, all of my feature classes are in the UTM NAD 83 coordinate system (all distances measured in meters).

• Thank you for the detailed description. Unfortunately I am not familiar using ArcPy and would have a hard time adjusting the code. However, I like the way you thought this through and will try to replicate it without ArcPy. – Kristina Dec 14 '17 at 13:38