Personally I do not like the random point algorithm in ArcGIS. Alternatively, use Geospatial Modelling Environment's (GME) genrandompnts
function. You will be able to identify specific polygons where random points will be excluded (see highlighted area in attached .jpg). Best of all this software is free.
GME provides you with a suite of analysis and modelling tools, ranging
from small 'building blocks' that you can use to construct a
sophisticated work-flow, to completely self-contained analysis
programs. It also uses the extraordinarily powerful open source
software R as the statistical engine to drive some of the analysis
tools. One of the many strengths of R is that it is open source,
completely transparent and well documented: important characteristics
for any scientific analytical software.
I made and tested the functions I need. For point generation I used a simplified version of the functions in trac.osgeo.org/postgis/wiki/UserWikiRandomPoint:
CREATE OR REPLACE FUNCTION random_point( x_min integer DEFAULT 13, y_min integer DEFAULT 49,
x_max integer DEFAULT 16, y_max integer DEFAULT 51, srid integer DEFAULT 4326 )
RETURNS geometry AS
$func$
BEGIN
RETURN (
SELECT ST_SetSRID(
ST_MakePoint(
random()*(x_max - x_min) + x_min,
random()*(y_max - y_min) + y_min
), srid
)
);
END
$func$ LANGUAGE plpgsql VOLATILE;
I don't need the position to be extra accurate, so I use approximate coordinates for Czech Republic.
I don't need to generate lines now. For the polygon I started from scratch, using the algorithm I described in the question (building the polygon around a quasicentroid):
--max_dist default is for WGS84 (EPSG4326) - slightly less than 200 meters
CREATE OR REPLACE FUNCTION random_polygon( qcen geometry, max_dist float DEFAULT 0.0002 )
RETURNS geometry AS
$func$
DECLARE
i INTEGER;
nodes geometry[];
angle FLOAT;
angle_start FLOAT;
dist FLOAT; --for polar areas, we would have too make separately lat and lon distance - lon distance would be too short otherwise
BEGIN
-- we can skip this step if we are sure we can get only points
-- it could handle multipoint too, but the results wouldn't be nice
IF ST_geometryType( qcen ) != 'ST_Point' THEN
RETURN NULL;
END IF;
-- PI/3 and 2/3 PI means that
angle_start := ( random()*1/3*PI() )::INTEGER;
angle := angle_start;
FOR i IN 1..20 LOOP -- we rarely reach 20 nodes, but now we are safe from endless loop
dist := random() * max_dist;
SELECT array_append( nodes, ST_Translate( qcen, sin(angle)*dist, cos(angle)*dist ) ) INTO nodes;
angle := angle + random()*2/3*PI();
IF angle > 2*PI() THEN EXIT; END IF;
END LOOP;
SELECT array_append( nodes, nodes[1] ) INTo nodes; -- assuming 1 is the lower bound
RETURN ST_MakePolygon( ST_MakeLine( nodes ) );
END
$func$ LANGUAGE plpgsql VOLATILE;
I picked few of the resulting geometries for the following image:
I don't mind when geometries intersect each other; otherwise there would be a check for this too.
Best Answer
Someting like this should do what you want, but please note that the random points here are all integers