I am working a code/button that will help me update address points attributes based on the parcel they are in.
I am trying to to use filed list to create a mach and update the points with dictionaries. I don't not get error but my address points don't get updated from the parcels. I would appreciate any help with my code.
import arcpy from datetime import datetime as d startTime = d.now() #set to folder where features are located arcpy.env.workspace = r"Database Servers\DSD15_SQLEXPRESS.gds\TEST (VERSION:dbo.DEFAULT)" #r"Database Servers\DSD15_SQLEXPRESS.gds\TonyTwoWay (VERSION:dbo.DEFAULT)" #on windows use \ instead of / arcpy.env.overwriteOutput = True arcpy.env.qualifiedFieldNames = False #define variables for cursor #--------------------------- FC = "TEST.DBO.CCAPTEST" #pointlayer TaxPar = "TaxParcels" #Parcels poly = "ACCOUNT"#'SiteAddres_1','SiteAddres_1', 'SiteNum_1', 'SiteNumSfx_1','Predir_1','SiteStreet_1', 'StreetType_1', 'Postdir_1', 'SiteCity_1', 'SiteZIP_1', 'OwnerName_1' Pnt = "Account"# TaxPar1 = "selectedParcels" parcelsCount = int(arcpy.GetCount_management(FC).getOutput(0)) dsc = arcpy.Describe(FC) selection_set = dsc.FIDSet if len(selection_set) == 0: print "There are no features selected" elif parcelsCount >= 1: #Select parcel within selected Address Points int(arcpy.GetCount_management(FC).getOutput(0)) arcpy.SelectLayerByLocation_management(TaxPar, "CONTAINS", FC) #Create in memory parcel layer selectedParcels = arcpy.MakeFeatureLayer_management(TaxPar, TaxPar1) arcpy.SelectLayerByAttribute_management(TaxPar1, "NEW_SELECTION", "OBJECTID= "+OID) # define the field list from the parcels sourceFieldsList = ["ACCOUNT", poly,"SiteAddres",'SiteNum', 'SiteStreet','SiteNumSfx','Predir','SiteStreet', 'StreetType', 'Postdir', 'SiteCity', 'SiteZIP', 'OwnerName'] # define the field list to the points updateFieldsList = ["Account", Pnt,"SiteAddres", 'SiteNum', 'SiteStreet', 'SiteNumSfx','Predir','SiteStreet', 'StreetType', 'Postdir', 'SiteCity', 'SiteZip', 'OwnerName'] # Start an edit session. Must provide the workspace. edit = arcpy.da.Editor(arcpy.env.workspace) # Edit session is started without an undo/redo stack for versioned data # (for second argument, use False for unversioned data) edit.startEditing(True) # Start an edit operation edit.startOperation() # populate the dictionary from the polygon valueDict = {r[0]:(r[1:]) for r in arcpy.da.SearchCursor(TaxPar1, sourceFieldsList)} with arcpy.da.UpdateCursor(FC, updateFieldsList) as updateRows: for updateRow in updateRows: keyValue = updateRow[0] if keyValue in valueDict: for n in range (1,len(sourceFieldsList)): updateRow= valueDict[keyValue][n-1] updateRows.updateRow(updateRow) # Stop the edit operation. edit.stopOperation() # Stop the edit session and save the changes edit.stopEditing(True) arcpy.RefreshActiveView() #arcpy.Delete_management(sjpoints)
Solved! Go to Solution.
The variable OID is not assigned a value as far as I can see. So line 40 is selecting nothing. But SelectLayerByAttribute is unnecessary.
If you are writing to SDE data that is versioned then you do need to do the Editor operation. However, you should use the with syntax to get errors reported and automated closure of the session
Also, you over complicating this and you are creating unnecessary selections. Selections inside selections is just another embedded loop and is BAD. Don't do that. Just use a dictionary and load every selected parcel into it all at once. It will do the Account look up 100 times faster than SelectLayerByAttribute. So use the code below.
I based this on your code. Your code assumes that Account in the address points already matches Account in the Parcels and that no two parcels have the same Account. If the Account of the Address that touches a parcel is not the same as the Account on the parcel then the update cursor won't work. If the real match is only spatial then this code approach won't work. Also if two addresses have the same Account and fall on two parcels with the same accounts, the result will just use the last parcel with that account that is read and not necessarily the parcel touched by the address.
For a pure spatial match I would do a Spatial Join with the One To Many option to an in_memory fc, not SelectLayerByLocation. The Spatial Join is much faster and more reliable for spatial matching than any attribute that may or may not be spatially related. The Spatial Join result would have the necessary ObjectID values to use the cursor for the transfer.
import arcpy from datetime import datetime as d startTime = d.now() #set to folder where features are located arcpy.env.workspace = r"Database Servers\DSD15_SQLEXPRESS.gds\TEST (VERSION:dbo.DEFAULT)" #r"Database Servers\DSD15_SQLEXPRESS.gds\TonyTwoWay (VERSION:dbo.DEFAULT)" #on windows use \ instead of / arcpy.env.overwriteOutput = True arcpy.env.qualifiedFieldNames = False #define variables for cursor FC = "TEST.DBO.CCAPTEST" #pointlayer TaxPar = "TaxParcels" #Parcels poly = "ACCOUNT"#'SiteAddres_1','SiteAddres_1', 'SiteNum_1', 'SiteNumSfx_1','Predir_1','SiteStreet_1', 'StreetType_1', 'Postdir_1', 'SiteCity_1', 'SiteZIP_1', 'OwnerName_1' Pnt = "Account"# parcelsCount = int(arcpy.GetCount_management(FC).getOutput(0)) dsc = arcpy.Describe(FC) selection_set = dsc.FIDSet if len(selection_set) == 0: print "There are no features selected" elif parcelsCount >= 1: #Select parcel within selected Address Points arcpy.SelectLayerByLocation_management(TaxPar, "CONTAINS", FC) # define the field list from the parcels sourceFieldsList = ["ACCOUNT", poly,"SiteAddres",'SiteNum', 'SiteStreet','SiteNumSfx','Predir','SiteStreet', 'StreetType', 'Postdir', 'SiteCity', 'SiteZIP', 'OwnerName'] # populate the dictionary will all selected polygons valueDict = {r[0]:(r[1:]) for r in arcpy.da.SearchCursor(TaxPar, sourceFieldsList)} # define the field list to the points updateFieldsList = ["Account", Pnt,"SiteAddres", 'SiteNum', 'SiteStreet', 'SiteNumSfx','Predir','SiteStreet', 'StreetType', 'Postdir', 'SiteCity', 'SiteZip', 'OwnerName'] # Start an edit session. Must provide the workspace. with arcpy.da.Editor(arcpy.env.workspace) as edit: with arcpy.da.UpdateCursor(FC, updateFieldsList) as updateRows: for updateRow in updateRows: keyValue = updateRow[0] if keyValue in valueDict: for n in range (1,len(sourceFieldsList)): updateRow= valueDict[keyValue][n-1] updateRows.updateRow(updateRow) arcpy.RefreshActiveView()
Your code is missing the indentation. Please make sure it pastes in with the indentation preserved or else it's difficult for us to interpret what you really intend your code to do.
Also, on line 48, did you mean to assign that to a variable or something?
I notice the indentations where off when attached my code. I tried to fix the indentations but it would not let me.
I am using line 48 to use the selected point for line 50 for the selectlayerbylocation.
2CDSD 2C,
for future reference with posting code Posting Code blocks in the new GeoNet
For me, what works best is copying it over first, then applying the syntax to the block I just copied over...which is what Curtis shows in the blog. It never seems to work for me if I try to fix it after it is formatted on GeoNet.
That's usually how post code too, but for some reason it mess it up. Maybe it's firefox....
It must be FireFox, i just posted an update to my code and worked .
I remove some unnecessary lines.
I usually use Firefox and it works ok....but there are so many flavors of Firefox, it's hard to say.
Why are you using an edit session? Update cursors do not need an edit session to work.
You should try debugging with some print statements to make sure the values that you are your dictionary keys and the values they are returning are correct.
I'm guessing you are basing this on Richard Fairhurst, Turbo Charging Data Manipulation with Python Cursors and Dictionaries. I have a working script that does the exact thing you are doing for converting data to LGIM for my other job. I can take a look at it tomorrow and help out if the edit session is not the issue. Also perhaps Richard could help you out, he is usually more than willing to help people adapt his code to thier own use.
The variable OID is not assigned a value as far as I can see. So line 40 is selecting nothing. But SelectLayerByAttribute is unnecessary.
If you are writing to SDE data that is versioned then you do need to do the Editor operation. However, you should use the with syntax to get errors reported and automated closure of the session
Also, you over complicating this and you are creating unnecessary selections. Selections inside selections is just another embedded loop and is BAD. Don't do that. Just use a dictionary and load every selected parcel into it all at once. It will do the Account look up 100 times faster than SelectLayerByAttribute. So use the code below.
I based this on your code. Your code assumes that Account in the address points already matches Account in the Parcels and that no two parcels have the same Account. If the Account of the Address that touches a parcel is not the same as the Account on the parcel then the update cursor won't work. If the real match is only spatial then this code approach won't work. Also if two addresses have the same Account and fall on two parcels with the same accounts, the result will just use the last parcel with that account that is read and not necessarily the parcel touched by the address.
For a pure spatial match I would do a Spatial Join with the One To Many option to an in_memory fc, not SelectLayerByLocation. The Spatial Join is much faster and more reliable for spatial matching than any attribute that may or may not be spatially related. The Spatial Join result would have the necessary ObjectID values to use the cursor for the transfer.
import arcpy from datetime import datetime as d startTime = d.now() #set to folder where features are located arcpy.env.workspace = r"Database Servers\DSD15_SQLEXPRESS.gds\TEST (VERSION:dbo.DEFAULT)" #r"Database Servers\DSD15_SQLEXPRESS.gds\TonyTwoWay (VERSION:dbo.DEFAULT)" #on windows use \ instead of / arcpy.env.overwriteOutput = True arcpy.env.qualifiedFieldNames = False #define variables for cursor FC = "TEST.DBO.CCAPTEST" #pointlayer TaxPar = "TaxParcels" #Parcels poly = "ACCOUNT"#'SiteAddres_1','SiteAddres_1', 'SiteNum_1', 'SiteNumSfx_1','Predir_1','SiteStreet_1', 'StreetType_1', 'Postdir_1', 'SiteCity_1', 'SiteZIP_1', 'OwnerName_1' Pnt = "Account"# parcelsCount = int(arcpy.GetCount_management(FC).getOutput(0)) dsc = arcpy.Describe(FC) selection_set = dsc.FIDSet if len(selection_set) == 0: print "There are no features selected" elif parcelsCount >= 1: #Select parcel within selected Address Points arcpy.SelectLayerByLocation_management(TaxPar, "CONTAINS", FC) # define the field list from the parcels sourceFieldsList = ["ACCOUNT", poly,"SiteAddres",'SiteNum', 'SiteStreet','SiteNumSfx','Predir','SiteStreet', 'StreetType', 'Postdir', 'SiteCity', 'SiteZIP', 'OwnerName'] # populate the dictionary will all selected polygons valueDict = {r[0]:(r[1:]) for r in arcpy.da.SearchCursor(TaxPar, sourceFieldsList)} # define the field list to the points updateFieldsList = ["Account", Pnt,"SiteAddres", 'SiteNum', 'SiteStreet', 'SiteNumSfx','Predir','SiteStreet', 'StreetType', 'Postdir', 'SiteCity', 'SiteZip', 'OwnerName'] # Start an edit session. Must provide the workspace. with arcpy.da.Editor(arcpy.env.workspace) as edit: with arcpy.da.UpdateCursor(FC, updateFieldsList) as updateRows: for updateRow in updateRows: keyValue = updateRow[0] if keyValue in valueDict: for n in range (1,len(sourceFieldsList)): updateRow= valueDict[keyValue][n-1] updateRows.updateRow(updateRow) arcpy.RefreshActiveView()
Richard thanks for the awesome feedback and suggestions. in this situation how would i go about getting syntax to get errors reported and automated closure of the session?
I have tried your code you posted but nothing happens the FC selected point doesn't get any updated attributes and there is no error.