# Functions to Generate Random Points

## Random Point in Polygon

Use this function when you need to create a random point inside a polygon. The function takes geometry as a required argument and max number of iterations as an optional argument (defaults to 1000 iterations). The function will raise an exception if max number of iterations has been exceeded. The function works by throwing a random point within the polygon bounding rectangle and then checking if the point is inside the polygon.

```CREATE OR REPLACE FUNCTION RandomPoint (
geom Geometry,
maxiter INTEGER DEFAULT 1000
)
RETURNS Geometry
AS \$\$
DECLARE
i INTEGER := 0;
x0 DOUBLE PRECISION;
dx DOUBLE PRECISION;
y0 DOUBLE PRECISION;
dy DOUBLE PRECISION;
xp DOUBLE PRECISION;
yp DOUBLE PRECISION;
rpoint Geometry;
BEGIN
-- find envelope
x0 = ST_XMin(geom);
dx = (ST_XMax(geom) - x0);
y0 = ST_YMin(geom);
dy = (ST_YMax(geom) - y0);

WHILE i < maxiter LOOP
i = i + 1;
xp = x0 + dx * random();
yp = y0 + dy * random();
rpoint = ST_SetSRID( ST_MakePoint( xp, yp ), ST_SRID(geom) );
EXIT WHEN ST_Within( rpoint, geom );
END LOOP;

IF i >= maxiter THEN
RAISE EXCEPTION 'RandomPoint: number of interations exceeded %', maxiter;
END IF;

RETURN rpoint;
END;
\$\$ LANGUAGE plpgsql;
```

This function takes too many iterations if the area of the polygon is small compared to the area of its bounding box. Practical example would be to place a random point inside any of the U.S. islands. The function below somewhat addresses this problem for the case of multipolygons:

```CREATE OR REPLACE FUNCTION RandomPointMulti (
geom Geometry
)
RETURNS Geometry
AS \$\$
DECLARE
maxiter INTEGER := 100000;
i INTEGER := 0;
n INTEGER := 0; -- total number of geometries in collection
g INTEGER := 0; -- geometry number in collection to find random point in
total_area DOUBLE PRECISION; -- total area
cgeom Geometry;
BEGIN
total_area = ST_Area(geom);
n = ST_NumGeometries(geom);

WHILE i < maxiter LOOP
i = i + 1;
g = floor(random() * n)::int;
cgeom = ST_GeometryN(geom, g); -- weight the probability of selecting a subpolygon by its relative area
IF random() < ST_Area(cgeom)/total_area THEN
RETURN RandomPoint( cgeom );
END IF;
END LOOP;

RAISE EXCEPTION 'RandomPointMulti: too many iterations';
END;
\$\$ LANGUAGE plpgsql;
```

However, there are still cases in which the function above fails. For example, to create a random point within 1 mi of the US coast one would first build a 1-mile one-sided buffer along the coast line. The resulting geometry would look like two long and narrow polygons along the East and West coast with a huge empty space in between. To handle a case like that a function that would use some other strategy is necessary.

## Random Points in Polygon

This set-returning function generates `num_points` uniform random points within a polygon geometry `geom`. See examples below.

```CREATE OR REPLACE FUNCTION RandomPointsInPolygon(geom geometry, num_points integer)
RETURNS SETOF geometry AS
\$BODY\$DECLARE
target_proportion numeric;
n_ret integer := 0;
loops integer := 0;
x_min float8;
y_min float8;
x_max float8;
y_max float8;
srid integer;
rpoint geometry;
BEGIN
-- Get envelope and SRID of source polygon
SELECT ST_XMin(geom), ST_YMin(geom), ST_XMax(geom), ST_YMax(geom), ST_SRID(geom)
INTO x_min, y_min, x_max, y_max, srid;
-- Get the area proportion of envelope size to determine if a
-- result can be returned in a reasonable amount of time
SELECT ST_Area(geom)/ST_Area(ST_Envelope(geom)) INTO target_proportion;
RAISE DEBUG 'geom: SRID %, NumGeometries %, NPoints %, area proportion within envelope %',
srid, ST_NumGeometries(geom), ST_NPoints(geom),
round(100.0*target_proportion, 2) || '%';
IF target_proportion < 0.0001 THEN
RAISE EXCEPTION 'Target area proportion of geometry is too low (%)',
100.0*target_proportion || '%';
END IF;
RAISE DEBUG 'bounds: % % % %', x_min, y_min, x_max, y_max;

WHILE n_ret < num_points LOOP
loops := loops + 1;
SELECT ST_SetSRID(ST_MakePoint(random()*(x_max - x_min) + x_min,
random()*(y_max - y_min) + y_min),
srid) INTO rpoint;
IF ST_Contains(geom, rpoint) THEN
n_ret := n_ret + 1;
RETURN NEXT rpoint;
END IF;
END LOOP;
RAISE DEBUG 'determined in % loops (% efficiency)', loops, round(100.0*num_points/loops, 2) || '%';
END\$BODY\$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000;
ALTER FUNCTION RandomPointsInPolygon(geometry, integer) OWNER TO postgres;
```

### Examples

```-- Return 10 random points/rows
SELECT ST_AsText(RandomPointsInPolygon('POLYGON ((10 20, 30 60, 50 20, 10 20))', 10));

-- Or 10 random points as a single MultiPoint
SELECT ST_AsText(ST_Union(geom))
FROM RandomPointsInPolygon('POLYGON ((10 20, 30 60, 50 20, 10 20))', 10) AS geom
```

Now for a more complicated and practical example using a geopolitical polygon of the world table, create 10000 random points in India:

```CREATE TABLE india_random_points
(
gid serial NOT NULL,
CONSTRAINT india_random_points_pkey PRIMARY KEY (gid)
);
SELECT AddGeometryColumn('india_random_points', 'geom', 4326, 'POINT', 2);

-- Populate table with 10000 random points within India
INSERT INTO india_random_points(geom)
SELECT RandomPointsInPolygon(geom, 10000)
FROM world WHERE name='INDIA';
```
Last modified on Jun 7, 2011 5:11:39 AM