diff --git a/doc/reference_raster.xml b/doc/reference_raster.xml index 5a6d641b6..6be05c376 100644 --- a/doc/reference_raster.xml +++ b/doc/reference_raster.xml @@ -8764,6 +8764,54 @@ a_rid | b_rid | overleft + + boolean ST_Intersects + + raster + rast + + + integer + nband + + + geometry + geommin + + + + + boolean ST_Intersects + + raster + rast + + + geometry + geommin + + + integer + nband=NULL + + + + + boolean ST_Intersects + + geometry + geommin + + + raster + rast + + + integer + nband=NULL + + + @@ -8786,7 +8834,7 @@ a_rid | b_rid | overleft - Changed: The ST_Intersects(raster, geometry) and ST_Intersects(geometry, raster) variants present before PostGIS 2.1.0 have been removed. Use ST_Intersects(ST_Polygon(raster), geometry) instead. + Changed: In 2.1.0, the behavior of the ST_Intersects(raster, geometry) variants changed to match that of ST_Intersects(geometry, raster). @@ -8864,6 +8912,12 @@ a_rid | b_rid | overleft This operand will make use of any indexes that may be available on the rasters. + + + + To test the spatial relationship of a raster and a geometry, use ST_Polygon on the raster, e.g. ST_Overlaps(ST_Polygon(raster), geometry). + + Availability: 2.1.0 @@ -8940,7 +8994,13 @@ a_rid | b_rid | overleft This operand will make use of any indexes that may be available on the rasters. - + + + + To test the spatial relationship of a raster and a geometry, use ST_Polygon on the raster, e.g. ST_Touches(ST_Polygon(raster), geometry). + + + Availability: 2.1.0 @@ -9016,7 +9076,13 @@ a_rid | b_rid | overleft This operand will make use of any indexes that may be available on the rasters. - + + + + To test the spatial relationship of a raster and a geometry, use ST_Polygon on the raster, e.g. ST_Contains(ST_Polygon(raster), geometry) or ST_Contains(geometry, ST_Polygon(raster)). + + + Availability: 2.1.0 diff --git a/raster/rt_pg/rtpostgis.sql.in.c b/raster/rt_pg/rtpostgis.sql.in.c index a2c84dde3..28be2978b 100644 --- a/raster/rt_pg/rtpostgis.sql.in.c +++ b/raster/rt_pg/rtpostgis.sql.in.c @@ -3134,6 +3134,162 @@ CREATE OR REPLACE FUNCTION st_intersects(rast1 raster, rast2 raster) LANGUAGE 'sql' IMMUTABLE COST 1000; +----------------------------------------------------------------------- +-- ST_Intersects(geometry, raster) +----------------------------------------------------------------------- + +-- This function can not be STRICT +CREATE OR REPLACE FUNCTION _st_intersects(geom geometry, rast raster, nband integer DEFAULT NULL) + RETURNS boolean AS $$ + DECLARE + hasnodata boolean := TRUE; + nodata float8 := 0.0; + convexhull geometry; + geomintersect geometry; + x1w double precision := 0.0; + x2w double precision := 0.0; + y1w double precision := 0.0; + y2w double precision := 0.0; + x1 integer := 0; + x2 integer := 0; + x3 integer := 0; + x4 integer := 0; + y1 integer := 0; + y2 integer := 0; + y3 integer := 0; + y4 integer := 0; + x integer := 0; + y integer := 0; + xinc integer := 0; + yinc integer := 0; + pixelval double precision; + bintersect boolean := FALSE; + gtype text; + scale float8; + w int; + h int; + BEGIN + convexhull := ST_ConvexHull(rast); + IF nband IS NOT NULL THEN + SELECT CASE WHEN bmd.nodatavalue IS NULL THEN FALSE ELSE NULL END INTO hasnodata FROM ST_BandMetaData(rast, nband) AS bmd; + END IF; + + IF ST_Intersects(geom, convexhull) IS NOT TRUE THEN + RETURN FALSE; + ELSEIF nband IS NULL OR hasnodata IS FALSE THEN + RETURN TRUE; + END IF; + + -- Get the intersection between with the geometry. + -- We will search for withvalue pixel only in this area. + geomintersect := st_intersection(geom, convexhull); + +--RAISE NOTICE 'geomintersect=%', st_astext(geomintersect); + + -- If the intersection is empty, return false + IF st_isempty(geomintersect) THEN + RETURN FALSE; + END IF; + + -- We create a minimalistic buffer around the intersection in order to scan every pixels + -- that would touch the edge or intersect with the geometry + SELECT sqrt(scalex * scalex + skewy * skewy), width, height INTO scale, w, h FROM ST_Metadata(rast); + IF scale != 0 THEN + geomintersect := st_buffer(geomintersect, scale / 1000000); + END IF; + +--RAISE NOTICE 'geomintersect2=%', st_astext(geomintersect); + + -- Find the world coordinates of the bounding box of the intersecting area + x1w := st_xmin(geomintersect); + y1w := st_ymin(geomintersect); + x2w := st_xmax(geomintersect); + y2w := st_ymax(geomintersect); + nodata := st_bandnodatavalue(rast, nband); + +--RAISE NOTICE 'x1w=%, y1w=%, x2w=%, y2w=%', x1w, y1w, x2w, y2w; + + -- Convert world coordinates to raster coordinates + x1 := st_world2rastercoordx(rast, x1w, y1w); + y1 := st_world2rastercoordy(rast, x1w, y1w); + x2 := st_world2rastercoordx(rast, x2w, y1w); + y2 := st_world2rastercoordy(rast, x2w, y1w); + x3 := st_world2rastercoordx(rast, x1w, y2w); + y3 := st_world2rastercoordy(rast, x1w, y2w); + x4 := st_world2rastercoordx(rast, x2w, y2w); + y4 := st_world2rastercoordy(rast, x2w, y2w); + +--RAISE NOTICE 'x1=%, y1=%, x2=%, y2=%, x3=%, y3=%, x4=%, y4=%', x1, y1, x2, y2, x3, y3, x4, y4; + + -- Order the raster coordinates for the upcoming FOR loop. + x1 := int4smaller(int4smaller(int4smaller(x1, x2), x3), x4); + y1 := int4smaller(int4smaller(int4smaller(y1, y2), y3), y4); + x2 := int4larger(int4larger(int4larger(x1, x2), x3), x4); + y2 := int4larger(int4larger(int4larger(y1, y2), y3), y4); + + -- Make sure the range is not lower than 1. + -- This can happen when world coordinate are exactly on the left border + -- of the raster and that they do not span on more than one pixel. + x1 := int4smaller(int4larger(x1, 1), w); + y1 := int4smaller(int4larger(y1, 1), h); + + -- Also make sure the range does not exceed the width and height of the raster. + -- This can happen when world coordinate are exactly on the lower right border + -- of the raster. + x2 := int4smaller(x2, w); + y2 := int4smaller(y2, h); + +--RAISE NOTICE 'x1=%, y1=%, x2=%, y2=%', x1, y1, x2, y2; + + -- Search exhaustively for withvalue pixel on a moving 3x3 grid + -- (very often more efficient than searching on a mere 1x1 grid) + FOR xinc in 0..2 LOOP + FOR yinc in 0..2 LOOP + FOR x IN x1+xinc..x2 BY 3 LOOP + FOR y IN y1+yinc..y2 BY 3 LOOP + -- Check first if the pixel intersects with the geometry. Often many won't. + bintersect := NOT st_isempty(st_intersection(st_pixelaspolygon(rast, x, y), geom)); + + IF bintersect THEN + -- If the pixel really intersects, check its value. Return TRUE if with value. + pixelval := st_value(rast, nband, x, y); + IF pixelval != nodata THEN + RETURN TRUE; + END IF; + END IF; + END LOOP; + END LOOP; + END LOOP; + END LOOP; + + RETURN FALSE; + END; + $$ LANGUAGE 'plpgsql' IMMUTABLE + COST 1000; + +-- This function can not be STRICT +CREATE OR REPLACE FUNCTION st_intersects(geom geometry, rast raster, nband integer DEFAULT NULL) + RETURNS boolean AS + $$ SELECT $1 && $2::geometry AND _st_intersects($1, $2, $3); $$ + LANGUAGE 'sql' IMMUTABLE + COST 1000; + +----------------------------------------------------------------------- +-- ST_Intersects(raster, geometry) +----------------------------------------------------------------------- + +CREATE OR REPLACE FUNCTION st_intersects(rast raster, geom geometry, nband integer DEFAULT NULL) + RETURNS boolean + AS $$ SELECT $1::geometry && $2 AND _st_intersects($2, $1, $3) $$ + LANGUAGE 'sql' IMMUTABLE + COST 1000; + +CREATE OR REPLACE FUNCTION st_intersects(rast raster, nband integer, geom geometry) + RETURNS boolean + AS $$ SELECT $1::geometry && $3 AND _st_intersects($3, $1, $2) $$ + LANGUAGE 'sql' IMMUTABLE + COST 1000; + ----------------------------------------------------------------------- -- ST_Overlaps(raster, raster) ----------------------------------------------------------------------- @@ -3200,6 +3356,28 @@ CREATE OR REPLACE FUNCTION st_contains(rast1 raster, rast2 raster) LANGUAGE 'sql' IMMUTABLE COST 1000; +----------------------------------------------------------------------- +-- ST_Within(raster, raster) +----------------------------------------------------------------------- + +CREATE OR REPLACE FUNCTION _st_within(rast1 raster, nband1 integer, rast2 raster, nband2 integer) + RETURNS boolean + AS $$ SELECT _st_contains($3, $4, $1, $2) $$ + LANGUAGE 'sql' IMMUTABLE + COST 1000; + +CREATE OR REPLACE FUNCTION st_within(rast1 raster, nband1 integer, rast2 raster, nband2 integer) + RETURNS boolean + AS $$ SELECT $1 && $3 AND CASE WHEN $2 IS NULL OR $4 IS NULL THEN st_within(st_convexhull($1), st_convexhull($3)) ELSE _st_contains($3, $4, $1, $2) END $$ + LANGUAGE 'sql' IMMUTABLE + COST 1000; + +CREATE OR REPLACE FUNCTION st_within(rast1 raster, rast2 raster) + RETURNS boolean + AS $$ SELECT st_within($1, NULL::integer, $2, NULL::integer) $$ + LANGUAGE 'sql' IMMUTABLE + COST 1000; + ----------------------------------------------------------------------- -- ST_Intersection(geometry, raster) in geometry-space ----------------------------------------------------------------------- diff --git a/raster/test/regress/rt_intersects.sql b/raster/test/regress/rt_intersects.sql index 26b9ce6bb..3458b59e8 100644 --- a/raster/test/regress/rt_intersects.sql +++ b/raster/test/regress/rt_intersects.sql @@ -1,8 +1,15 @@ +SET client_min_messages TO warning; + DROP TABLE IF EXISTS raster_intersects_rast; +DROP TABLE IF EXISTS raster_intersects_geom; CREATE TABLE raster_intersects_rast ( rid integer, rast raster ); +CREATE TABLE raster_intersects_geom ( + gid integer, + geom geometry +); CREATE OR REPLACE FUNCTION make_test_raster(rid integer, width integer DEFAULT 2, height integer DEFAULT 2, ul_x double precision DEFAULT 0, ul_y double precision DEFAULT 0, skew_x double precision DEFAULT 0, skew_y double precision DEFAULT 0) RETURNS void AS $$ @@ -169,4 +176,261 @@ JOIN raster_intersects_rast r2 ON r1.rid != r2.rid WHERE r1.rid = 0; +-- point +INSERT INTO raster_intersects_geom VALUES ( + 1, ( + SELECT ST_SetSRID(ST_MakePoint(0, 0), 0) + ) +), ( + 2, ( + SELECT ST_SetSRID(ST_MakePoint(0.1, 0.1), 0) + ) +), ( + 3, ( + SELECT ST_SetSRID(ST_MakePoint(-0.1, -0.1), 0) + ) +), ( + 4, ( + SELECT ST_SetSRID(ST_MakePoint(-1, -1), 0) + ) +), ( + 5, ( + SELECT ST_SetSRID(ST_MakePoint(-1.1, -1), 0) + ) +), ( + 6, ( + SELECT ST_SetSRID(ST_MakePoint(-1, -1.1), 0) + ) +), ( + 7, ( + SELECT ST_SetSRID(ST_MakePoint(-1.5, -1.5), 0) + ) +), ( + 8, ( + SELECT ST_SetSRID(ST_MakePoint(3, 3), 0) + ) +); + +-- multipoint +INSERT INTO raster_intersects_geom VALUES ( + 11, ( + SELECT ST_Collect(geom) FROM raster_intersects_geom WHERE gid BETWEEN 1 AND 10 + ) +), ( + 12, ( + SELECT ST_Collect(geom) FROM raster_intersects_geom WHERE gid BETWEEN 3 AND 10 + ) +), ( + 13, ( + SELECT ST_Collect(geom) FROM raster_intersects_geom WHERE gid BETWEEN 4 AND 10 + ) +), ( + 14, ( + SELECT ST_Collect(geom) FROM raster_intersects_geom WHERE gid BETWEEN 5 AND 10 + ) +), ( + 15, ( + SELECT ST_Collect(geom) FROM raster_intersects_geom WHERE gid BETWEEN 6 AND 10 + ) +); + +-- linestring +INSERT INTO raster_intersects_geom VALUES ( + 21, ( + SELECT ST_SetSRID(ST_MakeLine(ARRAY[ + ST_MakePoint(1, 1), + ST_MakePoint(1, 0) + ]), 0) + ) +), ( + 22, ( + SELECT ST_SetSRID(ST_MakeLine(ARRAY[ + ST_MakePoint(-1, -1), + ST_MakePoint(1, 1), + ST_MakePoint(1, 0) + ]), 0) + ) +), ( + 23, ( + SELECT ST_SetSRID(ST_MakeLine(ARRAY[ + ST_MakePoint(-1, -1), + ST_MakePoint(-1, 1), + ST_MakePoint(1, 1), + ST_MakePoint(1, -1) + ]), 0) + ) +), ( + 24, ( + SELECT ST_SetSRID(ST_MakeLine(ARRAY[ + ST_MakePoint(-1.1, 1.1), + ST_MakePoint(1.1, 1.1), + ST_MakePoint(1.1, -1.1), + ST_MakePoint(-1.1, -1.1), + ST_MakePoint(-1.1, 1.1) + ]), 0) + ) +), ( + 25, ( + SELECT ST_SetSRID(ST_MakeLine(ARRAY[ + ST_MakePoint(-2, 1), + ST_MakePoint(1, 2), + ST_MakePoint(2, -1), + ST_MakePoint(-1, -2), + ST_MakePoint(-2, 1) + ]), 0) + ) +), ( + 26, ( + SELECT ST_SetSRID(ST_MakeLine(ARRAY[ + ST_MakePoint(-0.5, 0.5), + ST_MakePoint(0, 0.5), + ST_MakePoint(0, 0), + ST_MakePoint(0, -0.5), + ST_MakePoint(-0.5, 0.5) + ]), 0) + ) +), ( + 27, ( + SELECT ST_SetSRID(ST_MakeLine(ARRAY[ + ST_MakePoint(0.5, 0.5), + ST_MakePoint(1, 1), + ST_MakePoint(1, 0), + ST_MakePoint(0.5, 0.5) + ]), 0) + ) +), ( + 28, ( + SELECT ST_SetSRID(ST_MakeLine(ARRAY[ + ST_MakePoint(1, 1), + ST_MakePoint(0, 2), + ST_MakePoint(1, 2), + ST_MakePoint(1, 1) + ]), 0) + ) +), ( + 29, ( + SELECT ST_SetSRID(ST_MakeLine(ARRAY[ + ST_MakePoint(0, 2), + ST_MakePoint(1, 2), + ST_MakePoint(1, 4), + ST_MakePoint(0, 2) + ]), 0) + ) +); + +-- polygon +INSERT INTO raster_intersects_geom VALUES ( + 31, ( + SELECT ST_MakePolygon(geom) FROM raster_intersects_geom WHERE gid = 24 + ) +), ( + 32, ( + SELECT ST_MakePolygon(geom) FROM raster_intersects_geom WHERE gid = 25 + ) +), ( + 33, ( + SELECT ST_MakePolygon(geom) FROM raster_intersects_geom WHERE gid = 26 + ) +), ( + 34, ( + SELECT ST_MakePolygon(geom) FROM raster_intersects_geom WHERE gid = 27 + ) +), ( + 35, ( + SELECT ST_MakePolygon(geom) FROM raster_intersects_geom WHERE gid = 28 + ) +), ( + 36, ( + SELECT ST_MakePolygon(geom) FROM raster_intersects_geom WHERE gid = 29 + ) +); + +-- multipolygon +INSERT INTO raster_intersects_geom VALUES ( + 41, ( + SELECT ST_Multi(ST_Union(geom)) FROM raster_intersects_geom WHERE gid BETWEEN 31 and 40 + ) +), ( + 42, ( + SELECT ST_Multi(ST_Union(geom)) FROM raster_intersects_geom WHERE gid BETWEEN 32 and 40 + ) +), ( + 43, ( + SELECT ST_Multi(ST_Union(geom)) FROM raster_intersects_geom WHERE gid BETWEEN 33 and 40 + ) +), ( + 44, ( + SELECT ST_Multi(ST_Union(geom)) FROM raster_intersects_geom WHERE gid BETWEEN 34 and 40 + ) +), ( + 45, ( + SELECT ST_Multi(ST_Union(geom)) FROM raster_intersects_geom WHERE gid BETWEEN 35 and 40 + ) +), ( + 46, ( + SELECT ST_Multi(ST_Union(geom)) FROM raster_intersects_geom WHERE gid BETWEEN 36 and 40 + ) +); + +SELECT + '2.1', + r1.rid, + g1.gid, + ST_GeometryType(g1.geom), + ST_Intersects(r1.rast, g1.geom) +FROM raster_intersects_rast r1 +CROSS JOIN raster_intersects_geom g1 +WHERE r1.rid = 0; + +SELECT + '2.2', + r1.rid, + g1.gid, + ST_GeometryType(g1.geom), + ST_Intersects(g1.geom, r1.rast) +FROM raster_intersects_rast r1 +CROSS JOIN raster_intersects_geom g1 +WHERE r1.rid = 0; + +SELECT + '2.3', + r1.rid, + g1.gid, + ST_GeometryType(g1.geom), + ST_Intersects(r1.rast, g1.geom) +FROM raster_intersects_rast r1 +CROSS JOIN raster_intersects_geom g1 +WHERE r1.rid = 2; + +SELECT + '2.4', + r1.rid, + g1.gid, + ST_GeometryType(g1.geom), + ST_Intersects(g1.geom, r1.rast) +FROM raster_intersects_rast r1 +CROSS JOIN raster_intersects_geom g1 +WHERE r1.rid = 2; + +SELECT + '2.5', + r1.rid, + g1.gid, + ST_GeometryType(g1.geom), + ST_Intersects(r1.rast, g1.geom, 1) +FROM raster_intersects_rast r1 +CROSS JOIN raster_intersects_geom g1 +WHERE r1.rid = 0; + +SELECT + '2.6', + r1.rid, + g1.gid, + ST_GeometryType(g1.geom), + ST_Intersects(r1.rast, g1.geom, 1) +FROM raster_intersects_rast r1 +CROSS JOIN raster_intersects_geom g1 +WHERE r1.rid = 2; + DROP TABLE IF EXISTS raster_intersects_rast; +DROP TABLE IF EXISTS raster_intersects_geom; diff --git a/raster/test/regress/rt_intersects_expected b/raster/test/regress/rt_intersects_expected index 2c51879c4..ae0ac1723 100644 --- a/raster/test/regress/rt_intersects_expected +++ b/raster/test/regress/rt_intersects_expected @@ -1,4 +1,3 @@ -NOTICE: table "raster_intersects_rast" does not exist, skipping 1.1|0|1|t 1.1|0|2|t 1.1|0|10|t @@ -31,3 +30,207 @@ NOTICE: table "raster_intersects_rast" does not exist, skipping 1.2|0|30|t 1.2|0|31|t 1.2|0|32|t +2.1|0|1|ST_Point|t +2.1|0|2|ST_Point|t +2.1|0|3|ST_Point|t +2.1|0|4|ST_Point|t +2.1|0|5|ST_Point|f +2.1|0|6|ST_Point|f +2.1|0|7|ST_Point|f +2.1|0|8|ST_Point|f +2.1|0|11|ST_MultiPoint|t +2.1|0|12|ST_MultiPoint|t +2.1|0|13|ST_MultiPoint|t +2.1|0|14|ST_MultiPoint|f +2.1|0|15|ST_MultiPoint|f +2.1|0|21|ST_LineString|t +2.1|0|22|ST_LineString|t +2.1|0|23|ST_LineString|t +2.1|0|24|ST_LineString|f +2.1|0|25|ST_LineString|f +2.1|0|26|ST_LineString|t +2.1|0|27|ST_LineString|t +2.1|0|28|ST_LineString|t +2.1|0|29|ST_LineString|f +2.1|0|31|ST_Polygon|t +2.1|0|32|ST_Polygon|t +2.1|0|33|ST_Polygon|t +2.1|0|34|ST_Polygon|t +2.1|0|35|ST_Polygon|t +2.1|0|36|ST_Polygon|f +2.1|0|41|ST_MultiPolygon|t +2.1|0|42|ST_MultiPolygon|t +2.1|0|43|ST_MultiPolygon|t +2.1|0|44|ST_MultiPolygon|t +2.1|0|45|ST_MultiPolygon|t +2.1|0|46|ST_MultiPolygon|f +2.2|0|1|ST_Point|t +2.2|0|2|ST_Point|t +2.2|0|3|ST_Point|t +2.2|0|4|ST_Point|t +2.2|0|5|ST_Point|f +2.2|0|6|ST_Point|f +2.2|0|7|ST_Point|f +2.2|0|8|ST_Point|f +2.2|0|11|ST_MultiPoint|t +2.2|0|12|ST_MultiPoint|t +2.2|0|13|ST_MultiPoint|t +2.2|0|14|ST_MultiPoint|f +2.2|0|15|ST_MultiPoint|f +2.2|0|21|ST_LineString|t +2.2|0|22|ST_LineString|t +2.2|0|23|ST_LineString|t +2.2|0|24|ST_LineString|f +2.2|0|25|ST_LineString|f +2.2|0|26|ST_LineString|t +2.2|0|27|ST_LineString|t +2.2|0|28|ST_LineString|t +2.2|0|29|ST_LineString|f +2.2|0|31|ST_Polygon|t +2.2|0|32|ST_Polygon|t +2.2|0|33|ST_Polygon|t +2.2|0|34|ST_Polygon|t +2.2|0|35|ST_Polygon|t +2.2|0|36|ST_Polygon|f +2.2|0|41|ST_MultiPolygon|t +2.2|0|42|ST_MultiPolygon|t +2.2|0|43|ST_MultiPolygon|t +2.2|0|44|ST_MultiPolygon|t +2.2|0|45|ST_MultiPolygon|t +2.2|0|46|ST_MultiPolygon|f +2.3|2|1|ST_Point|t +2.3|2|2|ST_Point|t +2.3|2|3|ST_Point|f +2.3|2|4|ST_Point|f +2.3|2|5|ST_Point|f +2.3|2|6|ST_Point|f +2.3|2|7|ST_Point|f +2.3|2|8|ST_Point|t +2.3|2|11|ST_MultiPoint|t +2.3|2|12|ST_MultiPoint|t +2.3|2|13|ST_MultiPoint|t +2.3|2|14|ST_MultiPoint|t +2.3|2|15|ST_MultiPoint|t +2.3|2|21|ST_LineString|t +2.3|2|22|ST_LineString|t +2.3|2|23|ST_LineString|t +2.3|2|24|ST_LineString|t +2.3|2|25|ST_LineString|t +2.3|2|26|ST_LineString|t +2.3|2|27|ST_LineString|t +2.3|2|28|ST_LineString|t +2.3|2|29|ST_LineString|t +2.3|2|31|ST_Polygon|t +2.3|2|32|ST_Polygon|t +2.3|2|33|ST_Polygon|t +2.3|2|34|ST_Polygon|t +2.3|2|35|ST_Polygon|t +2.3|2|36|ST_Polygon|t +2.3|2|41|ST_MultiPolygon|t +2.3|2|42|ST_MultiPolygon|t +2.3|2|43|ST_MultiPolygon|t +2.3|2|44|ST_MultiPolygon|t +2.3|2|45|ST_MultiPolygon|t +2.3|2|46|ST_MultiPolygon|t +2.4|2|1|ST_Point|t +2.4|2|2|ST_Point|t +2.4|2|3|ST_Point|f +2.4|2|4|ST_Point|f +2.4|2|5|ST_Point|f +2.4|2|6|ST_Point|f +2.4|2|7|ST_Point|f +2.4|2|8|ST_Point|t +2.4|2|11|ST_MultiPoint|t +2.4|2|12|ST_MultiPoint|t +2.4|2|13|ST_MultiPoint|t +2.4|2|14|ST_MultiPoint|t +2.4|2|15|ST_MultiPoint|t +2.4|2|21|ST_LineString|t +2.4|2|22|ST_LineString|t +2.4|2|23|ST_LineString|t +2.4|2|24|ST_LineString|t +2.4|2|25|ST_LineString|t +2.4|2|26|ST_LineString|t +2.4|2|27|ST_LineString|t +2.4|2|28|ST_LineString|t +2.4|2|29|ST_LineString|t +2.4|2|31|ST_Polygon|t +2.4|2|32|ST_Polygon|t +2.4|2|33|ST_Polygon|t +2.4|2|34|ST_Polygon|t +2.4|2|35|ST_Polygon|t +2.4|2|36|ST_Polygon|t +2.4|2|41|ST_MultiPolygon|t +2.4|2|42|ST_MultiPolygon|t +2.4|2|43|ST_MultiPolygon|t +2.4|2|44|ST_MultiPolygon|t +2.4|2|45|ST_MultiPolygon|t +2.4|2|46|ST_MultiPolygon|t +2.5|0|1|ST_Point|t +2.5|0|2|ST_Point|t +2.5|0|3|ST_Point|t +2.5|0|4|ST_Point|t +2.5|0|5|ST_Point|f +2.5|0|6|ST_Point|f +2.5|0|7|ST_Point|f +2.5|0|8|ST_Point|f +2.5|0|11|ST_MultiPoint|t +2.5|0|12|ST_MultiPoint|t +2.5|0|13|ST_MultiPoint|t +2.5|0|14|ST_MultiPoint|f +2.5|0|15|ST_MultiPoint|f +2.5|0|21|ST_LineString|t +2.5|0|22|ST_LineString|t +2.5|0|23|ST_LineString|t +2.5|0|24|ST_LineString|f +2.5|0|25|ST_LineString|f +2.5|0|26|ST_LineString|t +2.5|0|27|ST_LineString|t +2.5|0|28|ST_LineString|t +2.5|0|29|ST_LineString|f +2.5|0|31|ST_Polygon|t +2.5|0|32|ST_Polygon|t +2.5|0|33|ST_Polygon|t +2.5|0|34|ST_Polygon|t +2.5|0|35|ST_Polygon|t +2.5|0|36|ST_Polygon|f +2.5|0|41|ST_MultiPolygon|t +2.5|0|42|ST_MultiPolygon|t +2.5|0|43|ST_MultiPolygon|t +2.5|0|44|ST_MultiPolygon|t +2.5|0|45|ST_MultiPolygon|t +2.5|0|46|ST_MultiPolygon|f +2.6|2|1|ST_Point|t +2.6|2|2|ST_Point|t +2.6|2|3|ST_Point|f +2.6|2|4|ST_Point|f +2.6|2|5|ST_Point|f +2.6|2|6|ST_Point|f +2.6|2|7|ST_Point|f +2.6|2|8|ST_Point|t +2.6|2|11|ST_MultiPoint|t +2.6|2|12|ST_MultiPoint|t +2.6|2|13|ST_MultiPoint|t +2.6|2|14|ST_MultiPoint|t +2.6|2|15|ST_MultiPoint|t +2.6|2|21|ST_LineString|t +2.6|2|22|ST_LineString|t +2.6|2|23|ST_LineString|t +2.6|2|24|ST_LineString|t +2.6|2|25|ST_LineString|t +2.6|2|26|ST_LineString|t +2.6|2|27|ST_LineString|t +2.6|2|28|ST_LineString|t +2.6|2|29|ST_LineString|t +2.6|2|31|ST_Polygon|t +2.6|2|32|ST_Polygon|t +2.6|2|33|ST_Polygon|t +2.6|2|34|ST_Polygon|t +2.6|2|35|ST_Polygon|t +2.6|2|36|ST_Polygon|t +2.6|2|41|ST_MultiPolygon|t +2.6|2|42|ST_MultiPolygon|t +2.6|2|43|ST_MultiPolygon|t +2.6|2|44|ST_MultiPolygon|t +2.6|2|45|ST_MultiPolygon|t +2.6|2|46|ST_MultiPolygon|t