From 3f1a35b8fb462f5546dbae1a4e4e6f9afa8e194e Mon Sep 17 00:00:00 2001 From: Bborie Park Date: Fri, 11 Nov 2011 15:41:32 +0000 Subject: [PATCH] Add ST_MapAlgebraFct for 2 rasters. This is the sibling of 2-raster ST_MapAlgebraExpr. Renamed RASTER_mapAlgebra2Expr to RASTER_mapAlgebra2 as both 2-raster ST_MapAlgebraExpr and ST_MapAlgebraFct call the same function. Associated ticket is #1281. git-svn-id: http://svn.osgeo.org/postgis/trunk@8137 b70326c6-7e19-0410-871a-916f4a2858ee --- raster/rt_pg/rt_pg.c | 643 ++++++++++-------- raster/rt_pg/rtpostgis.sql.in.c | 44 +- raster/test/regress/Makefile.in | 1 + .../regress/rt_mapalgebraexpr_2raster.sql | 4 +- .../rt_mapalgebraexpr_2raster_expected | 34 +- .../test/regress/rt_mapalgebrafct_2raster.sql | 344 ++++++++++ .../regress/rt_mapalgebrafct_2raster_expected | 274 ++++++++ 7 files changed, 1060 insertions(+), 284 deletions(-) create mode 100644 raster/test/regress/rt_mapalgebrafct_2raster.sql create mode 100644 raster/test/regress/rt_mapalgebrafct_2raster_expected diff --git a/raster/rt_pg/rt_pg.c b/raster/rt_pg/rt_pg.c index 662e3b874..51f524a3d 100644 --- a/raster/rt_pg/rt_pg.c +++ b/raster/rt_pg/rt_pg.c @@ -244,7 +244,7 @@ Datum RASTER_intersects(PG_FUNCTION_ARGS); Datum RASTER_sameAlignment(PG_FUNCTION_ARGS); /* two-raster MapAlgebra */ -Datum RASTER_mapAlgebra2Expr(PG_FUNCTION_ARGS); +Datum RASTER_mapAlgebra2(PG_FUNCTION_ARGS); /* Datum RASTER_mapAlgebra2Fct(PG_FUNCTION_ARGS); */ @@ -7882,10 +7882,10 @@ Datum RASTER_sameAlignment(PG_FUNCTION_ARGS) } /** - * Two raster MapAlgebra + * Two raster MapAlgebra using expressions */ -PG_FUNCTION_INFO_V1(RASTER_mapAlgebra2Expr); -Datum RASTER_mapAlgebra2Expr(PG_FUNCTION_ARGS) +PG_FUNCTION_INFO_V1(RASTER_mapAlgebra2); +Datum RASTER_mapAlgebra2(PG_FUNCTION_ARGS) { const int set_count = 2; rt_pgraster *pgrast; @@ -7894,17 +7894,28 @@ Datum RASTER_mapAlgebra2Expr(PG_FUNCTION_ARGS) uint32_t bandindex[2] = {0}; rt_raster _rast[2] = {NULL}; rt_band _band[2] = {NULL}; + int _hasnodata[2] = {0}; + double _nodataval[2] = {0}; + double _offset[4] = {0.}; + double _rastoffset[2][4] = {{0.}}; + int _haspixel[2] = {0}; + double _pixel[2] = {0}; + uint16_t _dim[2][2] = {{0}}; char *pixtypename = NULL; rt_pixtype pixtype = PT_END; char *extenttypename = NULL; rt_extenttype extenttype = ET_INTERSECTION; + + rt_raster raster = NULL; + rt_band band = NULL; + uint16_t dim[2] = {0}; + int haspixel = 0; + double pixel = 0.; double nodataval = 0; - int _hasnodata[2] = {0}; - double _nodataval[2] = {0}; + double gt[6] = {0.}; - int hasnodatanodataval = 0; - double nodatanodataval = 0; + Oid calltype = InvalidOid; int spicount = 3; uint16_t exprpos[3] = {4, 7, 8}; @@ -7924,18 +7935,13 @@ Datum RASTER_mapAlgebra2Expr(PG_FUNCTION_ARGS) bool isnull = FALSE; int hasargval[3] = {0}; double argval[3] = {0.}; + int hasnodatanodataval = 0; + double nodatanodataval = 0; - double _offset[4] = {0.}; - double _rastoffset[2][4] = {{0.}}; - int _haspixel[2] = {0}; - double _pixel[2] = {0}; - uint16_t _dim[2][2] = {{0}}; - - rt_raster raster = NULL; - rt_band band = NULL; - uint16_t dim[2] = {0}; - int haspixel = 0; - double pixel = 0.; + Oid ufcnoid = InvalidOid; + FmgrInfo uflinfo; + FunctionCallInfoData ufcinfo; + int ufcnullcount = 0; uint32_t i = 0; uint32_t j = 0; @@ -7945,11 +7951,10 @@ Datum RASTER_mapAlgebra2Expr(PG_FUNCTION_ARGS) int _x = 0; int _y = 0; int err; - double gt[6] = {0.}; int aligned = 0; int len = 0; - POSTGIS_RT_DEBUG(3, "Starting RASTER_mapAlgebra2Expr"); + POSTGIS_RT_DEBUG(3, "Starting RASTER_mapAlgebra2"); for (i = 0, j = 0; i < set_count; i++) { if (!PG_ARGISNULL(j)) { @@ -7959,7 +7964,7 @@ Datum RASTER_mapAlgebra2Expr(PG_FUNCTION_ARGS) /* raster */ rast[i] = rt_raster_deserialize(pgrast, FALSE); if (!rast[i]) { - elog(ERROR, "RASTER_mapAlgebra2Expr: Could not deserialize the %s raster", i < 1 ? "first" : "second"); + elog(ERROR, "RASTER_mapAlgebra2: Could not deserialize the %s raster", i < 1 ? "first" : "second"); for (k = 0; k < i; k++) { if (rast[k] != NULL) rt_raster_destroy(rast[k]); } @@ -7995,7 +8000,7 @@ Datum RASTER_mapAlgebra2Expr(PG_FUNCTION_ARGS) raster = rt_raster_new(0, 0); if (raster == NULL) { - elog(ERROR, "RASTER_mapAlgebra2Expr: Unable to create empty raster"); + elog(ERROR, "RASTER_mapAlgebra2: Unable to create empty raster"); for (k = 0; k < i; k++) { if (rast[k] != NULL) rt_raster_destroy(rast[k]); } @@ -8041,7 +8046,7 @@ Datum RASTER_mapAlgebra2Expr(PG_FUNCTION_ARGS) _dim[j][1] ); if (_rast[i] == NULL) { - elog(ERROR, "RASTER_mapAlgebra2Expr: Unable to create nodata raster"); + elog(ERROR, "RASTER_mapAlgebra2: Unable to create nodata raster"); rt_raster_destroy(_rast[j]); PG_RETURN_NULL(); } @@ -8069,7 +8074,7 @@ Datum RASTER_mapAlgebra2Expr(PG_FUNCTION_ARGS) /* same alignment */ if (!rt_raster_same_alignment(_rast[0], _rast[1], &aligned)) { - elog(ERROR, "RASTER_mapAlgebra2Expr: Unable to test for alignment on the two rasters"); + elog(ERROR, "RASTER_mapAlgebra2: Unable to test for alignment on the two rasters"); for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); PG_RETURN_NULL(); } @@ -8085,7 +8090,7 @@ Datum RASTER_mapAlgebra2Expr(PG_FUNCTION_ARGS) /* Get the pixel type index */ pixtype = rt_pixtype_index_from_name(pixtypename); if (pixtype == PT_END ) { - elog(ERROR, "RASTER_mapAlgebra2Expr: Invalid pixel type: %s", pixtypename); + elog(ERROR, "RASTER_mapAlgebra2: Invalid pixel type: %s", pixtypename); for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); PG_RETURN_NULL(); } @@ -8105,7 +8110,7 @@ Datum RASTER_mapAlgebra2Expr(PG_FUNCTION_ARGS) &err, _offset ); if (!err) { - elog(ERROR, "RASTER_mapAlgebra2Expr: Unable to get output raster of correct extent"); + elog(ERROR, "RASTER_mapAlgebra2: Unable to get output raster of correct extent"); for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); PG_RETURN_NULL(); } @@ -8121,6 +8126,7 @@ Datum RASTER_mapAlgebra2Expr(PG_FUNCTION_ARGS) dim[1] = rt_raster_get_height(raster); i = 2; + /* handle special cases for extent */ switch (extenttype) { case ET_FIRST: i = 0; @@ -8172,7 +8178,7 @@ Datum RASTER_mapAlgebra2Expr(PG_FUNCTION_ARGS) raster = rt_raster_new(0, 0); if (raster == NULL) { - elog(ERROR, "RASTER_mapAlgebra2Expr: Unable to create no band raster"); + elog(ERROR, "RASTER_mapAlgebra2: Unable to create no band raster"); for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); PG_RETURN_NULL(); } @@ -8220,7 +8226,7 @@ Datum RASTER_mapAlgebra2Expr(PG_FUNCTION_ARGS) _band[i] = rt_raster_get_band(_rast[i], bandindex[i] - 1); if (_band[i] == NULL) { - elog(ERROR, "RASTER_mapAlgebra2Expr: Unable to get band %d of the %s raster", + elog(ERROR, "RASTER_mapAlgebra2: Unable to get band %d of the %s raster", bandindex[i], (i < 1 ? "FIRST" : "SECOND") ); @@ -8264,7 +8270,7 @@ Datum RASTER_mapAlgebra2Expr(PG_FUNCTION_ARGS) 1, nodataval, 0 ) < 0) { - elog(ERROR, "RASTER_mapAlgebra2Expr: Unable to add new band to output raster"); + elog(ERROR, "RASTER_mapAlgebra2: Unable to add new band to output raster"); for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); rt_raster_destroy(raster); PG_RETURN_NULL(); @@ -8273,7 +8279,7 @@ Datum RASTER_mapAlgebra2Expr(PG_FUNCTION_ARGS) /* get output band */ band = rt_raster_get_band(raster, 0); if (band == NULL) { - elog(ERROR, "RASTER_mapAlgebra2Expr: Unable to get newly added band of output raster"); + elog(ERROR, "RASTER_mapAlgebra2: Unable to get newly added band of output raster"); for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); rt_raster_destroy(raster); PG_RETURN_NULL(); @@ -8304,154 +8310,209 @@ Datum RASTER_mapAlgebra2Expr(PG_FUNCTION_ARGS) rt_band_get_nodata(band) ); - /* connect SPI */ - if (SPI_connect() != SPI_OK_CONNECT) { - elog(ERROR, "RASTER_mapAlgebra2Expr: Unable to connect to the SPI manager"); - for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); - rt_raster_destroy(raster); - PG_RETURN_NULL(); - } - /* - process expressions - - exprpos elements are: - 4 - expression => spiplan[0] - 7 - nodata1expr => spiplan[1] - 8 - nodata2expr => spiplan[2] + determine who called this function + Arg 4 will either be text or regprocedure */ - for (i = 0; i < spicount; i++) { - if (!PG_ARGISNULL(exprpos[i])) { - expr = strtoupper(text_to_cstring(PG_GETARG_TEXT_P(exprpos[i]))); - POSTGIS_RT_DEBUGF(3, "raw expr #%d: %s", i, expr); - expr = replace(expr, "RAST1", "$1", &len); - if (len) { - argcount[i]++; - argexists[i][0] = 1; - } - expr = replace(expr, "RAST2", (argexists[i][0] ? "$2" : "$1"), &len); - if (len) { - argcount[i]++; - argexists[i][1] = 1; - } + POSTGIS_RT_DEBUG(3, "checking parameter type for arg 4"); + calltype = get_fn_expr_argtype(fcinfo->flinfo, 4); + + switch(calltype) { + case TEXTOID: { + POSTGIS_RT_DEBUG(3, "arg 4 is \"expression\"!"); - len = strlen("SELECT (") + strlen(expr) + strlen(")::double precision"); - sql = (char *) palloc(len + 1); - if (sql == NULL) { - elog(ERROR, "RASTER_mapAlgebra2Expr: Unable to allocate memory for expression parameter %d", exprpos[i]); + /* connect SPI */ + if (SPI_connect() != SPI_OK_CONNECT) { + elog(ERROR, "RASTER_mapAlgebra2: Unable to connect to the SPI manager"); for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); rt_raster_destroy(raster); - for (k = 0; k < spicount; k++) SPI_freeplan(spiplan[k]); - SPI_finish(); PG_RETURN_NULL(); } - strncpy(sql, "SELECT (", strlen("SELECT (")); - strncpy(sql + strlen("SELECT ("), expr, strlen(expr)); - strncpy(sql + strlen("SELECT (") + strlen(expr), ")::double precision", strlen(")::double precision")); - sql[len] = '\0'; + /* reset hasargval */ + memset(hasargval, 0, spicount); + + /* + process expressions + + exprpos elements are: + 4 - expression => spiplan[0] + 7 - nodata1expr => spiplan[1] + 8 - nodata2expr => spiplan[2] + */ + for (i = 0; i < spicount; i++) { + if (!PG_ARGISNULL(exprpos[i])) { + expr = strtoupper(text_to_cstring(PG_GETARG_TEXT_P(exprpos[i]))); + POSTGIS_RT_DEBUGF(3, "raw expr #%d: %s", i, expr); + expr = replace(expr, "RAST1", "$1", &len); + if (len) { + argcount[i]++; + argexists[i][0] = 1; + } + expr = replace(expr, "RAST2", (argexists[i][0] ? "$2" : "$1"), &len); + if (len) { + argcount[i]++; + argexists[i][1] = 1; + } - POSTGIS_RT_DEBUGF(3, "sql #%d: %s", i, sql); + len = strlen("SELECT (") + strlen(expr) + strlen(")::double precision"); + sql = (char *) palloc(len + 1); + if (sql == NULL) { + elog(ERROR, "RASTER_mapAlgebra2: Unable to allocate memory for expression parameter %d", exprpos[i]); + for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); + rt_raster_destroy(raster); + for (k = 0; k < spicount; k++) SPI_freeplan(spiplan[k]); + SPI_finish(); + PG_RETURN_NULL(); + } - /* create prepared plan */ - if (argcount[i]) { - argtype = (Oid *) palloc(argcount[i] * sizeof(Oid)); - if (argtype == NULL) { - elog(ERROR, "RASTER_mapAlgebra2Expr: Unable to allocate memory for prepared plan argtypes of expression parameter %d", exprpos[i]); + strncpy(sql, "SELECT (", strlen("SELECT (")); + strncpy(sql + strlen("SELECT ("), expr, strlen(expr)); + strncpy(sql + strlen("SELECT (") + strlen(expr), ")::double precision", strlen(")::double precision")); + sql[len] = '\0'; - for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); - rt_raster_destroy(raster); + POSTGIS_RT_DEBUGF(3, "sql #%d: %s", i, sql); - for (k = 0; k < spicount; k++) SPI_freeplan(spiplan[k]); - SPI_finish(); + /* create prepared plan */ + if (argcount[i]) { + argtype = (Oid *) palloc(argcount[i] * sizeof(Oid)); + if (argtype == NULL) { + elog(ERROR, "RASTER_mapAlgebra2: Unable to allocate memory for prepared plan argtypes of expression parameter %d", exprpos[i]); - pfree(sql); + for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); + rt_raster_destroy(raster); - PG_RETURN_NULL(); - } - for (j = 0; j < argcount[i]; j++) argtype[j] = FLOAT8OID; + for (k = 0; k < spicount; k++) SPI_freeplan(spiplan[k]); + SPI_finish(); - spiplan[i] = SPI_prepare(sql, argcount[i], argtype); - if (spiplan[i] == NULL) { - elog(ERROR, "RASTER_mapAlgebra2Expr: Unable to create prepared plan of expression parameter %d", exprpos[i]); + pfree(sql); - for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); - rt_raster_destroy(raster); + PG_RETURN_NULL(); + } + for (j = 0; j < argcount[i]; j++) argtype[j] = FLOAT8OID; - for (k = 0; k < spicount; k++) SPI_freeplan(spiplan[k]); - SPI_finish(); + spiplan[i] = SPI_prepare(sql, argcount[i], argtype); + if (spiplan[i] == NULL) { + elog(ERROR, "RASTER_mapAlgebra2: Unable to create prepared plan of expression parameter %d", exprpos[i]); - pfree(sql); - pfree(argtype); + for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); + rt_raster_destroy(raster); - PG_RETURN_NULL(); - } + for (k = 0; k < spicount; k++) SPI_freeplan(spiplan[k]); + SPI_finish(); - pfree(argtype); - } - /* no args, just execute query */ - else { - err = SPI_execute(sql, TRUE, 0); - if (err != SPI_OK_SELECT || SPI_tuptable == NULL || SPI_processed != 1) { - elog(ERROR, "RASTER_mapAlgebra2Expr: Unable to evaluate expression parameter %d", exprpos[i]); + pfree(sql); + pfree(argtype); - for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); - rt_raster_destroy(raster); + PG_RETURN_NULL(); + } - for (k = 0; k < spicount; k++) SPI_freeplan(spiplan[k]); - SPI_finish(); + pfree(argtype); + } + /* no args, just execute query */ + else { + err = SPI_execute(sql, TRUE, 0); + if (err != SPI_OK_SELECT || SPI_tuptable == NULL || SPI_processed != 1) { + elog(ERROR, "RASTER_mapAlgebra2: Unable to evaluate expression parameter %d", exprpos[i]); - pfree(sql); + for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); + rt_raster_destroy(raster); - PG_RETURN_NULL(); - } + for (k = 0; k < spicount; k++) SPI_freeplan(spiplan[k]); + SPI_finish(); - /* get output of prepared plan */ - tupdesc = SPI_tuptable->tupdesc; - tuptable = SPI_tuptable; - tuple = tuptable->vals[0]; + pfree(sql); - datum = SPI_getbinval(tuple, tupdesc, 1, &isnull); - if (SPI_result == SPI_ERROR_NOATTRIBUTE) { - elog(ERROR, "RASTER_mapAlgebra2Expr: Unable to get result of expression parameter %d", exprpos[i]); + PG_RETURN_NULL(); + } - for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); - rt_raster_destroy(raster); + /* get output of prepared plan */ + tupdesc = SPI_tuptable->tupdesc; + tuptable = SPI_tuptable; + tuple = tuptable->vals[0]; - if (SPI_tuptable) SPI_freetuptable(tuptable); - for (k = 0; k < spicount; k++) SPI_freeplan(spiplan[k]); - SPI_finish(); + datum = SPI_getbinval(tuple, tupdesc, 1, &isnull); + if (SPI_result == SPI_ERROR_NOATTRIBUTE) { + elog(ERROR, "RASTER_mapAlgebra2: Unable to get result of expression parameter %d", exprpos[i]); - pfree(sql); + for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); + rt_raster_destroy(raster); - PG_RETURN_NULL(); - } + if (SPI_tuptable) SPI_freetuptable(tuptable); + for (k = 0; k < spicount; k++) SPI_freeplan(spiplan[k]); + SPI_finish(); - if (!isnull) { - hasargval[i] = 1; - argval[i] = DatumGetFloat8(datum); - } + pfree(sql); - if (SPI_tuptable) SPI_freetuptable(tuptable); + PG_RETURN_NULL(); + } + + if (!isnull) { + hasargval[i] = 1; + argval[i] = DatumGetFloat8(datum); + } + + if (SPI_tuptable) SPI_freetuptable(tuptable); + } + + pfree(sql); + } + else + spiempty++; } - pfree(sql); - } - else - spiempty++; - } + /* nodatanodataval */ + if (!PG_ARGISNULL(9)) { + hasnodatanodataval = 1; + nodatanodataval = PG_GETARG_FLOAT8(9); + } + else + hasnodatanodataval = 0; + } break; + case REGPROCEDUREOID: { + POSTGIS_RT_DEBUG(3, "arg 4 is \"userfunction\"!"); + if (!PG_ARGISNULL(4)) { + ufcnullcount = 0; + ufcnoid = PG_GETARG_OID(4); + + /* get function info */ + fmgr_info(ufcnoid, &uflinfo); + + /* prep function call data */ +#if POSTGIS_PGSQL_VERSION <= 90 + InitFunctionCallInfoData(ufcinfo, &uflinfo, 3, InvalidOid, NULL); +#else + InitFunctionCallInfoData(ufcinfo, &uflinfo, 3, InvalidOid, NULL, NULL); +#endif + memset(ufcinfo.argnull, FALSE, 3); - /* nodatanodataval */ - if (!PG_ARGISNULL(9)) { - hasnodatanodataval = 1; - nodatanodataval = PG_GETARG_FLOAT8(9); + if (!PG_ARGISNULL(7)) { + ufcinfo.arg[2] = PG_GETARG_DATUM(7); + } + else { + ufcinfo.arg[2] = (Datum) NULL; + ufcinfo.argnull[2] = TRUE; + ufcnullcount++; + } + } + } break; + default: + elog(ERROR, "RASTER_mapAlgebra2: Invalid data type for expression or userfunction"); + for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); + rt_raster_destroy(raster); + PG_RETURN_NULL(); + break; } - else - hasnodatanodataval = 0; /* loop over pixels */ /* if any expression present, run */ - if (spiempty != spicount || hasnodatanodataval) { + if (( + (calltype == TEXTOID) && ( + (spiempty != spicount) || hasnodatanodataval + ) + ) || ( + (calltype == REGPROCEDUREOID) && (ufcnoid != InvalidOid) + )) { for (x = 0; x < dim[0]; x++) { for (y = 0; y < dim[1]; y++) { @@ -8472,13 +8533,15 @@ Datum RASTER_mapAlgebra2Expr(PG_FUNCTION_ARGS) ) { err = rt_band_get_pixel(_band[i], _x, _y, &(_pixel[i])); if (err < 0) { - elog(ERROR, "RASTER_mapAlgebra2Expr: Unable to get pixel of %s raster", (i < 1 ? "FIRST" : "SECOND")); + elog(ERROR, "RASTER_mapAlgebra2: Unable to get pixel of %s raster", (i < 1 ? "FIRST" : "SECOND")); for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); rt_raster_destroy(raster); - for (k = 0; k < spicount; k++) SPI_freeplan(spiplan[k]); - SPI_finish(); + if (calltype == TEXTOID) { + for (k = 0; k < spicount; k++) SPI_freeplan(spiplan[k]); + SPI_finish(); + } PG_RETURN_NULL(); } @@ -8497,166 +8560,203 @@ Datum RASTER_mapAlgebra2Expr(PG_FUNCTION_ARGS) haspixel = 0; - /* which prepared plan to use? */ - /* !pixel0 && !pixel1 */ - /* use nodatanodataval */ - if (!_haspixel[0] && !_haspixel[1]) - i = 3; - /* pixel0 && !pixel1 */ - /* run spiplan[2] (nodata2expr) */ - else if (_haspixel[0] && !_haspixel[1]) - i = 2; - /* !pixel0 && pixel1 */ - /* run spiplan[1] (nodata1expr) */ - else if (!_haspixel[0] && _haspixel[1]) - i = 1; - /* pixel0 && pixel1 */ - /* run spiplan[0] (expression) */ - else - i = 0; + switch (calltype) { + case TEXTOID: { + /* which prepared plan to use? */ + /* !pixel0 && !pixel1 */ + /* use nodatanodataval */ + if (!_haspixel[0] && !_haspixel[1]) + i = 3; + /* pixel0 && !pixel1 */ + /* run spiplan[2] (nodata2expr) */ + else if (_haspixel[0] && !_haspixel[1]) + i = 2; + /* !pixel0 && pixel1 */ + /* run spiplan[1] (nodata1expr) */ + else if (!_haspixel[0] && _haspixel[1]) + i = 1; + /* pixel0 && pixel1 */ + /* run spiplan[0] (expression) */ + else + i = 0; - /* process values */ - if (i == 3) { - if (hasnodatanodataval) { - haspixel = 1; - pixel = nodatanodataval; - } - } - /* prepared plan exists */ - else if (spiplan[i] != NULL) { - POSTGIS_RT_DEBUGF(5, "Using prepared plan: %d", i); + /* process values */ + if (i == 3) { + if (hasnodatanodataval) { + haspixel = 1; + pixel = nodatanodataval; + } + } + /* has an evaluated value */ + else if (hasargval[i]) { + haspixel = 1; + pixel = argval[i]; + } + /* prepared plan exists */ + else if (spiplan[i] != NULL) { + POSTGIS_RT_DEBUGF(5, "Using prepared plan: %d", i); + + /* expression has argument(s) */ + if (argcount[i]) { + values = (Datum *) palloc(sizeof(Datum) * argcount[i]); + if (values == NULL) { + elog(ERROR, "RASTER_mapAlgebra2: Unable to allocate memory for value parameters of prepared statement %d", i); + + for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); + rt_raster_destroy(raster); + + for (k = 0; k < spicount; k++) SPI_freeplan(spiplan[k]); + SPI_finish(); + + PG_RETURN_NULL(); + } + + nulls = (bool *) palloc(sizeof(bool) * argcount[i]); + if (nulls == NULL) { + elog(ERROR, "RASTER_mapAlgebra2: Unable to allocate memory for NULL parameters of prepared statement %d", i); + + pfree(values); + + for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); + rt_raster_destroy(raster); + + for (k = 0; k < spicount; k++) SPI_freeplan(spiplan[k]); + SPI_finish(); + + PG_RETURN_NULL(); + } + memset(nulls, FALSE, argcount[i]); + + /* two arguments */ + if (argcount[i] > 1) { + for (j = 0; j < argcount[i]; j++) { + if (_isempty[j] || !_haspixel[j]) + nulls[j] = TRUE; + else + values[j] = Float8GetDatum(_pixel[j]); + } + } + /* only one argument */ + else { + if (argexists[i][0]) + j = 0; + else + j = 1; + + if (_isempty[j] || !_haspixel[j]) { + POSTGIS_RT_DEBUG(5, "using null"); + nulls[0] = TRUE; + } + else { + POSTGIS_RT_DEBUGF(5, "using value %f", _pixel[j]); + values[0] = Float8GetDatum(_pixel[j]); + } + } + } - /* expression has argument(s) */ - if (argcount[i]) { - values = (Datum *) palloc(sizeof(Datum) * argcount[i]); - if (values == NULL) { - elog(ERROR, "RASTER_mapAlgebra2Expr: Unable to allocate memory for value parameters of prepared statement %d", i); + /* run prepared plan */ + err = SPI_execute_plan(spiplan[i], values, nulls, TRUE, 1); + if (values != NULL) pfree(values); + if (nulls != NULL) pfree(nulls); + if (err != SPI_OK_SELECT || SPI_tuptable == NULL || SPI_processed != 1) { + elog(ERROR, "RASTER_mapAlgebra2: Unexpected error when running prepared statement %d", i); - for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); - rt_raster_destroy(raster); + for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); + rt_raster_destroy(raster); - for (k = 0; k < spicount; k++) SPI_freeplan(spiplan[k]); - SPI_finish(); + for (k = 0; k < spicount; k++) SPI_freeplan(spiplan[k]); + SPI_finish(); - PG_RETURN_NULL(); - } + PG_RETURN_NULL(); + } - nulls = (bool *) palloc(sizeof(bool) * argcount[i]); - if (nulls == NULL) { - elog(ERROR, "RASTER_mapAlgebra2Expr: Unable to allocate memory for NULL parameters of prepared statement %d", i); + /* get output of prepared plan */ + tupdesc = SPI_tuptable->tupdesc; + tuptable = SPI_tuptable; + tuple = tuptable->vals[0]; - pfree(values); + datum = SPI_getbinval(tuple, tupdesc, 1, &isnull); + if (SPI_result == SPI_ERROR_NOATTRIBUTE) { + elog(ERROR, "RASTER_mapAlgebra2: Unable to get result of prepared statement %d", i); - for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); - rt_raster_destroy(raster); + for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); + rt_raster_destroy(raster); - for (k = 0; k < spicount; k++) SPI_freeplan(spiplan[k]); - SPI_finish(); + if (SPI_tuptable) SPI_freetuptable(tuptable); + for (k = 0; k < spicount; k++) SPI_freeplan(spiplan[k]); + SPI_finish(); - PG_RETURN_NULL(); - } - memset(nulls, FALSE, argcount[i]); - - /* two arguments */ - if (argcount[i] > 1) { - for (j = 0; j < argcount[i]; j++) { - if (_isempty[j] || !_haspixel[j]) - nulls[j] = TRUE; - else - values[j] = Float8GetDatum(_pixel[j]); + PG_RETURN_NULL(); + } + + if (!isnull) { + haspixel = 1; + pixel = DatumGetFloat8(datum); } + + if (SPI_tuptable) SPI_freetuptable(tuptable); } - /* only one argument */ - else { - if (argexists[i][0]) - j = 0; - else - j = 1; - - if (_isempty[j] || !_haspixel[j]) { - POSTGIS_RT_DEBUG(5, "using null"); - nulls[0] = TRUE; + } break; + case REGPROCEDUREOID: { + /* build fcnarg */ + for (i = 0; i < set_count; i++) { + ufcinfo.arg[i] = Float8GetDatum(_pixel[i]); + + if (_haspixel[i]) { + ufcinfo.argnull[i] = FALSE; + ufcnullcount--; } else { - POSTGIS_RT_DEBUGF(5, "using value %f", _pixel[j]); - values[0] = Float8GetDatum(_pixel[j]); + ufcinfo.argnull[i] = TRUE; + ufcnullcount++; } } - } - - /* run prepared plan */ - err = SPI_execute_plan(spiplan[i], values, nulls, TRUE, 1); - if (values != NULL) pfree(values); - if (nulls != NULL) pfree(nulls); - if (err != SPI_OK_SELECT || SPI_tuptable == NULL || SPI_processed != 1) { - elog(ERROR, "RASTER_mapAlgebra2Expr: Unexpected error when running prepared statement %d", i); - - for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); - rt_raster_destroy(raster); - - for (k = 0; k < spicount; k++) SPI_freeplan(spiplan[k]); - SPI_finish(); - - PG_RETURN_NULL(); - } - - /* get output of prepared plan */ - tupdesc = SPI_tuptable->tupdesc; - tuptable = SPI_tuptable; - tuple = tuptable->vals[0]; - - datum = SPI_getbinval(tuple, tupdesc, 1, &isnull); - if (SPI_result == SPI_ERROR_NOATTRIBUTE) { - elog(ERROR, "RASTER_mapAlgebra2Expr: Unable to get result of prepared statement %d", i); - - for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); - rt_raster_destroy(raster); - - if (SPI_tuptable) SPI_freetuptable(tuptable); - for (k = 0; k < spicount; k++) SPI_freeplan(spiplan[k]); - SPI_finish(); - PG_RETURN_NULL(); - } + /* function is strict and null parameter is passed */ + /* http://archives.postgresql.org/pgsql-general/2011-11/msg00424.php */ + if (uflinfo.fn_strict && ufcnullcount) + break; - if (!isnull) { - haspixel = 1; - pixel = DatumGetFloat8(datum); - } + datum = FunctionCallInvoke(&ufcinfo); - if (SPI_tuptable) SPI_freetuptable(tuptable); - } - /* has an evaluated value */ - else if (hasargval[i]) { - haspixel = 1; - pixel = argval[i]; + /* result is not null*/ + if (!ufcinfo.isnull) { + haspixel = 1; + pixel = DatumGetFloat8(datum); + } + } break; } /* burn pixel if haspixel != 0 */ if (haspixel) { if (rt_band_set_pixel(band, x, y, pixel) < 0) { - elog(ERROR, "RASTER_mapAlgebra2Expr: Unable to set pixel value of output raster"); + elog(ERROR, "RASTER_mapAlgebra2: Unable to set pixel value of output raster"); for (k = 0; k < set_count; k++) rt_raster_destroy(_rast[k]); rt_raster_destroy(raster); - for (k = 0; k < spicount; k++) SPI_freeplan(spiplan[k]); - SPI_finish(); + if (calltype == TEXTOID) { + for (k = 0; k < spicount; k++) SPI_freeplan(spiplan[k]); + SPI_finish(); + } PG_RETURN_NULL(); } } POSTGIS_RT_DEBUGF(5, "(x, y, val) = (%d, %d, %f)", x, y, haspixel ? pixel : nodataval); - } - } + + } /* y: height */ + } /* x: width */ } /* CLEANUP */ - for (i = 0; i < spicount; i++) { - if (spiplan[i] != NULL) SPI_freeplan(spiplan[i]); + if (calltype == TEXTOID) { + for (i = 0; i < spicount; i++) { + if (spiplan[i] != NULL) SPI_freeplan(spiplan[i]); + } + SPI_finish(); } - SPI_finish(); for (k = 0; k < set_count; k++) { if (_rast[k] != NULL) rt_raster_destroy(_rast[k]); @@ -8666,10 +8766,23 @@ Datum RASTER_mapAlgebra2Expr(PG_FUNCTION_ARGS) rt_raster_destroy(raster); if (!pgrast) PG_RETURN_NULL(); + POSTGIS_RT_DEBUG(3, "Finished RASTER_mapAlgebra2"); + SET_VARSIZE(pgrast, pgrast->size); PG_RETURN_POINTER(pgrast); } +/** + * Two raster MapAlgebra using user function + */ + /* +PG_FUNCTION_INFO_V1(RASTER_mapAlgebra2Fct); +Datum RASTER_mapAlgebra2Fct(PG_FUNCTION_ARGS) +{ + PG_RETURN_NULL(); +} +*/ + /* ---------------------------------------------------------------- */ /* Memory allocation / error reporting hooks */ /* ---------------------------------------------------------------- */ diff --git a/raster/rt_pg/rtpostgis.sql.in.c b/raster/rt_pg/rtpostgis.sql.in.c index 1368f306c..683e55518 100644 --- a/raster/rt_pg/rtpostgis.sql.in.c +++ b/raster/rt_pg/rtpostgis.sql.in.c @@ -1668,22 +1668,56 @@ CREATE OR REPLACE FUNCTION st_mapalgebraexpr( rast2 raster, band2 integer, expression text, pixeltype text DEFAULT NULL, extenttype text DEFAULT 'INTERSECTION', - nodata1expr text DEFAULT NULL, nodata2expr text DEFAULT NULL, nodatanodataval double precision DEFAULT NULL + nodata1expr text DEFAULT NULL, nodata2expr text DEFAULT NULL, + nodatanodataval double precision DEFAULT NULL ) RETURNS raster - AS 'MODULE_PATHNAME', 'RASTER_mapAlgebra2Expr' - LANGUAGE 'C' IMMUTABLE; + AS 'MODULE_PATHNAME', 'RASTER_mapAlgebra2' + LANGUAGE 'C' STABLE; CREATE OR REPLACE FUNCTION st_mapalgebraexpr( rast1 raster, rast2 raster, expression text, pixeltype text DEFAULT NULL, extenttype text DEFAULT 'INTERSECTION', - nodata1expr text DEFAULT NULL, nodata2expr text DEFAULT NULL, nodatanodataval double precision DEFAULT NULL + nodata1expr text DEFAULT NULL, nodata2expr text DEFAULT NULL, + nodatanodataval double precision DEFAULT NULL ) RETURNS raster AS $$ SELECT st_mapalgebraexpr($1, 1, $2, 1, $3, $4, $5, $6, $7, $8) $$ - LANGUAGE 'SQL' IMMUTABLE; + LANGUAGE 'SQL' STABLE; + +CREATE OR REPLACE FUNCTION st_mapalgebrafct( + rast1 raster, band1 integer, + rast2 raster, band2 integer, + userfunction regprocedure, + pixeltype text DEFAULT NULL, extenttype text DEFAULT 'INTERSECTION', + VARIADIC userargs text[] DEFAULT NULL +) + RETURNS raster + AS 'MODULE_PATHNAME', 'RASTER_mapAlgebra2' + LANGUAGE 'C' STABLE; + +CREATE OR REPLACE FUNCTION st_mapalgebrafct( + rast1 raster, + rast2 raster, + userfunction regprocedure, + pixeltype text DEFAULT NULL, extenttype text DEFAULT 'INTERSECTION', + VARIADIC userargs text[] DEFAULT NULL +) + RETURNS raster + AS $$ SELECT st_mapalgebrafct($1, 1, $2, 1, $3, $4, $5, VARIADIC $6) $$ + LANGUAGE 'SQL' STABLE; + +CREATE OR REPLACE FUNCTION st_mapalgebrafct( + rast1 raster, + rast2 raster, + userfunction regprocedure, + VARIADIC userargs text[] +) + RETURNS raster + AS $$ SELECT st_mapalgebrafct($1, 1, $2, 1, $3, NULL, 'INTERSECTION', VARIADIC $4) $$ + LANGUAGE 'SQL' STABLE; ----------------------------------------------------------------------- -- Get information about the raster diff --git a/raster/test/regress/Makefile.in b/raster/test/regress/Makefile.in index 09f1ec24a..1c422cbcc 100644 --- a/raster/test/regress/Makefile.in +++ b/raster/test/regress/Makefile.in @@ -89,6 +89,7 @@ TEST_UTILITY = \ rt_mapalgebraexpr.sql \ rt_mapalgebrafct.sql \ rt_mapalgebraexpr_2raster.sql \ + rt_mapalgebrafct_2raster.sql \ rt_reclass.sql \ rt_resample.sql \ rt_asraster.sql \ diff --git a/raster/test/regress/rt_mapalgebraexpr_2raster.sql b/raster/test/regress/rt_mapalgebraexpr_2raster.sql index adb190da0..454ff6e79 100644 --- a/raster/test/regress/rt_mapalgebraexpr_2raster.sql +++ b/raster/test/regress/rt_mapalgebraexpr_2raster.sql @@ -167,7 +167,7 @@ INSERT INTO raster_mapalgebra_out AND r2.rid BETWEEN 1 AND 9 ) UNION ALL ( SELECT r1.rid, r2.rid, 'FIRST', st_mapalgebraexpr( - r1.rast, r2.rast, '((rast1 + rast2)/2.)::numeric', '32BF', 'UNION', 'rast2', 'rast1', NULL + r1.rast, r2.rast, 'CASE WHEN rast2 IS NOT NULL THEN NULL ELSE rast1 END', '32BF', 'FIRST', NULL, 'rast1', NULL ) FROM raster_mapalgebra r1 JOIN raster_mapalgebra r2 @@ -178,7 +178,7 @@ INSERT INTO raster_mapalgebra_out INSERT INTO raster_mapalgebra_out SELECT NULL AS rid, rid, 'FIRST', st_mapalgebraexpr( - NULL::raster, rast, 'CASE WHEN rast1 IS NOT NULL THEN NULL ELSE rast2 END', '32BF', 'SECOND', 'rast2', NULL, NULL + NULL::raster, rast, 'CASE WHEN rast1 IS NOT NULL THEN NULL ELSE rast2 END', '32BF', 'FIRST', 'rast2', NULL, NULL ) FROM raster_mapalgebra ; diff --git a/raster/test/regress/rt_mapalgebraexpr_2raster_expected b/raster/test/regress/rt_mapalgebraexpr_2raster_expected index 59182be8b..2d3f6d566 100644 --- a/raster/test/regress/rt_mapalgebraexpr_2raster_expected +++ b/raster/test/regress/rt_mapalgebraexpr_2raster_expected @@ -24,6 +24,16 @@ NOTICE: The two rasters provided have no intersection. Returning no band raste NOTICE: The two rasters provided have no intersection. Returning no band raster NOTICE: The two rasters provided are NULL. Returning NULL NOTICE: The two rasters provided are NULL. Returning NULL +NOTICE: The FIRST raster is NULL. Returning NULL +NOTICE: The FIRST raster is NULL. Returning NULL +NOTICE: The FIRST raster is NULL. Returning NULL +NOTICE: The FIRST raster is NULL. Returning NULL +NOTICE: The FIRST raster is NULL. Returning NULL +NOTICE: The FIRST raster is NULL. Returning NULL +NOTICE: The FIRST raster is NULL. Returning NULL +NOTICE: The FIRST raster is NULL. Returning NULL +NOTICE: The FIRST raster is NULL. Returning NULL +NOTICE: The FIRST raster is NULL. Returning NULL NOTICE: The two rasters provided are NULL. Returning NULL NOTICE: The SECOND raster is NULL. Returning NULL NOTICE: The SECOND raster is NULL. Returning NULL @@ -218,18 +228,18 @@ NOTICE: Could not find raster band of index 1 when getting pixel value. Returni 0|4|FIRST|-2.000|-2.000|4|4|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|1.000|1.000 10|11|FIRST|-2.000|-2.000|4|4|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|1.000|1.000 10|12|FIRST|-2.000|-2.000|4|4|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|1.000|1.000 -10|13|FIRST|-2.000|-2.000|4|5|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|1.000| -10|14|FIRST|-2.000|-2.000|4|6|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|1.000| -|0|FIRST|-2.000|-2.000|4|4|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|1.000|1.000 -|1|FIRST|0.000|0.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|2.000|2.000 -|2|FIRST|1.000|-1.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|3.000|3.000 -|3|FIRST|1.000|1.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|4.000|4.000 -|4|FIRST|2.000|2.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|5.000|5.000 -|10|FIRST|-2.000|-2.000|4|4|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|1.000|1.000 -|11|FIRST|0.000|0.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|2.000|2.000 -|12|FIRST|1.000|-1.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|3.000|3.000 -|13|FIRST|1.000|1.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|4.000|4.000 -|14|FIRST|2.000|2.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|5.000|5.000 +10|13|FIRST|-2.000|-2.000|4|4|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|1.000|1.000 +10|14|FIRST|-2.000|-2.000|4|4|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|1.000|1.000 +|0|FIRST||||||||||||||| +|1|FIRST||||||||||||||| +|2|FIRST||||||||||||||| +|3|FIRST||||||||||||||| +|4|FIRST||||||||||||||| +|10|FIRST||||||||||||||| +|11|FIRST||||||||||||||| +|12|FIRST||||||||||||||| +|13|FIRST||||||||||||||| +|14|FIRST||||||||||||||| 0||FIRST|-2.000|-2.000|4|4|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|1.000|1.000 1||FIRST|0.000|0.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|2.000|2.000 2||FIRST|1.000|-1.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|3.000|3.000 diff --git a/raster/test/regress/rt_mapalgebrafct_2raster.sql b/raster/test/regress/rt_mapalgebrafct_2raster.sql new file mode 100644 index 000000000..00ee8d9cb --- /dev/null +++ b/raster/test/regress/rt_mapalgebrafct_2raster.sql @@ -0,0 +1,344 @@ +DROP TABLE IF EXISTS raster_mapalgebra; +CREATE TABLE raster_mapalgebra ( + rid integer, + rast raster +); +DROP TABLE IF EXISTS raster_mapalgebra_out; +CREATE TABLE raster_mapalgebra_out ( + rid1 integer, + rid2 integer, + extent varchar, + rast raster +); +CREATE OR REPLACE FUNCTION make_test_raster( + rid integer, + width integer DEFAULT 2, + height integer DEFAULT 2, + ul_x double precision DEFAULT 0, + ul_y double precision DEFAULT 0, + skew_x double precision DEFAULT 0, + skew_y double precision DEFAULT 0, + initvalue double precision DEFAULT 1, + nodataval double precision DEFAULT 0 +) + RETURNS void + AS $$ + DECLARE + x int; + y int; + rast raster; + BEGIN + rast := ST_MakeEmptyRaster(width, height, ul_x, ul_y, 1, 1, skew_x, skew_y, 0); + rast := ST_AddBand(rast, 1, '8BUI', initvalue, nodataval); + + INSERT INTO raster_mapalgebra VALUES (rid, rast); + + RETURN; + END; + $$ LANGUAGE 'plpgsql'; +-- no skew +SELECT make_test_raster(0, 4, 4, -2, -2); +SELECT make_test_raster(1, 2, 2, 0, 0, 0, 0, 2); +SELECT make_test_raster(2, 2, 2, 1, -1, 0, 0, 3); +SELECT make_test_raster(3, 2, 2, 1, 1, 0, 0, 4); +SELECT make_test_raster(4, 2, 2, 2, 2, 0, 0, 5); + +-- skew +SELECT make_test_raster(10, 4, 4, -2, -2, 1, -1); +SELECT make_test_raster(11, 2, 2, 0, 0, 1, -1, 2); +SELECT make_test_raster(12, 2, 2, 1, -1, 1, -1, 3); +SELECT make_test_raster(13, 2, 2, 1, 1, 1, -1, 4); +SELECT make_test_raster(14, 2, 2, 2, 2, 1, -1, 5); + +DROP FUNCTION IF EXISTS make_test_raster(integer, integer, integer, double precision, double precision, double precision, double precision, double precision, double precision); + +CREATE OR REPLACE FUNCTION raster_mapalgebra_intersection( + rast1 double precision, + rast2 double precision, + VARIADIC userargs text[] +) + RETURNS double precision + AS $$ + DECLARE + BEGIN + IF rast1 IS NOT NULL AND rast2 IS NOT NULL THEN + RETURN rast1; + ELSE + RETURN NULL; + END IF; + + RETURN NULL; + END; + $$ LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION raster_mapalgebra_union( + rast1 double precision, + rast2 double precision, + VARIADIC userargs text[] +) + RETURNS double precision + AS $$ + DECLARE + BEGIN + CASE + WHEN rast1 IS NOT NULL AND rast2 IS NOT NULL THEN + RETURN ((rast1 + rast2)/2.); + WHEN rast1 IS NULL AND rast2 IS NULL THEN + RETURN NULL; + WHEN rast1 IS NULL THEN + RETURN rast2; + ELSE + RETURN rast1; + END CASE; + + RETURN NULL; + END; + $$ LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION raster_mapalgebra_first( + rast1 double precision, + rast2 double precision, + VARIADIC userargs text[] +) + RETURNS double precision + AS $$ + DECLARE + BEGIN + CASE + WHEN rast1 IS NOT NULL AND rast2 IS NOT NULL THEN + RETURN NULL; + WHEN rast1 IS NOT NULL THEN + RETURN rast1; + ELSE + RETURN NULL; + END CASE; + + RETURN NULL; + END; + $$ LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION raster_mapalgebra_second( + rast1 double precision, + rast2 double precision, + VARIADIC userargs text[] +) + RETURNS double precision + AS $$ + DECLARE + BEGIN + CASE + WHEN rast1 IS NOT NULL AND rast2 IS NOT NULL THEN + RETURN NULL; + WHEN rast2 IS NOT NULL THEN + RETURN rast2; + ELSE + RETURN NULL; + END CASE; + + RETURN NULL; + END; + $$ LANGUAGE 'plpgsql'; + +-- INTERSECTION +INSERT INTO raster_mapalgebra_out + (SELECT r1.rid, r2.rid, 'INTERSECTION', st_mapalgebrafct( + r1.rast, r2.rast, 'raster_mapalgebra_intersection(double precision, double precision, text[])'::regprocedure, '32BF', 'INTERSECTION' + ) + FROM raster_mapalgebra r1 + JOIN raster_mapalgebra r2 + ON r1.rid != r2.rid + WHERE r1.rid = 0 + AND r2.rid BETWEEN 1 AND 9 + ) UNION ALL ( + SELECT r1.rid, r2.rid, 'INTERSECTION', st_mapalgebrafct( + r1.rast, r2.rast, 'raster_mapalgebra_intersection(double precision, double precision, text[])'::regprocedure, '32BF', 'INTERSECTION' + ) + FROM raster_mapalgebra r1 + JOIN raster_mapalgebra r2 + ON r1.rid != r2.rid + WHERE r1.rid = 10 + AND r2.rid BETWEEN 11 AND 19) +; + +INSERT INTO raster_mapalgebra_out + SELECT NULL AS rid, rid, 'INTERSECTION', st_mapalgebrafct( + NULL::raster, rast, 'raster_mapalgebra_intersection(double precision, double precision, text[])'::regprocedure, '32BF', 'INTERSECTION' + ) + FROM raster_mapalgebra +; + +INSERT INTO raster_mapalgebra_out + SELECT rid, NULL AS rid, 'INTERSECTION', st_mapalgebrafct( + rast, NULL::raster, 'raster_mapalgebra_intersection(double precision, double precision, text[])'::regprocedure, '32BF', 'INTERSECTION' + ) + FROM raster_mapalgebra +; + +INSERT INTO raster_mapalgebra_out + SELECT NULL AS rid, NULL AS rid, 'INTERSECTION', st_mapalgebrafct( + NULL::raster, NULL::raster, 'raster_mapalgebra_intersection(double precision, double precision, text[])'::regprocedure, '32BF', 'INTERSECTION' + ) +; + +-- UNION +INSERT INTO raster_mapalgebra_out + (SELECT r1.rid, r2.rid, 'UNION', st_mapalgebrafct( + r1.rast, r2.rast, 'raster_mapalgebra_union(double precision, double precision, text[])'::regprocedure, '32BF', 'UNION' + ) + FROM raster_mapalgebra r1 + JOIN raster_mapalgebra r2 + ON r1.rid != r2.rid + WHERE r1.rid = 0 + AND r2.rid BETWEEN 1 AND 9 + ) UNION ALL ( + SELECT r1.rid, r2.rid, 'UNION', st_mapalgebrafct( + r1.rast, r2.rast, 'raster_mapalgebra_union(double precision, double precision, text[])'::regprocedure, '32BF', 'UNION' + ) + FROM raster_mapalgebra r1 + JOIN raster_mapalgebra r2 + ON r1.rid != r2.rid + WHERE r1.rid = 10 + AND r2.rid BETWEEN 11 AND 19) +; + +INSERT INTO raster_mapalgebra_out + SELECT NULL AS rid, rid, 'UNION', st_mapalgebrafct( + NULL::raster, rast, 'raster_mapalgebra_union(double precision, double precision, text[])'::regprocedure, '32BF', 'UNION' + ) + FROM raster_mapalgebra +; + +INSERT INTO raster_mapalgebra_out + SELECT rid, NULL AS rid, 'UNION', st_mapalgebrafct( + rast, NULL::raster, 'raster_mapalgebra_union(double precision, double precision, text[])'::regprocedure, '32BF', 'UNION' + ) + FROM raster_mapalgebra +; + +INSERT INTO raster_mapalgebra_out + SELECT NULL AS rid, NULL AS rid, 'UNION', st_mapalgebrafct( + NULL::raster, NULL::raster, 'raster_mapalgebra_union(double precision, double precision, text[])'::regprocedure, '32BF', 'UNION' + ) +; + +-- FIRST +INSERT INTO raster_mapalgebra_out + (SELECT r1.rid, r2.rid, 'FIRST', st_mapalgebrafct( + r1.rast, r2.rast, 'raster_mapalgebra_first(double precision, double precision, text[])'::regprocedure, '32BF', 'FIRST' + ) + FROM raster_mapalgebra r1 + JOIN raster_mapalgebra r2 + ON r1.rid != r2.rid + WHERE r1.rid = 0 + AND r2.rid BETWEEN 1 AND 9 + ) UNION ALL ( + SELECT r1.rid, r2.rid, 'FIRST', st_mapalgebrafct( + r1.rast, r2.rast, 'raster_mapalgebra_first(double precision, double precision, text[])'::regprocedure, '32BF', 'FIRST' + ) + FROM raster_mapalgebra r1 + JOIN raster_mapalgebra r2 + ON r1.rid != r2.rid + WHERE r1.rid = 10 + AND r2.rid BETWEEN 11 AND 19) +; + +INSERT INTO raster_mapalgebra_out + SELECT NULL AS rid, rid, 'FIRST', st_mapalgebrafct( + NULL::raster, rast, 'raster_mapalgebra_first(double precision, double precision, text[])'::regprocedure, '32BF', 'FIRST' + ) + FROM raster_mapalgebra +; + +INSERT INTO raster_mapalgebra_out + SELECT rid, NULL AS rid, 'FIRST', st_mapalgebrafct( + rast, NULL::raster, 'raster_mapalgebra_first(double precision, double precision, text[])'::regprocedure, '32BF', 'FIRST' + ) + FROM raster_mapalgebra +; + +INSERT INTO raster_mapalgebra_out + SELECT NULL AS rid, NULL AS rid, 'FIRST', st_mapalgebrafct( + NULL::raster, NULL::raster, 'raster_mapalgebra_first(double precision, double precision, text[])'::regprocedure, '32BF', 'FIRST' + ) +; + +-- SECOND +INSERT INTO raster_mapalgebra_out + (SELECT r1.rid, r2.rid, 'SECOND', st_mapalgebrafct( + r1.rast, r2.rast, 'raster_mapalgebra_second(double precision, double precision, text[])'::regprocedure, '32BF', 'SECOND' + ) + FROM raster_mapalgebra r1 + JOIN raster_mapalgebra r2 + ON r1.rid != r2.rid + WHERE r1.rid = 0 + AND r2.rid BETWEEN 1 AND 9 + ) UNION ALL ( + SELECT r1.rid, r2.rid, 'SECOND', st_mapalgebrafct( + r1.rast, r2.rast, 'raster_mapalgebra_second(double precision, double precision, text[])'::regprocedure, '32BF', 'SECOND' + ) + FROM raster_mapalgebra r1 + JOIN raster_mapalgebra r2 + ON r1.rid != r2.rid + WHERE r1.rid = 10 + AND r2.rid BETWEEN 11 AND 19) +; + +INSERT INTO raster_mapalgebra_out + SELECT NULL AS rid, rid, 'SECOND', st_mapalgebrafct( + NULL::raster, rast, 'raster_mapalgebra_second(double precision, double precision, text[])'::regprocedure, '32BF', 'SECOND' + ) + FROM raster_mapalgebra +; + +INSERT INTO raster_mapalgebra_out + SELECT rid, NULL AS rid, 'SECOND', st_mapalgebrafct( + rast, NULL::raster, 'raster_mapalgebra_second(double precision, double precision, text[])'::regprocedure, '32BF', 'SECOND' + ) + FROM raster_mapalgebra +; + +INSERT INTO raster_mapalgebra_out + SELECT NULL AS rid, NULL AS rid, 'SECOND', st_mapalgebrafct( + NULL::raster, NULL::raster, 'raster_mapalgebra_second(double precision, double precision, text[])'::regprocedure, '32BF', 'SECOND' + ) +; + +-- output +SELECT + rid1, + rid2, + extent, + round(upperleftx::numeric, 3) AS upperleftx, + round(upperlefty::numeric, 3) AS upperlefty, + width, + height, + round(scalex::numeric, 3) AS scalex, + round(scaley::numeric, 3) AS scaley, + round(skewx::numeric, 3) AS skewx, + round(skewy::numeric, 3) AS skewy, + srid, + numbands, + pixeltype, + hasnodata, + round(nodatavalue::numeric, 3) AS nodatavalue, + round(firstvalue::numeric, 3) AS firstvalue, + round(lastvalue::numeric, 3) AS lastvalue +FROM ( + SELECT + rid1, + rid2, + extent, + (ST_Metadata(rast)).*, + (ST_BandMetadata(rast, 1)).*, + ST_Value(rast, 1, 1, 1) AS firstvalue, + ST_Value(rast, 1, ST_Width(rast), ST_Height(rast)) AS lastvalue + FROM raster_mapalgebra_out +) AS r; + +DROP FUNCTION IF EXISTS raster_mapalgebra_intersection(double precision, double precision, VARIADIC text[]); +DROP FUNCTION IF EXISTS raster_mapalgebra_union(double precision, double precision, VARIADIC text[]); +DROP FUNCTION IF EXISTS raster_mapalgebra_first(double precision, double precision, VARIADIC text[]); +DROP FUNCTION IF EXISTS raster_mapalgebra_second(double precision, double precision, VARIADIC text[]); + +DROP TABLE IF EXISTS raster_mapalgebra; +DROP TABLE IF EXISTS raster_mapalgebra_out; diff --git a/raster/test/regress/rt_mapalgebrafct_2raster_expected b/raster/test/regress/rt_mapalgebrafct_2raster_expected new file mode 100644 index 000000000..199f09970 --- /dev/null +++ b/raster/test/regress/rt_mapalgebrafct_2raster_expected @@ -0,0 +1,274 @@ +NOTICE: table "raster_mapalgebra" does not exist, skipping +NOTICE: table "raster_mapalgebra_out" does not exist, skipping +NOTICE: The two rasters provided have no intersection. Returning no band raster +NOTICE: The two rasters provided have no intersection. Returning no band raster +NOTICE: The two rasters provided have no intersection. Returning no band raster +NOTICE: The two rasters provided have no intersection. Returning no band raster +NOTICE: The two rasters provided have no intersection. Returning no band raster +NOTICE: The two rasters provided have no intersection. Returning no band raster +NOTICE: The two rasters provided have no intersection. Returning no band raster +NOTICE: The two rasters provided have no intersection. Returning no band raster +NOTICE: The two rasters provided have no intersection. Returning no band raster +NOTICE: The two rasters provided have no intersection. Returning no band raster +NOTICE: The two rasters provided have no intersection. Returning no band raster +NOTICE: The two rasters provided have no intersection. Returning no band raster +NOTICE: The two rasters provided have no intersection. Returning no band raster +NOTICE: The two rasters provided have no intersection. Returning no band raster +NOTICE: The two rasters provided have no intersection. Returning no band raster +NOTICE: The two rasters provided have no intersection. Returning no band raster +NOTICE: The two rasters provided have no intersection. Returning no band raster +NOTICE: The two rasters provided have no intersection. Returning no band raster +NOTICE: The two rasters provided have no intersection. Returning no band raster +NOTICE: The two rasters provided have no intersection. Returning no band raster +NOTICE: The two rasters provided have no intersection. Returning no band raster +NOTICE: The two rasters provided have no intersection. Returning no band raster +NOTICE: The two rasters provided are NULL. Returning NULL +NOTICE: The two rasters provided are NULL. Returning NULL +NOTICE: The FIRST raster is NULL. Returning NULL +NOTICE: The FIRST raster is NULL. Returning NULL +NOTICE: The FIRST raster is NULL. Returning NULL +NOTICE: The FIRST raster is NULL. Returning NULL +NOTICE: The FIRST raster is NULL. Returning NULL +NOTICE: The FIRST raster is NULL. Returning NULL +NOTICE: The FIRST raster is NULL. Returning NULL +NOTICE: The FIRST raster is NULL. Returning NULL +NOTICE: The FIRST raster is NULL. Returning NULL +NOTICE: The FIRST raster is NULL. Returning NULL +NOTICE: The two rasters provided are NULL. Returning NULL +NOTICE: The SECOND raster is NULL. Returning NULL +NOTICE: The SECOND raster is NULL. Returning NULL +NOTICE: The SECOND raster is NULL. Returning NULL +NOTICE: The SECOND raster is NULL. Returning NULL +NOTICE: The SECOND raster is NULL. Returning NULL +NOTICE: The SECOND raster is NULL. Returning NULL +NOTICE: The SECOND raster is NULL. Returning NULL +NOTICE: The SECOND raster is NULL. Returning NULL +NOTICE: The SECOND raster is NULL. Returning NULL +NOTICE: The SECOND raster is NULL. Returning NULL +NOTICE: The two rasters provided are NULL. Returning NULL +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Raster provided has no bands +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +NOTICE: Could not find raster band of index 1 when getting pixel value. Returning NULL +0|1|INTERSECTION|0.000|0.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|1.000|1.000 +0|2|INTERSECTION|1.000|-1.000|1|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|1.000|1.000 +0|3|INTERSECTION|1.000|1.000|1|1|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|1.000|1.000 +0|4|INTERSECTION|0.000|0.000|0|0|0.000|0.000|0.000|0.000|0|0||||| +10|11|INTERSECTION|0.000|0.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|1.000|1.000 +10|12|INTERSECTION|1.000|-1.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|1.000|1.000 +10|13|INTERSECTION|1.000|1.000|2|1|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|1.000|1.000 +10|14|INTERSECTION|0.000|0.000|0|0|0.000|0.000|0.000|0.000|0|0||||| +|0|INTERSECTION|0.000|0.000|0|0|0.000|0.000|0.000|0.000|0|0||||| +|1|INTERSECTION|0.000|0.000|0|0|0.000|0.000|0.000|0.000|0|0||||| +|2|INTERSECTION|0.000|0.000|0|0|0.000|0.000|0.000|0.000|0|0||||| +|3|INTERSECTION|0.000|0.000|0|0|0.000|0.000|0.000|0.000|0|0||||| +|4|INTERSECTION|0.000|0.000|0|0|0.000|0.000|0.000|0.000|0|0||||| +|10|INTERSECTION|0.000|0.000|0|0|0.000|0.000|0.000|0.000|0|0||||| +|11|INTERSECTION|0.000|0.000|0|0|0.000|0.000|0.000|0.000|0|0||||| +|12|INTERSECTION|0.000|0.000|0|0|0.000|0.000|0.000|0.000|0|0||||| +|13|INTERSECTION|0.000|0.000|0|0|0.000|0.000|0.000|0.000|0|0||||| +|14|INTERSECTION|0.000|0.000|0|0|0.000|0.000|0.000|0.000|0|0||||| +0||INTERSECTION|0.000|0.000|0|0|0.000|0.000|0.000|0.000|0|0||||| +1||INTERSECTION|0.000|0.000|0|0|0.000|0.000|0.000|0.000|0|0||||| +2||INTERSECTION|0.000|0.000|0|0|0.000|0.000|0.000|0.000|0|0||||| +3||INTERSECTION|0.000|0.000|0|0|0.000|0.000|0.000|0.000|0|0||||| +4||INTERSECTION|0.000|0.000|0|0|0.000|0.000|0.000|0.000|0|0||||| +10||INTERSECTION|0.000|0.000|0|0|0.000|0.000|0.000|0.000|0|0||||| +11||INTERSECTION|0.000|0.000|0|0|0.000|0.000|0.000|0.000|0|0||||| +12||INTERSECTION|0.000|0.000|0|0|0.000|0.000|0.000|0.000|0|0||||| +13||INTERSECTION|0.000|0.000|0|0|0.000|0.000|0.000|0.000|0|0||||| +14||INTERSECTION|0.000|0.000|0|0|0.000|0.000|0.000|0.000|0|0||||| +||INTERSECTION||||||||||||||| +0|1|UNION|-2.000|-2.000|4|4|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|1.000|1.500 +0|2|UNION|-2.000|-2.000|5|4|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|1.000| +0|3|UNION|-2.000|-2.000|5|5|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|1.000|4.000 +0|4|UNION|-2.000|-2.000|6|6|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|1.000|5.000 +10|11|UNION|-2.000|-2.000|4|4|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|1.000|1.000 +10|12|UNION|-2.000|-2.000|4|4|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|1.000|1.000 +10|13|UNION|-2.000|-2.000|4|5|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|1.000| +10|14|UNION|-2.000|-2.000|4|6|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|1.000| +|0|UNION|-2.000|-2.000|4|4|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|1.000|1.000 +|1|UNION|0.000|0.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|2.000|2.000 +|2|UNION|1.000|-1.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|3.000|3.000 +|3|UNION|1.000|1.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|4.000|4.000 +|4|UNION|2.000|2.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|5.000|5.000 +|10|UNION|-2.000|-2.000|4|4|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|1.000|1.000 +|11|UNION|0.000|0.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|2.000|2.000 +|12|UNION|1.000|-1.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|3.000|3.000 +|13|UNION|1.000|1.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|4.000|4.000 +|14|UNION|2.000|2.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|5.000|5.000 +0||UNION|-2.000|-2.000|4|4|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|1.000|1.000 +1||UNION|0.000|0.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|2.000|2.000 +2||UNION|1.000|-1.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|3.000|3.000 +3||UNION|1.000|1.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|4.000|4.000 +4||UNION|2.000|2.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|5.000|5.000 +10||UNION|-2.000|-2.000|4|4|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|1.000|1.000 +11||UNION|0.000|0.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|2.000|2.000 +12||UNION|1.000|-1.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|3.000|3.000 +13||UNION|1.000|1.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|4.000|4.000 +14||UNION|2.000|2.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|5.000|5.000 +||UNION||||||||||||||| +0|1|FIRST|-2.000|-2.000|4|4|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|1.000| +0|2|FIRST|-2.000|-2.000|4|4|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|1.000|1.000 +0|3|FIRST|-2.000|-2.000|4|4|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|1.000| +0|4|FIRST|-2.000|-2.000|4|4|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|1.000|1.000 +10|11|FIRST|-2.000|-2.000|4|4|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|1.000|1.000 +10|12|FIRST|-2.000|-2.000|4|4|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|1.000|1.000 +10|13|FIRST|-2.000|-2.000|4|4|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|1.000|1.000 +10|14|FIRST|-2.000|-2.000|4|4|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|1.000|1.000 +|0|FIRST||||||||||||||| +|1|FIRST||||||||||||||| +|2|FIRST||||||||||||||| +|3|FIRST||||||||||||||| +|4|FIRST||||||||||||||| +|10|FIRST||||||||||||||| +|11|FIRST||||||||||||||| +|12|FIRST||||||||||||||| +|13|FIRST||||||||||||||| +|14|FIRST||||||||||||||| +0||FIRST|-2.000|-2.000|4|4|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|1.000|1.000 +1||FIRST|0.000|0.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|2.000|2.000 +2||FIRST|1.000|-1.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|3.000|3.000 +3||FIRST|1.000|1.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|4.000|4.000 +4||FIRST|2.000|2.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|5.000|5.000 +10||FIRST|-2.000|-2.000|4|4|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|1.000|1.000 +11||FIRST|0.000|0.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|2.000|2.000 +12||FIRST|1.000|-1.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|3.000|3.000 +13||FIRST|1.000|1.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|4.000|4.000 +14||FIRST|2.000|2.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|5.000|5.000 +||FIRST||||||||||||||| +0|1|SECOND|0.000|0.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|| +0|2|SECOND|1.000|-1.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000||3.000 +0|3|SECOND|1.000|1.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000||4.000 +0|4|SECOND|2.000|2.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|5.000|5.000 +10|11|SECOND|0.000|0.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|| +10|12|SECOND|1.000|-1.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|| +10|13|SECOND|1.000|1.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000||4.000 +10|14|SECOND|2.000|2.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|5.000|5.000 +|0|SECOND|-2.000|-2.000|4|4|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|1.000|1.000 +|1|SECOND|0.000|0.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|2.000|2.000 +|2|SECOND|1.000|-1.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|3.000|3.000 +|3|SECOND|1.000|1.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|4.000|4.000 +|4|SECOND|2.000|2.000|2|2|1.000|1.000|0.000|0.000|0|1|32BF|t|0.000|5.000|5.000 +|10|SECOND|-2.000|-2.000|4|4|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|1.000|1.000 +|11|SECOND|0.000|0.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|2.000|2.000 +|12|SECOND|1.000|-1.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|3.000|3.000 +|13|SECOND|1.000|1.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|4.000|4.000 +|14|SECOND|2.000|2.000|2|2|1.000|1.000|1.000|-1.000|0|1|32BF|t|0.000|5.000|5.000 +0||SECOND||||||||||||||| +1||SECOND||||||||||||||| +2||SECOND||||||||||||||| +3||SECOND||||||||||||||| +4||SECOND||||||||||||||| +10||SECOND||||||||||||||| +11||SECOND||||||||||||||| +12||SECOND||||||||||||||| +13||SECOND||||||||||||||| +14||SECOND||||||||||||||| +||SECOND||||||||||||||| -- 2.40.0