Python add in- update two fields in one feature based on same fields of another

8058
43
Jump to solution
04-14-2014 11:10 AM
AndrewMosley
Emerging Contributor
I'm totally new to making add ins and quite novice at python itself but I have a script that executes perfectly as a script tool or simply in the python window of ArcMap. I want to make this a quick button click so I looked into making an add-in toolbar with a button.

Just to get the hang of making the addin i made a print "Hello World!" that works as expected.

However, it seems something is going wrong with my code when put into the add-in. I can only assume I'm missing some syntax due to my inexperience with python.

The situation here is I have a percels layer and an address points layer, which both contain a PIN and TMS number. I simply want to have the function select the address points within the parcel geometry, find the value of PIN and TMS from the parcel layer's attribute table, and update the same fields in the address points' attribute table. Again, the script I've written executes perfectly inside the python console in ArcMap but not as an add-in. Thanks for any hints you all might have!

So here's what I've got:

import arcpy import pythonaddins  class BlueBird(object):     """Implementation for BlueBird.button (Button)"""     def __init__(self):         self.enabled = True         self.checked = False     def onClick(self): arcpy.SelectLayerByLocation_management("Address Points","COMPLETELY_WITHIN","Parcels")  #insert if statement checking that only one parcel is selected to prevent selecting all address points  with arcpy.da.SearchCursor("Parcels",'PIN') as cursor:  for row in cursor:   storedPIN = row[0] with arcpy.da.UpdateCursor("Address Points",'PIN') as cursor:  for row in cursor:   row[0] = storedPIN   cursor.updateRow(row)  del cursor  with arcpy.da.SearchCursor("Parcels",'TMS') as cursor:  for row in cursor:   storedTMS = row[0] with arcpy.da.UpdateCursor("Address Points",'TMS') as cursor:  for row in cursor:   row[0] = storedTMS   cursor.updateRow(row)  del cursor, storedPIN, storedTMS
Tags (2)
0 Kudos
43 Replies
TonyAlmeida
MVP Regular Contributor

ok got to run good.

I was trying to revert the process. Instead of selecting the parcel i was trying to select the address point and populate the address point from the taxparcels.

I have it running if only one is selected, if i select more then one point ArcMap freaks out.

the i get the following error

Runtime error  Traceback (most recent call last):   File "<string>", line 86, in <module>

File "c:\program files (x86)\arcgis\desktop10.2\arcpy\arcpy\management.py", line 6812, in SelectLayerByLocation

raise e ExecuteError: ERROR 000582: Error occurred

during execution.

Here is the code.

import arcpy 

import os 

import pythonaddins 

 

# Assign string literal layer names to variables to make them easier to change with just one line of code. 

# Good practice for template code that you may not want to do a search and replace to adapt it for new layers. 

parcels = "Taxparcels" 

addressPoints = "TonyTwoWay.DBO.CCAP_Test" 

arcpy.env.overwriteOutput = True

#Get the current map document and the first data frame. 

mxd = arcpy.mapping.MapDocument('current') 

df = arcpy.mapping.ListDataFrames(mxd)[0] 

#Check to make sure user had at least one parcel selected. 

pointCount = int(arcpy.GetCount_management(addressPoints).getOutput(0)) 

if 0 < pointCount < 2: 

    

    lyr = arcpy.mapping.ListLayers(mxd, addressPoints, df)[0] 

    workspace = r"C:\Users\talmeida\AppData\Roaming\ESRI\Desktop10.2\ArcCatalog\Connection to dsd15_sqlexpress.sde" 

    # Start an edit session. Must provide the workspace. 

    edit = arcpy.da.Editor(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() 

    #Select parcel within selected address Points  

    arcpy.SelectLayerByLocation_management(parcels, "INTERSECT", addressPoints) 

    with arcpy.da.SearchCursor(parcels, ['ACCOUNT','SiteStreet']) as singleCursor: 

        for singleRow in singleCursor: 

            ACCOUNT = singleRow[0] 

            SiteStreet = singleRow[1] 

    with arcpy.da.UpdateCursor(addressPoints, ['Account', 'SiteStreet']) as singleCursor: 

        for singleRow in singleCursor: 

            singleRow[0] = ACCOUNT 

            singleRow[1] = SiteStreet 

            singleCursor.updateRow(singleRow) 

    # Stop the edit operation. 

    edit.stopOperation()

    edit.stopEditing(True)

    print('Parcel address update is complete.') 

elif 1 < pointCount < 1000: 

    #Create in memory parcel layer 

    selectedPoints = arcpy.MakeFeatureLayer_management(addressPoints, "selectedPoints") 

    for lyr in arcpy.mapping.ListLayers(mxd, "", df): 

        if lyr.name == addressPoints: 

            addressLayer = lyr 

        if lyr.name == parcels: 

            parcelsLayer = lyr 

    workspace = r"C:\Users\talmeida\AppData\Roaming\ESRI\Desktop10.2\ArcCatalog\Connection to dsd15_sqlexpress.sde" 

    # Start an edit session. Must provide the workspace. 

    edit = arcpy.da.Editor(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() 

    with arcpy.da.SearchCursor(parcels, ['ACCOUNT', 'SiteStreet', 'OBJECTID']) as parcelCursor: 

        for parcelRow in parcelCursor: 

            ACCOUNT = parcelRow[0] 

            SiteStreet = parcelRow[1] 

            OID = parcelRow[2] 

            OID = str(OID) 

            arcpy.SelectLayerByAttribute_management("selectedPoints", "NEW_SELECTION", "OBJECTID= "+OID) 

            #Select parcels by location, within selected Address Points. 

            arcpy.SelectLayerByLocation_management(parcels,"INTERSECT","selectedPoints") 

            with arcpy.da.UpdateCursor(addressPoints, ['Account', 'SiteStreet']) as addressCursor: 

                for addressRow in addressCursor: 

                    addressRow[0] = ACCOUNT 

                    addressRow[1] = SiteStreet 

                    addressCursor.updateRow(addressRow) 

    # Stop the edit operation. 

    edit.stopOperation()

    edit.stopEditing(True)

    arcpy.Delete_management("selectedPoints") 

    print('Parcel address update is complete.') 

else: 

    print('Please select a valid number of parcels.')

0 Kudos
RichardFairhurst
MVP Alum

Tony:

Of course it is freaking out.  It was not designed to do what you want and the logic fundamentally is altered if you start with a selection of objects that need data written to them rather than a selection of objects with data to read.  Getting the first half of the code to work is the simplistic case and easy to reverse, it is the second half of the code where the complexities of the problem come into play and a simple reversal of code will never work.

You cannot simply reverse the process unless the address points are the data source with values to read and the parcels are the update target with fields to be written.  As long as the parcels have the data, you have to obtain a parcel selection one way or another and read it before you process the addresses, because you have to read the values you want to transfer and then update the features with blank fields.

Stop hacking the code.  You don't understand why I organized it the way I did if you think it can just be reversed.  It is dependent on several conditions that your data does not meet.  To have any hope of getting useful code you have to define the data you are starting with and what you want to end up with.

I gather you have the following:

1.  The parcels have the data you want to read (the same as the data the original code was based on)

2.  The address points are the target that need fields completed from parcel data.(the same as the data the original code was based on)

3.  You want to select addresses instead of parcels (opposite the original data not only in terms of the object type, but also in terms of switching the selection from the data source to the data target)

4.  One address point can only fall within one parcel.  (True of the original data and why it was easy to get the first half of the code to work where only one address, and therefore only one parcel, is involved).

5.  You only want to write to the selected address points. (not at all a requirement of the original code.)

6.  You cannot assume there is a parcel covering all of the address points you selected that have data to transfer (not a consideration with the original data, and this condition could still result in a failure in the first half of the code as well as the second half)

7.  You don't want to select the same parcel over and over if it covers many address points (easily done with the original data, not easily done with your data).

As you can see it is not a simple reversal of the code.  New errors and inefficiencies could arise with this scenario that were not a factor with the original code.  The code structure has to be rethought.  Before I can rethink it I need to verify all of the statements I have outlined are true, so that I can include all of the new error checks I now have to handle and the most efficient steps that will meet all of the new requirements of the problem.

0 Kudos
TonyAlmeida
MVP Regular Contributor

My apologies Richard if i offended you by trying to change the code. At some point i was trying to write some code similar to you posted but instead of selecting the parcels i was trying to select the address points and that's was what i was trying to accomplish.

0 Kudos
RichardFairhurst
MVP Alum

Tony:

Trying to change the code doesn't offend me.  However, I want you to realize that the code was designed to solve a specific problem and, from what you have told me, it appears you are now solving a different problem that requires some fundamental changes to the overall code design.  I cannot offer advice on why the original code does not work for the new problem without first analyzing and understanding how this new problem is both similar to and different from the original problem.

So look at each of the 7 items I described as a requirement of the new problem and tell me if I have correctly or incorrectly described what the code revisions must accomplish.

If all problems could simply be reversed than there would be only trivial differences between the recipe for taking a carton of eggs to make scrambled eggs and the recipe for taking scrambled eggs to fill a carton of eggs.  But the differences between the problems that have to be solved by those two recipes far outweigh any similarities.

That is an intentionally extreme example.  But i hope it makes it clear that before code designed to solve one problem is used to solve its opposite problem that the differences between the two problems must be limited and relatively trivial to make that adaptation.  While your problem is not unsolvable like the second half of the scrambled egg problem, based on the differences I have outlined it is not a trivial change that you are making.

0 Kudos