dabd772320
that fill in z/m coordinates of a geometry using data read from a raster. Also adds 'bilinear' interpolation mode to ST_Value() to allow sampling between pixes in reading values from rasters.
2510 lines
63 KiB
C
2510 lines
63 KiB
C
/*
|
|
*
|
|
* WKTRaster - Raster Types for PostGIS
|
|
* http://trac.osgeo.org/postgis/wiki/WKTRaster
|
|
*
|
|
* Copyright (C) 2011-2013 Regents of the University of California
|
|
* <bkpark@ucdavis.edu>
|
|
* Copyright (C) 2010-2011 Jorge Arevalo <jorge.arevalo@deimos-space.com>
|
|
* Copyright (C) 2010-2011 David Zwarg <dzwarg@azavea.com>
|
|
* Copyright (C) 2009-2011 Pierre Racine <pierre.racine@sbf.ulaval.ca>
|
|
* Copyright (C) 2009-2011 Mateusz Loskot <mateusz@loskot.net>
|
|
* Copyright (C) 2008-2009 Sandro Santilli <strk@kbt.io>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include <postgres.h>
|
|
#include <fmgr.h>
|
|
#include "utils/lsyscache.h" /* for get_typlenbyvalalign */
|
|
#include <funcapi.h>
|
|
#include "utils/array.h" /* for ArrayType */
|
|
#include "utils/builtins.h" /* for text_to_cstring */
|
|
#include "utils/formatting.h" /* for asc_tolower */
|
|
#include "catalog/pg_type.h" /* for INT2OID, INT4OID, FLOAT4OID, FLOAT8OID and TEXTOID */
|
|
|
|
#include "../../postgis_config.h"
|
|
#include "lwgeom_pg.h"
|
|
|
|
|
|
|
|
#include "access/htup_details.h" /* for heap_form_tuple() */
|
|
|
|
|
|
#include "rtpostgis.h"
|
|
|
|
/* Get pixel value */
|
|
Datum RASTER_getPixelValue(PG_FUNCTION_ARGS);
|
|
Datum RASTER_getPixelValueResample(PG_FUNCTION_ARGS);
|
|
Datum RASTER_dumpValues(PG_FUNCTION_ARGS);
|
|
|
|
/* Set pixel value(s) */
|
|
Datum RASTER_setPixelValue(PG_FUNCTION_ARGS);
|
|
Datum RASTER_setPixelValuesArray(PG_FUNCTION_ARGS);
|
|
Datum RASTER_setPixelValuesGeomval(PG_FUNCTION_ARGS);
|
|
Datum RASTER_getGeometryValues(PG_FUNCTION_ARGS);
|
|
|
|
/* Get pixels of value */
|
|
Datum RASTER_pixelOfValue(PG_FUNCTION_ARGS);
|
|
|
|
/* Get nearest value to a point */
|
|
Datum RASTER_nearestValue(PG_FUNCTION_ARGS);
|
|
|
|
/* Get the neighborhood around a pixel */
|
|
Datum RASTER_neighborhood(PG_FUNCTION_ARGS);
|
|
|
|
/**
|
|
* Return value of a single pixel.
|
|
* Pixel location is specified by 1-based index of Nth band of raster and
|
|
* X,Y coordinates (X <= RT_Width(raster) and Y <= RT_Height(raster)).
|
|
*
|
|
* TODO: Should we return NUMERIC instead of FLOAT8 ?
|
|
*/
|
|
PG_FUNCTION_INFO_V1(RASTER_getPixelValue);
|
|
Datum RASTER_getPixelValue(PG_FUNCTION_ARGS)
|
|
{
|
|
rt_pgraster *pgraster = NULL;
|
|
rt_raster raster = NULL;
|
|
rt_band band = NULL;
|
|
double pixvalue = 0;
|
|
int32_t bandindex = 0;
|
|
int32_t x = 0;
|
|
int32_t y = 0;
|
|
int result = 0;
|
|
bool exclude_nodata_value = TRUE;
|
|
int isnodata = 0;
|
|
|
|
/* Index is 1-based */
|
|
bandindex = PG_GETARG_INT32(1);
|
|
if ( bandindex < 1 ) {
|
|
elog(NOTICE, "Invalid band index (must use 1-based). Returning NULL");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
x = PG_GETARG_INT32(2);
|
|
|
|
y = PG_GETARG_INT32(3);
|
|
|
|
exclude_nodata_value = PG_GETARG_BOOL(4);
|
|
|
|
POSTGIS_RT_DEBUGF(3, "Pixel coordinates (%d, %d)", x, y);
|
|
|
|
/* Deserialize raster */
|
|
if (PG_ARGISNULL(0)) PG_RETURN_NULL();
|
|
pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
|
|
|
raster = rt_raster_deserialize(pgraster, FALSE);
|
|
if (!raster) {
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_getPixelValue: Could not deserialize raster");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* Fetch Nth band using 0-based internal index */
|
|
band = rt_raster_get_band(raster, bandindex - 1);
|
|
if (! band) {
|
|
elog(NOTICE, "Could not find raster band of index %d when getting pixel "
|
|
"value. Returning NULL", bandindex);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
PG_RETURN_NULL();
|
|
}
|
|
/* Fetch pixel using 0-based coordinates */
|
|
result = rt_band_get_pixel(band, x - 1, y - 1, &pixvalue, &isnodata);
|
|
|
|
/* If the result is -1 or the value is nodata and we take nodata into account
|
|
* then return nodata = NULL */
|
|
if (result != ES_NONE || (exclude_nodata_value && isnodata)) {
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
|
|
PG_RETURN_FLOAT8(pixvalue);
|
|
}
|
|
|
|
static rt_resample_type resample_text_to_type(text *txt)
|
|
{
|
|
char *resample = asc_tolower(VARDATA(txt), VARSIZE_ANY_EXHDR(txt));
|
|
if (strncmp(resample, "bilinear", 8) == 0)
|
|
return RT_BILINEAR;
|
|
else if (strncmp(resample, "nearest", 7) == 0)
|
|
return RT_NEAREST;
|
|
else {
|
|
elog(ERROR, "Unknown resample type '%s' requested", resample);
|
|
}
|
|
pfree(resample);
|
|
return RT_NEAREST;
|
|
}
|
|
|
|
/*
|
|
* ST_Value(
|
|
* rast raster,
|
|
* band integer,
|
|
* pt geometry,
|
|
* exclude_nodata_value boolean DEFAULT TRUE,
|
|
* resample text DEFAULT 'nearest'
|
|
*/
|
|
PG_FUNCTION_INFO_V1(RASTER_getPixelValueResample);
|
|
Datum RASTER_getPixelValueResample(PG_FUNCTION_ARGS)
|
|
{
|
|
rt_raster raster = NULL;
|
|
rt_band band = NULL;
|
|
rt_pgraster *pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
|
int32_t bandnum = PG_GETARG_INT32(1);
|
|
GSERIALIZED *gser;
|
|
LWPOINT *lwpoint;
|
|
LWGEOM *lwgeom;
|
|
bool exclude_nodata_value = PG_GETARG_BOOL(3);
|
|
rt_resample_type resample_type = RT_NEAREST;
|
|
double x, y, xr, yr;
|
|
double pixvalue = 0.0;
|
|
int isnodata = 0;
|
|
rt_errorstate err;
|
|
|
|
/* Index is 1-based */
|
|
if (bandnum < 1) {
|
|
elog(NOTICE, "Invalid band index (must use 1-based). Returning NULL");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
gser = (GSERIALIZED*)PG_DETOAST_DATUM(PG_GETARG_DATUM(2));
|
|
if (gserialized_get_type(gser) != POINTTYPE || gserialized_is_empty(gser)) {
|
|
elog(ERROR, "Attempting to get the value of a pixel with a non-point geometry");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
raster = rt_raster_deserialize(pgraster, FALSE);
|
|
if (!raster) {
|
|
elog(ERROR, "RASTER_getPixelValue: Could not deserialize raster");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
if (gserialized_get_srid(gser) != rt_raster_get_srid(raster)) {
|
|
elog(ERROR, "Raster and geometry do not have the same SRID");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
if (PG_NARGS() > 4) {
|
|
text *resample_text = PG_GETARG_TEXT_P(4);
|
|
resample_type = resample_text_to_type(resample_text);
|
|
}
|
|
|
|
/* Fetch Nth band using 0-based internal index */
|
|
band = rt_raster_get_band(raster, bandnum - 1);
|
|
if (!band) {
|
|
elog(ERROR, "Could not find raster band of index %d when getting pixel "
|
|
"value. Returning NULL", bandnum);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* Get the X/Y coordinates */
|
|
lwgeom = lwgeom_from_gserialized(gser);
|
|
lwpoint = lwgeom_as_lwpoint(lwgeom);
|
|
x = lwpoint_get_x(lwpoint);
|
|
y = lwpoint_get_y(lwpoint);
|
|
|
|
/* Convert X/Y world coordinates into raster coordinates */
|
|
err = rt_raster_geopoint_to_rasterpoint(raster, x, y, &xr, &yr, NULL);
|
|
if (err != ES_NONE) {
|
|
elog(ERROR, "Could not convert world coordinate to raster coordinate");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* Use appropriate resample algorithm */
|
|
err = rt_band_get_pixel_resample(
|
|
band,
|
|
xr, yr,
|
|
resample_type,
|
|
&pixvalue, &isnodata
|
|
);
|
|
|
|
/* If the result is -1 or the value is nodata and we take nodata into account
|
|
* then return nodata = NULL */
|
|
rt_raster_destroy(raster);
|
|
lwgeom_free(lwgeom);
|
|
if (err != ES_NONE || (exclude_nodata_value && isnodata)) {
|
|
PG_RETURN_NULL();
|
|
}
|
|
PG_RETURN_FLOAT8(pixvalue);
|
|
}
|
|
|
|
|
|
/*
|
|
* ST_SetZ(
|
|
* rast raster,
|
|
* pt geometry,
|
|
* resample text DEFAULT 'nearest' ),
|
|
* band integer default 1,
|
|
* dimension text default 'z'
|
|
*/
|
|
PG_FUNCTION_INFO_V1(RASTER_getGeometryValues);
|
|
Datum RASTER_getGeometryValues(PG_FUNCTION_ARGS)
|
|
{
|
|
rt_raster raster = NULL;
|
|
rt_pgraster *pgraster = NULL;
|
|
GSERIALIZED *gser;
|
|
LWGEOM *lwgeom_in, *lwgeom_out;
|
|
rt_resample_type resample_type = RT_NEAREST;
|
|
rt_errorstate err;
|
|
char dimension;
|
|
const char *func_name;
|
|
uint16_t num_bands;
|
|
int32_t band;
|
|
|
|
text *resample_text = PG_GETARG_TEXT_P(2);
|
|
|
|
/* Dimension depends on the name of calling SQL function */
|
|
/* ST_SetZ()? or ST_SetM()? */
|
|
func_name = get_func_name(fcinfo->flinfo->fn_oid);
|
|
if (strcmp(func_name, "st_setz") == 0)
|
|
dimension = 'z';
|
|
else if (strcmp(func_name, "st_setm") == 0)
|
|
dimension = 'm';
|
|
else
|
|
elog(ERROR, "%s called from unexpected SQL signature", __func__);
|
|
|
|
/* Geometry */
|
|
gser = (GSERIALIZED*)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
|
|
if (gserialized_is_empty(gser)) {
|
|
elog(ERROR, "Cannot copy value into an empty geometry");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* Raster */
|
|
pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
|
raster = rt_raster_deserialize(pgraster, FALSE);
|
|
num_bands = rt_raster_get_num_bands(raster);
|
|
if (!raster) {
|
|
elog(ERROR, "Could not deserialize raster");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* Bandnidex is 1-based */
|
|
band = PG_GETARG_INT32(3);
|
|
if (band < 1 || band > num_bands) {
|
|
elog(NOTICE, "Invalid band index %d. Must be between 1 and %u", band, num_bands);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* SRID consistency */
|
|
if (gserialized_get_srid(gser) != rt_raster_get_srid(raster)) {
|
|
elog(ERROR, "Raster and geometry do not have the same SRID");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* Process arguments */
|
|
resample_type = resample_text_to_type(resample_text);
|
|
|
|
/* Get the geometry */
|
|
lwgeom_in = lwgeom_from_gserialized(gser);
|
|
|
|
/* Run the sample */
|
|
err = rt_raster_copy_to_geometry(
|
|
raster,
|
|
band - 1, /* rtcore uses 0-based band number */
|
|
dimension, /* 'z' or 'm' */
|
|
resample_type, /* bilinear or nearest */
|
|
lwgeom_in,
|
|
&lwgeom_out
|
|
);
|
|
|
|
rt_raster_destroy(raster);
|
|
lwgeom_free(lwgeom_in);
|
|
if (err != ES_NONE || !lwgeom_out) {
|
|
PG_RETURN_NULL();
|
|
}
|
|
PG_RETURN_POINTER(gserialized_from_lwgeom(lwgeom_out, NULL));
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------- */
|
|
/* ST_DumpValues function */
|
|
/* ---------------------------------------------------------------- */
|
|
|
|
typedef struct rtpg_dumpvalues_arg_t *rtpg_dumpvalues_arg;
|
|
struct rtpg_dumpvalues_arg_t {
|
|
int numbands;
|
|
int rows;
|
|
int columns;
|
|
|
|
int *nbands; /* 0-based */
|
|
Datum **values;
|
|
bool **nodata;
|
|
};
|
|
|
|
static rtpg_dumpvalues_arg rtpg_dumpvalues_arg_init() {
|
|
rtpg_dumpvalues_arg arg = NULL;
|
|
|
|
arg = palloc(sizeof(struct rtpg_dumpvalues_arg_t));
|
|
if (arg == NULL) {
|
|
elog(ERROR, "rtpg_dumpvalues_arg_init: Could not allocate memory for arguments");
|
|
return NULL;
|
|
}
|
|
|
|
arg->numbands = 0;
|
|
arg->rows = 0;
|
|
arg->columns = 0;
|
|
|
|
arg->nbands = NULL;
|
|
arg->values = NULL;
|
|
arg->nodata = NULL;
|
|
|
|
return arg;
|
|
}
|
|
|
|
static void rtpg_dumpvalues_arg_destroy(rtpg_dumpvalues_arg arg) {
|
|
int i = 0;
|
|
|
|
if (arg->numbands > 0) {
|
|
if (arg->nbands != NULL)
|
|
pfree(arg->nbands);
|
|
|
|
if (arg->values != NULL) {
|
|
for (i = 0; i < arg->numbands; i++) {
|
|
|
|
if (arg->values[i] != NULL)
|
|
pfree(arg->values[i]);
|
|
|
|
if (arg->nodata[i] != NULL)
|
|
pfree(arg->nodata[i]);
|
|
}
|
|
|
|
pfree(arg->values);
|
|
}
|
|
|
|
if (arg->nodata != NULL)
|
|
pfree(arg->nodata);
|
|
}
|
|
|
|
pfree(arg);
|
|
}
|
|
|
|
#define VALUES_LENGTH 2
|
|
|
|
PG_FUNCTION_INFO_V1(RASTER_dumpValues);
|
|
Datum RASTER_dumpValues(PG_FUNCTION_ARGS)
|
|
{
|
|
FuncCallContext *funcctx;
|
|
TupleDesc tupdesc;
|
|
int call_cntr;
|
|
int max_calls;
|
|
int i = 0;
|
|
int x = 0;
|
|
int y = 0;
|
|
int z = 0;
|
|
|
|
int16 typlen;
|
|
bool typbyval;
|
|
char typalign;
|
|
|
|
rtpg_dumpvalues_arg arg1 = NULL;
|
|
rtpg_dumpvalues_arg arg2 = NULL;
|
|
|
|
/* 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 numbands = 0;
|
|
int j = 0;
|
|
bool exclude_nodata_value = TRUE;
|
|
|
|
ArrayType *array;
|
|
Oid etype;
|
|
Datum *e;
|
|
bool *nulls;
|
|
|
|
double val = 0;
|
|
int isnodata = 0;
|
|
|
|
POSTGIS_RT_DEBUG(2, "RASTER_dumpValues first call");
|
|
|
|
/* create a function context for cross-call persistence */
|
|
funcctx = SRF_FIRSTCALL_INIT();
|
|
|
|
/* switch to memory context appropriate for multiple function calls */
|
|
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
|
|
|
|
/* Get input arguments */
|
|
if (PG_ARGISNULL(0)) {
|
|
MemoryContextSwitchTo(oldcontext);
|
|
SRF_RETURN_DONE(funcctx);
|
|
}
|
|
pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
|
|
|
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);
|
|
}
|
|
|
|
/* check that raster is not empty */
|
|
/*
|
|
if (rt_raster_is_empty(raster)) {
|
|
elog(NOTICE, "Raster provided is empty");
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
MemoryContextSwitchTo(oldcontext);
|
|
SRF_RETURN_DONE(funcctx);
|
|
}
|
|
*/
|
|
|
|
/* raster has bands */
|
|
numbands = rt_raster_get_num_bands(raster);
|
|
if (!numbands) {
|
|
elog(NOTICE, "Raster provided has no bands");
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
MemoryContextSwitchTo(oldcontext);
|
|
SRF_RETURN_DONE(funcctx);
|
|
}
|
|
|
|
/* initialize arg1 */
|
|
arg1 = rtpg_dumpvalues_arg_init();
|
|
if (arg1 == NULL) {
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
MemoryContextSwitchTo(oldcontext);
|
|
elog(ERROR, "RASTER_dumpValues: Could not initialize argument structure");
|
|
SRF_RETURN_DONE(funcctx);
|
|
}
|
|
|
|
/* nband, array */
|
|
if (!PG_ARGISNULL(1)) {
|
|
array = PG_GETARG_ARRAYTYPE_P(1);
|
|
etype = ARR_ELEMTYPE(array);
|
|
get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
|
|
|
|
switch (etype) {
|
|
case INT2OID:
|
|
case INT4OID:
|
|
break;
|
|
default:
|
|
rtpg_dumpvalues_arg_destroy(arg1);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
MemoryContextSwitchTo(oldcontext);
|
|
elog(ERROR, "RASTER_dumpValues: Invalid data type for band indexes");
|
|
SRF_RETURN_DONE(funcctx);
|
|
break;
|
|
}
|
|
|
|
deconstruct_array(array, etype, typlen, typbyval, typalign, &e, &nulls, &(arg1->numbands));
|
|
|
|
arg1->nbands = palloc(sizeof(int) * arg1->numbands);
|
|
if (arg1->nbands == NULL) {
|
|
rtpg_dumpvalues_arg_destroy(arg1);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
MemoryContextSwitchTo(oldcontext);
|
|
elog(ERROR, "RASTER_dumpValues: Could not allocate memory for band indexes");
|
|
SRF_RETURN_DONE(funcctx);
|
|
}
|
|
|
|
for (i = 0, j = 0; i < arg1->numbands; i++) {
|
|
if (nulls[i]) continue;
|
|
|
|
switch (etype) {
|
|
case INT2OID:
|
|
arg1->nbands[j] = DatumGetInt16(e[i]) - 1;
|
|
break;
|
|
case INT4OID:
|
|
arg1->nbands[j] = DatumGetInt32(e[i]) - 1;
|
|
break;
|
|
}
|
|
|
|
j++;
|
|
}
|
|
|
|
if (j < arg1->numbands) {
|
|
arg1->nbands = repalloc(arg1->nbands, sizeof(int) * j);
|
|
if (arg1->nbands == NULL) {
|
|
rtpg_dumpvalues_arg_destroy(arg1);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
MemoryContextSwitchTo(oldcontext);
|
|
elog(ERROR, "RASTER_dumpValues: Could not reallocate memory for band indexes");
|
|
SRF_RETURN_DONE(funcctx);
|
|
}
|
|
|
|
arg1->numbands = j;
|
|
}
|
|
|
|
/* validate nbands */
|
|
for (i = 0; i < arg1->numbands; i++) {
|
|
if (!rt_raster_has_band(raster, arg1->nbands[i])) {
|
|
elog(NOTICE, "Band at index %d not found in raster", arg1->nbands[i] + 1);
|
|
rtpg_dumpvalues_arg_destroy(arg1);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
MemoryContextSwitchTo(oldcontext);
|
|
SRF_RETURN_DONE(funcctx);
|
|
}
|
|
}
|
|
|
|
}
|
|
/* no bands specified, return all bands */
|
|
else {
|
|
arg1->numbands = numbands;
|
|
arg1->nbands = palloc(sizeof(int) * arg1->numbands);
|
|
|
|
if (arg1->nbands == NULL) {
|
|
rtpg_dumpvalues_arg_destroy(arg1);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
MemoryContextSwitchTo(oldcontext);
|
|
elog(ERROR, "RASTER_dumpValues: Could not allocate memory for band indexes");
|
|
SRF_RETURN_DONE(funcctx);
|
|
}
|
|
|
|
for (i = 0; i < arg1->numbands; i++) {
|
|
arg1->nbands[i] = i;
|
|
POSTGIS_RT_DEBUGF(4, "arg1->nbands[%d] = %d", arg1->nbands[i], i);
|
|
}
|
|
}
|
|
|
|
arg1->rows = rt_raster_get_height(raster);
|
|
arg1->columns = rt_raster_get_width(raster);
|
|
|
|
/* exclude_nodata_value */
|
|
if (!PG_ARGISNULL(2))
|
|
exclude_nodata_value = PG_GETARG_BOOL(2);
|
|
POSTGIS_RT_DEBUGF(4, "exclude_nodata_value = %d", exclude_nodata_value);
|
|
|
|
/* allocate memory for each band's values and nodata flags */
|
|
arg1->values = palloc(sizeof(Datum *) * arg1->numbands);
|
|
arg1->nodata = palloc(sizeof(bool *) * arg1->numbands);
|
|
if (arg1->values == NULL || arg1->nodata == NULL) {
|
|
rtpg_dumpvalues_arg_destroy(arg1);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
MemoryContextSwitchTo(oldcontext);
|
|
elog(ERROR, "RASTER_dumpValues: Could not allocate memory for pixel values");
|
|
SRF_RETURN_DONE(funcctx);
|
|
}
|
|
memset(arg1->values, 0, sizeof(Datum *) * arg1->numbands);
|
|
memset(arg1->nodata, 0, sizeof(bool *) * arg1->numbands);
|
|
|
|
/* get each band and dump data */
|
|
for (z = 0; z < arg1->numbands; z++) {
|
|
/* shortcut if raster is empty */
|
|
if (rt_raster_is_empty(raster))
|
|
break;
|
|
|
|
band = rt_raster_get_band(raster, arg1->nbands[z]);
|
|
if (!band) {
|
|
int nband = arg1->nbands[z] + 1;
|
|
rtpg_dumpvalues_arg_destroy(arg1);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
MemoryContextSwitchTo(oldcontext);
|
|
elog(ERROR, "RASTER_dumpValues: Could not get band at index %d", nband);
|
|
SRF_RETURN_DONE(funcctx);
|
|
}
|
|
|
|
/* allocate memory for values and nodata flags */
|
|
arg1->values[z] = palloc(sizeof(Datum) * arg1->rows * arg1->columns);
|
|
arg1->nodata[z] = palloc(sizeof(bool) * arg1->rows * arg1->columns);
|
|
if (arg1->values[z] == NULL || arg1->nodata[z] == NULL) {
|
|
rtpg_dumpvalues_arg_destroy(arg1);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
MemoryContextSwitchTo(oldcontext);
|
|
elog(ERROR, "RASTER_dumpValues: Could not allocate memory for pixel values");
|
|
SRF_RETURN_DONE(funcctx);
|
|
}
|
|
memset(arg1->values[z], 0, sizeof(Datum) * arg1->rows * arg1->columns);
|
|
memset(arg1->nodata[z], 0, sizeof(bool) * arg1->rows * arg1->columns);
|
|
|
|
i = 0;
|
|
|
|
/* shortcut if band is NODATA */
|
|
if (rt_band_get_isnodata_flag(band)) {
|
|
for (i = (arg1->rows * arg1->columns) - 1; i >= 0; i--)
|
|
arg1->nodata[z][i] = TRUE;
|
|
continue;
|
|
}
|
|
|
|
for (y = 0; y < arg1->rows; y++) {
|
|
for (x = 0; x < arg1->columns; x++) {
|
|
/* get pixel */
|
|
if (rt_band_get_pixel(band, x, y, &val, &isnodata) != ES_NONE) {
|
|
int nband = arg1->nbands[z] + 1;
|
|
rtpg_dumpvalues_arg_destroy(arg1);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
MemoryContextSwitchTo(oldcontext);
|
|
elog(ERROR, "RASTER_dumpValues: Could not pixel (%d, %d) of band %d", x, y, nband);
|
|
SRF_RETURN_DONE(funcctx);
|
|
}
|
|
|
|
arg1->values[z][i] = Float8GetDatum(val);
|
|
POSTGIS_RT_DEBUGF(5, "arg1->values[z][i] = %f", DatumGetFloat8(arg1->values[z][i]));
|
|
POSTGIS_RT_DEBUGF(5, "clamped is?: %d", rt_band_clamped_value_is_nodata(band, val));
|
|
|
|
if (exclude_nodata_value && isnodata) {
|
|
arg1->nodata[z][i] = TRUE;
|
|
POSTGIS_RT_DEBUG(5, "nodata = 1");
|
|
}
|
|
else
|
|
POSTGIS_RT_DEBUG(5, "nodata = 0");
|
|
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* cleanup */
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
|
|
/* Store needed information */
|
|
funcctx->user_fctx = arg1;
|
|
|
|
/* total number of tuples to be returned */
|
|
funcctx->max_calls = arg1->numbands;
|
|
|
|
/* Build a tuple descriptor for our result type */
|
|
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) {
|
|
MemoryContextSwitchTo(oldcontext);
|
|
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;
|
|
arg2 = funcctx->user_fctx;
|
|
|
|
/* do when there is more left to send */
|
|
if (call_cntr < max_calls) {
|
|
Datum values[VALUES_LENGTH];
|
|
bool nulls[VALUES_LENGTH];
|
|
HeapTuple tuple;
|
|
Datum result;
|
|
ArrayType *mdValues = NULL;
|
|
int ndim = 2;
|
|
int dim[2] = {arg2->rows, arg2->columns};
|
|
int lbound[2] = {1, 1};
|
|
|
|
POSTGIS_RT_DEBUGF(3, "call number %d", call_cntr);
|
|
POSTGIS_RT_DEBUGF(4, "dim = %d, %d", dim[0], dim[1]);
|
|
|
|
memset(nulls, FALSE, sizeof(bool) * VALUES_LENGTH);
|
|
|
|
values[0] = Int32GetDatum(arg2->nbands[call_cntr] + 1);
|
|
|
|
/* info about the type of item in the multi-dimensional array (float8). */
|
|
get_typlenbyvalalign(FLOAT8OID, &typlen, &typbyval, &typalign);
|
|
|
|
/* if values is NULL, return empty array */
|
|
if (arg2->values[call_cntr] == NULL)
|
|
ndim = 0;
|
|
|
|
/* assemble 3-dimension array of values */
|
|
mdValues = construct_md_array(
|
|
arg2->values[call_cntr], arg2->nodata[call_cntr],
|
|
ndim, dim, lbound,
|
|
FLOAT8OID,
|
|
typlen, typbyval, typalign
|
|
);
|
|
values[1] = PointerGetDatum(mdValues);
|
|
|
|
/* build a tuple and datum */
|
|
tuple = heap_form_tuple(tupdesc, values, nulls);
|
|
result = HeapTupleGetDatum(tuple);
|
|
|
|
SRF_RETURN_NEXT(funcctx, result);
|
|
}
|
|
/* do when there is no more left */
|
|
else {
|
|
rtpg_dumpvalues_arg_destroy(arg2);
|
|
SRF_RETURN_DONE(funcctx);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Write value of raster sample on given position and in specified band.
|
|
*/
|
|
PG_FUNCTION_INFO_V1(RASTER_setPixelValue);
|
|
Datum RASTER_setPixelValue(PG_FUNCTION_ARGS)
|
|
{
|
|
rt_pgraster *pgraster = NULL;
|
|
rt_pgraster *pgrtn = NULL;
|
|
rt_raster raster = NULL;
|
|
rt_band band = NULL;
|
|
double pixvalue = 0;
|
|
int32_t bandindex = 0;
|
|
int32_t x = 0;
|
|
int32_t y = 0;
|
|
bool skipset = FALSE;
|
|
|
|
if (PG_ARGISNULL(0))
|
|
PG_RETURN_NULL();
|
|
|
|
/* Check index is not NULL or < 1 */
|
|
if (PG_ARGISNULL(1))
|
|
bandindex = -1;
|
|
else
|
|
bandindex = PG_GETARG_INT32(1);
|
|
|
|
if (bandindex < 1) {
|
|
elog(NOTICE, "Invalid band index (must use 1-based). Value not set. Returning original raster");
|
|
skipset = TRUE;
|
|
}
|
|
|
|
/* Validate pixel coordinates are not null */
|
|
if (PG_ARGISNULL(2)) {
|
|
elog(NOTICE, "X coordinate can not be NULL when setting pixel value. Value not set. Returning original raster");
|
|
skipset = TRUE;
|
|
}
|
|
else
|
|
x = PG_GETARG_INT32(2);
|
|
|
|
if (PG_ARGISNULL(3)) {
|
|
elog(NOTICE, "Y coordinate can not be NULL when setting pixel value. Value not set. Returning original raster");
|
|
skipset = TRUE;
|
|
}
|
|
else
|
|
y = PG_GETARG_INT32(3);
|
|
|
|
POSTGIS_RT_DEBUGF(3, "Pixel coordinates (%d, %d)", x, y);
|
|
|
|
/* Deserialize raster */
|
|
pgraster = (rt_pgraster *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0));
|
|
|
|
raster = rt_raster_deserialize(pgraster, FALSE);
|
|
if (!raster) {
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_setPixelValue: Could not deserialize raster");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
if (!skipset) {
|
|
/* Fetch requested band */
|
|
band = rt_raster_get_band(raster, bandindex - 1);
|
|
if (!band) {
|
|
elog(NOTICE, "Could not find raster band of index %d when setting "
|
|
"pixel value. Value not set. Returning original raster",
|
|
bandindex);
|
|
PG_RETURN_POINTER(pgraster);
|
|
}
|
|
else {
|
|
/* Set the pixel value */
|
|
if (PG_ARGISNULL(4)) {
|
|
if (!rt_band_get_hasnodata_flag(band)) {
|
|
elog(NOTICE, "Raster do not have a nodata value defined. "
|
|
"Set band nodata value first. Nodata value not set. "
|
|
"Returning original raster");
|
|
PG_RETURN_POINTER(pgraster);
|
|
}
|
|
else {
|
|
rt_band_get_nodata(band, &pixvalue);
|
|
rt_band_set_pixel(band, x - 1, y - 1, pixvalue, NULL);
|
|
}
|
|
}
|
|
else {
|
|
pixvalue = PG_GETARG_FLOAT8(4);
|
|
rt_band_set_pixel(band, x - 1, y - 1, pixvalue, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
pgrtn = rt_raster_serialize(raster);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
if (!pgrtn)
|
|
PG_RETURN_NULL();
|
|
|
|
SET_VARSIZE(pgrtn, pgrtn->size);
|
|
PG_RETURN_POINTER(pgrtn);
|
|
}
|
|
|
|
/**
|
|
* Set pixels to value from array
|
|
*/
|
|
PG_FUNCTION_INFO_V1(RASTER_setPixelValuesArray);
|
|
Datum RASTER_setPixelValuesArray(PG_FUNCTION_ARGS)
|
|
{
|
|
rt_pgraster *pgraster = NULL;
|
|
rt_pgraster *pgrtn = NULL;
|
|
rt_raster raster = NULL;
|
|
rt_band band = NULL;
|
|
int numbands = 0;
|
|
|
|
int nband = 0;
|
|
int width = 0;
|
|
int height = 0;
|
|
|
|
ArrayType *array;
|
|
Oid etype;
|
|
Datum *elements;
|
|
bool *nulls;
|
|
int16 typlen;
|
|
bool typbyval;
|
|
char typalign;
|
|
int ndims = 1;
|
|
int *dims;
|
|
int num = 0;
|
|
|
|
int ul[2] = {0};
|
|
struct pixelvalue {
|
|
int x;
|
|
int y;
|
|
|
|
bool noset;
|
|
bool nodata;
|
|
double value;
|
|
};
|
|
struct pixelvalue *pixval = NULL;
|
|
int numpixval = 0;
|
|
int dimpixval[2] = {1, 1};
|
|
int dimnoset[2] = {1, 1};
|
|
int hasnodata = FALSE;
|
|
double nodataval = 0;
|
|
bool keepnodata = FALSE;
|
|
bool hasnosetval = FALSE;
|
|
bool nosetvalisnull = FALSE;
|
|
double nosetval = 0;
|
|
|
|
int rtn = 0;
|
|
double val = 0;
|
|
int isnodata = 0;
|
|
|
|
int i = 0;
|
|
int j = 0;
|
|
int x = 0;
|
|
int y = 0;
|
|
|
|
/* pgraster is null, return null */
|
|
if (PG_ARGISNULL(0))
|
|
PG_RETURN_NULL();
|
|
pgraster = (rt_pgraster *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0));
|
|
|
|
/* raster */
|
|
raster = rt_raster_deserialize(pgraster, FALSE);
|
|
if (!raster) {
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_setPixelValuesArray: Could not deserialize raster");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* raster attributes */
|
|
numbands = rt_raster_get_num_bands(raster);
|
|
width = rt_raster_get_width(raster);
|
|
height = rt_raster_get_height(raster);
|
|
|
|
/* nband */
|
|
if (PG_ARGISNULL(1)) {
|
|
elog(NOTICE, "Band index cannot be NULL. Value must be 1-based. Returning original raster");
|
|
rt_raster_destroy(raster);
|
|
PG_RETURN_POINTER(pgraster);
|
|
}
|
|
|
|
nband = PG_GETARG_INT32(1);
|
|
if (nband < 1 || nband > numbands) {
|
|
elog(NOTICE, "Band index is invalid. Value must be 1-based. Returning original raster");
|
|
rt_raster_destroy(raster);
|
|
PG_RETURN_POINTER(pgraster);
|
|
}
|
|
|
|
/* x, y */
|
|
for (i = 2, j = 0; i < 4; i++, j++) {
|
|
if (PG_ARGISNULL(i)) {
|
|
elog(NOTICE, "%s cannot be NULL. Value must be 1-based. Returning original raster", j < 1 ? "X" : "Y");
|
|
rt_raster_destroy(raster);
|
|
PG_RETURN_POINTER(pgraster);
|
|
}
|
|
|
|
ul[j] = PG_GETARG_INT32(i);
|
|
if (
|
|
(ul[j] < 1) || (
|
|
(j < 1 && ul[j] > width) ||
|
|
(j > 0 && ul[j] > height)
|
|
)
|
|
) {
|
|
elog(NOTICE, "%s is invalid. Value must be 1-based. Returning original raster", j < 1 ? "X" : "Y");
|
|
rt_raster_destroy(raster);
|
|
PG_RETURN_POINTER(pgraster);
|
|
}
|
|
|
|
/* force 0-based from 1-based */
|
|
ul[j] -= 1;
|
|
}
|
|
|
|
/* new value set */
|
|
if (PG_ARGISNULL(4)) {
|
|
elog(NOTICE, "No values to set. Returning original raster");
|
|
rt_raster_destroy(raster);
|
|
PG_RETURN_POINTER(pgraster);
|
|
}
|
|
|
|
array = PG_GETARG_ARRAYTYPE_P(4);
|
|
etype = ARR_ELEMTYPE(array);
|
|
get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
|
|
|
|
switch (etype) {
|
|
case FLOAT4OID:
|
|
case FLOAT8OID:
|
|
break;
|
|
default:
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_setPixelValuesArray: Invalid data type for new values");
|
|
PG_RETURN_NULL();
|
|
break;
|
|
}
|
|
|
|
ndims = ARR_NDIM(array);
|
|
dims = ARR_DIMS(array);
|
|
POSTGIS_RT_DEBUGF(4, "ndims = %d", ndims);
|
|
|
|
if (ndims < 1 || ndims > 2) {
|
|
elog(NOTICE, "New values array must be of 1 or 2 dimensions. Returning original raster");
|
|
rt_raster_destroy(raster);
|
|
PG_RETURN_POINTER(pgraster);
|
|
}
|
|
/* outer element, then inner element */
|
|
/* i = 0, y */
|
|
/* i = 1, x */
|
|
if (ndims != 2)
|
|
dimpixval[1] = dims[0];
|
|
else {
|
|
dimpixval[0] = dims[0];
|
|
dimpixval[1] = dims[1];
|
|
}
|
|
POSTGIS_RT_DEBUGF(4, "dimpixval = (%d, %d)", dimpixval[0], dimpixval[1]);
|
|
|
|
deconstruct_array(
|
|
array,
|
|
etype,
|
|
typlen, typbyval, typalign,
|
|
&elements, &nulls, &num
|
|
);
|
|
|
|
/* # of elements doesn't match dims */
|
|
if (num < 1 || num != (dimpixval[0] * dimpixval[1])) {
|
|
if (num) {
|
|
pfree(elements);
|
|
pfree(nulls);
|
|
}
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_setPixelValuesArray: Could not deconstruct new values array");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* allocate memory for pixval */
|
|
numpixval = num;
|
|
pixval = palloc(sizeof(struct pixelvalue) * numpixval);
|
|
if (pixval == NULL) {
|
|
pfree(elements);
|
|
pfree(nulls);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_setPixelValuesArray: Could not allocate memory for new pixel values");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* load new values into pixval */
|
|
i = 0;
|
|
for (y = 0; y < dimpixval[0]; y++) {
|
|
for (x = 0; x < dimpixval[1]; x++) {
|
|
/* 0-based */
|
|
pixval[i].x = ul[0] + x;
|
|
pixval[i].y = ul[1] + y;
|
|
|
|
pixval[i].noset = FALSE;
|
|
pixval[i].nodata = FALSE;
|
|
pixval[i].value = 0;
|
|
|
|
if (nulls[i])
|
|
pixval[i].nodata = TRUE;
|
|
else {
|
|
switch (etype) {
|
|
case FLOAT4OID:
|
|
pixval[i].value = DatumGetFloat4(elements[i]);
|
|
break;
|
|
case FLOAT8OID:
|
|
pixval[i].value = DatumGetFloat8(elements[i]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
|
|
pfree(elements);
|
|
pfree(nulls);
|
|
|
|
/* now load noset flags */
|
|
if (!PG_ARGISNULL(5)) {
|
|
array = PG_GETARG_ARRAYTYPE_P(5);
|
|
etype = ARR_ELEMTYPE(array);
|
|
get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
|
|
|
|
switch (etype) {
|
|
case BOOLOID:
|
|
break;
|
|
default:
|
|
pfree(pixval);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_setPixelValuesArray: Invalid data type for noset flags");
|
|
PG_RETURN_NULL();
|
|
break;
|
|
}
|
|
|
|
ndims = ARR_NDIM(array);
|
|
dims = ARR_DIMS(array);
|
|
POSTGIS_RT_DEBUGF(4, "ndims = %d", ndims);
|
|
|
|
if (ndims < 1 || ndims > 2) {
|
|
elog(NOTICE, "Noset flags array must be of 1 or 2 dimensions. Returning original raster");
|
|
pfree(pixval);
|
|
rt_raster_destroy(raster);
|
|
PG_RETURN_POINTER(pgraster);
|
|
}
|
|
/* outer element, then inner element */
|
|
/* i = 0, y */
|
|
/* i = 1, x */
|
|
if (ndims != 2)
|
|
dimnoset[1] = dims[0];
|
|
else {
|
|
dimnoset[0] = dims[0];
|
|
dimnoset[1] = dims[1];
|
|
}
|
|
POSTGIS_RT_DEBUGF(4, "dimnoset = (%d, %d)", dimnoset[0], dimnoset[1]);
|
|
|
|
deconstruct_array(
|
|
array,
|
|
etype,
|
|
typlen, typbyval, typalign,
|
|
&elements, &nulls, &num
|
|
);
|
|
|
|
/* # of elements doesn't match dims */
|
|
if (num < 1 || num != (dimnoset[0] * dimnoset[1])) {
|
|
pfree(pixval);
|
|
if (num) {
|
|
pfree(elements);
|
|
pfree(nulls);
|
|
}
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_setPixelValuesArray: Could not deconstruct noset flags array");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
i = 0;
|
|
j = 0;
|
|
for (y = 0; y < dimnoset[0]; y++) {
|
|
if (y >= dimpixval[0]) break;
|
|
|
|
for (x = 0; x < dimnoset[1]; x++) {
|
|
/* fast forward noset elements */
|
|
if (x >= dimpixval[1]) {
|
|
i += (dimnoset[1] - dimpixval[1]);
|
|
break;
|
|
}
|
|
|
|
if (!nulls[i] && DatumGetBool(elements[i]))
|
|
pixval[j].noset = TRUE;
|
|
|
|
i++;
|
|
j++;
|
|
}
|
|
|
|
/* fast forward pixval */
|
|
if (x < dimpixval[1])
|
|
j += (dimpixval[1] - dimnoset[1]);
|
|
}
|
|
|
|
pfree(elements);
|
|
pfree(nulls);
|
|
}
|
|
/* hasnosetvalue and nosetvalue */
|
|
else if (!PG_ARGISNULL(6) && PG_GETARG_BOOL(6)) {
|
|
hasnosetval = TRUE;
|
|
if (PG_ARGISNULL(7))
|
|
nosetvalisnull = TRUE;
|
|
else
|
|
nosetval = PG_GETARG_FLOAT8(7);
|
|
}
|
|
|
|
#if POSTGIS_DEBUG_LEVEL > 0
|
|
for (i = 0; i < numpixval; i++) {
|
|
POSTGIS_RT_DEBUGF(4, "pixval[%d](x, y, noset, nodata, value) = (%d, %d, %d, %d, %f)",
|
|
i,
|
|
pixval[i].x,
|
|
pixval[i].y,
|
|
pixval[i].noset,
|
|
pixval[i].nodata,
|
|
pixval[i].value
|
|
);
|
|
}
|
|
#endif
|
|
|
|
/* keepnodata flag */
|
|
if (!PG_ARGISNULL(8))
|
|
keepnodata = PG_GETARG_BOOL(8);
|
|
|
|
/* get band */
|
|
band = rt_raster_get_band(raster, nband - 1);
|
|
if (!band) {
|
|
elog(NOTICE, "Could not find band at index %d. Returning original raster", nband);
|
|
pfree(pixval);
|
|
rt_raster_destroy(raster);
|
|
PG_RETURN_POINTER(pgraster);
|
|
}
|
|
|
|
/* get band nodata info */
|
|
/* has NODATA, use NODATA */
|
|
hasnodata = rt_band_get_hasnodata_flag(band);
|
|
if (hasnodata)
|
|
rt_band_get_nodata(band, &nodataval);
|
|
/* no NODATA, use min possible value */
|
|
else
|
|
nodataval = rt_band_get_min_value(band);
|
|
|
|
/* set pixels */
|
|
for (i = 0; i < numpixval; i++) {
|
|
/* noset = true, skip */
|
|
if (pixval[i].noset)
|
|
continue;
|
|
/* check against nosetval */
|
|
else if (hasnosetval) {
|
|
/* pixel = NULL AND nosetval = NULL */
|
|
if (pixval[i].nodata && nosetvalisnull)
|
|
continue;
|
|
/* pixel value = nosetval */
|
|
else if (!pixval[i].nodata && !nosetvalisnull && FLT_EQ(pixval[i].value, nosetval))
|
|
continue;
|
|
}
|
|
|
|
/* if pixel is outside bounds, skip */
|
|
if (
|
|
(pixval[i].x < 0 || pixval[i].x >= width) ||
|
|
(pixval[i].y < 0 || pixval[i].y >= height)
|
|
) {
|
|
elog(NOTICE, "Cannot set value for pixel (%d, %d) outside raster bounds: %d x %d",
|
|
pixval[i].x + 1, pixval[i].y + 1,
|
|
width, height
|
|
);
|
|
continue;
|
|
}
|
|
|
|
/* if hasnodata = TRUE and keepnodata = TRUE, inspect pixel value */
|
|
if (hasnodata && keepnodata) {
|
|
rtn = rt_band_get_pixel(band, pixval[i].x, pixval[i].y, &val, &isnodata);
|
|
if (rtn != ES_NONE) {
|
|
pfree(pixval);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "Cannot get value of pixel");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* pixel value = NODATA, skip */
|
|
if (isnodata) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (pixval[i].nodata)
|
|
rt_band_set_pixel(band, pixval[i].x, pixval[i].y, nodataval, NULL);
|
|
else
|
|
rt_band_set_pixel(band, pixval[i].x, pixval[i].y, pixval[i].value, NULL);
|
|
}
|
|
|
|
pfree(pixval);
|
|
|
|
/* serialize new raster */
|
|
pgrtn = rt_raster_serialize(raster);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
if (!pgrtn)
|
|
PG_RETURN_NULL();
|
|
|
|
SET_VARSIZE(pgrtn, pgrtn->size);
|
|
PG_RETURN_POINTER(pgrtn);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------- */
|
|
/* ST_SetValues using geomval array */
|
|
/* ---------------------------------------------------------------- */
|
|
|
|
typedef struct rtpg_setvaluesgv_arg_t *rtpg_setvaluesgv_arg;
|
|
typedef struct rtpg_setvaluesgv_geomval_t *rtpg_setvaluesgv_geomval;
|
|
|
|
struct rtpg_setvaluesgv_arg_t {
|
|
int ngv;
|
|
rtpg_setvaluesgv_geomval gv;
|
|
|
|
bool keepnodata;
|
|
};
|
|
|
|
struct rtpg_setvaluesgv_geomval_t {
|
|
struct {
|
|
int nodata;
|
|
double value;
|
|
} pixval;
|
|
|
|
LWGEOM *geom;
|
|
rt_raster mask;
|
|
};
|
|
|
|
static rtpg_setvaluesgv_arg rtpg_setvaluesgv_arg_init() {
|
|
rtpg_setvaluesgv_arg arg = palloc(sizeof(struct rtpg_setvaluesgv_arg_t));
|
|
if (arg == NULL) {
|
|
elog(ERROR, "rtpg_setvaluesgv_arg_init: Could not allocate memory for function arguments");
|
|
return NULL;
|
|
}
|
|
|
|
arg->ngv = 0;
|
|
arg->gv = NULL;
|
|
arg->keepnodata = 0;
|
|
|
|
return arg;
|
|
}
|
|
|
|
static void rtpg_setvaluesgv_arg_destroy(rtpg_setvaluesgv_arg arg) {
|
|
int i = 0;
|
|
|
|
if (arg->gv != NULL) {
|
|
for (i = 0; i < arg->ngv; i++) {
|
|
if (arg->gv[i].geom != NULL)
|
|
lwgeom_free(arg->gv[i].geom);
|
|
if (arg->gv[i].mask != NULL)
|
|
rt_raster_destroy(arg->gv[i].mask);
|
|
}
|
|
|
|
pfree(arg->gv);
|
|
}
|
|
|
|
pfree(arg);
|
|
}
|
|
|
|
static int rtpg_setvalues_geomval_callback(
|
|
rt_iterator_arg arg, void *userarg,
|
|
double *value, int *nodata
|
|
) {
|
|
rtpg_setvaluesgv_arg funcarg = (rtpg_setvaluesgv_arg) userarg;
|
|
int i = 0;
|
|
int j = 0;
|
|
|
|
*value = 0;
|
|
*nodata = 0;
|
|
|
|
POSTGIS_RT_DEBUGF(4, "keepnodata = %d", funcarg->keepnodata);
|
|
|
|
/* keepnodata = TRUE */
|
|
if (funcarg->keepnodata && arg->nodata[0][0][0]) {
|
|
POSTGIS_RT_DEBUG(3, "keepnodata = 1 AND source raster pixel is NODATA");
|
|
*nodata = 1;
|
|
return 1;
|
|
}
|
|
|
|
for (i = arg->rasters - 1, j = funcarg->ngv - 1; i > 0; i--, j--) {
|
|
POSTGIS_RT_DEBUGF(4, "checking raster %d", i);
|
|
|
|
/* mask is NODATA */
|
|
if (arg->nodata[i][0][0])
|
|
continue;
|
|
/* mask is NOT NODATA */
|
|
else {
|
|
POSTGIS_RT_DEBUGF(4, "Using information from geometry %d", j);
|
|
|
|
if (funcarg->gv[j].pixval.nodata)
|
|
*nodata = 1;
|
|
else
|
|
*value = funcarg->gv[j].pixval.value;
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
POSTGIS_RT_DEBUG(4, "Using information from source raster");
|
|
|
|
/* still here */
|
|
if (arg->nodata[0][0][0])
|
|
*nodata = 1;
|
|
else
|
|
*value = arg->values[0][0][0];
|
|
|
|
return 1;
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(RASTER_setPixelValuesGeomval);
|
|
Datum RASTER_setPixelValuesGeomval(PG_FUNCTION_ARGS)
|
|
{
|
|
rt_pgraster *pgraster = NULL;
|
|
rt_pgraster *pgrtn = NULL;
|
|
rt_raster raster = NULL;
|
|
rt_band band = NULL;
|
|
rt_raster _raster = NULL;
|
|
rt_band _band = NULL;
|
|
int nband = 0; /* 1-based */
|
|
|
|
int numbands = 0;
|
|
int width = 0;
|
|
int height = 0;
|
|
int32_t srid = 0;
|
|
double gt[6] = {0};
|
|
|
|
rt_pixtype pixtype = PT_END;
|
|
int hasnodata = 0;
|
|
double nodataval = 0;
|
|
|
|
rtpg_setvaluesgv_arg arg = NULL;
|
|
int allpoint = 0;
|
|
|
|
ArrayType *array;
|
|
Oid etype;
|
|
Datum *e;
|
|
bool *nulls;
|
|
int16 typlen;
|
|
bool typbyval;
|
|
char typalign;
|
|
int n = 0;
|
|
|
|
HeapTupleHeader tup;
|
|
bool isnull;
|
|
Datum tupv;
|
|
|
|
GSERIALIZED *gser = NULL;
|
|
uint8_t gtype;
|
|
lwvarlena_t *wkb = NULL;
|
|
|
|
int i = 0;
|
|
uint32_t j = 0;
|
|
int noerr = 1;
|
|
|
|
/* pgraster is null, return null */
|
|
if (PG_ARGISNULL(0))
|
|
PG_RETURN_NULL();
|
|
pgraster = (rt_pgraster *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0));
|
|
|
|
/* raster */
|
|
raster = rt_raster_deserialize(pgraster, FALSE);
|
|
if (!raster) {
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_setPixelValuesGeomval: Could not deserialize raster");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* raster attributes */
|
|
numbands = rt_raster_get_num_bands(raster);
|
|
width = rt_raster_get_width(raster);
|
|
height = rt_raster_get_height(raster);
|
|
srid = clamp_srid(rt_raster_get_srid(raster));
|
|
rt_raster_get_geotransform_matrix(raster, gt);
|
|
|
|
/* nband */
|
|
if (PG_ARGISNULL(1)) {
|
|
elog(NOTICE, "Band index cannot be NULL. Value must be 1-based. Returning original raster");
|
|
rt_raster_destroy(raster);
|
|
PG_RETURN_POINTER(pgraster);
|
|
}
|
|
|
|
nband = PG_GETARG_INT32(1);
|
|
if (nband < 1 || nband > numbands) {
|
|
elog(NOTICE, "Band index is invalid. Value must be 1-based. Returning original raster");
|
|
rt_raster_destroy(raster);
|
|
PG_RETURN_POINTER(pgraster);
|
|
}
|
|
|
|
/* get band attributes */
|
|
band = rt_raster_get_band(raster, nband - 1);
|
|
pixtype = rt_band_get_pixtype(band);
|
|
hasnodata = rt_band_get_hasnodata_flag(band);
|
|
if (hasnodata)
|
|
rt_band_get_nodata(band, &nodataval);
|
|
|
|
/* array of geomval (2) */
|
|
if (PG_ARGISNULL(2)) {
|
|
elog(NOTICE, "No values to set. Returning original raster");
|
|
rt_raster_destroy(raster);
|
|
PG_RETURN_POINTER(pgraster);
|
|
}
|
|
|
|
array = PG_GETARG_ARRAYTYPE_P(2);
|
|
etype = ARR_ELEMTYPE(array);
|
|
get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
|
|
|
|
deconstruct_array(
|
|
array,
|
|
etype,
|
|
typlen, typbyval, typalign,
|
|
&e, &nulls, &n
|
|
);
|
|
|
|
if (!n) {
|
|
elog(NOTICE, "No values to set. Returning original raster");
|
|
rt_raster_destroy(raster);
|
|
PG_RETURN_POINTER(pgraster);
|
|
}
|
|
|
|
/* init arg */
|
|
arg = rtpg_setvaluesgv_arg_init();
|
|
if (arg == NULL) {
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_setPixelValuesGeomval: Could not intialize argument structure");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
arg->gv = palloc(sizeof(struct rtpg_setvaluesgv_geomval_t) * n);
|
|
if (arg->gv == NULL) {
|
|
rtpg_setvaluesgv_arg_destroy(arg);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_setPixelValuesGeomval: Could not allocate memory for geomval array");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* process each element */
|
|
arg->ngv = 0;
|
|
for (i = 0; i < n; i++) {
|
|
if (nulls[i])
|
|
continue;
|
|
|
|
arg->gv[arg->ngv].pixval.nodata = 0;
|
|
arg->gv[arg->ngv].pixval.value = 0;
|
|
arg->gv[arg->ngv].geom = NULL;
|
|
arg->gv[arg->ngv].mask = NULL;
|
|
|
|
/* each element is a tuple */
|
|
tup = (HeapTupleHeader) DatumGetPointer(e[i]);
|
|
if (NULL == tup) {
|
|
rtpg_setvaluesgv_arg_destroy(arg);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_setPixelValuesGeomval: Invalid argument for geomval at index %d", i);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* first element, geometry */
|
|
POSTGIS_RT_DEBUG(4, "Processing first element (geometry)");
|
|
tupv = GetAttributeByName(tup, "geom", &isnull);
|
|
if (isnull) {
|
|
elog(NOTICE, "First argument (geom) of geomval at index %d is NULL. Skipping", i);
|
|
continue;
|
|
}
|
|
|
|
gser = (GSERIALIZED *) PG_DETOAST_DATUM(tupv);
|
|
arg->gv[arg->ngv].geom = lwgeom_from_gserialized(gser);
|
|
if (arg->gv[arg->ngv].geom == NULL) {
|
|
rtpg_setvaluesgv_arg_destroy(arg);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_setPixelValuesGeomval: Could not deserialize geometry of geomval at index %d", i);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* empty geometry */
|
|
if (lwgeom_is_empty(arg->gv[arg->ngv].geom)) {
|
|
elog(NOTICE, "First argument (geom) of geomval at index %d is an empty geometry. Skipping", i);
|
|
continue;
|
|
}
|
|
|
|
/* check SRID */
|
|
if (clamp_srid(gserialized_get_srid(gser)) != srid) {
|
|
elog(NOTICE, "Geometry provided for geomval at index %d does not have the same SRID as the raster: %d. Returning original raster", i, srid);
|
|
rtpg_setvaluesgv_arg_destroy(arg);
|
|
rt_raster_destroy(raster);
|
|
PG_RETURN_POINTER(pgraster);
|
|
}
|
|
|
|
/* Get a 2D version of the geometry if necessary */
|
|
if (lwgeom_ndims(arg->gv[arg->ngv].geom) > 2) {
|
|
LWGEOM *geom2d = lwgeom_force_2d(arg->gv[arg->ngv].geom);
|
|
lwgeom_free(arg->gv[arg->ngv].geom);
|
|
arg->gv[arg->ngv].geom = geom2d;
|
|
}
|
|
|
|
/* filter for types */
|
|
gtype = gserialized_get_type(gser);
|
|
|
|
/* shortcuts for POINT and MULTIPOINT */
|
|
if (gtype == POINTTYPE || gtype == MULTIPOINTTYPE)
|
|
allpoint++;
|
|
|
|
/* get wkb of geometry */
|
|
POSTGIS_RT_DEBUG(3, "getting wkb of geometry");
|
|
wkb = lwgeom_to_wkb_varlena(arg->gv[arg->ngv].geom, WKB_SFSQL);
|
|
|
|
/* rasterize geometry */
|
|
arg->gv[arg->ngv].mask = rt_raster_gdal_rasterize((unsigned char *)wkb->data,
|
|
LWSIZE_GET(wkb->size) - LWVARHDRSZ,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&(gt[1]),
|
|
&(gt[5]),
|
|
NULL,
|
|
NULL,
|
|
&(gt[0]),
|
|
&(gt[3]),
|
|
&(gt[2]),
|
|
&(gt[4]),
|
|
NULL);
|
|
|
|
pfree(wkb);
|
|
if (gtype != POINTTYPE && gtype != MULTIPOINTTYPE) {
|
|
lwgeom_free(arg->gv[arg->ngv].geom);
|
|
arg->gv[arg->ngv].geom = NULL;
|
|
}
|
|
|
|
if (arg->gv[arg->ngv].mask == NULL) {
|
|
rtpg_setvaluesgv_arg_destroy(arg);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_setPixelValuesGeomval: Could not rasterize geometry of geomval at index %d", i);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* set SRID */
|
|
rt_raster_set_srid(arg->gv[arg->ngv].mask, srid);
|
|
|
|
/* second element, value */
|
|
POSTGIS_RT_DEBUG(4, "Processing second element (val)");
|
|
tupv = GetAttributeByName(tup, "val", &isnull);
|
|
if (isnull) {
|
|
elog(NOTICE, "Second argument (val) of geomval at index %d is NULL. Treating as NODATA", i);
|
|
arg->gv[arg->ngv].pixval.nodata = 1;
|
|
}
|
|
else
|
|
arg->gv[arg->ngv].pixval.value = DatumGetFloat8(tupv);
|
|
|
|
(arg->ngv)++;
|
|
}
|
|
|
|
/* redim arg->gv if needed */
|
|
if (arg->ngv < n) {
|
|
arg->gv = repalloc(arg->gv, sizeof(struct rtpg_setvaluesgv_geomval_t) * arg->ngv);
|
|
if (arg->gv == NULL) {
|
|
rtpg_setvaluesgv_arg_destroy(arg);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_setPixelValuesGeomval: Could not reallocate memory for geomval array");
|
|
PG_RETURN_NULL();
|
|
}
|
|
}
|
|
|
|
/* keepnodata */
|
|
if (!PG_ARGISNULL(3))
|
|
arg->keepnodata = PG_GETARG_BOOL(3);
|
|
POSTGIS_RT_DEBUGF(3, "keepnodata = %d", arg->keepnodata);
|
|
|
|
/* keepnodata = TRUE and band is NODATA */
|
|
if (arg->keepnodata && rt_band_get_isnodata_flag(band)) {
|
|
POSTGIS_RT_DEBUG(3, "keepnodata = TRUE and band is NODATA. Not doing anything");
|
|
}
|
|
/* all elements are points */
|
|
else if (allpoint == arg->ngv) {
|
|
double igt[6] = {0};
|
|
double xy[2] = {0};
|
|
double value = 0;
|
|
int isnodata = 0;
|
|
|
|
LWCOLLECTION *coll = NULL;
|
|
LWPOINT *point = NULL;
|
|
POINT2D p;
|
|
|
|
POSTGIS_RT_DEBUG(3, "all geometries are points, using direct to pixel method");
|
|
|
|
/* cache inverse gretransform matrix */
|
|
rt_raster_get_inverse_geotransform_matrix(NULL, gt, igt);
|
|
|
|
/* process each geometry */
|
|
for (i = 0; i < arg->ngv; i++) {
|
|
/* convert geometry to collection */
|
|
coll = lwgeom_as_lwcollection(lwgeom_as_multi(arg->gv[i].geom));
|
|
|
|
/* process each point in collection */
|
|
for (j = 0; j < coll->ngeoms; j++) {
|
|
point = lwgeom_as_lwpoint(coll->geoms[j]);
|
|
getPoint2d_p(point->point, 0, &p);
|
|
|
|
if (rt_raster_geopoint_to_cell(raster, p.x, p.y, &(xy[0]), &(xy[1]), igt) != ES_NONE) {
|
|
rtpg_setvaluesgv_arg_destroy(arg);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_setPixelValuesGeomval: Could not process coordinates of point");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* skip point if outside raster */
|
|
if (
|
|
(xy[0] < 0 || xy[0] >= width) ||
|
|
(xy[1] < 0 || xy[1] >= height)
|
|
) {
|
|
elog(NOTICE, "Point is outside raster extent. Skipping");
|
|
continue;
|
|
}
|
|
|
|
/* get pixel value */
|
|
if (rt_band_get_pixel(band, xy[0], xy[1], &value, &isnodata) != ES_NONE) {
|
|
rtpg_setvaluesgv_arg_destroy(arg);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_setPixelValuesGeomval: Could not get pixel value");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* keepnodata = TRUE AND pixel value is NODATA */
|
|
if (arg->keepnodata && isnodata)
|
|
continue;
|
|
|
|
/* set pixel */
|
|
if (arg->gv[i].pixval.nodata)
|
|
noerr = rt_band_set_pixel(band, xy[0], xy[1], nodataval, NULL);
|
|
else
|
|
noerr = rt_band_set_pixel(band, xy[0], xy[1], arg->gv[i].pixval.value, NULL);
|
|
|
|
if (noerr != ES_NONE) {
|
|
rtpg_setvaluesgv_arg_destroy(arg);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_setPixelValuesGeomval: Could not set pixel value");
|
|
PG_RETURN_NULL();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* run iterator otherwise */
|
|
else {
|
|
rt_iterator itrset;
|
|
|
|
POSTGIS_RT_DEBUG(3, "a mix of geometries, using iterator method");
|
|
|
|
/* init itrset */
|
|
itrset = palloc(sizeof(struct rt_iterator_t) * (arg->ngv + 1));
|
|
if (itrset == NULL) {
|
|
rtpg_setvaluesgv_arg_destroy(arg);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_setPixelValuesGeomval: Could not allocate memory for iterator arguments");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* set first raster's info */
|
|
itrset[0].raster = raster;
|
|
itrset[0].nband = nband - 1;
|
|
itrset[0].nbnodata = 1;
|
|
|
|
/* set other raster's info */
|
|
for (i = 0, j = 1; i < arg->ngv; i++, j++) {
|
|
itrset[j].raster = arg->gv[i].mask;
|
|
itrset[j].nband = 0;
|
|
itrset[j].nbnodata = 1;
|
|
}
|
|
|
|
/* pass to iterator */
|
|
noerr = rt_raster_iterator(
|
|
itrset, arg->ngv + 1,
|
|
ET_FIRST, NULL,
|
|
pixtype,
|
|
hasnodata, nodataval,
|
|
0, 0,
|
|
NULL,
|
|
arg,
|
|
rtpg_setvalues_geomval_callback,
|
|
&_raster
|
|
);
|
|
pfree(itrset);
|
|
|
|
if (noerr != ES_NONE) {
|
|
rtpg_setvaluesgv_arg_destroy(arg);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_setPixelValuesGeomval: Could not run raster iterator function");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* copy band from _raster to raster */
|
|
_band = rt_raster_get_band(_raster, 0);
|
|
if (_band == NULL) {
|
|
rtpg_setvaluesgv_arg_destroy(arg);
|
|
rt_raster_destroy(_raster);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_setPixelValuesGeomval: Could not get band from working raster");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
_band = rt_raster_replace_band(raster, _band, nband - 1);
|
|
if (_band == NULL) {
|
|
rtpg_setvaluesgv_arg_destroy(arg);
|
|
rt_raster_destroy(_raster);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_setPixelValuesGeomval: Could not replace band in output raster");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
rt_band_destroy(_band);
|
|
rt_raster_destroy(_raster);
|
|
}
|
|
|
|
rtpg_setvaluesgv_arg_destroy(arg);
|
|
|
|
pgrtn = rt_raster_serialize(raster);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
|
|
POSTGIS_RT_DEBUG(3, "Finished");
|
|
|
|
if (!pgrtn)
|
|
PG_RETURN_NULL();
|
|
|
|
SET_VARSIZE(pgrtn, pgrtn->size);
|
|
PG_RETURN_POINTER(pgrtn);
|
|
}
|
|
|
|
#undef VALUES_LENGTH
|
|
#define VALUES_LENGTH 3
|
|
|
|
/**
|
|
* Get pixels of value
|
|
*/
|
|
PG_FUNCTION_INFO_V1(RASTER_pixelOfValue);
|
|
Datum RASTER_pixelOfValue(PG_FUNCTION_ARGS)
|
|
{
|
|
FuncCallContext *funcctx;
|
|
TupleDesc tupdesc;
|
|
|
|
rt_pixel pixels = NULL;
|
|
rt_pixel pixels2 = NULL;
|
|
int count = 0;
|
|
int i = 0;
|
|
int n = 0;
|
|
int call_cntr;
|
|
int max_calls;
|
|
|
|
if (SRF_IS_FIRSTCALL()) {
|
|
MemoryContext oldcontext;
|
|
|
|
rt_pgraster *pgraster = NULL;
|
|
rt_raster raster = NULL;
|
|
rt_band band = NULL;
|
|
int nband = 1;
|
|
int num_bands = 0;
|
|
double *search = NULL;
|
|
int nsearch = 0;
|
|
double val;
|
|
bool exclude_nodata_value = TRUE;
|
|
|
|
ArrayType *array;
|
|
Oid etype;
|
|
Datum *e;
|
|
bool *nulls;
|
|
int16 typlen;
|
|
bool typbyval;
|
|
char typalign;
|
|
|
|
/* 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));
|
|
raster = rt_raster_deserialize(pgraster, FALSE);
|
|
if (!raster) {
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
MemoryContextSwitchTo(oldcontext);
|
|
elog(ERROR, "RASTER_pixelOfValue: Could not deserialize raster");
|
|
SRF_RETURN_DONE(funcctx);
|
|
}
|
|
|
|
/* num_bands */
|
|
num_bands = rt_raster_get_num_bands(raster);
|
|
if (num_bands < 1) {
|
|
elog(NOTICE, "Raster provided has no bands");
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
MemoryContextSwitchTo(oldcontext);
|
|
SRF_RETURN_DONE(funcctx);
|
|
}
|
|
|
|
/* band index is 1-based */
|
|
if (!PG_ARGISNULL(1))
|
|
nband = PG_GETARG_INT32(1);
|
|
if (nband < 1 || nband > num_bands) {
|
|
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);
|
|
}
|
|
|
|
/* search values */
|
|
array = PG_GETARG_ARRAYTYPE_P(2);
|
|
etype = ARR_ELEMTYPE(array);
|
|
get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
|
|
|
|
switch (etype) {
|
|
case FLOAT4OID:
|
|
case FLOAT8OID:
|
|
break;
|
|
default:
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
MemoryContextSwitchTo(oldcontext);
|
|
elog(ERROR, "RASTER_pixelOfValue: Invalid data type for pixel values");
|
|
SRF_RETURN_DONE(funcctx);
|
|
break;
|
|
}
|
|
|
|
deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
|
|
&nulls, &n);
|
|
|
|
search = palloc(sizeof(double) * n);
|
|
for (i = 0, nsearch = 0; i < n; i++) {
|
|
if (nulls[i]) continue;
|
|
|
|
switch (etype) {
|
|
case FLOAT4OID:
|
|
val = (double) DatumGetFloat4(e[i]);
|
|
break;
|
|
case FLOAT8OID:
|
|
val = (double) DatumGetFloat8(e[i]);
|
|
break;
|
|
}
|
|
|
|
search[nsearch] = val;
|
|
POSTGIS_RT_DEBUGF(3, "search[%d] = %f", nsearch, search[nsearch]);
|
|
nsearch++;
|
|
}
|
|
|
|
/* not searching for anything */
|
|
if (nsearch < 1) {
|
|
elog(NOTICE, "No search values provided. Returning NULL");
|
|
pfree(search);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
MemoryContextSwitchTo(oldcontext);
|
|
SRF_RETURN_DONE(funcctx);
|
|
}
|
|
else if (nsearch < n)
|
|
search = repalloc(search, sizeof(double) * nsearch);
|
|
|
|
/* exclude_nodata_value flag */
|
|
if (!PG_ARGISNULL(3))
|
|
exclude_nodata_value = PG_GETARG_BOOL(3);
|
|
|
|
/* get band */
|
|
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);
|
|
}
|
|
|
|
/* get pixels of values */
|
|
count = rt_band_get_pixel_of_value(
|
|
band, exclude_nodata_value,
|
|
search, nsearch,
|
|
&pixels
|
|
);
|
|
pfree(search);
|
|
rt_band_destroy(band);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
if (count < 1) {
|
|
/* error */
|
|
if (count < 0)
|
|
elog(NOTICE, "Could not get the pixels of search values for band at index %d", nband);
|
|
/* no nearest pixel */
|
|
else
|
|
elog(NOTICE, "No pixels of search values found for band at index %d", nband);
|
|
|
|
MemoryContextSwitchTo(oldcontext);
|
|
SRF_RETURN_DONE(funcctx);
|
|
}
|
|
|
|
/* Store needed information */
|
|
funcctx->user_fctx = pixels;
|
|
|
|
/* total number of tuples to be returned */
|
|
funcctx->max_calls = count;
|
|
|
|
/* 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;
|
|
pixels2 = funcctx->user_fctx;
|
|
|
|
/* do when there is more left to send */
|
|
if (call_cntr < max_calls) {
|
|
Datum values[VALUES_LENGTH];
|
|
bool nulls[VALUES_LENGTH];
|
|
HeapTuple tuple;
|
|
Datum result;
|
|
|
|
memset(nulls, FALSE, sizeof(bool) * VALUES_LENGTH);
|
|
|
|
/* 0-based to 1-based */
|
|
pixels2[call_cntr].x += 1;
|
|
pixels2[call_cntr].y += 1;
|
|
|
|
values[0] = Float8GetDatum(pixels2[call_cntr].value);
|
|
values[1] = Int32GetDatum(pixels2[call_cntr].x);
|
|
values[2] = Int32GetDatum(pixels2[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);
|
|
}
|
|
else {
|
|
pfree(pixels2);
|
|
SRF_RETURN_DONE(funcctx);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return nearest value to a point
|
|
*/
|
|
PG_FUNCTION_INFO_V1(RASTER_nearestValue);
|
|
Datum RASTER_nearestValue(PG_FUNCTION_ARGS)
|
|
{
|
|
rt_pgraster *pgraster = NULL;
|
|
rt_raster raster = NULL;
|
|
rt_band band = NULL;
|
|
int bandindex = 1;
|
|
int num_bands = 0;
|
|
GSERIALIZED *geom;
|
|
bool exclude_nodata_value = TRUE;
|
|
LWGEOM *lwgeom;
|
|
LWPOINT *point = NULL;
|
|
POINT2D p;
|
|
|
|
double x;
|
|
double y;
|
|
int count;
|
|
rt_pixel npixels = NULL;
|
|
double value = 0;
|
|
int hasvalue = 0;
|
|
int isnodata = 0;
|
|
|
|
if (PG_ARGISNULL(0))
|
|
PG_RETURN_NULL();
|
|
pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
|
raster = rt_raster_deserialize(pgraster, FALSE);
|
|
if (!raster) {
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_nearestValue: Could not deserialize raster");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* band index is 1-based */
|
|
if (!PG_ARGISNULL(1))
|
|
bandindex = PG_GETARG_INT32(1);
|
|
num_bands = rt_raster_get_num_bands(raster);
|
|
if (bandindex < 1 || bandindex > num_bands) {
|
|
elog(NOTICE, "Invalid band index (must use 1-based). Returning NULL");
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* point */
|
|
geom = PG_GETARG_GSERIALIZED_P(2);
|
|
if (gserialized_get_type(geom) != POINTTYPE) {
|
|
elog(NOTICE, "Geometry provided must be a point");
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
PG_FREE_IF_COPY(geom, 2);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* exclude_nodata_value flag */
|
|
if (!PG_ARGISNULL(3))
|
|
exclude_nodata_value = PG_GETARG_BOOL(3);
|
|
|
|
/* SRIDs of raster and geometry must match */
|
|
if (clamp_srid(gserialized_get_srid(geom)) != clamp_srid(rt_raster_get_srid(raster))) {
|
|
elog(NOTICE, "SRIDs of geometry and raster do not match");
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
PG_FREE_IF_COPY(geom, 2);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* get band */
|
|
band = rt_raster_get_band(raster, bandindex - 1);
|
|
if (!band) {
|
|
elog(NOTICE, "Could not find band at index %d. Returning NULL", bandindex);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
PG_FREE_IF_COPY(geom, 2);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* process geometry */
|
|
lwgeom = lwgeom_from_gserialized(geom);
|
|
|
|
if (lwgeom_is_empty(lwgeom)) {
|
|
elog(NOTICE, "Geometry provided cannot be empty");
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
PG_FREE_IF_COPY(geom, 2);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* Get a 2D version of the geometry if necessary */
|
|
if (lwgeom_ndims(lwgeom) > 2) {
|
|
LWGEOM *lwgeom2d = lwgeom_force_2d(lwgeom);
|
|
lwgeom_free(lwgeom);
|
|
lwgeom = lwgeom2d;
|
|
}
|
|
|
|
point = lwgeom_as_lwpoint(lwgeom);
|
|
getPoint2d_p(point->point, 0, &p);
|
|
|
|
if (rt_raster_geopoint_to_cell(
|
|
raster,
|
|
p.x, p.y,
|
|
&x, &y,
|
|
NULL
|
|
) != ES_NONE) {
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
lwgeom_free(lwgeom);
|
|
PG_FREE_IF_COPY(geom, 2);
|
|
elog(ERROR, "RASTER_nearestValue: Could not compute pixel coordinates from spatial coordinates");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* get value at point */
|
|
if (
|
|
(x >= 0 && x < rt_raster_get_width(raster)) &&
|
|
(y >= 0 && y < rt_raster_get_height(raster))
|
|
) {
|
|
if (rt_band_get_pixel(band, x, y, &value, &isnodata) != ES_NONE) {
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
lwgeom_free(lwgeom);
|
|
PG_FREE_IF_COPY(geom, 2);
|
|
elog(ERROR, "RASTER_nearestValue: Could not get pixel value for band at index %d", bandindex);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* value at point, return value */
|
|
if (!exclude_nodata_value || !isnodata) {
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
lwgeom_free(lwgeom);
|
|
PG_FREE_IF_COPY(geom, 2);
|
|
|
|
PG_RETURN_FLOAT8(value);
|
|
}
|
|
}
|
|
|
|
/* get neighborhood */
|
|
count = rt_band_get_nearest_pixel(
|
|
band,
|
|
x, y,
|
|
0, 0,
|
|
exclude_nodata_value,
|
|
&npixels
|
|
);
|
|
rt_band_destroy(band);
|
|
/* error or no neighbors */
|
|
if (count < 1) {
|
|
/* error */
|
|
if (count < 0)
|
|
elog(NOTICE, "Could not get the nearest value for band at index %d", bandindex);
|
|
/* no nearest pixel */
|
|
else
|
|
elog(NOTICE, "No nearest value found for band at index %d", bandindex);
|
|
|
|
lwgeom_free(lwgeom);
|
|
PG_FREE_IF_COPY(geom, 2);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* more than one nearest value, see which one is closest */
|
|
if (count > 1) {
|
|
int i = 0;
|
|
LWPOLY *poly = NULL;
|
|
double lastdist = -1;
|
|
double dist;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
/* convex-hull of pixel */
|
|
poly = rt_raster_pixel_as_polygon(raster, npixels[i].x, npixels[i].y);
|
|
if (!poly) {
|
|
lwgeom_free(lwgeom);
|
|
PG_FREE_IF_COPY(geom, 2);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_nearestValue: Could not get polygon of neighboring pixel");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* distance between convex-hull and point */
|
|
dist = lwgeom_mindistance2d(lwpoly_as_lwgeom(poly), lwgeom);
|
|
if (lastdist < 0 || dist < lastdist) {
|
|
value = npixels[i].value;
|
|
hasvalue = 1;
|
|
}
|
|
lastdist = dist;
|
|
|
|
lwpoly_free(poly);
|
|
}
|
|
}
|
|
else {
|
|
value = npixels[0].value;
|
|
hasvalue = 1;
|
|
}
|
|
|
|
pfree(npixels);
|
|
lwgeom_free(lwgeom);
|
|
PG_FREE_IF_COPY(geom, 2);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
|
|
if (hasvalue)
|
|
PG_RETURN_FLOAT8(value);
|
|
else
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/**
|
|
* Return the neighborhood around a pixel
|
|
*/
|
|
PG_FUNCTION_INFO_V1(RASTER_neighborhood);
|
|
Datum RASTER_neighborhood(PG_FUNCTION_ARGS)
|
|
{
|
|
rt_pgraster *pgraster = NULL;
|
|
rt_raster raster = NULL;
|
|
rt_band band = NULL;
|
|
int bandindex = 1;
|
|
int num_bands = 0;
|
|
int x = 0;
|
|
int y = 0;
|
|
int _x = 0;
|
|
int _y = 0;
|
|
int distance[2] = {0};
|
|
bool exclude_nodata_value = TRUE;
|
|
double pixval;
|
|
int isnodata = 0;
|
|
|
|
rt_pixel npixels = NULL;
|
|
int count;
|
|
double **value2D = NULL;
|
|
int **nodata2D = NULL;
|
|
|
|
int i = 0;
|
|
int j = 0;
|
|
int k = 0;
|
|
Datum *value1D = NULL;
|
|
bool *nodata1D = NULL;
|
|
int dim[2] = {0};
|
|
int lbound[2] = {1, 1};
|
|
ArrayType *mdArray = NULL;
|
|
|
|
int16 typlen;
|
|
bool typbyval;
|
|
char typalign;
|
|
|
|
/* pgraster is null, return nothing */
|
|
if (PG_ARGISNULL(0))
|
|
PG_RETURN_NULL();
|
|
pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
|
|
|
raster = rt_raster_deserialize(pgraster, FALSE);
|
|
if (!raster) {
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
elog(ERROR, "RASTER_neighborhood: Could not deserialize raster");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* band index is 1-based */
|
|
if (!PG_ARGISNULL(1))
|
|
bandindex = PG_GETARG_INT32(1);
|
|
num_bands = rt_raster_get_num_bands(raster);
|
|
if (bandindex < 1 || bandindex > num_bands) {
|
|
elog(NOTICE, "Invalid band index (must use 1-based). Returning NULL");
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* pixel column, 1-based */
|
|
x = PG_GETARG_INT32(2);
|
|
_x = x - 1;
|
|
|
|
/* pixel row, 1-based */
|
|
y = PG_GETARG_INT32(3);
|
|
_y = y - 1;
|
|
|
|
/* distance X axis */
|
|
distance[0] = PG_GETARG_INT32(4);
|
|
if (distance[0] < 0) {
|
|
elog(NOTICE, "Invalid value for distancex (must be >= zero). Returning NULL");
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
PG_RETURN_NULL();
|
|
}
|
|
distance[0] = (uint16_t) distance[0];
|
|
|
|
/* distance Y axis */
|
|
distance[1] = PG_GETARG_INT32(5);
|
|
if (distance[1] < 0) {
|
|
elog(NOTICE, "Invalid value for distancey (must be >= zero). Returning NULL");
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
PG_RETURN_NULL();
|
|
}
|
|
distance[1] = (uint16_t) distance[1];
|
|
|
|
/* exclude_nodata_value flag */
|
|
if (!PG_ARGISNULL(6))
|
|
exclude_nodata_value = PG_GETARG_BOOL(6);
|
|
|
|
/* get band */
|
|
band = rt_raster_get_band(raster, bandindex - 1);
|
|
if (!band) {
|
|
elog(NOTICE, "Could not find band at index %d. Returning NULL", bandindex);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* get neighborhood */
|
|
count = 0;
|
|
npixels = NULL;
|
|
if (distance[0] > 0 || distance[1] > 0) {
|
|
count = rt_band_get_nearest_pixel(
|
|
band,
|
|
_x, _y,
|
|
distance[0], distance[1],
|
|
exclude_nodata_value,
|
|
&npixels
|
|
);
|
|
/* error */
|
|
if (count < 0) {
|
|
elog(NOTICE, "Could not get the pixel's neighborhood for band at index %d", bandindex);
|
|
|
|
rt_band_destroy(band);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
|
|
PG_RETURN_NULL();
|
|
}
|
|
}
|
|
|
|
/* get pixel's value */
|
|
if (
|
|
(_x >= 0 && _x < rt_band_get_width(band)) &&
|
|
(_y >= 0 && _y < rt_band_get_height(band))
|
|
) {
|
|
if (rt_band_get_pixel(
|
|
band,
|
|
_x, _y,
|
|
&pixval,
|
|
&isnodata
|
|
) != ES_NONE) {
|
|
elog(NOTICE, "Could not get the pixel of band at index %d. Returning NULL", bandindex);
|
|
rt_band_destroy(band);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
PG_RETURN_NULL();
|
|
}
|
|
}
|
|
/* outside band extent, set to NODATA */
|
|
else {
|
|
/* has NODATA, use NODATA */
|
|
if (rt_band_get_hasnodata_flag(band))
|
|
rt_band_get_nodata(band, &pixval);
|
|
/* no NODATA, use min possible value */
|
|
else
|
|
pixval = rt_band_get_min_value(band);
|
|
isnodata = 1;
|
|
}
|
|
POSTGIS_RT_DEBUGF(4, "pixval: %f", pixval);
|
|
|
|
|
|
/* add pixel to neighborhood */
|
|
count++;
|
|
if (count > 1)
|
|
npixels = (rt_pixel) repalloc(npixels, sizeof(struct rt_pixel_t) * count);
|
|
else
|
|
npixels = (rt_pixel) palloc(sizeof(struct rt_pixel_t));
|
|
if (npixels == NULL) {
|
|
|
|
rt_band_destroy(band);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
|
|
elog(ERROR, "RASTER_neighborhood: Could not reallocate memory for neighborhood");
|
|
PG_RETURN_NULL();
|
|
}
|
|
npixels[count - 1].x = _x;
|
|
npixels[count - 1].y = _y;
|
|
npixels[count - 1].nodata = 1;
|
|
npixels[count - 1].value = pixval;
|
|
|
|
/* set NODATA */
|
|
if (!exclude_nodata_value || !isnodata) {
|
|
npixels[count - 1].nodata = 0;
|
|
}
|
|
|
|
/* free unnecessary stuff */
|
|
rt_band_destroy(band);
|
|
rt_raster_destroy(raster);
|
|
PG_FREE_IF_COPY(pgraster, 0);
|
|
|
|
/* convert set of rt_pixel to 2D array */
|
|
/* dim is passed with element 0 being Y-axis and element 1 being X-axis */
|
|
count = rt_pixel_set_to_array(
|
|
npixels, count, NULL,
|
|
_x, _y,
|
|
distance[0], distance[1],
|
|
&value2D,
|
|
&nodata2D,
|
|
&(dim[1]), &(dim[0])
|
|
);
|
|
pfree(npixels);
|
|
if (count != ES_NONE) {
|
|
elog(NOTICE, "Could not create 2D array of neighborhood");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* 1D arrays for values and nodata from 2D arrays */
|
|
value1D = palloc(sizeof(Datum) * dim[0] * dim[1]);
|
|
nodata1D = palloc(sizeof(bool) * dim[0] * dim[1]);
|
|
|
|
if (value1D == NULL || nodata1D == NULL) {
|
|
|
|
for (i = 0; i < dim[0]; i++) {
|
|
pfree(value2D[i]);
|
|
pfree(nodata2D[i]);
|
|
}
|
|
pfree(value2D);
|
|
pfree(nodata2D);
|
|
|
|
elog(ERROR, "RASTER_neighborhood: Could not allocate memory for return 2D array");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* copy values from 2D arrays to 1D arrays */
|
|
k = 0;
|
|
/* Y-axis */
|
|
for (i = 0; i < dim[0]; i++) {
|
|
/* X-axis */
|
|
for (j = 0; j < dim[1]; j++) {
|
|
nodata1D[k] = (bool) nodata2D[i][j];
|
|
if (!nodata1D[k])
|
|
value1D[k] = Float8GetDatum(value2D[i][j]);
|
|
else
|
|
value1D[k] = PointerGetDatum(NULL);
|
|
|
|
k++;
|
|
}
|
|
}
|
|
|
|
/* no more need for 2D arrays */
|
|
for (i = 0; i < dim[0]; i++) {
|
|
pfree(value2D[i]);
|
|
pfree(nodata2D[i]);
|
|
}
|
|
pfree(value2D);
|
|
pfree(nodata2D);
|
|
|
|
/* info about the type of item in the multi-dimensional array (float8). */
|
|
get_typlenbyvalalign(FLOAT8OID, &typlen, &typbyval, &typalign);
|
|
|
|
mdArray = construct_md_array(
|
|
value1D, nodata1D,
|
|
2, dim, lbound,
|
|
FLOAT8OID,
|
|
typlen, typbyval, typalign
|
|
);
|
|
|
|
pfree(value1D);
|
|
pfree(nodata1D);
|
|
|
|
PG_RETURN_ARRAYTYPE_P(mdArray);
|
|
}
|
|
|