[postgis-users] geomunion revisited....
Craig de Stigter
craig.destigter at onetrackmind.co.nz
Mon Dec 17 13:45:56 PST 2007
I had to Union geometries with a high degree of overlap, and the ST_Union
function was far too slow (union on 18,000 polygons took over two hours.)
So I wrote a pl/pgsql implementation of CascadedUnion, which will tide me
over till PostGIS gets its own CascadedUnion.
The first function is a convenience function that just discovers the initial
extent of the table and calls the second function.
The second function is recursive and splits the extent until there are fewer
than a certain number of points, calling ST_Union on such subsets.
So for example:
> SELECT CascadedUnion('public', 'mytable', 'the_geom', 1000);
>
will split the bounding box recursively until there are fewer than 1000
geometries in each box, and call ST_Union on each of these subsets.
For my (highly overlapping) data set, this does in 18 seconds what a plain
ST_Union took more than two hours to do. For non-overlapping datasets it
will work out slower.
Heres the code:
> CREATE OR REPLACE FUNCTION CascadedUnion(character varying, character
> varying, character varying, integer)
>
RETURNS geometry AS
> $BODY$
> DECLARE
> curs1 refcursor;
> bounds BOX2D;
> BEGIN
> OPEN curs1 FOR EXECUTE 'SELECT extent(' || $3 || ') FROM ' || $1 ||
> '.' || $2;
> FETCH curs1 INTO bounds;
> RETURN CascadedUnion($1, $2, $3, $4, bounds);
> END;
> $BODY$
> LANGUAGE 'plpgsql' VOLATILE;
>
> CREATE OR REPLACE FUNCTION CascadedUnion(character varying, character
> varying, character varying, integer, box3d)
> RETURNS geometry AS
> $BODY$
> DECLARE
> end_geom GEOMETRY;
> bounds BOX3D;
> bounds1 BOX3D;
> bounds2 BOX3D;
> numfeatures integer;
> curs1 refcursor;
> curs2 refcursor;
> max_x real;
> max_y real;
> min_x real;
> min_y real;
> result1 geometry;
> result2 geometry;
> emptygeom geometry;
> BEGIN
> max_x := xmax($5);
> min_x := xmin($5);
> max_y := ymax($5);
> min_y := ymin($5);
>
> select into bounds $5;
> OPEN curs1 FOR EXECUTE 'SELECT count(*) FROM ' || $1 || '.' || $2 || '
> WHERE ' || $3 || ' && setsrid(box2d(''BOX(' || min_x || ' ' || min_y || ','
> || max_x || ' ' || max_y || ')''), srid(' || $3 || ')) AND
> intersects(centroid(' || $3 || '), setsrid(box2d(''BOX(' || min_x || ' ' ||
> min_y || ',' || max_x || ' ' || max_y || ')''), srid(' || $3 || ')))';
> FETCH curs1 INTO numfeatures;
>
> IF numfeatures < $4 THEN
> OPEN curs2 FOR EXECUTE 'select st_union(' || $3 || ') from ' || $1
> || '.' || $2 || ' where ' || $3 || ' && setsrid(box2d(''BOX(' || min_x || '
> ' || min_y || ',' || max_x || ' ' || max_y || ')''), srid(' || $3 || ')) AND
> intersects(centroid(' || $3 || '), setsrid(box2d(''BOX(' || min_x || ' ' ||
> min_y || ',' || max_x || ' ' || max_y || ')''), srid(' || $3 || ')))';
> FETCH curs2 INTO end_geom;
> ELSE
> --split in longest dimension
> IF max_x - min_x > max_y - min_y THEN
> bounds1 := makebox3d(makepoint(min_x, min_y),
> makepoint((max_x+min_x)/2, max_y));
> bounds2 := makebox3d(makepoint((max_x + min_x)/2, min_y),
> makepoint(max_x, max_y));
> ELSE
> bounds1 := makebox3d(makepoint(min_x, min_y), makepoint(max_x,
> (max_y+min_y)/2));
> bounds2 := makebox3d(makepoint(min_x, (max_y+min_y)/2),
> makepoint(max_x, max_y));
> END IF;
>
> --recurse
> SELECT INTO result1 CascadedUnion($1, $2, $3, $4, bounds1);
> SELECT INTO result2 CascadedUnion($1, $2, $3, $4, bounds2);
> SELECT INTO emptygeom geomfromtext('GEOMETRYCOLLECTION(EMPTY)',
> find_srid($1, $2, $3));
> SELECT INTO end_geom st_union(coalesce(result1, emptygeom),
> coalesce(result2, emptygeom));
> END IF;
> RETURN end_geom;
>
> END;
> $BODY$
> LANGUAGE 'plpgsql' VOLATILE;
>
Regards
Craig
One Track Mind Ltd.
PO Box 1604, Shortland St, Auckland, New Zealand
Phone +64-9-966 0433 Fax +64-9-969 0045
Web http://www.onetrackmind.co.nz
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osgeo.org/pipermail/postgis-users/attachments/20071218/b317bf3b/attachment.html>
More information about the postgis-users
mailing list