I know this isn't possible in arcpy, so I thought I'd give it a stab using comtypes and a modified version of the Snippets module. I am trying to translate some of the VB.NET code to create a network dataset from the help docs.
I'm trying to automate this process for an engineer as he is not very experienced with GIS. He will always be using a file geodatabase with one line feature class as edge features (fc name will vary) and two point feature classes as junction features (sources and sinks to act as origins and destinations). Once the network is built, he can use other script tools I am providing him to do the whole analysis.
I think I am pretty close as I am able to create the edge and junction features, but my code is failing at one of the last steps which is getting the extension for the IFeatureDatasetExtensionContainer interface via the FindExtension method. The help docs say I can use the esriGeoDatabase.esriDatasetType constant but this seems to be causing an error. Here is the snippet that is failing:
# create network data set based on data element
fdxc = CType(fdsGDS, esriGeoDatabase.IFeatureDatasetExtensionContainer)
fdx = fdxc.FindExtension(esriGeoDatabase.esriDatasetType(7)) # get error here
dsc = CType(fdx, esriGeoDatabase.IDatasetContainer2)
netds = CType(dsc.CreateDataset(nd), esriGeoDatabase.INetworkDataset)
And here his my error:
Traceback (most recent call last):
File "\\arcserver1\GIS\_Resources\ESRI\Python\BMI_Library\arcobjects\network_dataset.py", line 114, in <module>
create_nd(gdb)
File "\\arcserver1\GIS\_Resources\ESRI\Python\BMI_Library\arcobjects\network_dataset.py", line 98, in create_nd
fdx = fdxc.FindExtension(esriGeoDatabase.esriDatasetType(7))
COMError: (-2147220729, None, (u'The specified feature dataset extension type was not found.', u'Esri GeoDatabase', u'esri_csGeoDatabase.hlp', 0, None))
And here is my full code if that helps:
from arcobjects import * #my modified version of Snippets (has CType and NewObj functions) import arcpy, os def create_nd(gdb): arcpy.CheckOutExtension('Network') getModule('esriGeoDatabase.olb') getModule('esriDataSourcesGDB.olb') import comtypes.gen.esriGeoDatabase as esriGeoDatabase import comtypes.gen.esriDataSourcesGDB as esriDataSourcesGDB # create new empty data element for buildable network dataset nd = NewObj(esriGeoDatabase.DENetworkDataset, esriGeoDatabase.IDENetworkDataset2) nd.Buildable = True nd.NetworkType = esriGeoDatabase.esriNetworkDatasetType(1) # open feature dataset and create IGeoDataset interface pWSF = NewObj(esriDataSourcesGDB.FileGDBWorkspaceFactory, esriGeoDatabase.IWorkspaceFactory) gdbWSF = CType(pWSF, esriGeoDatabase.IWorkspaceFactory) print gdbWSF.WorkspaceDescription, gdbWSF.WorkspaceType gdbFWS = CType(gdbWSF.OpenFromFile(gdb, 0), esriGeoDatabase.IFeatureWorkspace) print gdbFWS openFWS = gdbFWS.OpenFeatureDataset('FlowNet') fdsGDS = CType(openFWS, esriGeoDatabase.IGeoDataset) print fdsGDS # copy feature dataset's extent and spatial reference to network dataset element deGDS = CType(nd, esriGeoDatabase.IDEGeoDataset) deGDS.Extent = fdsGDS.Extent deGDS.SpatialReference = fdsGDS.SpatialReference print deGDS.Extent # get flow lines arcpy.env.workspace = os.path.join(gdb, 'FlowNet') lines = arcpy.ListFeatureClasses('*', 'Polyline')[0] # create data element dataElement = CType(nd, esriGeoDatabase.IDataElement) dataElement.Name = 'FlowNetwork' print dataElement.Name # specify edge features edgeNet = NewObj(esriGeoDatabase.EdgeFeatureSource, esriGeoDatabase.INetworkSource) edgeNet.Name = lines edgeNet.ElementType = esriGeoDatabase.esriNetworkElementType(2) ## edgeNet.SourceType = esriGeoDatabase.esriNetworkSourceType(3) ## edgeNet.UsesGeometryInConnectivity = True #set edge feature's connectivity (Any Vertex) edgeFS = CType(edgeNet, esriGeoDatabase.IEdgeFeatureSource) edgeFS.UsesSubtypes = False edgeFS.ClassConnectivityGroup = 1 edgeFS.ClassConnectivityPolicy = esriGeoDatabase.esriNetworkEdgeConnectivityPolicy(0) # any vertex # add sources and sinks as junctions # make origins sinkNet = NewObj(esriGeoDatabase.JunctionFeatureSource, esriGeoDatabase.INetworkSource) sinkNet.Name = 'Sinks' sinkNet.ElementType = esriGeoDatabase.esriNetworkElementType(1) ## sinkNet.SourceType = esriGeoDatabase.esriNetworkSourceType(2) ## sinkNet.UsesGeometryInConnectivity = True # set connectivity sinkElm = CType(sinkNet, esriGeoDatabase.IJunctionFeatureSource) sinkElm.UsesSubtypes = False sinkElm.ClassConnectivityGroup = 1 sinkElm.ClassConnectivityPolicy = esriGeoDatabase.esriNetworkJunctionConnectivityPolicy(0) # honor # make destinations sourceNet = NewObj(esriGeoDatabase.JunctionFeatureSource, esriGeoDatabase.INetworkSource) sourceNet.Name = 'Sources' sourceNet.ElementType = esriGeoDatabase.esriNetworkElementType(1) ## sourceNet.SourceType = esriGeoDatabase.esriNetworkSourceType(2) ## sourceNet.UsesGeometryInConnectivity = True # set connectivity sourceElm = CType(sourceNet, esriGeoDatabase.IJunctionFeatureSource) sourceElm.UsesSubtypes = False sourceElm.ClassConnectivityGroup = 1 sourceElm.ClassConnectivityPolicy = esriGeoDatabase.esriNetworkJunctionConnectivityPolicy(0) # honor # add attributes (Length, Cost, Units) evalNetAttr = NewObj(esriGeoDatabase.EvaluatedNetworkAttribute, esriGeoDatabase.IEvaluatedNetworkAttribute) newAttr2 = CType(evalNetAttr, esriGeoDatabase.INetworkAttribute2) newAttr2.Name = 'Length' newAttr2.UsageType = esriGeoDatabase.esriNetworkAttributeUsageType(0) # cost newAttr2.DataType = esriGeoDatabase.esriNetworkAttributeDataType(2) # double newAttr2.Units = esriGeoDatabase.esriNetworkAttributeUnits(3) # feet newAttr2.UseByDefault = True # create network data set based on data element fdxc = CType(fdsGDS, esriGeoDatabase.IFeatureDatasetExtensionContainer) fdx = fdxc.FindExtension(esriGeoDatabase.esriDatasetType(7)) dsc = CType(fdx, esriGeoDatabase.IDatasetContainer2) netds = CType(dsc.CreateDataset(nd), esriGeoDatabase.INetworkDataset) # build network dataset now that it's created netBuild = CType(netds, esriGeoDatabase.INetworkBuild) for item in [edgeNet, sinkNet, sourceNet]: netBuild.AddSource(item) netBuild.AddAttribute(newAttr2) netBuild.BuildNetwork(fsdGDS.Extent) arcpy.CheckInExtension('Network') return if __name__ == '__main__': gdb = r'C:\Users\calebma\Desktop\NA_Testing\test2.gdb' create_nd(gdb) print 'done'
Just found one flaw, I should be using the value 19 for esriDTNetworkDataset. Changing that fixed that issue, but now I'm having other problems. I am almost there. It is creating the network dataset but now it is choking on adding the sources and sinks.
What is the new error?
Sorry, I should have posted that. Here is my error:
Traceback (most recent call last):
File "\\arcserver1\GIS\_Resources\ESRI\Python\BMI_Library\arcobjects\network_dataset.py", line 128, in <module>
create_nd(gdb)
File "\\arcserver1\GIS\_Resources\ESRI\Python\BMI_Library\arcobjects\network_dataset.py", line 120, in create_nd
netBuild.AddSource(item)
COMError: (-2147024809, 'The parameter is incorrect.', (u'A network source with the specified name does not exist.', u'Esri GeoDatabase', u'esri_csGeoDatabase.hlp', 0, None))
It is successfully creating the network dataset adding the edge features with Any Vertex as the connectivity policy , but it does not like my junction features for some reason. I do not know if I will be able to get this to work as I expected. It is too messy with Python.
I'm obviously not familiar with the data your working with, but a few observations:
- Have you tried putting in print() statements to see if the values are what you would expect? I would suggest putting a bunch of them between line 104 and 105 to print the various attributes of the "item" variable. Something might come to light there.
- Also, I noticed that on line 45 you wrote
edgeNet.Name = lines
I think lines is a list, no? But
sinkNet.Name = 'Sinks'
sourceNet.Name = 'Sources'
These are strings. Is this expected?
Thanks Thomas. I have added some print statements for testing along the way. To answer your questions:
My lines variable is actually returned as a string, but it is indeed made from a list. My user will be using a script tool before setting up the network (one that will create the file GDB and add the necessary feature classes). I am creating the sources and sinks from the script tool so I have them named as 'Sinks' and 'Sources'. The lines feature class will most likely be named something different every time, so I am using arcpy.ListFeatureClasses() to list out any polylines feature classes in the gdb. There should only be one, so I'm just grabbing that first value from the list.
In the code samples the INetworkSource interface's name property is indeed supposed to be a string.
I commented out the part to add the two junction features and my script does create a new Network Dataset and properly references the line work as the edge features and sets the connection policy to "Any Vertex". I must not be setting up the junction features correctly. I may just put this on the backburner for now and give him a word document with screenshots showing how to create the network using the wizard. It is pretty easy, I was just hoping to get that small piece automated as well.
This is quite messy with Python and it its not properly creating some of the objects. I am having difficulty setting the attributes for the network dataset as well. I appreciate you looking at this though!