3

I'm trying to create rectangles along a line and can't seem to find a solution anywhere.

I've got a curved line roughly 16km (~10miles) long that I've placed points along in 5m increments. From there I need to create a rectangular buffer around these points with dimensions of 1600 m length and 400 m width (200 m on either side of the line).

Is there a method that I could use that would allow me to create rectangular buffer around these points but have the rectangle rotate with the direction of the line?

  • 1
    10 k = 16 m ? Or other way around? – FelixIP Mar 23 '16 at 0:05
6

Here is how you can use Python to automate your task. It evaluates your line at each point location (determined by 'point_spacing'), offset by 'tolerance', then creates rectangles to fit those angles & distances.

line_fc = 'line' # line feature class
sr = arcpy.Describe(line_fc).spatialReference # spatial ref
point_spacing = 200 # how far apart to draw points
points = [] # points container
tolerance = 50 # how far from points to evaluate line AND place rectangle ends
width = 25 # distance perpendicular to line
buffs = [] # buffer container
with arcpy.da.SearchCursor(line_fc,'SHAPE@',spatial_reference=sr) as cursor:
    for row in cursor:
        for i in range(point_spacing,int(row[0].length),point_spacing): # loop through points
            current_point = row[0].positionAlongLine(i) # get point position
            points.append(current_point) # add to list
            point_ahead = row[0].positionAlongLine(i+tolerance).centroid # evaluate ahead
            point_behind = row[0].positionAlongLine(i-tolerance).centroid # evaluate behind
            dx = point_ahead.X - point_behind.X # x diff
            dy = point_ahead.Y - point_behind.Y # y diff
            angle = math.atan2(dy,dx) # angle between
            point_ahead = arcpy.Point(current_point.centroid.X+(math.cos(angle)*tolerance),current_point.centroid.Y+(math.sin(angle)*tolerance)) # find rectangle end ahead
            point_behind = arcpy.Point(current_point.centroid.X-(math.cos(angle)*tolerance),current_point.centroid.Y-(math.sin(angle)*tolerance)) # find rectangle end behind
            pt1 = arcpy.Point(point_ahead.X+(math.sin(angle)*width),point_ahead.Y-(math.cos(angle)*width)) # rectangle corners
            pt2 = arcpy.Point(point_ahead.X-(math.sin(angle)*width),point_ahead.Y+(math.cos(angle)*width))
            pt3 = arcpy.Point(point_behind.X-(math.sin(angle)*width),point_behind.Y+(math.cos(angle)*width))
            pt4 = arcpy.Point(point_behind.X+(math.sin(angle)*width),point_behind.Y-(math.cos(angle)*width))
            buffs.append(arcpy.Polygon(arcpy.Array([pt1,pt2,pt3,pt4]),sr)) # add buffer to list
arcpy.CopyFeatures_management(buffs,r'in_memory\buffs') # write buffers    
arcpy.CopyFeatures_management(points,r'in_memory\points') # write points

Screenshot

  • Nice answer! I can see this code being useful for all sorts of things. – Hornbydd Mar 22 '16 at 23:38
  • 1
    I'd use atan2 to define angle which is much safer option – FelixIP Mar 23 '16 at 0:02
  • Ok, edited. The result is the same. – phloem Mar 23 '16 at 16:23
  • I'm trying to get this to work using 10.0 and having trouble. Throws an error using arcpy.de.SearchCursor() and I cant seem to find a workaround using just arcpy.SearchCursor() using the variables listed. Could this be modified to use with 10.0? – cbannon Mar 31 '16 at 16:15
  • You can read (help.arcgis.com/En/Arcgisdesktop/10.0/Help/index.html#//…) and write (help.arcgis.com/En/Arcgisdesktop/10.0/Help/index.html#/…) geometries using the old cursors, but you'll have to figure that out yourself. The 10.0 Polyline object also does not have a positionAlongLine method, so you'll have to do a lot more trigonometry and bookkeeping. – phloem Mar 31 '16 at 16:35
4

A possible solution is:

  1. Create a field that will hold the rotation you will want to apply
  2. Populate this field with the rotation values
  3. Run the Table To Ellipse (Data Management) tool to create ellipses centre on each point rotated.
  4. Run the Minimum Bounding Geometry (Data Management) with RECTANGLE_BY_WIDTH set.

The problem is creating those rotation values, a possible way is this:

  1. Run the Split Line At Vertices (Data Management)
  2. Run Add Geometry Attributes (Data Management) with LINE_BEARING
  3. Run Spatial Join to pass back the bearing held in the line dataset into you point dataset.

This logic assumes you have an Advance license.

  • LINE-BEARING is the bearing from start of line to end of line. This might not be desired. I propose, buffer each Point by 1 meter, clip the roads by buffers, add Bearing-line to small segements. Join back to Points. Good post. – klewis Mar 22 '16 at 22:01
  • I was thinking the bearing would have been on the line segment split by the vertices, but there are issues with that, such as a dense sequence of vertices, I think you idea of buffering would be a more robust option. – Hornbydd Mar 22 '16 at 23:41
  • This method works but it seems that for any ellipse that is not on a right angle (90 or 0 degrees) I get an skewed rectangle (parallelogram). Where the line is roughly 45 degrees the Minimum Bounding Geometry tool give me a skewed rectangle with a width of ~420m instead of the desired 400m. – cbannon Mar 24 '16 at 20:42

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.