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.

buffer to both sides

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

  • 1
    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
  • 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
bearing = math.radians(bearing)
angle = math.radians(angle)
new1x_1, new1y_1 = (end1x + (length * math.cos(angle)), end1y + (length * math.cos(bearing)))
bearing = abs(normalize(azimuth1 - 90))
angle = 90 - bearing
bearing = math.radians(bearing)
angle = math.radians(angle)
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
bearing = math.radians(bearing)
angle = math.radians(angle)
new2x_1, new2y_1 = (end2x + (length * math.cos(angle)), end2y + (length * math.cos(bearing)))
bearing = abs(normalize(azimuth2 - 90))
angle = 90 - bearing
bearing = math.radians(bearing)
angle = math.radians(angle)
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 = []
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)
lsTemp2 = []
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)
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: enter image description here

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

enter image description here

Then use the merge tool to merge the two feature classes together: enter image description here

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.