I am using ArcGIS 10.4.1 for Desktop with a Standard level license and the Network Analyst extension.
It does not seem to be possible to create an ArcGIS network dataset except via manually running the New Network Dataset wizard from the Catalog window.
However, what I am hoping to do is to use ArcPy to read turn information in a table and create turns in a Turn Feature Class. Below I will present some ArcPy code which creates a file geodatabase, a feature dataset, a test street feature class and a table containing two fields FromStreetID and ToStreetID. In the example a row of FromStreetID = 8 and ToStreetID = 9 would mean that a turn from StreetID = 8 onto StreetID = 9 is NOT permitted.
To build the test data I run the script below from IDLE which takes about 10 seconds. Be warned that it will delete a file geodatabase named C:\Temp\test.gdb
, if it already exists.
import arcpy
if arcpy.Exists("C:/Temp/Test.gdb"):
arcpy.Delete_management("C:/Temp/Test.gdb")
arcpy.CreateFileGDB_management("C:/Temp","Test","CURRENT")
arcpy.CreateFeatureDataset_management("C:/Temp/Test.gdb","TestFD")
arcpy.CreateFishnet_management("C:/Temp/Test.gdb/TestFD/TestFishnetPoly",
"0 0","0 1","1","1","4","4","",
"NO_LABELS","DEFAULT","POLYGON")
arcpy.Intersect_analysis("C:/Temp/Test.gdb/TestFD/TestFishnetPoly #",
"C:/Temp/Test.gdb/TestFD/TestFishnetLine",
"ALL","-1 Unknown","LINE")
arcpy.Dissolve_management("C:/Temp/Test.gdb/TestFD/TestFishnetLine",
"C:/Temp/Test.gdb/TestFD/TestStreet",
"","","SINGLE_PART","DISSOLVE_LINES")
arcpy.AddField_management("C:/Temp/Test.gdb/TestFD/TestStreet","StreetID","LONG")
arcpy.CalculateField_management("C:/Temp/Test.gdb/TestFD/TestStreet","StreetID","!OBJECTID!","PYTHON_9.3")
arcpy.CreateTable_management("C:/Temp/Test.gdb","TurnNotAllowed")
arcpy.AddField_management("C:/Temp/Test.gdb/TurnNotAllowed","FromStreetID","LONG")
arcpy.AddField_management("C:/Temp/Test.gdb/TurnNotAllowed","ToStreetID","LONG")
cursor = arcpy.da.InsertCursor("C:/Temp/Test.gdb/TurnNotAllowed",["FromStreetID","ToStreetID"])
cursor.insertRow([8,9])
del cursor
arcpy.CreateTurnFeatureClass_na("C:/Temp/Test.gdb/TestFD","TestTurnFC","5")
arcpy.AddField_management("C:/Temp/Test.gdb/TestFD/TestTurnFC","TurnPermitted","SHORT")
I open ArcMap and in the Catalog window I right click on `C:\Temp\Test.gdb\TestFD and create a New > Network Dataset
- On the first panel accept the defaults by clicking Next
- On the second panel tick the checkbox next to TestStreet and click Next
- On the third panel (about turns) accept the defaults by clicking Next
- On the fourth panel (about connectivity) accept the defaults by clicking Next
- On the fifth panel (about elevation) click the radio button for None and click Next
- On the sixth panel click Add
- In the Add New Attribute dialog set Name: TurnPermitted, Usage Type: Restriction, Restriction Usage: Prohibited, and then click OK
- Click Next
- On the seventh panel (about travel mode) accept the defaults by clicking Next
- On the eighth panel (about driving directions) accept the defaults by clicking Next
- On the ninth panel (about building a service area index) accept the defaults by clicking Next
- On the tenth panel click Finish
- Click Yes to build the network dataset
- Click Yes to draw all datasets and ignore the no spatial reference warning
- Zoom To Layer on the TestFD_ND_Junctions layer and label the TestStreet layer using the StreetID field to get the display below.
Manually, I know how to look for a route between the junction that I have circled and labelled 1 in red to the one labelled 2.
- I open the Network Analyst toolbar
- from its pulldown I start a New Route
- I use the Create Network Location Tool to click on the two locations, and then click Solve
- I get the expected route through street IDs of 8, 9, and then 1
- I remove the Route from the Table of Contents
To stop turns being allowed from street 8 onto 9 I start editing the TestTurnFC layer and Continue past the warning.
- I open the Create Features window from the Editor toolbar and choose the TestTurnFC template to click anywhere along the edge which is Street 8 and then double-click anywhere on the edge which is Street 9. (Make sure Edge Snapping is set).
- While the turn is still selected I open the Attributes window and set TurnPermitted: -1
- I Save and Stop Editing.
- In the Catalog window I open the Properties of TestFD_ND to the Attributes tab and with TurnPermitted highlighted I click Evaluators
- On the Default Values tab I use the Evaluator Properties of element Turn to set a value of -1 and then make sure the Value is still Ignore Restriction.
- I click OK twice to accept the settings that I just made.
I build the network dataset from the Network Analyst toolbar.
When I start a New Route and use the same junctions as before the route I get goes through streets 8, 10, 11, 3 and 1 i.e. it observes the no right turn from street 8 to 9.
This is a lot of preamble to get to where I am stuck.
Is it possible to use ArcPy to automate the editing steps above where instead of manually digitizing from the edge on street 8 to the edge on street 9, I instead read those street IDs from the TurnNotAllowed table, and create the turns using code?
My attempt to automate the editing steps above is to run the code below which:
- uses list comprehension with a SearchCursor to make a list of prohibited turns
- uses dictionary comprehension with a SearchCursor to make a dictionary of point locations which are halfway along the streets from and to which turns are not allowed
- uses an Editor session to run an InsertCursor
- uses the InsertCursor to add a polyline from the midpoint of the street into the turn to the midpoint of the street that comes out of the turn.
import arcpy
turnNotAllowedList = [[row[0],row[1]] for row in arcpy.da.SearchCursor(
"C:/Temp/Test.gdb/TurnNotAllowed",["FromStreetID","ToStreetID"])]
print turnNotAllowedList
streetDict = {row[1]: row[0].positionAlongLine(0.5,True).firstPoint for row in arcpy.da.SearchCursor(
"C:/Temp/Test.gdb/TestFD/TestStreet", ["SHAPE@","StreetID"])}
print streetDict
edit = arcpy.da.Editor("C:/Temp/Test.gdb")
edit.startEditing(False,False)
edit.startOperation()
cursor = arcpy.da.InsertCursor("C:/Temp/Test.gdb/TestFD/TestTurnFC",
["SHAPE@","TurnPermitted"])
for turn in turnNotAllowedList:
array = arcpy.Array()
array.add(streetDict[turn[0]])
array.add(streetDict[turn[1]])
polyline = arcpy.Polyline(array)
cursor.insertRow([polyline,-1])
del cursor
edit.stopOperation()
edit.stopEditing(True)
The code appears to successfully add the turn feature (arrowed below). However, when I try to build the network the Network Dataset Build Report says:
The network was built, but with some errors. The network can still be used in analysis, however the invalid features will not participate in the network.
Clicking the Show Build Errors button indicates that it is the turn I created using the InsertCursor which is the problem:
SourceName: TestTurnFC, ObjectID: 1, Cannot interpret turn row.
My question therefore remains as above:
Is it possible to use ArcPy to automate the editing steps above where instead of manually digitizing from the edge on street 8 to the edge on street 9, I instead read those street IDs from the TurnNotAllowed table, and create the turns using code?