Cone Shaped Line Buffer

3128
19
01-21-2011 08:18 AM
TimVictor
New Contributor
Hello,

Does anyone know if it is possible to create a "cone" shaped line buffer? For example, a 20 mi buffer on one end that expands to a 50 mi buffer on the other end of the line.  View attached for visual.  I can use the editor to do this but i have hundreds of lines and it would be impractical.  I am using an ArcView license for version 10.

Please if anyone has any idea at all please let me know! It would be greatly appreciated!!
0 Kudos
19 Replies
FrédéricPRALLY
Esri Contributor
Hi Tim,

Can you send me an example of your data to test a python script?

Best regards,
0 Kudos
TimVictor
New Contributor
Here is an example of the data i am using.  Let me know if you have any questions! Thank you very much for looking into this and good luck!
0 Kudos
ChrisSnyder
Regular Contributor III
An idea:

Note this would only work if the line segment has only two vertices (the start node and end node), and you would have to use Python or ArcObjects to do this:

1. Extract the start/end points using the FeatureVerticesToPoints_management tool (specify the "BOTH_ENDS" parameter)

2. Buffer the points to different distances (one at 20 miles, one at 50 miles).

3. Now using the the original line segment, construct 4 new line segments that originate from the start/end nodes in a perpendicular fashion (relative to teh original line segment). Make these lines extent slightly past the buffer distance. You will then have something that looks "H" shaped. Make sure you start the new lines on the start end/nodes of the original line. This will take some math/python scripting to do.

4. Now intersect the new "sides of the H lines" with the buffers. The end nodes of the intersected lines now form the vertices of a new trapezoidal polygon.

5. Merge the buffer polys and the trapezoid, and dissolve it.

6. Presto!
0 Kudos
JasonScheirer
Occasional Contributor III
Instead of generating the trapezoids, you could probably get away with a convex hull on a dissolve of the two buffered points too.
0 Kudos
ChrisSnyder
Regular Contributor III
Oh yeah - convex hull - that's much easier, and you wouldn't have to use cursors.
0 Kudos
TimVictor
New Contributor
Those are awesome ideas but i only have an ArcView license and the FeatureVerticesToPoints_management tool will not work nor will the Minimum Bounding Geometry tool allow for me to use Convex Hull without an Info license.  Thank you for your response none the less.  Very much appreciated.
0 Kudos
ChrisSnyder
Regular Contributor III
How about this script from Dan Patterson: http://arcscripts.esri.com/details.asp?dbid=14535 for doing teh convex hulls?
0 Kudos
FrédéricPRALLY
Esri Contributor
Hi Tim,

Take a look at the python script below. I hope this script makes the job.
You can test it after modify inputs parameters.
I'm going to optimize it for adding it in cgeoprocessing script tool gallery.

# -*- coding: cp1252 -*-
""" 
ConeLineBuffer.py

Author : fpr
         Many thank to rba for algorythm

Date: 01/25/2011

Description : Create cone from line buffer with min buffer value at the start line
              and with max buffer value at the end line.
              
              Warning:
              you must have :
                  1. min value <> max value
                  2. start point of line always have the min value
                  3. end point of line always have the max value
               
Parameters list :
                                Parameters properties
           Name                         Data type          Type      Direction Multiple value Default    Filter    Obtain from
argv[1]   Input SHP                     Feature class      Required  Input     No              
argv[2]   Input min radius              long               Required  Input     No                                
argv[3]   Input max radius              long               Required  Input     No
 ----------------------------------------------------------------------
"""
# Import system modules
import arcpy, os

def getScratchWorkspace(outDataset):
    # Set the scratchWorkspace environment to local file geodatabase
    outPath = os.path.dirname(outDataset)
    arcpy.CreateFileGDB_management(outPath, "scratch.gdb", "CURRENT")
    scratchWorkspace = os.path.join(outPath, "scratch.gdb")
    arcpy.env.scratchWorkspace = scratchWorkspace
    return scratchWorkspace

def getStartPointOfLine(line):
    """
    Return first point of line
    """
    pointStart = arcpy.Point()
    pointStart = line.firstPoint
    return pointStart

def getEndPointOfLine(line):
    """
    Return end point of line
    """
    pointEnd = arcpy.Point()
    pointEnd = line.lastPoint
    return pointEnd

def bufferPoint(point, dis, scratchWorkspace, outName):
        
    # Process : buffer first point of line
    pointGeometry = arcpy.PointGeometry(point)
    outBufferPoint = scratchWorkspace + "\\" + outName
    if arcpy.Exists(outBufferPoint):
        outBufferPoint = outBufferPoint + "1"
        
    arcpy.Buffer_analysis(pointGeometry, outBufferPoint, dis, "FULL", "ROUND", "NONE", "")
    
    return outBufferPoint


def creerPolyligneSelonPoint(PointDebut, arcTan, longueurLigne):
    import math
    """Retourne un objet ligne à partir du point de début, un angle et une longueur.

    INPUTS:
    point de début, l'angle de la ligne, une longueur

    OUTPUT:
    objet ligne
    """

    #Créer une polyligne
    array = arcpy.Array()
    array.add(PointDebut)
    
    # Détermine le point de fin selo
    PointFin = arcpy.Point()
    PointFin.X = PointDebut.X - math.sin(arcTan)* longueurLigne
    PointFin.Y = PointDebut.Y - math.cos(arcTan)* longueurLigne
    array.add(PointFin)
    
    polyline = arcpy.Polyline(array)
    array.removeAll()
    return polyline

def arcTanEntreDeuxPoints(PointDebut, PointFin):
    import math
    """Retourne l'arc tan entre deux points.

    INPUTS:
    point de début et point de fin

    OUTPUT:
    arcTan
    """
    
    arcTan = math.atan2((PointDebut.X - PointFin.X),(PointDebut.Y - PointFin.Y))
    
    return arcTan

def distanceRelative(Distance, Pourcentage):
    """Retourne la distance issue du pourcentage souhaitée.

    INPUTS:
    distance

    OUTPUT:
    50 pourcent de la distance par exemple
    """
    distanceRel = (float((Distance)) * float(Pourcentage))/100
    
    return distanceRel

def distanceEntreDeuxPoints(PointDebut, PointFin):
    import math
    """Retourne la distance entre deux points.

    INPUTS:
    point de début et point de fin

    OUTPUT:
    distance
    """
    
    distance = math.sqrt((PointDebut.X - PointFin.X)**2 + (PointDebut.Y - PointFin.Y)**2)

    return distance
    
def getTangentIntersect(radiusCircleMin, radiusCircleMax, dist2points):
    """ Return intersection point of circle tangent a with the line

    """
    GetTangentIntersect = (radiusCircleMin * dist2points)/ (radiusCircleMax - radiusCircleMin)
    
    return GetTangentIntersect

def deleteFcStoreScratchWorkspace(pathFGDB, wild_card):
    arcpy.env.workspace = pathFGDB
    fcs = arcpy.ListFeatureClasses(wild_card)
    for fc in fcs:
        arcpy.Delete_management(fc)
    return

def run_ConeLineBuffer():
    inFC = r"D:\fpr10_PYDEV\fprPYDEV_v10\ConeLineBuffer\data\lines.shp" #  CHANGE
    inRadiusBufferMin = 50                                                                 #  CHANGE
    inRadiusBufferMax = 300                                                               #  CHANGE
    return create_ConeLineBuffer(inFC, inRadiusBufferMin, inRadiusBufferMax) 

def create_ConeLineBuffer(inFC, inRadiusBufferMin, inRadiusBufferMax):
    arcpy.AddMessage("*"*10)
    arcpy.AddMessage("Process : Create cone line buffer ...\nrunning ...")
    
    arcpy.OverWriteOutput = True
    
    # Process: Define the scratch workspace 
    sw = getScratchWorkspace(inFC)
    arcpy.env.workspace = sw
    
    # Process: get path of input ...
    outPath = os.path.dirname(inFC)
    
    # Process: prepare SQL query SHP or GDB
    OID = arcpy.Describe(inFC).OIDFieldName
    delimitedField = arcpy.AddFieldDelimiters(inFC, OID)
    
    # Iterate through the lines in the cursor 
    sRows = arcpy.SearchCursor(inFC)
    for sRow in sRows:
        index = sRow.getValue(OID)
        shapeLine = sRow.Shape
        
        # Process : Get start and end point of line
        startPoint = getStartPointOfLine(shapeLine)
        endPoint = getEndPointOfLine(shapeLine)
        
        # Process : Create buffer for start/end line
        bufferMin = bufferPoint(startPoint, str(inRadiusBufferMin) + " Meters", sw, "_bufferMin")
        bufferMax = bufferPoint(endPoint, str(inRadiusBufferMax) + " Meters", sw, "_bufferMax")

        ## Process : Search point a
        dist2points = distanceEntreDeuxPoints(startPoint, endPoint)
        a = getTangentIntersect(inRadiusBufferMin, inRadiusBufferMax, dist2points)
        radiusCircleMax = (dist2points + a)/2
        
        ## Process : Create circle c1 (Max)
        # Process: Get arctan of line
        valeurArcTan = arcTanEntreDeuxPoints(endPoint, startPoint)
        # Process: create line
        lineCircleMax = creerPolyligneSelonPoint(endPoint, valeurArcTan, radiusCircleMax)
        # Process : 
        endPtCircleMax = getEndPointOfLine(lineCircleMax)
        # Process : 
        circleMax = bufferPoint(endPtCircleMax, radiusCircleMax, sw, "_circleMax")
        # Process : Intersect BufferMaw with CircleMax to get
        # Process: Intersect
        intersectMax = sw + "\\_intersectMax"
        arcpy.Intersect_analysis(bufferMax + ";" + circleMax, intersectMax, "ALL", "", "POINT")
        
        ## Process : Create circle c2 (Min)
        radiusCircleMin = a/2
        # Process: create line
        lineCircleMin = creerPolyligneSelonPoint(startPoint, valeurArcTan, radiusCircleMin)
        # Process : 
        endPtCircleMin = getEndPointOfLine(lineCircleMin)
        # Process : 
        circleMin = bufferPoint(endPtCircleMin, radiusCircleMin, sw, "_circleMin")
        # Process :
        # Process : Intersect BufferMaw with CircleMax to get
        # Process: Intersect
        intersectMin = sw + "\\_intersectMin"
        arcpy.Intersect_analysis(bufferMin + ";" + circleMin, intersectMin, "ALL", "", "POINT")
       
        # Process :
        arcpy.env.workspace =sw
        arcpy.Merge_management([intersectMin, intersectMax], "_intersectMerge")
        
        # Process: Minimum Bounding Geometry
        arcpy.MinimumBoundingGeometry_management("_intersectMerge", "_Bounding", "CONVEX_HULL", "ALL", "", "NO_MBG_FIELDS")
        
        # Process: Merge
        Bounding = sw + "\\_Bounding"
        arcpy.Merge_management([bufferMin, bufferMax, Bounding], "_Bounding_Merge")
        
        # Process: Dissolve
        Bounding_Merge = sw + "\\_Bounding_Merge"
        arcpy.Dissolve_management(Bounding_Merge, "result" + str(index), "", "", "MULTI_PART", "DISSOLVE_LINES")
        
        # Process : delete intermediate fc begin by "_"
        deleteFcStoreScratchWorkspace(sw, "_*")
    
    # Process: Merge all result feature classes
    ## Create a value table to hold names of input feature classes for Merge tool.
    vt = arcpy.ValueTable()
    fcs = arcpy.ListFeatureClasses("r*")
    for fc in fcs:
        vt.addRow(fc)

    outFC = os.path.join(outPath,"ConeLineBuffer.shp")
    arcpy.Merge_management(vt, outFC)
    
    # Process: Delete scratch workspace
    arcpy.Delete_management(sw)
    
    arcpy.AddMessage("Process: End")
    arcpy.AddMessage("*"*10)
    
if __name__ == '__main__':
    run_ConeLineBuffer()
    

0 Kudos
TimVictor
New Contributor
Frédéric,

I will give it a shot and let you know if it works for me! I don't have any experience with python so it may take me a minute to catch on.  I really appreciate your time and effort though, can't thank you enough!
0 Kudos