Select only streets that run north-south

2253
7
02-10-2017 05:48 AM
ShannonGrumbly2
New Contributor III

Is it possible to select streets based on their direction? I am actually missing the N or S prefix for my street names, so I cannot filter based on that criteria. I am hoping to select these streets to run a field calculator that enters the prefix direction. 

Any help would be greatly appreciated!

Tags (2)
7 Replies
ChrisDonohue__GISP
MVP Alum

In theory, if one has an Advanced license, one could do this:

1.  Use Feature Vertices to Points with the Both Ends option to create end points for each street.  Feature Vertices To Points—Data Management toolbox | ArcGIS Desktop 

2. Copy the Feature Class.

3.  Use Generate Near Table, using the original and the copy, and with the ANGLE option on

ArcGIS Help 10.1 

4.  Cull the data down in the resulting output table to the original end points and check the angle of matching points (the output will be a comparison of all points to all points, so will be many records, of which only a few are actually needed).

  • I suspect some Spatial Joins will be needed here to keep the points correlated with the roads so one can eliminate the many unneeded angle results generated.  Alternatively, one could work out this process in Modelbuilder by Iterating just one street at a time so as to keep the confusion down.

  • Also, note the angle directions comment from the Generate Near Table tool help:

A near angle measures from the x-axis (horizontal axis) to the direction of the line connecting an input feature to its nearest feature at their closest locations, and it is within the range of 0 to 180 or 0 to -180 decimal degrees - 0 to the east, 90 to the north, 180 (-180°) to the west, and -90 to the south.

In other words, the measuring starts at zero with East, then increases as one goes counter-clockwise to North, producing positive angles.  So North is 90 degrees, increasing to a maximum of 180 degrees at West.

Starting at East and heading to South (clockwise), the angle values are increasingly negative.  East is zero and South is -90, and West is at -180.

As a result, to query out North for your streets, try checking for an angle between 45 and 135.  For South, check  between -45 to -135.

Caveats:

This assumes the street data is set up in a way that allows checking of whole streets.  If a street is segmented out into multiple pieces, a Dissolve or other aggregation method may be needed to combine the pieces.

Also, I suspect there probably is a refined way to do all this in Python.  I'll leave suggestions on how to do that to the many Python guru's we have here on GeoNet.

Chris Donohue, GISP

EDIT:  cleaned up some wording for clarity

ShannonGrumbly2
New Contributor III

This is a great work-around! I will try this out and let you know how it goes. Thank you!

0 Kudos
IanMurray
Frequent Contributor

Tagging several people who might already have solutions to something like this.

rfairhur24

xander_bakker

timw1984

Dan_Patterson

DanPatterson_Retired
MVP Emeritus

I just have a bunch of field calculator defs that work with angles, that may be useful

RichardFairhurst
MVP Honored Contributor

I find that it very useful to calculate the X and Y coordinates of both ends of my network lines into four separate double fields stored in the line attributes (FROM_X, FROM_Y, TO_X, TO_Y).  You can easily calculate the values for these fields using the geometry calculator.  I automatically maintain these fields whenever I edit the line geometry using the Attribute Assistant X_Coordinate and Y_Coordinate methods with the S (start) or E (end) value options (only works for geodatabases, not shapefiles).  As Chris Donohue, GISP, said, if your network is highly segmented and you want the overall trend of a complete road you would need to use the Dissolve tool, Create Route tool or some other aggregating tool first to get those trends.

Once you have these coordinate fields it is relatively straight forward to select the lines that are trending more north/south than east/west provided you are using a projected coordinate system (which expresses coordinates in linear units like meters or feet) and your lines are not very sinuous (curvy) or multipart lines with large gaps or self intersections.  You can do a selection like:

To find north trending lines:

ABS(TO_X - FROM_X) < ABS(TO_Y - FROM_Y) AND FROM_Y < TO_Y

To find south trending lines:

ABS(TO_X - FROM_X) < ABS(TO_Y - FROM_Y) AND FROM_Y > TO_Y

If you are concerned that some of your roads are very curvy you can separate those roads as long as you have a length field (for geodatabases this is built-in, but for shapefiles you have to calculate this using the geometry calculator or a python calculation).  Assuming the linear units of the length and coordinates are the same kind, you can exclude sinuous roads by adding this clause to the selection expressions above (this SQL works for a File Geodatabase and shapefile and may not work for Personal Geodatabases and enterprise geodatabases like SQL Server and Oracle):

AND Shape_Length / Power((Power(From_X - To_X, 2) + Power(From_Y - To_Y, 2)), 0.5) <= 1.5

Change the above expression to > 1.5 to find roads that are considered meandering.  You can adjust the sinuosity factor number (1.5) to higher numbers to tolerate more curviness, or to lower numbers to tolerate less curviness.  A perfectly straight singlepart line has a sinuosity factor of 1.  Closed lines (lines that start and end at the same point) have an infinite or divide by zero error sinuosity factor.  Multipart lines with large gaps between parts can have a sinuosity factor that is less than 1.

leahmaps
Occasional Contributor II

Is there a good way to use expressions (like you have above) for east/west roads?

0 Kudos
DanPatterson_Retired
MVP Emeritus

forgot the link.. here is one, you can substitute the firstpoint last point with a variety of other combinations.  for example, you can formulate a

  • 'start point' or 'end point' from
    • as show in the script... just using shape field and the first and last point
    • [ !shape.extent.Xmin!, !shape.extent.Ymin!]  (similarly for the end point)
    • [ !Xo!, !Yo!]   where Xo and Yo are existing fields of whatever name in your tablle

You get the drift. 

I have similar functions that enable you to feed in the whole geometry and calculate the Interpoint angles from which you can determine average, dominant or other directions types.  The numpy import is only done once.  The fromNorth allows you to do angles as they relate to the x-axis or from North, depending on what you want... fromNorth=True would be what you would set line_dir to

import numpy as np
def line_dir(orig, dest, fromNorth=False):
    """Direction of a line given 2 points
    : orig, dest - two points representing the start and end of a line.
    : fromNorth - True or False gives angle relative to x-axis)
    :
    """
    orig = np.asarray(orig)
    dest = np.asarray(dest)
    dx, dy = dest - orig
    ang = np.degrees(np.arctan2(dy, dx))
    if fromNorth:
        ang = np.mod((450.0 - ang), 360.)
    return ang

# Expression box
line_dir(!shape.firstPoint!, !shape.lastPoint!)