Is there a way to use a spatial selection to populate fields in another layer via python?

326
3
10-16-2019 09:57 AM
RobertPhillips
Occasional Contributor III

Hi,

I have a script where I spatially select another layer, and the selected records in that layer are then used to populate the fields in the layer that was used for the spatial selection. So specifically I am trying to transfer attributes, but I did some research and the only concern with scripting the Transfer Attributes is the additional field that may get added. I believe I could possibly delete the field to fix that issue but I am unfortunately working in an sde database and I don't want to risk messing with the schemas. If anyone has another possible solution for this I would be very open to it.

with arcpy.da.UpdateCursor(fc, ["OID@", "LANDDISTRICT", "LANDLOT"], """LANDDISTRICT IS NULL OR LANDLOT IS NULL""") as selected_fc:
for row in selected_fc:

#Create temp fc copy
arcpy.MakeFeatureLayer_management(fc, r"in_memory\fcLayer")

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

#Select layer by location
arcpy.SelectLayerByLocation_management(r"in_memory\DLL_layer", "INTERSECT", r"in_memory\fcLayer", '', "NEW_SELECTION")


#If selected district and landlot in row are null
if row[1] is None and row[2] is None:
print '{} Land District is {} and Land Lot is {} '.format(row[0], row[1], row[2])

def DL():

for DL in arcpy.da.SearchCursor(r"in_memory\DLL_layer", ["LAND_DIST"]):
print DL
return DL[0]

def LL():

for LL in arcpy.da.SearchCursor(r"in_memory\DLL_layer", ["LAND_LOT"]):
print LL
return LL[0]

row[1] = DL()
row[2] = LL()

selected_fc.updateRow(row)
print '{} Land District is {} and Land Lot is {} '.format(row[0], row[1], row[2])

I have tried several attempts with this and I either run into the same error or an error referring to something else.

Traceback (most recent call last):
File "U:\Models_Tools\Scripts related to Landlot and District\Populate Landlot and District Test 2.py", line 65, in <module>
row[1] = DL()
File "U:\Models_Tools\Scripts related to Landlot and District\Populate Landlot and District Test 2.py", line 57, in DL
return DL[0]
UnboundLocalError: local variable 'DL' referenced before assignment
0 Kudos
3 Replies
Arne_Gelfert
Regular Contributor

Stumbled over this while trying to refresh my grasp of arcpy cursors. Not entirely sure what you're doing here. But the first thing I notice is that your Update and Search Cursors reference different fields; "LANDLOT" vs "LAND_LOT", and "LANDDISTRICT" vs "LAND_DISTRICT". So DL never gets assigned anything. So when you call DL[0], your error screams: 

local variable 'DL' referenced before assignment

There is a lot of info on this kind of error out there, e.g. Understanding UnboundLocalError in Python - Eli Bendersky's website.

MicahBabinski
Regular Contributor

Hi Robert,

Is this the same process you were working on here?:

Trouble with selecting features by location using arcpy?

If so, I think you should take a look at Spatial Join. This is really the best/only way to transfer attributes the way you are describing via arcpy. Transfer Attributes is only for transferring attributes between linear features.

Anyways, I believe the workflow I recommended in your previous post is still your best bet. In code it would look something like this:

# set up environment to overwrite output and output to in memory
arcpy.env.overwriteOutput = True
arcpy.env.workspace = "in_memory"

# create feature layer from your DLL feature class
arcpy.MakeFeatureLayer_management(DLL_featureclass, "DLL_Layer")

# loop through your point layers, updating LANDDISTRICT first, then LANDLOT
for fc in fcs_to_update:

# make feature layer only containing the features with null values
arcpy.MakeFeatureLayer_management(fc, "temp_layer", "LANDDISTRICT IS NULL")

# spatial join to output a feature class in memory with the attributes of your polygon dataset
arcpy.SpatialJoin_analysis("temp_layer", "DLL_Layer", "tempSpatialJoinOutput",
"JOIN_ONE_TO_ONE", "KEEP_COMMON", "#", "INTERSECT")

# join the output back to your temporary layer based on TARGET_FID
arcpy.AddJoin_management("temp_layer", arcpy.Describe("temp_layer").OIDFieldName,
"tempSpatialJoinOutput", "TARGET_FID")

# get field name to update, since these field names will be qualified now that it is joined
LANDDISTRICT_field_name = "{}.LANDDISTRICT".format(fc.split("\\")[-1])

# update the values
arcpy.CalculateField_management("temp_layer", LANDDISTRICT_field_name,
"!tempSpatialJoinOutput.LANDDISTRICT!", "PYTHON_9.3")

# same thing, but for LANDLOT field
arcpy.MakeFeatureLayer_management(fc, "temp_layer", "LANDLOT IS NULL")

# spatial join to output a feature class in memory with the attributes of your polygon dataset
arcpy.SpatialJoin_analysis("temp_layer", "DLL_Layer", "tempSpatialJoinOutput",
"JOIN_ONE_TO_ONE", "KEEP_COMMON", "#", "INTERSECT")

# join the output back to your temporary layer based on TARGET_FID
arcpy.AddJoin_management("temp_layer", arcpy.Describe("temp_layer").OIDFieldName,
"tempSpatialJoinOutput", "TARGET_FID")

# get field name to update, since these field names will be qualified now that it is joined
LANDLOT_field_name = "{}.LANDLOT".format(fc.split("\\")[-1])

# update the values
arcpy.CalculateField_management("temp_layer", LANDDISTRICT_field_name,
"!tempSpatialJoinOutput.LANDLOT!", "PYTHON_9.3")

# remove the join
arcpy.RemoveJoin_management("temp_layer")

# cleanup
arcpy.Delete_management("in_memory")
arcpy.Delete_management("temp_layer")

I believe this would be much faster and simpler than any sort of feature-by-feature nested cursor approach.

Micah

0 Kudos
RobertPhillips
Occasional Contributor III

Thanks Micah,

I thought about doing a spatial join but my only concern is that I have a

line feature and, though I haven't fully tested it out, I got a different

result than what I was looking for. Some of the line features cross

multiple polygons and I'm trying to determine how to best go about this.

Some are entirely inside a polygon or cross either two or more polygons. So

I tried to convert the feature to point so I could determine which part of

the polygon either contained the largest portion of the line or at least

it's determine where the middle of the line fell. I looked at other ways

and I think I found a couple but I will give your example a try. I didn't

think about the spatial join after converting to point so that might

definitely be a better solution than what I thought to try or attempt.

0 Kudos