Interesting results with spatial Join using INTERSECT

07-15-2016 02:55 PM
Regular Contributor

I am not sure what's is going on here but if use arcpy.SpatialJoin_analysis and with INTERSECT as the match_option with two layers i get something that doesn't look right. The layer Fzon3 has zoning COM, IND or RES i do the spatial join to Taxparcels but the Spatial Join with INTERSECT doesn't look right, see picture. After the script is ran and i add the layers to arcmap and i query the FZon2 layer for "COM"(COM  is the red) you can see that the spatial join didn't spatial join correctly because there should be more parcels with "COM". Any help would be great thanks!

# Import arcpy module
import sys, arcpy, traceback, os
from arcpy import env
import win32com.client
from os import path as p
from datetime import datetime as d
startTime =

# Local variables:
arcpy.env.overwriteOutput = True
arcpy.env.workspace = "C:\TEMP\Test.gdb"
arcpy.env.qualifiedFieldNames = False

if arcpy.Exists("C:\TEMP\Test.gdb\FZon2"):

Tax = r"C:\TEMP\Test|Taxparcels"
Tax1 = "C:\TEMP\Test.gdb"
arcpy.FeatureClassToGeodatabase_conversion(Tax, Tax1)

Padm1 = "C:\TEMP\Test.gdb"
Taxparcels = "C:\TEMP\Test.gdb\Taxparcels"

# Process: Make Feature Layer
arcpy.MakeFeatureLayer_management(Taxparcels, "In_memory\TaxPar")
TaxPar1 = "In_memory\TaxPar"
arcpy.MakeFeatureLayer_management("In_memory\TaxPar", TaxPar1)


def Layers0(TaxPar1, FZon3):
    FieldMapString = "" \
                + """DXF_TEXT "DXF_TEXT" true true false 18 Text 0 0 ,First,#,""" + TaxPar1 + """,DXF_TEXT,-1,-1;"""\
                + """ACCOUNT "ACCOUNT" true true false 11 Text 0 0 ,First,#,""" + TaxPar1 + """,ACCOUNT,-1,-1;"""\
                + """ACRES "ACRES" true true false 4 Double 10 3 ,First,#,""" + TaxPar1 + """,ACRES,-1,-1;"""\
                + """Instrument "Instrument" true true false 10 Text 0 0 ,First,#,""" + TaxPar1 + """,Instrument,-1,-1;"""\
                + """SiteAddress "SiteAddress" true true false 106 Text 0 0 ,First,#,""" + TaxPar1 + """,SiteAddress,-1,-1;"""\
                + """SiteCity "SiteCity" true true false 32 Text 0 0 ,First,#,""" + TaxPar1 + """,SiteCity,-1,-1;"""\
                + """SiteZip "SiteZip" true true false 10 Text 0 0 ,First,#,""" + TaxPar1 + """,SiteZip,-1,-1;"""\
                + """SubName "SubName" true true false 20 Text 0 0 ,First,#,""" + TaxPar1 + """,SubName,-1,-1;"""\
                + """OwnerName "OwnerName" true true false 100 Text 0 0 ,First,#,""" + TaxPar1 + """,OwnerName,-1,-1;"""\
                + """OwnerSec "OwnerSec" true true false 64 Text 0 0 ,First,#,""" + TaxPar1 + """,OwnerSec,-1,-1;"""\
                + """Address "Address" true true false 64 Text 0 0 ,First,#,""" + TaxPar1 + """,Address,-1,-1;"""\
                + """City "City" true true false 32 Text 0 0 ,First,#,""" + TaxPar1 + """,City,-1,-1;"""\
                + """State "State" true true false 4 Text 0 0 ,First,#,""" + TaxPar1 + """,State,-1,-1;"""\
                + """ZipCode "ZipCode" true true false 10 Text 0 0 ,First,#,""" + TaxPar1 + """,ZipCode,-1,-1;"""\
                + """Legal "Legal" true true false 254 Text 0 0 ,First,#,""" + TaxPar1 + """,Legal,-1,-1;"""\
                + """Sec "Sec" true true false 2 Text 0 0 ,First,#,""" + TaxPar1 + """,Sec,-1,-1;"""\
                + """Twp "Twp" true true false 2 Text 0 0 ,First,#,""" + TaxPar1 + """,Twp,-1,-1;"""\
                + """Rng "Rng" true true false 2 Text 0 0 ,First,#,""" + TaxPar1 + """,Rng,-1,-1;"""\
                + """Quarter "Quarter" true true false 2 Text 0 0 ,First,#,""" + TaxPar1 + """,Quarter,-1,-1;"""\
                + """TaxCode "TaxCode" true true false 7 Text 0 0 ,First,#,""" + TaxPar1 + """,TaxCode,-1,-1;"""\
                + """FireDist "FireDist" true false false 64 Text 0 0 ,First,#,""" + TaxPar1 + """,FireDist,-1,-1;"""\
                + """HighwayDist "HighwayDist" true false false 74 Text 0 0 ,First,#,""" + TaxPar1 + """,HighwayDist,-1,-1;"""\
                + """SchoolDist "SchoolDist" true false false 64 Text 0 0 ,First,#,""" + TaxPar1 + """,SchoolDist,-1,-1;"""\
                + """Acres1 "Acres1" true false false 4 Double 10 3 ,First,#,"""+ TaxPar1 + """,Acres1,-1,-1;"""\
                + """SITE_ADDR "SITE_ADDR" true false false 50 Text 0 0 ,First,#,""" + TaxPar1 + """,SITE_ADDR,-1,-1;"""\
                + """FZONE_CODE "FZONE_CODE" true false false 10 Text 0 0 ,First,#,"""+ FZon3 + """,ZONE_CODE,-1,-1;"""\
    fieldmappings = arcpy.FieldMappings()
    return fieldmappings

arcpy.SpatialJoin_analysis(TaxPar1, FZon3, "C:\TEMP\Test.gdb\FZon2", "JOIN_ONE_TO_ONE", "KEEP_ALL",Layers0(TaxPar1, FZon3), "INTERSECT")

    print '(Elapsed time: ' + str( - 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
0 Kudos
5 Replies
MVP Esteemed Contributor

and what if you joined one to many? is that what you expected?

0 Kudos
MVP Honored Contributor

The tool behaved normally.  All of the parcels not selected that have red on them took the FZONE_CODE value from the FIRST feature encountered and ignored all other values (in the cases not selected the COM value was not on the first feature encountered and was ignored).  You either need to use the One to Many option as Dan mentioned or the One to One option using the JOIN field merge rule on the FZONE_CODE, increasing length for the Zone field and setting a delimiter.  For the later the line would change to (changes in bold):

+ """FZONE_CODE "FZONE_CODE" true false false 60 Text 0 0 ,Join,#,", ""+ FZon3 + """,ZONE_CODE,-1,-1;"""\

The difference is that the One to Many option will create two or more copies of the entire parcel (completely overlapping each other) to match each zoning polygon intersected, and the other will create a single copy of each parcel with a list of multiple zones in the FZONE_CODE field (i.e., "COM, IND").  If you used the field Join merge rule option you would have to select all parcels with "COM" zoning using a LIKE expression with wildcards, not an equals expression (i.e., FZONE_CODE LIKE '%COM%' for a FGDB).  Either way, something will be multiplied to capture all parcel and zoning combinations when more than one parcel and one zone intersect.

If you want your parcels cut up into separate portions for each zoning polygon that do not overlap and that have an individual code in the FZONE_CODE field then you need to use the INTERSECT tool.  However, the INTERSECT tool option requires your topology in both the parcel and zoning feature class to be very clean (no overlaps or gaps) and have very clean edge matches to avoid generating huge numbers of sliver polygons.  Spatial Join can overcome bad topology by using a negative tolerance on the features to avoid or at least reduce the effects of slivers at the polygon edges.  A negative tolerance also is needed to avoid taking on attributes of polygons that only share an edge if you use the Spatial Join method, since otherwise shared edges are considered an intersecting feature.

Regular Contributor

Thanks for the explanation I would like to join the zones if there is more than one. I made the following changes at line 61 + """FZONE_CODE "FZONE_CODE" true false false 60 Text 0 0 ,Join,#,", ""+ FZon3 + """,ZONE_CODE,-1,-1;"""\

I re- ran the code and i got interesting results again that i don't understand. referring to the picture i have attached, (A)Parcel has attributes of Ind (blue) & Com (red) "FZONE IndCom", this iss correct because Com(red) & Ind(blue) both over lap this parcel.

Now for the other two (C) Parcel has attributes of Ind (blue) & Res (yellow)  FZONE_CODE IndRes BUT  it should only have Ind (blue) and the same for (B). Now i think this is where the "INTERSECT" variable plays into it? what i am see is if the boundary line of any FZon3 touches any boundary line of the parcels it will get the attributes even thought it shouldn't. if this is the case what is the best way to spatial join the Fzon3 and taxparcels? The idea is that the spatial join will join like (A), if there is more than one zone overlays a parcel it will populate the FZONE_CODE "IndRes" and only populate the parcel with one zone if only one zone overlays the parcel.

I have checked the geometry and topology x10 for both layers and all is good, so i don't think the issue is why overlaps.


0 Kudos
MVP Honored Contributor

The results are again correct, since the Spatial Join intersect behavior does consider touching boundaries to be intersecting, since it has to in order to be able to Spatially Join lines or points to polygon features.  If it excluded touching of shared boundaries then it could not deal with Spatially Joining lines and point features, only polygon features, but since it does not have the options to specify the output geometry like the Intersect tool does, it has to treat line and point intersections on shared boundaries as valid intersections.

So while it is understandable that you do not want these results when both inputs are polygons, I have already explained that the result you got are consistent with the Spatial Join tool behavior for Intersect.  Since the Spatial Join tool was never built to require that any of the inputs had to be polygons, let alone both of them, it was by design made to always treat the intersect rules of all shapes with all shapes (polygon, lines and points) as valid intersections.

So this is what I meant in the last sentence of my post about needing to use a negative buffer even when the shapes are topologically correct every time you Spatially Join two polygon layers to avoid getting attributes where polygon edges touch.  However, do not use the negative buffer option of the Spatial Join tool.  The Buffer tool runs much faster than using the negative buffer option of the Spatial Join tool, so you should include the Buffer tool in your script before doing the Spatial Join so that one of your feature classes has a negative buffer applied first.  Then you would Spatially Join one of your original feature classes to the new feature class you have created with negative buffers.  Since your topology is good you can make the negative buffer very small as long as it is at least more than double your tolerance setting.  If one of the feature classes has fewer shapes than the other it should be the feature class that you apply the negative buffer to, since you can apply the negative buffer to either of the polygons sets and the only consideration is which will create the negative buffers the fastest.

I have a similar requirement for Building Permits and Tract boundaries, and I always have to apply a -10 foot buffer using the buffer tool because my topology is not that good, but even if it was perfect I would have to use a negative buffer to avoid counting all permits that aligned perfectly to the boundaries or corners of any two or more tracts that shared a boundary or a corner.  I do the negative buffer for the tracts, since there are far fewer tracts than there are permits.  I then join the count result back to the original tract shapes on a unique ID field and transfer the count, so that the shapes are not reduced in the final output.

Regular Contributor

Ok making sense .

Would you mind posting a copy of the script? I would like to see how you applied the buffer.


0 Kudos