]> granicus.if.org Git - postgis/commitdiff
Added ST_Tile() and regression tests. The circle is complete.
authorBborie Park <bkpark at ucdavis.edu>
Tue, 23 Oct 2012 22:44:42 +0000 (22:44 +0000)
committerBborie Park <bkpark at ucdavis.edu>
Tue, 23 Oct 2012 22:44:42 +0000 (22:44 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@10534 b70326c6-7e19-0410-871a-916f4a2858ee

raster/rt_core/rt_api.c
raster/rt_core/rt_api.h
raster/rt_pg/rt_pg.c
raster/rt_pg/rtpostgis.sql.in.c
raster/test/regress/Makefile.in
raster/test/regress/rt_tile.sql [new file with mode: 0644]
raster/test/regress/rt_tile_expected [new file with mode: 0644]

index 2a7e04968c3be9bda2e09136fd8196c1edacf729..e4b02b9e0939174da9a12421afb568ad950205b0 100644 (file)
@@ -2272,7 +2272,7 @@ rt_band_set_pixel(
  * @param vals : the pixel values
  * @param *nvals : the number of pixel values being returned
  *
- * @return values of multiple pixels
+ * @return 0 on success, -1 on error
  */
 int rt_band_get_pixel_line(
        rt_band band,
@@ -2291,6 +2291,9 @@ int rt_band_get_pixel_line(
 
        assert(NULL != band);
 
+       /* initialize to no values */
+       *nvals = 0;
+
        if (
                x < 0 || x >= band->width ||
                y < 0 || y >= band->height
@@ -2299,6 +2302,9 @@ int rt_band_get_pixel_line(
                return -1;
        }
 
+       if (len < 1)
+               return 0;
+
        data = rt_band_get_data(band);
        if (data == NULL) {
                rterror("rt_band_get_pixel_line: Cannot get band data");
@@ -2317,8 +2323,8 @@ int rt_band_get_pixel_line(
        _nvals = len;
        maxlen = band->width * band->height;
 
-       if ((maxlen - (int) (offset + _nvals)) < 0) {
-               _nvals = _nvals - (((int) (offset + _nvals)) - maxlen);
+       if (((int) (offset + _nvals)) > maxlen) {
+               _nvals = maxlen - offset;
                rtwarn("Limiting returning number values to %d", _nvals);
        }
        RASTER_DEBUGF(4, "_nvals = %d", _nvals);
index 88101f97ddd486abd2e91614e3b789477f22271c..9f261bd5eabd0759372971326a96576c84133431 100644 (file)
@@ -616,7 +616,7 @@ int rt_band_set_pixel(
  * @param vals : the pixel values
  * @param *nvals : the number of pixel values being returned
  *
- * @return 0 if success, -1 otherwise
+ * @return 0 on success, -1 on error
  */
 int rt_band_get_pixel_line(
        rt_band band,
index 70971b017bc62f5854499902f4d97c89396ce679..ced9e67ad05e0f096b367fc5dcef51d78621b58d 100644 (file)
@@ -269,6 +269,7 @@ Datum RASTER_makeEmpty(PG_FUNCTION_ARGS);
 Datum RASTER_addBand(PG_FUNCTION_ARGS);
 Datum RASTER_copyBand(PG_FUNCTION_ARGS);
 Datum RASTER_addBandRasterArray(PG_FUNCTION_ARGS);
+Datum RASTER_tile(PG_FUNCTION_ARGS);
 
 /* create new raster from existing raster's bands */
 Datum RASTER_band(PG_FUNCTION_ARGS);
@@ -5325,6 +5326,416 @@ Datum RASTER_addBandRasterArray(PG_FUNCTION_ARGS)
        PG_RETURN_NULL();
 }
 
+/**
+ * Break up a raster into smaller tiles. SRF function
+ */
+PG_FUNCTION_INFO_V1(RASTER_tile);
+Datum RASTER_tile(PG_FUNCTION_ARGS)
+{
+       FuncCallContext *funcctx;
+       int call_cntr;
+       int max_calls;
+       int i = 0;
+       int j = 0;
+
+       struct tile_arg_t {
+
+               struct {
+                       rt_raster raster;
+                       double gt[6];
+                       int srid;
+                       int width;
+                       int height;
+               } raster;
+
+               struct {
+                       int width;
+                       int height;
+
+                       int nx;
+                       int ny;
+               } tile;
+
+               int numbands;
+               int *nbands;
+       };
+       struct tile_arg_t *arg1 = NULL;
+       struct tile_arg_t *arg2 = NULL;
+
+       if (SRF_IS_FIRSTCALL()) {
+               MemoryContext oldcontext;
+               rt_pgraster *pgraster = NULL;
+               int numbands;
+
+               ArrayType *array;
+               Oid etype;
+               Datum *e;
+               bool *nulls;
+
+               int16 typlen;
+               bool typbyval;
+               char typalign;
+
+               POSTGIS_RT_DEBUG(2, "RASTER_tile: first call");
+
+               /* create a function context for cross-call persistence */
+               funcctx = SRF_FIRSTCALL_INIT();
+
+               /* switch to memory context appropriate for multiple function calls */
+               oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+               /* Get input arguments */
+               if (PG_ARGISNULL(0)) {
+                       MemoryContextSwitchTo(oldcontext);
+                       SRF_RETURN_DONE(funcctx);
+               }
+
+               /* allocate arg1 */
+               arg1 = palloc(sizeof(struct tile_arg_t));
+               if (arg1 == NULL) {
+                       elog(ERROR, "RASTER_tile: Unable to allocate memory for arguments");
+                       MemoryContextSwitchTo(oldcontext);
+                       SRF_RETURN_DONE(funcctx);
+               }
+
+               pgraster = (rt_pgraster *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0));
+               arg1->raster.raster = rt_raster_deserialize(pgraster, FALSE);
+               if (!arg1->raster.raster) {
+                       ereport(ERROR, (
+                               errcode(ERRCODE_OUT_OF_MEMORY),
+                               errmsg("Could not deserialize raster")
+                       ));
+                       pfree(arg1);
+                       PG_FREE_IF_COPY(pgraster, 0);
+                       MemoryContextSwitchTo(oldcontext);
+                       SRF_RETURN_DONE(funcctx);
+               }
+
+               /* raster has bands */
+               numbands = rt_raster_get_num_bands(arg1->raster.raster); 
+               if (!numbands) {
+                       elog(NOTICE, "Raster provided has no bands");
+                       rt_raster_destroy(arg1->raster.raster);
+                       pfree(arg1);
+                       PG_FREE_IF_COPY(pgraster, 0);
+                       MemoryContextSwitchTo(oldcontext);
+                       SRF_RETURN_DONE(funcctx);
+               }
+
+               /* width (1) */
+               if (PG_ARGISNULL(1)) {
+                       elog(NOTICE, "Width cannot be NULL. Returning NULL");
+                       rt_raster_destroy(arg1->raster.raster);
+                       pfree(arg1);
+                       PG_FREE_IF_COPY(pgraster, 0);
+                       MemoryContextSwitchTo(oldcontext);
+                       SRF_RETURN_DONE(funcctx);
+               }
+               arg1->tile.width = PG_GETARG_INT32(1);
+               if (arg1->tile.width < 1) {
+                       elog(NOTICE, "Width must be greater than zero. Returning NULL");
+                       rt_raster_destroy(arg1->raster.raster);
+                       pfree(arg1);
+                       PG_FREE_IF_COPY(pgraster, 0);
+                       MemoryContextSwitchTo(oldcontext);
+                       SRF_RETURN_DONE(funcctx);
+               }
+
+               /* height (2) */
+               if (PG_ARGISNULL(2)) {
+                       elog(NOTICE, "Height cannot be NULL. Returning NULL");
+                       rt_raster_destroy(arg1->raster.raster);
+                       pfree(arg1);
+                       PG_FREE_IF_COPY(pgraster, 0);
+                       MemoryContextSwitchTo(oldcontext);
+                       SRF_RETURN_DONE(funcctx);
+               }
+               arg1->tile.height = PG_GETARG_INT32(2);
+               if (arg1->tile.height < 1) {
+                       elog(NOTICE, "Height must be greater than zero. Returning NULL");
+                       rt_raster_destroy(arg1->raster.raster);
+                       pfree(arg1);
+                       PG_FREE_IF_COPY(pgraster, 0);
+                       MemoryContextSwitchTo(oldcontext);
+                       SRF_RETURN_DONE(funcctx);
+               }
+
+               /* nband, array (3) */
+               if (!PG_ARGISNULL(3)) {
+                       array = PG_GETARG_ARRAYTYPE_P(3);
+                       etype = ARR_ELEMTYPE(array);
+                       get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
+
+                       switch (etype) {
+                               case INT2OID:
+                               case INT4OID:
+                                       break;
+                               default:
+                                       elog(ERROR, "RASTER_tile: Invalid data type for band indexes");
+                                       rt_raster_destroy(arg1->raster.raster);
+                                       pfree(arg1);
+                                       PG_FREE_IF_COPY(pgraster, 0);
+                                       MemoryContextSwitchTo(oldcontext);
+                                       SRF_RETURN_DONE(funcctx);
+                                       break;
+                       }
+
+                       deconstruct_array(array, etype, typlen, typbyval, typalign, &e, &nulls, &(arg1->numbands));
+
+                       arg1->nbands = palloc(sizeof(int) * arg1->numbands);
+                       if (arg1->nbands == NULL) {
+                               elog(ERROR, "RASTER_tile: Unable to allocate memory for band indexes");
+                               rt_raster_destroy(arg1->raster.raster);
+                               pfree(arg1);
+                               PG_FREE_IF_COPY(pgraster, 0);
+                               MemoryContextSwitchTo(oldcontext);
+                               SRF_RETURN_DONE(funcctx);
+                       }
+
+                       for (i = 0, j = 0; i < arg1->numbands; i++) {
+                               if (nulls[i]) continue;
+
+                               switch (etype) {
+                                       case INT2OID:
+                                               arg1->nbands[j] = DatumGetInt16(e[i]) - 1;
+                                               break;
+                                       case INT4OID:
+                                               arg1->nbands[j] = DatumGetInt32(e[i]) - 1;
+                                               break;
+                               }
+
+                               j++;
+                       }
+
+                       if (j < arg1->numbands) {
+                               arg1->nbands = repalloc(arg1->nbands, sizeof(int) * j);
+                               if (arg1->nbands == NULL) {
+                                       elog(ERROR, "RASTER_tile: Unable to reallocate memory for band indexes");
+                                       rt_raster_destroy(arg1->raster.raster);
+                                       pfree(arg1);
+                                       PG_FREE_IF_COPY(pgraster, 0);
+                                       MemoryContextSwitchTo(oldcontext);
+                                       SRF_RETURN_DONE(funcctx);
+                               }
+
+                               arg1->numbands = j;
+                       }
+
+                       /* validate nbands */
+                       for (i = 0; i < arg1->numbands; i++) {
+                               if (!rt_raster_has_band(arg1->raster.raster, arg1->nbands[i])) {
+                                       elog(NOTICE, "Band at index %d not found in raster", arg1->nbands[i] + 1);
+                                       rt_raster_destroy(arg1->raster.raster);
+                                       pfree(arg1->nbands);
+                                       pfree(arg1);
+                                       PG_FREE_IF_COPY(pgraster, 0);
+                                       MemoryContextSwitchTo(oldcontext);
+                                       SRF_RETURN_DONE(funcctx);
+                               }
+                       }
+               }
+               else {
+                       arg1->numbands = numbands;
+                       arg1->nbands = palloc(sizeof(int) * arg1->numbands);
+
+                       if (arg1->nbands == NULL) {
+                               elog(ERROR, "RASTER_dumpValues: Unable to allocate memory for pixel values");
+                               rt_raster_destroy(arg1->raster.raster);
+                               pfree(arg1);
+                               PG_FREE_IF_COPY(pgraster, 0);
+                               MemoryContextSwitchTo(oldcontext);
+                               SRF_RETURN_DONE(funcctx);
+                       }
+
+                       for (i = 0; i < arg1->numbands; i++) {
+                               arg1->nbands[i] = i;
+                               POSTGIS_RT_DEBUGF(4, "arg1->nbands[%d] = %d", arg1->nbands[i], i);
+                       }
+               }
+
+               /* store some additional metadata */
+               arg1->raster.srid = rt_raster_get_srid(arg1->raster.raster);
+               arg1->raster.width = rt_raster_get_width(arg1->raster.raster);
+               arg1->raster.height = rt_raster_get_height(arg1->raster.raster);
+               rt_raster_get_geotransform_matrix(arg1->raster.raster, arg1->raster.gt);
+
+               /* determine maximum number of tiles from raster */
+               arg1->tile.nx = ceil(arg1->raster.width / (double) arg1->tile.width);
+               arg1->tile.ny = ceil(arg1->raster.height / (double) arg1->tile.height);
+               POSTGIS_RT_DEBUGF(4, "# of tiles (x, y) = (%d, %d)", arg1->tile.nx, arg1->tile.ny);
+
+               /* Store needed information */
+               funcctx->user_fctx = arg1;
+
+               /* total number of tuples to be returned */
+               funcctx->max_calls = (arg1->tile.nx * arg1->tile.ny);
+
+               MemoryContextSwitchTo(oldcontext);
+       }
+
+       /* stuff done on every call of the function */
+       funcctx = SRF_PERCALL_SETUP();
+
+       call_cntr = funcctx->call_cntr;
+       max_calls = funcctx->max_calls;
+       arg2 = funcctx->user_fctx;
+
+       /* do when there is more left to send */
+       if (call_cntr < max_calls) {
+               rt_pgraster *pgtile = NULL;
+               rt_raster tile = NULL;
+               rt_band _band = NULL;
+               rt_band band = NULL;
+               rt_pixtype pixtype = PT_END;
+               int hasnodata = 0;
+               double nodataval = 0;
+
+               int k = 0;
+               int tx = 0;
+               int ty = 0;
+               int rx = 0;
+               int ry = 0;
+               double ulx = 0;
+               double uly = 0;
+               uint16_t len = 0;
+               void *vals = NULL;
+               uint16_t nvals;
+
+               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 #
+
+                       0 1 2
+                       3 4 5
+                       6 7 8
+               */
+               ty = call_cntr / arg2->tile.nx;
+               tx = call_cntr % arg2->tile.nx;
+               POSTGIS_RT_DEBUGF(4, "tile (x, y) = (%d, %d)", tx, ty);
+
+               /* upper-left of tile */
+               rx = tx * arg2->tile.width;
+               ry = ty * arg2->tile.height;
+               POSTGIS_RT_DEBUGF(4, "raster coordinates = %d, %d", rx, ry);
+               if (!rt_raster_cell_to_geopoint(arg2->raster.raster, rx, ry, &ulx, &uly, arg2->raster.gt)) {
+                       elog(ERROR, "RASTER_tile: Unable to compute the coordinates of the upper-left corner of the output tile");
+                       rt_raster_destroy(tile);
+                       rt_raster_destroy(arg2->raster.raster);
+                       pfree(arg2->nbands);
+                       pfree(arg2);
+                       SRF_RETURN_DONE(funcctx);
+               }
+               POSTGIS_RT_DEBUGF(4, "spatial coordinates = %f, %f", ulx, uly);
+               rt_raster_set_offsets(tile, 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);
+
+               /* copy bands to tile */
+               for (i = 0; i < arg2->numbands; i++) {
+                       POSTGIS_RT_DEBUGF(4, "copying band %d to tile %d", arg2->nbands[i], call_cntr);
+
+                       _band = rt_raster_get_band(arg2->raster.raster, arg2->nbands[i]);
+                       if (_band == NULL) {
+                               elog(ERROR, "RASTER_tile: Unable to get band %d from source raster", arg2->nbands[i] + 1);
+                               rt_raster_destroy(tile);
+                               rt_raster_destroy(arg2->raster.raster);
+                               pfree(arg2->nbands);
+                               pfree(arg2);
+                               SRF_RETURN_DONE(funcctx);
+                       }
+
+                       pixtype = rt_band_get_pixtype(_band);
+                       hasnodata = rt_band_get_hasnodata_flag(_band);
+                       if (hasnodata)
+                               rt_band_get_nodata(_band, &nodataval);
+                       else
+                               nodataval = rt_band_get_min_value(_band);
+
+                       if (rt_raster_generate_new_band(tile, pixtype, nodataval, hasnodata, nodataval, i) < 0) {
+                               elog(ERROR, "RASTER_tile: Unable to add new band to output tile");
+                               rt_raster_destroy(tile);
+                               rt_raster_destroy(arg2->raster.raster);
+                               pfree(arg2->nbands);
+                               pfree(arg2);
+                               SRF_RETURN_DONE(funcctx);
+                       }
+                       band = rt_raster_get_band(tile, i);
+                       if (band == NULL) {
+                               elog(ERROR, "RASTER_tile: Unable to get newly added band from output tile");
+                               rt_raster_destroy(tile);
+                               rt_raster_destroy(arg2->raster.raster);
+                               pfree(arg2->nbands);
+                               pfree(arg2);
+                               SRF_RETURN_DONE(funcctx);
+                       }
+
+                       /* if isnodata, set flag and continue */
+                       if (rt_band_get_isnodata_flag(_band)) {
+                               rt_band_set_isnodata_flag(band, 1);
+                               continue;
+                       }
+
+                       /* copy data */
+                       for (j = 0; j < arg2->tile.height; j++) {
+                               k = ry + j;
+
+                               if (k >= arg2->raster.height) {
+                                       POSTGIS_RT_DEBUGF(4, "row %d is beyond extent of source raster. skipping", k);
+                                       continue;
+                               }
+
+                               POSTGIS_RT_DEBUGF(4, "getting pixel line %d, %d for %d pixels", rx, k, len);
+                               if (rt_band_get_pixel_line(_band, rx, k, len, &vals, &nvals) != 0) {
+                                       elog(ERROR, "RASTER_tile: Unable to get pixel line from source raster");
+                                       rt_raster_destroy(tile);
+                                       rt_raster_destroy(arg2->raster.raster);
+                                       pfree(arg2->nbands);
+                                       pfree(arg2);
+                                       SRF_RETURN_DONE(funcctx);
+                               }
+
+                               if (nvals && !rt_band_set_pixel_line(band, 0, j, vals, nvals)) {
+                                       elog(ERROR, "RASTER_tile: Unable to set pixel line of output tile");
+                                       rt_raster_destroy(tile);
+                                       rt_raster_destroy(arg2->raster.raster);
+                                       pfree(arg2->nbands);
+                                       pfree(arg2);
+                                       SRF_RETURN_DONE(funcctx);
+                               }
+                       }
+               }
+
+               pgtile = rt_raster_serialize(tile);
+               rt_raster_destroy(tile);
+               if (!pgtile) {
+                       rt_raster_destroy(arg2->raster.raster);
+                       pfree(arg2->nbands);
+                       pfree(arg2);
+                       SRF_RETURN_DONE(funcctx);
+               }
+
+               SET_VARSIZE(pgtile, pgtile->size);
+               SRF_RETURN_NEXT(funcctx, PointerGetDatum(pgtile));
+       }
+       /* do when there is no more left */
+       else {
+               rt_raster_destroy(arg2->raster.raster);
+               pfree(arg2->nbands);
+               pfree(arg2);
+               SRF_RETURN_DONE(funcctx);
+       }
+}
+
 /**
  * Copy a band from one raster to another one at the given position.
  */
index e1c67a11282091f284abb4dd0642f4c925d9aac5..6ca25c00fdc28efcffd78d26f60c8766bd023ff2 100644 (file)
@@ -3962,6 +3962,19 @@ CREATE OR REPLACE FUNCTION st_setgeoreference(rast raster, georef text, format t
     $$
     LANGUAGE 'plpgsql' IMMUTABLE STRICT; -- WITH (isstrict);
 
+-----------------------------------------------------------------------
+-- ST_Tile(raster)
+-----------------------------------------------------------------------
+
+CREATE OR REPLACE FUNCTION st_tile(
+       rast raster,
+       width integer, height integer,
+       nband int[] DEFAULT NULL
+)
+       RETURNS SETOF raster
+       AS 'MODULE_PATHNAME','RASTER_tile'
+       LANGUAGE 'c' IMMUTABLE;
+
 -----------------------------------------------------------------------
 -- Raster Band Editors
 -----------------------------------------------------------------------
index f13f32d84e69e31f276609237caa46e3208a8b61..47d6cbc24c46c140de140a9312e8f8f79f80b4e2 100644 (file)
@@ -43,7 +43,8 @@ TEST_BASIC_FUNC = \
        rt_bytea \
        box3d \
        rt_addband \
-       rt_band
+       rt_band \
+       rt_tile
 
 TEST_PROPS = \
        rt_dimensions \
diff --git a/raster/test/regress/rt_tile.sql b/raster/test/regress/rt_tile.sql
new file mode 100644 (file)
index 0000000..0e6117d
--- /dev/null
@@ -0,0 +1,53 @@
+DROP TABLE IF EXISTS raster_tile;
+CREATE TABLE raster_tile AS
+       WITH foo AS (
+               SELECT ST_AddBand(ST_AddBand(ST_MakeEmptyRaster(3, 3, 0, 0, 1, -1, 0, 0, 0), 1, '8BUI', 1, 0), 2, '8BUI', 10, 0) AS rast UNION ALL
+               SELECT ST_AddBand(ST_AddBand(ST_MakeEmptyRaster(3, 3, 3, 0, 1, -1, 0, 0, 0), 1, '8BUI', 2, 0), 2, '8BUI', 20, 0) AS rast UNION ALL
+               SELECT ST_AddBand(ST_AddBand(ST_MakeEmptyRaster(3, 3, 6, 0, 1, -1, 0, 0, 0), 1, '8BUI', 3, 0), 2, '8BUI', 30, 0) AS rast UNION ALL
+
+               SELECT ST_AddBand(ST_AddBand(ST_MakeEmptyRaster(3, 3, 0, -3, 1, -1, 0, 0, 0), 1, '8BUI', 4, 0), 2, '8BUI', 40, 0) AS rast UNION ALL
+               SELECT ST_AddBand(ST_AddBand(ST_MakeEmptyRaster(3, 3, 3, -3, 1, -1, 0, 0, 0), 1, '8BUI', 5, 0), 2, '8BUI', 50, 0) AS rast UNION ALL
+               SELECT ST_AddBand(ST_AddBand(ST_MakeEmptyRaster(3, 3, 6, -3, 1, -1, 0, 0, 0), 1, '8BUI', 6, 0), 2, '8BUI', 60, 0) AS rast UNION ALL
+
+               SELECT ST_AddBand(ST_AddBand(ST_MakeEmptyRaster(3, 3, 0, -6, 1, -1, 0, 0, 0), 1, '8BUI', 7, 0), 2, '8BUI', 70, 0) AS rast UNION ALL
+               SELECT ST_AddBand(ST_AddBand(ST_MakeEmptyRaster(3, 3, 3, -6, 1, -1, 0, 0, 0), 1, '8BUI', 8, 0), 2, '8BUI', 80, 0) AS rast UNION ALL
+               SELECT ST_AddBand(ST_AddBand(ST_MakeEmptyRaster(3, 3, 6, -6, 1, -1, 0, 0, 0), 1, '8BUI', 9, 0), 2, '8BUI', 90, 0) AS rast
+       )
+       SELECT ST_Union(rast) AS rast FROM foo;
+
+WITH foo AS (
+       SELECT ST_Tile(rast, 3, 3) AS rast FROM raster_tile
+)
+SELECT
+       ST_DumpValues(rast)
+FROM foo;
+
+WITH foo AS (
+       SELECT ST_Tile(rast, 3, 3, ARRAY[1]) AS rast FROM raster_tile
+)
+SELECT
+       ST_DumpValues(rast)
+FROM foo;
+
+WITH foo AS (
+       SELECT ST_Tile(rast, 2, 2) AS rast FROM raster_tile
+)
+SELECT
+       ST_DumpValues(rast)
+FROM foo;
+
+WITH foo AS (
+       SELECT ST_Tile(rast, 1, 1) AS rast FROM raster_tile
+)
+SELECT
+       ST_DumpValues(rast)
+FROM foo;
+
+WITH foo AS (
+       SELECT ST_Tile(rast, 5, 5) AS rast FROM raster_tile
+)
+SELECT
+       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
new file mode 100644 (file)
index 0000000..9cbc538
--- /dev/null
@@ -0,0 +1,248 @@
+NOTICE:  table "raster_tile" does not exist, skipping
+(1,"{{1,1,1},{1,1,1},{1,1,1}}")
+(2,"{{10,10,10},{10,10,10},{10,10,10}}")
+(1,"{{2,2,2},{2,2,2},{2,2,2}}")
+(2,"{{20,20,20},{20,20,20},{20,20,20}}")
+(1,"{{3,3,3},{3,3,3},{3,3,3}}")
+(2,"{{30,30,30},{30,30,30},{30,30,30}}")
+(1,"{{4,4,4},{4,4,4},{4,4,4}}")
+(2,"{{40,40,40},{40,40,40},{40,40,40}}")
+(1,"{{5,5,5},{5,5,5},{5,5,5}}")
+(2,"{{50,50,50},{50,50,50},{50,50,50}}")
+(1,"{{6,6,6},{6,6,6},{6,6,6}}")
+(2,"{{60,60,60},{60,60,60},{60,60,60}}")
+(1,"{{7,7,7},{7,7,7},{7,7,7}}")
+(2,"{{70,70,70},{70,70,70},{70,70,70}}")
+(1,"{{8,8,8},{8,8,8},{8,8,8}}")
+(2,"{{80,80,80},{80,80,80},{80,80,80}}")
+(1,"{{9,9,9},{9,9,9},{9,9,9}}")
+(2,"{{90,90,90},{90,90,90},{90,90,90}}")
+(1,"{{1,1,1},{1,1,1},{1,1,1}}")
+(1,"{{2,2,2},{2,2,2},{2,2,2}}")
+(1,"{{3,3,3},{3,3,3},{3,3,3}}")
+(1,"{{4,4,4},{4,4,4},{4,4,4}}")
+(1,"{{5,5,5},{5,5,5},{5,5,5}}")
+(1,"{{6,6,6},{6,6,6},{6,6,6}}")
+(1,"{{7,7,7},{7,7,7},{7,7,7}}")
+(1,"{{8,8,8},{8,8,8},{8,8,8}}")
+(1,"{{9,9,9},{9,9,9},{9,9,9}}")
+(1,"{{1,1},{1,1}}")
+(2,"{{10,10},{10,10}}")
+(1,"{{1,2},{1,2}}")
+(2,"{{10,20},{10,20}}")
+(1,"{{2,2},{2,2}}")
+(2,"{{20,20},{20,20}}")
+(1,"{{3,3},{3,3}}")
+(2,"{{30,30},{30,30}}")
+(1,"{{3,NULL},{3,NULL}}")
+(2,"{{30,NULL},{30,NULL}}")
+(1,"{{1,1},{4,4}}")
+(2,"{{10,10},{40,40}}")
+(1,"{{1,2},{4,5}}")
+(2,"{{10,20},{40,50}}")
+(1,"{{2,2},{5,5}}")
+(2,"{{20,20},{50,50}}")
+(1,"{{3,3},{6,6}}")
+(2,"{{30,30},{60,60}}")
+(1,"{{3,NULL},{6,NULL}}")
+(2,"{{30,NULL},{60,NULL}}")
+(1,"{{4,4},{4,4}}")
+(2,"{{40,40},{40,40}}")
+(1,"{{4,5},{4,5}}")
+(2,"{{40,50},{40,50}}")
+(1,"{{5,5},{5,5}}")
+(2,"{{50,50},{50,50}}")
+(1,"{{6,6},{6,6}}")
+(2,"{{60,60},{60,60}}")
+(1,"{{6,NULL},{6,NULL}}")
+(2,"{{60,NULL},{60,NULL}}")
+(1,"{{7,7},{7,7}}")
+(2,"{{70,70},{70,70}}")
+(1,"{{7,8},{7,8}}")
+(2,"{{70,80},{70,80}}")
+(1,"{{8,8},{8,8}}")
+(2,"{{80,80},{80,80}}")
+(1,"{{9,9},{9,9}}")
+(2,"{{90,90},{90,90}}")
+(1,"{{9,NULL},{9,NULL}}")
+(2,"{{90,NULL},{90,NULL}}")
+(1,"{{7,7},{NULL,NULL}}")
+(2,"{{70,70},{NULL,NULL}}")
+(1,"{{7,8},{NULL,NULL}}")
+(2,"{{70,80},{NULL,NULL}}")
+(1,"{{8,8},{NULL,NULL}}")
+(2,"{{80,80},{NULL,NULL}}")
+(1,"{{9,9},{NULL,NULL}}")
+(2,"{{90,90},{NULL,NULL}}")
+(1,"{{9,NULL},{NULL,NULL}}")
+(2,"{{90,NULL},{NULL,NULL}}")
+(1,{{1}})
+(2,{{10}})
+(1,{{1}})
+(2,{{10}})
+(1,{{1}})
+(2,{{10}})
+(1,{{2}})
+(2,{{20}})
+(1,{{2}})
+(2,{{20}})
+(1,{{2}})
+(2,{{20}})
+(1,{{3}})
+(2,{{30}})
+(1,{{3}})
+(2,{{30}})
+(1,{{3}})
+(2,{{30}})
+(1,{{1}})
+(2,{{10}})
+(1,{{1}})
+(2,{{10}})
+(1,{{1}})
+(2,{{10}})
+(1,{{2}})
+(2,{{20}})
+(1,{{2}})
+(2,{{20}})
+(1,{{2}})
+(2,{{20}})
+(1,{{3}})
+(2,{{30}})
+(1,{{3}})
+(2,{{30}})
+(1,{{3}})
+(2,{{30}})
+(1,{{1}})
+(2,{{10}})
+(1,{{1}})
+(2,{{10}})
+(1,{{1}})
+(2,{{10}})
+(1,{{2}})
+(2,{{20}})
+(1,{{2}})
+(2,{{20}})
+(1,{{2}})
+(2,{{20}})
+(1,{{3}})
+(2,{{30}})
+(1,{{3}})
+(2,{{30}})
+(1,{{3}})
+(2,{{30}})
+(1,{{4}})
+(2,{{40}})
+(1,{{4}})
+(2,{{40}})
+(1,{{4}})
+(2,{{40}})
+(1,{{5}})
+(2,{{50}})
+(1,{{5}})
+(2,{{50}})
+(1,{{5}})
+(2,{{50}})
+(1,{{6}})
+(2,{{60}})
+(1,{{6}})
+(2,{{60}})
+(1,{{6}})
+(2,{{60}})
+(1,{{4}})
+(2,{{40}})
+(1,{{4}})
+(2,{{40}})
+(1,{{4}})
+(2,{{40}})
+(1,{{5}})
+(2,{{50}})
+(1,{{5}})
+(2,{{50}})
+(1,{{5}})
+(2,{{50}})
+(1,{{6}})
+(2,{{60}})
+(1,{{6}})
+(2,{{60}})
+(1,{{6}})
+(2,{{60}})
+(1,{{4}})
+(2,{{40}})
+(1,{{4}})
+(2,{{40}})
+(1,{{4}})
+(2,{{40}})
+(1,{{5}})
+(2,{{50}})
+(1,{{5}})
+(2,{{50}})
+(1,{{5}})
+(2,{{50}})
+(1,{{6}})
+(2,{{60}})
+(1,{{6}})
+(2,{{60}})
+(1,{{6}})
+(2,{{60}})
+(1,{{7}})
+(2,{{70}})
+(1,{{7}})
+(2,{{70}})
+(1,{{7}})
+(2,{{70}})
+(1,{{8}})
+(2,{{80}})
+(1,{{8}})
+(2,{{80}})
+(1,{{8}})
+(2,{{80}})
+(1,{{9}})
+(2,{{90}})
+(1,{{9}})
+(2,{{90}})
+(1,{{9}})
+(2,{{90}})
+(1,{{7}})
+(2,{{70}})
+(1,{{7}})
+(2,{{70}})
+(1,{{7}})
+(2,{{70}})
+(1,{{8}})
+(2,{{80}})
+(1,{{8}})
+(2,{{80}})
+(1,{{8}})
+(2,{{80}})
+(1,{{9}})
+(2,{{90}})
+(1,{{9}})
+(2,{{90}})
+(1,{{9}})
+(2,{{90}})
+(1,{{7}})
+(2,{{70}})
+(1,{{7}})
+(2,{{70}})
+(1,{{7}})
+(2,{{70}})
+(1,{{8}})
+(2,{{80}})
+(1,{{8}})
+(2,{{80}})
+(1,{{8}})
+(2,{{80}})
+(1,{{9}})
+(2,{{90}})
+(1,{{9}})
+(2,{{90}})
+(1,{{9}})
+(2,{{90}})
+(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}}")
+(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}}")
+(1,"{{2,3,3,3,NULL},{2,3,3,3,NULL},{2,3,3,3,NULL},{5,6,6,6,NULL},{5,6,6,6,NULL}}")
+(2,"{{20,30,30,30,NULL},{20,30,30,30,NULL},{20,30,30,30,NULL},{50,60,60,60,NULL},{50,60,60,60,NULL}}")
+(1,"{{4,4,4,5,5},{7,7,7,8,8},{7,7,7,8,8},{7,7,7,8,8},{NULL,NULL,NULL,NULL,NULL}}")
+(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}}")
+(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}}")
+(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}}")