Hello,
Can someone tell me how to generate a specific number of random points based on the acreage of a polygon falling within a specified range?
Is there a way to make the Create Random Points tool generate a specific number of points based on the comparison of an area field to an area range?
is there another tool or combination of whic I can use?
This is what I'm talking about:
So, I have 158 polygons in my timber stand layer. I am trying to assign
random points to each individual polygon based on their acreage. So the
breakdown is as follows:
Size of polygon in acres----number of random points required----number of
polygons of this size on the layer.
0-10acres---3points---4polygons
10-20acres---4---37
20-30acres---5---39
30-40acres---6---20
40-50acres---7---11
50-60acres---8---17
60-70acres---9---13
70-80acres---10---6
80-90acres---11---2
90-100acres---12---3
100-110acres---13---3
133acres---16---1
155acres---18---1
I would like to have the points evenly distributed if possible, if not, they
need to be a minimum of 100meters apart.
Thanks in Advance,
Jim Labate
I think the sample I posted here: Create Random Points: respect minimal distance ACROSS features can be adjusted to include the number of points per polygon based on acreage.
Hi James,
The below script should get you started. It creates a feature layer from each feature, creates random points for each feature, then merges these points to a single feature class.
import arcpy from arcpy import env #Specify workspace env.workspace = r"E:\temp\python\test.gdb" env.overwriteOutput = 1 #Specify Feature Class fc = "Airports" minAcres = 0 maxAcres = 10 numPts = 3 #Iterate through feature class by Ascending order for the field Acres with arcpy.da.SearchCursor(fc, ["OBJECTID", "Acres"], sql_clause=("ASC", "ORDER BY Acres")) as cursor: for row in cursor: arcpy.MakeFeatureLayer_management(fc, "fLayer", "OBJECTID = {0}".format(row[0])) if row[1] > minAcres and row[1] < maxAcres: arcpy.CreateRandomPoints_management(env.workspace, "pts{0}".format(row[0]), "fLayer", "", numPts, "100 Meter") elif row[1] == 133: numPts = 16 arcpy.CreateRandomPoints_management(env.workspace, "pts{0}".format(row[0]), "fLayer", "", numPts, "100 Meter") elif row[1] == 133: numPts = 18 arcpy.CreateRandomPoints_management(env.workspace, "pts{0}".format(row[0]), "fLayer", "", numPts, "100 Meter") minAcres += 10 maxAcres += 10 numPts += 1 del cursor list = [] for fc in arcpy.ListFeatureClasses("Pts*"): list.append(fc) #Merge all point feature classes arcpy.Merge_management(list, "RandomPoints") #Delete individual point feature classes for fc in arcpy.ListFeatureClasses("Pts*"): arcpy.Delete_management(fc)
Jake (and all other respondents in this thread) -
Thanks very much for your input. My apologies for disappearing - I was in the middle of an extended project when I posted the question and have had no opportunity to pick this back up until today.
That said - your script is very impressive - direct and efficient. I must be leaving a slight edit out because the code runs to the end successfully, but so far the best output I've gotten is just a single point - the first point generated from the first polygon in the fc.
You can see the MakeFeatureLayer tool chugging away on every polygon in the input fc - but no temp fLayer (output) is generated - there should be one for every polygon correct? - that in turn is merged into the final "RandomPoints" fc.
A RandomPoints fc is created and the script does not error out - but as earlier stated, the out put is either empty or just contains the first point generated only. So it's very close to working.
There was an empty output warning early on in the results - when the CreateRandomPoints tool first kicked off
Here is the script as I ran it
import arcpy
... from arcpy import env
... #Specify workspace
... env.workspace = r"C:\GIS\BrettWalker\PointsByAcreage.gdb"
... env.overwriteOutput = 1
...
... #Specify Feature Class
... fc = r"C:\GIS\BrettWalker\PointsByAcreage.gdb\TimberInventoryStands"
...
... minAcres = 0
... maxAcres = 10
... numPts = 3
...
... #Iterate through feature class by Ascending order for the field Acres
... with arcpy.da.SearchCursor(fc, ["OBJECTID", "Acres"], sql_clause=("ASC", "ORDER BY Acres")) as cursor:
... for row in cursor:
... arcpy.MakeFeatureLayer_management(fc, "fLayer", "OBJECTID = {0}".format(row[0]))
... if row[1] > minAcres and row[1] < maxAcres:
... arcpy.CreateRandomPoints_management(env.workspace, "pts{0}".format(row[0]), "fLayer", "", numPts, "100 Meter")
... elif row[1] == 133:
... numPts = 16
... arcpy.CreateRandomPoints_management(env.workspace, "pts{0}".format(row[0]), "fLayer", "", numPts, "100 Meter")
... elif row[1] == 133:
... numPts = 18
... arcpy.CreateRandomPoints_management(env.workspace, "pts{0}".format(row[0]), "fLayer", "", numPts, "100 Meter")
...
... minAcres += 10
... maxAcres += 10
... numPts += 1
...
... del cursor
...
... list = []
...
... for fc in arcpy.ListFeatureClasses("Pts*"):
... list.append(fc)
...
... #Merge all point feature classes
... arcpy.Merge_management(list, "RandomPoints")
...
... #Delete individual point feature classes
... for fc in arcpy.ListFeatureClasses("Pts*"):
... arcpy.Delete_management(fc)
Does Darren Wiens suggestion about getting the need to be incorporated?
Thanks again for all your help.
Jim Labate
So you are trying to generate 3 points with a minimum distance of 100 meters between them in an area between 0 and 10 acres? I don't think that is going to work. Not sure if that is the reason for the script to fail, but you might want to reduce the minimum space between the points when the areas are small.
OK, taking a closer look, the main problem is that you test the field Acres and it will only do something if the area is between 0 and 10 acres or the acres = 133. That is the reason it is not working.
Hi Xander,
Thanks for response. The code does test for all the other ranges e.g. 10 - 20; 20- 30 etc. via the 3 lines using the shorthand operator "+=" - Is it that the SearchCursor is not looping through those lines?
Also, when the script has generated a single point output file - for the smallest polygon (4.59 acres) it did not generate 3 points as the variable "numPts" should have instructed it to do.(plenty of room for a min distance of 100 meters)
Below is the status of how the script is operating:
So it appears that all the required steps are accounted for in the code in order for it to assess a value in one field against a range and create the assigned number of points for that range via the CreateRandomPoints tool.
So is the problem:
Again,
it appears to be close to accomplishing the task. Do you see where it's going wrong? Any ideas Jake Skinner - are you still out there?
Thanks to all for assistance,
Jim
Hi James Labate ,
The code does test for all the other ranges e.g. 10 - 20; 20- 30 etc. via the 3 lines using the shorthand operator "+=" - Is it that the SearchCursor is not looping through those lines?
Sure, the SearchCursor will loop through these lines, but the chances that the current area will be in that range are very small. Just have a look at this example:
Loop | Area | minAcres | maxAcres |
1 | 4,59 | 0 | 10 |
2 | 4,89 | 10 | 20 |
3 | 10,19 | 20 | 30 |
4 | 60,74 | 30 | 40 |
5 | 70,69 | 40 | 50 |
The first one is, the next ones aren't... You will have to create a function that returns the desired number of points based on the area.
Also, when the script has generated a single point output file - for the smallest polygon (4.59 acres) it did not generate 3 points as the variable "numPts" should have instructed it to do.(plenty of room for a min distance of 100 meters)
It really depends where the first point is located and if the system can generate more points inside the polygon with the specified minimum distance. From the Help:
When unable to place anymore random points within a constraining area without breaking the minimum allowed distance specified, the number of random points in the constraining area will be reduced to the maximum possible under the minimum allowed distance.
3. Then two elif statements check for the two specific non-range values – 133 & 155.
You are testing twice for the value 133 in the code you pasted in this thread.
Xander,
1) Doesn't the SearchCursor keep looping through the 3 lines of shorthand operator
minAcres += 10
maxAcres += 10
numPts += 1
until the acreage value for a specified polygon falls within on of the ten acre ranges?
For example, OBJECTID # 33 in my feature class has an acreage value of 106 acres.
Shouldn't the SearchCursor keep looping through the 3 lines of code above until the tenth time through where the range the acreage value being tested against has reached 100 - 110 acres? Then, having found the proper range match which through the shorthand operator should now assign 13 points - can continue on to MakeFeatureLayer and then CreateRandomPoints.
Is the issue here that the 3 lines of shorthand operator Do Not have a cumulative effect? In other words, every time it loops through is it just testing for the 10 - 20 acre range. Even if that was the case, there are 37 polygons that fall in the 10 - 20 acre range so shouldn't those 37 at lease - along with the polys that fall within the 0 - 10 range have points generated?
2) If I can manually place 3 points at a minimum distance of 100 meters in a 4.59 acre polygon - why can't the CreateRandomPoints tool do it? I don't follow that. To test, I placed a point in the NW section of the 4.59 acre poly and then measure 100 meters SE and then 100 meters SW - 3 points at a minimum distance of 100 meters fit in that polygon - the tool doesn't seem to find the proper starting place for the first point's placement.
3) The second elif has subsequently been edited to check for 155acres.
I'll also add this bit of the RESULT output.
MakeFeatureLayer does execute for every polygon in the fc but nothing seems to be "visible" to it - see below one example - 157 others similar
Executing: MakeFeatureLayer C:\GIS\BrettWalker\PointsByAcreage.gdb\TimberInventoryStands fLayer "OBJECTID = 84" # "OBJECTID OBJECTID VISIBLE NONE;Shape Shape VISIBLE NONE;AREA_SIZE AREA_SIZE VISIBLE NONE;INV_PA_AC_ INV_PA_AC_ VISIBLE NONE;STD_AC STD_AC VISIBLE NONE;FEAT_ID_ FEAT_ID_ VISIBLE NONE;standID standID VISIBLE NONE;treatment_ treatment_ VISIBLE NONE;remarks remarks VISIBLE NONE;siteindex siteindex VISIBLE NONE;sispecies sispecies VISIBLE NONE;total_basa total_basa VISIBLE NONE;AGS_ba AGS_ba VISIBLE NONE;medial_dia medial_dia VISIBLE NONE;eff_age eff_age VISIBLE NONE;gross_bdft gross_bdft VISIBLE NONE;net_bdft_v net_bdft_v VISIBLE NONE;value value VISIBLE NONE;Shape_Length Shape_Length VISIBLE NONE;Shape_Area Shape_Area VISIBLE NONE;Acres Acres VISIBLE NONE"
Start Time: Fri Feb 26 16:19:16 2016
Succeeded at Fri Feb 26 16:19:16 2016 (Elapsed Time: 0.00 seconds)
Is the tool not temporarily creating the polygon?
Thanks for all the help Xander,
Jim
Let's explain something with some code examples. What you are doing is this:
# generate a list of areas (acres) similar to your data from random import uniform lst_areas = [] for i in range(0, 100): lst_areas.append(uniform(4.5, 110)) lst_areas += [133, 155] # now continue with the logic of your script minAcres = 0 maxAcres = 10 numPts = 3 for area in sorted(lst_areas): if area > minAcres and area < maxAcres: print area, numPts elif area == 133: # if it isn't exactly 133, it will not do this! numPts = 16 print area, numPts elif area == 155: # you check for 133 twice! numPts = 18 print area, numPts minAcres += 10 maxAcres += 10 numPts += 1
This will yield only three results (area<space>numPts):
4.5589899377 3 133 16 155 18
In your case you test twice for 133, so you may have only two results and if the area is not an integer you will not find 133 nor 155.
Probably you meant to do something along the lines of this (a loop within a loop):
from random import uniform lst_areas = [] for i in range(0, 100): lst_areas.append(uniform(4.5, 110)) lst_areas += [133, 155] for area in sorted(lst_areas): minAcres = 0 maxAcres = 10 numPts = 3 cont = True while cont: if area > minAcres and area < maxAcres: print area, numPts cont = False elif area == 133: # if it isn't exactly 133, it will not do this! numPts = 16 print area, numPts cont = False elif area == 155: # you check for 133 twice! numPts = 18 print area, numPts cont = False minAcres += 10 maxAcres += 10 numPts += 1
This will yield a numPts for each individual area:
5.90022859369 3 6.06444530915 3 6.34175139149 3 ... 107.897255347 13 109.498385359 13 133 16 155 18
The point is, there is no need to make thing this complex, since you could use something like:
from random import uniform lst_areas = [] for i in range(0, 100): lst_areas.append(uniform(4.5, 110)) lst_areas += [133, 155] for area in sorted(lst_areas): print area, int(area // 10) + 3
... providing the same result.