Table of Contents
Introduction
The Create Random Points (Data Management) tool is available with an Advanced license or on a Basic or Standard license if you have 3D Analyst or Spatial Analyst. In this post we will look at creating the tool for use with a Basic license. We’ll keep it simple for now and just generate the desired amount of points per feature. Check out the Esri documentation for the Create Random Points tool. This workflow was created using ArcGIS Pro 3.2.2.
Leverage ArcPy for geospatial data management workflows within ArcGIS Pro. Learn the fundamentals of utilising ArcGIS Pro geoprocessing tools with ArcPy for data management, conversions, and analysis. This course is packed with amazing content that will help you discover the power of automating tasks within the ArcGIS Pro environment. Take your ArcPy skills from beginner to snake charmer. A little code goes a long way, a little ArcPy provides you with an in-demand skill. Sign up now for our highly rated course.
Syntax
The syntax for the Create Random Points tool is as follows…
arcpy.management.CreateRandomPoints(
out_path,
out_name,
{constraining_feature_class},
{constraining_extent},
{number_of_points_or_field},
{minimum_allowed_distance},
{create_multipoint_output},
{multipoint_size}
)
We’re going to take a different direction from the Create Random Points syntax and use three required parameters; in_features, the features you wish to generate random points for, these can be Point, Polyline, or Polygon; out_feature_class, the output Point feature class to create containing the random points; and number_of_points, the number of points to generate for each feature from the in_features.
Import the modules
ArcPy is a Python site package that enables automation, analysis, and management of geographic data within the ArcGIS software environment. The random module is a Python library that provides functions for generating random numbers and making random selections from sequences, useful for tasks like simulation, games, and cryptography.
import arcpy
import random
Define the parameters
We require three parameters; in_features, out_feature_class, number_of_points.
## the input features to generate random points for
in_features = arcpy.GetParameterAsText(0)
## the output point feature class
out_feature_class = arcpy.GetParameterAsText(1)
## the number of points to generate for each feature
number_of_points = arcpy.GetParameterAsText(2)
Required Python objects for the tool
We require four objects to help us with our workflow; the shape type of the in_features, whether it is Point, Polyline, or Polygon; the Spatial Reference System code so we can add as the SRS to the out_feature_class; the OID field from the in_features so we can assign to the ORIG_FID in the out_feature_class; and then to make sure that we cast the number of points from the input to an integer.
## shape type of in features - Point, Polygon, Polyline
shape_type = arcpy.Describe(in_features).shapeType
## srs id of the in_features so we can assign the output the same
srs_id = arcpy.Describe(in_features).spatialReference.factoryCode
## the OID field name from the in_features
oid_fld = [fld.name for fld in arcpy.ListFields(in_features) if fld.type=="OID"][0]
## make sure the number of points is an integer
number_of_points = int(number_of_points)
Create a temporary feature class and define schema
The memory workspace is fantastic for holding intermediate data in the workflow so we will use it to store or random generated points. We create a feature class in the memory workspace and then add an ORIG_FID field.
## a memory workspace feature class to store the random points generated
temp_fc = arcpy.management.CreateFeatureclass("memory", "temp_fc", "POINT", spatial_reference=srs_id)
## add ORIG_FID field to teh feature class
arcpy.management.AddField(temp_fc, "ORIG_FID", "LONG")
The Main Event: Generate the Random Points
Now for the magic to happen! We need to account for our three possible shape types; Point, Polyline, and Polygon.
If the in_features are Points, we need to know if the number_of _points entered is greater than or equal to the number of records in the in_features. If the number_of_points is indeed greater or equal to the record count, then all points are exported as ‘random points’. However, if the number_of_points is less than the record count of the in_features, then we generate a random selection of OID values and use an Insert Cursor with a where clause to add the points to our memory feature class.
If the in_features are Polylines, we combine the use of an Insert Cursor with our memory feature class with a Search Cursor for our in_features and use a random distance along each line to generate each random point.
Finally, if the in_features are Polygons, we combine the use of an Insert Cursor with our memory feature class with a Search Cursor for our in_features, we get the minimum bounding extent for each Polygon features and generate random X and Y coordinate pairs the fall within the bounding extent. We perform a test that the random point is contained by the polygon geometry and if not, go again until it is.
## if the in_features are Points
if shape_type == "Point":
num_points = int(arcpy.management.GetCount(in_features)[0])
## if the number of random points required is more than the points in in_features
if number_of_points >= num_points:
## no need for a where clause if the number of points from user input
## matches or exceeds the number of points in the in_features
sql_exp = ""
## otherwise get a random selection
else:
## get a list of all OIDs
oid_list = [row[0] for row in arcpy.da.SearchCursor(in_features, oid_fld)]
## get a random list of OIDs
random_list = random.sample(oid_list, number_of_points)
## create the where clause sql expression
sql_exp = "{0} IN ({1})".format(oid_fld, ",".join(str(x) for x in random_list))
## start an insert cursor for the temp_fc
with arcpy.da.InsertCursor(temp_fc, ["ORIG_FID", "SHAPE@"]) as insert_cursor:
## iterate through the records of the in_features
with arcpy.da.SearchCursor(in_features, [oid_fld, "SHAPE@"], sql_exp) as search_cursor:
## add the points to the temp_fc
for row in search_cursor:
insert_cursor.insertRow((row[0], row[1]))
elif shape_type == "Polyline":
## start an insert cursor for the temp_fc
with arcpy.da.InsertCursor(temp_fc, ["ORIG_FID", "SHAPE@"]) as insert_cursor:
## iterate through the records for the in_features
with arcpy.da.SearchCursor(in_features, [oid_fld, "SHAPE@", "SHAPE@LENGTH"]) as search_cursor:
## for each linear record
for row in search_cursor:
## generate a random point along the line and add to temp_fc
for num in range(number_of_points):
distance = round(random.uniform(0.0, row[2]), 3)
pt = row[1].positionAlongLine(distance)
insert_cursor.insertRow((row[0], pt))
elif shape_type == "Polygon":
## start an insert cursor for the temp_fc
with arcpy.da.InsertCursor(temp_fc, ["ORIG_FID", "SHAPE@"]) as insert_cursor:
## iterate through the records for the in_features
with arcpy.da.SearchCursor(in_features, [oid_fld, "SHAPE@"]) as search_cursor:
## for each polygon record
for row in search_cursor:
## to hold random points
random_points = []
## get the extent of the feature
extent = row[1].extent
min_x, max_x = extent.XMin, extent.XMax
min_y, max_y = extent.YMin, extent.YMax
## generate points
for num in range(number_of_points):
while len(random_points) < number_of_points:
x = random.uniform(min_x, max_x)
y = random.uniform(min_y, max_y)
pt = arcpy.Point(x, y)
## if the point is inside the polygon then add to the list
if row[1].contains(pt):
random_points.append(pt)
## insert points into the temp_fc
for pt in random_points:
insert_cursor.insertRow((row[0], pt))
Write the output to disk and clean-up
Almost there! Let’s write the data to disk and clean-up the memory workspace.
## export random generated points to a feature class on disk
arcpy.conversion.ExportFeatures(temp_fc, out_feature_class)
## clean-up memory workspace
arcpy.management.Delete(temp_fc)
Mastering ArcGIS Pro ArcPy Search, Insert, & Update Cursors
Unlock the full potential of ArcPy Cursors with our intensive and in-depth course. Designed for GIS professionals, analysts, and enthusiasts, this course delves into the intricacies of Search, Insert, and Update Cursors in the ArcPy library, empowering you to manipulate and manage spatial data with precision and efficiency.
ArcPy Cursors enable you to streamline your GIS workflows, automate repetitive tasks, and witness the full potential of spatial data manipulation in ArcGIS Pro.
Create the tool in ArcGIS Pro
Save your script and open up ArcGIS Pro. Right-click on your toolbox/toolset of choice and select New > Script. The New Script window will appear. In the General tab set Name to createRandomPoints, Label to Create Random Points (Basic), and the Description to Create Random Points with a Basic License.
In the Parameters tab set as per below. Set the Filter for Input Features to Feature Type; Point, Polyline, Polygon. Set the Direction for Output Feature Class to Output and a Filter for Feature Type of Point.
In the Execution tab, click the folder icon in the top-right corner and ad your saved Python script. Click OK and take the tool for a spin.
You can download the tool and other Advanced tools with a Basic license over on this page.
All the code in one place
You can find the entire code workflow below with links to important components in the documentation that were used.
import arcpy
import random
################################################################################
## Esri Documentation
## https://pro.arcgis.com/en/pro-app/3.2/arcpy/functions/getparameterastext.htm
## https://pro.arcgis.com/en/pro-app/3.2/arcpy/functions/describe.htm
## https://pro.arcgis.com/en/pro-app/3.2/arcpy/functions/listfields.htm
## https://pro.arcgis.com/en/pro-app/3.2/tool-reference/data-management/add-field.htm
## https://pro.arcgis.com/en/pro-app/3.2/tool-reference/data-management/create-feature-class.htm
## https://pro.arcgis.com/en/pro-app/3.2/arcpy/classes/polyline.htm
## https://pro.arcgis.com/en/pro-app/3.2/arcpy/classes/polygon.htm
## https://pro.arcgis.com/en/pro-app/3.2/tool-reference/data-management/get-count.htm
## https://pro.arcgis.com/en/pro-app/3.2/arcpy/data-access/searchcursor-class.htm
## https://pro.arcgis.com/en/pro-app/3.2/arcpy/data-access/updatecursor-class.htm
## https://pro.arcgis.com/en/pro-app/3.2/tool-reference/conversion/export-features.htm
## https://pro.arcgis.com/en/pro-app/3.2/tool-reference/data-management/delete.htm
##
## ArcGIS Pro Version: 3.2.2
##
################################################################################
################################################################################
## USER INPUTS
## the input features to generate random points for
in_features = arcpy.GetParameterAsText(0)
## the output point feature class
out_feature_class = arcpy.GetParameterAsText(1)
## the number of points to generate for each feature
number_of_points = arcpy.GetParameterAsText(2)
################################################################################
## REQUIRED OBJECTS
## shape type of in features - Point, Polygon, Polyline
shape_type = arcpy.Describe(in_features).shapeType
## srs id of the in_features so we can assign the output the same
srs_id = arcpy.Describe(in_features).spatialReference.factoryCode
## the OID field name from the in_features
oid_fld = [fld.name for fld in arcpy.ListFields(in_features) if fld.type=="OID"][0]
## make sure the number of points is an integer
number_of_points = int(number_of_points)
################################################################################
## CREATE POINT FEATURE CLASS IN MEMORY WORKSPACE
## a memory workspace feature class to store the random points generated
temp_fc = arcpy.management.CreateFeatureclass("memory", "temp_fc", "POINT", spatial_reference=srs_id)
## add ORIG_FID field to teh feature class
arcpy.management.AddField(temp_fc, "ORIG_FID", "LONG")
################################################################################
## CREATE RANDOM POINTS
## if the in_features are Points
if shape_type == "Point":
num_points = int(arcpy.management.GetCount(in_features)[0])
## if the number of random points required is more than the points in in_features
if number_of_points >= num_points:
## no need for a where clause if the number of points from user input
## matches or exceeds the number of points in the in_features
sql_exp = ""
## otherwise get a random selection
else:
## get a list of all OIDs
oid_list = [row[0] for row in arcpy.da.SearchCursor(in_features, oid_fld)]
## get a random list of OIDs
random_list = random.sample(oid_list, number_of_points)
## create the where clause sql expression
sql_exp = "{0} IN ({1})".format(oid_fld, ",".join(str(x) for x in random_list))
## start an insert cursor for the temp_fc
with arcpy.da.InsertCursor(temp_fc, ["ORIG_FID", "SHAPE@"]) as insert_cursor:
## iterate through the records of the in_features
with arcpy.da.SearchCursor(in_features, [oid_fld, "SHAPE@"], sql_exp) as search_cursor:
## add the points to the temp_fc
for row in search_cursor:
insert_cursor.insertRow((row[0], row[1]))
elif shape_type == "Polyline":
## start an insert cursor for the temp_fc
with arcpy.da.InsertCursor(temp_fc, ["ORIG_FID", "SHAPE@"]) as insert_cursor:
## iterate through the records for the in_features
with arcpy.da.SearchCursor(in_features, [oid_fld, "SHAPE@", "SHAPE@LENGTH"]) as search_cursor:
## for each linear record
for row in search_cursor:
## generate a random point along the line and add to temp_fc
for num in range(number_of_points):
distance = round(random.uniform(0.0, row[2]), 3)
pt = row[1].positionAlongLine(distance)
insert_cursor.insertRow((row[0], pt))
elif shape_type == "Polygon":
## start an insert cursor for the temp_fc
with arcpy.da.InsertCursor(temp_fc, ["ORIG_FID", "SHAPE@"]) as insert_cursor:
## iterate through the records for the in_features
with arcpy.da.SearchCursor(in_features, [oid_fld, "SHAPE@"]) as search_cursor:
## for each polygon record
for row in search_cursor:
## to hold random points
random_points = []
## get the extent of the feature
extent = row[1].extent
min_x, max_x = extent.XMin, extent.XMax
min_y, max_y = extent.YMin, extent.YMax
## generate points
for num in range(number_of_points):
while len(random_points) < number_of_points:
x = random.uniform(min_x, max_x)
y = random.uniform(min_y, max_y)
pt = arcpy.Point(x, y)
## if the point is inside the polygon then add to the list
if row[1].contains(pt):
random_points.append(pt)
## insert points into the temp_fc
for pt in random_points:
insert_cursor.insertRow((row[0], pt))
################################################################################
## SAVE MEMORY FEATURE CLASS TO DISK
## export random generated points to a feature class on disk
arcpy.conversion.ExportFeatures(temp_fc, out_feature_class)
################################################################################
## CLEAN-UP MEMORY WORKSPACE
## clean-up memory workspace
arcpy.management.Delete(temp_fc)
################################################################################