From: Bborie Park Date: Tue, 3 Jul 2012 22:29:43 +0000 (+0000) Subject: Refactored ST_PixelAsPolygons to call a C function and have enhanced X-Git-Tag: 2.1.0beta2~831 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ce1cb8b46faa309a4b0026fe4789aab8630ddaa8;p=postgis Refactored ST_PixelAsPolygons to call a C function and have enhanced capabilities. Additional variants and regression tests are next. git-svn-id: http://svn.osgeo.org/postgis/trunk@10020 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/doc/reference_raster.xml b/doc/reference_raster.xml index 8be56817f..8960bde73 100644 --- a/doc/reference_raster.xml +++ b/doc/reference_raster.xml @@ -6225,6 +6225,7 @@ FROM (SELECT ST_SetRotation(rast, 0.1, 0.1) As rast setof geomval ST_DumpAsPolygons raster rast integer band_num=1 + boolean exclude_nodata_value=TRUE diff --git a/raster/rt_core/rt_api.c b/raster/rt_core/rt_api.c index e640824f4..65a96b14b 100644 --- a/raster/rt_core/rt_api.c +++ b/raster/rt_core/rt_api.c @@ -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"); diff --git a/raster/rt_core/rt_api.h b/raster/rt_core/rt_api.h index ed2cabe6b..450843983 100644 --- a/raster/rt_core/rt_api.h +++ b/raster/rt_core/rt_api.h @@ -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 */ diff --git a/raster/rt_pg/rt_pg.c b/raster/rt_pg/rt_pg.c index 57ffc54ea..65b8a67a0 100644 --- a/raster/rt_pg/rt_pg.c +++ b/raster/rt_pg/rt_pg.c @@ -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 */ diff --git a/raster/rt_pg/rtpostgis.sql.in.c b/raster/rt_pg/rtpostgis.sql.in.c index 2dc880255..941d2a18a 100644 --- a/raster/rt_pg/rtpostgis.sql.in.c +++ b/raster/rt_pg/rtpostgis.sql.in.c @@ -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 diff --git a/raster/test/core/testapi.c b/raster/test/core/testapi.c index 893b0fba0..83a831776 100644 --- a/raster/test/core/testapi.c +++ b/raster/test/core/testapi.c @@ -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)); diff --git a/raster/test/regress/rt_intersection.sql b/raster/test/regress/rt_intersection.sql index a0188efd3..9434c9635 100644 --- a/raster/test/regress/rt_intersection.sql +++ b/raster/test/regress/rt_intersection.sql @@ -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; diff --git a/raster/test/regress/rt_intersection_expected b/raster/test/regress/rt_intersection_expected index bdad7862d..5ad82374f 100644 --- a/raster/test/regress/rt_intersection_expected +++ b/raster/test/regress/rt_intersection_expected @@ -23,84 +23,60 @@ 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)) @@ -110,59 +86,35 @@ 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))