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.)<br><br>So I wrote a pl/pgsql implementation of CascadedUnion, which will tide me over till PostGIS gets its own CascadedUnion.
<br><br>The first function is a convenience function that just discovers the initial extent of the table and calls the second function.<br>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.
<br><br>So for example:<br><blockquote style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;" class="gmail_quote">SELECT CascadedUnion('public', 'mytable', 'the_geom', 1000);
<br></blockquote>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.<br><br>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.
<br><br>Heres the code:<br><br><div>  <br></div><blockquote style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;" class="gmail_quote">CREATE OR REPLACE FUNCTION CascadedUnion(character varying, character varying, character varying, integer)  
<br></blockquote><blockquote style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;" class="gmail_quote">RETURNS geometry AS<br>$BODY$<br>DECLARE<br>    curs1 refcursor;<br>    bounds BOX2D;
<br>BEGIN<br>    OPEN curs1 FOR EXECUTE 'SELECT extent(' || $3 || ') FROM ' || $1 || '.' || $2;<br>    FETCH curs1 INTO bounds;<br>    RETURN CascadedUnion($1, $2, $3, $4, bounds);<br>END;<br>$BODY$
<br>LANGUAGE 'plpgsql' VOLATILE;<br><br>CREATE OR REPLACE FUNCTION CascadedUnion(character varying, character varying, character varying, integer, box3d)<br>RETURNS geometry AS<br>$BODY$<br>DECLARE<br>    end_geom GEOMETRY;
<br>    bounds BOX3D;<br>    bounds1 BOX3D;<br>    bounds2 BOX3D;<br>    numfeatures integer;<br>    curs1 refcursor;<br>    curs2 refcursor;<br>    max_x real;<br>    max_y real;<br>    min_x real;<br>    min_y real;<br>
    result1 geometry;<br>    result2 geometry;<br>    emptygeom geometry;<br>BEGIN<br>    max_x := xmax($5);<br>    min_x := xmin($5);<br>    max_y := ymax($5);<br>    min_y := ymin($5);<br><br>    select into bounds $5;<br>
    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 || ')))';
<br>    FETCH curs1 INTO numfeatures;<br><br>    IF numfeatures < $4 THEN <br>        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 || ')))';
<br>        FETCH curs2 INTO end_geom;<br>    ELSE<br>        --split in longest dimension<br>        IF max_x - min_x > max_y - min_y THEN<br>            bounds1 := makebox3d(makepoint(min_x, min_y), makepoint((max_x+min_x)/2, max_y));
<br>            bounds2 := makebox3d(makepoint((max_x + min_x)/2, min_y), makepoint(max_x, max_y));<br>        ELSE<br>            bounds1 := makebox3d(makepoint(min_x, min_y), makepoint(max_x, (max_y+min_y)/2));<br>            bounds2 := makebox3d(makepoint(min_x, (max_y+min_y)/2), makepoint(max_x, max_y));
<br>        END IF;<br><br>        --recurse<br>        SELECT INTO result1 CascadedUnion($1, $2, $3, $4, bounds1);<br>        SELECT INTO result2 CascadedUnion($1, $2, $3, $4, bounds2);<br>        SELECT INTO emptygeom geomfromtext('GEOMETRYCOLLECTION(EMPTY)', find_srid($1, $2, $3));
<br>        SELECT INTO end_geom st_union(coalesce(result1, emptygeom), coalesce(result2, emptygeom));<br>    END IF;<br>    RETURN end_geom;<br><br>END;<br>$BODY$<br>LANGUAGE 'plpgsql' VOLATILE;<br></blockquote><br>
<br>Regards<br>Craig<br><br>One Track Mind Ltd.<br>PO Box 1604, Shortland St, Auckland, New Zealand<br>Phone +64-9-966 0433 Fax +64-9-969 0045<br>Web <a href="http://www.onetrackmind.co.nz">http://www.onetrackmind.co.nz</a>
<br>