From: Bborie Park Date: Fri, 14 Dec 2012 20:27:06 +0000 (+0000) Subject: Added parameters to ST_Tile(raster) to control padding of generated tiles. X-Git-Tag: 2.1.0beta2~295 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=25b8f66978554196e88ea8b42c3f77b82d290d0a;p=postgis Added parameters to ST_Tile(raster) to control padding of generated tiles. ST_Tile(raster) no longer defaults to padding tiles. Ticket #2069 git-svn-id: http://svn.osgeo.org/postgis/trunk@10836 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/doc/reference_raster.xml b/doc/reference_raster.xml index 03285aa9a..baae1e348 100644 --- a/doc/reference_raster.xml +++ b/doc/reference_raster.xml @@ -1633,6 +1633,8 @@ FROM (SELECT rid, ST_MetaData(rast) As md int[] nband integer width integer height + boolean padwithnodata=FALSE + double precision nodataval=NULL @@ -1641,6 +1643,8 @@ FROM (SELECT rid, ST_MetaData(rast) As md integer nband integer width integer height + boolean padwithnodata=FALSE + double precision nodataval=NULL @@ -1648,6 +1652,8 @@ FROM (SELECT rid, ST_MetaData(rast) As md raster rast integer width integer height + boolean padwithnodata=FALSE + double precision nodataval=NULL @@ -1660,6 +1666,10 @@ FROM (SELECT rid, ST_MetaData(rast) As md Returns a set of rasters resulting from the split of the input raster based upon the desired dimensions of the output rasters. + + If padwithnodata = FALSE, edge tiles on the right and bottom sides of the raster may have different dimensions than the rest of the tiles. If padwithnodata = TRUE, all tiles will have the same dimensions with the possibilty that edge tiles being padded with NODATA values. If raster band(s) do not have NODATA value(s) specified, one can be specified by setting nodataval. + + If a specified band of the input raster is out-of-db, the corresponding band in the output rasters will also be out-of-db. @@ -1688,7 +1698,7 @@ WITH foo AS ( ), bar AS ( SELECT ST_Union(rast) AS rast FROM foo ), baz AS ( - SELECT ST_Tile(rast, 3, 3) AS rast FROM bar + SELECT ST_Tile(rast, 3, 3, TRUE) AS rast FROM bar ) SELECT ST_DumpValues(rast) diff --git a/raster/rt_pg/rt_pg.c b/raster/rt_pg/rt_pg.c index 968cce5c5..97e678085 100644 --- a/raster/rt_pg/rt_pg.c +++ b/raster/rt_pg/rt_pg.c @@ -5383,6 +5383,12 @@ Datum RASTER_tile(PG_FUNCTION_ARGS) int numbands; int *nbands; + + struct { + int pad; + double hasnodata; + double nodataval; + } pad; }; struct tile_arg_t *arg1 = NULL; struct tile_arg_t *arg2 = NULL; @@ -5578,6 +5584,25 @@ Datum RASTER_tile(PG_FUNCTION_ARGS) } } + /* pad (4) and padnodata (5) */ + if (!PG_ARGISNULL(4)) { + arg1->pad.pad = PG_GETARG_BOOL(4) ? 1 : 0; + + if (arg1->pad.pad && !PG_ARGISNULL(5)) { + arg1->pad.hasnodata = 1; + arg1->pad.nodataval = PG_GETARG_FLOAT8(5); + } + else { + arg1->pad.hasnodata = 0; + arg1->pad.nodataval = 0; + } + } + else { + arg1->pad.pad = 0; + arg1->pad.hasnodata = 0; + arg1->pad.nodataval = 0; + } + /* store some additional metadata */ arg1->raster.srid = rt_raster_get_srid(arg1->raster.raster); arg1->raster.width = rt_raster_get_width(arg1->raster.raster); @@ -5614,12 +5639,16 @@ Datum RASTER_tile(PG_FUNCTION_ARGS) rt_pixtype pixtype = PT_END; int hasnodata = 0; double nodataval = 0; + int width = 0; + int height = 0; int k = 0; int tx = 0; int ty = 0; int rx = 0; int ry = 0; + int ex = 0; /* edge tile on right */ + int ey = 0; /* edge tile on bottom */ double ulx = 0; double uly = 0; uint16_t len = 0; @@ -5628,11 +5657,6 @@ Datum RASTER_tile(PG_FUNCTION_ARGS) POSTGIS_RT_DEBUGF(3, "call number %d", call_cntr); - /* create empty raster */ - tile = rt_raster_new(arg2->tile.width, arg2->tile.height); - rt_raster_set_geotransform_matrix(tile, arg2->raster.gt); - rt_raster_set_srid(tile, arg2->raster.srid); - /* find offset based upon tile # @@ -5644,10 +5668,40 @@ Datum RASTER_tile(PG_FUNCTION_ARGS) tx = call_cntr % arg2->tile.nx; POSTGIS_RT_DEBUGF(4, "tile (x, y) = (%d, %d)", tx, ty); - /* upper-left of tile */ + /* edge tile? only important if padding is false */ + if (!arg2->pad.pad) { + if (ty + 1 == arg2->tile.ny) + ey = 1; + if (tx + 1 == arg2->tile.nx) + ex = 1; + } + + /* upper-left of tile in raster coordinates */ rx = tx * arg2->tile.width; ry = ty * arg2->tile.height; POSTGIS_RT_DEBUGF(4, "raster coordinates = %d, %d", rx, ry); + + /* determine tile width and height */ + /* default to user-defined */ + width = arg2->tile.width; + height = arg2->tile.height; + + /* override user-defined if edge tile (only possible if padding is false */ + if (ex || ey) { + /* right edge */ + if (ex) + width = arg2->raster.width - rx; + /* bottom edge */ + if (ey) + height = arg2->raster.height - ry; + } + + /* create empty raster */ + tile = rt_raster_new(width, height); + rt_raster_set_geotransform_matrix(tile, arg2->raster.gt); + rt_raster_set_srid(tile, arg2->raster.srid); + + /* upper-left of tile in spatial coordinates */ if (rt_raster_cell_to_geopoint(arg2->raster.raster, rx, ry, &ulx, &uly, arg2->raster.gt) != ES_NONE) { elog(ERROR, "RASTER_tile: Unable to compute the coordinates of the upper-left corner of the output tile"); rt_raster_destroy(tile); @@ -5656,14 +5710,14 @@ Datum RASTER_tile(PG_FUNCTION_ARGS) pfree(arg2); SRF_RETURN_DONE(funcctx); } - POSTGIS_RT_DEBUGF(4, "spatial coordinates = %f, %f", ulx, uly); rt_raster_set_offsets(tile, ulx, uly); + POSTGIS_RT_DEBUGF(4, "spatial coordinates = %f, %f", ulx, uly); /* compute length of pixel line to read */ len = arg2->tile.width; if (rx + arg2->tile.width >= arg2->raster.width) len = arg2->raster.width - rx; - POSTGIS_RT_DEBUGF(3, "len = %d", len); + POSTGIS_RT_DEBUGF(3, "read line len = %d", len); /* copy bands to tile */ for (i = 0; i < arg2->numbands; i++) { @@ -5683,6 +5737,10 @@ Datum RASTER_tile(PG_FUNCTION_ARGS) hasnodata = rt_band_get_hasnodata_flag(_band); if (hasnodata) rt_band_get_nodata(_band, &nodataval); + else if (arg2->pad.pad && arg2->pad.hasnodata) { + hasnodata = 1; + nodataval = arg2->pad.nodataval; + } else nodataval = rt_band_get_min_value(_band); diff --git a/raster/rt_pg/rtpostgis.sql.in.c b/raster/rt_pg/rtpostgis.sql.in.c index 396d0a4d6..dfd9cfdb1 100644 --- a/raster/rt_pg/rtpostgis.sql.in.c +++ b/raster/rt_pg/rtpostgis.sql.in.c @@ -4365,7 +4365,8 @@ CREATE OR REPLACE FUNCTION st_setgeoreference(rast raster, georef text, format t CREATE OR REPLACE FUNCTION _st_tile( rast raster, width integer, height integer, - nband int[] DEFAULT NULL + nband integer[] DEFAULT NULL, + padwithnodata boolean DEFAULT FALSE, nodataval double precision DEFAULT NULL ) RETURNS SETOF raster AS 'MODULE_PATHNAME','RASTER_tile' @@ -4373,26 +4374,29 @@ CREATE OR REPLACE FUNCTION _st_tile( CREATE OR REPLACE FUNCTION st_tile( rast raster, nband integer[], - width integer, height integer + width integer, height integer, + padwithnodata boolean DEFAULT FALSE, nodataval double precision DEFAULT NULL ) RETURNS SETOF raster - AS $$ SELECT _st_tile($1, $3, $4, $2) $$ + AS $$ SELECT _st_tile($1, $3, $4, $2, $5, $6) $$ LANGUAGE 'sql' IMMUTABLE; CREATE OR REPLACE FUNCTION st_tile( rast raster, nband integer, - width integer, height integer + width integer, height integer, + padwithnodata boolean DEFAULT FALSE, nodataval double precision DEFAULT NULL ) RETURNS SETOF raster - AS $$ SELECT _st_tile($1, $3, $4, ARRAY[$2]::integer[]) $$ + AS $$ SELECT _st_tile($1, $3, $4, ARRAY[$2]::integer[], $5, $6) $$ LANGUAGE 'sql' IMMUTABLE; CREATE OR REPLACE FUNCTION st_tile( rast raster, - width integer, height integer + width integer, height integer, + padwithnodata boolean DEFAULT FALSE, nodataval double precision DEFAULT NULL ) RETURNS SETOF raster - AS $$ SELECT _st_tile($1, $2, $3, NULL::integer[]) $$ + AS $$ SELECT _st_tile($1, $2, $3, NULL::integer[], $4, $5) $$ LANGUAGE 'sql' IMMUTABLE; ----------------------------------------------------------------------- diff --git a/raster/rt_pg/rtpostgis_drop.sql.in.c b/raster/rt_pg/rtpostgis_drop.sql.in.c index ac3d13012..e0cae36e1 100644 --- a/raster/rt_pg/rtpostgis_drop.sql.in.c +++ b/raster/rt_pg/rtpostgis_drop.sql.in.c @@ -474,3 +474,10 @@ DROP FUNCTION IF EXISTS _st_resample(raster, text, double precision, integer, do -- function signatures changed DROP FUNCTION IF EXISTS st_resample(raster, integer, double precision, double precision, double precision, double precision, double precision, double precision, text, double precision); DROP FUNCTION IF EXISTS st_resample(raster, integer, integer, integer, double precision, double precision, double precision, double precision, text, double precision); + +-- function signatures changed +DROP FUNCTION IF EXISTS _st_tile(raster, integer, integer, int[]); +DROP FUNCTION IF EXISTS st_tile(raster, integer[], integer, integer); +DROP FUNCTION IF EXISTS st_tile(raster, integer, integer, integer); +DROP FUNCTION IF EXISTS st_tile(raster, integer, integer); + diff --git a/raster/test/regress/rt_tile.sql b/raster/test/regress/rt_tile.sql index c8e4ec9f4..f010c8e33 100644 --- a/raster/test/regress/rt_tile.sql +++ b/raster/test/regress/rt_tile.sql @@ -16,52 +16,129 @@ CREATE TABLE raster_tile AS SELECT ST_Union(rast) AS rast FROM foo; WITH foo AS ( - SELECT ST_Tile(rast, 3, 3) AS rast FROM raster_tile + SELECT ST_Tile(rast, 3, 3, TRUE) AS rast FROM raster_tile ) SELECT 1, ST_DumpValues(rast) FROM foo; WITH foo AS ( - SELECT ST_Tile(rast, ARRAY[1], 3, 3) AS rast FROM raster_tile + SELECT ST_Tile(rast, ARRAY[1], 3, 3, TRUE) AS rast FROM raster_tile ) SELECT 2, ST_DumpValues(rast) FROM foo; WITH foo AS ( - SELECT ST_Tile(rast, ARRAY[2, 1], 3, 3) AS rast FROM raster_tile + SELECT ST_Tile(rast, ARRAY[2, 1], 3, 3, TRUE) AS rast FROM raster_tile ) SELECT 3, ST_DumpValues(rast) FROM foo; WITH foo AS ( - SELECT ST_Tile(rast, 2, 3, 3) AS rast FROM raster_tile + SELECT ST_Tile(rast, 2, 3, 3, TRUE) AS rast FROM raster_tile ) SELECT 4, ST_DumpValues(rast) FROM foo; WITH foo AS ( - SELECT ST_Tile(rast, 2, 2) AS rast FROM raster_tile + SELECT ST_Tile(rast, 2, 2, TRUE) AS rast FROM raster_tile ) SELECT 5, ST_DumpValues(rast) FROM foo; WITH foo AS ( - SELECT ST_Tile(rast, 1, 1) AS rast FROM raster_tile + SELECT ST_Tile(rast, 1, 1, TRUE) AS rast FROM raster_tile ) SELECT 6, ST_DumpValues(rast) FROM foo; WITH foo AS ( - SELECT ST_Tile(rast, 5, 5) AS rast FROM raster_tile + SELECT ST_Tile(rast, 5, 5, TRUE) AS rast FROM raster_tile ) SELECT 7, ST_DumpValues(rast) FROM foo; +WITH foo AS ( + SELECT ST_Tile(rast, 2, 3, TRUE) AS rast FROM raster_tile +) +SELECT + 8, ST_DumpValues(rast) +FROM foo; + +WITH foo AS ( + SELECT ST_Tile(rast, 3, 2, TRUE) AS rast FROM raster_tile +) +SELECT + 9, ST_DumpValues(rast) +FROM foo; + +WITH foo AS ( + SELECT ST_Tile(rast, 3, 3) AS rast FROM raster_tile +) +SELECT + 11, ST_DumpValues(rast) +FROM foo; + +WITH foo AS ( + SELECT ST_Tile(rast, ARRAY[1], 3, 3) AS rast FROM raster_tile +) +SELECT + 12, ST_DumpValues(rast) +FROM foo; + +WITH foo AS ( + SELECT ST_Tile(rast, ARRAY[2, 1], 3, 3) AS rast FROM raster_tile +) +SELECT + 13, ST_DumpValues(rast) +FROM foo; + +WITH foo AS ( + SELECT ST_Tile(rast, 2, 3, 3) AS rast FROM raster_tile +) +SELECT + 14, ST_DumpValues(rast) +FROM foo; + +WITH foo AS ( + SELECT ST_Tile(rast, 2, 2) AS rast FROM raster_tile +) +SELECT + 15, ST_DumpValues(rast) +FROM foo; + +WITH foo AS ( + SELECT ST_Tile(rast, 1, 1) AS rast FROM raster_tile +) +SELECT + 16, ST_DumpValues(rast) +FROM foo; + +WITH foo AS ( + SELECT ST_Tile(rast, 5, 5) AS rast FROM raster_tile +) +SELECT + 17, ST_DumpValues(rast) +FROM foo; + +WITH foo AS ( + SELECT ST_Tile(rast, 2, 3) AS rast FROM raster_tile +) +SELECT + 18, ST_DumpValues(rast) +FROM foo; + +WITH foo AS ( + SELECT ST_Tile(rast, 3, 2) AS rast FROM raster_tile +) +SELECT + 19, ST_DumpValues(rast) +FROM foo; + DROP TABLE IF EXISTS raster_tile; diff --git a/raster/test/regress/rt_tile_expected b/raster/test/regress/rt_tile_expected index 2c04bca53..bb2edfa9a 100644 --- a/raster/test/regress/rt_tile_expected +++ b/raster/test/regress/rt_tile_expected @@ -273,3 +273,397 @@ NOTICE: table "raster_tile" does not exist, skipping 7|(2,"{{40,40,40,50,50},{70,70,70,80,80},{70,70,70,80,80},{70,70,70,80,80},{NULL,NULL,NULL,NULL,NULL}}") 7|(1,"{{5,6,6,6,NULL},{8,9,9,9,NULL},{8,9,9,9,NULL},{8,9,9,9,NULL},{NULL,NULL,NULL,NULL,NULL}}") 7|(2,"{{50,60,60,60,NULL},{80,90,90,90,NULL},{80,90,90,90,NULL},{80,90,90,90,NULL},{NULL,NULL,NULL,NULL,NULL}}") +8|(1,"{{1,1},{1,1},{1,1}}") +8|(2,"{{10,10},{10,10},{10,10}}") +8|(1,"{{1,2},{1,2},{1,2}}") +8|(2,"{{10,20},{10,20},{10,20}}") +8|(1,"{{2,2},{2,2},{2,2}}") +8|(2,"{{20,20},{20,20},{20,20}}") +8|(1,"{{3,3},{3,3},{3,3}}") +8|(2,"{{30,30},{30,30},{30,30}}") +8|(1,"{{3,NULL},{3,NULL},{3,NULL}}") +8|(2,"{{30,NULL},{30,NULL},{30,NULL}}") +8|(1,"{{4,4},{4,4},{4,4}}") +8|(2,"{{40,40},{40,40},{40,40}}") +8|(1,"{{4,5},{4,5},{4,5}}") +8|(2,"{{40,50},{40,50},{40,50}}") +8|(1,"{{5,5},{5,5},{5,5}}") +8|(2,"{{50,50},{50,50},{50,50}}") +8|(1,"{{6,6},{6,6},{6,6}}") +8|(2,"{{60,60},{60,60},{60,60}}") +8|(1,"{{6,NULL},{6,NULL},{6,NULL}}") +8|(2,"{{60,NULL},{60,NULL},{60,NULL}}") +8|(1,"{{7,7},{7,7},{7,7}}") +8|(2,"{{70,70},{70,70},{70,70}}") +8|(1,"{{7,8},{7,8},{7,8}}") +8|(2,"{{70,80},{70,80},{70,80}}") +8|(1,"{{8,8},{8,8},{8,8}}") +8|(2,"{{80,80},{80,80},{80,80}}") +8|(1,"{{9,9},{9,9},{9,9}}") +8|(2,"{{90,90},{90,90},{90,90}}") +8|(1,"{{9,NULL},{9,NULL},{9,NULL}}") +8|(2,"{{90,NULL},{90,NULL},{90,NULL}}") +9|(1,"{{1,1,1},{1,1,1}}") +9|(2,"{{10,10,10},{10,10,10}}") +9|(1,"{{2,2,2},{2,2,2}}") +9|(2,"{{20,20,20},{20,20,20}}") +9|(1,"{{3,3,3},{3,3,3}}") +9|(2,"{{30,30,30},{30,30,30}}") +9|(1,"{{1,1,1},{4,4,4}}") +9|(2,"{{10,10,10},{40,40,40}}") +9|(1,"{{2,2,2},{5,5,5}}") +9|(2,"{{20,20,20},{50,50,50}}") +9|(1,"{{3,3,3},{6,6,6}}") +9|(2,"{{30,30,30},{60,60,60}}") +9|(1,"{{4,4,4},{4,4,4}}") +9|(2,"{{40,40,40},{40,40,40}}") +9|(1,"{{5,5,5},{5,5,5}}") +9|(2,"{{50,50,50},{50,50,50}}") +9|(1,"{{6,6,6},{6,6,6}}") +9|(2,"{{60,60,60},{60,60,60}}") +9|(1,"{{7,7,7},{7,7,7}}") +9|(2,"{{70,70,70},{70,70,70}}") +9|(1,"{{8,8,8},{8,8,8}}") +9|(2,"{{80,80,80},{80,80,80}}") +9|(1,"{{9,9,9},{9,9,9}}") +9|(2,"{{90,90,90},{90,90,90}}") +9|(1,"{{7,7,7},{NULL,NULL,NULL}}") +9|(2,"{{70,70,70},{NULL,NULL,NULL}}") +9|(1,"{{8,8,8},{NULL,NULL,NULL}}") +9|(2,"{{80,80,80},{NULL,NULL,NULL}}") +9|(1,"{{9,9,9},{NULL,NULL,NULL}}") +9|(2,"{{90,90,90},{NULL,NULL,NULL}}") +11|(1,"{{1,1,1},{1,1,1},{1,1,1}}") +11|(2,"{{10,10,10},{10,10,10},{10,10,10}}") +11|(1,"{{2,2,2},{2,2,2},{2,2,2}}") +11|(2,"{{20,20,20},{20,20,20},{20,20,20}}") +11|(1,"{{3,3,3},{3,3,3},{3,3,3}}") +11|(2,"{{30,30,30},{30,30,30},{30,30,30}}") +11|(1,"{{4,4,4},{4,4,4},{4,4,4}}") +11|(2,"{{40,40,40},{40,40,40},{40,40,40}}") +11|(1,"{{5,5,5},{5,5,5},{5,5,5}}") +11|(2,"{{50,50,50},{50,50,50},{50,50,50}}") +11|(1,"{{6,6,6},{6,6,6},{6,6,6}}") +11|(2,"{{60,60,60},{60,60,60},{60,60,60}}") +11|(1,"{{7,7,7},{7,7,7},{7,7,7}}") +11|(2,"{{70,70,70},{70,70,70},{70,70,70}}") +11|(1,"{{8,8,8},{8,8,8},{8,8,8}}") +11|(2,"{{80,80,80},{80,80,80},{80,80,80}}") +11|(1,"{{9,9,9},{9,9,9},{9,9,9}}") +11|(2,"{{90,90,90},{90,90,90},{90,90,90}}") +12|(1,"{{1,1,1},{1,1,1},{1,1,1}}") +12|(1,"{{2,2,2},{2,2,2},{2,2,2}}") +12|(1,"{{3,3,3},{3,3,3},{3,3,3}}") +12|(1,"{{4,4,4},{4,4,4},{4,4,4}}") +12|(1,"{{5,5,5},{5,5,5},{5,5,5}}") +12|(1,"{{6,6,6},{6,6,6},{6,6,6}}") +12|(1,"{{7,7,7},{7,7,7},{7,7,7}}") +12|(1,"{{8,8,8},{8,8,8},{8,8,8}}") +12|(1,"{{9,9,9},{9,9,9},{9,9,9}}") +13|(1,"{{10,10,10},{10,10,10},{10,10,10}}") +13|(2,"{{1,1,1},{1,1,1},{1,1,1}}") +13|(1,"{{20,20,20},{20,20,20},{20,20,20}}") +13|(2,"{{2,2,2},{2,2,2},{2,2,2}}") +13|(1,"{{30,30,30},{30,30,30},{30,30,30}}") +13|(2,"{{3,3,3},{3,3,3},{3,3,3}}") +13|(1,"{{40,40,40},{40,40,40},{40,40,40}}") +13|(2,"{{4,4,4},{4,4,4},{4,4,4}}") +13|(1,"{{50,50,50},{50,50,50},{50,50,50}}") +13|(2,"{{5,5,5},{5,5,5},{5,5,5}}") +13|(1,"{{60,60,60},{60,60,60},{60,60,60}}") +13|(2,"{{6,6,6},{6,6,6},{6,6,6}}") +13|(1,"{{70,70,70},{70,70,70},{70,70,70}}") +13|(2,"{{7,7,7},{7,7,7},{7,7,7}}") +13|(1,"{{80,80,80},{80,80,80},{80,80,80}}") +13|(2,"{{8,8,8},{8,8,8},{8,8,8}}") +13|(1,"{{90,90,90},{90,90,90},{90,90,90}}") +13|(2,"{{9,9,9},{9,9,9},{9,9,9}}") +14|(1,"{{10,10,10},{10,10,10},{10,10,10}}") +14|(1,"{{20,20,20},{20,20,20},{20,20,20}}") +14|(1,"{{30,30,30},{30,30,30},{30,30,30}}") +14|(1,"{{40,40,40},{40,40,40},{40,40,40}}") +14|(1,"{{50,50,50},{50,50,50},{50,50,50}}") +14|(1,"{{60,60,60},{60,60,60},{60,60,60}}") +14|(1,"{{70,70,70},{70,70,70},{70,70,70}}") +14|(1,"{{80,80,80},{80,80,80},{80,80,80}}") +14|(1,"{{90,90,90},{90,90,90},{90,90,90}}") +15|(1,"{{1,1},{1,1}}") +15|(2,"{{10,10},{10,10}}") +15|(1,"{{1,2},{1,2}}") +15|(2,"{{10,20},{10,20}}") +15|(1,"{{2,2},{2,2}}") +15|(2,"{{20,20},{20,20}}") +15|(1,"{{3,3},{3,3}}") +15|(2,"{{30,30},{30,30}}") +15|(1,"{{3},{3}}") +15|(2,"{{30},{30}}") +15|(1,"{{1,1},{4,4}}") +15|(2,"{{10,10},{40,40}}") +15|(1,"{{1,2},{4,5}}") +15|(2,"{{10,20},{40,50}}") +15|(1,"{{2,2},{5,5}}") +15|(2,"{{20,20},{50,50}}") +15|(1,"{{3,3},{6,6}}") +15|(2,"{{30,30},{60,60}}") +15|(1,"{{3},{6}}") +15|(2,"{{30},{60}}") +15|(1,"{{4,4},{4,4}}") +15|(2,"{{40,40},{40,40}}") +15|(1,"{{4,5},{4,5}}") +15|(2,"{{40,50},{40,50}}") +15|(1,"{{5,5},{5,5}}") +15|(2,"{{50,50},{50,50}}") +15|(1,"{{6,6},{6,6}}") +15|(2,"{{60,60},{60,60}}") +15|(1,"{{6},{6}}") +15|(2,"{{60},{60}}") +15|(1,"{{7,7},{7,7}}") +15|(2,"{{70,70},{70,70}}") +15|(1,"{{7,8},{7,8}}") +15|(2,"{{70,80},{70,80}}") +15|(1,"{{8,8},{8,8}}") +15|(2,"{{80,80},{80,80}}") +15|(1,"{{9,9},{9,9}}") +15|(2,"{{90,90},{90,90}}") +15|(1,"{{9},{9}}") +15|(2,"{{90},{90}}") +15|(1,"{{7,7}}") +15|(2,"{{70,70}}") +15|(1,"{{7,8}}") +15|(2,"{{70,80}}") +15|(1,"{{8,8}}") +15|(2,"{{80,80}}") +15|(1,"{{9,9}}") +15|(2,"{{90,90}}") +15|(1,{{9}}) +15|(2,{{90}}) +16|(1,{{1}}) +16|(2,{{10}}) +16|(1,{{1}}) +16|(2,{{10}}) +16|(1,{{1}}) +16|(2,{{10}}) +16|(1,{{2}}) +16|(2,{{20}}) +16|(1,{{2}}) +16|(2,{{20}}) +16|(1,{{2}}) +16|(2,{{20}}) +16|(1,{{3}}) +16|(2,{{30}}) +16|(1,{{3}}) +16|(2,{{30}}) +16|(1,{{3}}) +16|(2,{{30}}) +16|(1,{{1}}) +16|(2,{{10}}) +16|(1,{{1}}) +16|(2,{{10}}) +16|(1,{{1}}) +16|(2,{{10}}) +16|(1,{{2}}) +16|(2,{{20}}) +16|(1,{{2}}) +16|(2,{{20}}) +16|(1,{{2}}) +16|(2,{{20}}) +16|(1,{{3}}) +16|(2,{{30}}) +16|(1,{{3}}) +16|(2,{{30}}) +16|(1,{{3}}) +16|(2,{{30}}) +16|(1,{{1}}) +16|(2,{{10}}) +16|(1,{{1}}) +16|(2,{{10}}) +16|(1,{{1}}) +16|(2,{{10}}) +16|(1,{{2}}) +16|(2,{{20}}) +16|(1,{{2}}) +16|(2,{{20}}) +16|(1,{{2}}) +16|(2,{{20}}) +16|(1,{{3}}) +16|(2,{{30}}) +16|(1,{{3}}) +16|(2,{{30}}) +16|(1,{{3}}) +16|(2,{{30}}) +16|(1,{{4}}) +16|(2,{{40}}) +16|(1,{{4}}) +16|(2,{{40}}) +16|(1,{{4}}) +16|(2,{{40}}) +16|(1,{{5}}) +16|(2,{{50}}) +16|(1,{{5}}) +16|(2,{{50}}) +16|(1,{{5}}) +16|(2,{{50}}) +16|(1,{{6}}) +16|(2,{{60}}) +16|(1,{{6}}) +16|(2,{{60}}) +16|(1,{{6}}) +16|(2,{{60}}) +16|(1,{{4}}) +16|(2,{{40}}) +16|(1,{{4}}) +16|(2,{{40}}) +16|(1,{{4}}) +16|(2,{{40}}) +16|(1,{{5}}) +16|(2,{{50}}) +16|(1,{{5}}) +16|(2,{{50}}) +16|(1,{{5}}) +16|(2,{{50}}) +16|(1,{{6}}) +16|(2,{{60}}) +16|(1,{{6}}) +16|(2,{{60}}) +16|(1,{{6}}) +16|(2,{{60}}) +16|(1,{{4}}) +16|(2,{{40}}) +16|(1,{{4}}) +16|(2,{{40}}) +16|(1,{{4}}) +16|(2,{{40}}) +16|(1,{{5}}) +16|(2,{{50}}) +16|(1,{{5}}) +16|(2,{{50}}) +16|(1,{{5}}) +16|(2,{{50}}) +16|(1,{{6}}) +16|(2,{{60}}) +16|(1,{{6}}) +16|(2,{{60}}) +16|(1,{{6}}) +16|(2,{{60}}) +16|(1,{{7}}) +16|(2,{{70}}) +16|(1,{{7}}) +16|(2,{{70}}) +16|(1,{{7}}) +16|(2,{{70}}) +16|(1,{{8}}) +16|(2,{{80}}) +16|(1,{{8}}) +16|(2,{{80}}) +16|(1,{{8}}) +16|(2,{{80}}) +16|(1,{{9}}) +16|(2,{{90}}) +16|(1,{{9}}) +16|(2,{{90}}) +16|(1,{{9}}) +16|(2,{{90}}) +16|(1,{{7}}) +16|(2,{{70}}) +16|(1,{{7}}) +16|(2,{{70}}) +16|(1,{{7}}) +16|(2,{{70}}) +16|(1,{{8}}) +16|(2,{{80}}) +16|(1,{{8}}) +16|(2,{{80}}) +16|(1,{{8}}) +16|(2,{{80}}) +16|(1,{{9}}) +16|(2,{{90}}) +16|(1,{{9}}) +16|(2,{{90}}) +16|(1,{{9}}) +16|(2,{{90}}) +16|(1,{{7}}) +16|(2,{{70}}) +16|(1,{{7}}) +16|(2,{{70}}) +16|(1,{{7}}) +16|(2,{{70}}) +16|(1,{{8}}) +16|(2,{{80}}) +16|(1,{{8}}) +16|(2,{{80}}) +16|(1,{{8}}) +16|(2,{{80}}) +16|(1,{{9}}) +16|(2,{{90}}) +16|(1,{{9}}) +16|(2,{{90}}) +16|(1,{{9}}) +16|(2,{{90}}) +17|(1,"{{1,1,1,2,2},{1,1,1,2,2},{1,1,1,2,2},{4,4,4,5,5},{4,4,4,5,5}}") +17|(2,"{{10,10,10,20,20},{10,10,10,20,20},{10,10,10,20,20},{40,40,40,50,50},{40,40,40,50,50}}") +17|(1,"{{2,3,3,3},{2,3,3,3},{2,3,3,3},{5,6,6,6},{5,6,6,6}}") +17|(2,"{{20,30,30,30},{20,30,30,30},{20,30,30,30},{50,60,60,60},{50,60,60,60}}") +17|(1,"{{4,4,4,5,5},{7,7,7,8,8},{7,7,7,8,8},{7,7,7,8,8}}") +17|(2,"{{40,40,40,50,50},{70,70,70,80,80},{70,70,70,80,80},{70,70,70,80,80}}") +17|(1,"{{5,6,6,6},{8,9,9,9},{8,9,9,9},{8,9,9,9}}") +17|(2,"{{50,60,60,60},{80,90,90,90},{80,90,90,90},{80,90,90,90}}") +18|(1,"{{1,1},{1,1},{1,1}}") +18|(2,"{{10,10},{10,10},{10,10}}") +18|(1,"{{1,2},{1,2},{1,2}}") +18|(2,"{{10,20},{10,20},{10,20}}") +18|(1,"{{2,2},{2,2},{2,2}}") +18|(2,"{{20,20},{20,20},{20,20}}") +18|(1,"{{3,3},{3,3},{3,3}}") +18|(2,"{{30,30},{30,30},{30,30}}") +18|(1,"{{3},{3},{3}}") +18|(2,"{{30},{30},{30}}") +18|(1,"{{4,4},{4,4},{4,4}}") +18|(2,"{{40,40},{40,40},{40,40}}") +18|(1,"{{4,5},{4,5},{4,5}}") +18|(2,"{{40,50},{40,50},{40,50}}") +18|(1,"{{5,5},{5,5},{5,5}}") +18|(2,"{{50,50},{50,50},{50,50}}") +18|(1,"{{6,6},{6,6},{6,6}}") +18|(2,"{{60,60},{60,60},{60,60}}") +18|(1,"{{6},{6},{6}}") +18|(2,"{{60},{60},{60}}") +18|(1,"{{7,7},{7,7},{7,7}}") +18|(2,"{{70,70},{70,70},{70,70}}") +18|(1,"{{7,8},{7,8},{7,8}}") +18|(2,"{{70,80},{70,80},{70,80}}") +18|(1,"{{8,8},{8,8},{8,8}}") +18|(2,"{{80,80},{80,80},{80,80}}") +18|(1,"{{9,9},{9,9},{9,9}}") +18|(2,"{{90,90},{90,90},{90,90}}") +18|(1,"{{9},{9},{9}}") +18|(2,"{{90},{90},{90}}") +19|(1,"{{1,1,1},{1,1,1}}") +19|(2,"{{10,10,10},{10,10,10}}") +19|(1,"{{2,2,2},{2,2,2}}") +19|(2,"{{20,20,20},{20,20,20}}") +19|(1,"{{3,3,3},{3,3,3}}") +19|(2,"{{30,30,30},{30,30,30}}") +19|(1,"{{1,1,1},{4,4,4}}") +19|(2,"{{10,10,10},{40,40,40}}") +19|(1,"{{2,2,2},{5,5,5}}") +19|(2,"{{20,20,20},{50,50,50}}") +19|(1,"{{3,3,3},{6,6,6}}") +19|(2,"{{30,30,30},{60,60,60}}") +19|(1,"{{4,4,4},{4,4,4}}") +19|(2,"{{40,40,40},{40,40,40}}") +19|(1,"{{5,5,5},{5,5,5}}") +19|(2,"{{50,50,50},{50,50,50}}") +19|(1,"{{6,6,6},{6,6,6}}") +19|(2,"{{60,60,60},{60,60,60}}") +19|(1,"{{7,7,7},{7,7,7}}") +19|(2,"{{70,70,70},{70,70,70}}") +19|(1,"{{8,8,8},{8,8,8}}") +19|(2,"{{80,80,80},{80,80,80}}") +19|(1,"{{9,9,9},{9,9,9}}") +19|(2,"{{90,90,90},{90,90,90}}") +19|(1,"{{7,7,7}}") +19|(2,"{{70,70,70}}") +19|(1,"{{8,8,8}}") +19|(2,"{{80,80,80}}") +19|(1,"{{9,9,9}}") +19|(2,"{{90,90,90}}")