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
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]])
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.sstatic.net/HUAqs.jpg
-
1Your 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.2022年06月24日 21:44:50 +00:00Commented Jun 24, 2022 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.matthiastrek– matthiastrek2022年06月27日 17:40:33 +00:00Commented Jun 27, 2022 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]
armedwiththeword– armedwiththeword2022年06月28日 14:35:30 +00:00Commented Jun 28, 2022 at 14:35 -
Ok so I added to the line
fieldNames =
withfieldNames = [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 lineinsertRow = [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.matthiastrek– matthiastrek2022年07月06日 19:00:32 +00:00Commented Jul 6, 2022 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 oninsertCursor
andinsertRow
if they contain the fields I want to add to the new feature class (Shape@, FC, FC_objectID, AttributeMissing)matthiastrek– matthiastrek2022年07月07日 12:47:17 +00:00Commented Jul 7, 2022 at 12:47
Explore related questions
See similar questions with these tags.