Build Network Dataset with python comtypes and ArcObjects

3785
5
08-07-2014 08:25 AM
CalebMackey1
Regular Contributor II

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'

0 Kudos
5 Replies
CalebMackey1
Regular Contributor II

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.

0 Kudos
ThomasStanley-Jones
New Contributor III

What is the new error?

0 Kudos
CalebMackey1
Regular Contributor II

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.

0 Kudos
ThomasStanley-Jones
New Contributor III

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?

CalebMackey1
Regular Contributor II

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!

0 Kudos