Is there a way to use python to select features by location from selected features?

3606
6
10-08-2019 07:38 AM
RPGIS
by
Occasional Contributor III

Hi,

I have a script that seems to work but is unfortunately returning incorrect results. I am trying to figure out the best way to go about it. I have tried several other alternatives but those alternatives either return an incorrect number of results or doesn't have the data I need to work with. What I am trying to do is:

  • simply iterate through a set of features that have like fields
  • iterate through each null value in the fields, selecting another layer based on location to that null record in the feature class field
  • derive a certain field value from the feature that is selected based on the location of the feature class with null field value

import arcpy
import os

arcpy.env.overwriteOutput = True

workspace = r'Database Connections\......sde'
fcs = []
walk = arcpy.da.Walk(workspace, datatype="FeatureClass")

#make district landlot layer
Dist_LL = "Database Connections\.....sde\......Landlots"
DistLL_layer = arcpy.MakeFeatureLayer_management(Dist_LL, 'DLL_layer')

for dirpath, dirnames, filenames in walk:
    for filename in filenames:
        fcs.append(os.path.join(dirpath, filename))

for fc in fcs:
    geometryType = arcpy.Describe(fc).shapeType

    #Get feature class name
    fcsname = os.path.basename(fc)
    name = os.path.splitext(fcsname)
    y = name[1].lstrip('.')
    print y

    #Create lists (OID)
    OID = []
    
    if geometryType == 'Point':
        print geometryType
        
        for row in arcpy.da.SearchCursor(fc, ["OID@", "LANDDISTRICT", "LANDLOT"], """LANDDISTRICT IS NULL OR LANDLOT IS NULL"""):
            
            appendOID = OID.append(row[0])
            OIDstrnum = str(len(OID))
            
            #Create unique naming conventions
            a = '{}{}'.format(y, OIDstrnum)
                       
            if (row[1] is None or row[2] is None):

                #Create temp fc copy
                b = arcpy.MakeFeatureLayer_management(fc, a)
                
                #Select layer by location
                selection = arcpy.SelectLayerByLocation_management(DistLL_layer, "INTERSECT", b, '', "NEW_SELECTION")
                
                print '{} District is {} and Land Lot is {} '.format(row[0], row[1], row[2])
                for selected in arcpy.da.SearchCursor(selection, ["LAND_DIST", "LAND_LOT"]):
                    print selected
                    break
            elif (row[1] is None):
                print '{} Land District is {} '.format(row[0], row[1])
                for selected_Lot in arcpy.da.SearchCursor(selection, ["LAND_DIST"]):
                    print selected_Lot
                    break
            elif (row[2] is None):
                print '{} Land Lot is {}'.format(row[0], row[2])
                for selected_District in arcpy.da.SearchCursor(selection, ["LAND_LOT"]):
                    print selected_District
                    break
            else:
                print 'No null values'

            del OID[:]

I have something that works(partially) but does not return the correct features with a corresponding spatial location. If anyone could assist with this I would greatly appreciate it.

Robert

0 Kudos
6 Replies
JoshuaBixby
MVP Esteemed Contributor

I don't have time to fully look over your code, but I have a couple of things to share after taking a quick look.

First, and this may just be a terminology mixup, but the following code:

#Create temp fc copy
b = arcpy.MakeFeatureLayer_management(fc, a)

Make feature layer does not create a temporary feature class.  If you really want a temporary feature class, you should use Copy Features, Feature Class to Feature Class, or one of the other several tools for copying feature classes data.

Regarding your if/else structure,

if (row[1] is None or row[2] is None):
    ...
elif (row[1] is None):
    ...
elif (row[2] is None):
    ...
else:
    ...

The two elif statements will never get evaluated because if either row[1] or row[2] is None, it will be caught by the first if statement.

0 Kudos
RPGIS
by
Occasional Contributor III

Thanks Joshua,

I actually was able to work out creating a temporary feature layer (not class). I found

a simple solution and it seems to be working just fine. If need be I can

re-post the code to show.

For my if/else statements I was concerned about the evaluations. I wasn't

sure if there was a way to write the statements so that if both fields are

null it returns the null fields, or if one of the fields is null it returns

one of the null fields. I think I may figured it out but any

assistance would be well appreciated.

Robert

0 Kudos
JoshuaBixby
MVP Esteemed Contributor

I would post your new code and comment on what issues you are seeing with the new code. 

0 Kudos
RPGIS
by
Occasional Contributor III

Hi Joshua,

Here is the updated script.

Issue 1: I came some of the features that may be corrupted and so being able to identify those and deleting them is something I am still trying to figure out.

Issue 2: I am not sure how to set up the if/else statements in such a way that the three conditions can be identified.

import arcpy
import os

arcpy.env.overwriteOutput = True

workspace = r'Database Connections\....sde'
fcs = []
walk = arcpy.da.Walk(workspace, datatype="FeatureClass")

#make district landlot layer
Dist_LL = "Database Connections\....sde\.....fc"
arcpy.MakeFeatureLayer_management(Dist_LL, "DLL_layer")

for dirpath, dirnames, filenames in walk:
    for filename in filenames:
        fcs.append(os.path.join(dirpath, filename))

for fc in fcs:
    geometryType = arcpy.Describe(fc).shapeType

    #Get feature class name
    fcsname = os.path.basename(fc)
    name = os.path.splitext(fcsname)
    y = name[1].lstrip('.')
    print y

    #Create lists (OID)
    OID = []
    
    if geometryType == 'Point':
        print geometryType
        
        with arcpy.da.SearchCursor(fc, ["OID@", "LANDDISTRICT", "LANDLOT"], """LANDDISTRICT IS NULL OR LANDLOT IS NULL""") as selected_fc:
            for row in selected_fc:
                print row

                #Create temp fc layer
                arcpy.MakeFeatureLayer_management(fc, "fcLayer")

                #Select layer by attribute
                arcpy.SelectLayerByAttribute_management ("fcLayer", "NEW_SELECTION", "OBJECTID = {}".format(row[0]))

                #Select layer by location
                arcpy.SelectLayerByLocation_management("DLL_layer", "INTERSECT", "fcLayer", '', "NEW_SELECTION")
                
                if (row[1] is None) and (row[2] is None):
                    print '{} District is {} and Land Lot is {} '.format(row[0], row[1], row[2])                                 

                    for selectrows in arcpy.da.SearchCursor("DLL_layer", ["OID@", "LAND_DIST", "LAND_LOT"]):
                        if selectrows is None:                            
                            print selectrows
                                            
                elif (row[1] is None) and (row[2] is not None):
                    print '{} Land District is {} '.format(row[0], row[1])

                    for selected_Lot in arcpy.da.SearchCursor("DLL_layer", ["OID@", "LAND_DIST"]):
                        print '{} Land District = {}'.format(row[0], row[1])
                                            
                elif (row[1] is not None) and (row[2] is None):
                    print '{} Land Lot is {}'.format(row[0], row[2])
                    
                    for selected_District in arcpy.da.SearchCursor("DLL_layer", ["OID@", "LAND_LOT"]):
                        print '{} Land Lot = {}'.format(row[0], row[1])
                                                                   
                else:
                    print 'No null values'

                del OID[:]‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

The script works but I think there are areas where I could easily simplify this script. Any help on simplifying/identifying the best ways to resolve my issues would be greatly appreciated.

Thanks,

Robert

0 Kudos
JoshuaBixby
MVP Esteemed Contributor

Some quick comments.  Your if/elif structure look better.  From a functional perspective, you don't have to have parentheses around all the checks.  For example, you could write:

if row[1] is None and row[2] is None:

Whether that is cleaner or easier to understand depends on the individual, but I just wanted to point it out.

In general, you do not want to construct feature layers and cursors within a cursor loop.  Having nested cursors is a performance killer, and constructing lots of feature layers within a cursor loop tends to create memory management issues if the data set is large. 

I recommend reading /blogs/richard_fairhurst/2014/11/08/turbo-charging-data-manipulation-with-python-cursors-and-diction...‌.  It will give you some ideas for how you can using Python dictionaries to cut down or cut out nested cursors.

0 Kudos
RPGIS
by
Occasional Contributor III

Thanks Joshua,

I checked out the article and it makes complete sense to use a dictionary for this instance. The only thing is is that I have never used or created a dictionary before. It seems like it is a list almost but I am completely unfamiliar with this. If you or anyone gets a chance, could you possibly show me what that would look like specifically. The other thing that I am trying to obtain a single value that is derived for each value in a given instance. If there is a way to achieve this I would be very interested.

Thanks,

Robert

0 Kudos