Since you don't need most of the data from the green lines, you could take a very simple (simplistic?) approach and make a a point file consisting of the start and end nodes of the green lines, attributed with a line ID. Then use the ST_Snap function to snap the new points to the existing intersections (red points). Then use the ST_MakeLine function to convert your snapped start/end points into your blue lines.
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
I've managed to solve this, without using the mentioned GRASS tools or topological functions.
Basically I take all start- and endnodes, put them in a new, temporary table, put a buffer around them, union the buffer objects, and move all found nodes in each buffer to the centroid of the buffer.
When that's done I move the original begin and end points to the new location.
Easier than expected, and still fast, but I expected PostGIS to have some built-in function for this - that would be even quicker.
Edit: in the interest of giving back to the community, this is my (quite crappy) code for now.