From: Pierre Racine Date: Wed, 29 Feb 2012 02:47:35 +0000 (+0000) Subject: Fix for ticket 1536. Added a nodataval[] parameter to ST_Intersection and removed... X-Git-Tag: 2.0.0beta1~20 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=82dd3e883cb05c0ef1991292de5224ab1ac7cecf;p=postgis Fix for ticket 1536. Added a nodataval[] parameter to ST_Intersection and removed the otheruserfunc one. Changed the ST_Clip trimraster parameter name to crop and set it to true by default. Updated the doc. git-svn-id: http://svn.osgeo.org/postgis/trunk@9336 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/doc/reference_raster.xml b/doc/reference_raster.xml index 125f2c477..90ad48737 100644 --- a/doc/reference_raster.xml +++ b/doc/reference_raster.xml @@ -5099,8 +5099,8 @@ rid | rastbox ST_Clip - Returns the raster clipped by the input geometry. If no band is specified all bands are returned. If trimraster setting is not specified, false is assumed meaning - the output raster has the same extent as the input raster. + Returns the raster clipped by the input geometry. If no band is specified all bands are returned. If crop is not specified, true is assumed meaning + the output raster is cropped. @@ -5109,41 +5109,15 @@ rid | rastbox raster ST_Clip raster rast geometry geom - double precision[] nodata=NULL - boolean trimraster=false - - - - raster ST_Clip - raster rast - integer band - geometry geom - double precision[] nodata=NULL - boolean trimraster=false - - - - raster ST_Clip - raster rast - geometry geom - double precision nodata - boolean trimraster=false - - - - raster ST_Clip - raster rast - integer band - geometry geom - double precision nodata=NULL - boolean trimraster=false + double precision[] nodataval=NULL + boolean crop=true raster ST_Clip raster rast geometry geom - boolean trimraster + boolean crop @@ -5151,7 +5125,7 @@ rid | rastbox raster rast integer band geometry geom - boolean trimraster + boolean crop @@ -5159,10 +5133,14 @@ rid | rastbox Description - Returns a raster that is clipped by the input geometry geom. If no band is specified all bands are returned. - An array of nodata values replacing the nodata values of the input raster can be provided, one for each band. Some variants also accept a single value. If none are provided and the input raster do not have a nodata value defined, nodata values of the resulting raster are set to ST_MinPossibleValue(ST_BandPixelType(rast, band)). When the number of nodata value in the array is smaller than the number of band, the last one in the array is used for the remaining bands. If the number of nodata value is greater than the number of band, the extra nodata values are ignored. - If trimraster setting is not specified, false is assumed meaning the output raster has the same extent as the input raster. If trimraster is set to true, the new raster has the same extent as the intersection of geomand rast. + Returns a raster that is clipped by the input geometry geom. If no band is specified all bands are returned. + + Rasters resulting from ST_Clip must have a nodata value assigned for areas clipped, one for each band. If none are provided and the input raster do not have a nodata value defined, nodata values of the resulting raster are set to ST_MinPossibleValue(ST_BandPixelType(rast, band)). When the number of nodata value in the array is smaller than the number of band, the last one in the array is used for the remaining bands. If the number of nodata value is greater than the number of band, the extra nodata values are ignored. All variants accepting an array of nodata values also accept a single value which will be assigned to each band. + + If crop is not specified, true is assumed meaning the output raster is cropped to the intersection of the geomand rast extents. If crop is set to false, the new raster gets the same extent as rast. + Availability: 2.0.0 + Examples here use Massachusetts aerial data available on MassGIS site MassGIS Aerial Orthos. Coordinates are in Massachusetts State Plane Meters. @@ -5174,12 +5152,12 @@ SELECT ST_Clip(rast, 1, ) from aerials.boston WHERE rid = 4; --- Demonstrate effect of trimraster on final dimensions of raster --- Note how in trimmed final envelope is clipped to that of the clipper --- if trimraster = true -SELECT ST_XMax(ST_Envelope(ST_Clip(rast,1,clipper,true))) As xmax_w_trim, +-- Demonstrate effect of crop on final dimensions of raster +-- Note how final extent is clipped to that of the geometry +-- if crop = true +SELECT ST_XMax(ST_Envelope(ST_Clip(rast, 1, clipper, true))) As xmax_w_trim, ST_XMax(clipper) As xmax_clipper, - ST_XMax(ST_Envelope(ST_Clip(rast,1,clipper,false))) As xmax_wo_trim, + ST_XMax(ST_Envelope(ST_Clip(rast, 1, clipper, false))) As xmax_wo_trim, ST_XMax(ST_Envelope(rast)) As xmax_rast_orig FROM (SELECT rast, ST_Buffer(ST_Centroid(ST_Envelope(rast)),6) As clipper FROM aerials.boston @@ -5222,7 +5200,8 @@ WHERE rid = 6) As foo; -- Only difference is we don't specify a specific band to clip -- so all bands are clipped SELECT ST_Clip(rast, - ST_Buffer(ST_Centroid(ST_Envelope(rast)),20) + ST_Buffer(ST_Centroid(ST_Envelope(rast)), 20), + false ) from aerials.boston WHERE rid = 4; @@ -5640,33 +5619,31 @@ FROM dummy_rast; - raster ST_Intersection + setof geomval ST_Intersection raster rast geometry geom - regprocedure otheruserfunc=NULL - raster ST_Intersection + setof geomval ST_Intersection raster rast integer band_num geometry geom - regprocedure otheruserfunc=NULL raster ST_Intersection raster rast1 raster rast2 - text returnband='BOTH' - regprocedure otheruserfunc=NULL + double precision[] nodataval raster ST_Intersection raster rast1 raster rast2 - regprocedure otheruserfunc=NULL + text returnband='BOTH' + double precision[] nodataval=NULL @@ -5675,7 +5652,7 @@ FROM dummy_rast; integer band_num1 raster rast2 integer band_num2 - regprocedure otheruserfunc + double precision[] nodataval @@ -5684,8 +5661,8 @@ FROM dummy_rast; integer band_num1 raster rast2 integer band_num2 - text returnband - regprocedure otheruserfunc=NULL + text returnband='BOTH' + double precision[] nodataval=NULL @@ -5693,27 +5670,27 @@ FROM dummy_rast; Description - Returns a raster or a set of geometry-pixelvalue pairs representing the shared portion of two rasters or the geometrical intersection of a vectorization of the raster and a geometry. The order in which the geometry or the raster are passed determine if the operation is performed in vector or raster world. + Returns a raster or a set of geometry-pixelvalue pairs representing the shared portion of two rasters or the geometrical intersection of a vectorization of the raster and a geometry. - The first variant, returning a setof geomval, works in vector world. The raster is first vectorized (using ST_DumpAsPolygon) into a set of geomval rows and those rows are then intersected with the geometry using the ST_Intersection(geometry, geometry) PostGIS function. Geometries intersecting only with a nodata value area of a raster returns an empty geometry. They are normally excluded from the results by the proper usage of ST_Intersect in the WHERE clause. + The first three variants, returning a setof geomval, works in vector space. The raster is first vectorized (using ST_DumpAsPolygon) into a set of geomval rows and those rows are then intersected with the geometry using the ST_Intersection(geometry, geometry) PostGIS function. Geometries intersecting only with a nodata value area of a raster returns an empty geometry. They are normally excluded from the results by the proper usage of ST_Intersect in the WHERE clause. You can access the geometry and the value parts of the resulting set of geomval by surrounding them with parenthesis and adding '.geom' or '.val' at the end of the expression. e.g. (ST_Intersection(rast, geom)).geom - The other variants, returning a raster, works in raster world. They are using the two rasters version of ST_MapAlgebraExpr to perform the intersection. In the variants taking a geometry, the geometry is rasterized (using ST_AsRaster) before being passed to ST_MapAlgebraExpr. + The other variants, returning a raster, works in raster space. They are using the two rasters version of ST_MapAlgebraExpr to perform the intersection. - The extent of the resulting raster corresponds to the geometrical intersection of the two raster extents. The resulting raster includes the bands from the 'FIRST', 'SECOND' or 'BOTH' rasters following what is passed as the returnband parameter. Nodata value areas present in any band results in nodata value areas in every bands of the result. In other word, any pixel intersecting with a nodata value pixel in the other raster a nodata value pixel in the resulting band if it is returned. + The extent of the resulting raster corresponds to the geometrical intersection of the two raster extents. The resulting raster includes 'BAND1', 'BAND2' or 'BOTH' bands, following what is passed as the returnband parameter. Nodata value areas present in any band results in nodata value areas in every bands of the result. In other words, any pixel intersecting with a nodata value pixel becomes a nodata value pixel in the result. - You can optionally produce a one band raster and control the values it contains by providing a custom function of type regprocedure accepting two double precision values. See for an example on how to do that. + Rasters resulting from ST_Intersection must have a nodata value assigned for areas not intersecting. You can define or replace the nodata value for any resulting band by providing a nodataval[] array of one or two nodata values depending if you request 'BAND1', 'BAND2' or 'BOTH' bands. The first value in the array replace the nodata value in the first band and the second value replace the nodata value in the second band. If one input band do not have a nodata value defined and none are provided as an array, one is chosen using the ST_MinPossibleValue function. All variant accepting an array of nodata value can also accept a single value which will be assigned to each requested band. In all variants, if no band number is specified band 1 is assumed. To get more control on the resulting extent or on what to return when encountering a nodata value, use the two rasters version of . - The variants returning a raster and taking a geometry are very similar to ST_Clip with the exception that ST_Clip() works on multiple band rasters and never returns a band corresponding to the rasterized geometry. ST_Intersection(raster, geometry) works only on one band and may return a band corresponding to the rasterized geometry (when 'BOTH' or 'SECOND' are passed as the returnband parameter). + To compute the intersection of a raster band with a geometry in raster space, use - ST_Intersection() Should be used in conjunction with ST_Intersects and an index on the raster column or the geometry column. + ST_Intersection should be used in conjunction with ST_Intersects and an index on the raster column and/or the geometry column. - Enhanced: 2.0.0 - Intersection in the raster world was introduced. In earlier pre-2.0.0 versions, only intersection performed in vector world were supported. + Enhanced: 2.0.0 - Intersection in the raster space was introduced. In earlier pre-2.0.0 versions, only intersection performed in vector space were supported. @@ -6614,7 +6591,7 @@ SELECT ST_MapAlgebraFct(m1.rast, 1, m1.rast, 3, pixeltype - The resulting pixel type of the output raster. Must be one listed in or left out or set to NULL. If not passed in or set to NULL, will default to the pixeltype of the rast. Results are truncated if they are larger than what is allowed for the pixeltype + The resulting pixel type of the output raster. Must be one listed in or left out or set to NULL. If not passed in or set to NULL, will default to the pixeltype of the rast. Results are truncated if they are larger than what is allowed for the pixeltype. ngbwidth diff --git a/raster/rt_pg/rtpostgis.sql.in.c b/raster/rt_pg/rtpostgis.sql.in.c index 244b5915b..b1adfd59f 100644 --- a/raster/rt_pg/rtpostgis.sql.in.c +++ b/raster/rt_pg/rtpostgis.sql.in.c @@ -3155,7 +3155,6 @@ CREATE OR REPLACE FUNCTION st_intersects(geom geometry, rast raster, nband integ ----------------------------------------------------------------------- -- ST_Intersection (geometry, raster in vector space) ----------------------------------------------------------------------- - CREATE OR REPLACE FUNCTION st_intersection(geomin geometry, rast raster, band integer DEFAULT 1) RETURNS SETOF geomval AS $$ DECLARE @@ -3191,46 +3190,54 @@ CREATE OR REPLACE FUNCTION st_intersection(geomin geometry, rast raster, band in $$ LANGUAGE 'plpgsql' IMMUTABLE STRICT; +CREATE OR REPLACE FUNCTION st_intersection(rast raster, band integer, geomin geometry) + RETURNS SETOF geomval AS + $$ SELECT st_intersection($3, $1, $2) $$ + LANGUAGE 'sql' STABLE; + +CREATE OR REPLACE FUNCTION st_intersection(rast raster, geomin geometry) + RETURNS SETOF geomval AS + $$ SELECT st_intersection($2, $1, 1) $$ + LANGUAGE 'sql' STABLE; + ----------------------------------------------------------------------- -- ST_Intersection (2-raster in raster space) ----------------------------------------------------------------------- - CREATE OR REPLACE FUNCTION st_intersection( rast1 raster, band1 int, rast2 raster, band2 int, returnband text DEFAULT 'BOTH', - otheruserfunc regprocedure DEFAULT NULL + nodataval double precision[] DEFAULT NULL ) RETURNS raster AS $$ DECLARE rtn raster; _returnband text; + newnodata1 float8; + newnodata2 float8; BEGIN - -- returnband + newnodata1 := coalesce(nodataval[1], ST_BandNodataValue(rast1, band1), ST_MinPossibleValue(ST_BandPixelType(rast1, band1))); + newnodata2 := coalesce(nodataval[2], ST_BandNodataValue(rast2, band2), ST_MinPossibleValue(ST_BandPixelType(rast2, band2))); + _returnband := upper(returnband); - IF _returnband NOT IN ('FIRST', 'SECOND', 'BOTH', 'OTHER') THEN - RAISE EXCEPTION 'Unknown value provided for returnband: %', returnband; - RETURN NULL; - END IF; - - -- returnband is OTHER, otheruserfunc provided? - IF _returnband = 'OTHER' AND otheruserfunc IS NULL THEN - RAISE EXCEPTION 'Function must be provided for otheruserfunc if return band is OTHER'; - RETURN NULL; - END IF; rtn := NULL; CASE - WHEN _returnband = 'FIRST' THEN - rtn := ST_MapAlgebraExpr(rast1, band1, rast2, band2, '[rast1.val]', ST_BandPixelType(rast1, band1), 'INTERSECTION'); - WHEN _returnband = 'SECOND' THEN - rtn := ST_MapAlgebraExpr(rast1, band1, rast2, band2, '[rast2.val]', ST_BandPixelType(rast2, band2), 'INTERSECTION'); - WHEN _returnband = 'OTHER' THEN - rtn := ST_MapAlgebraFct(rast1, band1, rast2, band2, otheruserfunc, NULL, 'INTERSECTION'); - ELSE -- BOTH - rtn := ST_MapAlgebraExpr(rast1, band1, rast2, band2, '[rast1.val]', ST_BandPixelType(rast1, band1), 'INTERSECTION'); - rtn := ST_AddBand(rtn, ST_MapAlgebraExpr(rast1, band1, rast2, band2, '[rast2.val]', ST_BandPixelType(rast2, band2), 'INTERSECTION')); + WHEN _returnband = 'BAND1' THEN + rtn := ST_MapAlgebraExpr(rast1, band1, rast2, band2, '[rast1.val]', ST_BandPixelType(rast1, band1), 'INTERSECTION', newnodata1::text, newnodata1::text, newnodata1); + rtn := ST_SetBandNodataValue(rtn, 1, newnodata1); + WHEN _returnband = 'BAND2' THEN + rtn := ST_MapAlgebraExpr(rast1, band1, rast2, band2, '[rast2.val]', ST_BandPixelType(rast2, band2), 'INTERSECTION', newnodata2::text, newnodata2::text, newnodata2); + rtn := ST_SetBandNodataValue(rtn, 1, newnodata2); + WHEN _returnband = 'BOTH' THEN + rtn := ST_MapAlgebraExpr(rast1, band1, rast2, band2, '[rast1.val]', ST_BandPixelType(rast1, band1), 'INTERSECTION', newnodata1::text, newnodata1::text, newnodata1); + rtn := ST_SetBandNodataValue(rtn, 1, newnodata1); + rtn := ST_AddBand(rtn, ST_MapAlgebraExpr(rast1, band1, rast2, band2, '[rast2.val]', ST_BandPixelType(rast2, band2), 'INTERSECTION', newnodata2::text, newnodata2::text, newnodata2)); + rtn := ST_SetBandNodataValue(rtn, 2, newnodata2); + ELSE + RAISE EXCEPTION 'Unknown value provided for returnband: %', returnband; + RETURN NULL; END CASE; RETURN rtn; @@ -3240,17 +3247,37 @@ CREATE OR REPLACE FUNCTION st_intersection( CREATE OR REPLACE FUNCTION st_intersection( rast1 raster, band1 int, rast2 raster, band2 int, - otheruserfunc regprocedure + returnband text, + nodataval double precision +) + RETURNS raster AS + $$ SELECT st_intersection($1, $2, $3, $4, $5, ARRAY[$6, $6]) $$ + LANGUAGE 'sql' STABLE; + +CREATE OR REPLACE FUNCTION st_intersection( + rast1 raster, band1 int, + rast2 raster, band2 int, + nodataval double precision[] ) RETURNS raster AS - $$ SELECT st_intersection($1, $2, $3, $4, 'OTHER', $5) $$ + $$ SELECT st_intersection($1, $2, $3, $4, 'BOTH', $5) $$ LANGUAGE 'sql' STABLE; +CREATE OR REPLACE FUNCTION st_intersection( + rast1 raster, band1 int, + rast2 raster, band2 int, + nodataval double precision +) + RETURNS raster AS + $$ SELECT st_intersection($1, $2, $3, $4, 'BOTH', ARRAY[$5, $5]) $$ + LANGUAGE 'sql' STABLE; + +-- Variants without band number CREATE OR REPLACE FUNCTION st_intersection( rast1 raster, rast2 raster, returnband text DEFAULT 'BOTH', - otheruserfunc regprocedure DEFAULT NULL + nodataval double precision[] DEFAULT NULL ) RETURNS raster AS $$ SELECT st_intersection($1, 1, $2, 1, $3, $4) $$ @@ -3259,44 +3286,29 @@ CREATE OR REPLACE FUNCTION st_intersection( CREATE OR REPLACE FUNCTION st_intersection( rast1 raster, rast2 raster, - otheruserfunc regprocedure + returnband text, + nodataval double precision ) RETURNS raster AS - $$ SELECT st_intersection($1, 1, $2, 1, 'OTHER', $3) $$ + $$ SELECT st_intersection($1, 1, $2, 1, $3, ARRAY[$4, $4]) $$ LANGUAGE 'sql' STABLE; ------------------------------------------------------------------------ --- ST_Intersection (raster, geometry in raster space) ------------------------------------------------------------------------ - CREATE OR REPLACE FUNCTION st_intersection( - rast raster, band int, - geom geometry, - otheruserfunc regprocedure DEFAULT NULL + rast1 raster, + rast2 raster, + nodataval double precision[] ) - RETURNS raster AS $$ - DECLARE - rtn raster; - BEGIN - rtn := NULL; - - IF $4 IS NULL THEN - rtn := st_intersection($1, $2, ST_AsRaster($3, $1), 1, 'FIRST'); - ELSE - rtn := st_intersection($1, $2, ST_AsRaster($3, $1), 1, 'OTHER', $4); - END IF; - - RETURN rtn; - END; - $$ LANGUAGE 'plpgsql' STABLE; + RETURNS raster AS + $$ SELECT st_intersection($1, 1, $2, 1, 'BOTH', $3) $$ + LANGUAGE 'sql' STABLE; CREATE OR REPLACE FUNCTION st_intersection( - rast raster, - geom geometry, - otheruserfunc regprocedure DEFAULT NULL + rast1 raster, + rast2 raster, + nodataval double precision ) RETURNS raster AS - $$ SELECT st_intersection($1, 1, $2, $3) $$ + $$ SELECT st_intersection($1, 1, $2, 1, 'BOTH', ARRAY[$3, $3]) $$ LANGUAGE 'sql' STABLE; ----------------------------------------------------------------------- @@ -3428,27 +3440,21 @@ CREATE AGGREGATE ST_Union(raster, integer, text) ( ); ------------------------------------------------------------------- --- ST_Clip(rast raster, band int, geom geometry, nodata float8 DEFAULT null, trimraster boolean DEFAULT false) +-- ST_Clip(rast raster, band int, geom geometry, nodata float8 DEFAULT null, crop boolean DEFAULT true) -- Clip the values of a raster to the shape of a polygon. -- -- rast - raster to be clipped -- band - limit the result to only one band -- geom - geometry defining the shape to clip the raster -- nodata - define (if there is none defined) or replace the raster nodata value with this value --- trimraster - limit the extent of the result to the extent of the geometry --- Todo: --- test point --- test line --- test polygon smaller than pixel --- test and optimize raster totally included in polygon - +-- crop - limit the extent of the result to the extent of the geometry ----------------------------------------------------------------------- -- ST_Clip ----------------------------------------------------------------------- --- Nodata as array series +-- nodataval as array series -- Major variant -CREATE OR REPLACE FUNCTION st_clip(rast raster, band int, geom geometry, nodata float8[] DEFAULT NULL, trimraster boolean DEFAULT FALSE) +CREATE OR REPLACE FUNCTION st_clip(rast raster, band int, geom geometry, nodataval double precision[] DEFAULT NULL, crop boolean DEFAULT TRUE) RETURNS raster AS $$ DECLARE @@ -3458,7 +3464,7 @@ CREATE OR REPLACE FUNCTION st_clip(rast raster, band int, geom geometry, nodata bandstart int; bandend int; newextent text; - newnodata float8; + newnodataval double precision; newpixtype text; bandi int; BEGIN @@ -3481,57 +3487,55 @@ CREATE OR REPLACE FUNCTION st_clip(rast raster, band int, geom geometry, nodata END IF; newpixtype := ST_BandPixelType(rast, bandstart); - newnodata := coalesce(nodata[1], ST_BandNodataValue(rast, bandstart), ST_MinPossibleValue(newpixtype)); - newextent := CASE WHEN trimraster THEN 'INTERSECTION' ELSE 'FIRST' END; + newnodataval := coalesce(nodataval[1], ST_BandNodataValue(rast, bandstart), ST_MinPossibleValue(newpixtype)); + newextent := CASE WHEN crop THEN 'INTERSECTION' ELSE 'FIRST' END; ---RAISE NOTICE 'newextent=%', newextent; -- Convert the geometry to a raster - geomrast := ST_AsRaster(geom, rast, ST_BandPixelType(rast, band), 1, newnodata); + geomrast := ST_AsRaster(geom, rast, ST_BandPixelType(rast, band), 1, newnodataval); -- Compute the first raster band - newrast := ST_MapAlgebraExpr(rast, bandstart, geomrast, 1, '[rast1.val]', newpixtype, newextent, newnodata::text, newnodata::text, newnodata); - -- Set the newnodata - newrast := ST_SetBandNodataValue(newrast, bandstart, newnodata); + newrast := ST_MapAlgebraExpr(rast, bandstart, geomrast, 1, '[rast1.val]', newpixtype, newextent, newnodataval::text, newnodataval::text, newnodataval); + -- Set the newnodataval + newrast := ST_SetBandNodataValue(newrast, bandstart, newnodataval); FOR bandi IN bandstart+1..bandend LOOP ---RAISE NOTICE 'bandi=%', bandi; -- for each band we must determine the nodata value newpixtype := ST_BandPixelType(rast, bandi); - newnodata := coalesce(nodata[bandi], nodata[array_upper(nodata, 1)], ST_BandNodataValue(rast, bandi), ST_MinPossibleValue(newpixtype)); - newrast := ST_AddBand(newrast, ST_MapAlgebraExpr(rast, bandi, geomrast, 1, '[rast1.val]', newpixtype, newextent, newnodata::text, newnodata::text, newnodata)); - newrast := ST_SetBandNodataValue(newrast, bandi, newnodata); + newnodataval := coalesce(nodataval[bandi], nodataval[array_upper(nodataval, 1)], ST_BandNodataValue(rast, bandi), ST_MinPossibleValue(newpixtype)); + newrast := ST_AddBand(newrast, ST_MapAlgebraExpr(rast, bandi, geomrast, 1, '[rast1.val]', newpixtype, newextent, newnodataval::text, newnodataval::text, newnodataval)); + newrast := ST_SetBandNodataValue(newrast, bandi, newnodataval); END LOOP; RETURN newrast; END; $$ LANGUAGE 'plpgsql' STABLE; --- Variant defaulting to all bands -CREATE OR REPLACE FUNCTION st_clip(rast raster, geom geometry, nodata float8[] DEFAULT NULL, trimraster boolean DEFAULT FALSE) +-- Nodata values as integer series +CREATE OR REPLACE FUNCTION st_clip(rast raster, band int, geom geometry, nodataval double precision, crop boolean DEFAULT TRUE) RETURNS raster AS - $$ SELECT ST_Clip($1, NULL, $2, $3, $4) $$ + $$ SELECT ST_Clip($1, $2, $3, ARRAY[$4], $5) $$ LANGUAGE 'SQL' STABLE; --- Nodata values as integer series -CREATE OR REPLACE FUNCTION st_clip(rast raster, band int, geom geometry, nodata float8 DEFAULT NULL, trimraster boolean DEFAULT FALSE) +-- Variant defaulting nodataval to the one of the raster or the min possible value +CREATE OR REPLACE FUNCTION st_clip(rast raster, band int, geom geometry, crop boolean) RETURNS raster AS - $$ SELECT ST_Clip($1, $2, $3, ARRAY[$4], $5) $$ + $$ SELECT ST_Clip($1, $2, $3, null::float8[], $4) $$ LANGUAGE 'SQL' STABLE; -- Variant defaulting to all bands -CREATE OR REPLACE FUNCTION st_clip(rast raster, geom geometry, nodata float8, trimraster boolean DEFAULT FALSE) +CREATE OR REPLACE FUNCTION st_clip(rast raster, geom geometry, nodataval double precision[] DEFAULT NULL, crop boolean DEFAULT TRUE) RETURNS raster AS - $$ SELECT ST_Clip($1, NULL, $2, ARRAY[$3], $4) $$ + $$ SELECT ST_Clip($1, NULL, $2, $3, $4) $$ LANGUAGE 'SQL' STABLE; --- Variant defaulting nodata to the one of the raster or the min possible value -CREATE OR REPLACE FUNCTION st_clip(rast raster, band int, geom geometry, trimraster boolean) +-- Variant defaulting to all bands +CREATE OR REPLACE FUNCTION st_clip(rast raster, geom geometry, nodataval double precision, crop boolean DEFAULT TRUE) RETURNS raster AS - $$ SELECT ST_Clip($1, $2, $3, null::float8[], $4) $$ + $$ SELECT ST_Clip($1, NULL, $2, ARRAY[$3], $4) $$ LANGUAGE 'SQL' STABLE; --- Variant defaulting nodata to the one of the raster or the min possible value and returning all bands -CREATE OR REPLACE FUNCTION st_clip(rast raster, geom geometry, trimraster boolean) +-- Variant defaulting nodataval to the one of the raster or the min possible value and returning all bands +CREATE OR REPLACE FUNCTION st_clip(rast raster, geom geometry, crop boolean) RETURNS raster AS $$ SELECT ST_Clip($1, NULL, $2, null::float8[], $3) $$ LANGUAGE 'SQL' STABLE; diff --git a/raster/test/regress/rt_clip.sql b/raster/test/regress/rt_clip.sql index 4b412c72d..418602354 100644 --- a/raster/test/regress/rt_clip.sql +++ b/raster/test/regress/rt_clip.sql @@ -77,7 +77,7 @@ DROP FUNCTION make_test_raster(integer, integer, integer, double precision, doub -- Test 1 without trimming, without defining a nodata value INSERT INTO raster_clip_out -SELECT 1, rid, gid, ST_Clip(rast, geom) +SELECT 1, rid, gid, ST_Clip(rast, geom, false) FROM raster_clip, geom_clip; -- Test 2 with trimming, without defining a nodata value @@ -85,9 +85,9 @@ INSERT INTO raster_clip_out SELECT 2, rid, gid, ST_Clip(rast, geom, true) FROM raster_clip, geom_clip; --- Test 3 with trimming, defining a nodata value +-- Test 3 without trimming, defining a nodata value INSERT INTO raster_clip_out -SELECT 3, rid, gid, ST_Clip(rast, geom, ARRAY[255, 254, 253]) +SELECT 3, rid, gid, ST_Clip(rast, geom, ARRAY[255, 254, 253], false) FROM raster_clip, geom_clip; -- Test 4 with trimming, defining a nodata value diff --git a/raster/test/regress/rt_intersection.sql b/raster/test/regress/rt_intersection.sql index bedd07bce..c82d9228e 100644 --- a/raster/test/regress/rt_intersection.sql +++ b/raster/test/regress/rt_intersection.sql @@ -76,7 +76,7 @@ INSERT INTO raster_intersection_out INSERT INTO raster_intersection_out (SELECT r1.rid, r2.rid, ST_Intersection( - r1.rast, r2.rast, 'FIRST' + r1.rast, r2.rast, 'BAND1' ) FROM raster_intersection r1 JOIN raster_intersection r2 @@ -85,7 +85,7 @@ INSERT INTO raster_intersection_out AND r2.rid BETWEEN 1 AND 9 ) UNION ALL ( SELECT r1.rid, r2.rid, ST_Intersection( - r1.rast, r2.rast, 'FIRST' + r1.rast, r2.rast, 'BAND1' ) FROM raster_intersection r1 JOIN raster_intersection r2 @@ -96,7 +96,7 @@ INSERT INTO raster_intersection_out INSERT INTO raster_intersection_out (SELECT r1.rid, r2.rid, ST_Intersection( - r1.rast, r2.rast, 'SECOND' + r1.rast, r2.rast, 'BAND2' ) FROM raster_intersection r1 JOIN raster_intersection r2 @@ -105,46 +105,7 @@ INSERT INTO raster_intersection_out AND r2.rid BETWEEN 1 AND 9 ) UNION ALL ( SELECT r1.rid, r2.rid, ST_Intersection( - r1.rast, r2.rast, 'SECOND' - ) - FROM raster_intersection r1 - JOIN raster_intersection r2 - ON r1.rid != r2.rid - WHERE r1.rid = 10 - AND r2.rid BETWEEN 11 AND 19) -; - -CREATE OR REPLACE FUNCTION raster_intersection_other( - rast1 double precision, - rast2 double precision, - VARIADIC userargs text[] -) - RETURNS double precision - AS $$ - DECLARE - BEGIN - IF rast1 IS NOT NULL AND rast2 IS NOT NULL THEN - RETURN sqrt(power(rast1, 2) + power(rast2, 2)); - ELSE - RETURN NULL; - END IF; - - RETURN NULL; - END; - $$ LANGUAGE 'plpgsql' IMMUTABLE; - -INSERT INTO raster_intersection_out - (SELECT r1.rid, r2.rid, ST_Intersection( - r1.rast, r2.rast, 'raster_intersection_other(double precision, double precision, text[])'::regprocedure - ) - FROM raster_intersection r1 - JOIN raster_intersection r2 - ON r1.rid != r2.rid - WHERE r1.rid = 0 - AND r2.rid BETWEEN 1 AND 9 - ) UNION ALL ( - SELECT r1.rid, r2.rid, ST_Intersection( - r1.rast, r2.rast, 'raster_intersection_other(double precision, double precision, text[])'::regprocedure + r1.rast, r2.rast, 'BAND2' ) FROM raster_intersection r1 JOIN raster_intersection r2 @@ -182,6 +143,11 @@ FROM ( FROM raster_intersection_out ) AS r; +-- Display the pixels and the values of the resulting rasters +SELECT rid1, rid2, band, (gvxy).x, (gvxy).y, (gvxy).val, ST_AsText((gvxy).geom) geom +FROM (SELECT rid1, rid2, band, ST_PixelAsPolygons(rast, band) gvxy + FROM raster_intersection_out, generate_series(1, 2) band +) foo; + DROP TABLE IF EXISTS raster_intersection; DROP TABLE IF EXISTS raster_intersection_out; -DROP FUNCTION raster_intersection_other(rast1 double precision, rast2 double precision, VARIADIC userargs text[]) diff --git a/raster/test/regress/rt_intersection_expected b/raster/test/regress/rt_intersection_expected index 6df979826..d5857da27 100644 --- a/raster/test/regress/rt_intersection_expected +++ b/raster/test/regress/rt_intersection_expected @@ -22,11 +22,147 @@ 10|12|-1.900|-1.000|2|2|1.000|1.000|0.100|0.100|0|1|8BUI|t|0.000|3.000|3.000 10|13|0.000|-1.800|2|2|1.000|1.000|0.100|0.100|0|1|8BUI|t|0.000|4.000|4.000 10|14||||||||||||||| -0|1|0.000|0.000|2|2|1.000|1.000|0.000|0.000|0|1|8BUI|t|0.000|2.000|2.000 -0|2|1.000|-1.000|1|2|1.000|1.000|0.000|0.000|0|1|8BUI|t|0.000|3.000|3.000 -0|3|1.000|1.000|1|1|1.000|1.000|0.000|0.000|0|1|8BUI|t|0.000|4.000|4.000 -0|4|0.000|0.000|0|0|0.000|0.000|0.000|0.000|0|0||||| -10|11|-0.900|-0.900|3|3|1.000|1.000|0.100|0.100|0|1|8BUI|t|0.000|2.000|2.000 -10|12|-1.900|-1.000|2|2|1.000|1.000|0.100|0.100|0|1|8BUI|t|0.000|3.000|3.000 -10|13|0.000|-1.800|2|2|1.000|1.000|0.100|0.100|0|1|8BUI|t|0.000|4.000|4.000 -10|14||||||||||||||| +0|1|1|1|1|1|POLYGON((0 0,1 0,1 1,0 1,0 0)) +0|1|1|1|2|1|POLYGON((0 1,1 1,1 2,0 2,0 1)) +0|1|1|2|1|1|POLYGON((1 0,2 0,2 1,1 1,1 0)) +0|1|1|2|2|1|POLYGON((1 1,2 1,2 2,1 2,1 1)) +0|2|1|1|1|1|POLYGON((1 -1,2 -1,2 0,1 0,1 -1)) +0|2|1|1|2|1|POLYGON((1 0,2 0,2 1,1 1,1 0)) +0|3|1|1|1|1|POLYGON((1 1,2 1,2 2,1 2,1 1)) +10|11|1|1|1|1|POLYGON((-0.9 -0.9,0.1 -0.8,0.2 0.2,-0.8 0.1,-0.9 -0.9)) +10|11|1|1|2|1|POLYGON((-0.8 0.1,0.2 0.2,0.3 1.2,-0.7 1.1,-0.8 0.1)) +10|11|1|1|3|1|POLYGON((-0.7 1.1,0.3 1.2,0.4 2.2,-0.6 2.1,-0.7 1.1)) +10|11|1|2|1|1|POLYGON((0.1 -0.8,1.1 -0.7,1.2 0.3,0.2 0.2,0.1 -0.8)) +10|11|1|2|2|1|POLYGON((0.2 0.2,1.2 0.3,1.3 1.3,0.3 1.2,0.2 0.2)) +10|11|1|2|3|1|POLYGON((0.3 1.2,1.3 1.3,1.4 2.3,0.4 2.2,0.3 1.2)) +10|11|1|3|1|1|POLYGON((1.1 -0.7,2.1 -0.6,2.2 0.4,1.2 0.3,1.1 -0.7)) +10|11|1|3|2|1|POLYGON((1.2 0.3,2.2 0.4,2.3 1.4,1.3 1.3,1.2 0.3)) +10|11|1|3|3|1|POLYGON((1.3 1.3,2.3 1.4,2.4 2.4,1.4 2.3,1.3 1.3)) +10|12|1|1|1|1|POLYGON((-1.9 -1,-0.9 -0.9,-0.8 0.1,-1.8 0,-1.9 -1)) +10|12|1|1|2|1|POLYGON((-1.8 0,-0.8 0.1,-0.7 1.1,-1.7 1,-1.8 0)) +10|12|1|2|1|1|POLYGON((-0.9 -0.9,0.1 -0.8,0.2 0.2,-0.8 0.1,-0.9 -0.9)) +10|12|1|2|2|1|POLYGON((-0.8 0.1,0.2 0.2,0.3 1.2,-0.7 1.1,-0.8 0.1)) +10|13|1|1|1|1|POLYGON((0 -1.8,1 -1.7,1.1 -0.7,0.1 -0.8,0 -1.8)) +10|13|1|1|2|1|POLYGON((0.1 -0.8,1.1 -0.7,1.2 0.3,0.2 0.2,0.1 -0.8)) +10|13|1|2|1|1|POLYGON((1 -1.7,2 -1.6,2.1 -0.6,1.1 -0.7,1 -1.7)) +10|13|1|2|2|1|POLYGON((1.1 -0.7,2.1 -0.6,2.2 0.4,1.2 0.3,1.1 -0.7)) +0|1|1|1|1|1|POLYGON((0 0,1 0,1 1,0 1,0 0)) +0|1|1|1|2|1|POLYGON((0 1,1 1,1 2,0 2,0 1)) +0|1|1|2|1|1|POLYGON((1 0,2 0,2 1,1 1,1 0)) +0|1|1|2|2|1|POLYGON((1 1,2 1,2 2,1 2,1 1)) +0|2|1|1|1|1|POLYGON((1 -1,2 -1,2 0,1 0,1 -1)) +0|2|1|1|2|1|POLYGON((1 0,2 0,2 1,1 1,1 0)) +0|3|1|1|1|1|POLYGON((1 1,2 1,2 2,1 2,1 1)) +10|11|1|1|1|1|POLYGON((-0.9 -0.9,0.1 -0.8,0.2 0.2,-0.8 0.1,-0.9 -0.9)) +10|11|1|1|2|1|POLYGON((-0.8 0.1,0.2 0.2,0.3 1.2,-0.7 1.1,-0.8 0.1)) +10|11|1|1|3|1|POLYGON((-0.7 1.1,0.3 1.2,0.4 2.2,-0.6 2.1,-0.7 1.1)) +10|11|1|2|1|1|POLYGON((0.1 -0.8,1.1 -0.7,1.2 0.3,0.2 0.2,0.1 -0.8)) +10|11|1|2|2|1|POLYGON((0.2 0.2,1.2 0.3,1.3 1.3,0.3 1.2,0.2 0.2)) +10|11|1|2|3|1|POLYGON((0.3 1.2,1.3 1.3,1.4 2.3,0.4 2.2,0.3 1.2)) +10|11|1|3|1|1|POLYGON((1.1 -0.7,2.1 -0.6,2.2 0.4,1.2 0.3,1.1 -0.7)) +10|11|1|3|2|1|POLYGON((1.2 0.3,2.2 0.4,2.3 1.4,1.3 1.3,1.2 0.3)) +10|11|1|3|3|1|POLYGON((1.3 1.3,2.3 1.4,2.4 2.4,1.4 2.3,1.3 1.3)) +10|12|1|1|1|1|POLYGON((-1.9 -1,-0.9 -0.9,-0.8 0.1,-1.8 0,-1.9 -1)) +10|12|1|1|2|1|POLYGON((-1.8 0,-0.8 0.1,-0.7 1.1,-1.7 1,-1.8 0)) +10|12|1|2|1|1|POLYGON((-0.9 -0.9,0.1 -0.8,0.2 0.2,-0.8 0.1,-0.9 -0.9)) +10|12|1|2|2|1|POLYGON((-0.8 0.1,0.2 0.2,0.3 1.2,-0.7 1.1,-0.8 0.1)) +10|13|1|1|1|1|POLYGON((0 -1.8,1 -1.7,1.1 -0.7,0.1 -0.8,0 -1.8)) +10|13|1|1|2|1|POLYGON((0.1 -0.8,1.1 -0.7,1.2 0.3,0.2 0.2,0.1 -0.8)) +10|13|1|2|1|1|POLYGON((1 -1.7,2 -1.6,2.1 -0.6,1.1 -0.7,1 -1.7)) +10|13|1|2|2|1|POLYGON((1.1 -0.7,2.1 -0.6,2.2 0.4,1.2 0.3,1.1 -0.7)) +0|1|1|1|1|2|POLYGON((0 0,1 0,1 1,0 1,0 0)) +0|1|1|1|2|2|POLYGON((0 1,1 1,1 2,0 2,0 1)) +0|1|1|2|1|2|POLYGON((1 0,2 0,2 1,1 1,1 0)) +0|1|1|2|2|2|POLYGON((1 1,2 1,2 2,1 2,1 1)) +0|2|1|1|1|3|POLYGON((1 -1,2 -1,2 0,1 0,1 -1)) +0|2|1|1|2|3|POLYGON((1 0,2 0,2 1,1 1,1 0)) +0|3|1|1|1|4|POLYGON((1 1,2 1,2 2,1 2,1 1)) +10|11|1|1|1|2|POLYGON((-0.9 -0.9,0.1 -0.8,0.2 0.2,-0.8 0.1,-0.9 -0.9)) +10|11|1|1|2|2|POLYGON((-0.8 0.1,0.2 0.2,0.3 1.2,-0.7 1.1,-0.8 0.1)) +10|11|1|1|3|2|POLYGON((-0.7 1.1,0.3 1.2,0.4 2.2,-0.6 2.1,-0.7 1.1)) +10|11|1|2|1|2|POLYGON((0.1 -0.8,1.1 -0.7,1.2 0.3,0.2 0.2,0.1 -0.8)) +10|11|1|2|2|2|POLYGON((0.2 0.2,1.2 0.3,1.3 1.3,0.3 1.2,0.2 0.2)) +10|11|1|2|3|2|POLYGON((0.3 1.2,1.3 1.3,1.4 2.3,0.4 2.2,0.3 1.2)) +10|11|1|3|1|2|POLYGON((1.1 -0.7,2.1 -0.6,2.2 0.4,1.2 0.3,1.1 -0.7)) +10|11|1|3|2|2|POLYGON((1.2 0.3,2.2 0.4,2.3 1.4,1.3 1.3,1.2 0.3)) +10|11|1|3|3|2|POLYGON((1.3 1.3,2.3 1.4,2.4 2.4,1.4 2.3,1.3 1.3)) +10|12|1|1|1|3|POLYGON((-1.9 -1,-0.9 -0.9,-0.8 0.1,-1.8 0,-1.9 -1)) +10|12|1|1|2|3|POLYGON((-1.8 0,-0.8 0.1,-0.7 1.1,-1.7 1,-1.8 0)) +10|12|1|2|1|3|POLYGON((-0.9 -0.9,0.1 -0.8,0.2 0.2,-0.8 0.1,-0.9 -0.9)) +10|12|1|2|2|3|POLYGON((-0.8 0.1,0.2 0.2,0.3 1.2,-0.7 1.1,-0.8 0.1)) +10|13|1|1|1|4|POLYGON((0 -1.8,1 -1.7,1.1 -0.7,0.1 -0.8,0 -1.8)) +10|13|1|1|2|4|POLYGON((0.1 -0.8,1.1 -0.7,1.2 0.3,0.2 0.2,0.1 -0.8)) +10|13|1|2|1|4|POLYGON((1 -1.7,2 -1.6,2.1 -0.6,1.1 -0.7,1 -1.7)) +10|13|1|2|2|4|POLYGON((1.1 -0.7,2.1 -0.6,2.2 0.4,1.2 0.3,1.1 -0.7)) +0|1|2|1|1|2|POLYGON((0 0,1 0,1 1,0 1,0 0)) +0|1|2|1|2|2|POLYGON((0 1,1 1,1 2,0 2,0 1)) +0|1|2|2|1|2|POLYGON((1 0,2 0,2 1,1 1,1 0)) +0|1|2|2|2|2|POLYGON((1 1,2 1,2 2,1 2,1 1)) +0|2|2|1|1|3|POLYGON((1 -1,2 -1,2 0,1 0,1 -1)) +0|2|2|1|2|3|POLYGON((1 0,2 0,2 1,1 1,1 0)) +0|3|2|1|1|4|POLYGON((1 1,2 1,2 2,1 2,1 1)) +10|11|2|1|1|2|POLYGON((-0.9 -0.9,0.1 -0.8,0.2 0.2,-0.8 0.1,-0.9 -0.9)) +10|11|2|1|2|2|POLYGON((-0.8 0.1,0.2 0.2,0.3 1.2,-0.7 1.1,-0.8 0.1)) +10|11|2|1|3|2|POLYGON((-0.7 1.1,0.3 1.2,0.4 2.2,-0.6 2.1,-0.7 1.1)) +10|11|2|2|1|2|POLYGON((0.1 -0.8,1.1 -0.7,1.2 0.3,0.2 0.2,0.1 -0.8)) +10|11|2|2|2|2|POLYGON((0.2 0.2,1.2 0.3,1.3 1.3,0.3 1.2,0.2 0.2)) +10|11|2|2|3|2|POLYGON((0.3 1.2,1.3 1.3,1.4 2.3,0.4 2.2,0.3 1.2)) +10|11|2|3|1|2|POLYGON((1.1 -0.7,2.1 -0.6,2.2 0.4,1.2 0.3,1.1 -0.7)) +10|11|2|3|2|2|POLYGON((1.2 0.3,2.2 0.4,2.3 1.4,1.3 1.3,1.2 0.3)) +10|11|2|3|3|2|POLYGON((1.3 1.3,2.3 1.4,2.4 2.4,1.4 2.3,1.3 1.3)) +10|12|2|1|1|3|POLYGON((-1.9 -1,-0.9 -0.9,-0.8 0.1,-1.8 0,-1.9 -1)) +10|12|2|1|2|3|POLYGON((-1.8 0,-0.8 0.1,-0.7 1.1,-1.7 1,-1.8 0)) +10|12|2|2|1|3|POLYGON((-0.9 -0.9,0.1 -0.8,0.2 0.2,-0.8 0.1,-0.9 -0.9)) +10|12|2|2|2|3|POLYGON((-0.8 0.1,0.2 0.2,0.3 1.2,-0.7 1.1,-0.8 0.1)) +10|13|2|1|1|4|POLYGON((0 -1.8,1 -1.7,1.1 -0.7,0.1 -0.8,0 -1.8)) +10|13|2|1|2|4|POLYGON((0.1 -0.8,1.1 -0.7,1.2 0.3,0.2 0.2,0.1 -0.8)) +10|13|2|2|1|4|POLYGON((1 -1.7,2 -1.6,2.1 -0.6,1.1 -0.7,1 -1.7)) +10|13|2|2|2|4|POLYGON((1.1 -0.7,2.1 -0.6,2.2 0.4,1.2 0.3,1.1 -0.7)) +0|1|2|1|1||POLYGON((0 0,1 0,1 1,0 1,0 0)) +0|1|2|1|2||POLYGON((0 1,1 1,1 2,0 2,0 1)) +0|1|2|2|1||POLYGON((1 0,2 0,2 1,1 1,1 0)) +0|1|2|2|2||POLYGON((1 1,2 1,2 2,1 2,1 1)) +0|2|2|1|1||POLYGON((1 -1,2 -1,2 0,1 0,1 -1)) +0|2|2|1|2||POLYGON((1 0,2 0,2 1,1 1,1 0)) +0|3|2|1|1||POLYGON((1 1,2 1,2 2,1 2,1 1)) +10|11|2|1|1||POLYGON((-0.9 -0.9,0.1 -0.8,0.2 0.2,-0.8 0.1,-0.9 -0.9)) +10|11|2|1|2||POLYGON((-0.8 0.1,0.2 0.2,0.3 1.2,-0.7 1.1,-0.8 0.1)) +10|11|2|1|3||POLYGON((-0.7 1.1,0.3 1.2,0.4 2.2,-0.6 2.1,-0.7 1.1)) +10|11|2|2|1||POLYGON((0.1 -0.8,1.1 -0.7,1.2 0.3,0.2 0.2,0.1 -0.8)) +10|11|2|2|2||POLYGON((0.2 0.2,1.2 0.3,1.3 1.3,0.3 1.2,0.2 0.2)) +10|11|2|2|3||POLYGON((0.3 1.2,1.3 1.3,1.4 2.3,0.4 2.2,0.3 1.2)) +10|11|2|3|1||POLYGON((1.1 -0.7,2.1 -0.6,2.2 0.4,1.2 0.3,1.1 -0.7)) +10|11|2|3|2||POLYGON((1.2 0.3,2.2 0.4,2.3 1.4,1.3 1.3,1.2 0.3)) +10|11|2|3|3||POLYGON((1.3 1.3,2.3 1.4,2.4 2.4,1.4 2.3,1.3 1.3)) +10|12|2|1|1||POLYGON((-1.9 -1,-0.9 -0.9,-0.8 0.1,-1.8 0,-1.9 -1)) +10|12|2|1|2||POLYGON((-1.8 0,-0.8 0.1,-0.7 1.1,-1.7 1,-1.8 0)) +10|12|2|2|1||POLYGON((-0.9 -0.9,0.1 -0.8,0.2 0.2,-0.8 0.1,-0.9 -0.9)) +10|12|2|2|2||POLYGON((-0.8 0.1,0.2 0.2,0.3 1.2,-0.7 1.1,-0.8 0.1)) +10|13|2|1|1||POLYGON((0 -1.8,1 -1.7,1.1 -0.7,0.1 -0.8,0 -1.8)) +10|13|2|1|2||POLYGON((0.1 -0.8,1.1 -0.7,1.2 0.3,0.2 0.2,0.1 -0.8)) +10|13|2|2|1||POLYGON((1 -1.7,2 -1.6,2.1 -0.6,1.1 -0.7,1 -1.7)) +10|13|2|2|2||POLYGON((1.1 -0.7,2.1 -0.6,2.2 0.4,1.2 0.3,1.1 -0.7)) +0|1|2|1|1||POLYGON((0 0,1 0,1 1,0 1,0 0)) +0|1|2|1|2||POLYGON((0 1,1 1,1 2,0 2,0 1)) +0|1|2|2|1||POLYGON((1 0,2 0,2 1,1 1,1 0)) +0|1|2|2|2||POLYGON((1 1,2 1,2 2,1 2,1 1)) +0|2|2|1|1||POLYGON((1 -1,2 -1,2 0,1 0,1 -1)) +0|2|2|1|2||POLYGON((1 0,2 0,2 1,1 1,1 0)) +0|3|2|1|1||POLYGON((1 1,2 1,2 2,1 2,1 1)) +10|11|2|1|1||POLYGON((-0.9 -0.9,0.1 -0.8,0.2 0.2,-0.8 0.1,-0.9 -0.9)) +10|11|2|1|2||POLYGON((-0.8 0.1,0.2 0.2,0.3 1.2,-0.7 1.1,-0.8 0.1)) +10|11|2|1|3||POLYGON((-0.7 1.1,0.3 1.2,0.4 2.2,-0.6 2.1,-0.7 1.1)) +10|11|2|2|1||POLYGON((0.1 -0.8,1.1 -0.7,1.2 0.3,0.2 0.2,0.1 -0.8)) +10|11|2|2|2||POLYGON((0.2 0.2,1.2 0.3,1.3 1.3,0.3 1.2,0.2 0.2)) +10|11|2|2|3||POLYGON((0.3 1.2,1.3 1.3,1.4 2.3,0.4 2.2,0.3 1.2)) +10|11|2|3|1||POLYGON((1.1 -0.7,2.1 -0.6,2.2 0.4,1.2 0.3,1.1 -0.7)) +10|11|2|3|2||POLYGON((1.2 0.3,2.2 0.4,2.3 1.4,1.3 1.3,1.2 0.3)) +10|11|2|3|3||POLYGON((1.3 1.3,2.3 1.4,2.4 2.4,1.4 2.3,1.3 1.3)) +10|12|2|1|1||POLYGON((-1.9 -1,-0.9 -0.9,-0.8 0.1,-1.8 0,-1.9 -1)) +10|12|2|1|2||POLYGON((-1.8 0,-0.8 0.1,-0.7 1.1,-1.7 1,-1.8 0)) +10|12|2|2|1||POLYGON((-0.9 -0.9,0.1 -0.8,0.2 0.2,-0.8 0.1,-0.9 -0.9)) +10|12|2|2|2||POLYGON((-0.8 0.1,0.2 0.2,0.3 1.2,-0.7 1.1,-0.8 0.1)) +10|13|2|1|1||POLYGON((0 -1.8,1 -1.7,1.1 -0.7,0.1 -0.8,0 -1.8)) +10|13|2|1|2||POLYGON((0.1 -0.8,1.1 -0.7,1.2 0.3,0.2 0.2,0.1 -0.8)) +10|13|2|2|1||POLYGON((1 -1.7,2 -1.6,2.1 -0.6,1.1 -0.7,1 -1.7)) +10|13|2|2|2||POLYGON((1.1 -0.7,2.1 -0.6,2.2 0.4,1.2 0.3,1.1 -0.7))