Rotate Polylines based on Existing Polyline Angles

6568
30
Jump to solution
01-14-2019 02:17 PM
AdamThompson3
New Contributor III

Hey Everyone,

Currently what I am trying to do is rotate polylines in certain grid sections to the same orientation of existing polylines in that grid. This seems to be quite complicated as I have looked quite extensively for something like this, but to no success. Currently right now I have implemented a tool that was posted here arcpy - Drawing parallel lines inside polygons (Well Paths) using ArcGIS Desktop? - Geographic Infor...  now this works for me and does what it should... but here is my problem posted below. 

The grid lines that were drawn automatically (yellow) are  horizontal where as I would like them be vertical. If there is a way through arcpy or other tools where I can rotate these lines to be the same direction of polylines that are currently there. This is just one example, this happens many times in different areas. 

Thank you!

30 Replies
DanPatterson_Retired
MVP Emeritus

Adam, I would stick with Randy's since it is designed to work with the azimuth within a table.

Mine is designed to work from scratch given a start location, known distance and azimuths.

It is in essence the line version of my...

Sampling Grids for ArcGIS Pro

I have a point version in my Point Tools toolbox, also on the code sharing site (search using Dan_Patterson should bring up a list

AdamThompson3
New Contributor III

Thanks Dan, I will take a look and see what I can find, thank you!

0 Kudos
AdamThompson3
New Contributor III

Have been trying to replicate an inside buffer and intersect lines within that buffer while iterating through it all but keep running into this error... Not sure what is occurring.

Here is the code so far;

Once again have to thank you for all the help and cooperation with me! 

0 Kudos
DanPatterson_Retired
MVP Emeritus

You are going to have to throw in some print statements to make sure 'feature' does indeed have at least 2 points

The 'cursor' people may want to jump in... my eyes tend to glaze over when I see them

Also

/blogs/dan_patterson/2016/08/14/script-formatting 

help us refer line numbers to people

0 Kudos
AdamThompson3
New Contributor III

Hey Dan, Thanks for the tips and link on that! Was helpful, 

I created a print statement for 'feature' and was returned with this. 

so I do believe those features have at least 2 points, unsure as to why the intersect on the buffer is not working. 

0 Kudos
RandyBurton
MVP Alum

I've been experimenting with some code that I will post later today or this weekend.

Right now I'm wondering if the spatial reference is getting messed with when creating "polyBuff".  Lines 1-2 and 6 clarify the spatial reference.

desc = arcpy.Describe(polyFC)
SR = desc.spatialReference

# ... 

with arcpy.da.SearchCursor(polyFC,['SHAPE@', 'AVGAzimuth'], spatial_reference=SR) as cursor:
    for row in cursor:
        polyBuff = row[0].buffer(buffNum * -1)‍‍‍‍‍‍‍
0 Kudos
AdamThompson3
New Contributor III

It's possible, I will look into the spatial referencing issues possibly, but thanks again Randy for that, this has proved to be quite a challenging project to complete! But cant thank you all enough for the help.

0 Kudos
RandyBurton
MVP Alum

Here is the code I've been working with:

import arcpy
import math

def rotate(origin, point, angle):
    """
    Rotate a point counterclockwise by a given angle around a given origin.
    The angle should be given in radians.
    """
    ox, oy = origin
    px, py = point

    qx = ox + math.cos(angle) * (px - ox) - math.sin(angle) * (py - oy)
    qy = oy + math.sin(angle) * (px - ox) + math.cos(angle) * (py - oy)
    return qx, qy

def make_lines(origin, num_lines, length, spacing, bearing):
    '''
    origin = (x,y), num_lines = number of lines to draw
    length = 1/2 length of line, spacing = spacing between lines
    bearing = angle to rotate points
    '''
    angle = math.radians(360.0 - bearing)
    lines = []
    x, y = origin
    # set up line points before rotation
    xx = x - (spacing*(num_lines-1))/2.0
    y1 = y + length*1.0
    y2 = y - length*1.0
    # rotate points around origin
    for lns in range(1,num_lines+1):
        qx1,qy1 = rotate((x,y),(xx,y1),angle)
        qx2,qy2 = rotate((x,y),(xx,y2),angle)
        lines.append([qx1,qy1,qx2,qy2])
        xx += spacing
    return lines

# set input/output parameters
polyFC = arcpy.GetParameterAsText(0)      # input polygons : feature layer, input
angleField = arcpy.GetParameterAsText(1)  # field containing rotation: field, input (obtained from param0)
numLines = arcpy.GetParameterAsText(2)    # number of lines: long, input
lineSpacing = arcpy.GetParameterAsText(3) # line spacing : linear unit, input
buffDist = arcpy.GetParameterAsText(4)    # inner buffer distance : linear unit, input
outLines = arcpy.GetParameterAsText(5) # output parallel lines : feature class, output

desc = arcpy.Describe(polyFC)
SR = desc.spatialReference # get spatial reference of polygon feature
arcpy.env.overwriteOutput = True # output will be overwritten
arcpy.env.outputCoordinateSystem = SR # set default spatial reference

D2M = { # dictionary for converting distance to meters
    'CENTIMETERS': .01,
    'DECIMALDEGREES': 1.0, # no conversion
    'DECIMETERS': 0.1,
    'FEET': 0.3048, # 0.304800609601 Foot_US
    'INCHES': 0.0254,
    'KILOMETERS': 1000.0,
    'METERS': 1.0, # no conversion
    'MILES': 1609.344,
    'MILLIMETERS': 0.001,
    'NAUTICALMILES': 1852.0,
    'POINTS': 0.000352778,
    'UNKNOWN': 1.0, # no conversion, tool may select data frame default
    'YARDS': 0.9144,
    }

# parse numbers from parameters
numLines = int(numLines)

lineSpacing = lineSpacing.split(' ')
if lineSpacing[1].upper() not in D2M.keys():
    lineSpacing[1] = 'UNKNOWN'
spacing = float(lineSpacing[0]) * D2M[lineSpacing[1].upper()] * SR.metersPerUnit # convert spacing unit to meters, then to SR units

buffDist = buffDist.split(' ')
if buffDist[1].upper() not in D2M.keys():
    buffDist[1] = 'UNKNOWN'
buffNum = float(buffDist[0]) * D2M[buffDist[1].upper()] * SR.metersPerUnit # convert buffer unit to meters, then to SR units

# list to save lines
lines = []

with arcpy.da.SearchCursor(polyFC,['SHAPE@', angleField]) as cursor:
    for row in cursor:
        # buffer for trimming lines to polygon
        polyBuff = row[0].buffer(buffNum * -1)

        # get centroid of polygon
        centroid = row[0].centroid

        # find distance from centroid to farthest point, this will be used as 1/2 line length     
        dist = 0
        for part in row[0]:
            for pnt in part:
                cent_vert_dist = arcpy.PointGeometry(pnt).distanceTo(centroid)
                if cent_vert_dist > dist:
                    dist = cent_vert_dist

        # pass centroid, dist for 1/2 line length, and other variables to make lines
        mkLns = make_lines(((centroid.X, centroid.Y)), numLines, dist, spacing, row[1])

        # make polyline and trim to buffer
        for ln in mkLns:
            pl = arcpy.Polyline(arcpy.Array([arcpy.Point(ln[0],ln[1]), arcpy.Point(ln[2],ln[3])]), SR)
            bufLn = pl.intersect(polyBuff,2)
            lines.append(bufLn)

# save lines to feature class
arcpy.CopyFeatures_management(lines,outLines)‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

I added some code to better deal with the linear units; the original tool ignored the type of unit selection and used the default from the spatial reference.  I create points for the ends of the lines and then rotate the points using the polygon's centroid as the rotation origin.  The length for the lines (before buffering) is twice the distance from the centroid to the far point.

I've modified and added to the tool parameters:

Script tool dialog

And the results:

Line results

Hope this helps.

AdamThompson3
New Contributor III

Hey Randy! 

This looks almost exactly what I was trying to achieve, when I am in the office tomorrow I will give it a go! Thank you very much for this! I have been reading it over and researching a lot of the stuff you used so I can understand it fully! Outside of forum postings here, and if you're comfortable of course do you have any other form of communication I could possibly send questions should I have them? I am not sure of GeoNet's messaging or other communication means it has! 

Thank you again.  

Cheers

Adam

0 Kudos
matthewgarner
New Contributor

I am just getting into the Python scripting world, so my understanding of all the logic is very basic at this point, but I have found the script you put together extremely helpful.  The script works wonderful on solid polygons but I am having a issue when it comes to polygons with holes it is giving me this error message, if you have any idea on how I could correct this or lead me in the right direction I would really appreciate it.

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Thank you

0 Kudos