randomly select polygons and stepwise omit overlapping polygons

1096
5
Jump to solution
04-24-2014 12:35 AM
EmilLarsson
New Contributor
hi there,

I have ~300 buffer polygons (see attached) and want to randomly select ~50 of them. when one polygon is selected, I want to omit all other polygons that overlap that selected polygon from the next step of selection.


I want to do this stepwise for all 50 polygons I select. in the end, none of my 50 selected polygons will overlap.

any ideas? I am new to python.

thanks

emil
Tags (2)
0 Kudos
1 Solution

Accepted Solutions
AdamCox1
Occasional Contributor II
Ok.  I was working on a reply assuming a single feature class, but I've modified it so this should be fine. What I have in mind uses values from the FID field, SelectLayerByLocation and the randrange() function from the random module. I'll write it as a standalone script, but it wouldn't be hard to create a custom tool from it.  I'll add a lot of comments so it should be clear what's going on.  I got a little carried away.
import random import arcpy  ##a couple of quick useful functions def MakeQuery(fid_list):     templist = ['"FID" = ' + str(fid) for fid in fid_list]     return " OR ".join(templist) def TakeOutTrash(dataset):     if arcpy.Exists(dataset):         arcpy.management.Delete(dataset)  ##first get the feature class, and make feature layer for later use shp = r"C:\CLI_GIS\Intermountain Region\GRTE\850491\New_Shapefile.shp" TakeOutTrash("fl1") flayer = arcpy.management.MakeFeatureLayer(shp,"fl1")  ##begin with list of all FIDs rows = arcpy.SearchCursor(shp) fids_to_pick_from = [row.getValue("FID") for row in rows] del rows  #make empty list to hold the fids you'll use for final selection use_fids = []  ##use while loop to do the iterative selection process while True:          ##get random index number for fid list which will point to a valid id     rand_index = random.randrange(0,len(fids_to_pick_from)+1)     rand_fid = fids_to_pick_from[rand_index]     use_fids.append(rand_fid)     fids_to_pick_from.remove(rand_fid)      ##make feature layer using fids picked up to this point     q = MakeQuery(use_fids)     TakeOutTrash("fl2")     fl = arcpy.management.MakeFeatureLayer(shp,"fl2",q)          ##selection by location to find which of the original features overlap with the polygons     ##in the use_fids list thus far, then remove them from the fids_to_pick_from list     arcpy.management.SelectLayerByLocation(flayer,"INTERSECT",fl,"","NEW_SELECTION")     if not arcpy.management.GetCount(flayer).getOutput(0) ==\        arcpy.management.GetCount(fl).getOutput(0):         rows = arcpy.SearchCursor(flayer)         all_overlapping = [row.getValue("FID") for row in rows]         del rows         bad_overlapping = [i for i in all_overlapping if not i in use_fids]         fids_to_pick_from = [f for f in fids_to_pick_from if not f in bad_overlapping]          ##break loop once 50 fids have been selected     if len(use_fids) == 50:         break  ##make final selection final_q = MakeQuery(use_fids) arcpy.management.SelectLayerByAttribute(flayer,"NEW_SELECTION",final_q) print str(len(use_fids)) + " polygons have been chosen" print final_q


I'm finding that running this in the python console takes forever.  It's much faster to create a little stand alone script from it, and just copy and paste the printed query at the end into the select by attribute dialogue box in ArcMap.  At any rate, this can serve as the core of the script.

View solution in original post

0 Kudos
5 Replies
AdamCox1
Occasional Contributor II
Hi emil,

How are these polygons stored, in one feature class, in a variety of feature classes, shapefiles, etc.?  This will dramatically change the code, but it is certainly doable.
0 Kudos
EmilLarsson
New Contributor
hi adam,

they are all stored in one single shapefile.

thanks
0 Kudos
AdamCox1
Occasional Contributor II
Ok.  I was working on a reply assuming a single feature class, but I've modified it so this should be fine. What I have in mind uses values from the FID field, SelectLayerByLocation and the randrange() function from the random module. I'll write it as a standalone script, but it wouldn't be hard to create a custom tool from it.  I'll add a lot of comments so it should be clear what's going on.  I got a little carried away.
import random import arcpy  ##a couple of quick useful functions def MakeQuery(fid_list):     templist = ['"FID" = ' + str(fid) for fid in fid_list]     return " OR ".join(templist) def TakeOutTrash(dataset):     if arcpy.Exists(dataset):         arcpy.management.Delete(dataset)  ##first get the feature class, and make feature layer for later use shp = r"C:\CLI_GIS\Intermountain Region\GRTE\850491\New_Shapefile.shp" TakeOutTrash("fl1") flayer = arcpy.management.MakeFeatureLayer(shp,"fl1")  ##begin with list of all FIDs rows = arcpy.SearchCursor(shp) fids_to_pick_from = [row.getValue("FID") for row in rows] del rows  #make empty list to hold the fids you'll use for final selection use_fids = []  ##use while loop to do the iterative selection process while True:          ##get random index number for fid list which will point to a valid id     rand_index = random.randrange(0,len(fids_to_pick_from)+1)     rand_fid = fids_to_pick_from[rand_index]     use_fids.append(rand_fid)     fids_to_pick_from.remove(rand_fid)      ##make feature layer using fids picked up to this point     q = MakeQuery(use_fids)     TakeOutTrash("fl2")     fl = arcpy.management.MakeFeatureLayer(shp,"fl2",q)          ##selection by location to find which of the original features overlap with the polygons     ##in the use_fids list thus far, then remove them from the fids_to_pick_from list     arcpy.management.SelectLayerByLocation(flayer,"INTERSECT",fl,"","NEW_SELECTION")     if not arcpy.management.GetCount(flayer).getOutput(0) ==\        arcpy.management.GetCount(fl).getOutput(0):         rows = arcpy.SearchCursor(flayer)         all_overlapping = [row.getValue("FID") for row in rows]         del rows         bad_overlapping = [i for i in all_overlapping if not i in use_fids]         fids_to_pick_from = [f for f in fids_to_pick_from if not f in bad_overlapping]          ##break loop once 50 fids have been selected     if len(use_fids) == 50:         break  ##make final selection final_q = MakeQuery(use_fids) arcpy.management.SelectLayerByAttribute(flayer,"NEW_SELECTION",final_q) print str(len(use_fids)) + " polygons have been chosen" print final_q


I'm finding that running this in the python console takes forever.  It's much faster to create a little stand alone script from it, and just copy and paste the printed query at the end into the select by attribute dialogue box in ArcMap.  At any rate, this can serve as the core of the script.
0 Kudos
EmilLarsson
New Contributor
hi adam,

this script worked like magic! I still ran it in the python window (did not understand the other option) and it took 10-15 minutes when selecting 50 polygons.

thanks a million!

emil
0 Kudos
AdamCox1
Occasional Contributor II
Hi Emil, I was just testing this again, and realized that the line
rand_index = random.randrange(0,len(fids_to_pick_from)+1)

should be modified to
rand_index = random.randrange(0,len(fids_to_pick_from))

that + 1 will cause an error sometimes ("list index out of range").

Here's the other method, these steps should work just fine and it only takes a few seconds to run:
1. right-click on the desktop to make a new text file (.txt) and name it whatever you want.
2. change the file extension to ".py" from ".txt"
3. right-click on the file and "Edit in IDLE"
4. paste in the code, and enter the correct path for the shapefile with the polygons.
5. press F5 or click Run > Run Module
6. you should get the query when the script is done (takes only a couple of seconds) which you can paste into ArcMap, as a definition query or a Select By Attributes query.
7. just rerun the module as many times as you want for new sets of 50 random polygons.
0 Kudos