Copy selected features to geodatabase feature class

5807
34
Jump to solution
06-11-2015 08:23 AM
CCWeedcontrol
Occasional Contributor III

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()
Tags (1)
0 Kudos
34 Replies
CCWeedcontrol
Occasional Contributor III

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?

0 Kudos
RichardFairhurst
MVP Honored Contributor

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.

CCWeedcontrol
Occasional Contributor III

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.

0 Kudos
RichardFairhurst
MVP Honored Contributor

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.

CCWeedcontrol
Occasional Contributor III

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)  
0 Kudos
RichardFairhurst
MVP Honored Contributor

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.

CCWeedcontrol
Occasional Contributor III

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?

0 Kudos
RichardFairhurst
MVP Honored Contributor

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]

0 Kudos
CCWeedcontrol
Occasional Contributor III

Thanks for the reply Richard, i am not familiar with dictionaries and lists so am unable to incorporate your suggestions to my code.

0 Kudos
RichardFairhurst
MVP Honored Contributor

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