True Arcs, Geocoding and other musings

3016
10
06-17-2020 01:52 PM
JoeBorgione
MVP Emeritus

Earlier today I discovered a short coming with creating a street address role locator in Pro 2.5.1 if the feature class contains true arcs.  (See: ArcGIS Pro Create Locator fails (again/still)  The Create Locator tool can't handle a true arc and will fail when they are present in the feature class.  It's my understanding that the problem will be fixed in version release 2.6. 

Full disclaimer:  I'm going to let my old school flag fly freely here.  I have never in my life created a true curve and I have no intention of ever doing so; I have never seen the point in them and have experienced problems with them in the past when it comes to geocoding.  That said...

Over the past few hours I've learned more about true arcs than I have in the past 25 years. It seems that in transportation design, the cool, trendy thing is to have are Roundabouts: intersections that allow a continual flow of traffic instead of the traditional 4 way stop. And in order to represent these features in ArcGIS, the cool, trendy thing is use true arcs. Here is an example of a 5 way roundabout not far from where I live:

The direction of travel in the roundabout is counter clock wise.

  

True arcs can have just two vertices (from/to node) or multiple as shown above.  That makes it hard to find them, and I'm not aware of a specific tool or function that can identify them.  I fooled around a little bit armed with the information provided in Counting vertices via Python  and added a long integer field called VertexCount to my feature class calculating its value with !shape.pointcount!  in the field calculator.  I selected for just those records where VertexCount = 2.  The problem with that is all the streets that run straight from one intersection to the next only have two vertices, but I was able to find a bunch of roundabouts including the one in my example.

There are two methods with which to convert true arcs into traditional lines.  You can export the feature class with true arcs to a shapefile, and then import the shapefile back into a feature class.  Shapefiles don't support true arcs.  Here is the same roundabout that I applied this method to:

You can see there are way more vertices from the conversion to shapefile , and the shape itself looks okay to me.

The other method is to use the Densify Tool ; if you apply this tool to an entire feature class, it will add vertices in a lot of places you don't want them, like those straight intersection to intersection segments. However,  the cool thing about this tool is you get to set the interval of vertices, and as an added bonus it honors the selected set. 

Let's pretend that I just added 5 true arcs to my centerlines representing a roundabout, but I know in another department, an old gray-hair guy insists true arcs mess with his geocoding.  With the densify tool I can keep him happy and preserve the shape of my roundabout:

Okay, a 6 inch vertex interval is pretty extreme, but the old guy in the other department can really be a grouch:

Now each segment of the roundabout has all sorts of vertices and looks really good, the single block straight line segments remain the same, and there are no more issues with creating a street address role locator in Pro 2.5.1.

Is this a great time to be alive or what?

That should just about do it....
0 Kudos
10 Replies
JoeBorgione
MVP Emeritus

Bill Fox: you got my votes for the greater good...  

That should just about do it....
JoshuaBixby
MVP Esteemed Contributor

If you want to find polylines with curves, iterate over the feature class and find the ObjectIDs that have a shape.JSON containing "curvePaths".  You can then use the ObjectIDs to select only those features and densify them instead of doing all the features.

JoeBorgione
MVP Emeritus

Joshua Bixby-  Following your lead, I was hoping something like this would work for me:

for row in arcpy.da.SearchCursor('OriginalCenterlines',['OBJECTID','SHAPE@JSON']):
    if 'curvePaths' in row[1]:
        print(row[0])‍‍‍‍‍‍
    else:
        print('Nope')

If I have a true curve manually selected it works, in a python window in Pro.  If I run it as a stand alone script, it returns 1520 object ids, and I know there aren't  that many.  For example, running it in a spyder console and then selecting one of the oids  back in Pro gives me this centerline:

I guess I don't understand the difference between shape.JSON and SHAPE@JASON.

How does one use shape.JSON ?

That should just about do it....
0 Kudos
JoshuaBixby
MVP Esteemed Contributor

They are the same, just different ways of getting the same information.  Using the fields of ["OID@", "SHAPE@JSON"] would probably perform faster than ["OID@", "SHAPE@"] .

Depending on how the lines were created, you can have a curved path that looks exactly like a straight line, but it is still technically a curved path.  If you are finding more curved paths than expected by looking at the data set, you are likely finding linear segments that were created using tools that stored them as curved paths.

0 Kudos
JoeBorgione
MVP Emeritus

...you are likely finding linear segments that were created using tools that stored them as curved paths...

 

Interesting.  That makes it kind of tough then if I want to densify those that are selected, I'll end up with a bunch of 'straight' true curves with however many vertices I choose.  But, there should be a way to backtrack, and clean them up...  Thanks!

That should just about do it....
0 Kudos
JoshuaBixby
MVP Esteemed Contributor

Joe, a curved path that is effectively a linear segment will have a differently looking JSON than curved path with a curve.  I don't have time to pull up an example now, but take a look at the JSON from a curve path feature that has an obvious curve and one that appears straight.

0 Kudos
JoeBorgione
MVP Emeritus

Some final thoughts...

True curves allow for some very detailed cartographic representations of linear features, but they can and do raise havoc at times in other geoprocessing functions.  The data I'm working with comes from an enterprise GDB upon which multiple agencies perform edits.  If someone wants to use a true curve to represent a fairly straight segment, it seems to me they should go right ahead and do so; I'm just an end consumer of the data.  My days of database management standards and procedures are behind me. 

I've always been of the opinion that an enterprise GDB is great place to store and edit data, especially if there is a need for multiple editors, but for doing day to day work such as geocoding, I much prefer the file geodatabase.  Since I am copying the egdb feature class to a fgdb, it's easy enough for me to apply the following python utility to the data prior to using create locator:

fc = r'Path:\to\fgdb\centerlines'
fields = ['OID@', 'SHAPE@JSON']

arcpy.MakeFeatureLayer_management(fc, 'fcLayer')  
with arcpy.da.SearchCursor('fcLayer', fields)as cursor:
    for row in cursor:     
        select = f'OBJECTID = {row[0]}'
        if 'curvePaths' in row[1]:
            arcpy.SelectLayerByAttribute_management('fcLayer', 'ADD_TO_SELECTION', select)
arcpy.Densify_edit('fcLayer', 'DISTANCE', 200)

I could probably bump up the 200 foot value to something quite a bit higher, and I may experiment with that just to see if the geoprocessing time drops which would also equate to a drop in cpu usage so our server that runs our overnight scheduled tasks isn't hit too hard. I do know that after running the above script, and then running the next one below, I don't get any returns, and that's all I'm after.

with arcpy.da.SearchCursor(fc, fields)as cursor:
    for row in cursor:
        if 'curvePaths' in row[1]:
            print(row)

As always, my thanks to Joshua Bixby for taking the time to show me the way...

That should just about do it....
0 Kudos
JoshuaBixby
MVP Esteemed Contributor

Joe, for fun, change line 8 to the following and let me know what the difference is:

if any(s in row[1] for s in ('"a"', '"b"', '"c"')):

If you have straight lines stored as curved paths, the above should leave those alone, so your second code snippet should still fine some records with 'curvePaths' in it.

0 Kudos