I am working on creating a addon button to update some address points and copy that updated feature. The first part of the code does what it is suppose to do (only update the selected feature) but the second part of the code doesn't do what it is suppose to do and that is copy "only" the selected feature to the file geodatbase feature class. The code runs fine i don't get any errors but the second part of the codes doesn't copy the selected feature.
I would also like to an an "if" statement. if there is no feature selected i want it to do nothing or tell me there is nothing selected. if there is something selected i want it to only updates the selected feature and only copy the selected feature. Any ideas?
import arcpy, time import pythonaddins import os #populate selected feature APT = "TEST" arcpy.env.overwriteOutput = True mxd = arcpy.mapping.MapDocument("CURRENT") if int(arcpy.GetCount_management(APT).getOutput(0)) > 0: rows = arcpy.UpdateCursor(APT) for row in rows: row.FacltyType = ("Single Family Home") row.StructType = ("Primary, Private") row.Verified = ("Yes, GRM, TA") row.Status = ("Active") row.StructCat = ("Residential") row.APA_CODE = ("1110") rows.updateRow(row) del row, rows #Copies address point to backup address points in filegeodatabase fc1 = "CCAP" #Database workspace = r"C:\GIS\CCAP\CCAP.mdb" arcpy.env.overwriteOutput = True # Start an edit session. Must provide the worksapce. 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() if int(arcpy.GetCount_management(APT).getOutput(0)) > 0: arcpy.Append_management(APT, fc1, "NO_TEST") # Stop the edit operation. edit.stopOperation() # Stop the edit session and save the changes edit.stopEditing(True) arcpy.RefreshActiveView()
Solved! Go to Solution.
I deleted line 20, ran the current code with one point selected and it updated the selected point for APT but still did not copy the feature to "CCAP".
If nothing is selected and i run it it keeps repeating the process the process indicator goes from 0 to 100 and then refreshes results. I have to terminate arcmap through task manager.
Maybe i am not going about this the right way? is there a batter way to do what i am trying to do?
Append Management has a bug that won't refresh until you do things like manually remove the entire layer from the map and add it back. You may find that you have a huge number of copies of the record created if you do that. It does not respond to any refresh code when used in a script. I got no help from esri support on the subject and had to abandon a tool I was developing based on using the Append tool in an interactive process. It only works if the script is standalone and never needs to refresh an active ArcMap session. I would consider using an insert cursor instead, although I seem to recall that it also has some quirks when trying to do a refresh in an interactive ArcMap session.
I think esri filed a bug report. You may want to let support know that you also expected the Append tool to work in an interactive scripted workflow, and that the inability to refresh the tool's output makes it useless for that purpose. If they don't intend to fix it they need to document this behavior to avoid wasting our time developing tools that logically should work, but really won't if the user can only tell that the tool has actually altered the table by doing an unscripted manual set of actions after our scripts complete.
I just reviewed my e-mails to esri support on the subject and see that they did not actually classify this behavior as a bug, but instead that the behavior you and I expect is an "enhancement". Here is what they said in April of 2014:
"After testing this behavior, I have logged an enhancement on your behalf to document this. The reference number and subject line for this enhancement is as follows:
[#NIM100778 Ability to refresh the attribute table automatically after running the append tool when the target attribute table is already open. ]
This enhancement will now be reviewed by our development team, and they will take further action as necessary to address this issues as soon as possible. For updates on this enhancement, please see the My Support portal at http://support.esri.com or contact Esri Support Services"
I believe my original incident report only focused on the in ability to refresh a Standalone Table. It may not have covered feature classes and the behavior of the Append tool in connection with refreshing the map view, so the NIM probably needs to be expanded.
Going back to my original code, i made some modifications. It seems to some what run to my needs. my python skills are not so great.
I changed line 26 on my original post from, workspace = r"C:\GIS\CCAP\CCAP.mdb" to arcpy.env.workspace = r"C:\GIS\CCAP\CCAP.mdb".
After that change the Append Management works and even refreshed fine. The only issue is that it takes 1 min to copy just one point from one layer to the other. The other thing is that i need so some times select 1 or 50 points at a time and run this scrip\tool, but i do not know how set that part up. any help would be great. Currently it can only run against one point.
Current code.
import arcpy APT = "TEST" fc1 = "CCAP" arcpy.env.workspace = r"C:\GIS\CCAP\CCAP_Copy_NEW.gdb" arcpy.env.overwriteOutput = True mxd = arcpy.mapping.MapDocument("CURRENT") #Fill in attributes on point layer Pointcount = int(arcpy.GetCount_management(APT).getOutput(0)) if 0 < Pointcount < 2: rows = arcpy.UpdateCursor(APT) for row in rows: row.FacltyType = ("Single Family Home") row.StructType = ("Primary, Private") row.Verified = ("Yes, GRM, TA") row.Status = ("Active") row.StructCat = ("Residential") row.APA_CODE = ("1110") rows.updateRow(row) del row, rows else: # Handle Exception print "No features selected." #Check to make sure user had at least one point is selected. #If point selected Copy to back up point layer. parcelsCount2 = int(arcpy.GetCount_management(APT).getOutput(0)) if 0 < parcelsCount2 < 2: arcpy.Append_management(APT, fc1, "NO_TEST") else: # Handle Exception print "No features selected." arcpy.RefreshActiveView()
Richard you mentioned insert cursor, do have any similar code available as to what i am trying to accomplish? i would be gratefully appreciative... I am not the greatest python writer.
I just tried the insert cursor with a standaline table and it appears to have the same problem as the append tool. It is inserting the records, but if the table view is open, the user will not see the records they just inserted until they manually close and then reopen the table view. That makes interactive automation impossible, since most users would just assume the insert failed if they don't see any new records. Training them to close and reopen the table view to show that the tool worked is not a good tool design. There is no way to use arcpy to automatically close and reopen the table view as far as I can see or to get the table view cache to refresh.
Your code is slow because you are not using arcpy.da cursors. The old cursors are useless. Lookup the arcpy.da.update cursor syntax in the help and rewrite your code to use that type of cursor.
The only reason this only works for one point is that you added code to restrict it to only copy 1 point (if 0 < parcelsCount2 < 2: means only 1 point. At least 1 point would be if 0 < parcelsCount2:). The append tool is not limited to copying a single point. If you want one point per each feature class, then that is another matter, but why would you need that? It is backed up whether there is one point or 50 points in your backup layer. Anyway, I don't think you have clearly explained what you want to do with the 1 to 50 points with your code.
The purpose of this script/tool would be that i can select one or two or 50 points at once and update just those selected points with the info in the rows = arcpy.UpdateCursor(APT). Then take those selected points and copy them to a different personal/filedatabase but with the info that was updated in the rows = arcpy.UpdateCursor(APT) and other fields. I have tried to implementing your suggestion on insert cursor and i have been able to create a point with insertcursor but it seems as the arcpy.da.UpdateCursor is still very slow. takes about 2-4 minutes to add one point to the backup data base. sometimes after testing and testing it takes 20 mins... weird The insertcursor is superfast it's the and arcpy.update cursor and arcpy.da.UpdateCursor that are super slow. Maybe i am not doing it the most efficient way...
apologies my code is a little mangled
import arcpy from datetime import datetime as d startTime = d.now() #set to folder where features are located arcpy.env.workspace = r"C:\CCAP_Copy_NEW.gdb" #on windows use \ instead of / arcpy.env.overwriteOutput = True FC = "test" #--------------------------- #define variables for cursor #--------------------------- rows = arcpy.SearchCursor(FC) for row in rows: geom = row.Shape X = geom.centroid.X Y = geom.centroid.Y del rows incidentsFC = "CCAP" # Create point inPoint = arcpy.PointGeometry(arcpy.Point(X,Y)) #Check to make sure user had at least one point is selected. parcelsCount = int(arcpy.GetCount_management(FC).getOutput(0)) """SEARCHCURSOR""" stable = "test" sfield = ["ACCOUNT","SiteNum","OwnerName","SiteAddres","SiteNumSfx","SiteStreet","Predir"] #where clause will be written in SQL and not assigned to variable """UPDATECURSOR""" utable = "CCAP" ufield = ["ACCOUNT","SiteNum","OwnerName","SiteAddres","SiteNumSfx","SiteStreet","Predir"] #where clause will be written in SQL and not assigned to variable #Check to make sure user had at least one point is selected. dsc = arcpy.Describe(FC) dsc = arcpy.Describe(stable) selection_set = dsc.FIDSet if len(selection_set) == 0: print "There are no features selected" #-------------------------- #start the loop #-------------------------- elif parcelsCount >= 1: rows = arcpy.UpdateCursor(FC) for row in rows: row.FacltyType = ("Single Family Home") row.StructType = ("Primary, Private") row.Verified = ("Yes, GRM, TA") row.Status = ("Active") row.StructCat = ("Residential") row.APA_CODE = ("1110") rows.updateRow(row) del row, rows #arcpy.Append_management(APT, fc1, "NO_TEST") # Create the insert cursor and a new empty row rowInserter = arcpy.InsertCursor(incidentsFC) newIncident = rowInserter.newRow() # Populate attributes of new row newIncident.SHAPE = inPoint #newIncident.setValue(descriptionField, inDescription) # Insert the new row into the shapefile rowInserter.insertRow(newIncident)# Handle Exception del rowInserter with arcpy.da.SearchCursor(stable, sfield) as scursor: for srow in scursor: print srow with arcpy.da.UpdateCursor(utable, ufield) as ucursor: for urow in ucursor: ucursor.updateRow(srow) arcpy.RefreshActiveView() try: print '(Elapsed time: ' + str(d.now() - startTime)[:-3] + ')' except Exception, e: # If an error occurred, print line number and error message import traceback, sys tb = sys.exc_info()[2] print "Line %i" % tb.tb_lineno print e.message
arcpy.da.UpdateCursor
rows = arcpy.SearchCursor(FC) for row in rows: geom = row.Shape X = geom.centroid.X Y = geom.centroid.Y del rows incidentsFC = "CCAP" # Create point inPoint = arcpy.PointGeometry(arcpy.Point(X,Y)) #Check to make sure user had at least one point is selected. 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: rows = arcpy.UpdateCursor(FC) for row in rows: row.FacltyType = ("Single Family Home") row.StructType = ("Primary, Private") row.Verified = ("Yes, GRM, TA") row.Status = ("Active") row.StructCat = ("Residential") row.APA_CODE = ("1110") rows.updateRow(row) #arcpy.Append_management(APT, fc1, "NO_TEST") # Create the insert cursor and a new empty row rowInserter = arcpy.InsertCursor(incidentsFC) newIncident = rowInserter.newRow() # Populate attributes of new row newIncident.SHAPE = inPoint #newIncident.setValue(descriptionField, inDescription) # Insert the new row into the shapefile rowInserter.insertRow(newIncident)# Handle Exception #print "No features selected." del rowInserter with arcpy.da.SearchCursor(FC, ['ACCOUNT', 'SiteStreet']) as parcelCursor: for parcelRow in parcelCursor: ACCOUNT = parcelRow[0] SiteStreet = parcelRow[1] with arcpy.da.UpdateCursor(incidentsFC, ['Account', 'SiteStreet']) as addressCursor: for addressRow in addressCursor: addressRow[0] = ACCOUNT addressRow[1] = SiteStreet addressCursor.updateRow(addressRow)
The cursor is not to blame. You either have a corrupt fgdb, fc, or a bad install of ArcMap. If you have tech support available to you, then after creating a new fgdb and testing that, then if the speed problem is fixed call them.
Richard you were correct my fgdb were corrupted, thanks. I deleted and recreated them and the cursor ran FAST.
Although i still have an issue. When I select one record and run either codes from above it creates the point to the backup database, but it updates the fields of all the points in the backup database with the same attribute info as the selected point. I only want it to back up and update the selected points only. I also tried selected two or more points but only creates one point on the backup database and it also updates every point as well. What adjustments do i need to make to my code to only copy and update the selected points?
You also need to use the da insert cursor in your code in addition to the Search or Update cursors. Create a list variable and append the objectIDs returned by the InsertRow method. Then use that list to build an expression ot only update points with those objectIDs, either using python logic or SQL.
OIDList = []
... Set up insert cursor
OIDList.append(cursor.InsertRow(row))
# use this for an update cursor's SQL where clause
whereclause = 'OBJECTID IN (' + ', '.join(OIDList) + ')'
If each point is individually attributed then create a dictionary that uses the ObjectIDs returned by the InsertRow method as the key and store a list of attributes as the dictionary value for the key. Then use the dictionaries keys as the join list.
OIDDict = {}
OID = cursor.InsertRow(row)
OIDDict[OID] = [fieldAttribute1, fieldAttribute2, fieldAttribute3]
# use this for an update cursor's SQL where clause
whereclause = 'OBJECTID IN (' + ', '.join(OIDDict.keys()) + ')'
# Assume first field is OBJECTID
OID = row[0]
row[1] = OIDDict[OID][0]
row[2] = OIDDict[OID][1]
row[3] = OIDDict[OID][2]
Thanks for the reply Richard, i am not familiar with dictionaries and lists so am unable to incorporate your suggestions to my code.
After looking more carefully at your code you are opening way too many cursors on the same data. Based on how I read what your code is actually doing, you only need one UpdateCursor and one InsertCursor as shown below:
import arcpy from datetime import datetime as d startTime = d.now() #set to folder where features are located arcpy.env.workspace = r"C:\CCAP_Copy_NEW.gdb" #on windows use \ instead of / arcpy.env.overwriteOutput = True #--------------------------- #define variables for cursor #--------------------------- FC = "test" incidentsFC = "CCAP" dsc = arcpy.Describe(FC) rowInserter = arcpy.da.InsertCursor(incidentsFC, ["SHAPE@", 'Account', 'SiteStreet']) selection_set = dsc.FIDSet if len(selection_set) == 0: print "There are no features selected" elif parcelsCount >= 1: with arcpy.da.UpdateCursor(FC, ["FacltyType", "StructType", 'Verified", "Status", "StructCat", "APA_CODE", "ACCOUNT", 'SiteStreet', 'SHAPE@X', 'SHAPE@Y']) as rows for row in rows: row[0] = ("Single Family Home") row[1] = ("Primary, Private") row[2] = ("Yes, GRM, TA") row[3] = ("Active") row[4] = ("Residential") row[5] = ("1110") ACCOUNT = row[6] SiteStreet = row[7] X = row[8] Y = row[9] rows.updateRow(row) inPoint = arcpy.PointGeometry(arcpy.Point(X,Y)) rowInserter.insertRow((inPoint, ACCOUNT, SiteStreet)) del row del rows del rowInserter arcpy.RefreshActiveView() try: print '(Elapsed time: ' + str(d.now() - startTime)[:-3] + ')' except Exception, e: # If an error occurred, print line number and error message import traceback, sys tb = sys.exc_info()[2] print "Line %i" % tb.tb_lineno print e.message