Select to view content in your preferred language

Search Cursor not obeying selection rules

170
6
a week ago
rsnider43
Emerging Contributor

Hello, I have a script that uses a search cursor to go to a selected feature in a table called SplicePoints_test, that is selected based on it intersecting with some fibercable. After that I use the selected feature and extract the last 2 digits of the string of the selected entry in the SplicePoints_test layer. However when I use the searchcursor to extract the closure_name row, it iterates through all splice points and only appends the last one in the table. I used a break statement to get it to stop iterating, but it only goes to the first one instead, any ideas on how I might get the search cursor to only acknowledge the selected feature? 

 

with arcpy.da.SearchCursor("Fibercable_Test", ['comments']) as cursor:
    # Iterate through the rows
    
        for row in cursor:
            arcpy.management.SelectLayerByLocation(
            in_layer="SplicePoints_test",
            overlap_type="INTERSECT",
            select_features="Fibercable_test",
            search_distance=None,
            selection_type="NEW_SELECTION",
            invert_spatial_relationship="NOT_INVERT"
            )
            #iterate through the selected splice point, and split the last digit
            with arcpy.da.SearchCursor("SplicePoints_Select", ['closure_name']) as cursor:
                for row in cursor:
                    last_2 = row[0].split('_')[3]
                    arcpy.AddMessage(last_2)
            
            # add split string to the end of the comments field    
            expr = "'{}{}{}'".format(entry, '.', last_2)
            arcpy.management.CalculateField(
            in_table="Fibercable_test",
            field="comments",
            expression=expr,
            expression_type="PYTHON3",
            code_block="",
            field_type="TEXT",
            enforce_domains="NO_ENFORCE_DOMAINS"
            )

 

0 Kudos
6 Replies
VenkataKondepati
Occasional Contributor

You’re selecting on a feature class, then cursors read the entire table. Make a feature layer first—SearchCursor will then honor the selection. Also don’t reuse cursor/row names and use next() if you only want the first selected splice point.

Try this pattern:

# Make layers so selections stick
arcpy.management.MakeFeatureLayer("Fibercable_Test",  "fc_lyr")
arcpy.management.MakeFeatureLayer("SplicePoints_test","sp_lyr")

with arcpy.da.SearchCursor("fc_lyr", ["OID@", "SHAPE@", "comments"]) as fc_cur:
    for oid, geom, comments in fc_cur:
        # select splice points intersecting the current fiber feature
        arcpy.management.SelectLayerByLocation(
            in_layer="sp_lyr",
            overlap_type="INTERSECT",
            select_features="fc_lyr",  # selection is the current feature because fc_lyr is selected by OID below
            selection_type="NEW_SELECTION"
        )
        # limit fc_lyr to the current feature so location uses just this one
        arcpy.management.SelectLayerByAttribute("fc_lyr", "NEW_SELECTION", f"OBJECTID = {oid}")

        # read ONLY the selected splice points
        last_2 = None
        with arcpy.da.SearchCursor("sp_lyr", ["closure_name"]) as sp_cur:
            row = next(sp_cur, None)  # first selected
            if row and row[0]:
                parts = row[0].split("_")
                if len(parts) > 3:
                    last_2 = parts[3][-2:]  # or parts[3] if exactly two digits

        if last_2:
            expr = f"'{comments}.{last_2}'"
            arcpy.management.CalculateField(
                in_table="fc_lyr", field="comments",
                expression=expr, expression_type="PYTHON3"
            )

 


Key points:

MakeFeatureLayer for both inputs; run SelectLayerByLocation on layers, not raw FCs.

SearchCursor("sp_lyr", …) will return only selected rows.

Use SelectLayerByAttribute to isolate the current fiber feature by OID before the spatial select.

Use next() to avoid looping and accidentally grabbing the last record.

If multiple splice points can intersect one fiber and you need a specific one, add an order (e.g., nearest) or filter before next()

DavidPike
MVP Notable Contributor

Might be misunderstanding - but I feel a spatial join then a calculate field would work.

0 Kudos
Clubdebambos
MVP Regular Contributor

Hi @rsnider43.,

You need to clean up your code, you have a nested SearchCursor an using "as cursor" and "row" twice. Keep these separate for each SearchCursor call. See below.

## iterate over the fiber_fc
## use the geometry of each line in the Select by Location
with arcpy.da.SearchCursor(fiber_fc, ["comments", "SHAPE@"]) as s_cursor_1:
    ## for each row in the Search Cursor
    for s1_row in s_cursor_1:
        ## select the Splice Points that intersect with the cable
        splice_selections = arcpy.management.SelectLayerByLocation(
            in_layer=splice_fc,
            overlap_type="INTERSECT",
            select_features=s1_row[1], # using the geometry of the fibre
            selection_type="NEW_SELECTION"
        )

        ## iterate over the splice point selection with a second Search Cursor
        with arcpy.da.SearchCursor(splice_selections, ["closure_name"]) as s_cursor_2:
            ## for each splice point
            for s2_row in s_cursor_2:
                ## get the last two characters
                last_2 = s2_row[0].split('_')[3]
                arcpy.AddMessage(last_2)

     ## I would probably make the first SearchCursor an Update Cursor and just
     ## update the Fibre comments here.
~ learn.finaldraftmapping.com
0 Kudos
HaydenWelch
MVP Regular Contributor

You can actually accomplish this whole thing using cursors:

from arcpy.da import SearchCursor, UpdateCursor

with UpdateCursor("Fibercable_Test", ['OID@', 'SHAPE@', 'comments']) as cur:
    for oid, cable, comment in cur:
        int_closures: list[str] = [
            closure_name.split('_')[-1]
            for closure_name, in
            SearchCursor(
                "SplicePoints_test", ['closure_name'], 
                spatial_filter=cable)
        ]
        if not int_closures:
            continue
        
        print(f"Found {len(int_closures)} closures for cable {oid}")
        
        # Your original code only stores the last intersecting splice
        # You may want to join them?
        all_closures = f"({','.join(int_closures)})"
        cur.updateRow((cable, f"{comment}.{int_closures[-1]}"))

Edit: typo in that script, I forgot to add oid to the updateRow call. It should be the first element of the tuple.

Cursors accept geometry objects as spatial filters! Which means you can skip using layer selections and just keep it compact.

 

Edit: Made some minor changes after re-reading your script and realizing that you just drop all but the last intersecting splice closure.

Clubdebambos
MVP Regular Contributor

Beautiful! I always forget about the (not so) new spatial_filter. Very neat.

~ learn.finaldraftmapping.com
HaydenWelch
MVP Regular Contributor

In my experience it can sometimes be significantly faster than SelectByLocation or a full iterative filter. Here's a quick test using my cursor module that wraps all that in a nice interface:

HaydenWelch_0-1755693108817.png

 

The top call is getting a count of features that intersect the indexed polygon using the Cursor spatial_filter and the last one is using a SelectByLocation.

0 Kudos