Iterating through geometries in a layer for multiple if conditional statements.

305
11
Jump to solution
2 weeks ago
Labels (1)
CristinaBurgosOtero
Emerging Contributor

I am trying to iterate through the features of a layer and verify two different variables in two different fields in the same row. If both variables are true then it will apply a geoprocessing tool with one of the 2  results. The geoprocessing is Feature vertices to Point. I want to run it to create an output with points at the start of the line feature or on both sides of the line feature. 

I used already If/elif/else and it didn't work. I added the selection feature to apply the function of the geoprocessing tool to only the features that meet the conditions because without it it will apply it to all the lines in the layer once the condition was True in some of them and not just to the ones that meet the conditions. 

I tried this code and it works in separates blocks using notebook in ArcPro but when I put it all together it doesn't work. 

What do you think could be change to make it work? or what am I doing wrong here? 



import arcpy
from arcpy import env

 

arcpy.env.workspace = "workspace_gdb"
feature_class = "Line_Feature_Class"
output_vertices_topoint = "output_location"
output_vertices_topoint2 = "output_location"

 

value_x1 = "W"
value_x2 = "N"
value_x3 = "NW"
value_y1 = "NS"
value_y2 = "WE"
field_name1 = "WindDegrees1"
field_name2 = "WBDirection"
domain_name = "Wind Direction"

domains = arcpy.da.ListDomains(arcpy.env.workspace)
target_domain= None
for domain in domains:
if domain_name == domain.name:
target_domain=domain
break

if target_domain:
# Create a dictionary of coded values.
coded_values = domain.codedValues

arcpy.management.MakeFeatureLayer(feature_class, "temp_layer")

with arcpy.da.SearchCursor("temp_layer", ["SHAPE@", field_name1, field_name2]) as search_cursor:
for row in search_cursor:
object_id = row[0]
coded_value1 = row[1]
coded_value2 = row[2]

if coded_value1 == value_y1 and coded_value2 == value_x3:
# Select the feature
arcpy.management.SelectLayerByAttribute("temp_layer", "NEW_SELECTION", f"{field_name1} = '{value_y1}' AND {field_name2} = '{value_x3}'")

# Perform actions on the selected feature
arcpy.management.FeatureVerticesToPoints("temp_layer", output_vertices_topoint, "START")

else:

print(f"Feature does not meet any condition.")


arcpy.management.SelectLayerByAttribute(feature_class, "CLEAR_SELECTION")
break

with arcpy.da.SearchCursor("temp_layer", ["SHAPE@", field_name1, field_name2]) as search_cursor:
for row in search_cursor:
object_id = row[0]
coded_value1 = row[1]
coded_value2 = row[2]

if coded_value1 == value_y2 and coded_value2 == value_x3:
# Select the feature
arcpy.management.SelectLayerByAttribute("temp_layer", "NEW_SELECTION", f"{field_name1} = '{value_y2}' AND {field_name2} = '{value_x3}'")

# Perform actions on the selected feature
arcpy.management.FeatureVerticesToPoints("temp_layer", output_vertices_topoint2, "START")

else:

print(f"Feature does not meet any condition.")


arcpy.management.SelectLayerByAttribute(feature_class, "CLEAR_SELECTION")
break

with arcpy.da.SearchCursor("temp_layer", ["SHAPE@", field_name1, field_name2]) as search_cursor:
for row in search_cursor:
object_id = row[0]
coded_value1 = row[1]
coded_value2 = row[2]

if coded_value1 == value_y1 and coded_value2 == value_x2:
# Select the feature
arcpy.management.SelectLayerByAttribute("temp_layer", "NEW_SELECTION", f"{field_name1} = '{value_y1}' AND {field_name2} = '{value_x2}'")

# Perform actions on the selected feature
arcpy.management.FeatureVerticesToPoints("temp_layer", output_vertices_topoint2, "START")

else:

print(f"Feature does not meet any condition.")


arcpy.management.SelectLayerByAttribute(feature_class, "CLEAR_SELECTION")
break

with arcpy.da.SearchCursor("temp_layer", ["SHAPE@", field_name1, field_name2]) as search_cursor:
for row in search_cursor:
object_id = row[0]
coded_value1 = row[1]
coded_value2 = row[2]

if coded_value1 == value_y1 and coded_value2 == value_x1:
# Select the feature
arcpy.management.SelectLayerByAttribute("temp_layer", "NEW_SELECTION", f"{field_name1} = '{value_y1}' AND {field_name2} = '{value_x1}'")

# Perform actions on the selected feature
arcpy.management.FeatureVerticesToPoints("temp_layer", output_vertices_topoint, "BOTH_ENDS")

else:

print(f"Feature does not meet any condition.")


arcpy.management.SelectLayerByAttribute(feature_class, "CLEAR_SELECTION")
break


with arcpy.da.SearchCursor("temp_layer", ["SHAPE@", field_name1, field_name2]) as search_cursor:
for row in search_cursor:
object_id = row[0]
coded_value1 = row[1]
coded_value2 = row[2]

if coded_value1 == value_y2 and coded_value2 == value_x2:
# Select the feature
arcpy.management.SelectLayerByAttribute("temp_layer", "NEW_SELECTION", f"{field_name1} = '{value_y2}' AND {field_name2} = '{value_x2}'")

# Perform actions on the selected feature
arcpy.management.FeatureVerticesToPoints("temp_layer", output_vertices_topoint, "BOTH_ENDS")

else:

print(f"Feature does not meet any condition.")


arcpy.management.SelectLayerByAttribute(feature_class, "CLEAR_SELECTION")
break

with arcpy.da.SearchCursor("temp_layer", ["SHAPE@", field_name1, field_name2]) as search_cursor:
for row in search_cursor:
object_id = row[0]
coded_value1 = row[1]
coded_value2 = row[2]

if coded_value1 == value_y2 and coded_value2 == value_x1:
# Select the feature
arcpy.management.SelectLayerByAttribute("temp_layer", "NEW_SELECTION", f"{field_name1} = '{value_y2}' AND {field_name2} = '{value_x1}'")

# Perform actions on the selected feature
arcpy.management.FeatureVerticesToPoints("temp_layer", output_vertices_topoint2, "START")

else:

print(f"Feature does not meet any condition.")
print (row[1], row[2])

arcpy.management.SelectLayerByAttribute(feature_class, "CLEAR_SELECTION")
break

0 Kudos
1 Solution

Accepted Solutions
HaydenWelch
MVP Regular Contributor

It seems like you're overwriting "temp_layer" with each cursor operation and your selections are also shadowing each other. So only the last matching selection and "temp_layer" are created.

View solution in original post

11 Replies
HaydenWelch
MVP Regular Contributor

Yould you mind re-sharing your code using a codeblock? Python requires correct whitespace to be read properly and you have flattened your code. 

HaydenWelch_0-1750278823351.png

 

0 Kudos
HaydenWelch
MVP Regular Contributor

Since it's difficult to follow your logic in the provided code, I went ahead and did my best to create a version that's as simple as possible. I think from here it'll be easier to figure out what exactly you need:

def main():
    feature_class = 'Line_Feature_Class'
        
    # Build this however you want
    queries ={
        'NS-NW': "WindDegrees1 = 'NS' AND WBDirection = 'NW'",
        'WE-NW': "WindDegrees1 = 'WE' AND WBDirection = 'NW'",
        'NS-N' : "WindDegrees1 = 'NS' AND WBDirection = 'N'",
        'NS-W' : "WindDegrees1 = 'NS' AND WBDirection = 'W'",
        'WE-N' : "WindDegrees1 = 'WE' AND WBDirection = 'N'",
        'WE-W' : "WindDegrees1 = 'WE' AND WBDirection = 'W'",
    }
    
    # Add more of these as needed
    start_points = []
    both_points = []
    
    for query_name, query in queries.items():
        # Get all matching feature shapes
        matches: list[arcpy.Polyline] = [
            shape
            for shape, in arcpy.da.SearchCursor(feature_class, ['SHAPE@'], where_clause=query)
            if isinstance(shape, arcpy.Polyline) # For the type linter
        ]
        
        # Handle based on query name
        if query_name in ('NS-NW', 'WE-NW', 'NS-N', 'WE-W'):
            # Get start
            for line in matches:
                # First Point
                start_points.append(arcpy.PointGeometry(line.firstPoint, spatial_reference=line.spatialReference))
                
        elif query_name in ('NS-W', 'WE-N'):
            # Get Both
            for line in matches:
                # First Point
                both_points.append(arcpy.PointGeometry(line.firstPoint, spatial_reference=line.spatialReference))
                # Last Point
                both_points.append(arcpy.PointGeometry(line.lastPoint, spatial_reference=line.spatialReference))
        
        # Raise an error if the query is not handled      
        else:
            raise ValueError(f'Undefined Query `{query_name}`!')
    
    arcpy.management.CopyFeatures(start_points, 'StartPoints')
    arcpy.management.CopyFeatures(both_points, 'BothPoints')
    
if __name__ == '__main__':
    main()

Fixed the index error on line 21 as per Blake's reply

Switched to singleton unpacking instead so i don't have to see magic indexes (`shape,` will unpack (1,) as shape=1)

BlakeTerhune
MVP Frequent Contributor

I think there's something missing on line 20.

HaydenWelch
MVP Regular Contributor

Oops. Originally I was unpacking oid and shape so I didn't need the index. Totally forgot to fix it.

0 Kudos
BlakeTerhune
MVP Frequent Contributor

What threw me off was the type hinting for matches. I've never seen that before! 🌈

But now that you mention the index issue, I think you also need to update line 23 with an index too.

0 Kudos
HaydenWelch
MVP Regular Contributor

I need to stop programming on 3 hours of sleep haha

Decided to use another fun Python feature instead of indexing in case we need to add stuff to the result

 

Also you should definitely be hinting everything you write if you aren't already. If you use an editor that has Pylance, the code basically writes itself. You get highlighting and auto-completion for everything. This is an instance of me forcing the type since the SearchCursor returns a tuple with the signature tuple[Any, ...] which means I lose that auto-complete with the results.

Because I know the 'SHAPE@' field is a Polyline, I can go ahead and assign that type to the result. The instance check isn't necessary, but if you have your settings for Pylance set to `strict` it will yell at you that you don't actually know that the type of that value is Polyline, so adding a guard there will ensure that if you run the script on a Point feature or something it'll error out ASAP

BlakeTerhune
MVP Frequent Contributor

In addition to formatting your code with a code block, like @HaydenWelch mentioned, please clarify what you mean when you say, "it doesn't work." Do you get an error message? Is the output empty? If the output has data, is it incorrect? If it's a data issue, please clarify what you want it to look like and what you're getting instead.

CristinaBurgosOtero
Emerging Contributor

Hi! Thank you for the responses and the recommendations, all this is very helpful for me. When I mentioned it doesn't work, I mean when I put together the code in just one cell, since I've been working in Notebooks in ArcPro, the code runs but it gives me back just one of the results I'm expecting. It doesn't get any errors message. While I had them in separate cells for testing the code running all of them, it would work and give me the results I was expecting. I set the data in the attribute table for it to give me back two outputs, from a "vertices to points" function, one output with the features that meet the conditions for one point at the start of the line and another output with the features that meet the conditions for the points on both side of the line. When I run it, now it is just giving me one result (both ends) and not the other one too, while in the data are features that meet the conditions for that function. 

I haven't used Pylance, but will do. 👍

Here is the code I have been using. 

import arcpy
from arcpy import env

arcpy.env.workspace = 'workspace'
feature_class = 'line_feature_class'
output_vertices_topoint = 'output1_point_feature_class'
output_vertices_topoint2 = 'output2_point_feature_class'

value_x1 = "W"
value_x2 = "N"
value_x3 = "NW"
value_y1 = "NS"
value_y2 = "WE"
field_name1 = "WindDegrees1"
field_name2 = "WBDirection"
domain_name = "Wind Direction"

domains = arcpy.da.ListDomains(arcpy.env.workspace)
target_domain= None
for domain in domains:
    if domain_name == domain.name:
        target_domain=domain
        break

if target_domain:
    # Create a dictionary of coded values.
    coded_values = domain.codedValues
    
arcpy.management.MakeFeatureLayer(feature_class, "temp_layer")

with arcpy.da.SearchCursor("temp_layer", ["SHAPE@", field_name1, field_name2]) as search_cursor:
    for row in search_cursor:
        object_id = row[0]
        coded_value1 = row[1]
        coded_value2 = row[2]

        if coded_value1 == value_x1 and coded_value2 == value_y1:
            # Select the feature
            arcpy.SelectLayerByAttribute_management("temp_layer", "NEW_SELECTION", f"{field_name1} = '{value_x1}' AND {field_name2} = '{value_y1}'")
                
            # Perform actions on the selected feature
            arcpy.management.FeatureVerticesToPoints("temp_layer", output_vertices_topoint, "START")
                   
        else:
                # Cases where the condition is not met
            print(f"Feature does not meet any condition.")
                # Clear selection 
                
            arcpy.SelectLayerByAttribute_management(feature_class, "CLEAR_SELECTION")
            
with arcpy.da.SearchCursor("temp_layer", ["SHAPE@", field_name1, field_name2]) as search_cursor:
    for row in search_cursor:
        object_id = row[0]
        coded_value1 = row[1]
        coded_value2 = row[2]

        if coded_value1 == value_x2 and coded_value2 == value_y1:
            # Select the feature
            arcpy.SelectLayerByAttribute_management("temp_layer", "NEW_SELECTION", f"{field_name1} = '{value_x2}' AND {field_name2} = '{value_y1}'")
                
            # Perform actions on the selected feature
            arcpy.management.FeatureVerticesToPoints("temp_layer", output_vertices_topoint2, "BOTH_ENDS")
                   
        else:
                # Cases where the condition is not met
            print(f"Feature does not meet any condition.")
                # Clear selection 
                
            arcpy.SelectLayerByAttribute_management(feature_class, "CLEAR_SELECTION")
        
with arcpy.da.SearchCursor("temp_layer", ["SHAPE@", field_name1, field_name2]) as search_cursor:
    for row in search_cursor:
        object_id = row[0]
        coded_value1 = row[1]
        coded_value2 = row[2]

        if coded_value1 == value_x2 and coded_value2 == value_y2:
            # Select the feature
            arcpy.SelectLayerByAttribute_management("temp_layer", "NEW_SELECTION", f"{field_name1} = '{value_x2}' AND {field_name2} = '{value_y2}'") 
                
            # Perform actions on the selected feature
            arcpy.management.FeatureVerticesToPoints("temp_layer", output_vertices_topoint, "START")
                   
        else:
                # Cases where the condition is not met
            print(f"Feature does not meet any condition.")
                # Clear selection 
                
            arcpy.SelectLayerByAttribute_management(feature_class, "CLEAR_SELECTION")
            
with arcpy.da.SearchCursor("temp_layer", ["SHAPE@", field_name1, field_name2]) as search_cursor:
    for row in search_cursor:
        object_id = row[0]
        coded_value1 = row[1]
        coded_value2 = row[2]

        if coded_value1 == value_x1 and coded_value2 == value_y2:
            # Select the feature
            arcpy.SelectLayerByAttribute_management("temp_layer", "NEW_SELECTION", f"{field_name1} = '{value_x1}' AND {field_name2} = '{value_y2}'") 
                
            # Perform actions on the selected feature
            arcpy.management.FeatureVerticesToPoints("temp_layer", output_vertices_topoint2, "BOTH_ENDS")
                   
        else:
                # Cases where the condition is not met
            print(f"Feature does not meet any condition.")
                # Clear selection 
                
            arcpy.SelectLayerByAttribute_management(feature_class, "CLEAR_SELECTION")
                       
with arcpy.da.SearchCursor("temp_layer", ["SHAPE@", field_name1, field_name2]) as search_cursor:
    for row in search_cursor:
        object_id = row[0]
        coded_value1 = row[1]
        coded_value2 = row[2]

        if coded_value1 == value_x3 and coded_value2 == value_y1:
            # Select the feature
            arcpy.SelectLayerByAttribute_management("temp_layer", "NEW_SELECTION", f"{field_name1} = '{value_x3}' AND {field_name2} = '{value_y1}'")
                
            # Perform actions on the selected feature
            arcpy.management.FeatureVerticesToPoints("temp_layer", output_vertices_topoint, "START")
                   
        else:
                # Cases where the condition is not met
            print(f"Feature does not meet any condition.")
                # Clear selection 
                
            arcpy.SelectLayerByAttribute_management(feature_class, "CLEAR_SELECTION")
    
with arcpy.da.SearchCursor("temp_layer", ["SHAPE@", field_name1, field_name2]) as search_cursor:
    for row in search_cursor:
        object_id = row[0]
        coded_value1 = row[1]
        coded_value2 = row[2]

        if coded_value1 == value_x3 and coded_value2 == value_y2:
            # Select the feature
            arcpy.SelectLayerByAttribute_management("temp_layer", "NEW_SELECTION", f"{field_name1} = '{value_x3}' AND {field_name2} = '{value_y2}'")
                
            # Perform actions on the selected feature
            arcpy.management.FeatureVerticesToPoints("temp_layer", output_vertices_topoint, "START")
                   
        else:
                # Cases where the condition is not met
            print(f"Feature does not meet any condition.")
                # Clear selection 
                
            arcpy.SelectLayerByAttribute_management(feature_class, "CLEAR_SELECTION")
            break
            

 

HaydenWelch
MVP Regular Contributor

It seems like you're overwriting "temp_layer" with each cursor operation and your selections are also shadowing each other. So only the last matching selection and "temp_layer" are created.