0

I am trying to create a custom tool via python script that selects features in multiple fc's within a feature dataset that are missing values, then from the selected features, creates two outputs: one for points and one for lines. I also want the outputs to have the following fields: OBJECTID, TYPEOFFEATURE, and WHATISMISSING, with the fields populated with their respective attributes. For example, WHATISMISSING should have the field from the fc that has null values and TYPEOFFEATURE should have the fc that . I am not sure how to create two separate outputs determined by the feature type or how to create the fields in them.

This is what I have so far. BTW the datasets are in an enterprise geodatabase so if my workspace is the wrong syntax, please let me know:

import arcpy

# Set the workspace for ListFeatureClasses
arcpy.env.workspace = r"C:\DatabaseCon\GIS.sde"
# Use the ListFeatureClasses function to return a list of feature classes.
fc = list(set(arcpy.ListFeatureClasses("ARCFM.Light", "Point", feature_dataset="ARCFM.ElectricDataset")) | set(arcpy.ListFeatureClasses("ARCFM.Pole", "Point", feature_dataset="ARCFM.ElectricDataset")) | set(arcpy.ListFeatureClasses("ARCFM.SurfaceStructure", "Line", feature_dataset="ARCFM.ElectricDataset")) | set(arcpy.ListFeatureClasses("ARCFM.UndergroundStructure", "Line", feature_dataset="ARCFM.ElectricDataset")))
# loop through the list of feature classes, select by attribute depending on the fc, and export the selected features to a shapefile.
for ifc in fc:
    if ifc = "ARCFM.Light":
         arcpy.management.SelectLayerByAttribute(ifc, '"LIGHTTYPE" IS NULL')
    elif ifc = "ARCFM.Pole":
         arcpy.management.SelectLayerByAttribute(ifc, '"Subtype" IS NULL')
    elif ifc = "ARCFM.SurfaceStructure":
         arcpy.management.SelectLayerByAttribute(ifc, '"Subtype" IS NULL')
    elif ifc = "ARCFM.UndergroundStructure":
         arcpy.management.SelectLayerByAttribute(ifc, '"Subtype" IS NULL')
# create layer from selection
for mfl in fc:
    if mfl.arcpy.management.ListDatasets.feature_type = "POINT": 
         arcpy.management.MakeFeatureLayer(ARCFM.Light, 
         "r"C:\DatabaseConnection\DirectConnectGISPROD.sde\points_missing_features, "", "", 
         "OBJECTID", "TYPEOFFEATURE", "WHATISMISSING") | 
          arcpy.management.MakeFeatureLayer(ARCFM.Pole, 
         "r"C:\DatabaseConnection\DirectConnectGISPROD.sde\points_missing_features, "", "", 
         "OBJECTID", "TYPEOFFEATURE", "WHATISMISSING")
    else:
         arcpy.management.MakeFeatureLayer(ARCFM.SurfaceStructure, 
         "r"C:\DatabaseConnection\DirectConnectGISPROD.sde\lines_missing_features, "", "", 
         "OBJECTID", "TYPEOFFEATURE", "WHATISMISSING") | 
          arcpy.management.MakeFeatureLayer(ARCFM.UndergroundStructure, 
         "r"C:\DatabaseConnection\DirectConnectGISPROD.sde\lines_missing_features, "", "", 
         "OBJECTID", "TYPEOFFEATURE", "WHATISMISSING")

I know I am missing things like determining constraints for the fields in the output fc's, I just don't know how to do that in Python.

2 Answers 2

0

I figured it out:

import arcpy
import os

# Set the workspace to the geodatabase that contains the feature classes
arcpy.env.overwriteOutput = True

# define the feature classes that the rows that contain null values will be inserted into.
outputLocation = arcpy.GetParameterAsText(0)
nullPntFc = arcpy.GetParameterAsText(1)
nullLineFc = arcpy.GetParameterAsText(2)
connection = arcpy.GetParameterAsText(3)
arcpy.env.workspace = connection
#nullPolyFc = arcpy.GetParameterAsText(4)

#Create the output null point feature class
sr = arcpy.SpatialReference(2283)
arcpy.CreateFeatureclass_management(outputLocation, nullPntFc, "POINT", spatial_reference=sr)
arcpy.management.AddField(os.path.join(outputLocation, nullPntFc), "FC", "TEXT")
arcpy.management.AddField(os.path.join(outputLocation, nullPntFc), "FC_objectId", "LONG")
arcpy.management.AddField(os.path.join(outputLocation, nullPntFc), "AttributeMissing", "TEXT")

#Create the output null line feature class
arcpy.CreateFeatureclass_management(outputLocation, nullLineFc, "POLYLINE", spatial_reference=sr)
arcpy.management.AddField(os.path.join(outputLocation, nullLineFc), "FC", "TEXT")
arcpy.management.AddField(os.path.join(outputLocation, nullLineFc), "FC_objectId", "LONG")
arcpy.management.AddField(os.path.join(outputLocation, nullLineFc), "AttributeMissing", "TEXT")

#Create output null polygon feature class
#arcpy.CreateFeatureclass_management(outputLocation, nullPolyFc, "POLYGON", spatial_reference=sr)
#arcpy.management.AddField(os.path.join(outputLocation, nullPolyFc), "FC", "TEXT")
#arcpy.management.AddField(os.path.join(outputLocation, nullPolyFc), "FC_objectId", "LONG")
#arcpy.management.AddField(os.path.join(outputLocation, nullPolyFc), "AttributeMissing", "TEXT")


# Get the list of feature classes in the geodatabase
fcListLine = arcpy.ListFeatureClasses("*Section", feature_dataset = "ARCFM.ElectricDataSet")
fcListPoint = arcpy.ListFeatureClasses("*Transformer", feature_dataset = "ARCFM.ElectricDataSet") + arcpy.ListFeatureClasses("*Bank", feature_dataset = "ARCFM.ElectricDataSet") + arcpy.ListFeatureClasses("*Location", feature_dataset = "ARCFM.ElectricDataSet") 
fcListSelection =fcListLine+fcListPoint
#fcListSelection = ['ARCFM.SecondaryUGLineSection', 'ARCFM.RegulatorBank']
arcpy.AddMessage('feature classes :'+str(fcListSelection))
for fc in fcListSelection:
    # Describe feature class to get attributes used in nullPntFc, nullPolylineFc, and nullPolygonFc.
    desc = arcpy.Describe(fc)
    fcName = desc.name
    fcType = desc.shapeType # Point, Polyline, Polygon
    arcpy.AddMessage(fcName + ' - '+ fcType)
    # get the field names to be used in the search cursor
    fieldNames = [field.name for field in arcpy.ListFields(fc) if field.name.endswith("FEEDERID") or field.name.endswith("LOCATIONID") or field.name.endswith("PHASINGCODE")]+['SHAPE@', 'OID@']
    arcpy.AddMessage(fieldNames)
    # Get the shape field index from the list of fields.  The index will be used in the search cursor to pull the shape value
    #fields = arcpy.ListFields(fc)

    # Dump point features with null attributes in the point feature classes into nullPntFc
    if fcType == 'Point':
        insertCursor = arcpy.da.InsertCursor(os.path.join(outputLocation, nullPntFc), ['SHAPE@XY', 'FC', 'FC_objectId', 'AttributeMissing'])
        searchCursor = arcpy.da.SearchCursor(fc, fieldNames)
        for row in searchCursor:
            null_fields = ''
            for i in range(len(fieldNames)-3):
                if row[i]==None:
                    null_fields+=fieldNames[i]+','
                oid = row[5]
                fcName = fc
                if len(null_fields) > 1:
                    insertCursor.insertRow([row[4], fcName, oid, null_fields[:-1]])
                    
               
    if fcType == 'Polyline':
        insertCursor = arcpy.da.InsertCursor(os.path.join(outputLocation, nullLineFc), ['SHAPE@', 'FC', 'FC_objectId', 'AttributeMissing'])
        searchCursor = arcpy.da.SearchCursor(fc, fieldNames)
        for row in searchCursor:
            null_fields = ''
            for i in range(len(fieldNames)-2):
                if row[i]==None:
                    null_fields+=fieldNames[i]+','
            oid = row[4]
            fcName = fc
            if len(null_fields) > 1:
                insertCursor.insertRow([row[3], fcName, oid, null_fields[:-1]])

    if fcType == 'Polygon':
        insertCursor = arcpy.da.InsertCursor(os.path.join(outputLocation, nullPolyFc), ['SHAPE@', 'FC', 'FC_objectId', 'AttributeMissing'])
        searchCursor = arcpy.da.SearchCursor(fc, fieldNames)
        for row in searchCursor:
            null_fields = ''
            for i in range(len(fieldNames)-2):
                if row[i]==None:
                    null_fields+=fieldNames[i]+','
            oid = row[4]
            fcName = fc
            insertCursor.insertRow([row[3], fcName, oid, null_fields[:-1]])
-1

Please see comments for information

# Set the workspace to the geodatabase that contains the feature classes
arcpy.env.workspace = r'C:\Temp\SelectNullFeatures\Geodatabase.gdb'
arcpy.env.overwriteOutput = True

# define the feature classes that the rows that contain null values will be inserted into.
nullPntFc = r'C:\Temp\SelectNullFeatures\Geodatabase2.gdb\NullPoints'
outPolyPath = r'C:\Temp\SelectNullFeatures\Geodatabase2.gdb'

# Get the list of feature classes in the geodatabase
fcList = arcpy.ListFeatureClasses()
# print(fcList)
for fc in fcList:
    # Describe feature class to get attributes used in nullPntFc, nullPolylineFc, and nullPolygonFc.
    desc = arcpy.Describe(fc)
    fcName = desc.name
    fcType = desc.shapeType # Point, Polyline, Polygon
    print(fcName + ' - '+ fcType)
    # get the field names to be used in the search cursor
    fieldNames = [field.name for field in arcpy.ListFields(fc)]
    print(fieldNames)
    # Get the shape field index from the list of fields.  The index will be used in the search cursor to pull the shape value
    fields = arcpy.ListFields(fc)
    for field in fields:
        if field.type == 'Geometry':
            shapeFieldIndex = fields.index(field)

    # Dump point features with null attributes in the point feature classes into nullPntFc
    if fcType == 'Point':
        count = 0
        while count < len(fieldNames) - 1:
            with arcpy.da.SearchCursor(fc,fieldNames) as sc:
                for row in sc:
                    if None in row:
                        fieldName = fieldNames[count]
                        insertCursor = arcpy.da.InsertCursor(nullPntFc, ('SHAPE@XY', 'FC', 'FC_objectId', 'AttributeMissing'))
                        insertRow = [row[shapeFieldIndex], fcName, str(row[0]), fieldName]
                        print(insertRow)
                        insertCursor.insertRow(insertRow)
                        del insertCursor
            count += 1

    ''' using insert cursor on polylines and polygons is more difficult.  I think you would have to build the
        geometry object to insert.  Someone can correct me if I am wrong.  I chose to go a different route with these shape types'''

    if fcType == 'Polyline':
        # Make a feature layer to be able to select rows with null values
        fcFeatLyr = arcpy.MakeFeatureLayer_management(fc, 'polylineFcLyr')
        # Insert the objectID column into the field names list.  the objectID will be part of the selection where clause
        fieldNames.insert(0, 'OID@')
        with arcpy.da.SearchCursor(fc, fieldNames) as sc:
            for row in sc:
                #selects any row in the feature class that has a null attribute
                if None in row:
                    selectedFcFeatLyr = arcpy.SelectLayerByAttribute_management(fcFeatLyr, 'ADD_TO_SELECTION', 'OBJECTID = {}'.format(row[0]))
        #export the selected rows to a new feature class
        arcpy.FeatureClassToFeatureClass_conversion(selectedFcFeatLyr, outPolyPath, fcName + '_Nulls')

    # same as polylines, but just with different variables
    if fcType == 'Polygon':
        fcFeatLyr = arcpy.MakeFeatureLayer_management(fc,'polygonFcLyr')
        fieldNames.insert(0,'OID@')
        with arcpy.da.SearchCursor(fc, fieldNames) as sc:
            for row in sc:
                if None in row:
                    selectedFcFeatLyr = arcpy.SelectLayerByAttribute_management(fcFeatLyr, 'ADD_TO_SELECTION', 'OBJECTID = {}'.format(row[0]))
        arcpy.FeatureClassToFeatureClass_conversion(selectedFcFeatLyr, outPolyPath,fcName + '_Nulls')```

Your insertCursor must match the the fields in the nullPntFc.
Example:
insertCursor = arcpy.da.InsertCursor(nullPntFc, ('SHAPE@XY', 'FC', 'FC_objectId', 'AttributeMissing'))


Image of my nullPtFc fields:
[![NullPtsFc fields][1]][1]
  [1]: https://i.stack.imgur.com/HUAqs.jpg
11
  • 1
    Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
    – Community Bot
    Jun 24 at 21:44
  • Thank you! I have a question about the arcpy.ListFeatureClasses function. Is it possible to have multiple arguments to narrow down the list of fc's in the function? I want 10 specific fc's in a dataset to be selectable, and they have different names and geometry types. Jun 27 at 17:40
  • I am not aware of being able to pass in multiple arguments into the arcpy.ListFeatureClasses. One way to get the list you want, would be to compare the arcpy.ListFeatureClasses list to a list of Feature classes you want using list comprehension. For example: returnedList = [fc1, fc2, fc3] desiredFcList = [fc1, fc2] fcList = [fc for fc in returnedList if fc in desiredFcList] Jun 28 at 14:35
  • Ok so I added to the line fieldNames = with fieldNames = [field.name for field in arcpy.ListFields(fc) if field.name.endswith("FEEDERID") or field.name.endswith("LOCATIONID") or field.name.endswith("PHASINGCODE")] + ['SHAPE@XY'] But I am getting an error on the line insertRow = [row[shapeFieldIndex], fcName, str(row[0]), fieldName] that says "IndexError: tuple index out of range" I can't figure out why this is the case because I defined the fields for the searchCursor. Jul 6 at 19:00
  • Modified to fieldNames = [field.name for field in arcpy.ListFields(fc) if field.name.endswith("FEEDERID") or field.name.endswith("LOCATIONID") or field.name.endswith("PHASINGCODE") or field.name.endswith('SHAPE@')] but I am not sure what to modify on insertCursor and insertRow if they contain the fields I want to add to the new feature class (Shape@, FC, FC_objectID, AttributeMissing) Jul 7 at 12:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.