What process is necessary to select each row of a feature class and then perform a select by location using the individually selected rows?

5881
19
12-06-2017 07:12 AM
Travispreston
Emerging Contributor

I am trying to use cursor and for loop to select each row of a feature class, then select features from another feature class using select by location and the selection from the former feature class. Then add up the shape areas of the selection and out put them into a field in the first feature class. 

1. Step through each row of a feature class selecting them one at a time (Parcels)

   -I tried using a cursor for this step

2. perform a select by location using the selection from the first step and another feature class (Buildings)

   -I cannot figure out how to get the row to be used in the select by location

3. Sum the "shape area" of the selection from step 2

   - I don't know the command to perform this step

4. Output the sum into a field in the first feature class (Parcels)

   - I don't know the command to perform this step

5. divide the shape area of Buildings by the Parcel shape area to get a % of development

   - I believe I can write this command but I have not been able to get this far in the code

I have not been able to find a similar python process on stack exchange. I have made several attempts at writing the code in what I feel like is a logical order but have not had any luck. Does anyone possibly have a similar code or can anyone provide me with a list of python processes to use?

Thank you

0 Kudos
19 Replies
Travispreston
Emerging Contributor

It worked!!! Now I need to add those values to their corresponding parcel. Would I use and input cursor of some kind for that?

0 Kudos
Travispreston
Emerging Contributor

I tried adding an update cursor to the end of the code to add the areasum

to the "Bldg_Area" field of the parcel layer. It didn't work.

  1. Import the Arcpy Module

import arcinfo

import arcpy

from arcpy import env

  1. Set up the working Environment

env.workspace =

r"D:\Travis\Personal\Geoff\Nantucket\Zoning\Backlots\Backlots.gdb"

env.overwriteOutput = True

#Variables

Parcels ="R40_80000sqft"

Structures = "Nantucket_Structres"

arcpy.MakeFeatureLayer_management(parcels,"Parcels_lyr")

arcpy.MakeFeatureLayer_management(Structures,"Struc_Lyr")

Struc_Lyr = "Struc_Lyr"

Parc_Lyr = "Parcels_Lyr"

#Add a field to parcels to be populated with building area

arcpy.AddField_management("R40_80000sqft","Bldg_Area","DOUBLE")

with arcpy.da.SearchCursor(Parc_Lyr, ) as parcelcursor:

for parcelrow in parcelcursor:

#get the geometry to use in the spatial selection

geom = parcelrow[0]

#select feature from the other layer using the geom variable

arcpy.SelectLayerByLocation_management(Struc_Lyr,

"HAVE_THEIR_CENTER_IN", geom, "", "NEW_SELECTION")

#get the area of the selected features and sum

areasum = 0

with arcpy.da.SearchCursor(Struc_Lyr,['Shape@AREA']) as newcursor:

for newrow in newcursor:

areasum = areasum + newrow[0]

print areasum

with arcpy.da.UpdateCursor(Parcels, ) as parcelcursor:

for parcelrow in parcelcursor:

row[0]=areasum

0 Kudos
JamesCrandall
MVP Frequent Contributor

Not sure if having update cursor within search cursors is a good idea.  I think you may want to research on populating some kind of list or dictionary with the area totals and their OID's, then use that dictionary in a new update cursor.  Sorry I haven't gone down that road.

0 Kudos
JakeSkinner
Esri Esteemed Contributor

Instead of using a SearchCursor, you can use an UpdateCursor for the parcel layer.  You can then update the additional fields.  In the below example I added to fields to the parcel feature class called 'BuildingArea' and 'FreeArea'.

with arcpy.da.UpdateCursor(Parc_Lyr, ['Shape@', 'BuildingArea', 'FreeArea']) as parcelcursor:

    for parcelrow in parcelcursor:

        #get the geometry to use in the spatial selection
        geom = parcelrow[0]

        #select feature from the other layer using the geom variable
        arcpy.SelectLayerByLocation_management(Struc_Lyr, "HAVE_THEIR_CENTER_IN", geom, "", "NEW_SELECTION")

        #get the area of the selected features and sum
        areasum = 0
        with arcpy.da.SearchCursor(Struc_Lyr,['Shape@AREA']) as newcursor:
            for newrow in newcursor:
                areasum = areasum + newrow[0]
        del newcursor

        parcelrow[1] = areasum

        if areasum != 0:
            parcelrow[2] = geom.area / areasum
        else:
            parcelrow[2] = 0

        parcelcursor.updateRow(parcelrow)

del parcelcursor

Also, here is a link that discusses how to post code in GeoNET:

https://community.esri.com/docs/DOC-8691-posting-code-with-syntax-highlighting-on-geonet 

Travispreston
Emerging Contributor

Thank you for the additional help. I added the field to the table that you selected and added the additional code. I am getting an error at line 60. Any idea why I would be getting the error?

# Import the Arcpy Module
import arcinfo
import arcpy
from arcpy import env

# Set up the working Environment

env.workspace = r"D:\Travis\Personal\Geoff\Nantucket\Zoning\Backlots\Backlots.gdb"
env.overwriteOutput = True

#Variables
Parcels ="R40_80000sqft"
Structures = "Nantucket_Structres"

arcpy.MakeFeatureLayer_management(parcels,"Parcels_lyr")
arcpy.MakeFeatureLayer_management(Structures,"Struc_Lyr")

Struc_Lyr = "Struc_Lyr"
Parc_Lyr = "Parcels_Lyr"

#Add a field to parcels to be populated with building area
arcpy.AddField_management(Parcels,"Bldg_Area","DOUBLE")
arcpy.AddField_management(Parcels,"Free_Area","DOUBLE")

with arcpy.da.UpdateCursor(Parc_Lyr, ['Shape@']) as parcelcursor:
    for parcelrow in parcelcursor:

     #get the geometry to use in the spatial selection
     geom = parcelrow[0]

     #select feature from the other layer using the geom variable
     arcpy.SelectLayerByLocation_management(Struc_Lyr, "HAVE_THEIR_CENTER_IN", geom, "", "NEW_SELECTION")

     #get the area of the selected features and sum
     areasum = 0
     with arcpy.da.SearchCursor(Struc_Lyr,['Shape@AREA']) as newcursor:
        for newrow in newcursor:
            areasum = areasum + newrow[0]

     print areasum

     del newcursor

     parcelrow[1]= areasum

     if areasum != 0:
        parcelrow[2] = geom.area / areasum

     else:
        parcelrow[2] = 0

     parcelcursor.updateRow(parcelrow)

del parcelcursor
0 Kudos
JakeSkinner
Esri Esteemed Contributor

You need to add your additional fields to line 25:

with arcpy.da.UpdateCursor(Parc_Lyr, ['Shape@']) as parcelcursor:

Take a look at what I have:

with arcpy.da.UpdateCursor(Parc_Lyr, ['Shape@', 'BuildingArea', 'FreeArea']) as parcelcursor:
0 Kudos
Travispreston
Emerging Contributor

Thank you! The Script is working exactly as I had hoped. I am now trying to apply the same script to figuring out frontage. I want the script to select the lines I created from the parcels that fall within the buffer of the road, sum the total length of frontage lines and out put the sum to the frontage field. The script runs but nothing is put into the frontage field. Any suggestions?

# Import the Arcpy Module
import arcinfo
import arcpy
from arcpy import env

# Set up the working Environment

env.workspace = r"D:\Travis\Personal\Geoff\Nantucket\Zoning\Backlots\Backlots.gdb"
env.overwriteOutput = True

#Variables
Parcels = "R40_80000sqft"
Street_Center = "ROAD_CL_2017_12"

arcpy.MakeFeatureLayer_management(Parcels,"Parcels_lyr")
arcpy.MakeFeatureLayer_management(Street_Center,"Street_Lyr")

Street_Lyr = "Street_Lyr"
Parcels_lyr = "Parcels_Lyr"

#Add a field to parcels to be populated with building area
arcpy.AddField_management(Parcels,"Frontage","DOUBLE")

arcpy.Buffer_analysis(Street_Lyr,"Street_Buff","40 Feet","FULL","ROUND","ALL")

Street_Buff = "Street_Buff"

arcpy.PolygonToLine_management(Parcels_lyr,"R40_PolyToLine")

R40_Line = "R40_PolyToLine"

arcpy.MakeFeatureLayer_management(R40_Line,"R40_Line_Lyr")

R40_Line_Lyr = "R40_Line_Lyr"

#Create a cursor to select all parcel lines that fall within the street buffer
with arcpy.da.UpdateCursor(Parcels_lyr, ['Shape@','Frontage']) as linecursor:
    for linerow in linecursor:

     #get the geometry to use in the spatial selection
     geom = linerow[0]

     #select feature from the other layer using the geom variable
     arcpy.SelectLayerByLocation_management(R40_Line_Lyr, "COMPLETELY_WITHIN", Street_Buff, "", "NEW_SELECTION")

     #get the Length of the selected features and sum
     lengthsum = 0
     with arcpy.da.SearchCursor(Street_Lyr,['SHAPE@LENGTH']) as newcursor:
        for newrow in newcursor:
            lengthsum = lengthsum + newrow[0]

     print lengthsum

     del newcursor

     parcelrow[1]= lengthsum

     if areasum != 0:
        parcelrow[2] = geom.length / lengthsum

     else:
        parcelrow[2] = 0

     linecursor.updateRow(linerow)

del linecursor
0 Kudos
Travispreston
Emerging Contributor

I realized I have another problem. I need it to use the other attribute to determine which parcel the line segments belong to otherwise it will just sum every line segment that falls within the buffer area. 

0 Kudos
MarianneRohrbach
Occasional Contributor

I would suggest to use the Geoprocessing Tool "Tabulate Intersect", which is very fast also on huge data:

arcpy.TabulateIntersection_analysis("Parcel", "OBJECTID", "Building", r"in_memory\TabIntSum")

This results in a Table with the fields

- OBJECTID_1 = Parcel-Objectid

- AREA = Sum of overlapped Building area

- PERCENTAGE = Overlapping Area Percentage

You may then use "MakeFeatureLayer" for the parcels, "Add Join" to Join the resulting table by objectid/objectid_1 and "Calculate Field" to populate the desired field. For clean code you should then use "Remove Join" or "Delete" for the Feature layer.

If you have overlapping parcels, then you would have to add a "Sort" for the resulting table on "OBJECTID_1 and AREA (Descending)" followed by a summary statistics on the OBJECTID_1 to select first Areas per parcel.

Travispreston
Emerging Contributor

Thank you! I am trying to use your suggestion for another code. The purpose is to determine the frontage of parcels along a road system. I have created a buffer to use in select by location. I want to select all lines that completely fall within the buffer and then calculate the sum of lines that correspond to each parcel and then populate a field called "Frontage". I have used most of the code from this thread to try and accomplish the frontage calculation. I dont think I put the TabulateIntersection_analysis in the correct place and I am not sure that everything from line 72 down is necessary. Any suggestions?

# Import the Arcpy Module
import arcinfo
import arcpy
from arcpy import env

# Set up the working Environment

env.workspace = r"D:\Travis\Personal\Geoff\Nantucket\Zoning\Backlots\Backlots.gdb"
env.overwriteOutput = True

#Variables
Parcels = "R40_80000sqft"
Street_Center = "ROAD_CL_2017_12"

arcpy.MakeFeatureLayer_management(Parcels,"Parcels_lyr")
arcpy.MakeFeatureLayer_management(Street_Center,"Street_Lyr")

Street_Lyr = "Street_Lyr"
Parcels_lyr = "Parcels_Lyr"

#Add a field to parcels to be populated with building area
arcpy.AddField_management(Parcels,"Frontage","DOUBLE")

# Buffer Street Center Lines
arcpy.Buffer_analysis(Street_Lyr,"Street_Buff","40 Feet","FULL","ROUND","ALL")

Street_Buff = "Street_Buff"

#Convert parcel layer polygons to lines
arcpy.PolygonToLine_management(Parcels_lyr,"R40_PolyToLine")

R40_Line = "R40_PolyToLine"

###Convert parcel lines to a layer for use in select by location
##arcpy.MakeFeatureLayer_management(R40_Line,"R40_Line_Lyr")
##
##R40_Line_Lyr = "R40_Line_Lyr"

#Create a cursor to select all parcel lines that fall within the street buffer
with arcpy.da.UpdateCursor(Parcels_lyr, ['Shape@','Frontage','OID@']) as linecursor:
    for linerow in linecursor:

     #get the geometry to use in the spatial selection
     geom = linerow[0]
     oid = str(linerow[2])

     R40_Line = "R40_PolyToLine"

     arcpy.TabulateIntersection_analysis(R40_Line, "OBJECTID", Parcels_lyr, r"in_memory\TabIntSum")

     #Convert parcel lines to a layer for use in a select by location
     arcpy.MakeFeatureLayer_management(R40_Line,"R40_Line_Lyr","LEFT_FID = " + oid)

     arcpy.JoinField_management(r"in_memory\TabIntSum","OBJECTID_1",Parcels_lyr,"OBJECTID")

     #select parcel lines from the street buffer using the geom variable
     arcpy.SelectLayerByLocation_management("R40_Line_Lyr", "COMPLETELY_WITHIN", Street_Buff, "", "NEW_SELECTION")

     #get the Length of the selected features and sum
     lengthsum = 0
     with arcpy.da.SearchCursor("R40_Line_Lyr",['SHAPE@LENGTH']) as newcursor:
        for newrow in newcursor:
            lengthsum = lengthsum + newrow[0]

     print lengthsum

     del newcursor

     parcelrow[1]= lengthsum

     if lengthsum != 0:
        parcelrow[2] = lengthsum

     else:
        parcelrow[2] = 0

     linecursor.updateRow(linerow)

del linecursor
0 Kudos