Transfer attributes without spatial join

4867
11
06-10-2014 06:21 PM
EdSaunders
Occasional Contributor
Hi all,

Apologies if this has been answered elsewhere.  I have some points that are located within polygons and need to transfer attributes from the polys to the points.  Is there a way to do this without using a spatial join?  Spatial joins create a new layer which is not preferable.  The best I can think of is using select by location somehow.  Anyone got any better ideas?

Thanks heaps,
Ed
Tags (2)
0 Kudos
11 Replies
MattEiben
Occasional Contributor
You could try selecting the points, adding a field for that attribute you want added, and using an update cursor to populate them.  If you have a more specific example, we may be able to help out with the code.
0 Kudos
Zeke
by
Regular Contributor III
Feature to Point would give you a point for each polygon with the polygon attributes, but I take it that's not what you're looking for...
0 Kudos
BrianLord1
Occasional Contributor
Here is what I might try, although I am new to python so it might not be the best way... 

In python...
1. Add fields for each attribute you want to transfer to the point feature class.  Lets say you want to transfer two attributes from the polygon you would add those two fields to the point feature class.

2. Loop through the polygons and select each polygon

3. Save the two attributes you want to transfer to variables so they can be used later

4. Do a select by location to select all the points within the selected polygon

5. Use an update cursor to loop through the selected points and populate the two fields with the variables you created earlier

I think that would get you your desired outcome and should be fairly simple to put the code together since it is all straight out of esri's help pages.

P.S. - I have not tried this so I am not positive it will work jut thinking out loud here.

Thanks,
Mark
0 Kudos
AdamCox1
Occasional Contributor II

In python...
1. Add fields for each attribute you want to transfer to the point feature class.  Lets say you want to transfer two attributes from the polygon you would add those two fields to the point feature class.

2. Loop through the polygons and select each polygon

3. Save the two attributes you want to transfer to variables so they can be used later

4. Do a select by location to select all the points within the selected polygon

5. Use an update cursor to loop through the selected points and populate the two fields with the variables you created earlier


Yes, this is basically what I have done many times to achieve this operation.  Here's a little pseudo code:

#make feature layer for point shapefile
for row in arcpy.SearchCursor(polygon shapefile):
    #get FID and use it to construct a query that would only select the row (polygon) that you're looking at
    #make feature layer using that query so now you have a one-feature feature layer
    value = row.getValue("name of field you want a value from")
    #use this feature layer to make a select by location operation on the point layer you made above
    updaterows = arcpy.UpdateCursor(point feature layer) #the cursor should only contain the selected feature(s)
    for rw in updaterows:
        rw.setValue("field name you want to put the value in",value)
        updaterows.UpdateRow(rw)
0 Kudos
EdSaunders
Occasional Contributor
Thanks all - very helpful comments.  I'll try it using a combo of Adam's and Mark's responses - select by location and then iterate through selected feature and transfer attributes.  Might be quite time consuming but preferable to creating a new layer.  Feature to point would work but again I don't want to create an extra layer.

Thanks again, much appreciated.
0 Kudos
RichardFairhurst
MVP Honored Contributor
Might be quite time consuming but preferable to creating a new layer.


It will take 10 to 100 times longer than doing a spatial join.  If you sit and wait on this operation it will not be preferable.  Your time is the most costly and valuable thing and code that performs this badly will be a waste of effort.

Create an in-memory layer if you are that opposed to creating a permanent layer.  If you use a cursor to create a dictionary from the in-memory spatial join and do the transfer with another da cursor rather than a standard join you will beat the code proposed by 100 times.  I have seen code that takes 2 hours with the select by attribute/location approach take only 20 seconds after doing the approach I am suggesting.  The most time consuming thing about my proposed code is adding the fields to the original layer.  I will never, ever, ever use embedded cursor loops with select by anything inside the loop again now that I have this code.  It even beats a standard join and field calculator operation by more than 10 times.

Look at this post for the basic dictionary transfer code.

Almost all Geoprocessing tools are designed around creating new layers, not modifying existing layers. For the most part they are much more optimized than writing code that emulates a manual desktop process in a loop.
0 Kudos
AdamCox1
Occasional Contributor II
Look at this post for the basic dictionary transfer code.


Dictionaries are absolutely wonderful, but they rely on being able to link the correct two rows.   From looking the code in the link, it doesn't seem to have anything to do with location, it's all based on the OBJECTID/FID field.  You are assuming that OBJECTID 1 in the poly feature class is the polygon that overlaps OBJECTID 1 in the point feature class, which doesn't seem like a fair assumption at all.  That is a nice piece of code though, and you could also simplify it a little bit:

if ObjectIDVal in valueDict:
    for n in range (0,len(updateFieldsList)):
        updateRow[n+1] = valueDict[ObjectIDVal]
    updateRows.updateRow(updateRow)
0 Kudos
RichardFairhurst
MVP Honored Contributor
Dictionaries are absolutely wonderful, but they rely on being able to link the correct two rows.   From looking the code in the link, it doesn't seem to have anything to do with location, it's all based on the OBJECTID/FID field.  You are assuming that OBJECTID 1 in the poly feature class is the polygon that overlaps OBJECTID 1 in the point feature class, which doesn't seem like a fair assumption at all.  That is a nice piece of code though, and you could also simplify it a little bit:

if ObjectIDVal in valueDict:
    for n in range (0,len(updateFieldsList)):
        updateRow[n+1] = valueDict[ObjectIDVal]
    updateRows.updateRow(updateRow)


I am saying that you should use the Spatial Join tool and do not do embedded loops.  Embedded loops with the Search tools are the worst possible design for doing this task as far as I am concerned and will cost at least 10 times the processing overhead.  You won't come anywhere close to the Spatial Join performance with that approach.

Create a Spatial Join to an in-memory output and then use the TargetFID of that output to join back to the polygon ObjectID.  TargetFID 1 of the Spatial Join will match ObjectID 1 of the polygons.  The code does not create the spatial association, but it does the attribute transfer from the separate Spatial Join output in blazing speed through those join field associations.  Since the in-memory output is merely temporary, this is similar to using the scratch workspace of the geoprocessing tools, which is part of how ESRI optimizes their tools.

The code you proposed would work for the example.  The actual code I normally use does not necessarily align the source field and target field numbering in the simple pattern of the example and typically includes parsing, concatenation or mathematical operations that are specific to each field.  The code is flexible and can be adapted to handle any pattern of transfer operation you can imagine.
0 Kudos
AdamCox1
Occasional Contributor II
Create a Spatial Join to an in-memory output and then use the TargetFID of that output to join back to the polygon ObjectID.  TargetFID 1 of the Spatial Join will match ObjectID 1 of the polygons.


Ok cool, that sounds like a good method.  I like it.  I've mostly dealt with situations where I only want to transfer values if certain spatial and attribute criteria match (not what we're talking about here), which didn't seem like a good use for spatial joins.  However, now I'm thinking that there may be a good way to incorporate a method similar to this.
0 Kudos