Select to view content in your preferred language

Help with UdateCursor populating a  layer from another selected layer

1613
13
Jump to solution
11-22-2013 08:15 AM
TonyAlmeida
MVP Regular Contributor
I am having trouble populating Points layer "FacltyType" field with the selected layers CPUC field.
I need to able to select multiple points from the Points_2 layer then select by location on the Points layer by using the SelectLayerByLocation. Then i need to update the "facltyType" field of Points layer with the selected Points_2 CPUC field.
If the Points_2 layer CPUC filed has "DWELL" i need the Points layer FacltyType field to populate as "Home", if it is NULL i need the FacltyType field of the Points layer to populate as "MobileHome"

My current code tempt makes a permanent Join between Points and Points_2 on the TOC. Is there a way on how to make a temporary Join then populate as i mentioned? Also the current code just populates the "FacltyType" with DELL, but i need to do as i mentioned above.

import arcpy, os from arcpy import env  arcpy.env.overwriteOutput = True mxd = arcpy.mapping.MapDocument("CURRENT") df = arcpy.mapping.ListDataFrames(mxd, "Layers")[0] lyr = arcpy.mapping.ListLayers(mxd, "Points")[0]  arcpy.env.workspace = os.path.dirname(mxd.filePath) wp = os.path.dirname(mxd.filePath) #env.workspace = r"C:\GIS\Addressing\test"  Points = "Points" Points_2 = "Points_2" Fileds = "APA_CODE"  selection = 1 while selection < 3:         rows = arcpy.SearchCursor(Points) #opens search cursor on the info table         for row in rows:                                facltyType = row.FacltyType # extracts the name of the destination field             # Process: Select Layer By Location         print "select by location"         arcpy.SelectLayerByLocation_management(Points, "INTERSECT", Points_2, "", "NEW_SELECTION")         if int(arcpy.GetCount_management(Points).getOutput(0)) > 0:                           arcpy.MakeFeatureLayer_management(Points, "PointsLyr")             arcpy.MakeFeatureLayer_management(Points_2, "PointsLyr_2")             arcpy.JoinField_management("PointsLyr", "Account", "PointsLyr_2", "Account", "")                   # Update the FacltyType field with the CPUC values         rows = arcpy.UpdateCursor(Points)         for row in rows:             row.FacltyType = row.CPUC             rows.updateRow(row)         del rows         del row         selection +=1  arcpy.RemoveJoin_management("PointsLyr2") 


I tried making a feature layer in_memory but i got the following error:
Runtime error  Traceback (most recent call last):   File "<string>", line 33, in <module>   File "C:\Program Files (x86)\ArcGIS\Desktop10.1\arcpy\arcpy\management.py", line 5357, in JoinField     raise e ExecuteError: Failed to execute. Parameters are not valid. ERROR 000732: Input Table: Dataset PointsLyrA does not exist or is not supported ERROR 000732: Join Table: Dataset Points2 does not exist or is not supported Failed to execute (JoinField).

import arcpy, os from arcpy import env  arcpy.env.overwriteOutput = True mxd = arcpy.mapping.MapDocument("CURRENT") df = arcpy.mapping.ListDataFrames(mxd, "Layers")[0] lyr = arcpy.mapping.ListLayers(mxd, "Points")[0]  arcpy.env.workspace = os.path.dirname(mxd.filePath) wp = os.path.dirname(mxd.filePath) #env.workspace = r"C:\GIS\Addressing\test"  Points = "Points" Points_2 = "Points_2" Fileds = "APA_CODE"  selection = 1 while selection < 3:         rows = arcpy.SearchCursor(Points) #opens search cursor on the info table         for row in rows:                                facltyType = row.FacltyType # extracts the name of the destination field             # Process: Select Layer By Location         print "select by location"         arcpy.SelectLayerByLocation_management(Points, "INTERSECT", Points_2, "", "NEW_SELECTION")         if int(arcpy.GetCount_management(Points).getOutput(0)) > 0:                           arcpy.MakeFeatureLayer_management(Points, "in_memory\PointsLyrA")             arcpy.MakeFeatureLayer_management(Points_2, "in_memory\Points2")             arcpy.JoinField_management("PointsLyrA", "Account", "Points2", "Account", "")                   # Update the FacltyType field with the CPUC values         rows = arcpy.UpdateCursor(Points)         for row in rows:             row.FacltyType = row.CPUC             rows.updateRow(row)         del rows         del row         selection +=1   arcpy.Delete_management("in_memory")


Any code help would be great.

Thanks.
Tags (2)
0 Kudos
1 Solution

Accepted Solutions
T__WayneWhitley
Honored Contributor
Tony, you were almost there, see this -- definitely need to test the code below (I did not).  I wasn't sure how you wanted the fields coded since your last code was a little mixed up.  But no worries, for anything conditional to code a field, you'd put the under the if statement for whether it should be applied to Mobile Homes or Single Family Homes... see my comments in the code below - notice the indention alignment; for anything NOT conditional, that is it is coded the same way whether Mobile Home or Single Family Homes, enter your statements above the line, curW.updateRow(rowW).

Also, notice the 1st line where the 'time' module was imported...then further down, how the time was formatted for the Verified field.  Insert extra text formatting as you need it, as in this example (without '-' or '/' the date is 'mashed' together) - this is how you return the current local time (verify this yourself, I tested the following in IDLE; your corrected script it further below for you to test):

>>> import time
>>> time.strftime('%m%d%Y')
'11262013'
>>> time.strftime('%m-%d-%Y')
'11-26-2013'

>>> 'Yes, GRM, TA, ' + time.strftime('%m-%d-%Y')
'Yes, GRM, TA, 11-26-2013'
>>>


import arcpy, time arcpy.env.overwriteOutput = True  arcpy.env.workspace = r"C:\Temp\Defult.gdb" fcTarget = 'Points_1' fcJoin = 'Points_2' fcOutput = 'Points_joined'  arcpy.SpatialJoin_analysis(fcTarget, fcJoin, fcOutput, 'JOIN_ONE_TO_ONE', 'KEEP_COMMON')  curR = arcpy.SearchCursor(fcOutput, '', '', '', 'AddressID A') curW = arcpy.UpdateCursor(fcTarget, '', '', '', 'AddressID A')  # init rowW and rowR rowW = curW.next() rowR = curR.next()  while rowR:     currentAddress = rowR.AddressID     print 'current add: ' + currentAddress     while rowW.AddressID != currentAddress:         rowW = curW.next()     if rowR.CPUC == 'DWELL':         rowW.FacltyType = 'Single Family Home'         rowW.APA_CODE = '1110'         rowW.StructType = 'Primary, Private'         rowW.Verified = 'Yes, GRM, TA, ' + time.strftime('%m-%d-%Y')         rowW.Status = 'Active'         rowW.StructCat = 'Residential'     else:         rowW.FacltyType = 'MobileHome'         rowW.APA_CODE = '1150'         # put any additional conditional field statements here      # put any 'global' field statements here (applies to both SF/MH)             curW.updateRow(rowW)     rowR = curR.next()  # changed the delete statement, targeting the cursor objs (rather than the row objs) if curW:     del curW if curR:     del curR


Hope that helps - and by the way, you should probably mark this post answered...

Enjoy,
Wayne


EDIT:
I notice you made an earlier note about 'disappearance' of data - this could be due to cursor-locking.  I mentioned that you may explicitly need a del statement at the end of the script, but my previous one targeted only the row objects -- this should really be on the cursor objs (at minimum), and I don't think you caught that, so I'm editing the script in this post to reflect that correction.

View solution in original post

0 Kudos
13 Replies
RichardFairhurst
MVP Alum
First of all, I would never use Null to represent an actual classification value for a real object, since data like that always complicates code and makes it impossible to tell when something exists or does not exist without very careful processing.  If you mean to classify Mobilehome wells as actual objects, create a real value in your field to represent that and leave Null to mean it does not exist or has not been classified.

I would just use the Spatial Join tool on every feature in the entire dataset in one go.  I would use the One-To-One relationship and a modification to the field list to use the Join merge policy and specify a delimiter (right click the field you want merged into a list of values and change the settings).  While you are modifying the output field expand the number of characters in the field to be large enough to hold all values.  I would probably uncheck the keep all target features option, or include at least 2 fields in the summary where one of the fields will never be null if a Join occurs and preferably numeric.

The output will be a comma separated list.  I believe Nulls will be excluded, so if it is all Nulls the List will be Null, otherwise it could be one or more instances of the "DWELL", with multiple values separated by the delimiter you specified.  The field calculator could convert the values to the ones you want or fill in a new field with the values you want (safer) using a VB Script calculation like this (untested against any data like yours, since I don't approach data the way you seem to be doing):

If IsNull(InStr([fieldName], "DWELL")) then
  Output = "MOBILEHOME"
ElseIf InSrt([fieldName], "DWELL") then
  Output = "HOME"
Else
  Output = "MOBILEHOME"
End If


Expression:  Output

If there were 2 summaries you could test for Nulls in the other field to indicate there are no wells.  If you only got target features where an join occurred you could join that result to your original feature class and do the above calculation to a new field, adding a test for Null ObjectID of the join as a way of detecting no join occurred.  You would have the option of doing something different with multiple wells.

It is all much easier to do a calculation if a real value was present when a Mobilehome well existed, rather than Null.  Then my IsNull would really mean there is no Well at all and I could classify those as Null, Vacant or something else.  Also it would let you detect when both a Home and Mobilehome have wells on a property, multiple Homes or Mobilehomes are on a single property, etc.
0 Kudos
TonyAlmeida
MVP Regular Contributor
rfairhur24 thank you for the reply.

I did go about creating the Points layer initially with the spatial join you mentioned. Now i am maintain the layer.
The Points_2 layer is updated daily or weekly so that's why I've been working on a script to help me automate the data entry. I don't want recreate the layer ever time i get a Points_2 update. A few features my only need to be updated at a time.


Thanks.
0 Kudos
T__WayneWhitley
Honored Contributor
Just 2 comments, in brief, and maybe you've already noticed:
- I think some of your errors stem from referencing variable names that don't exist: should Points2 be Points_2?
- You're working with the JoinFields tool which I think permanently 'appends' fields to a table; I think AddJoin and RemoveJoin tools were meant to work together, adding/removing 'virtual' table fields.

Would it help if you worked with cursors instead to find and modify/append records as needed?

EDIT:  I meant work with cursors exclusively, possibly with the aid of a dictionary to temp store vals you want to 'transfer'...I don't think it makes sense to combine use of cursors and making joins - doesn't seem efficient.  It may help to post a sample of your data?  (and restate your problem)
0 Kudos
TonyAlmeida
MVP Regular Contributor
I noticed the error Points2 be Points_2 does not exist after i posted it.

I would like to populate the Points layer FacltyType field with attributes of the Points_2 CPUC field, but only those that are selected.
If the selected Points_2 feature are attributed with DWELL in the CPUC i would like to populate the FacltyType with "Home.  if it is blank i need the FacltyType field of the Points layer to populate as "MobileHome".

I also don't need to make the permanent join. I have never use the temp directory. I am newbie with python.

I have attached a sample of the data. I would be very grateful if you could help me to get this code right.
0 Kudos
T__WayneWhitley
Honored Contributor
Hello Tony - I tested the below code in 10.0 in the 'live' Python window (in ArcMap) on your 2 layers provided in your zip.  It runs faster than I thought although you could easily adapt it to a da set of cursors...or turn it into a script tool - not sure of the benefit of that, but that is one of your options.  The important thing is it does work from a selected set of your Point 2 dataset with a Spatial Join (via Intersect, so the points must generally match in very close proximity).  I simply allowed to write the entire field copy join output to my default gdb (because I'm lazy - optimize as you will). ...did not need the MFL tool since the output was made to retain only what was matched.  A simple trick was employed to use sorted cursors with the write cursor nested to skip over records not matching what I took to be the unique ID, AddressID.

Pretty short and simple really - no error trapping so use at your own risk.  Here it is below - any questions, let me know...

-Wayne

import arcpy
arcpy.env.overwriteOutput = True

# Spatial Join (Analysis) [webhelp 10.0]
# http://help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#/Spatial_Join/00080000000q000000/

fcTarget = 'Points_1'
fcJoin = 'Points_2'
fcOutput = 'Points_joined'

arcpy.SpatialJoin_analysis(fcTarget, fcJoin, fcOutput, 'JOIN_ONE_TO_ONE', 'KEEP_COMMON')

curR = arcpy.SearchCursor(fcOutput, '', '', '', 'AddressID A')
curW = arcpy.UpdateCursor(fcTarget, '', '', '', 'AddressID A')

# init rowW and rowR
rowW = curW.next()
rowR = curR.next()

while rowR:
    currentAddress = rowR.AddressID
    print 'current add: ' + currentAddress
    while rowW.AddressID != currentAddress:
        rowW = curW.next()
    print 'found current: ' + rowW.AddressID
    if rowR.CPUC == 'DWELL':
        rowW.FacltyType = 'HOME'
        print 'calc FacltyType HOME'
    else:
        rowW.FacltyType = 'MobileHome'
        print 'calc FacltyType MobileHome'
    curW.updateRow(rowW)
    rowR = curR.next()

del rowW, rowR
0 Kudos
TonyAlmeida
MVP Regular Contributor
Wayne thank you for the code it worked great. could you explain what the code is doing?
I am still interested in see the "dictionary to temp store vals you want to 'transfer'" that you mentioned.
I am a visual learner.

I did add some lines to your code to populate other fields that i needed.
On the code I posted I tried to populate the "APA_CODE" field but nothing happen, nothing was populated.
I also need to add the Date(current date) to the end of the "Verified" field, i am not sure how to do this?
Also when i try to Populate Single Family Home fields it Does not do anything, does not populate

import arcpy
arcpy.env.overwriteOutput = True

arcpy.env.workspace = r"C:\Temp\Defult.gdb"
fcTarget = 'Points_1'
fcJoin = 'Points_2'
fcOutput = 'Points_joined'

arcpy.SpatialJoin_analysis(fcTarget, fcJoin, fcOutput, 'JOIN_ONE_TO_ONE', 'KEEP_COMMON')

curR = arcpy.SearchCursor(fcOutput, '', '', '', 'AddressID A')
curW = arcpy.UpdateCursor(fcTarget, '', '', '', 'AddressID A')

# init rowW and rowR
rowW = curW.next()
rowR = curR.next()

while rowR:
    currentAddress = rowR.AddressID
    print 'current add: ' + currentAddress
    while rowW.AddressID != currentAddress:
        rowW = curW.next()
    print 'found current: ' + rowW.AddressID
    if rowR.CPUC == 'DWELL':
        rowW.FacltyType = 'Single Family Home'
        print 'calc FacltyType Single Family Home'
    else:
        rowW.FacltyType = 'MobileHome'
        print 'calc FacltyType MobileHome'
        rowW.StructType = 'Primary, Private'
        rowW.Verified = 'Yes, GRM, TA' #Date?
        rowW.Status = 'Active'
        rowW.StructCat = 'Residential'        
    if rowR.FacltyType == 'Single Family Home':
        rowW.APA_CODE = '1110'
        print 'calc FacltyType Single Family Home'
    
#Populate Single Family Home fiels** Does not do anything, does not populate    
    if rowR.FacltyType == 'Single Family Home':
        rowW.StructType = 'Primary, Private'
        rowW.Verified = 'Yes, GRM, TA' #Date?
        rowW.Status = 'Active'
        rowW.StructCat = 'Residential'
    if rowR.FacltyType == 'MobileHome':
        rowW.APA_CODE = '1150'
        print 'calc FacltyType MobileHome'
        
    curW.updateRow(rowW)
    rowR = curR.next()

del rowW, rowR



Note: The first extra fields appear to be populated at first then if i close the attributes table and reopen the table they are not there...?
0 Kudos
T__WayneWhitley
Honored Contributor
Isn't that more than one question, lol?...It's okay - I will try to answer in the order of importance.

Let's start with what is failing - your further attempt to populate fields.  I didn't test but I think I see your problem - the cursor object 'hands out' row objects and you're trying to 'ask about' a field value the cursor hasn't been informed about yet...in other words, we gained access to the appropriate row for coding the 'Single Family Home' value in the FacltyType field via the read cursor row's (rowR) 'DWELL' value in the CPUC field (see this if statement in the orig code):
if rowR.CPUC == 'DWELL':
        rowW.FacltyType = 'Single Family Home'


We set the value and the cursor's not aware of it until we use 'updateRow', which of course comes later when done populating the current row.  Make sense?  So I suggest you group commands for assigning field vals when you know you have an appropriate handle on a specific row.  I'll explain more in a bit about how we know which rows are which using AddressID...

I'm assuming the verified field is a text field? (I don't remember)  That's probably the case since you're jamming other stuff in there - that's okay, just make sure the field is long enough to hold it all.  So if indeed that is the case, you need to string format time values...in other words, strftime - see this (small examples are there too):

http://docs.python.org/2.7/library/time.html#time.strftime

So you can format your time value how you like and append it to your other string value...then add that to the field.


That should answer your 'field population' questions.  Now about explaining in short what the code's doing - I'll quickly answer without giving details at this time about what I meant about loading into a dictionary, then back out to your feature class.  As you can see with our use of cursors we're editing 'dynamically' at the same time we're reading the other table.  With a dictionary structure, you'd likely recognize some performance gains -- the dictionary you can think of as a 'virtual data structure' or maybe an 'in-memory' device ordered in a way you can read whole tables in, likely organize it and edit more efficiently, then write in the rows back out in various ways (depending on your need) in 'bigger chunks' rather than the piecemeal fashion we used.  Not that there's anything wrong with our method - just expect it to be somewhat slower.  (Of course that depends on how much data you're dealing with.)  Let's take a break on that for now, it's just more important at the moment to understand the difference.

For now, it's probably enough to understand what's going on with these cursors and get it right -- worry about optimizing in different ways when you deem that it's necessary (and we can call in bigger guns, lol!...I know a few, some on this forum, fortunately).  I'll try to be brief, then if you have further questions, proceed from there...I will repaste the original code with my comments in a few minutes, maybe make a separate post since this one's a little long.  If you have any specific questions meanwhile, just post them.
0 Kudos
T__WayneWhitley
Honored Contributor
Here you go - hope this helps explain further.
import arcpy

# Need overwrite turned on to enable temp layer
# 'Points_joined' to be reused on successive executions.
arcpy.env.overwriteOutput = True

# Spatial Join (Analysis) [webhelp 10.0]
# http://help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#/Spatial_Join/00080000000q000000/

# fcTarget (Points_1) is to be written to...
fcTarget = 'Points_1'

# fcJoin (Points_2) contains the critical new info to assimilate...
fcJoin = 'Points_2'

# fcOutput (Points_joined) is simply the Spatial Join tool output class,
# the Target and Join classes 'melded' together...
fcOutput = 'Points_joined'

# Critical webhelp doc:
# Spatial Join (Analysis) [webhelp 10.0]
# http://help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#/Spatial_Join/00080000000q000000/

# I allowed fcOutput to be 'hard-written' to my default gdb...do as you like,
# setting your workspace accordingly.
# 'JOIN_ONE_TO_ONE' assures one record joined
# if a 'spatial match' is found (intersect is the default spatial search method).
# 'KEEP_COMMON' accommodates your selection set, dropping all records from Target not 'joined'.
arcpy.SpatialJoin_analysis(fcTarget, fcJoin, fcOutput, 'JOIN_ONE_TO_ONE', 'KEEP_COMMON')

# You wish to populate based on the spatial join your original Target.
# Instead of adding a join, I elected to use 2 sorted cursors to search for
# matching AddressID values (provided they're indeed unique) - this can actually
# be based on any unique ID in the Target class that the Output class inherited
# from the previous spatial join process.  (Again, you can adapt this to 10.1 da cursors.)
# 'AddressID A' is the sort parameter; 'A' means ascending order.
# To keep track of what is read, what is write, I refer to:
# curR as the 'Read'cursor (R - read)
# curW as the 'Write' cursor (W - write)
curR = arcpy.SearchCursor(fcOutput, '', '', '', 'AddressID A')
curW = arcpy.UpdateCursor(fcTarget, '', '', '', 'AddressID A')

# init rowW and rowR; same with the row obj, referring similarly:
# rowW belongs to the write cursor; rowR belongs to the read cursor.
rowW = curW.next()
rowR = curR.next()

# while loop; loops while there are read rows being handed out by read cursor.
while rowR:
    # Current address to find a match for is from the 1st read row.
    # This var is 'reinitialized' every loop on read row.
    currentAddress = rowR.AddressID
    
    # This checks for the write row match on the current address.
    # No match means the next row is fetched from the write cursor.
    while rowW.AddressID != currentAddress:
        rowW = curW.next()

    # When the correct write row is found, i.e., the currentAddress is matched,
    # the CPUC value is read from the read row and conditionally set:
    # 'DWELL' in the read row means FacltyType is set to 'HOME'
    # Otherwise (else), FacltyType is set to 'MobileHome'.
    if rowR.CPUC == 'DWELL':
        rowW.FacltyType = 'HOME'
    else:
        rowW.FacltyType = 'MobileHome'

    # The write row obj must be committed to the write cursor obj.
    # If this step is forgotten, the table is not updated.
    curW.updateRow(rowW)

    # The next read row is fetched to start again back at 'while rowR'.
    rowR = curR.next()

# cleanup, deleting obj refs; forgot about del the curW and curR objs.
# This may be necessary to remove locks on your data...
del rowW, rowR
0 Kudos
TonyAlmeida
MVP Regular Contributor
Wayne thank you very much for the explanation.

my apologies i am a dimwit.
I understand what you are saying but i am having a hard time putting the code together for group commands as you stated.
I also don't know how to put the strftime in with the other information i need in the field. 'Yes, GRM, TA, '%m%d%Y' Does not work i get parsing error

I am totally a visual learner, i have to be able to see the code with my data in order to understand.
I really appreciate all your help and the explanation on the code you posted.
0 Kudos