]> granicus.if.org Git - postgis/commitdiff
Refactored ST_PixelAsPolygons to call a C function and have enhanced
authorBborie Park <bkpark at ucdavis.edu>
Tue, 3 Jul 2012 22:29:43 +0000 (22:29 +0000)
committerBborie Park <bkpark at ucdavis.edu>
Tue, 3 Jul 2012 22:29:43 +0000 (22:29 +0000)
capabilities.  Additional variants and regression tests are next.

git-svn-id: http://svn.osgeo.org/postgis/trunk@10020 b70326c6-7e19-0410-871a-916f4a2858ee

doc/reference_raster.xml
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/core/testapi.c
raster/test/regress/rt_intersection.sql
raster/test/regress/rt_intersection_expected

index 8be56817fefff098e7bc060e86ebf35dbea08083..8960bde73cb9a4a4b333dcc724ab304a8ce6e3d8 100644 (file)
@@ -6225,6 +6225,7 @@ FROM (SELECT ST_SetRotation(rast, 0.1, 0.1) As rast
                                <funcdef>setof geomval <function>ST_DumpAsPolygons</function></funcdef>
                                <paramdef><type>raster </type> <parameter>rast</parameter></paramdef>
                                <paramdef choice='opt'><type>integer </type> <parameter>band_num=1</parameter></paramdef>
+                               <paramdef choice='opt'><type>boolean </type> <parameter>exclude_nodata_value=TRUE</parameter></paramdef>
                          </funcprototype>
                        </funcsynopsis>
                  </refsynopsisdiv>
index e640824f46833cc04d03dbb901d3a1ea931bed9e..65a96b14bf6c6b82883121272884735c72b6a4f8 100644 (file)
@@ -5641,6 +5641,8 @@ rt_raster_geopoint_to_cell(rt_raster raster,
  *
  * @param raster: the raster to get info from.
  * @param nband: the band to polygonize. 0-based
+ * @param exclude_nodata_value: if non-zero, ignore nodata values
+ * to check for pixels with value
  *
  * @return A set of "geomval" values, one for each group of pixels
  * sharing the same value for the provided band. The returned values are
@@ -5649,6 +5651,7 @@ rt_raster_geopoint_to_cell(rt_raster raster,
 rt_geomval
 rt_raster_gdal_polygonize(
        rt_raster raster, int nband,
+       int exclude_nodata_value,
        int *pnElements
 ) {
        CPLErr cplerr = CE_None;
@@ -5684,6 +5687,7 @@ rt_raster_gdal_polygonize(
 #endif
 
        uint32_t bandNums[1] = {nband};
+       int excludeNodataValues[1] = {exclude_nodata_value};
 
        /* checks */
        assert(NULL != raster);
@@ -5700,13 +5704,15 @@ rt_raster_gdal_polygonize(
                return NULL;
        }
 
-       iBandHasNodataValue = rt_band_get_hasnodata_flag(band);
-       if (iBandHasNodataValue) dBandNoData = rt_band_get_nodata(band);
+       if (exclude_nodata_value) {
+               iBandHasNodataValue = rt_band_get_hasnodata_flag(band);
+               if (iBandHasNodataValue) dBandNoData = rt_band_get_nodata(band);
+       }
 
        /*****************************************************
         * Convert raster to GDAL MEM dataset
         *****************************************************/
-       memdataset = rt_raster_to_gdal_mem(raster, NULL, bandNums, 1, &gdal_drv);
+       memdataset = rt_raster_to_gdal_mem(raster, NULL, bandNums, excludeNodataValues, 1, &gdal_drv);
        if (NULL == memdataset) {
                rterror("rt_raster_gdal_polygonize: Couldn't convert raster to GDAL MEM dataset");
                return NULL;
@@ -8118,7 +8124,7 @@ rt_raster_to_gdal(rt_raster raster, const char *srs,
        RASTER_DEBUGF(3, "rt_raster_to_gdal: output format is %s", format);
 
        /* load raster into a GDAL MEM raster */
-       src_ds = rt_raster_to_gdal_mem(raster, srs, NULL, 0, &src_drv);
+       src_ds = rt_raster_to_gdal_mem(raster, srs, NULL, NULL, 0, &src_drv);
        if (NULL == src_ds) {
                rterror("rt_raster_to_gdal: Unable to convert raster to GDAL MEM format");
                return 0;
@@ -8263,15 +8269,23 @@ rt_raster_gdal_drivers(uint32_t *drv_count, uint8_t cancc) {
  * @param raster : raster to convert to GDAL MEM
  * @param srs : the raster's coordinate system in OGC WKT
  * @param bandNums : array of band numbers to extract from raster
- *                   and include in the GDAL dataset (0 based)
+ *   and include in the GDAL dataset (0 based)
+ * @param excludeNodataValues: array of zero, nonzero where if non-zero,
+ *   ignore nodata values for the band
  * @param count : number of elements in bandNums
  * @param rtn_drv : is set to the GDAL driver object
  *
  * @return GDAL dataset using GDAL MEM driver
  */
 GDALDatasetH
-rt_raster_to_gdal_mem(rt_raster raster, const char *srs,
-       uint32_t *bandNums, int count, GDALDriverH *rtn_drv) {
+rt_raster_to_gdal_mem(
+       rt_raster raster,
+       const char *srs,
+       uint32_t *bandNums,
+       int *excludeNodataValues,
+       int count,
+       GDALDriverH *rtn_drv
+) {
        GDALDriverH drv = NULL;
        GDALDatasetH ds = NULL;
        double gt[6] = {0.0};
@@ -8284,6 +8298,7 @@ rt_raster_to_gdal_mem(rt_raster raster, const char *srs,
        char *apszOptions[4];
        double nodata = 0.0;
        int allocBandNums = 0;
+       int allocNodataValues = 0;
 
        int i;
        int numBands;
@@ -8359,12 +8374,25 @@ rt_raster_to_gdal_mem(rt_raster raster, const char *srs,
                for (i = 0; i < count; i++) bandNums[i] = i;
        }
 
+       /* process exclude_nodata_values */
+       if (NULL == excludeNodataValues) {
+               excludeNodataValues = (int *) rtalloc(sizeof(int) * count);
+               if (NULL == excludeNodataValues) {
+                       rterror("rt_raster_to_gdal_mem: Unable to allocate memory for NODATA flags");
+                       GDALClose(ds);
+                       return 0;
+               }
+               allocNodataValues = 1;
+               for (i = 0; i < count; i++) excludeNodataValues[i] = 1;
+       }
+
        /* add band(s) */
        for (i = 0; i < count; i++) {
                rtband = rt_raster_get_band(raster, bandNums[i]);
                if (NULL == rtband) {
                        rterror("rt_raster_to_gdal_mem: Unable to get requested band index %d", bandNums[i]);
                        if (allocBandNums) rtdealloc(bandNums);
+                       if (allocNodataValues) rtdealloc(excludeNodataValues);
                        GDALClose(ds);
                        return 0;
                }
@@ -8418,6 +8446,7 @@ rt_raster_to_gdal_mem(rt_raster raster, const char *srs,
                        if (GDALAddBand(ds, gdal_pt, NULL) == CE_Failure) {
                                rterror("rt_raster_to_gdal_mem: Could not add GDAL raster band");
                                if (allocBandNums) rtdealloc(bandNums);
+                               if (allocNodataValues) rtdealloc(excludeNodataValues);
                                GDALClose(ds);
                                return 0;
                        }
@@ -8427,6 +8456,7 @@ rt_raster_to_gdal_mem(rt_raster raster, const char *srs,
                if (GDALGetRasterCount(ds) != i + 1) {
                        rterror("rt_raster_to_gdal_mem: Error creating GDAL MEM raster band");
                        if (allocBandNums) rtdealloc(bandNums);
+                       if (allocNodataValues) rtdealloc(excludeNodataValues);
                        GDALClose(ds);
                        return 0;
                }
@@ -8437,6 +8467,7 @@ rt_raster_to_gdal_mem(rt_raster raster, const char *srs,
                if (NULL == band) {
                        rterror("rt_raster_to_gdal_mem: Could not get GDAL band for additional processing");
                        if (allocBandNums) rtdealloc(bandNums);
+                       if (allocNodataValues) rtdealloc(excludeNodataValues);
                        GDALClose(ds);
                        return 0;
                }
@@ -8468,6 +8499,7 @@ rt_raster_to_gdal_mem(rt_raster raster, const char *srs,
                        if (NULL == values) {
                                rterror("rt_raster_to_gdal_mem: Could not allocate memory for GDAL band pixel values");
                                if (allocBandNums) rtdealloc(bandNums);
+                               if (allocNodataValues) rtdealloc(excludeNodataValues);
                                GDALClose(ds);
                                return 0;
                        }
@@ -8505,6 +8537,7 @@ rt_raster_to_gdal_mem(rt_raster raster, const char *srs,
                                                                rterror("rt_raster_to_gdal_mem: Could not get pixel value to convert from 8BSI to 16BSI");
                                                                rtdealloc(values);
                                                                if (allocBandNums) rtdealloc(bandNums);
+                                                               if (allocNodataValues) rtdealloc(excludeNodataValues);
                                                                GDALClose(ds);
                                                                return 0;
                                                        }
@@ -8525,6 +8558,7 @@ rt_raster_to_gdal_mem(rt_raster raster, const char *srs,
                                                rterror("rt_raster_to_gdal_mem: Could not write converted 8BSI to 16BSI values to GDAL band");
                                                rtdealloc(values);
                                                if (allocBandNums) rtdealloc(bandNums);
+                                               if (allocNodataValues) rtdealloc(excludeNodataValues);
                                                GDALClose(ds);
                                                return 0;
                                        }
@@ -8535,7 +8569,7 @@ rt_raster_to_gdal_mem(rt_raster raster, const char *srs,
                }
 
                /* Add nodata value for band */
-               if (rt_band_get_hasnodata_flag(rtband) != FALSE) {
+               if (rt_band_get_hasnodata_flag(rtband) != FALSE && excludeNodataValues[i]) {
                        nodata = rt_band_get_nodata(rtband);
                        if (GDALSetRasterNoDataValue(band, nodata) != CE_None)
                                rtwarn("rt_raster_to_gdal_mem: Could not set nodata value for band");
@@ -8547,6 +8581,7 @@ rt_raster_to_gdal_mem(rt_raster raster, const char *srs,
        GDALFlushCache(ds);
 
        if (allocBandNums) rtdealloc(bandNums);
+       if (allocNodataValues) rtdealloc(excludeNodataValues);
 
        return ds;
 }
@@ -8898,7 +8933,7 @@ rt_raster rt_raster_gdal_warp(
                _dst_srs = rt_util_gdal_convert_sr(dst_srs, 0);
 
        /* load raster into a GDAL MEM dataset */
-       src_ds = rt_raster_to_gdal_mem(raster, _src_srs, NULL, 0, &src_drv);
+       src_ds = rt_raster_to_gdal_mem(raster, _src_srs, NULL, NULL, 0, &src_drv);
        if (NULL == src_ds) {
                rterror("rt_raster_gdal_warp: Unable to convert raster to GDAL MEM format");
 
index ed2cabe6b0961bbcef3f023155d1d88c5794fc2a..450843983d85d591227758a5f4dcbf3b65ac854a 100644 (file)
@@ -1188,6 +1188,8 @@ LWPOLY* rt_raster_pixel_as_polygon(rt_raster raster, int x, int y);
  *
  * @param raster: the raster to get info from.
  * @param nband: the band to polygonize. 0-based
+ * @param exclude_nodata_value: if non-zero, ignore nodata values
+ * to check for pixels with value
  *
  * @return A set of "geomval" values, one for each group of pixels
  * sharing the same value for the provided band. The returned values are
@@ -1196,6 +1198,7 @@ LWPOLY* rt_raster_pixel_as_polygon(rt_raster raster, int x, int y);
 rt_geomval
 rt_raster_gdal_polygonize(
        rt_raster raster, int nband,
+       int exclude_nodata_value,
        int * pnElements
 );
 
@@ -1313,14 +1316,23 @@ rt_gdaldriver rt_raster_gdal_drivers(uint32_t *drv_count, uint8_t cancc);
  * @param raster : raster to convert to GDAL MEM
  * @param srs : the raster's coordinate system in OGC WKT
  * @param bandNums : array of band numbers to extract from raster
- *                   and include in the GDAL dataset (0 based)
- * @param count : number of elements in bandNums
+ *   and include in the GDAL dataset (0 based)
+ * @param excludeNodataValues: array of zero, nonzero where if non-zero,
+ *   ignore nodata values for the band
+ * to check for pixels with value
+ * @param count : number of elements in bandNums and exclude_nodata_values
  * @param rtn_drv : is set to the GDAL driver object
  *
  * @return GDAL dataset using GDAL MEM driver
  */
-GDALDatasetH rt_raster_to_gdal_mem(rt_raster raster, const char *srs,
-       uint32_t *bandNums, int count, GDALDriverH *rtn_drv);
+GDALDatasetH rt_raster_to_gdal_mem(
+       rt_raster raster,
+       const char *srs,
+       uint32_t *bandNums,
+       int *excludeNodataValues,
+       int count,
+       GDALDriverH *rtn_drv
+);
 
 /**
  * Return a raster from a GDAL dataset
@@ -1720,6 +1732,8 @@ struct rt_pixel_t {
 
        uint8_t nodata;
        double value;
+
+       LWGEOM *geom;
 };
 
 /* polygon as LWPOLY with associated value */
index 57ffc54ea0ccc1289e33cfffb9c6cbba12d84983..65b8a67a09e600c1fed33998d15c9f603fcb1ec0 100644 (file)
@@ -201,6 +201,7 @@ Datum RASTER_setPixelValue(PG_FUNCTION_ARGS);
 
 /* Get pixel geographical shape */
 Datum RASTER_getPixelPolygon(PG_FUNCTION_ARGS);
+Datum RASTER_getPixelPolygons(PG_FUNCTION_ARGS);
 
 /* Get pixels of value */
 Datum RASTER_pixelOfValue(PG_FUNCTION_ARGS);
@@ -910,6 +911,7 @@ Datum RASTER_dumpAsPolygons(PG_FUNCTION_ARGS) {
                rt_pgraster *pgraster = NULL;
                rt_raster raster = NULL;
                int nband;
+               bool exclude_nodata_value = TRUE;
                int nElements;
 
                POSTGIS_RT_DEBUG(2, "RASTER_dumpAsPolygons first call");
@@ -937,7 +939,7 @@ Datum RASTER_dumpAsPolygons(PG_FUNCTION_ARGS) {
                        SRF_RETURN_DONE(funcctx);
                }
 
-               if (PG_NARGS() == 2)
+               if (!PG_ARGISNULL(1))
                        nband = PG_GETARG_UINT32(1);
                else
                        nband = 1; /* By default, first band */
@@ -953,12 +955,15 @@ Datum RASTER_dumpAsPolygons(PG_FUNCTION_ARGS) {
                        SRF_RETURN_DONE(funcctx);
                }
 
+               if (!PG_ARGISNULL(2))
+                       exclude_nodata_value = PG_GETARG_BOOL(2);
+
                /* Polygonize raster */
 
                /**
                 * Dump raster
                 */
-               geomval = rt_raster_gdal_polygonize(raster, nband - 1, &nElements);
+               geomval = rt_raster_gdal_polygonize(raster, nband - 1, exclude_nodata_value, &nElements);
                rt_raster_destroy(raster);
                PG_FREE_IF_COPY(pgraster, 0);
                if (NULL == geomval) {
@@ -2492,6 +2497,286 @@ Datum RASTER_getPixelPolygon(PG_FUNCTION_ARGS)
     PG_RETURN_POINTER(gser);
 }
 
+/**
+ * Return the geographical shape of all pixels
+ */
+PG_FUNCTION_INFO_V1(RASTER_getPixelPolygons);
+Datum RASTER_getPixelPolygons(PG_FUNCTION_ARGS)
+{
+       FuncCallContext *funcctx;
+       TupleDesc tupdesc;
+       rt_pixel pix = NULL;
+       rt_pixel pix2;
+       int call_cntr;
+       int max_calls;
+       int i = 0;
+
+       /* stuff done only on the first call of the function */
+       if (SRF_IS_FIRSTCALL()) {
+               MemoryContext oldcontext;
+               rt_pgraster *pgraster = NULL;
+               rt_raster raster = NULL;
+               rt_band band = NULL;
+               int hasnodata = FALSE;
+               double nodataval = 0;
+               int nband = 1;
+               int numbands;
+               bool noband = FALSE;
+               bool exclude_nodata_value = TRUE;
+               bool nocolumnx = FALSE;
+               bool norowy = FALSE;
+               int x = 0;
+               int y = 0;
+               int bounds[4] = {0};
+               int pixcount = 0;
+
+               LWPOLY *poly;
+
+               /* 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);
+
+               if (PG_ARGISNULL(0)) {
+                       MemoryContextSwitchTo(oldcontext);
+                       SRF_RETURN_DONE(funcctx);
+               }
+               pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+               /* band */
+               if (PG_ARGISNULL(1))
+                       noband = TRUE;
+               else {
+                       nband = PG_GETARG_INT32(1);
+                       noband = FALSE;
+               }
+
+               /* column */
+               if (PG_ARGISNULL(2))
+                       nocolumnx = TRUE;
+               else {
+                       bounds[0] = PG_GETARG_INT32(2);
+                       bounds[1] = bounds[0];
+               }
+
+               /* row */
+               if (PG_ARGISNULL(3))
+                       norowy = TRUE;
+               else {
+                       bounds[2] = PG_GETARG_INT32(3);
+                       bounds[3] = bounds[2];
+               }
+
+               /* exclude NODATA */
+               if (!PG_ARGISNULL(4))
+                       exclude_nodata_value = PG_GETARG_BOOL(4);
+
+               raster = rt_raster_deserialize(pgraster, FALSE);
+               if (!raster) {
+                       PG_FREE_IF_COPY(pgraster, 0);
+                       ereport(ERROR, (
+                               errcode(ERRCODE_OUT_OF_MEMORY),
+                               errmsg("Could not deserialize raster")
+                       ));
+                       MemoryContextSwitchTo(oldcontext);
+                       SRF_RETURN_DONE(funcctx);
+               }
+
+               /* band specified, load band and info */
+               if (!noband) {
+                       POSTGIS_RT_DEBUGF(3, "band %d", nband);
+                       numbands = rt_raster_get_num_bands(raster);
+
+                       if (nband < 1 || nband > numbands) {
+                               elog(NOTICE, "Invalid band index (must use 1-based). Returning NULL");
+                               rt_raster_destroy(raster);
+                               PG_FREE_IF_COPY(pgraster, 0);
+                               MemoryContextSwitchTo(oldcontext);
+                               SRF_RETURN_DONE(funcctx);
+                       }
+
+                       band = rt_raster_get_band(raster, nband - 1);
+                       if (!band) {
+                               elog(NOTICE, "Could not find band at index %d. Returning NULL", nband);
+                               rt_raster_destroy(raster);
+                               PG_FREE_IF_COPY(pgraster, 0);
+                               MemoryContextSwitchTo(oldcontext);
+                               SRF_RETURN_DONE(funcctx);
+                       }
+
+                       hasnodata = rt_band_get_hasnodata_flag(band);
+                       if (hasnodata)
+                               nodataval = rt_band_get_nodata(band);
+               }
+
+               /* set bounds if columnx, rowy not set */
+               if (nocolumnx) {
+                       bounds[0] = 1;
+                       bounds[1] = rt_raster_get_width(raster);
+               }
+               if (norowy) {
+                       bounds[2] = 1;
+                       bounds[3] = rt_raster_get_height(raster);
+               }
+
+               /* rowy */
+               pixcount = 0;
+               for (y = bounds[2]; y <= bounds[3]; y++) {
+                       /* columnx */
+                       for (x = bounds[0]; x <= bounds[1]; x++) {
+                               /* geometry */
+                               poly = rt_raster_pixel_as_polygon(raster, x - 1, y - 1);
+                               if (!poly) {
+                                       elog(ERROR, "RASTER_getPixelPolygons: Could not get pixel polygon");
+
+                                       for (i = 0; i < pixcount; i++)
+                                               lwgeom_free(pix[i].geom);
+                                       if (pixcount) pfree(pix);
+
+                                       if (!noband) rt_band_destroy(band);
+                                       rt_raster_destroy(raster);
+                                       PG_FREE_IF_COPY(pgraster, 0);
+
+                                       MemoryContextSwitchTo(oldcontext);
+                                       SRF_RETURN_DONE(funcctx);
+                               }
+
+                               if (!pixcount)
+                                       pix = palloc(sizeof(struct rt_pixel_t) * (pixcount + 1));
+                               else
+                                       pix = repalloc(pix, sizeof(struct rt_pixel_t) * (pixcount + 1));
+                               if (pix == NULL) {
+                                       elog(ERROR, "RASTER_getPixelPolygons: Could not allocate memory for storing pixel polygons");
+
+                                       lwpoly_free(poly);
+                                       if (!noband) rt_band_destroy(band);
+                                       rt_raster_destroy(raster);
+                                       PG_FREE_IF_COPY(pgraster, 0);
+
+                                       MemoryContextSwitchTo(oldcontext);
+                                       SRF_RETURN_DONE(funcctx);
+                               }
+                               pix[pixcount].geom = (LWGEOM *) poly;
+                               POSTGIS_RT_DEBUGF(4, "poly @ %p", poly);
+                               POSTGIS_RT_DEBUGF(4, "geom @ %p", pix[pixcount].geom);
+
+                               /* x, y */
+                               pix[pixcount].x = x;
+                               pix[pixcount].y = y;
+
+                               /* value, NODATA flag */
+                               if (!noband) {
+                                       if (rt_band_get_pixel(band, x - 1, y - 1, &(pix[pixcount].value)) != 0) {
+                                               elog(ERROR, "RASTER_getPixelPolygons: Could not get pixel value");
+
+                                               for (i = 0; i < pixcount; i++)
+                                                       lwgeom_free(pix[i].geom);
+                                               if (pixcount) pfree(pix);
+
+                                               if (!noband) rt_band_destroy(band);
+                                               rt_raster_destroy(raster);
+                                               PG_FREE_IF_COPY(pgraster, 0);
+
+                                               MemoryContextSwitchTo(oldcontext);
+                                               SRF_RETURN_DONE(funcctx);
+                                       }
+
+                                       if (
+                                               !exclude_nodata_value || (
+                                                       exclude_nodata_value &&
+                                                       (hasnodata != FALSE) && (
+                                                               FLT_NEQ(pix[pixcount].value, nodataval) &&
+                                                               (rt_band_clamped_value_is_nodata(band, pix[pixcount].value) != 1)
+                                                       )
+                                               )
+                                       ) {
+                                               pix[pixcount].nodata = 0;
+                                       }
+                                       else
+                                               pix[pixcount].nodata = 1;
+                               }
+                               else
+                                       pix[pixcount].nodata = 1;
+
+                               pixcount++;
+                       }
+               }
+
+               if (!noband) rt_band_destroy(band);
+               rt_raster_destroy(raster);
+               PG_FREE_IF_COPY(pgraster, 0);
+
+               /* Store needed information */
+               funcctx->user_fctx = pix;
+
+               /* total number of tuples to be returned */
+               funcctx->max_calls = pixcount;
+
+               /* Build a tuple descriptor for our result type */
+               if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) {
+                       ereport(ERROR, (
+                               errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                               errmsg("function returning record called in context that cannot accept type record")
+                       ));
+               }
+
+               BlessTupleDesc(tupdesc);
+               funcctx->tuple_desc = tupdesc;
+
+               MemoryContextSwitchTo(oldcontext);
+       }
+
+       /* stuff done on every call of the function */
+       funcctx = SRF_PERCALL_SETUP();
+
+       call_cntr = funcctx->call_cntr;
+       max_calls = funcctx->max_calls;
+       tupdesc = funcctx->tuple_desc;
+       pix2 = funcctx->user_fctx;
+
+       /* do when there is more left to send */
+       if (call_cntr < max_calls) {
+               int values_length = 4;
+               Datum values[values_length];
+               bool nulls[values_length];
+               HeapTuple    tuple;
+               Datum        result;
+
+               GSERIALIZED *gser = NULL;
+               size_t gser_size = 0;
+
+               POSTGIS_RT_DEBUGF(3, "call number %d", call_cntr);
+
+               memset(nulls, FALSE, values_length);
+
+               /* convert LWGEOM to GSERIALIZED */
+               gser = gserialized_from_lwgeom(pix2[call_cntr].geom, 0, &gser_size);
+               lwgeom_free(pix2[call_cntr].geom);
+
+               values[0] = PointerGetDatum(gser);
+               if (pix2[call_cntr].nodata)
+                       nulls[1] = TRUE;
+               else
+                       values[1] = Float8GetDatum(pix2[call_cntr].value);
+               values[2] = Int32GetDatum(pix2[call_cntr].x);
+               values[3] = Int32GetDatum(pix2[call_cntr].y);
+
+               /* build a tuple */
+               tuple = heap_form_tuple(tupdesc, values, nulls);
+
+               /* make the tuple into a datum */
+               result = HeapTupleGetDatum(tuple);
+
+               SRF_RETURN_NEXT(funcctx, result);
+       }
+       /* do when there is no more left */
+       else {
+               pfree(pix2);
+               SRF_RETURN_DONE(funcctx);
+       }
+}
+
 /**
  * Get pixels of value
  */
index 2dc880255d7d2e0eb04492d2857c6a7532ac64d8..941d2a18a7d3c9205c4fb58026b5e0e77e532cc0 100644 (file)
@@ -2198,7 +2198,7 @@ CREATE OR REPLACE FUNCTION st_value(rast raster, band integer, pt geometry, excl
                         band,
                         st_world2rastercoordx(rast, x, y),
                         st_world2rastercoordy(rast, x, y),
-                        hasnodata);
+                        exclude_nodata_value);
     END;
     $$
     LANGUAGE 'plpgsql' IMMUTABLE STRICT;
@@ -2484,7 +2484,7 @@ CREATE TYPE geomval AS (
        val double precision
 );
 
-CREATE OR REPLACE FUNCTION st_dumpaspolygons(rast raster, band integer DEFAULT 1)
+CREATE OR REPLACE FUNCTION st_dumpaspolygons(rast raster, band integer DEFAULT 1, exclude_nodata_value boolean DEFAULT TRUE)
        RETURNS SETOF geomval
        AS 'MODULE_PATHNAME','RASTER_dumpAsPolygons'
        LANGUAGE 'c' IMMUTABLE STRICT;
@@ -2497,49 +2497,47 @@ CREATE OR REPLACE FUNCTION st_polygon(rast raster, band integer DEFAULT 1)
     $$
     LANGUAGE 'sql' IMMUTABLE STRICT;
 
-CREATE OR REPLACE FUNCTION st_pixelaspolygon(rast raster, x integer, y integer)
-    RETURNS geometry 
-    AS 'MODULE_PATHNAME','RASTER_getPixelPolygon'
-    LANGUAGE 'c' IMMUTABLE STRICT;
-
 -----------------------------------------------------------------------
 -- ST_PixelAsPolygons
 -- Return all the pixels of a raster as a geom, val, x, y record
 -- Should be called like this:
 -- SELECT (gv).geom, (gv).val FROM (SELECT ST_PixelAsPolygons(rast) gv FROM mytable) foo
 -----------------------------------------------------------------------
-CREATE OR REPLACE FUNCTION ST_PixelAsPolygons(rast raster, band integer DEFAULT 1, OUT geom geometry, OUT val double precision, OUT x int, OUT y int)
-    RETURNS SETOF record AS
-    $$
-    DECLARE
-        rast alias for $1;
-        var_w integer;
-        var_h integer;
-        var_x integer;
-        var_y integer;
-        value float8 := NULL;
-        hasband boolean := TRUE;
-    BEGIN
-        IF rast IS NOT NULL AND NOT ST_IsEmpty(rast) THEN
-            IF ST_HasNoBand(rast, band) THEN
-                RAISE NOTICE 'Raster do not have band %. Returning null values', band;
-                hasband := false;
-            END IF;
-            SELECT ST_Width(rast), ST_Height(rast) INTO var_w, var_h;
-            FOR var_x IN 1..var_w LOOP
-                FOR var_y IN 1..var_h LOOP
-                    IF hasband THEN
-                        value := ST_Value(rast, band, var_x, var_y);
-                    END IF;
-                    SELECT ST_PixelAsPolygon(rast, var_x, var_y), value, var_x, var_y INTO geom,val,x,y;
-                    RETURN NEXT;
-                END LOOP;
-            END LOOP;
-        END IF;
-        RETURN;
-    END;
-    $$
-    LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION _st_pixelaspolygons(
+       rast raster,
+       band integer DEFAULT 1,
+       columnx integer DEFAULT NULL,
+       rowy integer DEFAULT NULL,
+       exclude_nodata_value boolean DEFAULT TRUE,
+       OUT geom geometry,
+       OUT val double precision,
+       OUT x integer,
+       OUT y integer
+)
+       RETURNS SETOF record
+       AS 'MODULE_PATHNAME', 'RASTER_getPixelPolygons'
+       LANGUAGE 'c' IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION st_pixelaspolygons(
+       rast raster,
+       band integer DEFAULT 1,
+       OUT geom geometry,
+       OUT val double precision,
+       OUT x int,
+       OUT y int
+)
+       RETURNS SETOF record
+       AS $$ SELECT geom, val, x, y FROM _st_pixelaspolygons($1, $2, NULL, NULL, TRUE) $$
+       LANGUAGE 'sql' IMMUTABLE STRICT;
+
+-----------------------------------------------------------------------
+-- ST_PixelAsPolygons
+-----------------------------------------------------------------------
+
+CREATE OR REPLACE FUNCTION st_pixelaspolygon(rast raster, x integer, y integer)
+       RETURNS geometry
+       AS $$ SELECT geom FROM _st_pixelaspolygons($1, NULL, $2, $3) $$
+       LANGUAGE 'sql' IMMUTABLE STRICT;
 
 -----------------------------------------------------------------------
 -- Raster Utility Functions
index 893b0fba080314ef2d699739b1ca97c8c764cc2a..83a8317763ea057d72fa5650806874bb754a3749 100644 (file)
@@ -121,7 +121,7 @@ static void testGDALPolygonize() {
        CHECK(!rt_raster_has_no_band(rt, 0));
 
        nPols = 0;
-       gv = rt_raster_gdal_polygonize(rt, 0, &nPols);
+       gv = rt_raster_gdal_polygonize(rt, 0, TRUE, &nPols);
 
        /*
        for (i = 0; i < nPols; i++) {
@@ -176,7 +176,7 @@ static void testGDALPolygonize() {
        CHECK(!rt_raster_has_no_band(rt, 0));
 
        nPols = 0;
-       gv = rt_raster_gdal_polygonize(rt, 0, &nPols);
+       gv = rt_raster_gdal_polygonize(rt, 0, TRUE, &nPols);
 
        /*
        for (i = 0; i < nPols; i++) {
@@ -233,7 +233,7 @@ static void testGDALPolygonize() {
        CHECK(!rt_raster_has_no_band(rt, 0));
 
        nPols = 0;
-       gv = rt_raster_gdal_polygonize(rt, 0, &nPols);
+       gv = rt_raster_gdal_polygonize(rt, 0, TRUE, &nPols);
 
        /*
        for (i = 0; i < nPols; i++) {
@@ -278,7 +278,7 @@ static void testGDALPolygonize() {
        CHECK(!rt_raster_has_no_band(rt, 0));
 
        nPols = 0;
-       gv = rt_raster_gdal_polygonize(rt, 0, &nPols);
+       gv = rt_raster_gdal_polygonize(rt, 0, TRUE, &nPols);
        
        /*
        for (i = 0; i < nPols; i++) {
@@ -318,7 +318,7 @@ static void testGDALPolygonize() {
        CHECK(!rt_raster_has_no_band(rt, 0));
 
        nPols = 0;
-       gv = rt_raster_gdal_polygonize(rt, 0, &nPols);
+       gv = rt_raster_gdal_polygonize(rt, 0, TRUE, &nPols);
 
        /*
        for (i = 0; i < nPols; i++) {
@@ -1703,7 +1703,7 @@ static void testGDALToRaster() {
                }
        }
 
-       gdds = rt_raster_to_gdal_mem(raster, NULL, NULL, 0, &gddrv);
+       gdds = rt_raster_to_gdal_mem(raster, NULL, NULL, NULL, 0, &gddrv);
        CHECK(gddrv);
        CHECK(gdds);
        CHECK((GDALGetRasterXSize(gdds) == xmax));
@@ -1746,7 +1746,7 @@ static void testGDALToRaster() {
                }
        }
 
-       gdds = rt_raster_to_gdal_mem(raster, NULL, NULL, 0, &gddrv);
+       gdds = rt_raster_to_gdal_mem(raster, NULL, NULL, NULL, 0, &gddrv);
        CHECK(gddrv);
        CHECK(gdds);
        CHECK((GDALGetRasterXSize(gdds) == xmax));
index a0188efd39238b7b7e906ff8be0e17dfc0699670..9434c9635d81f69a70ab024c59ffee7d1d1c1396 100644 (file)
@@ -143,10 +143,24 @@ FROM (
 ) 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;
+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
+       CROSS JOIN generate_series(1, 2) band
+) foo
+ORDER BY 1, 2, 3, 4, 5, 7;
 
 DROP TABLE IF EXISTS raster_intersection;
 DROP TABLE IF EXISTS raster_intersection_out;
index bdad7862d3b282f3b38ac925ae59ae8f4f3cd5cb..5ad82374ff6a24c49f25aa21a666da2b6a987569 100644 (file)
 10|13|0.000|-1.800|2|2|1.000|1.000|0.100|0.100|0|1|8BUI|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|2|POLYGON((0 0,1 0,1 1,0 1,0 0))
 0|1|1|1|1|1|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|1|2|1|POLYGON((0 1,1 1,1 2,0 2,0 1))
 0|1|1|1|2|1|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|1|1|POLYGON((1 0,2 0,2 1,1 1,1 0))
 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|1|1|2|2|1|POLYGON((1 1,2 1,2 2,1 2,1 1))
+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|1|1|1|1|POLYGON((1 -1,2 -1,2 0,1 0,1 -1))
 0|2|1|1|1|3|POLYGON((1 -1,2 -1,2 0,1 0,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|2|1|1|2|3|POLYGON((1 0,2 0,2 1,1 1,1 0))
+0|2|1|1|2|1|POLYGON((1 0,2 0,2 1,1 1,1 0))
+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|1|1|1|1|POLYGON((1 1,2 1,2 2,1 2,1 1))
+0|3|1|1|1|1|POLYGON((1 1,2 1,2 2,1 2,1 1))
 0|3|1|1|1|4|POLYGON((1 1,2 1,2 2,1 2,1 1))
+0|3|2|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|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|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|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|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|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|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|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|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|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|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|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|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|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|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|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|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|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|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|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|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|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|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|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|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|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|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|1|1|POLYGON((-1.9 -1,-0.9 -0.9,-0.8 0.1,-1.8 0,-1.9 -1))
+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|1|POLYGON((-1.8 0,-0.8 0.1,-0.7 1.1,-1.7 1,-1.8 0))
+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|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|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|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|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|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|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|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|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|1|4|POLYGON((0 -1.8,1 -1.7,1.1 -0.7,0.1 -0.8,0 -1.8))
+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|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|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|1|1|POLYGON((1 -1.7,2 -1.6,2.1 -0.6,1.1 -0.7,1 -1.7))
+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|1|POLYGON((1.1 -0.7,2.1 -0.6,2.2 0.4,1.2 0.3,1.1 -0.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))
+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))
 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))