From 972305ae7ccf02c789828970cd9081c3af38608e Mon Sep 17 00:00:00 2001 From: Bborie Park Date: Tue, 23 Apr 2013 19:32:06 +0000 Subject: [PATCH] Addition of ST_AddBand(raster, ...) for out-db bands. Ticket #2276 git-svn-id: http://svn.osgeo.org/postgis/trunk@11311 b70326c6-7e19-0410-871a-916f4a2858ee --- NEWS | 8 +- doc/reference_raster.xml | 84 ++++-- raster/rt_core/rt_api.c | 103 +++++-- raster/rt_core/rt_api.h | 12 + raster/rt_pg/rt_pg.c | 359 ++++++++++++++++++++++++ raster/rt_pg/rtpostgis.sql.in | 20 ++ raster/test/regress/rt_addband-post.pl | 8 + raster/test/regress/rt_addband-pre.pl | 54 ++++ raster/test/regress/rt_addband.sql | 42 +++ raster/test/regress/rt_addband_expected | 10 + regress/run_test.pl | 19 +- 11 files changed, 656 insertions(+), 63 deletions(-) create mode 100644 raster/test/regress/rt_addband-post.pl create mode 100644 raster/test/regress/rt_addband-pre.pl diff --git a/NEWS b/NEWS index 0a0e3ccd5..953017829 100644 --- a/NEWS +++ b/NEWS @@ -79,6 +79,7 @@ PostGIS 2.1.0 - #2228, TopoJSON output for TopoGeometry (Sandro Santilli / Vizzuality) - #2123, ST_FromGDALRaster - #613, ST_SetGeoReference with numerical parameters instead of text + - #2276, ST_AddBand(raster) variant for out-db bands * Enhancements * @@ -118,11 +119,12 @@ PostGIS 2.1.0 - #2148, Addition of coverage_tile constraint for raster - #2149, Addition of spatially_unique constraint for raster - TopologySummary output now includes unregistered layers and a count - of missing TopoGeometry objects from their natural layer. + of missing TopoGeometry objects from their natural layer. - ST_HillShade(), ST_Aspect() and ST_Slope() have one new optional - parameter to interpolate NODATA pixels before running the operation. + parameter to interpolate NODATA pixels before running the + operation. - Point variant of ST_SetValue(raster) is now a wrapper around geomval - variant of ST_SetValues(rast). + variant of ST_SetValues(rast). - Proper support for raster band's isnodata flag in core API and loader. - Additional default values for parameters of ST_Aspect and ST_HillShade - #2178, ST_Summary now advertises presence of known srid with an [S] flag diff --git a/doc/reference_raster.xml b/doc/reference_raster.xml index b837bd1e6..f689737b0 100644 --- a/doc/reference_raster.xml +++ b/doc/reference_raster.xml @@ -1036,34 +1036,30 @@ WHERE short_name = 'GTiff') As g; - raster ST_AddBand - + (1) raster ST_AddBand raster rast addbandarg[] addbandargset - raster ST_AddBand - + (2) raster ST_AddBand raster rast + integer index text pixeltype double precision initialvalue=0 double precision nodataval=NULL - raster ST_AddBand - + (3) raster ST_AddBand raster rast - integer index text pixeltype double precision initialvalue=0 double precision nodataval=NULL - raster ST_AddBand - + (4) raster ST_AddBand raster torast raster fromrast integer fromband=1 @@ -1071,14 +1067,31 @@ WHERE short_name = 'GTiff') As g; - raster ST_AddBand - + (5) raster ST_AddBand raster torast raster[] fromrasts integer fromband=1 integer torastindex=at_end + + (6) raster ST_AddBand + raster rast + integer index + text outdbfile + integer[] outdbindex + double precision nodataval=NULL + + + + (7) raster ST_AddBand + raster rast + text outdbfile + integer[] outdbindex + integer index=at_end + double precision nodataval=NULL + + @@ -1086,21 +1099,27 @@ WHERE short_name = 'GTiff') As g; Description - Returns a raster with a new band added in given position (index), of given type, of given initial value, and of given nodata value. If no index is specified, the band is added to the end. If no fromband is specified, band 1 is assumed. Pixel type is a string representation of one of the pixel types specified in . If an existing index is specified all subsequent bands >= that index are incremented by 1. If an initial value greater than the max of the pixel type is specified, then the initial value is set to the highest value allowed by the pixel type. The last version add the fromband from fromrast raster to torast in position torastindex. + Returns a raster with a new band added in given position (index), of given type, of given initial value, and of given nodata value. If no index is specified, the band is added to the end. If no fromband is specified, band 1 is assumed. Pixel type is a string representation of one of the pixel types specified in . If an existing index is specified all subsequent bands >= that index are incremented by 1. If an initial value greater than the max of the pixel type is specified, then the initial value is set to the highest value allowed by the pixel type. + + + + For the variant that takes an array of (Variant 1), a specific addbandarg's index value is relative to the raster at the time when the band described by that addbandarg is being added to the raster. See the Multiple New Bands example below. - For the version that takes an array of , a specific addbandarg's index value is relative to the raster at the time when the band described by that addbandarg is being added to the raster. See the Multiple New Bands example below. + For the variant that takes an array of rasters (Variant 5), if torast is NULL then the fromband band of each raster in the array is accumulated into a new raster. - For the version that takes an array of bands if torast is NULL, then the fromband band of each raster in the array is accumulated into a new raster. + For the variants that take outdbfile (Variants 6 and 7), the value must include the full path to the raster file. The file must also be accessible to the postgres server process. - Enhanced: 2.1.0 support addbandarg was introduced. + + Enhanced: 2.1.0 support for addbandarg added. + Enhanced: 2.1.0 support for new out-db bands added. - Examples: Single New Band versions + Examples: Single New Band -- Add another band of type 8 bit unsigned integer with pixels initialized to 200 @@ -1169,10 +1188,6 @@ FROM ST_BandMetadata( 3 | 32BUI | 12 | f | 4 | 16BUI | 2 | f | - - - - Examples: Multi-Band versions -- Aggregate the 1st band of a table of like rasters into a single raster @@ -1181,9 +1196,32 @@ FROM ST_BandMetadata( -- for 8.4 and below it usually works to order your data in a subselect (but not guaranteed) -- The resulting raster will have a band for each test_type alphabetical by test_type -- For mouse lovers: No mice were harmed in this exercise -SELECT mouse, ST_AddBand(NULL, array_agg(rast ORDER BY test_type), 1 ) As rast - FROM mice_studies - GROUP BY mouse; +SELECT + mouse, + ST_AddBand(NULL, array_agg(rast ORDER BY test_type), 1) As rast +FROM mice_studies +GROUP BY mouse; + + + + + Examples: New Out-db band + +SELECT + * +FROM ST_BandMetadata( + ST_AddBand( + ST_MakeEmptyRaster(10, 10, 0, 0, 1, -1, 0, 0, 0), + '/home/raster/mytestraster.tif'::text, NULL::int[] + ), + ARRAY[]::integer[] +); + + bandnum | pixeltype | nodatavalue | isoutdb | path +---------+-----------+-------------+---------+------ + 1 | 8BUI | | t | /home/raster/mytestraster.tif + 2 | 8BUI | | t | /home/raster/mytestraster.tif + 3 | 8BUI | | t | /home/raster/mytestraster.tif diff --git a/raster/rt_core/rt_api.c b/raster/rt_core/rt_api.c index 4e5a7cf90..6e3a56f9f 100644 --- a/raster/rt_core/rt_api.c +++ b/raster/rt_core/rt_api.c @@ -367,6 +367,62 @@ rt_util_gdal_supported_sr(const char *srs) { return 0; } +/** + * Get auth name and code + * + * @param authname: authority organization of code. calling function + * is expected to free the memory allocated for value + * @param authcode: code assigned by authority organization. calling function + * is expected to free the memory allocated for value + * + * @return ES_NONE on success, ES_ERROR on error + */ +rt_errorstate +rt_util_gdal_sr_auth_info(GDALDatasetH hds, char **authname, char **authcode) { + const char *srs = NULL; + + assert(authname != NULL); + assert(authcode != NULL); + + *authname = NULL; + *authcode = NULL; + + srs = GDALGetProjectionRef(hds); + if (srs != NULL && srs[0] != '\0') { + OGRSpatialReferenceH hSRS = OSRNewSpatialReference(NULL); + + if (OSRSetFromUserInput(hSRS, srs) == OGRERR_NONE) { + const char* pszAuthorityName = OSRGetAuthorityName(hSRS, NULL); + const char* pszAuthorityCode = OSRGetAuthorityCode(hSRS, NULL); + + if (pszAuthorityName != NULL && pszAuthorityCode != NULL) { + *authname = rtalloc(sizeof(char) * (strlen(pszAuthorityName) + 1)); + *authcode = rtalloc(sizeof(char) * (strlen(pszAuthorityCode) + 1)); + + if (*authname == NULL || *authcode == NULL) { + rterror("rt_util_gdal_sr_auth_info: Unable to allocate memory for auth name and code"); + if (*authname != NULL) rtdealloc(*authname); + if (*authcode != NULL) rtdealloc(*authcode); + OSRDestroySpatialReference(hSRS); + return ES_ERROR; + } + + strncpy(*authname, pszAuthorityName, strlen(pszAuthorityName) + 1); + strncpy(*authcode, pszAuthorityCode, strlen(pszAuthorityCode) + 1); + } + else { + rtinfo("Cound not get auth name and code. The SRS may be custom"); + OSRDestroySpatialReference(hSRS); + return ES_NONE; + } + } + + OSRDestroySpatialReference(hSRS); + } + + return ES_NONE; +} + /* is GDAL configured correctly? */ @@ -1663,11 +1719,11 @@ rt_band_load_offline_data(rt_band band) { _rast = rt_raster_new(1, 1); rt_raster_set_geotransform_matrix(_rast, ogt); rt_raster_set_srid(_rast, band->raster->srid); - err =rt_raster_same_alignment(band->raster, _rast, &aligned, NULL); + err = rt_raster_same_alignment(band->raster, _rast, &aligned, NULL); rt_raster_destroy(_rast); if (err != ES_NONE) { - rterror("rt_band_load_offline_data: : Could not test alignment of in-db representation of out-db raster"); + rterror("rt_band_load_offline_data: Could not test alignment of in-db representation of out-db raster"); GDALClose(hdsSrc); return ES_ERROR; } @@ -9146,9 +9202,8 @@ rt_raster_from_gdal_dataset(GDALDatasetH ds) { uint32_t height = 0; uint32_t numBands = 0; int i = 0; - int status; - - const char *srs = NULL; + char *authname = NULL; + char *authcode = NULL; GDALRasterBandH gdband = NULL; GDALDataType gdpixtype = GDT_Unknown; @@ -9156,7 +9211,7 @@ rt_raster_from_gdal_dataset(GDALDatasetH ds) { int32_t idx; rt_pixtype pt = PT_END; uint32_t ptlen = 0; - uint32_t hasnodata = 0; + int hasnodata = 0; double nodataval; int x; @@ -9207,22 +9262,20 @@ rt_raster_from_gdal_dataset(GDALDatasetH ds) { gt[0], gt[1], gt[2], gt[3], gt[4], gt[5]); /* srid */ - srs = GDALGetProjectionRef(ds); - if (srs != NULL && srs[0] != '\0') { - OGRSpatialReferenceH hSRS = OSRNewSpatialReference(NULL); - if (OSRSetFromUserInput(hSRS, srs) == OGRERR_NONE) { - const char* pszAuthorityName = OSRGetAuthorityName(hSRS, NULL); - const char* pszAuthorityCode = OSRGetAuthorityCode(hSRS, NULL); - if ( - pszAuthorityName != NULL && - strcmp(pszAuthorityName, "EPSG") == 0 && - pszAuthorityCode != NULL - ) { - rt_raster_set_srid(rast, atoi(pszAuthorityCode)); - RASTER_DEBUGF(3, "New raster's SRID = %d", rast->srid); - } + if (rt_util_gdal_sr_auth_info(ds, &authname, &authcode) == ES_NONE) { + if ( + authname != NULL && + strcmp(authname, "EPSG") == 0 && + authcode != NULL + ) { + rt_raster_set_srid(rast, atoi(authcode)); + RASTER_DEBUGF(3, "New raster's SRID = %d", rast->srid); } - OSRDestroySpatialReference(hSRS); + + if (authname != NULL) + rtdealloc(authname); + if (authcode != NULL) + rtdealloc(authcode); } numBands = GDALGetRasterCount(ds); @@ -9270,13 +9323,7 @@ rt_raster_from_gdal_dataset(GDALDatasetH ds) { RASTER_DEBUGF(3, "GDAL band dimensions (width x height): %d x %d", width, height); /* nodata */ - nodataval = GDALGetRasterNoDataValue(gdband, &status); - if (!status) { - nodataval = 0; - hasnodata = 0; - } - else - hasnodata = 1; + nodataval = GDALGetRasterNoDataValue(gdband, &hasnodata); RASTER_DEBUGF(3, "(hasnodata, nodataval) = (%d, %f)", hasnodata, nodataval); /* create band object */ diff --git a/raster/rt_core/rt_api.h b/raster/rt_core/rt_api.h index ccc83c2d4..b225b1c4c 100644 --- a/raster/rt_core/rt_api.h +++ b/raster/rt_core/rt_api.h @@ -2071,6 +2071,18 @@ rt_util_gdal_convert_sr(const char *srs, int proj4); int rt_util_gdal_supported_sr(const char *srs); +/** + * Get auth name and code + * + * @param authname: authority organization of code. calling function + * is expected to free the memory allocated for value + * @param authcode: code assigned by authority organization. calling function + * is expected to free the memory allocated for value + * + * @return ES_NONE on success, ES_ERROR on error + */ +rt_errorstate rt_util_gdal_sr_auth_info(GDALDatasetH hds, char **authname, char **authcode); + /* is GDAL configured correctly? */ diff --git a/raster/rt_pg/rt_pg.c b/raster/rt_pg/rt_pg.c index 55b49d70d..7bce5dd4c 100644 --- a/raster/rt_pg/rt_pg.c +++ b/raster/rt_pg/rt_pg.c @@ -286,6 +286,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_addBandOutDB(PG_FUNCTION_ARGS); Datum RASTER_tile(PG_FUNCTION_ARGS); /* create new raster from existing raster's bands */ @@ -5401,6 +5402,364 @@ Datum RASTER_addBandRasterArray(PG_FUNCTION_ARGS) PG_RETURN_NULL(); } +/** + * Add out-db band to the given raster at the given position + */ +PG_FUNCTION_INFO_V1(RASTER_addBandOutDB); +Datum RASTER_addBandOutDB(PG_FUNCTION_ARGS) +{ + rt_pgraster *pgraster = NULL; + rt_pgraster *pgrtn = NULL; + + rt_raster raster = NULL; + rt_band band = NULL; + int numbands = 0; + int dstnband = 1; /* 1-based */ + int appendband = FALSE; + char *outdbfile = NULL; + int *srcnband = NULL; /* 1-based */ + int numsrcnband = 0; + int allbands = FALSE; + int hasnodata = FALSE; + double nodataval = 0.; + uint16_t width = 0; + uint16_t height = 0; + char *authname = NULL; + char *authcode = NULL; + + int i = 0; + int j = 0; + + GDALDatasetH hdsOut; + GDALRasterBandH hbandOut; + GDALDataType gdpixtype; + + rt_pixtype pt = PT_END; + double gt[6] = {0.}; + double ogt[6] = {0.}; + rt_raster _rast = NULL; + int aligned = 0; + int err = 0; + + /* destination raster */ + if (!PG_ARGISNULL(0)) { + pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + + /* raster */ + raster = rt_raster_deserialize(pgraster, FALSE); + if (!raster) { + elog(ERROR, "RASTER_addBandOutDB: Could not deserialize destination raster"); + PG_FREE_IF_COPY(pgraster, 0); + PG_RETURN_NULL(); + } + + POSTGIS_RT_DEBUG(4, "destination raster isn't NULL"); + } + + /* destination band index (1) */ + if (!PG_ARGISNULL(1)) + dstnband = PG_GETARG_INT32(1); + else + appendband = TRUE; + + /* outdb file (2) */ + if (PG_ARGISNULL(2)) { + elog(NOTICE, "Out-db raster file not provided. Returning original raster"); + if (pgraster != NULL) { + rt_raster_destroy(raster); + PG_RETURN_POINTER(pgraster); + } + else + PG_RETURN_NULL(); + } + else { + outdbfile = text_to_cstring(PG_GETARG_TEXT_P(2)); + if (!strlen(outdbfile)) { + elog(NOTICE, "Out-db raster file not provided. Returning original raster"); + if (pgraster != NULL) { + rt_raster_destroy(raster); + PG_RETURN_POINTER(pgraster); + } + else + PG_RETURN_NULL(); + } + } + + /* outdb band index (3) */ + if (!PG_ARGISNULL(3)) { + ArrayType *array; + Oid etype; + Datum *e; + bool *nulls; + + int16 typlen; + bool typbyval; + char typalign; + + allbands = FALSE; + + 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_addBandOutDB: Invalid data type for band indexes"); + if (pgraster != NULL) { + rt_raster_destroy(raster); + PG_FREE_IF_COPY(pgraster, 0); + } + PG_RETURN_NULL(); + break; + } + + deconstruct_array(array, etype, typlen, typbyval, typalign, &e, &nulls, &numsrcnband); + + srcnband = palloc(sizeof(int) * numsrcnband); + if (srcnband == NULL) { + elog(ERROR, "RASTER_addBandOutDB: Unable to allocate memory for band indexes"); + if (pgraster != NULL) { + rt_raster_destroy(raster); + PG_FREE_IF_COPY(pgraster, 0); + } + PG_RETURN_NULL(); + } + + for (i = 0, j = 0; i < numsrcnband; i++) { + if (nulls[i]) continue; + + switch (etype) { + case INT2OID: + srcnband[j] = DatumGetInt16(e[i]); + break; + case INT4OID: + srcnband[j] = DatumGetInt32(e[i]); + break; + } + j++; + } + + if (j < numsrcnband) { + srcnband = repalloc(srcnband, sizeof(int) * j); + if (srcnband == NULL) { + elog(ERROR, "RASTER_addBandOutDB: Unable to reallocate memory for band indexes"); + if (pgraster != NULL) { + rt_raster_destroy(raster); + PG_FREE_IF_COPY(pgraster, 0); + } + PG_RETURN_NULL(); + } + + numsrcnband = j; + } + } + else + allbands = TRUE; + + /* nodataval (4) */ + if (!PG_ARGISNULL(4)) { + hasnodata = TRUE; + nodataval = PG_GETARG_FLOAT8(4); + } + else + hasnodata = FALSE; + + /* validate input */ + + /* make sure dstnband is valid */ + if (raster != NULL) { + numbands = rt_raster_get_num_bands(raster); + if (!appendband) { + if (dstnband < 1) { + elog(NOTICE, "Invalid band index %d for adding bands. Using band index 1", dstnband); + dstnband = 1; + } + else if (dstnband > numbands) { + elog(NOTICE, "Invalid band index %d for adding bands. Using band index %d", dstnband, numbands); + dstnband = numbands + 1; + } + } + else + dstnband = numbands + 1; + } + + /* open outdb raster file */ + rt_util_gdal_register_all(); + hdsOut = GDALOpenShared(outdbfile, GA_ReadOnly); + if (hdsOut == NULL) { + elog(ERROR, "RASTER_addBandOutDB: Could not open out-db file with GDAL"); + if (pgraster != NULL) { + rt_raster_destroy(raster); + PG_FREE_IF_COPY(pgraster, 0); + } + PG_RETURN_NULL(); + } + + /* get offline raster's geotransform */ + if (GDALGetGeoTransform(hdsOut, ogt) != CE_None) { + ogt[0] = 0; + ogt[1] = 1; + ogt[2] = 0; + ogt[3] = 0; + ogt[4] = 0; + ogt[5] = -1; + } + + /* raster doesn't exist, create it now */ + if (raster == NULL) { + raster = rt_raster_new(GDALGetRasterXSize(hdsOut), GDALGetRasterYSize(hdsOut)); + if (rt_raster_is_empty(raster)) { + elog(ERROR, "RASTER_addBandOutDB: Could not create new raster"); + PG_RETURN_NULL(); + } + rt_raster_set_geotransform_matrix(raster, ogt); + rt_raster_get_geotransform_matrix(raster, gt); + + if (rt_util_gdal_sr_auth_info(hdsOut, &authname, &authcode) == ES_NONE) { + if ( + authname != NULL && + strcmp(authname, "EPSG") == 0 && + authcode != NULL + ) { + rt_raster_set_srid(raster, atoi(authcode)); + } + else + elog(INFO, "Unknown SRS auth name and code from out-db file. Defaulting SRID of new raster to %d", SRID_UNKNOWN); + } + else + elog(INFO, "Unable to get SRS auth name and code from out-db file. Defaulting SRID of new raster to %d", SRID_UNKNOWN); + } + + /* some raster info */ + width = rt_raster_get_width(raster); + height = rt_raster_get_width(raster); + + /* are rasters aligned? */ + _rast = rt_raster_new(1, 1); + rt_raster_set_geotransform_matrix(_rast, ogt); + rt_raster_set_srid(_rast, rt_raster_get_srid(raster)); + err = rt_raster_same_alignment(raster, _rast, &aligned, NULL); + rt_raster_destroy(_rast); + + if (err != ES_NONE) { + elog(ERROR, "RASTER_addBandOutDB: Could not test alignment of out-db file"); + GDALClose(hdsOut); + if (raster != NULL) + rt_raster_destroy(raster); + if (pgraster != NULL) + PG_FREE_IF_COPY(pgraster, 0); + return ES_ERROR; + } + else if (!aligned) + elog(WARNING, "The in-db representation of the out-db raster is not aligned. Band data may be incorrect"); + + numbands = GDALGetRasterCount(hdsOut); + + /* build up srcnband */ + if (allbands) { + numsrcnband = numbands; + srcnband = palloc(sizeof(int) * numsrcnband); + if (srcnband == NULL) { + elog(ERROR, "RASTER_addBandOutDB: Unable to allocate memory for band indexes"); + GDALClose(hdsOut); + if (raster != NULL) + rt_raster_destroy(raster); + if (pgraster != NULL) + PG_FREE_IF_COPY(pgraster, 0); + PG_RETURN_NULL(); + } + + for (i = 0, j = 1; i < numsrcnband; i++, j++) + srcnband[i] = j; + } + + /* check band properties and add band */ + for (i = 0, j = dstnband - 1; i < numsrcnband; i++, j++) { + /* valid index? */ + if (srcnband[i] < 1 || srcnband[i] > numbands) { + elog(NOTICE, "Out-db file does not have a band at index %d. Returning original raster", srcnband[i]); + GDALClose(hdsOut); + if (raster != NULL) + rt_raster_destroy(raster); + if (pgraster != NULL) + PG_RETURN_POINTER(pgraster); + else + PG_RETURN_NULL(); + } + + /* get outdb band */ + hbandOut = NULL; + hbandOut = GDALGetRasterBand(hdsOut, srcnband[i]); + if (NULL == hbandOut) { + elog(ERROR, "RASTER_addBandOutDB: Unable to get band %d from GDAL dataset", srcnband[i]); + GDALClose(hdsOut); + if (raster != NULL) + rt_raster_destroy(raster); + if (pgraster != NULL) + PG_FREE_IF_COPY(pgraster, 0); + PG_RETURN_NULL(); + } + + /* supported pixel type */ + gdpixtype = GDALGetRasterDataType(hbandOut); + pt = rt_util_gdal_datatype_to_pixtype(gdpixtype); + if (pt == PT_END) { + elog(NOTICE, "Pixel type %s of band %d from GDAL dataset is not supported. Returning original raster", GDALGetDataTypeName(gdpixtype), srcnband[i]); + GDALClose(hdsOut); + if (raster != NULL) + rt_raster_destroy(raster); + if (pgraster != NULL) + PG_RETURN_POINTER(pgraster); + else + PG_RETURN_NULL(); + } + + /* use out-db band's nodata value if nodataval not already set */ + if (!hasnodata) + nodataval = GDALGetRasterNoDataValue(hbandOut, &hasnodata); + + /* add band */ + band = rt_band_new_offline( + width, height, + pt, + hasnodata, nodataval, + srcnband[i] - 1, outdbfile + ); + if (band == NULL) { + elog(ERROR, "RASTER_addBandOutDB: Unable to create new out-db band"); + GDALClose(hdsOut); + if (raster != NULL) + rt_raster_destroy(raster); + if (pgraster != NULL) + PG_FREE_IF_COPY(pgraster, 0); + PG_RETURN_NULL(); + } + + if (rt_raster_add_band(raster, band, j) < 0) { + elog(ERROR, "RASTER_addBandOutDB: Unable to add new out-db band to raster"); + GDALClose(hdsOut); + if (raster != NULL) + rt_raster_destroy(raster); + if (pgraster != NULL) + PG_FREE_IF_COPY(pgraster, 0); + PG_RETURN_NULL(); + } + } + + pgrtn = rt_raster_serialize(raster); + rt_raster_destroy(raster); + if (pgraster != NULL) + PG_FREE_IF_COPY(pgraster, 0); + if (!pgrtn) + PG_RETURN_NULL(); + + SET_VARSIZE(pgrtn, pgrtn->size); + PG_RETURN_POINTER(pgrtn); +} + /** * Break up a raster into smaller tiles. SRF function */ diff --git a/raster/rt_pg/rtpostgis.sql.in b/raster/rt_pg/rtpostgis.sql.in index fd6e8bd84..184f10133 100644 --- a/raster/rt_pg/rtpostgis.sql.in +++ b/raster/rt_pg/rtpostgis.sql.in @@ -303,6 +303,26 @@ CREATE OR REPLACE FUNCTION st_addband( AS 'MODULE_PATHNAME', 'RASTER_addBandRasterArray' LANGUAGE 'c' IMMUTABLE; +CREATE OR REPLACE FUNCTION st_addband( + rast raster, + index int, + outdbfile text, outdbindex int[], + nodataval double precision DEFAULT NULL +) + RETURNS raster + AS 'MODULE_PATHNAME', 'RASTER_addBandOutDB' + LANGUAGE 'c' IMMUTABLE; + +CREATE OR REPLACE FUNCTION st_addband( + rast raster, + outdbfile text, outdbindex int[], + index int DEFAULT NULL, + nodataval double precision DEFAULT NULL +) + RETURNS raster + AS $$ SELECT ST_AddBand($1, $4, $2, $3, $5) $$ + LANGUAGE 'sql' IMMUTABLE; + ----------------------------------------------------------------------- -- Constructor ST_Band ----------------------------------------------------------------------- diff --git a/raster/test/regress/rt_addband-post.pl b/raster/test/regress/rt_addband-post.pl new file mode 100644 index 000000000..a48c932ff --- /dev/null +++ b/raster/test/regress/rt_addband-post.pl @@ -0,0 +1,8 @@ +use File::Basename; +use Cwd 'abs_path'; + +my $REGDIR = abs_path(dirname($0)); +my $RASTERDIR = abs_path($REGDIR . "/../raster/test/regress"); + +unlink $RASTERDIR . '/' . $TEST . '-pre.sql'; +unlink $RASTERDIR . '/' . $TEST . '-post.sql'; diff --git a/raster/test/regress/rt_addband-pre.pl b/raster/test/regress/rt_addband-pre.pl new file mode 100644 index 000000000..8a05dd3a0 --- /dev/null +++ b/raster/test/regress/rt_addband-pre.pl @@ -0,0 +1,54 @@ +use File::Basename; +use Cwd 'abs_path'; + +my $REGDIR = abs_path(dirname($0)); +my $RASTERDIR = abs_path($REGDIR . "/../raster/test/regress"); +my $FILERASTER = $RASTERDIR . "/loader/testraster.tif"; + +my $sql = <<"END"; +DROP TABLE IF EXISTS raster_outdb_template; +CREATE TABLE raster_outdb_template AS +SELECT + 1 AS rid, + ST_AddBand( + ST_MakeEmptyRaster(90, 90, 0., 0., 1, -1, 0, 0, 0), + 1, '$FILERASTER'::text, NULL::int[] + ) AS rast +UNION ALL +SELECT + 2 AS rid, + ST_AddBand( + ST_MakeEmptyRaster(90, 90, 0., 0., 1, -1, 0, 0, 0), + '$FILERASTER'::text, NULL::int[] + ) AS rast +UNION ALL +SELECT + 3 AS rid, + ST_AddBand( + ST_AddBand( + ST_MakeEmptyRaster(90, 90, 0., 0., 1, -1, 0, 0, 0), + 1, '8BUI', 1, 0 + ), + '$FILERASTER'::text, ARRAY[2]::int[] + ) AS rast +UNION ALL +SELECT + 4 AS rid, + ST_AddBand( + ST_AddBand( + ST_MakeEmptyRaster(90, 90, 0., 0., 1, -1, 0, 0, 0), + 1, '8BUI', 1, 0 + ), + '$FILERASTER'::text, ARRAY[2]::int[], + 1, + 255 + ) AS rast +END + +open(PRESQL, '>', $RASTERDIR . '/' . $TEST . '-pre.sql'); +print PRESQL $sql; +close(PRESQL); + +open(POSTSQL, '>', $RASTERDIR . '/' . $TEST . '-post.sql'); +print POSTSQL "DROP TABLE IF EXISTS raster_outdb_template;\n"; +close(POSTSQL); diff --git a/raster/test/regress/rt_addband.sql b/raster/test/regress/rt_addband.sql index 569305df2..b932ef415 100644 --- a/raster/test/regress/rt_addband.sql +++ b/raster/test/regress/rt_addband.sql @@ -170,3 +170,45 @@ SELECT * FROM ST_BandMetadata( -- raster array version test SELECT (ST_DumpAsPolygons(newrast,3)).val As b3val FROM (SELECT ST_AddBand(NULL, array_agg(rast)) AS newrast FROM (SELECT ST_AsRaster(ST_Buffer(ST_Point(10,10), 34),200,200, '8BUI',i*30) As rast FROM generate_series(1,3) As i ) As foo ) As foofoo; + +-- out-db variants +SELECT + 1, + bandnum, + isoutdb, + CASE + WHEN isoutdb IS TRUE + THEN strpos(path, 'testraster.tif') > 0 + ELSE NULL + END +FROM ST_BandMetadata((SELECT rast FROM raster_outdb_template WHERE rid = 1), ARRAY[]::int[]); +SELECT + 2, + bandnum, + isoutdb, + CASE + WHEN isoutdb IS TRUE + THEN strpos(path, 'testraster.tif') > 0 + ELSE NULL + END +FROM ST_BandMetadata((SELECT rast FROM raster_outdb_template WHERE rid = 2), ARRAY[]::int[]); +SELECT + 3, + bandnum, + isoutdb, + CASE + WHEN isoutdb IS TRUE + THEN strpos(path, 'testraster.tif') > 0 + ELSE NULL + END +FROM ST_BandMetadata((SELECT rast FROM raster_outdb_template WHERE rid = 3), ARRAY[]::int[]); +SELECT + 4, + bandnum, + isoutdb, + CASE + WHEN isoutdb IS TRUE + THEN strpos(path, 'testraster.tif') > 0 + ELSE NULL + END +FROM ST_BandMetadata((SELECT rast FROM raster_outdb_template WHERE rid = 4), ARRAY[]::int[]); diff --git a/raster/test/regress/rt_addband_expected b/raster/test/regress/rt_addband_expected index 801ce9557..08d765db9 100644 --- a/raster/test/regress/rt_addband_expected +++ b/raster/test/regress/rt_addband_expected @@ -108,3 +108,13 @@ NOTICE: RASTER_copyBand: Could not add band to raster. Returning original raste 5|64BF|0|f| 6|2BUI|0|f| 90 +1|1|t|t +1|2|t|t +1|3|t|t +2|1|t|t +2|2|t|t +2|3|t|t +3|1|f| +3|2|t|t +4|1|t|t +4|2|f| diff --git a/regress/run_test.pl b/regress/run_test.pl index 30dc4f887..40800724d 100755 --- a/regress/run_test.pl +++ b/regress/run_test.pl @@ -30,11 +30,11 @@ if ( @ARGV < 1 ) # Global configuration items ################################################################## -my $DB = "postgis_reg"; -my $REGDIR = abs_path(dirname($0)); -my $SHP2PGSQL = $REGDIR . "/../loader/shp2pgsql"; -my $PGSQL2SHP = $REGDIR . "/../loader/pgsql2shp"; -my $RASTER2PGSQL = $REGDIR . "/../raster/loader/raster2pgsql"; +our $DB = "postgis_reg"; +our $REGDIR = abs_path(dirname($0)); +our $SHP2PGSQL = $REGDIR . "/../loader/shp2pgsql"; +our $PGSQL2SHP = $REGDIR . "/../loader/pgsql2shp"; +our $RASTER2PGSQL = $REGDIR . "/../raster/loader/raster2pgsql"; ################################################################## @@ -493,10 +493,11 @@ sub eval_file my $pl; if ( -r $file ) { - open(PL, $file); - $pl = ; - close(PL); - eval($pl); + #open(PL, $file); + #$pl = ; + #close(PL); + #eval($pl); + do $file; } } -- 2.40.0