Select to view content in your preferred language

Select points and update attributes base on parcels

5336
14
Jump to solution
07-08-2015 01:35 PM
CCWeedcontrol
Frequent Contributor

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)
0 Kudos
1 Solution

Accepted Solutions
RichardFairhurst
MVP Honored Contributor

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()

View solution in original post

14 Replies
BlakeTerhune
MVP Regular Contributor

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?

0 Kudos
CCWeedcontrol
Frequent Contributor

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.

0 Kudos
RebeccaStrauch__GISP
MVP Emeritus

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.

0 Kudos
CCWeedcontrol
Frequent Contributor

That's usually how post code too, but for some reason it mess it up. Maybe it's firefox....

0 Kudos
CCWeedcontrol
Frequent Contributor

It must be FireFox, i just posted an update to my code and worked .

I remove some unnecessary lines.

0 Kudos
RebeccaStrauch__GISP
MVP Emeritus

I usually use Firefox and it works ok....but there are so many flavors of Firefox, it's hard to say.

0 Kudos
IanMurray
Honored Contributor

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.

RichardFairhurst
MVP Honored Contributor

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()
CCWeedcontrol
Frequent Contributor

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.

0 Kudos