* Important / Breaking Changes *
+ - Split raster/rt_core/rt_api.c and raster/rt_pg/rt_pg.c files into
+ smaller files for ease of long-term maintenance and development
+
* Deprecated signatures *
* New Features *
#include <stdlib.h>
#include <string.h>
-#include "../rt_core/rt_api.h"
+#include "librtcore.h"
+
+#include "../../postgis_config.h"
+#include "../raster_config.h"
#define CSEQUAL(a,b) (strcmp(a,b)==0)
#
#############################################################################
-AR=ar rs
-
-CC=@CC@
-LIBLWGEOM_LDFLAGS=../../liblwgeom/.libs/liblwgeom.a
-LIBLWGEOM_CFLAGS=-I../../liblwgeom
-LIBGDAL_CFLAGS=@LIBGDAL_CFLAGS@
-LIBGDAL_LDFLAGS=@LIBGDAL_LDFLAGS@
-GEOS_LDFLAGS=@GEOS_LDFLAGS@ -lgeos_c
-PROJ_LDFLAGS=@PROJ_LDFLAGS@ -lproj
-LDFLAGS=$(LIBLWGEOM_LDFLAGS) $(LIBGDAL_LDFLAGS) $(PROJ_LDFLAGS) $(GEOS_LDFLAGS)
-CFLAGS=@CFLAGS@ @PICFLAGS@ @WARNFLAGS@ $(LIBLWGEOM_CFLAGS) $(LIBGDAL_CFLAGS) @PROJ_CPPFLAGS@ @GEOS_CPPFLAGS@
+AR = ar rs
+
+CC = @CC@
+LIBLWGEOM_LDFLAGS = ../../liblwgeom/.libs/liblwgeom.a
+LIBLWGEOM_CFLAGS = -I../../liblwgeom
+LIBGDAL_CFLAGS = @LIBGDAL_CFLAGS@
+LIBGDAL_LDFLAGS = @LIBGDAL_LDFLAGS@
+GEOS_LDFLAGS = @GEOS_LDFLAGS@ -lgeos_c
+PROJ_LDFLAGS = @PROJ_LDFLAGS@ -lproj
+LDFLAGS = $(LIBLWGEOM_LDFLAGS) $(LIBGDAL_LDFLAGS) $(PROJ_LDFLAGS) $(GEOS_LDFLAGS)
+CFLAGS = @CFLAGS@ @PICFLAGS@ @WARNFLAGS@ $(LIBLWGEOM_CFLAGS) $(LIBGDAL_CFLAGS) @PROJ_CPPFLAGS@ @GEOS_CPPFLAGS@
# Standalone RTCORE objects
-RT_OBJS=rt_api.o
-RT_LIB=librtcore.a
-RT_HEADERS=rt_api.h
+RT_OBJS = \
+ rt_util.o \
+ rt_spatial_relationship.o \
+ rt_mapalgebra.o \
+ rt_geometry.o \
+ rt_statistics.o \
+ rt_pixel.o \
+ rt_warp.o \
+ rt_band.o \
+ rt_raster.o \
+ rt_serialize.o \
+ rt_wkb.o \
+ rt_context.o
+
+RT_LIB = librtcore.a
+RT_HEADERS = \
+ rt_serialize.h \
+ librtcore.h \
+ librtcore_internal.h
all: $(RT_LIB)
*
*/
-#ifndef RT_API_H_INCLUDED
-#define RT_API_H_INCLUDED
+/**
+ * @file librtcore.h
+ *
+ * This library is the generic raster handling section of PostGIS. The raster
+ * objects, constructors, destructors, and a set of spatial processing functions
+ * are implemented here.
+ *
+ * The library is designed for use in non-PostGIS applications if necessary. The
+ * units tests at test/core (and the future loader/dumper programs) are examples
+ * of non-PostGIS applications using rt_core.
+ *
+ * Programs using this library should set up the default memory managers and error
+ * handlers by implementing an rt_init_allocators() function, which can be as
+ * a wrapper around the rt_install_default_allocators() function if you want
+ * no special handling for memory management and error reporting.
+ *
+ **/
+
+/******************************************************************************
+* Some rules for *.(c|h) files in rt_core
+*
+* All functions in rt_core that receive a band index parameter
+* must be 0-based
+*
+* Variables and functions internal for a public function should be prefixed
+* with _rti_, e.g. _rti_iterator_arg.
+******************************************************************************/
+
+#ifndef LIBRTCORE_H_INCLUDED
+#define LIBRTCORE_H_INCLUDED
/* define the systems */
#if defined(__linux__) /* (predefined) */
#endif
#endif
+#include <stdio.h> /* for printf, sprintf */
#include <stdlib.h> /* For size_t, srand and rand */
#include <stdint.h> /* For C99 int types */
+#include <string.h> /* for memcpy, strlen, etc */
#include <float.h> /* for FLT_EPSILON, DBL_EPSILON and float type limits */
#include <limits.h> /* for integer type limits */
-#include <math.h>
-#include "lwgeom_geos.h"
#include "liblwgeom.h"
#include "gdal_alg.h"
#include "gdal_frmts.h"
#include "gdal.h"
#include "gdalwarper.h"
-#include "ogr_api.h"
-#include "ogr_srs_api.h"
#include "cpl_vsi.h"
#include "cpl_conv.h"
-#include "../../postgis_config.h"
-#include "../raster_config.h"
-
-/**
- * @file rt_api.h
- *
- * This library is the generic raster handling section of PostGIS. The raster
- * objects, constructors, destructors, and a set of spatial processing functions
- * are implemented here.
- *
- * The library is designed for use in non-PostGIS applications if necessary. The
- * units tests at test/core (and the future loader/dumper programs) are examples
- * of non-PostGIS applications using rt_core.
- *
- * Programs using this library should set up the default memory managers and error
- * handlers by implementing an rt_init_allocators() function, which can be as
- * a wrapper around the rt_install_default_allocators() function if you want
- * no special handling for memory management and error reporting.
- *
- **/
+#include "ogr_api.h"
+#include "ogr_srs_api.h"
/**
* Types definitions
rt_colormap_entry entry;
};
-#endif /* RT_API_H_INCLUDED */
+#endif /* LIBRTCORE_H_INCLUDED */
--- /dev/null
+/*
+ * $Id$
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef LIBRTCORE_INTERNAL_H_INCLUDED
+#define LIBRTCORE_INTERNAL_H_INCLUDED
+
+#include <assert.h>
+
+#include "../../postgis_config.h"
+#include "../raster_config.h"
+
+#include "lwgeom_geos.h"
+
+#include "librtcore.h"
+
+#endif /* LIBRTCORE_INTERNAL_H_INCLUDED */
+++ /dev/null
-/*
- * $Id$
- *
- * 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@keybit.net>
- *
- * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#include <math.h>
-#include <stdio.h> /* for printf (default message handler) */
-#include <stdarg.h> /* for va_list, va_start etc */
-#include <string.h> /* for memcpy and strlen */
-#include <assert.h>
-#include <time.h> /* for time */
-#include "rt_api.h"
-#include "gdal_vrt.h"
-
-/******************************************************************************
-* Some rules for *.(c|h) files in rt_core
-*
-* All functions in rt_core that receive a band index parameter
-* must be 0-based
-*
-* Variables and functions internal for a public function should be prefixed
-* with _rti_, e.g. _rti_iterator_arg.
-******************************************************************************/
-
-/*--- Utilities -------------------------------------------------*/
-
-static void
-swap_char(uint8_t *a, uint8_t *b) {
- uint8_t c = 0;
-
- assert(NULL != a && NULL != b);
-
- c = *a;
- *a = *b;
- *b = c;
-}
-
-static void
-flip_endian_16(uint8_t *d) {
- assert(NULL != d);
-
- swap_char(d, d + 1);
-}
-
-static void
-flip_endian_32(uint8_t *d) {
- assert(NULL != d);
-
- swap_char(d, d + 3);
- swap_char(d + 1, d + 2);
-}
-
-static void
-flip_endian_64(uint8_t *d) {
- assert(NULL != d);
-
- swap_char(d + 7, d);
- swap_char(d + 6, d + 1);
- swap_char(d + 5, d + 2);
- swap_char(d + 4, d + 3);
-}
-
-uint8_t
-rt_util_clamp_to_1BB(double value) {
- return (uint8_t)fmin(fmax((value), 0), POSTGIS_RT_1BBMAX);
-}
-
-uint8_t
-rt_util_clamp_to_2BUI(double value) {
- return (uint8_t)fmin(fmax((value), 0), POSTGIS_RT_2BUIMAX);
-}
-
-uint8_t
-rt_util_clamp_to_4BUI(double value) {
- return (uint8_t)fmin(fmax((value), 0), POSTGIS_RT_4BUIMAX);
-}
-
-int8_t
-rt_util_clamp_to_8BSI(double value) {
- return (int8_t)fmin(fmax((value), SCHAR_MIN), SCHAR_MAX);
-}
-
-uint8_t
-rt_util_clamp_to_8BUI(double value) {
- return (uint8_t)fmin(fmax((value), 0), UCHAR_MAX);
-}
-
-int16_t
-rt_util_clamp_to_16BSI(double value) {
- return (int16_t)fmin(fmax((value), SHRT_MIN), SHRT_MAX);
-}
-
-uint16_t
-rt_util_clamp_to_16BUI(double value) {
- return (uint16_t)fmin(fmax((value), 0), USHRT_MAX);
-}
-
-int32_t
-rt_util_clamp_to_32BSI(double value) {
- return (int32_t)fmin(fmax((value), INT_MIN), INT_MAX);
-}
-
-uint32_t
-rt_util_clamp_to_32BUI(double value) {
- return (uint32_t)fmin(fmax((value), 0), UINT_MAX);
-}
-
-float
-rt_util_clamp_to_32F(double value) {
- return (float)fmin(fmax((value), -FLT_MAX), FLT_MAX);
-}
-
-/* quicksort */
-#define SWAP(x, y) { double t; t = x; x = y; y = t; }
-#define ORDER(x, y) if (x > y) SWAP(x, y)
-
-static double pivot(double *left, double *right) {
- double l, m, r, *p;
-
- l = *left;
- m = *(left + (right - left) / 2);
- r = *right;
-
- /* order */
- ORDER(l, m);
- ORDER(l, r);
- ORDER(m, r);
-
- /* pivot is higher of two values */
- if (l < m) return m;
- if (m < r) return r;
-
- /* find pivot that isn't left */
- for (p = left + 1; p <= right; ++p) {
- if (*p != *left)
- return (*p < *left) ? *left : *p;
- }
-
- /* all values are same */
- return -1;
-}
-
-static double *partition(double *left, double *right, double pivot) {
- while (left <= right) {
- while (*left < pivot) ++left;
- while (*right >= pivot) --right;
-
- if (left < right) {
- SWAP(*left, *right);
- ++left;
- --right;
- }
- }
-
- return left;
-}
-
-static void quicksort(double *left, double *right) {
- double p = pivot(left, right);
- double *pos;
-
- if (p != -1) {
- pos = partition(left, right, p);
- quicksort(left, pos - 1);
- quicksort(pos, right);
- }
-}
-
-/**
- * Convert cstring name to GDAL Resample Algorithm
- *
- * @param algname : cstring name to convert
- *
- * @return valid GDAL resampling algorithm
- */
-GDALResampleAlg
-rt_util_gdal_resample_alg(const char *algname) {
- assert(algname != NULL && strlen(algname) > 0);
-
- if (strcmp(algname, "NEARESTNEIGHBOUR") == 0)
- return GRA_NearestNeighbour;
- else if (strcmp(algname, "NEARESTNEIGHBOR") == 0)
- return GRA_NearestNeighbour;
- else if (strcmp(algname, "BILINEAR") == 0)
- return GRA_Bilinear;
- else if (strcmp(algname, "CUBICSPLINE") == 0)
- return GRA_CubicSpline;
- else if (strcmp(algname, "CUBIC") == 0)
- return GRA_Cubic;
- else if (strcmp(algname, "LANCZOS") == 0)
- return GRA_Lanczos;
-
- return GRA_NearestNeighbour;
-}
-
-/**
- * Convert rt_pixtype to GDALDataType
- *
- * @param pt : pixeltype to convert
- *
- * @return valid GDALDataType
- */
-GDALDataType
-rt_util_pixtype_to_gdal_datatype(rt_pixtype pt) {
- switch (pt) {
- case PT_1BB:
- case PT_2BUI:
- case PT_4BUI:
- case PT_8BUI:
- return GDT_Byte;
- case PT_8BSI:
- case PT_16BSI:
- return GDT_Int16;
- case PT_16BUI:
- return GDT_UInt16;
- case PT_32BSI:
- return GDT_Int32;
- case PT_32BUI:
- return GDT_UInt32;
- case PT_32BF:
- return GDT_Float32;
- case PT_64BF:
- return GDT_Float64;
- default:
- return GDT_Unknown;
- }
-
- return GDT_Unknown;
-}
-
-/**
- * Convert GDALDataType to rt_pixtype
- *
- * @param gdt : GDAL datatype to convert
- *
- * @return valid rt_pixtype
- */
-rt_pixtype
-rt_util_gdal_datatype_to_pixtype(GDALDataType gdt) {
- switch (gdt) {
- case GDT_Byte:
- return PT_8BUI;
- case GDT_UInt16:
- return PT_16BUI;
- case GDT_Int16:
- return PT_16BSI;
- case GDT_UInt32:
- return PT_32BUI;
- case GDT_Int32:
- return PT_32BSI;
- case GDT_Float32:
- return PT_32BF;
- case GDT_Float64:
- return PT_64BF;
- default:
- return PT_END;
- }
-
- return PT_END;
-}
-
-/*
- get GDAL runtime version information
-*/
-const char*
-rt_util_gdal_version(const char *request) {
- if (NULL == request || !strlen(request))
- return GDALVersionInfo("RELEASE_NAME");
- else
- return GDALVersionInfo(request);
-}
-
-/*
- computed extent type
-*/
-rt_extenttype
-rt_util_extent_type(const char *name) {
- assert(name != NULL && strlen(name) > 0);
-
- if (strcmp(name, "UNION") == 0)
- return ET_UNION;
- else if (strcmp(name, "FIRST") == 0)
- return ET_FIRST;
- else if (strcmp(name, "SECOND") == 0)
- return ET_SECOND;
- else if (strcmp(name, "LAST") == 0)
- return ET_LAST;
- else if (strcmp(name, "CUSTOM") == 0)
- return ET_CUSTOM;
- else
- return ET_INTERSECTION;
-}
-
-/*
- convert the spatial reference string from a GDAL recognized format to either WKT or Proj4
-*/
-char*
-rt_util_gdal_convert_sr(const char *srs, int proj4) {
- OGRSpatialReferenceH hsrs;
- char *rtn = NULL;
-
- assert(srs != NULL);
-
- hsrs = OSRNewSpatialReference(NULL);
- if (OSRSetFromUserInput(hsrs, srs) == OGRERR_NONE) {
- if (proj4)
- OSRExportToProj4(hsrs, &rtn);
- else
- OSRExportToWkt(hsrs, &rtn);
- }
- else {
- rterror("rt_util_gdal_convert_sr: Could not process the provided srs: %s", srs);
- return NULL;
- }
-
- OSRDestroySpatialReference(hsrs);
- if (rtn == NULL) {
- rterror("rt_util_gdal_convert_sr: Could not process the provided srs: %s", srs);
- return NULL;
- }
-
- return rtn;
-}
-
-/*
- is the spatial reference string supported by GDAL
-*/
-int
-rt_util_gdal_supported_sr(const char *srs) {
- OGRSpatialReferenceH hsrs;
- OGRErr rtn = OGRERR_NONE;
-
- assert(srs != NULL);
-
- hsrs = OSRNewSpatialReference(NULL);
- rtn = OSRSetFromUserInput(hsrs, srs);
- OSRDestroySpatialReference(hsrs);
-
- if (rtn == OGRERR_NONE)
- return 1;
- else
- return 0;
-}
-
-/**
- * Get auth name and code
- *
- * @param authname: authority organization of code. calling function
- * is expected to free the memory allocated for value
- * @param authcode: code assigned by authority organization. calling function
- * is expected to free the memory allocated for value
- *
- * @return ES_NONE on success, ES_ERROR on error
- */
-rt_errorstate
-rt_util_gdal_sr_auth_info(GDALDatasetH hds, char **authname, char **authcode) {
- const char *srs = NULL;
-
- assert(authname != NULL);
- assert(authcode != NULL);
-
- *authname = NULL;
- *authcode = NULL;
-
- srs = GDALGetProjectionRef(hds);
- if (srs != NULL && srs[0] != '\0') {
- OGRSpatialReferenceH hSRS = OSRNewSpatialReference(NULL);
-
- if (OSRSetFromUserInput(hSRS, srs) == OGRERR_NONE) {
- const char* pszAuthorityName = OSRGetAuthorityName(hSRS, NULL);
- const char* pszAuthorityCode = OSRGetAuthorityCode(hSRS, NULL);
-
- if (pszAuthorityName != NULL && pszAuthorityCode != NULL) {
- *authname = rtalloc(sizeof(char) * (strlen(pszAuthorityName) + 1));
- *authcode = rtalloc(sizeof(char) * (strlen(pszAuthorityCode) + 1));
-
- if (*authname == NULL || *authcode == NULL) {
- rterror("rt_util_gdal_sr_auth_info: Could not allocate memory for auth name and code");
- if (*authname != NULL) rtdealloc(*authname);
- if (*authcode != NULL) rtdealloc(*authcode);
- OSRDestroySpatialReference(hSRS);
- return ES_ERROR;
- }
-
- strncpy(*authname, pszAuthorityName, strlen(pszAuthorityName) + 1);
- strncpy(*authcode, pszAuthorityCode, strlen(pszAuthorityCode) + 1);
- }
- }
-
- OSRDestroySpatialReference(hSRS);
- }
-
- return ES_NONE;
-}
-
-/*
- is GDAL configured correctly?
-*/
-int rt_util_gdal_configured(void) {
-
- /* set of EPSG codes */
- if (!rt_util_gdal_supported_sr("EPSG:4326"))
- return 0;
- if (!rt_util_gdal_supported_sr("EPSG:4269"))
- return 0;
- if (!rt_util_gdal_supported_sr("EPSG:4267"))
- return 0;
- if (!rt_util_gdal_supported_sr("EPSG:3310"))
- return 0;
- if (!rt_util_gdal_supported_sr("EPSG:2163"))
- return 0;
-
- return 1;
-}
-
-/*
- register all GDAL drivers
-*/
-void
-rt_util_gdal_register_all(void) {
- static int registered = 0;
-
- if (registered)
- return;
-
- GDALAllRegister();
- registered = 1;
-}
-
-/*
- is the driver registered?
-*/
-int
-rt_util_gdal_driver_registered(const char *drv) {
- int count = GDALGetDriverCount();
- int i = 0;
- GDALDriverH hdrv = NULL;
-
- if (drv == NULL || !strlen(drv) || count < 1)
- return 0;
-
- for (i = 0; i < count; i++) {
- hdrv = GDALGetDriver(i);
- if (hdrv == NULL) continue;
-
- if (strcmp(drv, GDALGetDriverShortName(hdrv)) == 0)
- return 1;
- }
-
- return 0;
-}
-
-void
-rt_util_from_ogr_envelope(
- OGREnvelope env,
- rt_envelope *ext
-) {
- assert(ext != NULL);
-
- ext->MinX = env.MinX;
- ext->MaxX = env.MaxX;
- ext->MinY = env.MinY;
- ext->MaxY = env.MaxY;
-
- ext->UpperLeftX = env.MinX;
- ext->UpperLeftY = env.MaxY;
-}
-
-void
-rt_util_to_ogr_envelope(
- rt_envelope ext,
- OGREnvelope *env
-) {
- assert(env != NULL);
-
- env->MinX = ext.MinX;
- env->MaxX = ext.MaxX;
- env->MinY = ext.MinY;
- env->MaxY = ext.MaxY;
-}
-
-LWPOLY *
-rt_util_envelope_to_lwpoly(
- rt_envelope env
-) {
- LWPOLY *npoly = NULL;
- POINTARRAY **rings = NULL;
- POINTARRAY *pts = NULL;
- POINT4D p4d;
-
- rings = (POINTARRAY **) rtalloc(sizeof (POINTARRAY*));
- if (!rings) {
- rterror("rt_util_envelope_to_lwpoly: Out of memory building envelope's geometry");
- return NULL;
- }
- rings[0] = ptarray_construct(0, 0, 5);
- if (!rings[0]) {
- rterror("rt_util_envelope_to_lwpoly: Out of memory building envelope's geometry ring");
- return NULL;
- }
-
- pts = rings[0];
-
- /* Upper-left corner (first and last points) */
- p4d.x = env.MinX;
- p4d.y = env.MaxY;
- ptarray_set_point4d(pts, 0, &p4d);
- ptarray_set_point4d(pts, 4, &p4d);
-
- /* Upper-right corner (we go clockwise) */
- p4d.x = env.MaxX;
- p4d.y = env.MaxY;
- ptarray_set_point4d(pts, 1, &p4d);
-
- /* Lower-right corner */
- p4d.x = env.MaxX;
- p4d.y = env.MinY;
- ptarray_set_point4d(pts, 2, &p4d);
-
- /* Lower-left corner */
- p4d.x = env.MinX;
- p4d.y = env.MinY;
- ptarray_set_point4d(pts, 3, &p4d);
-
- npoly = lwpoly_construct(SRID_UNKNOWN, 0, 1, rings);
- if (npoly == NULL) {
- rterror("rt_util_envelope_to_lwpoly: Could not build envelope's geometry");
- return NULL;
- }
-
- return npoly;
-}
-
-int
-rt_util_same_geotransform_matrix(double *gt1, double *gt2) {
- int k = 0;
-
- if (gt1 == NULL || gt2 == NULL)
- return FALSE;
-
- for (k = 0; k < 6; k++) {
- if (FLT_NEQ(gt1[k], gt2[k]))
- return FALSE;
- }
-
- return TRUE;
-}
-
-/* coordinates in RGB and HSV are floating point values between 0 and 1 */
-rt_errorstate
-rt_util_rgb_to_hsv(double rgb[3], double hsv[3]) {
- int i;
-
- double minc;
- double maxc;
-
- double h = 0.;
- double s = 0.;
- double v = 0.;
-
- minc = rgb[0];
- maxc = rgb[0];
-
- /* get min and max values from RGB */
- for (i = 1; i < 3; i++) {
- if (rgb[i] > maxc)
- maxc = rgb[i];
- if (rgb[i] < minc)
- minc = rgb[i];
- }
- v = maxc;
-
- if (maxc != minc) {
- double diff = 0.;
- double rc = 0.;
- double gc = 0.;
- double bc = 0.;
- double junk = 0.;
-
- diff = maxc - minc;
- s = diff / maxc;
- rc = (maxc - rgb[0]) / diff;
- gc = (maxc - rgb[1]) / diff;
- bc = (maxc - rgb[2]) / diff;
-
- if (DBL_EQ(rgb[0], maxc))
- h = bc - gc;
- else if (DBL_EQ(rgb[1], maxc))
- h = 2.0 + rc - bc;
- else
- h = 4.0 + gc - rc;
-
- h = modf((h / 6.0), &junk);
- }
-
- hsv[0] = h;
- hsv[1] = s;
- hsv[2] = v;
-
- return ES_NONE;
-}
-
-/* coordinates in RGB and HSV are floating point values between 0 and 1 */
-rt_errorstate
-rt_util_hsv_to_rgb(double hsv[3], double rgb[3]) {
- double r = 0;
- double g = 0;
- double b = 0;
- double v = hsv[2];
-
- if (DBL_EQ(hsv[1], 0.))
- r = g = b = v;
- else {
- double i;
- double f;
- double p;
- double q;
- double t;
-
- int a;
-
- i = floor(hsv[0] * 6.);
- f = (hsv[0] * 6.0) - i;
- p = v * (1. - hsv[1]);
- q = v * (1. - hsv[1] * f);
- t = v * (1. - hsv[1] * (1. - f));
-
- a = (int) i;
- switch (a) {
- case 1:
- r = q;
- g = v;
- b = p;
- break;
- case 2:
- r = p;
- g = v;
- b = t;
- break;
- case 3:
- r = p;
- g = q;
- b = v;
- break;
- case 4:
- r = t;
- g = p;
- b = v;
- break;
- case 5:
- r = v;
- g = p;
- b = q;
- break;
- case 0:
- case 6:
- default:
- r = v;
- g = t;
- b = p;
- break;
- }
- }
-
- rgb[0] = r;
- rgb[1] = g;
- rgb[2] = b;
-
- return ES_NONE;
-}
-
-/*- rt_context -------------------------------------------------------*/
-
-/* Functions definitions */
-void * init_rt_allocator(size_t size);
-void * init_rt_reallocator(void * mem, size_t size);
-void init_rt_deallocator(void * mem);
-void init_rt_errorreporter(const char * fmt, va_list ap);
-void init_rt_warnreporter(const char * fmt, va_list ap);
-void init_rt_inforeporter(const char * fmt, va_list ap);
-
-/*
- * Default allocators
- *
- * We include some default allocators that use malloc/free/realloc
- * along with stdout/stderr since this is the most common use case
- *
- */
-void *
-default_rt_allocator(size_t size)
-{
- void *mem = malloc(size);
- return mem;
-}
-
-void *
-default_rt_reallocator(void *mem, size_t size)
-{
- void *ret = realloc(mem, size);
- return ret;
-}
-
-void
-default_rt_deallocator(void *mem)
-{
- free(mem);
-}
-
-void
-default_rt_error_handler(const char *fmt, va_list ap) {
-
- static const char *label = "ERROR: ";
- char newfmt[1024] = {0};
- snprintf(newfmt, 1024, "%s%s\n", label, fmt);
- newfmt[1023] = '\0';
-
- vprintf(newfmt, ap);
-
- va_end(ap);
-}
-
-void
-default_rt_warning_handler(const char *fmt, va_list ap) {
-
- static const char *label = "WARNING: ";
- char newfmt[1024] = {0};
- snprintf(newfmt, 1024, "%s%s\n", label, fmt);
- newfmt[1023] = '\0';
-
- vprintf(newfmt, ap);
-
- va_end(ap);
-}
-
-void
-default_rt_info_handler(const char *fmt, va_list ap) {
-
- static const char *label = "INFO: ";
- char newfmt[1024] = {0};
- snprintf(newfmt, 1024, "%s%s\n", label, fmt);
- newfmt[1023] = '\0';
-
- vprintf(newfmt, ap);
-
- va_end(ap);
-}
-
-/**
- * Struct definition here
- */
-struct rt_context_t {
- rt_allocator alloc;
- rt_reallocator realloc;
- rt_deallocator dealloc;
- rt_message_handler err;
- rt_message_handler warn;
- rt_message_handler info;
-};
-
-/* Static variable, to be used for all rt_core functions */
-static struct rt_context_t ctx_t = {
- .alloc = init_rt_allocator,
- .realloc = init_rt_reallocator,
- .dealloc = init_rt_deallocator,
- .err = init_rt_errorreporter,
- .warn = init_rt_warnreporter,
- .info = init_rt_inforeporter
-};
-
-
-/**
- * This function is normally called by rt_init_allocators when no special memory
- * management is needed. Useful in raster core testing and in the (future)
- * loader, when we need to use raster core functions but we don't have
- * PostgreSQL backend behind. We must take care of memory by ourselves in those
- * situations
- */
-void
-rt_install_default_allocators(void)
-{
- ctx_t.alloc = default_rt_allocator;
- ctx_t.realloc = default_rt_reallocator;
- ctx_t.dealloc = default_rt_deallocator;
- ctx_t.err = default_rt_error_handler;
- ctx_t.info = default_rt_info_handler;
- ctx_t.warn = default_rt_warning_handler;
-}
-
-
-/**
- * This function is called by rt_init_allocators when the PostgreSQL backend is
- * taking care of the memory and we want to use palloc family
- */
-void
-rt_set_handlers(rt_allocator allocator, rt_reallocator reallocator,
- rt_deallocator deallocator, rt_message_handler error_handler,
- rt_message_handler info_handler, rt_message_handler warning_handler) {
-
- ctx_t.alloc = allocator;
- ctx_t.realloc = reallocator;
- ctx_t.dealloc = deallocator;
-
- ctx_t.err = error_handler;
- ctx_t.info = info_handler;
- ctx_t.warn = warning_handler;
-}
-
-
-
-/**
- * Initialisation allocators
- *
- * These are used the first time any of the allocators are called to enable
- * executables/libraries that link into raster to be able to set up their own
- * allocators. This is mainly useful for older PostgreSQL versions that don't
- * have functions that are called upon startup.
- **/
-void *
-init_rt_allocator(size_t size)
-{
- rt_init_allocators();
-
- return ctx_t.alloc(size);
-}
-
-void
-init_rt_deallocator(void *mem)
-{
- rt_init_allocators();
-
- ctx_t.dealloc(mem);
-}
-
-
-void *
-init_rt_reallocator(void *mem, size_t size)
-{
- rt_init_allocators();
-
- return ctx_t.realloc(mem, size);
-}
-
-void
-init_rt_inforeporter(const char *fmt, va_list ap)
-{
- rt_init_allocators();
-
- (*ctx_t.info)(fmt, ap);
-}
-
-void
-init_rt_warnreporter(const char *fmt, va_list ap)
-{
- rt_init_allocators();
-
- (*ctx_t.warn)(fmt, ap);
-}
-
-
-void
-init_rt_errorreporter(const char *fmt, va_list ap)
-{
- rt_init_allocators();
-
- (*ctx_t.err)(fmt, ap);
-
-}
-
-
-
-/**
- * Raster core memory management functions.
- *
- * They use the functions defined by the caller.
- */
-void *
-rtalloc(size_t size) {
- void * mem = ctx_t.alloc(size);
- RASTER_DEBUGF(5, "rtalloc called: %d@%p", size, mem);
- return mem;
-}
-
-
-void *
-rtrealloc(void * mem, size_t size) {
- void * result = ctx_t.realloc(mem, size);
- RASTER_DEBUGF(5, "rtrealloc called: %d@%p", size, result);
- return result;
-}
-
-void
-rtdealloc(void * mem) {
- ctx_t.dealloc(mem);
- RASTER_DEBUG(5, "rtdealloc called");
-}
-
-/**
- * Raster core error and info handlers
- *
- * Since variadic functions cannot pass their parameters directly, we need
- * wrappers for these functions to convert the arguments into a va_list
- * structure.
- */
-void
-rterror(const char *fmt, ...) {
- va_list ap;
-
- va_start(ap, fmt);
-
- /* Call the supplied function */
- (*ctx_t.err)(fmt, ap);
-
- va_end(ap);
-}
-
-void
-rtinfo(const char *fmt, ...) {
- va_list ap;
-
- va_start(ap, fmt);
-
- /* Call the supplied function */
- (*ctx_t.info)(fmt, ap);
-
- va_end(ap);
-}
-
-
-void
-rtwarn(const char *fmt, ...) {
- va_list ap;
-
- va_start(ap, fmt);
-
- /* Call the supplied function */
- (*ctx_t.warn)(fmt, ap);
-
- va_end(ap);
-}
-
-
-
-int
-rt_util_dbl_trunc_warning(
- double initialvalue,
- int32_t checkvalint, uint32_t checkvaluint,
- float checkvalfloat, double checkvaldouble,
- rt_pixtype pixtype
-) {
- int result = 0;
-
- switch (pixtype) {
- case PT_1BB:
- case PT_2BUI:
- case PT_4BUI:
- case PT_8BSI:
- case PT_8BUI:
- case PT_16BSI:
- case PT_16BUI:
- case PT_32BSI: {
- if (fabs(checkvalint - initialvalue) >= 1) {
-#if POSTGIS_RASTER_WARN_ON_TRUNCATION > 0
- rtwarn("Value set for %s band got clamped from %f to %d",
- rt_pixtype_name(pixtype),
- initialvalue, checkvalint
- );
-#endif
- result = 1;
- }
- else if (FLT_NEQ(checkvalint, initialvalue)) {
-#if POSTGIS_RASTER_WARN_ON_TRUNCATION > 0
- rtwarn("Value set for %s band got truncated from %f to %d",
- rt_pixtype_name(pixtype),
- initialvalue, checkvalint
- );
-#endif
- result = 1;
- }
- break;
- }
- case PT_32BUI: {
- if (fabs(checkvaluint - initialvalue) >= 1) {
-#if POSTGIS_RASTER_WARN_ON_TRUNCATION > 0
- rtwarn("Value set for %s band got clamped from %f to %u",
- rt_pixtype_name(pixtype),
- initialvalue, checkvaluint
- );
-#endif
- result = 1;
- }
- else if (FLT_NEQ(checkvaluint, initialvalue)) {
-#if POSTGIS_RASTER_WARN_ON_TRUNCATION > 0
- rtwarn("Value set for %s band got truncated from %f to %u",
- rt_pixtype_name(pixtype),
- initialvalue, checkvaluint
- );
-#endif
- result = 1;
- }
- break;
- }
- case PT_32BF: {
- /*
- For float, because the initial value is a double,
- there is very often a difference between the desired value and the obtained one
- */
- if (FLT_NEQ(checkvalfloat, initialvalue)) {
-#if POSTGIS_RASTER_WARN_ON_TRUNCATION > 0
- rtwarn("Value set for %s band got converted from %f to %f",
- rt_pixtype_name(pixtype),
- initialvalue, checkvalfloat
- );
-#endif
- result = 1;
- }
- break;
- }
- case PT_64BF: {
- if (FLT_NEQ(checkvaldouble, initialvalue)) {
-#if POSTGIS_RASTER_WARN_ON_TRUNCATION > 0
- rtwarn("Value set for %s band got converted from %f to %f",
- rt_pixtype_name(pixtype),
- initialvalue, checkvaldouble
- );
-#endif
- result = 1;
- }
- break;
- }
- case PT_END:
- break;
- }
-
- return result;
-}
-
-/*--- Debug and Testing Utilities --------------------------------------------*/
-
-#if POSTGIS_DEBUG_LEVEL > 2
-
-static char*
-d_binary_to_hex(const uint8_t * const raw, uint32_t size, uint32_t *hexsize) {
- char* hex = NULL;
- uint32_t i = 0;
-
-
- assert(NULL != raw);
- assert(NULL != hexsize);
-
-
- *hexsize = size * 2; /* hex is 2 times bytes */
- hex = (char*) rtalloc((*hexsize) + 1);
- if (!hex) {
- rterror("d_binary_to_hex: Out of memory hexifying raw binary");
- return NULL;
- }
- hex[*hexsize] = '\0'; /* Null-terminate */
-
- for (i = 0; i < size; ++i) {
- deparse_hex(raw[i], &(hex[2 * i]));
- }
-
- assert(NULL != hex);
- assert(0 == strlen(hex) % 2);
- return hex;
-}
-
-static void
-d_print_binary_hex(const char* msg, const uint8_t * const raw, uint32_t size) {
- char* hex = NULL;
- uint32_t hexsize = 0;
-
-
- assert(NULL != msg);
- assert(NULL != raw);
-
-
- hex = d_binary_to_hex(raw, size, &hexsize);
- if (NULL != hex) {
- rtinfo("%s\t%s", msg, hex);
- rtdealloc(hex);
- }
-}
-
-static size_t
-d_binptr_to_pos(const uint8_t * const ptr, const uint8_t * const end, size_t size) {
- assert(NULL != ptr && NULL != end);
-
- return (size - (end - ptr));
-}
-
-#define CHECK_BINPTR_POSITION(ptr, end, size, pos) \
- { if (pos != d_binptr_to_pos(ptr, end, size)) { \
- fprintf(stderr, "Check of binary stream pointer position failed on line %d\n", __LINE__); \
- fprintf(stderr, "\tactual = %lu, expected = %lu\n", (long unsigned)d_binptr_to_pos(ptr, end, size), (long unsigned)pos); \
- } }
-
-#else
-
-#define CHECK_BINPTR_POSITION(ptr, end, size, pos) ((void)0);
-
-#endif /* ifndef POSTGIS_DEBUG_LEVEL > 3 */
-
-/*- rt_pixeltype -----------------------------------------------------*/
-
-int
-rt_pixtype_size(rt_pixtype pixtype) {
- int pixbytes = -1;
-
- switch (pixtype) {
- case PT_1BB:
- case PT_2BUI:
- case PT_4BUI:
- case PT_8BSI:
- case PT_8BUI:
- pixbytes = 1;
- break;
- case PT_16BSI:
- case PT_16BUI:
- pixbytes = 2;
- break;
- case PT_32BSI:
- case PT_32BUI:
- case PT_32BF:
- pixbytes = 4;
- break;
- case PT_64BF:
- pixbytes = 8;
- break;
- default:
- rterror("rt_pixtype_size: Unknown pixeltype %d", pixtype);
- pixbytes = -1;
- break;
- }
-
- RASTER_DEBUGF(3, "Pixel type = %s and size = %d bytes",
- rt_pixtype_name(pixtype), pixbytes);
-
- return pixbytes;
-}
-
-int
-rt_pixtype_alignment(rt_pixtype pixtype) {
- return rt_pixtype_size(pixtype);
-}
-
-rt_pixtype
-rt_pixtype_index_from_name(const char* pixname) {
- assert(pixname && strlen(pixname) > 0);
-
- if (strcmp(pixname, "1BB") == 0)
- return PT_1BB;
- else if (strcmp(pixname, "2BUI") == 0)
- return PT_2BUI;
- else if (strcmp(pixname, "4BUI") == 0)
- return PT_4BUI;
- else if (strcmp(pixname, "8BSI") == 0)
- return PT_8BSI;
- else if (strcmp(pixname, "8BUI") == 0)
- return PT_8BUI;
- else if (strcmp(pixname, "16BSI") == 0)
- return PT_16BSI;
- else if (strcmp(pixname, "16BUI") == 0)
- return PT_16BUI;
- else if (strcmp(pixname, "32BSI") == 0)
- return PT_32BSI;
- else if (strcmp(pixname, "32BUI") == 0)
- return PT_32BUI;
- else if (strcmp(pixname, "32BF") == 0)
- return PT_32BF;
- else if (strcmp(pixname, "64BF") == 0)
- return PT_64BF;
-
- return PT_END;
-}
-
-const char*
-rt_pixtype_name(rt_pixtype pixtype) {
- switch (pixtype) {
- case PT_1BB:
- return "1BB";
- case PT_2BUI:
- return "2BUI";
- case PT_4BUI:
- return "4BUI";
- case PT_8BSI:
- return "8BSI";
- case PT_8BUI:
- return "8BUI";
- case PT_16BSI:
- return "16BSI";
- case PT_16BUI:
- return "16BUI";
- case PT_32BSI:
- return "32BSI";
- case PT_32BUI:
- return "32BUI";
- case PT_32BF:
- return "32BF";
- case PT_64BF:
- return "64BF";
- default:
- rterror("rt_pixtype_name: Unknown pixeltype %d", pixtype);
- return "Unknown";
- }
-}
-
-/**
- * Return minimum value possible for pixel type
- *
- * @param pixtype : the pixel type to get minimum possible value for
- *
- * @return the minimum possible value for the pixel type.
- */
-double
-rt_pixtype_get_min_value(rt_pixtype pixtype) {
- switch (pixtype) {
- case PT_1BB: {
- return (double) rt_util_clamp_to_1BB((double) CHAR_MIN);
- }
- case PT_2BUI: {
- return (double) rt_util_clamp_to_2BUI((double) CHAR_MIN);
- }
- case PT_4BUI: {
- return (double) rt_util_clamp_to_4BUI((double) CHAR_MIN);
- }
- case PT_8BUI: {
- return (double) rt_util_clamp_to_8BUI((double) CHAR_MIN);
- }
- case PT_8BSI: {
- return (double) rt_util_clamp_to_8BSI((double) SCHAR_MIN);
- }
- case PT_16BSI: {
- return (double) rt_util_clamp_to_16BSI((double) SHRT_MIN);
- }
- case PT_16BUI: {
- return (double) rt_util_clamp_to_16BUI((double) SHRT_MIN);
- }
- case PT_32BSI: {
- return (double) rt_util_clamp_to_32BSI((double) INT_MIN);
- }
- case PT_32BUI: {
- return (double) rt_util_clamp_to_32BUI((double) INT_MIN);
- }
- case PT_32BF: {
- return (double) -FLT_MAX;
- }
- case PT_64BF: {
- return (double) -DBL_MAX;
- }
- default: {
- rterror("rt_pixtype_get_min_value: Unknown pixeltype %d", pixtype);
- return (double) rt_util_clamp_to_8BUI((double) CHAR_MIN);
- }
- }
-}
-
-/**
- * Returns 1 if clamped values are equal, 0 if not equal, -1 if error
- *
- * @param pixtype : the pixel type to clamp the provided values
- * @param val : value to compare to reference value
- * @param refval : reference value to be compared with
- * @param isequal : non-zero if clamped values are equal, 0 otherwise
- *
- * @return ES_NONE on success, ES_ERROR on error
- */
-rt_errorstate rt_pixtype_compare_clamped_values(
- rt_pixtype pixtype,
- double val, double refval,
- int *isequal
-) {
- assert(isequal != NULL);
- *isequal = 0;
-
- switch (pixtype) {
- case PT_1BB:
- if (rt_util_clamp_to_1BB(val) == rt_util_clamp_to_1BB(refval))
- *isequal = 1;
- break;
- case PT_2BUI:
- if (rt_util_clamp_to_2BUI(val) == rt_util_clamp_to_2BUI(refval))
- *isequal = 1;
- break;
- case PT_4BUI:
- if (rt_util_clamp_to_4BUI(val) == rt_util_clamp_to_4BUI(refval))
- *isequal = 1;
- break;
- case PT_8BSI:
- if (rt_util_clamp_to_8BSI(val) == rt_util_clamp_to_8BSI(refval))
- *isequal = 1;
- break;
- case PT_8BUI:
- if (rt_util_clamp_to_8BUI(val) == rt_util_clamp_to_8BUI(refval))
- *isequal = 1;
- break;
- case PT_16BSI:
- if (rt_util_clamp_to_16BSI(val) == rt_util_clamp_to_16BSI(refval))
- *isequal = 1;
- break;
- case PT_16BUI:
- if (rt_util_clamp_to_16BUI(val) == rt_util_clamp_to_16BUI(refval))
- *isequal = 1;
- break;
- case PT_32BSI:
- if (rt_util_clamp_to_32BSI(val) == rt_util_clamp_to_32BSI(refval))
- *isequal = 1;
- break;
- case PT_32BUI:
- if (rt_util_clamp_to_32BUI(val) == rt_util_clamp_to_32BUI(refval))
- *isequal = 1;
- break;
- case PT_32BF:
- if (FLT_EQ(rt_util_clamp_to_32F(val), rt_util_clamp_to_32F(refval)))
- *isequal = 1;
- break;
- case PT_64BF:
- if (FLT_EQ(val, refval))
- *isequal = 1;
- break;
- default:
- rterror("rt_pixtype_compare_clamped_values: Unknown pixeltype %d", pixtype);
- return ES_ERROR;
- }
-
- return ES_NONE;
-}
-
-/*- rt_pixel ----------------------------------------------------------*/
-
-/*
- * Convert an array of rt_pixel objects to two 2D arrays of value and NODATA.
- * The dimensions of the returned 2D array are [Y][X], going by row Y and
- * then column X.
- *
- * @param npixel : array of rt_pixel objects
- * @param count : number of elements in npixel
- * @param x : the column of the center pixel (0-based)
- * @param y : the line of the center pixel (0-based)
- * @param distancex : the number of pixels around the specified pixel
- * along the X axis
- * @param distancey : the number of pixels around the specified pixel
- * along the Y axis
- * @param value : pointer to pointer for 2D value array
- * @param nodata : pointer to pointer for 2D NODATA array
- * @param dimx : size of value and nodata along the X axis
- * @param dimy : size of value and nodata along the Y axis
- *
- * @return ES_NONE on success, ES_ERROR on error
- */
-rt_errorstate rt_pixel_set_to_array(
- rt_pixel npixel, int count,
- int x, int y,
- uint16_t distancex, uint16_t distancey,
- double ***value,
- int ***nodata,
- int *dimx, int *dimy
-) {
- uint32_t i;
- uint32_t j;
- uint32_t dim[2] = {0};
- double **values = NULL;
- int **nodatas = NULL;
- int zero[2] = {0};
- int _x;
- int _y;
-
- assert(npixel != NULL && count > 0);
- assert(value != NULL);
- assert(nodata != NULL);
-
- /* dimensions */
- dim[0] = distancex * 2 + 1;
- dim[1] = distancey * 2 + 1;
- RASTER_DEBUGF(4, "dimensions = %d x %d", dim[0], dim[1]);
-
- /* establish 2D arrays (Y axis) */
- values = rtalloc(sizeof(double *) * dim[1]);
- nodatas = rtalloc(sizeof(int *) * dim[1]);
-
- if (values == NULL || nodatas == NULL) {
- rterror("rt_pixel_set_to_array: Could not allocate memory for 2D array");
- return ES_ERROR;
- }
-
- /* initialize X axis */
- for (i = 0; i < dim[1]; i++) {
- values[i] = rtalloc(sizeof(double) * dim[0]);
- nodatas[i] = rtalloc(sizeof(int) * dim[0]);
-
- if (values[i] == NULL || nodatas[i] == NULL) {
- rterror("rt_pixel_set_to_array: Could not allocate memory for dimension of 2D array");
-
- if (values[i] == NULL) {
- for (j = 0; j < i; j++) {
- rtdealloc(values[j]);
- rtdealloc(nodatas[j]);
- }
- }
- else {
- for (j = 0; j <= i; j++) {
- rtdealloc(values[j]);
- if (j < i)
- rtdealloc(nodatas[j]);
- }
- }
-
- rtdealloc(values);
- rtdealloc(nodatas);
-
- return ES_ERROR;
- }
-
- /* set values to 0 */
- memset(values[i], 0, sizeof(double) * dim[0]);
-
- /* set nodatas to 1 */
- for (j = 0; j < dim[0]; j++)
- nodatas[i][j] = 1;
- }
-
- /* get 0,0 of grid */
- zero[0] = x - distancex;
- zero[1] = y - distancey;
-
- /* populate 2D arrays */
- for (i = 0; i < count; i++) {
- if (npixel[i].nodata)
- continue;
-
- _x = npixel[i].x - zero[0];
- _y = npixel[i].y - zero[1];
-
- RASTER_DEBUGF(4, "absolute x,y: %d x %d", npixel[i].x, npixel[i].y);
- RASTER_DEBUGF(4, "relative x,y: %d x %d", _x, _y);
-
- values[_y][_x] = npixel[i].value;
- nodatas[_y][_x] = 0;
-
- RASTER_DEBUGF(4, "(x, y, nodata, value) = (%d, %d, %d, %f)", _x, _y, nodatas[_y][_x], values[_y][_x]);
- }
-
- *value = &(*values);
- *nodata = &(*nodatas);
- if (dimx != NULL)
- *dimx = dim[0];
- if (dimy != NULL)
- *dimy = dim[1];
-
- return ES_NONE;
-}
-
-/*- rt_band ----------------------------------------------------------*/
-
-/**
- * Create an in-db rt_band with no data
- *
- * @param width : number of pixel columns
- * @param height : number of pixel rows
- * @param pixtype : pixel type for the band
- * @param hasnodata : indicates if the band has nodata value
- * @param nodataval : the nodata value, will be appropriately
- * truncated to fit the pixtype size.
- * @param data : pointer to actual band data, required to
- * be aligned accordingly to
- * rt_pixtype_aligment(pixtype) and big enough
- * to hold raster width*height values.
- * Data will NOT be copied, ownership is left
- * to caller which is responsible to keep it
- * allocated for the whole lifetime of the returned
- * rt_band.
- *
- * @return an rt_band, or 0 on failure
- */
-rt_band
-rt_band_new_inline(
- uint16_t width, uint16_t height,
- rt_pixtype pixtype,
- uint32_t hasnodata, double nodataval,
- uint8_t* data
-) {
- rt_band band = NULL;
-
- assert(NULL != data);
-
- band = rtalloc(sizeof(struct rt_band_t));
- if (band == NULL) {
- rterror("rt_band_new_inline: Out of memory allocating rt_band");
- return NULL;
- }
-
- RASTER_DEBUGF(3, "Created rt_band @ %p with pixtype %s", band, rt_pixtype_name(pixtype));
-
- band->pixtype = pixtype;
- band->offline = 0;
- band->width = width;
- band->height = height;
- band->hasnodata = hasnodata ? 1 : 0;
- band->isnodata = FALSE; /* we don't know what is in data, so must be FALSE */
- band->nodataval = 0;
- band->data.mem = data;
- band->ownsdata = 0; /* we do NOT own this data!!! */
- band->raster = NULL;
-
- RASTER_DEBUGF(3, "Created rt_band with dimensions %d x %d", band->width, band->height);
-
- /* properly set nodataval as it may need to be constrained to the data type */
- if (hasnodata && rt_band_set_nodata(band, nodataval, NULL) != ES_NONE) {
- rterror("rt_band_new_inline: Could not set NODATA value");
- rt_band_destroy(band);
- return NULL;
- }
-
- return band;
-}
-
-/**
- * Create an out-db rt_band
- *
- * @param width : number of pixel columns
- * @param height : number of pixel rows
- * @param pixtype : pixel type for the band
- * @param hasnodata : indicates if the band has nodata value
- * @param nodataval : the nodata value, will be appropriately
- * truncated to fit the pixtype size.
- * @param bandNum : 0-based band number in the external file
- * to associate this band with.
- * @param path : NULL-terminated path string pointing to the file
- * containing band data. The string will NOT be
- * copied, ownership is left to caller which is
- * responsible to keep it allocated for the whole
- * lifetime of the returned rt_band.
- *
- * @return an rt_band, or 0 on failure
- */
-rt_band
-rt_band_new_offline(
- uint16_t width, uint16_t height,
- rt_pixtype pixtype,
- uint32_t hasnodata, double nodataval,
- uint8_t bandNum, const char* path
-) {
- rt_band band = NULL;
- int pathlen = 0;
-
- assert(NULL != path);
-
- band = rtalloc(sizeof(struct rt_band_t));
- if (band == NULL) {
- rterror("rt_band_new_offline: Out of memory allocating rt_band");
- return NULL;
- }
-
- RASTER_DEBUGF(3, "Created rt_band @ %p with pixtype %s",
- band, rt_pixtype_name(pixtype)
- );
-
- band->pixtype = pixtype;
- band->offline = 1;
- band->width = width;
- band->height = height;
- band->hasnodata = hasnodata ? 1 : 0;
- band->nodataval = 0;
- band->isnodata = FALSE; /* we don't know if the offline band is NODATA */
- band->ownsdata = 0; /* offline, flag is useless as all offline data cache is owned internally */
- band->raster = NULL;
-
- /* properly set nodataval as it may need to be constrained to the data type */
- if (hasnodata && rt_band_set_nodata(band, nodataval, NULL) != ES_NONE) {
- rterror("rt_band_new_offline: Could not set NODATA value");
- rt_band_destroy(band);
- return NULL;
- }
-
- band->data.offline.bandNum = bandNum;
-
- /* memory for data.offline.path is managed internally */
- pathlen = strlen(path);
- band->data.offline.path = rtalloc(sizeof(char) * (pathlen + 1));
- if (band->data.offline.path == NULL) {
- rterror("rt_band_new_offline: Out of memory allocating offline path");
- rt_band_destroy(band);
- return NULL;
- }
- memcpy(band->data.offline.path, path, pathlen);
- band->data.offline.path[pathlen] = '\0';
-
- band->data.offline.mem = NULL;
-
- return band;
-}
-
-/**
- * Create a new band duplicated from source band. Memory is allocated
- * for band path (if band is offline) or band data (if band is online).
- * The caller is responsible for freeing the memory when the returned
- * rt_band is destroyed.
- *
- * @param : the band to copy
- *
- * @return an rt_band or NULL on failure
- */
-rt_band
-rt_band_duplicate(rt_band band) {
- rt_band rtn = NULL;
-
- assert(band != NULL);
-
- /* offline */
- if (band->offline) {
- rtn = rt_band_new_offline(
- band->width, band->height,
- band->pixtype,
- band->hasnodata, band->nodataval,
- band->data.offline.bandNum, (const char *) band->data.offline.path
- );
- }
- /* online */
- else {
- uint8_t *data = NULL;
- data = rtalloc(rt_pixtype_size(band->pixtype) * band->width * band->height);
- if (data == NULL) {
- rterror("rt_band_duplicate: Out of memory allocating online band data");
- return NULL;
- }
- memcpy(data, band->data.mem, rt_pixtype_size(band->pixtype) * band->width * band->height);
-
- rtn = rt_band_new_inline(
- band->width, band->height,
- band->pixtype,
- band->hasnodata, band->nodataval,
- data
- );
- rt_band_set_ownsdata_flag(rtn, 1); /* we DO own this data!!! */
- }
-
- if (rtn == NULL) {
- rterror("rt_band_duplicate: Could not copy band");
- return NULL;
- }
-
- return rtn;
-}
-
-int
-rt_band_is_offline(rt_band band) {
-
- assert(NULL != band);
-
-
- return band->offline ? 1 : 0;
-}
-
-/**
- * Destroy a raster band
- *
- * @param band : the band to destroy
- */
-void
-rt_band_destroy(rt_band band) {
- if (band == NULL)
- return;
-
- RASTER_DEBUGF(3, "Destroying rt_band @ %p", band);
-
- /* offline band */
- if (band->offline) {
- /* memory cache */
- if (band->data.offline.mem != NULL)
- rtdealloc(band->data.offline.mem);
- /* offline file path */
- if (band->data.offline.path != NULL)
- rtdealloc(band->data.offline.path);
- }
- /* inline band and band owns the data */
- else if (band->data.mem != NULL && band->ownsdata)
- rtdealloc(band->data.mem);
-
- rtdealloc(band);
-}
-
-const char*
-rt_band_get_ext_path(rt_band band) {
-
- assert(NULL != band);
-
-
- if (!band->offline) {
- RASTER_DEBUG(3, "rt_band_get_ext_path: Band is not offline");
- return NULL;
- }
- return band->data.offline.path;
-}
-
-rt_errorstate
-rt_band_get_ext_band_num(rt_band band, uint8_t *bandnum) {
- assert(NULL != band);
- assert(NULL != bandnum);
-
- *bandnum = 0;
-
- if (!band->offline) {
- RASTER_DEBUG(3, "rt_band_get_ext_band_num: Band is not offline");
- return ES_ERROR;
- }
-
- *bandnum = band->data.offline.bandNum;
-
- return ES_NONE;
-}
-
-/**
- * Get pointer to raster band data
- *
- * @param band : the band who's data to get
- *
- * @return pointer to band data or NULL if error
- */
-void *
-rt_band_get_data(rt_band band) {
- assert(NULL != band);
-
- if (band->offline) {
- if (band->data.offline.mem != NULL)
- return band->data.offline.mem;
-
- if (rt_band_load_offline_data(band) != ES_NONE)
- return NULL;
- else
- return band->data.offline.mem;
- }
- else
- return band->data.mem;
-}
-
-/**
- * Load offline band's data. Loaded data is internally owned
- * and should not be released by the caller. Data will be
- * released when band is destroyed with rt_band_destroy().
- *
- * @param band : the band who's data to get
- *
- * @return ES_NONE if success, ES_ERROR if failure
- */
-rt_errorstate
-rt_band_load_offline_data(rt_band band) {
- GDALDatasetH hdsSrc = NULL;
- int nband = 0;
- VRTDatasetH hdsDst = NULL;
- VRTSourcedRasterBandH hbandDst = NULL;
- double gt[6] = {0.};
- double ogt[6] = {0};
- double offset[2] = {0};
-
- rt_raster _rast = NULL;
- rt_band _band = NULL;
- int aligned = 0;
- int err = ES_NONE;
-
- assert(band != NULL);
- assert(band->raster != NULL);
-
- if (!band->offline) {
- rterror("rt_band_load_offline_data: Band is not offline");
- return ES_ERROR;
- }
- else if (!strlen(band->data.offline.path)) {
- rterror("rt_band_load_offline_data: Offline band does not a have a specified file");
- return ES_ERROR;
- }
-
- rt_util_gdal_register_all();
- hdsSrc = GDALOpenShared(band->data.offline.path, GA_ReadOnly);
- if (hdsSrc == NULL) {
- rterror("rt_band_load_offline_data: Cannot open offline raster: %s", band->data.offline.path);
- return ES_ERROR;
- }
-
- /* # of bands */
- nband = GDALGetRasterCount(hdsSrc);
- if (!nband) {
- rterror("rt_band_load_offline_data: No bands found in offline raster: %s", band->data.offline.path);
- GDALClose(hdsSrc);
- return ES_ERROR;
- }
- /* bandNum is 0-based */
- else if (band->data.offline.bandNum + 1 > nband) {
- rterror("rt_band_load_offline_data: Specified band %d not found in offline raster: %s", band->data.offline.bandNum, band->data.offline.path);
- GDALClose(hdsSrc);
- return ES_ERROR;
- }
-
- /* get raster's geotransform */
- rt_raster_get_geotransform_matrix(band->raster, gt);
- RASTER_DEBUGF(3, "Raster geotransform (%f, %f, %f, %f, %f, %f)",
- gt[0], gt[1], gt[2], gt[3], gt[4], gt[5]);
-
- /* get offline raster's geotransform */
- if (GDALGetGeoTransform(hdsSrc, ogt) != CE_None) {
- RASTER_DEBUG(4, "Using default geotransform matrix (0, 1, 0, 0, 0, -1)");
- ogt[0] = 0;
- ogt[1] = 1;
- ogt[2] = 0;
- ogt[3] = 0;
- ogt[4] = 0;
- ogt[5] = -1;
- }
- RASTER_DEBUGF(3, "Offline geotransform (%f, %f, %f, %f, %f, %f)",
- ogt[0], ogt[1], ogt[2], ogt[3], ogt[4], ogt[5]);
-
- /* are rasters aligned? */
- _rast = rt_raster_new(1, 1);
- rt_raster_set_geotransform_matrix(_rast, ogt);
- rt_raster_set_srid(_rast, band->raster->srid);
- err = rt_raster_same_alignment(band->raster, _rast, &aligned, NULL);
- rt_raster_destroy(_rast);
-
- if (err != ES_NONE) {
- rterror("rt_band_load_offline_data: Could not test alignment of in-db representation of out-db raster");
- GDALClose(hdsSrc);
- return ES_ERROR;
- }
- else if (!aligned) {
- rtwarn("The in-db representation of the out-db raster is not aligned. Band data may be incorrect");
- }
-
- /* get offsets */
- rt_raster_geopoint_to_cell(
- band->raster,
- ogt[0], ogt[3],
- &(offset[0]), &(offset[1]),
- NULL
- );
-
- RASTER_DEBUGF(4, "offsets: (%f, %f)", offset[0], offset[1]);
-
- /* create VRT dataset */
- hdsDst = VRTCreate(band->width, band->height);
- GDALSetGeoTransform(hdsDst, gt);
- /*
- GDALSetDescription(hdsDst, "/tmp/offline.vrt");
- */
-
- /* add band as simple sources */
- GDALAddBand(hdsDst, rt_util_pixtype_to_gdal_datatype(band->pixtype), NULL);
- hbandDst = (VRTSourcedRasterBandH) GDALGetRasterBand(hdsDst, 1);
-
- if (band->hasnodata)
- GDALSetRasterNoDataValue(hbandDst, band->nodataval);
-
- VRTAddSimpleSource(
- hbandDst, GDALGetRasterBand(hdsSrc, band->data.offline.bandNum + 1),
- abs(offset[0]), abs(offset[1]),
- band->width, band->height,
- 0, 0,
- band->width, band->height,
- "near", VRT_NODATA_UNSET
- );
-
- /* make sure VRT reflects all changes */
- VRTFlushCache(hdsDst);
-
- /* convert VRT dataset to rt_raster */
- _rast = rt_raster_from_gdal_dataset(hdsDst);
-
- GDALClose(hdsDst);
- /* XXX: need to find a way to clean up the GDALOpenShared datasets at end of transaction */
- /* GDALClose(hdsSrc); */
-
- if (_rast == NULL) {
- rterror("rt_band_load_offline_data: Cannot load data from offline raster: %s", band->data.offline.path);
- return ES_ERROR;
- }
-
- _band = rt_raster_get_band(_rast, 0);
- if (_band == NULL) {
- rterror("rt_band_load_offline_data: Cannot load data from offline raster: %s", band->data.offline.path);
- rt_raster_destroy(_rast);
- return ES_ERROR;
- }
-
- /* band->data.offline.mem not NULL, free first */
- if (band->data.offline.mem != NULL) {
- rtdealloc(band->data.offline.mem);
- band->data.offline.mem = NULL;
- }
-
- band->data.offline.mem = _band->data.mem;
-
- rtdealloc(_band); /* cannot use rt_band_destroy */
- rt_raster_destroy(_rast);
-
- return ES_NONE;
-}
-
-rt_pixtype
-rt_band_get_pixtype(rt_band band) {
-
- assert(NULL != band);
-
-
- return band->pixtype;
-}
-
-uint16_t
-rt_band_get_width(rt_band band) {
-
- assert(NULL != band);
-
-
- return band->width;
-}
-
-uint16_t
-rt_band_get_height(rt_band band) {
-
- assert(NULL != band);
-
-
- return band->height;
-}
-
-/* Get ownsdata flag */
-int
-rt_band_get_ownsdata_flag(rt_band band) {
- assert(NULL != band);
-
- return band->ownsdata ? 1 : 0;
-}
-
-/* set ownsdata flag */
-void
-rt_band_set_ownsdata_flag(rt_band band, int flag) {
- assert(NULL != band);
-
- band->ownsdata = flag ? 1 : 0;
-}
-
-#ifdef OPTIMIZE_SPACE
-
-/*
- * Set given number of bits of the given byte,
- * starting from given bitOffset (from the first)
- * to the given value.
- *
- * Examples:
- * char ch;
- * ch=0; setBits(&ch, 1, 1, 0) -> ch==8
- * ch=0; setBits(&ch, 3, 2, 1) -> ch==96 (0x60)
- *
- * Note that number of bits set must be <= 8-bitOffset
- *
- */
-static void
-setBits(char* ch, double val, int bits, int bitOffset) {
- char mask = 0xFF >> (8 - bits);
- char ival = val;
-
-
- assert(ch != NULL);
- assert(8 - bitOffset >= bits);
-
- RASTER_DEBUGF(4, "ival:%d bits:%d mask:%hhx bitoffset:%d\n",
- ival, bits, mask, bitOffset);
-
- /* clear all but significant bits from ival */
- ival &= mask;
-#if POSTGIS_RASTER_WARN_ON_TRUNCATION > 0
- if (ival != val) {
- rtwarn("Pixel value for %d-bits band got truncated"
- " from %g to %hhu", bits, val, ival);
- }
-#endif /* POSTGIS_RASTER_WARN_ON_TRUNCATION */
-
- RASTER_DEBUGF(4, " cleared ival:%hhx\n", ival);
-
-
- /* Shift ival so the significant bits start at
- * the first bit */
- ival <<= (8 - bitOffset - bits);
-
- RASTER_DEBUGF(4, " ival shifted:%hhx\n", ival);
- RASTER_DEBUGF(4, " ch:%hhx\n", *ch);
-
- /* clear first bits of target */
- *ch &= ~(mask << (8 - bits - bitOffset));
-
- RASTER_DEBUGF(4, " ch cleared:%hhx\n", *ch);
-
- /* Set the first bit of target */
- *ch |= ival;
-
- RASTER_DEBUGF(4, " ch ored:%hhx\n", *ch);
-
-}
-#endif /* OPTIMIZE_SPACE */
-
-int
-rt_band_get_hasnodata_flag(rt_band band) {
- assert(NULL != band);
-
- return band->hasnodata ? 1 : 0;
-}
-
-void
-rt_band_set_hasnodata_flag(rt_band band, int flag) {
-
- assert(NULL != band);
-
- band->hasnodata = (flag) ? 1 : 0;
-
- /* isnodata depends on hasnodata */
- if (!band->hasnodata && band->isnodata) {
- RASTER_DEBUG(3, "Setting isnodata to FALSE as band no longer has NODATA");
- band->isnodata = 0;
- }
-}
-
-rt_errorstate
-rt_band_set_isnodata_flag(rt_band band, int flag) {
- assert(NULL != band);
-
- if (!band->hasnodata) {
- /* silently permit setting isnodata flag to FALSE */
- if (!flag)
- band->isnodata = 0;
- else {
- rterror("rt_band_set_isnodata_flag: Cannot set isnodata flag as band has no NODATA");
- return ES_ERROR;
- }
- }
- else
- band->isnodata = (flag) ? 1 : 0;
-
- return ES_NONE;
-}
-
-int
-rt_band_get_isnodata_flag(rt_band band) {
- assert(NULL != band);
-
- if (band->hasnodata)
- return band->isnodata ? 1 : 0;
- else
- return 0;
-}
-
-/**
- * Set nodata value
- *
- * @param band : the band to set nodata value to
- * @param val : the nodata value
- * @param converted : if non-zero, value was truncated/clamped/coverted
- *
- * @return ES_NONE or ES_ERROR
- */
-rt_errorstate
-rt_band_set_nodata(rt_band band, double val, int *converted) {
- rt_pixtype pixtype = PT_END;
- int32_t checkvalint = 0;
- uint32_t checkvaluint = 0;
- float checkvalfloat = 0;
- double checkvaldouble = 0;
-
- assert(NULL != band);
-
- if (converted != NULL)
- *converted = 0;
-
- pixtype = band->pixtype;
-
- RASTER_DEBUGF(3, "rt_band_set_nodata: setting nodata value %g with band type %s", val, rt_pixtype_name(pixtype));
-
- /* return -1 on out of range */
- switch (pixtype) {
- case PT_1BB: {
- band->nodataval = rt_util_clamp_to_1BB(val);
- checkvalint = band->nodataval;
- break;
- }
- case PT_2BUI: {
- band->nodataval = rt_util_clamp_to_2BUI(val);
- checkvalint = band->nodataval;
- break;
- }
- case PT_4BUI: {
- band->nodataval = rt_util_clamp_to_4BUI(val);
- checkvalint = band->nodataval;
- break;
- }
- case PT_8BSI: {
- band->nodataval = rt_util_clamp_to_8BSI(val);
- checkvalint = band->nodataval;
- break;
- }
- case PT_8BUI: {
- band->nodataval = rt_util_clamp_to_8BUI(val);
- checkvalint = band->nodataval;
- break;
- }
- case PT_16BSI: {
- band->nodataval = rt_util_clamp_to_16BSI(val);
- checkvalint = band->nodataval;
- break;
- }
- case PT_16BUI: {
- band->nodataval = rt_util_clamp_to_16BUI(val);
- checkvalint = band->nodataval;
- break;
- }
- case PT_32BSI: {
- band->nodataval = rt_util_clamp_to_32BSI(val);
- checkvalint = band->nodataval;
- break;
- }
- case PT_32BUI: {
- band->nodataval = rt_util_clamp_to_32BUI(val);
- checkvaluint = band->nodataval;
- break;
- }
- case PT_32BF: {
- band->nodataval = rt_util_clamp_to_32F(val);
- checkvalfloat = band->nodataval;
- break;
- }
- case PT_64BF: {
- band->nodataval = val;
- checkvaldouble = band->nodataval;
- break;
- }
- default: {
- rterror("rt_band_set_nodata: Unknown pixeltype %d", pixtype);
- band->hasnodata = 0;
- return ES_ERROR;
- }
- }
-
- RASTER_DEBUGF(3, "rt_band_set_nodata: band->hasnodata = %d", band->hasnodata);
- RASTER_DEBUGF(3, "rt_band_set_nodata: band->nodataval = %f", band->nodataval);
- /* the nodata value was just set, so this band has NODATA */
- band->hasnodata = 1;
-
- /* also set isnodata flag to false */
- band->isnodata = 0;
-
- if (rt_util_dbl_trunc_warning(
- val,
- checkvalint, checkvaluint,
- checkvalfloat, checkvaldouble,
- pixtype
- ) && converted != NULL) {
- *converted = 1;
- }
-
- return ES_NONE;
-}
-
-/**
- * Set values of multiple pixels. Unlike rt_band_set_pixel,
- * values in vals are expected to be of the band's pixel type
- * as this function uses memcpy.
- *
- * It is important to be careful when using this function as
- * the number of values being set may exceed a pixel "row".
- * Remember that the band values are stored in a stream (1-D array)
- * regardless of what the raster's width and height might be.
- * So, setting a number of values may cross multiple pixel "rows".
- *
- * @param band : the band to set value to
- * @param x : X coordinate (0-based)
- * @param y : Y coordinate (0-based)
- * @param vals : the pixel values to apply
- * @param len : # of elements in vals
- *
- * @return ES_NONE on success, ES_ERROR on error
- */
-rt_errorstate
-rt_band_set_pixel_line(
- rt_band band,
- int x, int y,
- void *vals, uint32_t len
-) {
- rt_pixtype pixtype = PT_END;
- int size = 0;
- uint8_t *data = NULL;
- uint32_t offset = 0;
-
- assert(NULL != band);
- assert(vals != NULL && len > 0);
-
- RASTER_DEBUGF(3, "length of values = %d", len);
-
- if (band->offline) {
- rterror("rt_band_set_pixel_line not implemented yet for OFFDB bands");
- return ES_ERROR;
- }
-
- pixtype = band->pixtype;
- size = rt_pixtype_size(pixtype);
-
- if (
- x < 0 || x >= band->width ||
- y < 0 || y >= band->height
- ) {
- rterror("rt_band_set_pixel_line: Coordinates out of range (%d, %d) vs (%d, %d)", x, y, band->width, band->height);
- return ES_ERROR;
- }
-
- data = rt_band_get_data(band);
- offset = x + (y * band->width);
- RASTER_DEBUGF(4, "offset = %d", offset);
-
- /* make sure len of values to copy don't exceed end of data */
- if (len > (band->width * band->height) - offset) {
- rterror("rt_band_set_pixel_line: Could not apply pixels as values length exceeds end of data");
- return ES_ERROR;
- }
-
- switch (pixtype) {
- case PT_1BB:
- case PT_2BUI:
- case PT_4BUI:
- case PT_8BUI:
- case PT_8BSI: {
- uint8_t *ptr = data;
- ptr += offset;
- memcpy(ptr, vals, size * len);
- break;
- }
- case PT_16BUI: {
- uint16_t *ptr = (uint16_t *) data;
- ptr += offset;
- memcpy(ptr, vals, size * len);
- break;
- }
- case PT_16BSI: {
- int16_t *ptr = (int16_t *) data;
- ptr += offset;
- memcpy(ptr, vals, size * len);
- break;
- }
- case PT_32BUI: {
- uint32_t *ptr = (uint32_t *) data;
- ptr += offset;
- memcpy(ptr, vals, size * len);
- break;
- }
- case PT_32BSI: {
- int32_t *ptr = (int32_t *) data;
- ptr += offset;
- memcpy(ptr, vals, size * len);
- break;
- }
- case PT_32BF: {
- float *ptr = (float *) data;
- ptr += offset;
- memcpy(ptr, vals, size * len);
- break;
- }
- case PT_64BF: {
- double *ptr = (double *) data;
- ptr += offset;
- memcpy(ptr, vals, size * len);
- break;
- }
- default: {
- rterror("rt_band_set_pixel_line: Unknown pixeltype %d", pixtype);
- return ES_ERROR;
- }
- }
-
-#if POSTGIS_DEBUG_LEVEL > 0
- {
- double value;
- rt_band_get_pixel(band, x, y, &value, NULL);
- RASTER_DEBUGF(4, "pixel at (%d, %d) = %f", x, y, value);
- }
-#endif
-
- /* set band's isnodata flag to FALSE */
- if (rt_band_get_hasnodata_flag(band))
- rt_band_set_isnodata_flag(band, 0);
-
- return ES_NONE;
-}
-
-/**
- * Set single pixel's value
- *
- * @param band : the band to set value to
- * @param x : x ordinate (0-based)
- * @param y : y ordinate (0-based)
- * @param val : the pixel value
- * @param converted : (optional) non-zero if value truncated/clamped/converted
- *
- * @return ES_NONE on success, ES_ERROR on error
- */
-rt_errorstate
-rt_band_set_pixel(
- rt_band band,
- int x, int y,
- double val,
- int *converted
-) {
- rt_pixtype pixtype = PT_END;
- unsigned char* data = NULL;
- uint32_t offset = 0;
-
- int32_t checkvalint = 0;
- uint32_t checkvaluint = 0;
- float checkvalfloat = 0;
- double checkvaldouble = 0;
-
- assert(NULL != band);
-
- if (converted != NULL)
- *converted = 0;
-
- if (band->offline) {
- rterror("rt_band_set_pixel not implemented yet for OFFDB bands");
- return ES_ERROR;
- }
-
- pixtype = band->pixtype;
-
- if (
- x < 0 || x >= band->width ||
- y < 0 || y >= band->height
- ) {
- rterror("rt_band_set_pixel: Coordinates out of range");
- return ES_ERROR;
- }
-
- /* check that clamped value isn't clamped NODATA */
- if (band->hasnodata && pixtype != PT_64BF) {
- double newval;
- int corrected;
-
- rt_band_corrected_clamped_value(band, val, &newval, &corrected);
-
- if (corrected) {
-#if POSTGIS_RASTER_WARN_ON_TRUNCATION > 0
- rtwarn("Value for pixel %d x %d has been corrected as clamped value becomes NODATA", x, y);
-#endif
- val = newval;
-
- if (converted != NULL)
- *converted = 1;
- }
- }
-
- data = rt_band_get_data(band);
- offset = x + (y * band->width);
-
- switch (pixtype) {
- case PT_1BB: {
- data[offset] = rt_util_clamp_to_1BB(val);
- checkvalint = data[offset];
- break;
- }
- case PT_2BUI: {
- data[offset] = rt_util_clamp_to_2BUI(val);
- checkvalint = data[offset];
- break;
- }
- case PT_4BUI: {
- data[offset] = rt_util_clamp_to_4BUI(val);
- checkvalint = data[offset];
- break;
- }
- case PT_8BSI: {
- data[offset] = rt_util_clamp_to_8BSI(val);
- checkvalint = (int8_t) data[offset];
- break;
- }
- case PT_8BUI: {
- data[offset] = rt_util_clamp_to_8BUI(val);
- checkvalint = data[offset];
- break;
- }
- case PT_16BSI: {
- int16_t *ptr = (int16_t*) data; /* we assume correct alignment */
- ptr[offset] = rt_util_clamp_to_16BSI(val);
- checkvalint = (int16_t) ptr[offset];
- break;
- }
- case PT_16BUI: {
- uint16_t *ptr = (uint16_t*) data; /* we assume correct alignment */
- ptr[offset] = rt_util_clamp_to_16BUI(val);
- checkvalint = ptr[offset];
- break;
- }
- case PT_32BSI: {
- int32_t *ptr = (int32_t*) data; /* we assume correct alignment */
- ptr[offset] = rt_util_clamp_to_32BSI(val);
- checkvalint = (int32_t) ptr[offset];
- break;
- }
- case PT_32BUI: {
- uint32_t *ptr = (uint32_t*) data; /* we assume correct alignment */
- ptr[offset] = rt_util_clamp_to_32BUI(val);
- checkvaluint = ptr[offset];
- break;
- }
- case PT_32BF: {
- float *ptr = (float*) data; /* we assume correct alignment */
- ptr[offset] = rt_util_clamp_to_32F(val);
- checkvalfloat = ptr[offset];
- break;
- }
- case PT_64BF: {
- double *ptr = (double*) data; /* we assume correct alignment */
- ptr[offset] = val;
- checkvaldouble = ptr[offset];
- break;
- }
- default: {
- rterror("rt_band_set_pixel: Unknown pixeltype %d", pixtype);
- return ES_ERROR;
- }
- }
-
- /* If the stored value is not NODATA, reset the isnodata flag */
- if (!rt_band_clamped_value_is_nodata(band, val)) {
- RASTER_DEBUG(3, "Band has a value that is not NODATA. Setting isnodata to FALSE");
- band->isnodata = FALSE;
- }
-
- /* Overflow checking */
- if (rt_util_dbl_trunc_warning(
- val,
- checkvalint, checkvaluint,
- checkvalfloat, checkvaldouble,
- pixtype
- ) && converted != NULL) {
- *converted = 1;
- }
-
- return ES_NONE;
-}
-
-/**
- * Get values of multiple pixels. Unlike rt_band_get_pixel,
- * values in vals are of the band's pixel type so cannot be
- * assumed to be double. Function uses memcpy.
- *
- * It is important to be careful when using this function as
- * the number of values being fetched may exceed a pixel "row".
- * Remember that the band values are stored in a stream (1-D array)
- * regardless of what the raster's width and height might be.
- * So, getting a number of values may cross multiple pixel "rows".
- *
- * @param band : the band to get pixel value from
- * @param x : pixel column (0-based)
- * @param y : pixel row (0-based)
- * @param len : the number of pixels to get
- * @param **vals : the pixel values
- * @param *nvals : the number of pixel values being returned
- *
- * @return ES_NONE on success, ES_ERROR on error
- */
-rt_errorstate rt_band_get_pixel_line(
- rt_band band,
- int x, int y,
- uint16_t len,
- void **vals, uint16_t *nvals
-) {
- uint8_t *_vals = NULL;
- int pixsize = 0;
- uint8_t *data = NULL;
- uint32_t offset = 0;
- uint16_t _nvals = 0;
- int maxlen = 0;
- uint8_t *ptr = NULL;
-
- assert(NULL != band);
- assert(vals != NULL && nvals != NULL);
-
- /* initialize to no values */
- *nvals = 0;
-
- if (
- x < 0 || x >= band->width ||
- y < 0 || y >= band->height
- ) {
- rtwarn("Attempting to get pixel values with out of range raster coordinates: (%d, %d)", x, y);
- return ES_ERROR;
- }
-
- if (len < 1)
- return ES_NONE;
-
- data = rt_band_get_data(band);
- if (data == NULL) {
- rterror("rt_band_get_pixel_line: Cannot get band data");
- return ES_ERROR;
- }
-
- /* +1 for the nodata value */
- offset = x + (y * band->width);
- RASTER_DEBUGF(4, "offset = %d", offset);
-
- pixsize = rt_pixtype_size(band->pixtype);
- RASTER_DEBUGF(4, "pixsize = %d", pixsize);
-
- /* cap _nvals so that it doesn't overflow */
- _nvals = len;
- maxlen = band->width * band->height;
-
- if (((int) (offset + _nvals)) > maxlen) {
- _nvals = maxlen - offset;
- rtwarn("Limiting returning number values to %d", _nvals);
- }
- RASTER_DEBUGF(4, "_nvals = %d", _nvals);
-
- ptr = data + (offset * pixsize);
-
- _vals = rtalloc(_nvals * pixsize);
- if (_vals == NULL) {
- rterror("rt_band_get_pixel_line: Could not allocate memory for pixel values");
- return ES_ERROR;
- }
-
- /* copy pixels */
- memcpy(_vals, ptr, _nvals * pixsize);
-
- *vals = _vals;
- *nvals = _nvals;
-
- return ES_NONE;
-}
-
-/**
- * Get pixel value. If band's isnodata flag is TRUE, value returned
- * will be the band's NODATA value
- *
- * @param band : the band to set nodata value to
- * @param x : x ordinate (0-based)
- * @param y : x ordinate (0-based)
- * @param *value : pixel value
- * @param *nodata : 0 if pixel is not NODATA
- *
- * @return 0 on success, -1 on error (value out of valid range).
- */
-rt_errorstate
-rt_band_get_pixel(
- rt_band band,
- int x, int y,
- double *value,
- int *nodata
-) {
- rt_pixtype pixtype = PT_END;
- uint8_t* data = NULL;
- uint32_t offset = 0;
-
- assert(NULL != band);
- assert(NULL != value);
-
- /* set nodata to 0 */
- if (nodata != NULL)
- *nodata = 0;
-
- if (
- x < 0 || x >= band->width ||
- y < 0 || y >= band->height
- ) {
- rtwarn("Attempting to get pixel value with out of range raster coordinates: (%d, %d)", x, y);
- return ES_ERROR;
- }
-
- /* band is NODATA */
- if (band->isnodata) {
- RASTER_DEBUG(3, "Band's isnodata flag is TRUE. Returning NODATA value");
- *value = band->nodataval;
- if (nodata != NULL) *nodata = 1;
- return ES_NONE;
- }
-
- data = rt_band_get_data(band);
- if (data == NULL) {
- rterror("rt_band_get_pixel: Cannot get band data");
- return ES_ERROR;
- }
-
- /* +1 for the nodata value */
- offset = x + (y * band->width);
-
- pixtype = band->pixtype;
-
- switch (pixtype) {
- case PT_1BB:
-#ifdef OPTIMIZE_SPACE
- {
- int byteOffset = offset / 8;
- int bitOffset = offset % 8;
- data += byteOffset;
-
- /* Bit to set is bitOffset into data */
- *value = getBits(data, val, 1, bitOffset);
- break;
- }
-#endif
- case PT_2BUI:
-#ifdef OPTIMIZE_SPACE
- {
- int byteOffset = offset / 4;
- int bitOffset = offset % 4;
- data += byteOffset;
-
- /* Bits to set start at bitOffset into data */
- *value = getBits(data, val, 2, bitOffset);
- break;
- }
-#endif
- case PT_4BUI:
-#ifdef OPTIMIZE_SPACE
- {
- int byteOffset = offset / 2;
- int bitOffset = offset % 2;
- data += byteOffset;
-
- /* Bits to set start at bitOffset into data */
- *value = getBits(data, val, 2, bitOffset);
- break;
- }
-#endif
- case PT_8BSI: {
- int8_t val = data[offset];
- *value = val;
- break;
- }
- case PT_8BUI: {
- uint8_t val = data[offset];
- *value = val;
- break;
- }
- case PT_16BSI: {
- int16_t *ptr = (int16_t*) data; /* we assume correct alignment */
- *value = ptr[offset];
- break;
- }
- case PT_16BUI: {
- uint16_t *ptr = (uint16_t*) data; /* we assume correct alignment */
- *value = ptr[offset];
- break;
- }
- case PT_32BSI: {
- int32_t *ptr = (int32_t*) data; /* we assume correct alignment */
- *value = ptr[offset];
- break;
- }
- case PT_32BUI: {
- uint32_t *ptr = (uint32_t*) data; /* we assume correct alignment */
- *value = ptr[offset];
- break;
- }
- case PT_32BF: {
- float *ptr = (float*) data; /* we assume correct alignment */
- *value = ptr[offset];
- break;
- }
- case PT_64BF: {
- double *ptr = (double*) data; /* we assume correct alignment */
- *value = ptr[offset];
- break;
- }
- default: {
- rterror("rt_band_get_pixel: Unknown pixeltype %d", pixtype);
- return ES_ERROR;
- }
- }
-
- /* set NODATA flag */
- if (band->hasnodata && nodata != NULL) {
- if (rt_band_clamped_value_is_nodata(band, *value))
- *nodata = 1;
- }
-
- return ES_NONE;
-}
-
-/**
- * Get nearest pixel(s) with value (not NODATA) to specified pixel
- *
- * @param band : the band to get nearest pixel(s) from
- * @param x : the column of the pixel (0-based)
- * @param y : the line of the pixel (0-based)
- * @param distancex : the number of pixels around the specified pixel
- * along the X axis
- * @param distancey : the number of pixels around the specified pixel
- * along the Y axis
- * @param exclude_nodata_value : if non-zero, ignore nodata values
- * to check for pixels with value
- * @param npixels : return set of rt_pixel object or NULL
- *
- * @return -1 on error, otherwise the number of rt_pixel objects
- * in npixels
- */
-int rt_band_get_nearest_pixel(
- rt_band band,
- int x, int y,
- uint16_t distancex, uint16_t distancey,
- int exclude_nodata_value,
- rt_pixel *npixels
-) {
- rt_pixel npixel = NULL;
- int extent[4] = {0};
- int max_extent[4] = {0};
- int d0 = 0;
- int distance[2] = {0};
- uint32_t _d[2] = {0};
- uint32_t i = 0;
- uint32_t j = 0;
- uint32_t k = 0;
- int _max = 0;
- int _x = 0;
- int _y = 0;
- int *_min = NULL;
- double pixval = 0;
- double minval = 0;
- uint32_t count = 0;
- int isnodata = 0;
-
- int inextent = 0;
-
- assert(NULL != band);
- assert(NULL != npixels);
-
- RASTER_DEBUG(3, "Starting");
-
- /* process distance */
- distance[0] = distancex;
- distance[1] = distancey;
-
- /* no distance, means get nearest pixels and return */
- if (!distance[0] && !distance[1])
- d0 = 1;
-
- RASTER_DEBUGF(4, "Selected pixel: %d x %d", x, y);
- RASTER_DEBUGF(4, "Distances: %d x %d", distance[0], distance[1]);
-
- /* shortcuts if outside band extent */
- if (
- exclude_nodata_value && (
- (x < 0 || x > band->width) ||
- (y < 0 || y > band->height)
- )
- ) {
- /* no distances specified, jump to pixel close to extent */
- if (d0) {
- if (x < 0)
- x = -1;
- else if (x > band->width)
- x = band->width;
-
- if (y < 0)
- y = -1;
- else if (y > band->height)
- y = band->height;
-
- RASTER_DEBUGF(4, "Moved selected pixel: %d x %d", x, y);
- }
- /*
- distances specified
- if distances won't capture extent of band, return 0
- */
- else if (
- ((x < 0 && abs(x) > distance[0]) || (x - band->width >= distance[0])) ||
- ((y < 0 && abs(y) > distance[1]) || (y - band->height >= distance[1]))
- ) {
- RASTER_DEBUG(4, "No nearest pixels possible for provided pixel and distances");
- return 0;
- }
- }
-
- /* no NODATA, exclude is FALSE */
- if (!band->hasnodata)
- exclude_nodata_value = FALSE;
- /* band is NODATA and excluding NODATA */
- else if (exclude_nodata_value && band->isnodata) {
- RASTER_DEBUG(4, "No nearest pixels possible as band is NODATA and excluding NODATA values");
- return 0;
- }
-
- /* determine the maximum distance to prevent an infinite loop */
- if (d0) {
- int a, b;
-
- /* X axis */
- a = abs(x);
- b = abs(x - band->width);
-
- if (a > b)
- distance[0] = a;
- else
- distance[0] = b;
-
- /* Y axis */
- a = abs(y);
- b = abs(y - band->height);
- if (a > b)
- distance[1] = a;
- else
- distance[1] = b;
-
- RASTER_DEBUGF(4, "Maximum distances: %d x %d", distance[0], distance[1]);
- }
-
- /* minimum possible value for pixel type */
- minval = rt_pixtype_get_min_value(band->pixtype);
- RASTER_DEBUGF(4, "pixtype: %s", rt_pixtype_name(band->pixtype));
- RASTER_DEBUGF(4, "minval: %f", minval);
-
- /* set variables */
- count = 0;
- *npixels = NULL;
-
- /* maximum extent */
- max_extent[0] = x - distance[0]; /* min X */
- max_extent[1] = y - distance[1]; /* min Y */
- max_extent[2] = x + distance[0]; /* max X */
- max_extent[3] = y + distance[1]; /* max Y */
- RASTER_DEBUGF(4, "Maximum Extent: (%d, %d, %d, %d)",
- max_extent[0], max_extent[1], max_extent[2], max_extent[3]);
-
- _d[0] = 0;
- _d[1] = 0;
- do {
- _d[0]++;
- _d[1]++;
-
- extent[0] = x - _d[0]; /* min x */
- extent[1] = y - _d[1]; /* min y */
- extent[2] = x + _d[0]; /* max x */
- extent[3] = y + _d[1]; /* max y */
-
- RASTER_DEBUGF(4, "Processing distances: %d x %d", _d[0], _d[1]);
- RASTER_DEBUGF(4, "Extent: (%d, %d, %d, %d)",
- extent[0], extent[1], extent[2], extent[3]);
-
- for (i = 0; i < 2; i++) {
-
- /* by row */
- if (i < 1)
- _max = extent[2] - extent[0] + 1;
- /* by column */
- else
- _max = extent[3] - extent[1] + 1;
- _max = abs(_max);
-
- for (j = 0; j < 2; j++) {
- /* by row */
- if (i < 1) {
- _x = extent[0];
- _min = &_x;
-
- /* top row */
- if (j < 1)
- _y = extent[1];
- /* bottom row */
- else
- _y = extent[3];
- }
- /* by column */
- else {
- _y = extent[1] + 1;
- _min = &_y;
-
- /* left column */
- if (j < 1) {
- _x = extent[0];
- _max -= 2;
- }
- /* right column */
- else
- _x = extent[2];
- }
-
- RASTER_DEBUGF(4, "_min, _max: %d, %d", *_min, _max);
- for (k = 0; k < _max; k++) {
- /* check that _x and _y are not outside max extent */
- if (
- _x < max_extent[0] || _x > max_extent[2] ||
- _y < max_extent[1] || _y > max_extent[3]
- ) {
- (*_min)++;
- continue;
- }
-
- /* outside band extent, set to NODATA */
- if (
- (_x < 0 || _x >= band->width) ||
- (_y < 0 || _y >= band->height)
- ) {
- /* no NODATA, set to minimum possible value */
- if (!band->hasnodata)
- pixval = minval;
- /* has NODATA, use NODATA */
- else
- pixval = band->nodataval;
- RASTER_DEBUGF(4, "NODATA pixel outside band extent: (x, y, val) = (%d, %d, %f)", _x, _y, pixval);
- inextent = 0;
- isnodata = 1;
- }
- else {
- if (rt_band_get_pixel(
- band,
- _x, _y,
- &pixval,
- &isnodata
- ) != ES_NONE) {
- rterror("rt_band_get_nearest_pixel: Could not get pixel value");
- if (count) rtdealloc(*npixels);
- return -1;
- }
- RASTER_DEBUGF(4, "Pixel: (x, y, val) = (%d, %d, %f)", _x, _y, pixval);
- inextent = 1;
- }
-
- /* use pixval? */
- if (!exclude_nodata_value || (exclude_nodata_value && !isnodata)) {
- /* add pixel to result set */
- RASTER_DEBUGF(4, "Adding pixel to set of nearest pixels: (x, y, val) = (%d, %d, %f)", _x, _y, pixval);
- count++;
-
- if (*npixels == NULL)
- *npixels = (rt_pixel) rtalloc(sizeof(struct rt_pixel_t) * count);
- else
- *npixels = (rt_pixel) rtrealloc(*npixels, sizeof(struct rt_pixel_t) * count);
- if (*npixels == NULL) {
- rterror("rt_band_get_nearest_pixel: Could not allocate memory for nearest pixel(s)");
- return -1;
- }
-
- npixel = &((*npixels)[count - 1]);
- npixel->x = _x;
- npixel->y = _y;
- npixel->value = pixval;
-
- /* special case for when outside band extent */
- if (!inextent && !band->hasnodata)
- npixel->nodata = 1;
- else
- npixel->nodata = 0;
- }
-
- (*_min)++;
- }
- }
- }
-
- /* distance threshholds met */
- if (_d[0] >= distance[0] && _d[1] >= distance[1])
- break;
- else if (d0 && count)
- break;
- }
- while (1);
-
- RASTER_DEBUGF(3, "Nearest pixels in return: %d", count);
-
- return count;
-}
-
-/**
- * Search band for pixel(s) with search values
- *
- * @param band : the band to query for minimum and maximum pixel values
- * @param exclude_nodata_value : if non-zero, ignore nodata values
- * @param searchset : array of values to count
- * @param searchcount : the number of search values
- * @param pixels : pixels with the search value
- *
- * @return -1 on error, otherwise number of pixels
- */
-int
-rt_band_get_pixel_of_value(
- rt_band band, int exclude_nodata_value,
- double *searchset, int searchcount,
- rt_pixel *pixels
-) {
- int x;
- int y;
- int i;
- double pixval;
- int err;
- int count = 0;
- int isnodata = 0;
- int isequal = 0;
-
- rt_pixel pixel = NULL;
-
- assert(NULL != band);
- assert(NULL != pixels);
- assert(NULL != searchset && searchcount > 0);
-
- if (!band->hasnodata)
- exclude_nodata_value = FALSE;
- /* band is NODATA and exclude_nodata_value = TRUE, nothing to search */
- else if (exclude_nodata_value && band->isnodata) {
- RASTER_DEBUG(4, "Pixels cannot be searched as band is NODATA and excluding NODATA values");
- return 0;
- }
-
- for (x = 0; x < band->width; x++) {
- for (y = 0; y < band->height; y++) {
- err = rt_band_get_pixel(band, x, y, &pixval, &isnodata);
- if (err != ES_NONE) {
- rterror("rt_band_get_pixel_of_value: Cannot get band pixel");
- return -1;
- }
- else if (exclude_nodata_value && isnodata)
- continue;
-
- for (i = 0; i < searchcount; i++) {
- if (rt_pixtype_compare_clamped_values(band->pixtype, searchset[i], pixval, &isequal) != ES_NONE) {
- continue;
- }
-
- if (FLT_NEQ(pixval, searchset[i]) || !isequal)
- continue;
-
- /* match found */
- count++;
- if (*pixels == NULL)
- *pixels = (rt_pixel) rtalloc(sizeof(struct rt_pixel_t) * count);
- else
- *pixels = (rt_pixel) rtrealloc(*pixels, sizeof(struct rt_pixel_t) * count);
- if (*pixels == NULL) {
- rterror("rt_band_get_pixel_of_value: Could not allocate memory for pixel(s)");
- return -1;
- }
-
- pixel = &((*pixels)[count - 1]);
- pixel->x = x;
- pixel->y = y;
- pixel->nodata = 0;
- pixel->value = pixval;
- }
- }
- }
-
- return count;
-}
-
-/**
- * Get NODATA value
- *
- * @param band : the band whose NODATA value will be returned
- * @param nodata : the band's NODATA value
- *
- * @return ES_NONE or ES_ERROR
- */
-rt_errorstate
-rt_band_get_nodata(rt_band band, double *nodata) {
- assert(NULL != band);
- assert(NULL != nodata);
-
- *nodata = band->nodataval;
-
- if (!band->hasnodata) {
- rterror("rt_band_get_nodata: Band has no NODATA value");
- return ES_ERROR;
- }
-
- return ES_NONE;
-}
-
-double
-rt_band_get_min_value(rt_band band) {
- assert(NULL != band);
-
- return rt_pixtype_get_min_value(band->pixtype);
-}
-
-int
-rt_band_check_is_nodata(rt_band band) {
- int i, j, err;
- double pxValue;
- int isnodata = 0;
-
- assert(NULL != band);
-
- /* Check if band has nodata value */
- if (!band->hasnodata) {
- RASTER_DEBUG(3, "Band has no NODATA value");
- band->isnodata = FALSE;
- return FALSE;
- }
-
- pxValue = band->nodataval;
-
- /* Check all pixels */
- for (i = 0; i < band->width; i++) {
- for (j = 0; j < band->height; j++) {
- err = rt_band_get_pixel(band, i, j, &pxValue, &isnodata);
- if (err != ES_NONE) {
- rterror("rt_band_check_is_nodata: Cannot get band pixel");
- return FALSE;
- }
- else if (!isnodata) {
- band->isnodata = FALSE;
- return FALSE;
- }
- }
- }
-
- band->isnodata = TRUE;
- return TRUE;
-}
-
-/**
- * Compare clamped value to band's clamped NODATA value.
- *
- * @param band : the band whose NODATA value will be used for comparison
- * @param val : the value to compare to the NODATA value
- *
- * @return 2 if unclamped value is unclamped NODATA
- * 1 if clamped value is clamped NODATA
- * 0 if clamped value is NOT clamped NODATA
- */
-int
-rt_band_clamped_value_is_nodata(rt_band band, double val) {
- int isequal = 0;
-
- assert(NULL != band);
-
- /* no NODATA, so never equal */
- if (!band->hasnodata)
- return 0;
-
- /* value is exactly NODATA */
- if (FLT_EQ(val, band->nodataval))
- return 2;
-
- /* ignore error from rt_pixtype_compare_clamped_values */
- rt_pixtype_compare_clamped_values(
- band->pixtype,
- val, band->nodataval,
- &isequal
- );
-
- return isequal ? 1 : 0;
-}
-
-/**
- * Correct value when clamped value is equal to clamped NODATA value.
- * Correction does NOT occur if unclamped value is exactly unclamped
- * NODATA value.
- *
- * @param band : the band whose NODATA value will be used for comparison
- * @param val : the value to compare to the NODATA value and correct
- * @param *newval : pointer to corrected value
- * @param *corrected : (optional) non-zero if val was corrected
- *
- * @return ES_NONE if success, ES_ERROR if error
- */
-rt_errorstate
-rt_band_corrected_clamped_value(
- rt_band band,
- double val,
- double *newval, int *corrected
-) {
- double minval = 0.;
-
- assert(NULL != band);
- assert(NULL != newval);
-
- if (corrected != NULL)
- *corrected = 0;
-
- /* no need to correct if clamped values IS NOT clamped NODATA */
- if (rt_band_clamped_value_is_nodata(band, val) != 1) {
- *newval = val;
- return ES_NONE;
- }
-
- minval = rt_pixtype_get_min_value(band->pixtype);
- *newval = val;
-
- switch (band->pixtype) {
- case PT_1BB:
- *newval = !band->nodataval;
- break;
- case PT_2BUI:
- if (rt_util_clamp_to_2BUI(val) == rt_util_clamp_to_2BUI(minval))
- (*newval)++;
- else
- (*newval)--;
- break;
- case PT_4BUI:
- if (rt_util_clamp_to_4BUI(val) == rt_util_clamp_to_4BUI(minval))
- (*newval)++;
- else
- (*newval)--;
- break;
- case PT_8BSI:
- if (rt_util_clamp_to_8BSI(val) == rt_util_clamp_to_8BSI(minval))
- (*newval)++;
- else
- (*newval)--;
- break;
- case PT_8BUI:
- if (rt_util_clamp_to_8BUI(val) == rt_util_clamp_to_8BUI(minval))
- (*newval)++;
- else
- (*newval)--;
- break;
- case PT_16BSI:
- if (rt_util_clamp_to_16BSI(val) == rt_util_clamp_to_16BSI(minval))
- (*newval)++;
- else
- (*newval)--;
- break;
- case PT_16BUI:
- if (rt_util_clamp_to_16BUI(val) == rt_util_clamp_to_16BUI(minval))
- (*newval)++;
- else
- (*newval)--;
- break;
- case PT_32BSI:
- if (rt_util_clamp_to_32BSI(val) == rt_util_clamp_to_32BSI(minval))
- (*newval)++;
- else
- (*newval)--;
- break;
- case PT_32BUI:
- if (rt_util_clamp_to_32BUI(val) == rt_util_clamp_to_32BUI(minval))
- (*newval)++;
- else
- (*newval)--;
- break;
- case PT_32BF:
- if (FLT_EQ(rt_util_clamp_to_32F(val), rt_util_clamp_to_32F(minval)))
- *newval += FLT_EPSILON;
- else
- *newval -= FLT_EPSILON;
- break;
- case PT_64BF:
- break;
- default:
- rterror("rt_band_corrected_clamped_value: Unknown pixeltype %d", band->pixtype);
- return ES_ERROR;
- }
-
- if (corrected != NULL)
- *corrected = 1;
-
- return ES_NONE;
-}
-
-/**
- * Compute summary statistics for a band
- *
- * @param band : the band to query for summary stats
- * @param exclude_nodata_value : if non-zero, ignore nodata values
- * @param sample : percentage of pixels to sample
- * @param inc_vals : flag to include values in return struct
- * @param cK : number of pixels counted thus far in coverage
- * @param cM : M component of 1-pass stddev for coverage
- * @param cQ : Q component of 1-pass stddev for coverage
- *
- * @return the summary statistics for a band or NULL
- */
-rt_bandstats
-rt_band_get_summary_stats(
- rt_band band,
- int exclude_nodata_value, double sample, int inc_vals,
- uint64_t *cK, double *cM, double *cQ
-) {
- uint32_t x = 0;
- uint32_t y = 0;
- uint32_t z = 0;
- uint32_t offset = 0;
- uint32_t diff = 0;
- int rtn;
- int hasnodata = FALSE;
- double nodata = 0;
- double *values = NULL;
- double value;
- int isnodata = 0;
- rt_bandstats stats = NULL;
-
- uint32_t do_sample = 0;
- uint32_t sample_size = 0;
- uint32_t sample_per = 0;
- uint32_t sample_int = 0;
- uint32_t i = 0;
- uint32_t j = 0;
- double sum = 0;
- uint32_t k = 0;
- double M = 0;
- double Q = 0;
-
-#if POSTGIS_DEBUG_LEVEL > 0
- clock_t start, stop;
- double elapsed = 0;
-#endif
-
- RASTER_DEBUG(3, "starting");
-#if POSTGIS_DEBUG_LEVEL > 0
- start = clock();
-#endif
-
- assert(NULL != band);
-
- /* band is empty (width < 1 || height < 1) */
- if (band->width < 1 || band->height < 1) {
- stats = (rt_bandstats) rtalloc(sizeof(struct rt_bandstats_t));
- if (NULL == stats) {
- rterror("rt_band_get_summary_stats: Could not allocate memory for stats");
- return NULL;
- }
-
- rtwarn("Band is empty as width and/or height is 0");
-
- stats->sample = 1;
- stats->sorted = 0;
- stats->values = NULL;
- stats->count = 0;
- stats->min = stats->max = 0;
- stats->sum = 0;
- stats->mean = 0;
- stats->stddev = -1;
-
- return stats;
- }
-
- hasnodata = rt_band_get_hasnodata_flag(band);
- if (hasnodata != FALSE)
- rt_band_get_nodata(band, &nodata);
- else
- exclude_nodata_value = 0;
-
- RASTER_DEBUGF(3, "nodata = %f", nodata);
- RASTER_DEBUGF(3, "hasnodata = %d", hasnodata);
- RASTER_DEBUGF(3, "exclude_nodata_value = %d", exclude_nodata_value);
-
- /* entire band is nodata */
- if (rt_band_get_isnodata_flag(band) != FALSE) {
- stats = (rt_bandstats) rtalloc(sizeof(struct rt_bandstats_t));
- if (NULL == stats) {
- rterror("rt_band_get_summary_stats: Could not allocate memory for stats");
- return NULL;
- }
-
- stats->sample = 1;
- stats->sorted = 0;
- stats->values = NULL;
-
- if (exclude_nodata_value) {
- rtwarn("All pixels of band have the NODATA value");
-
- stats->count = 0;
- stats->min = stats->max = 0;
- stats->sum = 0;
- stats->mean = 0;
- stats->stddev = -1;
- }
- else {
- stats->count = band->width * band->height;
- stats->min = stats->max = nodata;
- stats->sum = stats->count * nodata;
- stats->mean = nodata;
- stats->stddev = 0;
- }
-
- return stats;
- }
-
- /* clamp percentage */
- if (
- (sample < 0 || FLT_EQ(sample, 0.0)) ||
- (sample > 1 || FLT_EQ(sample, 1.0))
- ) {
- do_sample = 0;
- sample = 1;
- }
- else
- do_sample = 1;
- RASTER_DEBUGF(3, "do_sample = %d", do_sample);
-
- /* sample all pixels */
- if (!do_sample) {
- sample_size = band->width * band->height;
- sample_per = band->height;
- }
- /*
- randomly sample a percentage of available pixels
- sampling method is known as
- "systematic random sample without replacement"
- */
- else {
- sample_size = round((band->width * band->height) * sample);
- sample_per = round(sample_size / band->width);
- if (sample_per < 1)
- sample_per = 1;
- sample_int = round(band->height / sample_per);
- srand(time(NULL));
- }
-
- RASTER_DEBUGF(3, "sampling %d of %d available pixels w/ %d per set"
- , sample_size, (band->width * band->height), sample_per);
-
- if (inc_vals) {
- values = rtalloc(sizeof(double) * sample_size);
- if (NULL == values) {
- rtwarn("Could not allocate memory for values");
- inc_vals = 0;
- }
- }
-
- /* initialize stats */
- stats = (rt_bandstats) rtalloc(sizeof(struct rt_bandstats_t));
- if (NULL == stats) {
- rterror("rt_band_get_summary_stats: Could not allocate memory for stats");
- return NULL;
- }
- stats->sample = sample;
- stats->count = 0;
- stats->sum = 0;
- stats->mean = 0;
- stats->stddev = -1;
- stats->min = stats->max = 0;
- stats->values = NULL;
- stats->sorted = 0;
-
- for (x = 0, j = 0, k = 0; x < band->width; x++) {
- y = -1;
- diff = 0;
-
- for (i = 0, z = 0; i < sample_per; i++) {
- if (!do_sample)
- y = i;
- else {
- offset = (rand() % sample_int) + 1;
- y += diff + offset;
- diff = sample_int - offset;
- }
- RASTER_DEBUGF(5, "(x, y, z) = (%d, %d, %d)", x, y, z);
- if (y >= band->height || z > sample_per) break;
-
- rtn = rt_band_get_pixel(band, x, y, &value, &isnodata);
-
- j++;
- if (rtn == ES_NONE && (!exclude_nodata_value || (exclude_nodata_value && !isnodata))) {
-
- /* inc_vals set, collect pixel values */
- if (inc_vals) values[k] = value;
-
- /* average */
- k++;
- sum += value;
-
- /*
- one-pass standard deviation
- http://www.eecs.berkeley.edu/~mhoemmen/cs194/Tutorials/variance.pdf
- */
- if (k == 1) {
- Q = 0;
- M = value;
- }
- else {
- Q += (((k - 1) * pow(value - M, 2)) / k);
- M += ((value - M ) / k);
- }
-
- /* coverage one-pass standard deviation */
- if (NULL != cK) {
- (*cK)++;
- if (*cK == 1) {
- *cQ = 0;
- *cM = value;
- }
- else {
- *cQ += (((*cK - 1) * pow(value - *cM, 2)) / *cK);
- *cM += ((value - *cM ) / *cK);
- }
- }
-
- /* min/max */
- if (stats->count < 1) {
- stats->count = 1;
- stats->min = stats->max = value;
- }
- else {
- if (value < stats->min)
- stats->min = value;
- if (value > stats->max)
- stats->max = value;
- }
-
- }
-
- z++;
- }
- }
-
- RASTER_DEBUG(3, "sampling complete");
-
- stats->count = k;
- if (k > 0) {
- if (inc_vals) {
- /* free unused memory */
- if (sample_size != k) {
- values = rtrealloc(values, k * sizeof(double));
- }
-
- stats->values = values;
- }
-
- stats->sum = sum;
- stats->mean = sum / k;
-
- /* standard deviation */
- if (!do_sample)
- stats->stddev = sqrt(Q / k);
- /* sample deviation */
- else {
- if (k < 2)
- stats->stddev = -1;
- else
- stats->stddev = sqrt(Q / (k - 1));
- }
- }
- /* inc_vals thus values allocated but not used */
- else if (inc_vals)
- rtdealloc(values);
-
- /* if do_sample is one */
- if (do_sample && k < 1)
- rtwarn("All sampled pixels of band have the NODATA value");
-
-#if POSTGIS_DEBUG_LEVEL > 0
- stop = clock();
- elapsed = ((double) (stop - start)) / CLOCKS_PER_SEC;
- RASTER_DEBUGF(3, "(time, count, mean, stddev, min, max) = (%0.4f, %d, %f, %f, %f, %f)",
- elapsed, stats->count, stats->mean, stats->stddev, stats->min, stats->max);
-#endif
-
- RASTER_DEBUG(3, "done");
- return stats;
-}
-
-/**
- * Count the distribution of data
- *
- * @param stats : a populated stats struct for processing
- * @param bin_count : the number of bins to group the data by
- * @param bin_width : the width of each bin as an array
- * @param bin_width_count : number of values in bin_width
- * @param right : evaluate bins by (a,b] rather than default [a,b)
- * @param min : user-defined minimum value of the histogram
- * a value less than the minimum value is not counted in any bins
- * if min = max, min and max are not used
- * @param max : user-defined maximum value of the histogram
- * a value greater than the max value is not counted in any bins
- * if min = max, min and max are not used
- * @param rtn_count : set to the number of bins being returned
- *
- * @return the histogram of the data or NULL
- */
-rt_histogram
-rt_band_get_histogram(
- rt_bandstats stats,
- int bin_count, double *bin_width, int bin_width_count,
- int right, double min, double max,
- uint32_t *rtn_count
-) {
- rt_histogram bins = NULL;
- int init_width = 0;
- int i;
- int j;
- double tmp;
- double value;
- int sum = 0;
- double qmin;
- double qmax;
-
-#if POSTGIS_DEBUG_LEVEL > 0
- clock_t start, stop;
- double elapsed = 0;
-#endif
-
- RASTER_DEBUG(3, "starting");
-#if POSTGIS_DEBUG_LEVEL > 0
- start = clock();
-#endif
-
- assert(NULL != stats);
- assert(NULL != rtn_count);
-
- if (stats->count < 1 || NULL == stats->values) {
- rterror("rt_util_get_histogram: rt_bandstats object has no value");
- return NULL;
- }
-
- /* bin width must be positive numbers and not zero */
- if (NULL != bin_width && bin_width_count > 0) {
- for (i = 0; i < bin_width_count; i++) {
- if (bin_width[i] < 0 || FLT_EQ(bin_width[i], 0.0)) {
- rterror("rt_util_get_histogram: bin_width element is less than or equal to zero");
- return NULL;
- }
- }
- }
-
- /* ignore min and max parameters */
- if (FLT_EQ(max, min)) {
- qmin = stats->min;
- qmax = stats->max;
- }
- else {
- qmin = min;
- qmax = max;
- if (qmin > qmax) {
- qmin = max;
- qmax = min;
- }
- }
-
- /* # of bins not provided */
- if (bin_count <= 0) {
- /*
- determine # of bins
- http://en.wikipedia.org/wiki/Histogram
-
- all computed bins are assumed to have equal width
- */
- /* Square-root choice for stats->count < 30 */
- if (stats->count < 30)
- bin_count = ceil(sqrt(stats->count));
- /* Sturges' formula for stats->count >= 30 */
- else
- bin_count = ceil(log2((double) stats->count) + 1.);
-
- /* bin_width_count provided and bin_width has value */
- if (bin_width_count > 0 && NULL != bin_width) {
- /* user has defined something specific */
- if (bin_width_count > bin_count)
- bin_count = bin_width_count;
- else if (bin_width_count > 1) {
- tmp = 0;
- for (i = 0; i < bin_width_count; i++) tmp += bin_width[i];
- bin_count = ceil((qmax - qmin) / tmp) * bin_width_count;
- }
- else
- bin_count = ceil((qmax - qmin) / bin_width[0]);
- }
- /* set bin width count to zero so that one can be calculated */
- else {
- bin_width_count = 0;
- }
- }
-
- /* min and max the same */
- if (FLT_EQ(qmax, qmin)) bin_count = 1;
-
- RASTER_DEBUGF(3, "bin_count = %d", bin_count);
-
- /* bin count = 1, all values are in one bin */
- if (bin_count < 2) {
- bins = rtalloc(sizeof(struct rt_histogram_t));
- if (NULL == bins) {
- rterror("rt_util_get_histogram: Could not allocate memory for histogram");
- return NULL;
- }
-
- bins->count = stats->count;
- bins->percent = -1;
- bins->min = qmin;
- bins->max = qmax;
- bins->inc_min = bins->inc_max = 1;
-
- *rtn_count = bin_count;
- return bins;
- }
-
- /* establish bin width */
- if (bin_width_count == 0) {
- bin_width_count = 1;
-
- /* bin_width unallocated */
- if (NULL == bin_width) {
- bin_width = rtalloc(sizeof(double));
- if (NULL == bin_width) {
- rterror("rt_util_get_histogram: Could not allocate memory for bin widths");
- return NULL;
- }
- init_width = 1;
- }
-
- bin_width[0] = (qmax - qmin) / bin_count;
- }
-
- /* initialize bins */
- bins = rtalloc(bin_count * sizeof(struct rt_histogram_t));
- if (NULL == bins) {
- rterror("rt_util_get_histogram: Could not allocate memory for histogram");
- if (init_width) rtdealloc(bin_width);
- return NULL;
- }
- if (!right)
- tmp = qmin;
- else
- tmp = qmax;
- for (i = 0; i < bin_count;) {
- for (j = 0; j < bin_width_count; j++) {
- bins[i].count = 0;
- bins->percent = -1;
-
- if (!right) {
- bins[i].min = tmp;
- tmp += bin_width[j];
- bins[i].max = tmp;
-
- bins[i].inc_min = 1;
- bins[i].inc_max = 0;
- }
- else {
- bins[i].max = tmp;
- tmp -= bin_width[j];
- bins[i].min = tmp;
-
- bins[i].inc_min = 0;
- bins[i].inc_max = 1;
- }
-
- i++;
- }
- }
- if (!right) {
- bins[bin_count - 1].inc_max = 1;
-
- /* align last bin to the max value */
- if (bins[bin_count - 1].max < qmax)
- bins[bin_count - 1].max = qmax;
- }
- else {
- bins[bin_count - 1].inc_min = 1;
-
- /* align first bin to the min value */
- if (bins[bin_count - 1].min > qmin)
- bins[bin_count - 1].min = qmin;
- }
-
- /* process the values */
- for (i = 0; i < stats->count; i++) {
- value = stats->values[i];
-
- /* default, [a, b) */
- if (!right) {
- for (j = 0; j < bin_count; j++) {
- if (
- (!bins[j].inc_max && value < bins[j].max) || (
- bins[j].inc_max && (
- (value < bins[j].max) ||
- FLT_EQ(value, bins[j].max)
- )
- )
- ) {
- bins[j].count++;
- sum++;
- break;
- }
- }
- }
- /* (a, b] */
- else {
- for (j = 0; j < bin_count; j++) {
- if (
- (!bins[j].inc_min && value > bins[j].min) || (
- bins[j].inc_min && (
- (value > bins[j].min) ||
- FLT_EQ(value, bins[j].min)
- )
- )
- ) {
- bins[j].count++;
- sum++;
- break;
- }
- }
- }
- }
-
- for (i = 0; i < bin_count; i++) {
- bins[i].percent = ((double) bins[i].count) / sum;
- }
-
-#if POSTGIS_DEBUG_LEVEL > 0
- stop = clock();
- elapsed = ((double) (stop - start)) / CLOCKS_PER_SEC;
- RASTER_DEBUGF(3, "elapsed time = %0.4f", elapsed);
-
- for (j = 0; j < bin_count; j++) {
- RASTER_DEBUGF(5, "(min, max, inc_min, inc_max, count, sum, percent) = (%f, %f, %d, %d, %d, %d, %f)",
- bins[j].min, bins[j].max, bins[j].inc_min, bins[j].inc_max, bins[j].count, sum, bins[j].percent);
- }
-#endif
-
- if (init_width) rtdealloc(bin_width);
- *rtn_count = bin_count;
- RASTER_DEBUG(3, "done");
- return bins;
-}
-
-/**
- * Compute the default set of or requested quantiles for a set of data
- * the quantile formula used is same as Excel and R default method
- *
- * @param stats : a populated stats struct for processing
- * @param quantiles : the quantiles to be computed
- * @param quantiles_count : the number of quantiles to be computed
- * @param rtn_count : set to the number of quantiles being returned
- *
- * @return the default set of or requested quantiles for a band or NULL
- */
-rt_quantile
-rt_band_get_quantiles(
- rt_bandstats stats,
- double *quantiles, int quantiles_count,
- uint32_t *rtn_count
-) {
- rt_quantile rtn;
- int init_quantiles = 0;
- int i = 0;
- double h;
- int hl;
-
-#if POSTGIS_DEBUG_LEVEL > 0
- clock_t start, stop;
- double elapsed = 0;
-#endif
-
- RASTER_DEBUG(3, "starting");
-#if POSTGIS_DEBUG_LEVEL > 0
- start = clock();
-#endif
-
- assert(NULL != stats);
- assert(NULL != rtn_count);
-
- if (stats->count < 1 || NULL == stats->values) {
- rterror("rt_band_get_quantiles: rt_bandstats object has no value");
- return NULL;
- }
-
- /* quantiles not provided */
- if (NULL == quantiles) {
- /* quantile count not specified, default to quartiles */
- if (quantiles_count < 2)
- quantiles_count = 5;
-
- quantiles = rtalloc(sizeof(double) * quantiles_count);
- init_quantiles = 1;
- if (NULL == quantiles) {
- rterror("rt_band_get_quantiles: Could not allocate memory for quantile input");
- return NULL;
- }
-
- quantiles_count--;
- for (i = 0; i <= quantiles_count; i++)
- quantiles[i] = ((double) i) / quantiles_count;
- quantiles_count++;
- }
-
- /* check quantiles */
- for (i = 0; i < quantiles_count; i++) {
- if (quantiles[i] < 0. || quantiles[i] > 1.) {
- rterror("rt_band_get_quantiles: Quantile value not between 0 and 1");
- if (init_quantiles) rtdealloc(quantiles);
- return NULL;
- }
- }
- quicksort(quantiles, quantiles + quantiles_count - 1);
-
- /* initialize rt_quantile */
- rtn = rtalloc(sizeof(struct rt_quantile_t) * quantiles_count);
- if (NULL == rtn) {
- rterror("rt_band_get_quantiles: Could not allocate memory for quantile output");
- if (init_quantiles) rtdealloc(quantiles);
- return NULL;
- }
-
- /* sort values */
- if (!stats->sorted) {
- quicksort(stats->values, stats->values + stats->count - 1);
- stats->sorted = 1;
- }
-
- /*
- make quantiles
-
- formula is that used in R (method 7) and Excel from
- http://en.wikipedia.org/wiki/Quantile
- */
- for (i = 0; i < quantiles_count; i++) {
- rtn[i].quantile = quantiles[i];
-
- h = ((stats->count - 1.) * quantiles[i]) + 1.;
- hl = floor(h);
-
- /* h greater than hl, do full equation */
- if (h > hl)
- rtn[i].value = stats->values[hl - 1] + ((h - hl) * (stats->values[hl] - stats->values[hl - 1]));
- /* shortcut as second part of equation is zero */
- else
- rtn[i].value = stats->values[hl - 1];
- }
-
-#if POSTGIS_DEBUG_LEVEL > 0
- stop = clock();
- elapsed = ((double) (stop - start)) / CLOCKS_PER_SEC;
- RASTER_DEBUGF(3, "elapsed time = %0.4f", elapsed);
-#endif
-
- *rtn_count = quantiles_count;
- if (init_quantiles) rtdealloc(quantiles);
- RASTER_DEBUG(3, "done");
- return rtn;
-}
-
-static struct quantile_llist_element *quantile_llist_search(
- struct quantile_llist_element *element,
- double needle
-) {
- if (NULL == element)
- return NULL;
- else if (FLT_NEQ(needle, element->value)) {
- if (NULL != element->next)
- return quantile_llist_search(element->next, needle);
- else
- return NULL;
- }
- else
- return element;
-}
-
-static struct quantile_llist_element *quantile_llist_insert(
- struct quantile_llist_element *element,
- double value,
- uint32_t *idx
-) {
- struct quantile_llist_element *qle = NULL;
-
- if (NULL == element) {
- qle = rtalloc(sizeof(struct quantile_llist_element));
- RASTER_DEBUGF(4, "qle @ %p is only element in list", qle);
- if (NULL == qle) return NULL;
-
- qle->value = value;
- qle->count = 1;
-
- qle->prev = NULL;
- qle->next = NULL;
-
- if (NULL != idx) *idx = 0;
- return qle;
- }
- else if (value > element->value) {
- if (NULL != idx) *idx += 1;
- if (NULL != element->next)
- return quantile_llist_insert(element->next, value, idx);
- /* insert as last element in list */
- else {
- qle = rtalloc(sizeof(struct quantile_llist_element));
- RASTER_DEBUGF(4, "insert qle @ %p as last element", qle);
- if (NULL == qle) return NULL;
-
- qle->value = value;
- qle->count = 1;
-
- qle->prev = element;
- qle->next = NULL;
- element->next = qle;
-
- return qle;
- }
- }
- /* insert before current element */
- else {
- qle = rtalloc(sizeof(struct quantile_llist_element));
- RASTER_DEBUGF(4, "insert qle @ %p before current element", qle);
- if (NULL == qle) return NULL;
-
- qle->value = value;
- qle->count = 1;
-
- if (NULL != element->prev) element->prev->next = qle;
- qle->next = element;
- qle->prev = element->prev;
- element->prev = qle;
-
- return qle;
- }
-}
-
-static int quantile_llist_delete(struct quantile_llist_element *element) {
- if (NULL == element) return 0;
-
- /* beginning of list */
- if (NULL == element->prev && NULL != element->next) {
- element->next->prev = NULL;
- }
- /* end of list */
- else if (NULL != element->prev && NULL == element->next) {
- element->prev->next = NULL;
- }
- /* within list */
- else if (NULL != element->prev && NULL != element->next) {
- element->prev->next = element->next;
- element->next->prev = element->prev;
- }
-
- RASTER_DEBUGF(4, "qle @ %p destroyed", element);
- rtdealloc(element);
-
- return 1;
-}
-
-int quantile_llist_destroy(struct quantile_llist **list, uint32_t list_count) {
- struct quantile_llist_element *element = NULL;
- uint32_t i;
-
- if (NULL == *list) return 0;
-
- for (i = 0; i < list_count; i++) {
- element = (*list)[i].head;
- while (NULL != element->next) {
- quantile_llist_delete(element->next);
- }
- quantile_llist_delete(element);
-
- rtdealloc((*list)[i].index);
- }
-
- rtdealloc(*list);
- return 1;
-}
-
-static void quantile_llist_index_update(struct quantile_llist *qll, struct quantile_llist_element *qle, uint32_t idx) {
- uint32_t anchor = (uint32_t) floor(idx / 100);
-
- if (qll->tail == qle) return;
-
- if (
- (anchor != 0) && (
- NULL == qll->index[anchor].element ||
- idx <= qll->index[anchor].index
- )
- ) {
- qll->index[anchor].index = idx;
- qll->index[anchor].element = qle;
- }
-
- if (anchor != 0 && NULL == qll->index[0].element) {
- qll->index[0].index = 0;
- qll->index[0].element = qll->head;
- }
-}
-
-static void quantile_llist_index_delete(struct quantile_llist *qll, struct quantile_llist_element *qle) {
- uint32_t i = 0;
-
- for (i = 0; i < qll->index_max; i++) {
- if (
- NULL == qll->index[i].element ||
- (qll->index[i].element) != qle
- ) {
- continue;
- }
-
- RASTER_DEBUGF(5, "deleting index: %d => %f", i, qle->value);
- qll->index[i].index = -1;
- qll->index[i].element = NULL;
- }
-}
-
-static struct quantile_llist_element *quantile_llist_index_search(
- struct quantile_llist *qll,
- double value,
- uint32_t *index
-) {
- uint32_t i = 0, j = 0;
- RASTER_DEBUGF(5, "searching index for value %f", value);
-
- for (i = 0; i < qll->index_max; i++) {
- if (NULL == qll->index[i].element) {
- if (i < 1) break;
- continue;
- }
- if (value > (qll->index[i]).element->value) continue;
-
- if (FLT_EQ(value, qll->index[i].element->value)) {
- RASTER_DEBUGF(5, "using index value at %d = %f", i, qll->index[i].element->value);
- *index = i * 100;
- return qll->index[i].element;
- }
- else if (i > 0) {
- for (j = 1; j < i; j++) {
- if (NULL != qll->index[i - j].element) {
- RASTER_DEBUGF(5, "using index value at %d = %f", i - j, qll->index[i - j].element->value);
- *index = (i - j) * 100;
- return qll->index[i - j].element;
- }
- }
- }
- }
-
- *index = 0;
- return qll->head;
-}
-
-static void quantile_llist_index_reset(struct quantile_llist *qll) {
- uint32_t i = 0;
-
- RASTER_DEBUG(5, "resetting index");
- for (i = 0; i < qll->index_max; i++) {
- qll->index[i].index = -1;
- qll->index[i].element = NULL;
- }
-}
-
-/**
- * Compute the default set of or requested quantiles for a coverage
- *
- * This function is based upon the algorithm described in:
- *
- * A One-Pass Space-Efficient Algorithm for Finding Quantiles (1995)
- * by Rakesh Agrawal, Arun Swami
- * in Proc. 7th Intl. Conf. Management of Data (COMAD-95)
- *
- * http://www.almaden.ibm.com/cs/projects/iis/hdb/Publications/papers/comad95.pdf
- *
- * In the future, it may be worth exploring algorithms that don't
- * require the size of the coverage
- *
- * @param band : the band to include in the quantile search
- * @param exclude_nodata_value : if non-zero, ignore nodata values
- * @param sample : percentage of pixels to sample
- * @param cov_count : number of values in coverage
- * @param qlls : set of quantile_llist structures
- * @param qlls_count : the number of quantile_llist structures
- * @param quantiles : the quantiles to be computed
- * if bot qlls and quantiles provided, qlls is used
- * @param quantiles_count : the number of quantiles to be computed
- * @param rtn_count : the number of quantiles being returned
- *
- * @return the default set of or requested quantiles for a band or NULL
- */
-rt_quantile
-rt_band_get_quantiles_stream(
- rt_band band,
- int exclude_nodata_value, double sample,
- uint64_t cov_count,
- struct quantile_llist **qlls, uint32_t *qlls_count,
- double *quantiles, int quantiles_count,
- uint32_t *rtn_count
-) {
- rt_quantile rtn = NULL;
- int init_quantiles = 0;
-
- struct quantile_llist *qll = NULL;
- struct quantile_llist_element *qle = NULL;
- struct quantile_llist_element *qls = NULL;
- const uint32_t MAX_VALUES = 750;
-
- uint8_t *data = NULL;
- double value;
- int isnodata = 0;
-
- uint32_t a = 0;
- uint32_t i = 0;
- uint32_t j = 0;
- uint32_t k = 0;
- uint32_t x = 0;
- uint32_t y = 0;
- uint32_t z = 0;
- uint32_t idx = 0;
- uint32_t offset = 0;
- uint32_t diff = 0;
- uint8_t exists = 0;
-
- uint32_t do_sample = 0;
- uint32_t sample_size = 0;
- uint32_t sample_per = 0;
- uint32_t sample_int = 0;
- int status;
-
- RASTER_DEBUG(3, "starting");
-
- assert(NULL != band);
- assert(cov_count > 1);
- assert(NULL != rtn_count);
- RASTER_DEBUGF(3, "cov_count = %d", cov_count);
-
- data = rt_band_get_data(band);
- if (data == NULL) {
- rterror("rt_band_get_summary_stats: Cannot get band data");
- return NULL;
- }
-
- if (!rt_band_get_hasnodata_flag(band))
- exclude_nodata_value = 0;
- RASTER_DEBUGF(3, "exclude_nodata_value = %d", exclude_nodata_value);
-
- /* quantile_llist not provided */
- if (NULL == *qlls) {
- /* quantiles not provided */
- if (NULL == quantiles) {
- /* quantile count not specified, default to quartiles */
- if (quantiles_count < 2)
- quantiles_count = 5;
-
- quantiles = rtalloc(sizeof(double) * quantiles_count);
- init_quantiles = 1;
- if (NULL == quantiles) {
- rterror("rt_band_get_quantiles_stream: Could not allocate memory for quantile input");
- return NULL;
- }
-
- quantiles_count--;
- for (i = 0; i <= quantiles_count; i++)
- quantiles[i] = ((double) i) / quantiles_count;
- quantiles_count++;
- }
-
- /* check quantiles */
- for (i = 0; i < quantiles_count; i++) {
- if (quantiles[i] < 0. || quantiles[i] > 1.) {
- rterror("rt_band_get_quantiles_stream: Quantile value not between 0 and 1");
- if (init_quantiles) rtdealloc(quantiles);
- return NULL;
- }
- }
- quicksort(quantiles, quantiles + quantiles_count - 1);
-
- /* initialize linked-list set */
- *qlls_count = quantiles_count * 2;
- RASTER_DEBUGF(4, "qlls_count = %d", *qlls_count);
- *qlls = rtalloc(sizeof(struct quantile_llist) * *qlls_count);
- if (NULL == *qlls) {
- rterror("rt_band_get_quantiles_stream: Could not allocate memory for quantile output");
- if (init_quantiles) rtdealloc(quantiles);
- return NULL;
- }
-
- j = (uint32_t) floor(MAX_VALUES / 100.) + 1;
- for (i = 0; i < *qlls_count; i++) {
- qll = &((*qlls)[i]);
- qll->quantile = quantiles[(i * quantiles_count) / *qlls_count];
- qll->count = 0;
- qll->sum1 = 0;
- qll->sum2 = 0;
- qll->head = NULL;
- qll->tail = NULL;
-
- /* initialize index */
- qll->index = rtalloc(sizeof(struct quantile_llist_index) * j);
- if (NULL == qll->index) {
- rterror("rt_band_get_quantiles_stream: Could not allocate memory for quantile output");
- if (init_quantiles) rtdealloc(quantiles);
- return NULL;
- }
- qll->index_max = j;
- quantile_llist_index_reset(qll);
-
- /* AL-GEQ */
- if (!(i % 2)) {
- qll->algeq = 1;
- qll->tau = (uint64_t) ROUND(cov_count - (cov_count * qll->quantile), 0);
- if (qll->tau < 1) qll->tau = 1;
- }
- /* AL-GT */
- else {
- qll->algeq = 0;
- qll->tau = cov_count - (*qlls)[i - 1].tau + 1;
- }
-
- RASTER_DEBUGF(4, "qll init: (algeq, quantile, count, tau, sum1, sum2) = (%d, %f, %d, %d, %d, %d)",
- qll->algeq, qll->quantile, qll->count, qll->tau, qll->sum1, qll->sum2);
- RASTER_DEBUGF(4, "qll init: (head, tail) = (%p, %p)", qll->head, qll->tail);
- }
-
- if (init_quantiles) rtdealloc(quantiles);
- }
-
- /* clamp percentage */
- if (
- (sample < 0 || FLT_EQ(sample, 0.0)) ||
- (sample > 1 || FLT_EQ(sample, 1.0))
- ) {
- do_sample = 0;
- sample = 1;
- }
- else
- do_sample = 1;
- RASTER_DEBUGF(3, "do_sample = %d", do_sample);
-
- /* sample all pixels */
- if (!do_sample) {
- sample_size = band->width * band->height;
- sample_per = band->height;
- }
- /*
- randomly sample a percentage of available pixels
- sampling method is known as
- "systematic random sample without replacement"
- */
- else {
- sample_size = round((band->width * band->height) * sample);
- sample_per = round(sample_size / band->width);
- sample_int = round(band->height / sample_per);
- srand(time(NULL));
- }
- RASTER_DEBUGF(3, "sampling %d of %d available pixels w/ %d per set"
- , sample_size, (band->width * band->height), sample_per);
-
- for (x = 0, j = 0, k = 0; x < band->width; x++) {
- y = -1;
- diff = 0;
-
- /* exclude_nodata_value = TRUE and band is NODATA */
- if (exclude_nodata_value && rt_band_get_isnodata_flag(band)) {
- RASTER_DEBUG(3, "Skipping quantile calcuation as band is NODATA");
- break;
- }
-
- for (i = 0, z = 0; i < sample_per; i++) {
- if (do_sample != 1)
- y = i;
- else {
- offset = (rand() % sample_int) + 1;
- y += diff + offset;
- diff = sample_int - offset;
- }
- RASTER_DEBUGF(5, "(x, y, z) = (%d, %d, %d)", x, y, z);
- if (y >= band->height || z > sample_per) break;
-
- status = rt_band_get_pixel(band, x, y, &value, &isnodata);
-
- j++;
- if (status == ES_NONE && (!exclude_nodata_value || (exclude_nodata_value && !isnodata))) {
-
- /* process each quantile */
- for (a = 0; a < *qlls_count; a++) {
- qll = &((*qlls)[a]);
- qls = NULL;
- RASTER_DEBUGF(4, "%d of %d (%f)", a + 1, *qlls_count, qll->quantile);
- RASTER_DEBUGF(5, "qll before: (algeq, quantile, count, tau, sum1, sum2) = (%d, %f, %d, %d, %d, %d)",
- qll->algeq, qll->quantile, qll->count, qll->tau, qll->sum1, qll->sum2);
- RASTER_DEBUGF(5, "qll before: (head, tail) = (%p, %p)", qll->head, qll->tail);
-
- /* OPTIMIZATION: shortcuts for quantiles of zero or one */
- if (FLT_EQ(qll->quantile, 0.)) {
- if (NULL != qll->head) {
- if (value < qll->head->value)
- qll->head->value = value;
- }
- else {
- qle = quantile_llist_insert(qll->head, value, NULL);
- qll->head = qle;
- qll->tail = qle;
- qll->count = 1;
- }
-
- RASTER_DEBUGF(4, "quantile shortcut for %f\n\n", qll->quantile);
- continue;
- }
- else if (FLT_EQ(qll->quantile, 1.)) {
- if (NULL != qll->head) {
- if (value > qll->head->value)
- qll->head->value = value;
- }
- else {
- qle = quantile_llist_insert(qll->head, value, NULL);
- qll->head = qle;
- qll->tail = qle;
- qll->count = 1;
- }
-
- RASTER_DEBUGF(4, "quantile shortcut for %f\n\n", qll->quantile);
- continue;
- }
-
- /* value exists in list */
- /* OPTIMIZATION: check to see if value exceeds last value */
- if (NULL != qll->tail && value > qll->tail->value)
- qle = NULL;
- /* OPTIMIZATION: check to see if value equals last value */
- else if (NULL != qll->tail && FLT_EQ(value, qll->tail->value))
- qle = qll->tail;
- /* OPTIMIZATION: use index if possible */
- else {
- qls = quantile_llist_index_search(qll, value, &idx);
- qle = quantile_llist_search(qls, value);
- }
-
- /* value found */
- if (NULL != qle) {
- RASTER_DEBUGF(4, "%f found in list", value);
- RASTER_DEBUGF(5, "qle before: (value, count) = (%f, %d)", qle->value, qle->count);
-
- qle->count++;
- qll->sum1++;
-
- if (qll->algeq)
- qll->sum2 = qll->sum1 - qll->head->count;
- else
- qll->sum2 = qll->sum1 - qll->tail->count;
-
- RASTER_DEBUGF(4, "qle after: (value, count) = (%f, %d)", qle->value, qle->count);
- }
- /* can still add element */
- else if (qll->count < MAX_VALUES) {
- RASTER_DEBUGF(4, "Adding %f to list", value);
-
- /* insert */
- /* OPTIMIZATION: check to see if value exceeds last value */
- if (NULL != qll->tail && (value > qll->tail->value || FLT_EQ(value, qll->tail->value))) {
- idx = qll->count;
- qle = quantile_llist_insert(qll->tail, value, &idx);
- }
- /* OPTIMIZATION: use index if possible */
- else
- qle = quantile_llist_insert(qls, value, &idx);
- if (NULL == qle) return NULL;
- RASTER_DEBUGF(5, "value added at index: %d => %f", idx, value);
- qll->count++;
- qll->sum1++;
-
- /* first element */
- if (NULL == qle->prev)
- qll->head = qle;
- /* last element */
- if (NULL == qle->next)
- qll->tail = qle;
-
- if (qll->algeq)
- qll->sum2 = qll->sum1 - qll->head->count;
- else
- qll->sum2 = qll->sum1 - qll->tail->count;
-
- /* index is only needed if there are at least 100 values */
- quantile_llist_index_update(qll, qle, idx);
-
- RASTER_DEBUGF(5, "qle, prev, next, head, tail = %p, %p, %p, %p, %p", qle, qle->prev, qle->next, qll->head, qll->tail);
- }
- /* AL-GEQ */
- else if (qll->algeq) {
- RASTER_DEBUGF(4, "value, head->value = %f, %f", value, qll->head->value);
-
- if (value < qll->head->value) {
- /* ignore value if test isn't true */
- if (qll->sum1 >= qll->tau) {
- RASTER_DEBUGF(4, "skipping %f", value);
- }
- else {
-
- /* delete last element */
- RASTER_DEBUGF(4, "deleting %f from list", qll->tail->value);
- qle = qll->tail->prev;
- RASTER_DEBUGF(5, "to-be tail is %f with count %d", qle->value, qle->count);
- qle->count += qll->tail->count;
- quantile_llist_index_delete(qll, qll->tail);
- quantile_llist_delete(qll->tail);
- qll->tail = qle;
- qll->count--;
- RASTER_DEBUGF(5, "tail is %f with count %d", qll->tail->value, qll->tail->count);
-
- /* insert value */
- RASTER_DEBUGF(4, "adding %f to list", value);
- /* OPTIMIZATION: check to see if value exceeds last value */
- if (NULL != qll->tail && (value > qll->tail->value || FLT_EQ(value, qll->tail->value))) {
- idx = qll->count;
- qle = quantile_llist_insert(qll->tail, value, &idx);
- }
- /* OPTIMIZATION: use index if possible */
- else {
- qls = quantile_llist_index_search(qll, value, &idx);
- qle = quantile_llist_insert(qls, value, &idx);
- }
- if (NULL == qle) return NULL;
- RASTER_DEBUGF(5, "value added at index: %d => %f", idx, value);
- qll->count++;
- qll->sum1++;
-
- /* first element */
- if (NULL == qle->prev)
- qll->head = qle;
- /* last element */
- if (NULL == qle->next)
- qll->tail = qle;
-
- qll->sum2 = qll->sum1 - qll->head->count;
-
- quantile_llist_index_update(qll, qle, idx);
-
- RASTER_DEBUGF(5, "qle, head, tail = %p, %p, %p", qle, qll->head, qll->tail);
-
- }
- }
- else {
- qle = qll->tail;
- while (NULL != qle) {
- if (qle->value < value) {
- qle->count++;
- qll->sum1++;
- qll->sum2 = qll->sum1 - qll->head->count;
- RASTER_DEBUGF(4, "incremented count of %f by 1 to %d", qle->value, qle->count);
- break;
- }
-
- qle = qle->prev;
- }
- }
- }
- /* AL-GT */
- else {
- RASTER_DEBUGF(4, "value, tail->value = %f, %f", value, qll->tail->value);
-
- if (value > qll->tail->value) {
- /* ignore value if test isn't true */
- if (qll->sum1 >= qll->tau) {
- RASTER_DEBUGF(4, "skipping %f", value);
- }
- else {
-
- /* delete last element */
- RASTER_DEBUGF(4, "deleting %f from list", qll->head->value);
- qle = qll->head->next;
- RASTER_DEBUGF(5, "to-be tail is %f with count %d", qle->value, qle->count);
- qle->count += qll->head->count;
- quantile_llist_index_delete(qll, qll->head);
- quantile_llist_delete(qll->head);
- qll->head = qle;
- qll->count--;
- quantile_llist_index_update(qll, NULL, 0);
- RASTER_DEBUGF(5, "tail is %f with count %d", qll->head->value, qll->head->count);
-
- /* insert value */
- RASTER_DEBUGF(4, "adding %f to list", value);
- /* OPTIMIZATION: check to see if value exceeds last value */
- if (NULL != qll->tail && (value > qll->tail->value || FLT_EQ(value, qll->tail->value))) {
- idx = qll->count;
- qle = quantile_llist_insert(qll->tail, value, &idx);
- }
- /* OPTIMIZATION: use index if possible */
- else {
- qls = quantile_llist_index_search(qll, value, &idx);
- qle = quantile_llist_insert(qls, value, &idx);
- }
- if (NULL == qle) return NULL;
- RASTER_DEBUGF(5, "value added at index: %d => %f", idx, value);
- qll->count++;
- qll->sum1++;
-
- /* first element */
- if (NULL == qle->prev)
- qll->head = qle;
- /* last element */
- if (NULL == qle->next)
- qll->tail = qle;
-
- qll->sum2 = qll->sum1 - qll->tail->count;
-
- quantile_llist_index_update(qll, qle, idx);
-
- RASTER_DEBUGF(5, "qle, head, tail = %p, %p, %p", qle, qll->head, qll->tail);
-
- }
- }
- else {
- qle = qll->head;
- while (NULL != qle) {
- if (qle->value > value) {
- qle->count++;
- qll->sum1++;
- qll->sum2 = qll->sum1 - qll->tail->count;
- RASTER_DEBUGF(4, "incremented count of %f by 1 to %d", qle->value, qle->count);
- break;
- }
-
- qle = qle->next;
- }
- }
- }
-
- RASTER_DEBUGF(5, "sum2, tau = %d, %d", qll->sum2, qll->tau);
- if (qll->sum2 >= qll->tau) {
- /* AL-GEQ */
- if (qll->algeq) {
- RASTER_DEBUGF(4, "deleting first element %f from list", qll->head->value);
-
- if (NULL != qll->head->next) {
- qle = qll->head->next;
- qll->sum1 -= qll->head->count;
- qll->sum2 = qll->sum1 - qle->count;
- quantile_llist_index_delete(qll, qll->head);
- quantile_llist_delete(qll->head);
- qll->head = qle;
- qll->count--;
-
- quantile_llist_index_update(qll, NULL, 0);
- }
- else {
- quantile_llist_delete(qll->head);
- qll->head = NULL;
- qll->tail = NULL;
- qll->sum1 = 0;
- qll->sum2 = 0;
- qll->count = 0;
-
- quantile_llist_index_reset(qll);
- }
- }
- /* AL-GT */
- else {
- RASTER_DEBUGF(4, "deleting first element %f from list", qll->tail->value);
-
- if (NULL != qll->tail->prev) {
- qle = qll->tail->prev;
- qll->sum1 -= qll->tail->count;
- qll->sum2 = qll->sum1 - qle->count;
- quantile_llist_index_delete(qll, qll->tail);
- quantile_llist_delete(qll->tail);
- qll->tail = qle;
- qll->count--;
- }
- else {
- quantile_llist_delete(qll->tail);
- qll->head = NULL;
- qll->tail = NULL;
- qll->sum1 = 0;
- qll->sum2 = 0;
- qll->count = 0;
-
- quantile_llist_index_reset(qll);
- }
- }
- }
-
- RASTER_DEBUGF(5, "qll after: (algeq, quantile, count, tau, sum1, sum2) = (%d, %f, %d, %d, %d, %d)",
- qll->algeq, qll->quantile, qll->count, qll->tau, qll->sum1, qll->sum2);
- RASTER_DEBUGF(5, "qll after: (head, tail) = (%p, %p)\n\n", qll->head, qll->tail);
- }
-
- }
- else {
- RASTER_DEBUGF(5, "skipping value at (x, y) = (%d, %d)", x, y);
- }
-
- z++;
- }
- }
-
- /* process quantiles */
- *rtn_count = *qlls_count / 2;
- rtn = rtalloc(sizeof(struct rt_quantile_t) * *rtn_count);
- if (NULL == rtn) return NULL;
-
- RASTER_DEBUGF(3, "returning %d quantiles", *rtn_count);
- for (i = 0, k = 0; i < *qlls_count; i++) {
- qll = &((*qlls)[i]);
-
- exists = 0;
- for (x = 0; x < k; x++) {
- if (FLT_EQ(qll->quantile, rtn[x].quantile)) {
- exists = 1;
- break;
- }
- }
- if (exists) continue;
-
- RASTER_DEBUGF(5, "qll: (algeq, quantile, count, tau, sum1, sum2) = (%d, %f, %d, %d, %d, %d)",
- qll->algeq, qll->quantile, qll->count, qll->tau, qll->sum1, qll->sum2);
- RASTER_DEBUGF(5, "qll: (head, tail) = (%p, %p)", qll->head, qll->tail);
-
- rtn[k].quantile = qll->quantile;
- rtn[k].has_value = 0;
-
- /* check that qll->head and qll->tail have value */
- if (qll->head == NULL || qll->tail == NULL)
- continue;
-
- /* AL-GEQ */
- if (qll->algeq)
- qle = qll->head;
- /* AM-GT */
- else
- qle = qll->tail;
-
- exists = 0;
- for (j = i + 1; j < *qlls_count; j++) {
- if (FLT_EQ((*qlls)[j].quantile, qll->quantile)) {
-
- RASTER_DEBUGF(5, "qlls[%d]: (algeq, quantile, count, tau, sum1, sum2) = (%d, %f, %d, %d, %d, %d)",
- j, (*qlls)[j].algeq, (*qlls)[j].quantile, (*qlls)[j].count, (*qlls)[j].tau, (*qlls)[j].sum1, (*qlls)[j].sum2);
- RASTER_DEBUGF(5, "qlls[%d]: (head, tail) = (%p, %p)", j, (*qlls)[j].head, (*qlls)[j].tail);
-
- exists = 1;
- break;
- }
- }
-
- /* weighted average for quantile */
- if (exists) {
- if ((*qlls)[j].algeq) {
- rtn[k].value = ((qle->value * qle->count) + ((*qlls)[j].head->value * (*qlls)[j].head->count)) / (qle->count + (*qlls)[j].head->count);
- RASTER_DEBUGF(5, "qlls[%d].head: (value, count) = (%f, %d)", j, (*qlls)[j].head->value, (*qlls)[j].head->count);
- }
- else {
- rtn[k].value = ((qle->value * qle->count) + ((*qlls)[j].tail->value * (*qlls)[j].tail->count)) / (qle->count + (*qlls)[j].tail->count);
- RASTER_DEBUGF(5, "qlls[%d].tail: (value, count) = (%f, %d)", j, (*qlls)[j].tail->value, (*qlls)[j].tail->count);
- }
- }
- /* straight value for quantile */
- else {
- rtn[k].value = qle->value;
- }
- rtn[k].has_value = 1;
- RASTER_DEBUGF(3, "(quantile, value) = (%f, %f)\n\n", rtn[k].quantile, rtn[k].value);
-
- k++;
- }
-
- RASTER_DEBUG(3, "done");
- return rtn;
-}
-
-/**
- * Count the number of times provided value(s) occur in
- * the band
- *
- * @param band : the band to query for minimum and maximum pixel values
- * @param exclude_nodata_value : if non-zero, ignore nodata values
- * @param search_values : array of values to count
- * @param search_values_count : the number of search values
- * @param roundto : the decimal place to round the values to
- * @param rtn_total : the number of pixels examined in the band
- * @param rtn_count : the number of value counts being returned
- *
- * @return the number of times the provide value(s) occur or NULL
- */
-rt_valuecount
-rt_band_get_value_count(
- rt_band band, int exclude_nodata_value,
- double *search_values, uint32_t search_values_count, double roundto,
- uint32_t *rtn_total, uint32_t *rtn_count
-) {
- rt_valuecount vcnts = NULL;
- rt_pixtype pixtype = PT_END;
- uint8_t *data = NULL;
- double nodata = 0;
-
- int scale = 0;
- int doround = 0;
- double tmpd = 0;
- int i = 0;
-
- uint32_t x = 0;
- uint32_t y = 0;
- int rtn;
- double pxlval;
- int isnodata = 0;
- double rpxlval;
- uint32_t total = 0;
- int vcnts_count = 0;
- int new_valuecount = 0;
-
-#if POSTGIS_DEBUG_LEVEL > 0
- clock_t start, stop;
- double elapsed = 0;
-#endif
-
- RASTER_DEBUG(3, "starting");
-#if POSTGIS_DEBUG_LEVEL > 0
- start = clock();
-#endif
-
- assert(NULL != band);
- assert(NULL != rtn_count);
-
- data = rt_band_get_data(band);
- if (data == NULL) {
- rterror("rt_band_get_summary_stats: Cannot get band data");
- return NULL;
- }
-
- pixtype = band->pixtype;
-
- if (rt_band_get_hasnodata_flag(band)) {
- rt_band_get_nodata(band, &nodata);
- RASTER_DEBUGF(3, "hasnodata, nodataval = 1, %f", nodata);
- }
- else {
- exclude_nodata_value = 0;
- RASTER_DEBUG(3, "hasnodata, nodataval = 0, 0");
- }
-
- RASTER_DEBUGF(3, "exclude_nodata_value = %d", exclude_nodata_value);
-
- /* process roundto */
- if (roundto < 0 || FLT_EQ(roundto, 0.0)) {
- roundto = 0;
- scale = 0;
- }
- /* tenths, hundredths, thousandths, etc */
- else if (roundto < 1) {
- switch (pixtype) {
- /* integer band types don't have digits after the decimal place */
- case PT_1BB:
- case PT_2BUI:
- case PT_4BUI:
- case PT_8BSI:
- case PT_8BUI:
- case PT_16BSI:
- case PT_16BUI:
- case PT_32BSI:
- case PT_32BUI:
- roundto = 0;
- break;
- /* floating points, check the rounding */
- case PT_32BF:
- case PT_64BF:
- for (scale = 0; scale <= 20; scale++) {
- tmpd = roundto * pow(10, scale);
- if (FLT_EQ((tmpd - ((int) tmpd)), 0.0)) break;
- }
- break;
- case PT_END:
- break;
- }
- }
- /* ones, tens, hundreds, etc */
- else {
- for (scale = 0; scale >= -20; scale--) {
- tmpd = roundto * pow(10, scale);
- if (tmpd < 1 || FLT_EQ(tmpd, 1.0)) {
- if (scale == 0) doround = 1;
- break;
- }
- }
- }
-
- if (scale != 0 || doround)
- doround = 1;
- else
- doround = 0;
- RASTER_DEBUGF(3, "scale = %d", scale);
- RASTER_DEBUGF(3, "doround = %d", doround);
-
- /* process search_values */
- if (search_values_count > 0 && NULL != search_values) {
- vcnts = (rt_valuecount) rtalloc(sizeof(struct rt_valuecount_t) * search_values_count);
- if (NULL == vcnts) {
- rterror("rt_band_get_count_of_values: Could not allocate memory for value counts");
- *rtn_count = 0;
- return NULL;
- }
-
- for (i = 0; i < search_values_count; i++) {
- vcnts[i].count = 0;
- vcnts[i].percent = 0;
- if (!doround)
- vcnts[i].value = search_values[i];
- else
- vcnts[i].value = ROUND(search_values[i], scale);
- }
- vcnts_count = i;
- }
- else
- search_values_count = 0;
- RASTER_DEBUGF(3, "search_values_count = %d", search_values_count);
-
- /* entire band is nodata */
- if (rt_band_get_isnodata_flag(band) != FALSE) {
- if (exclude_nodata_value) {
- rtwarn("All pixels of band have the NODATA value");
- return NULL;
- }
- else {
- if (search_values_count > 0) {
- /* check for nodata match */
- for (i = 0; i < search_values_count; i++) {
- if (!doround)
- tmpd = nodata;
- else
- tmpd = ROUND(nodata, scale);
-
- if (FLT_NEQ(tmpd, vcnts[i].value))
- continue;
-
- vcnts[i].count = band->width * band->height;
- if (NULL != rtn_total) *rtn_total = vcnts[i].count;
- vcnts->percent = 1.0;
- }
-
- *rtn_count = vcnts_count;
- }
- /* no defined search values */
- else {
- vcnts = (rt_valuecount) rtalloc(sizeof(struct rt_valuecount_t));
- if (NULL == vcnts) {
- rterror("rt_band_get_count_of_values: Could not allocate memory for value counts");
- *rtn_count = 0;
- return NULL;
- }
-
- vcnts->value = nodata;
- vcnts->count = band->width * band->height;
- if (NULL != rtn_total) *rtn_total = vcnts[i].count;
- vcnts->percent = 1.0;
-
- *rtn_count = 1;
- }
-
- return vcnts;
- }
- }
-
- for (x = 0; x < band->width; x++) {
- for (y = 0; y < band->height; y++) {
- rtn = rt_band_get_pixel(band, x, y, &pxlval, &isnodata);
-
- /* error getting value, continue */
- if (rtn != ES_NONE)
- continue;
-
- if (!exclude_nodata_value || (exclude_nodata_value && !isnodata)) {
- total++;
- if (doround) {
- rpxlval = ROUND(pxlval, scale);
- }
- else
- rpxlval = pxlval;
- RASTER_DEBUGF(5, "(pxlval, rpxlval) => (%0.6f, %0.6f)", pxlval, rpxlval);
-
- new_valuecount = 1;
- /* search for match in existing valuecounts */
- for (i = 0; i < vcnts_count; i++) {
- /* match found */
- if (FLT_EQ(vcnts[i].value, rpxlval)) {
- vcnts[i].count++;
- new_valuecount = 0;
- RASTER_DEBUGF(5, "(value, count) => (%0.6f, %d)", vcnts[i].value, vcnts[i].count);
- break;
- }
- }
-
- /*
- don't add new valuecount either because
- - no need for new one
- - user-defined search values
- */
- if (!new_valuecount || search_values_count > 0) continue;
-
- /* add new valuecount */
- vcnts = rtrealloc(vcnts, sizeof(struct rt_valuecount_t) * (vcnts_count + 1));
- if (NULL == vcnts) {
- rterror("rt_band_get_count_of_values: Could not allocate memory for value counts");
- *rtn_count = 0;
- return NULL;
- }
-
- vcnts[vcnts_count].value = rpxlval;
- vcnts[vcnts_count].count = 1;
- vcnts[vcnts_count].percent = 0;
- RASTER_DEBUGF(5, "(value, count) => (%0.6f, %d)", vcnts[vcnts_count].value, vcnts[vcnts_count].count);
- vcnts_count++;
- }
- }
- }
-
-#if POSTGIS_DEBUG_LEVEL > 0
- stop = clock();
- elapsed = ((double) (stop - start)) / CLOCKS_PER_SEC;
- RASTER_DEBUGF(3, "elapsed time = %0.4f", elapsed);
-#endif
-
- for (i = 0; i < vcnts_count; i++) {
- vcnts[i].percent = (double) vcnts[i].count / total;
- RASTER_DEBUGF(5, "(value, count) => (%0.6f, %d)", vcnts[i].value, vcnts[i].count);
- }
-
- RASTER_DEBUG(3, "done");
- if (NULL != rtn_total) *rtn_total = total;
- *rtn_count = vcnts_count;
- return vcnts;
-}
-
-/**
- * Returns new band with values reclassified
- *
- * @param srcband : the band who's values will be reclassified
- * @param pixtype : pixel type of the new band
- * @param hasnodata : indicates if the band has a nodata value
- * @param nodataval : nodata value for the new band
- * @param exprset : array of rt_reclassexpr structs
- * @param exprcount : number of elements in expr
- *
- * @return a new rt_band or NULL on error
- */
-rt_band
-rt_band_reclass(
- rt_band srcband, rt_pixtype pixtype,
- uint32_t hasnodata, double nodataval,
- rt_reclassexpr *exprset, int exprcount
-) {
- rt_band band = NULL;
- uint32_t width = 0;
- uint32_t height = 0;
- int numval = 0;
- int memsize = 0;
- void *mem = NULL;
- uint32_t src_hasnodata = 0;
- double src_nodataval = 0.0;
- int isnodata = 0;
-
- int rtn;
- uint32_t x;
- uint32_t y;
- int i;
- double or = 0;
- double ov = 0;
- double nr = 0;
- double nv = 0;
- int do_nv = 0;
- rt_reclassexpr expr = NULL;
-
- assert(NULL != srcband);
- assert(NULL != exprset && exprcount > 0);
- RASTER_DEBUGF(4, "exprcount = %d", exprcount);
- RASTER_DEBUGF(4, "exprset @ %p", exprset);
-
- /* source nodata */
- src_hasnodata = rt_band_get_hasnodata_flag(srcband);
- if (src_hasnodata)
- rt_band_get_nodata(srcband, &src_nodataval);
-
- /* size of memory block to allocate */
- width = rt_band_get_width(srcband);
- height = rt_band_get_height(srcband);
- numval = width * height;
- memsize = rt_pixtype_size(pixtype) * numval;
- mem = (int *) rtalloc(memsize);
- if (!mem) {
- rterror("rt_band_reclass: Could not allocate memory for band");
- return 0;
- }
-
- /* initialize to zero */
- if (!hasnodata) {
- memset(mem, 0, memsize);
- }
- /* initialize to nodataval */
- else {
- int32_t checkvalint = 0;
- uint32_t checkvaluint = 0;
- double checkvaldouble = 0;
- float checkvalfloat = 0;
-
- switch (pixtype) {
- case PT_1BB:
- {
- uint8_t *ptr = mem;
- uint8_t clamped_initval = rt_util_clamp_to_1BB(nodataval);
- for (i = 0; i < numval; i++)
- ptr[i] = clamped_initval;
- checkvalint = ptr[0];
- break;
- }
- case PT_2BUI:
- {
- uint8_t *ptr = mem;
- uint8_t clamped_initval = rt_util_clamp_to_2BUI(nodataval);
- for (i = 0; i < numval; i++)
- ptr[i] = clamped_initval;
- checkvalint = ptr[0];
- break;
- }
- case PT_4BUI:
- {
- uint8_t *ptr = mem;
- uint8_t clamped_initval = rt_util_clamp_to_4BUI(nodataval);
- for (i = 0; i < numval; i++)
- ptr[i] = clamped_initval;
- checkvalint = ptr[0];
- break;
- }
- case PT_8BSI:
- {
- int8_t *ptr = mem;
- int8_t clamped_initval = rt_util_clamp_to_8BSI(nodataval);
- for (i = 0; i < numval; i++)
- ptr[i] = clamped_initval;
- checkvalint = ptr[0];
- break;
- }
- case PT_8BUI:
- {
- uint8_t *ptr = mem;
- uint8_t clamped_initval = rt_util_clamp_to_8BUI(nodataval);
- for (i = 0; i < numval; i++)
- ptr[i] = clamped_initval;
- checkvalint = ptr[0];
- break;
- }
- case PT_16BSI:
- {
- int16_t *ptr = mem;
- int16_t clamped_initval = rt_util_clamp_to_16BSI(nodataval);
- for (i = 0; i < numval; i++)
- ptr[i] = clamped_initval;
- checkvalint = ptr[0];
- break;
- }
- case PT_16BUI:
- {
- uint16_t *ptr = mem;
- uint16_t clamped_initval = rt_util_clamp_to_16BUI(nodataval);
- for (i = 0; i < numval; i++)
- ptr[i] = clamped_initval;
- checkvalint = ptr[0];
- break;
- }
- case PT_32BSI:
- {
- int32_t *ptr = mem;
- int32_t clamped_initval = rt_util_clamp_to_32BSI(nodataval);
- for (i = 0; i < numval; i++)
- ptr[i] = clamped_initval;
- checkvalint = ptr[0];
- break;
- }
- case PT_32BUI:
- {
- uint32_t *ptr = mem;
- uint32_t clamped_initval = rt_util_clamp_to_32BUI(nodataval);
- for (i = 0; i < numval; i++)
- ptr[i] = clamped_initval;
- checkvaluint = ptr[0];
- break;
- }
- case PT_32BF:
- {
- float *ptr = mem;
- float clamped_initval = rt_util_clamp_to_32F(nodataval);
- for (i = 0; i < numval; i++)
- ptr[i] = clamped_initval;
- checkvalfloat = ptr[0];
- break;
- }
- case PT_64BF:
- {
- double *ptr = mem;
- for (i = 0; i < numval; i++)
- ptr[i] = nodataval;
- checkvaldouble = ptr[0];
- break;
- }
- default:
- {
- rterror("rt_band_reclass: Unknown pixeltype %d", pixtype);
- rtdealloc(mem);
- return 0;
- }
- }
-
- /* Overflow checking */
- rt_util_dbl_trunc_warning(
- nodataval,
- checkvalint, checkvaluint,
- checkvalfloat, checkvaldouble,
- pixtype
- );
- }
- RASTER_DEBUGF(3, "rt_band_reclass: width = %d height = %d", width, height);
-
- band = rt_band_new_inline(width, height, pixtype, hasnodata, nodataval, mem);
- if (!band) {
- rterror("rt_band_reclass: Could not create new band");
- rtdealloc(mem);
- return 0;
- }
- rt_band_set_ownsdata_flag(band, 1); /* we DO own this data!!! */
- RASTER_DEBUGF(3, "rt_band_reclass: new band @ %p", band);
-
- for (x = 0; x < width; x++) {
- for (y = 0; y < height; y++) {
- rtn = rt_band_get_pixel(srcband, x, y, &ov, &isnodata);
-
- /* error getting value, skip */
- if (rtn != ES_NONE) {
- RASTER_DEBUGF(3, "Cannot get value at %d, %d", x, y);
- continue;
- }
-
- do {
- do_nv = 0;
-
- /* no data*/
- if (hasnodata && isnodata) {
- do_nv = 1;
- break;
- }
-
- for (i = 0; i < exprcount; i++) {
- expr = exprset[i];
-
- /* ov matches min and max*/
- if (
- FLT_EQ(expr->src.min, ov) &&
- FLT_EQ(expr->src.max, ov)
- ) {
- do_nv = 1;
- break;
- }
-
- /* process min */
- if ((
- expr->src.exc_min && (
- expr->src.min > ov ||
- FLT_EQ(expr->src.min, ov)
- )) || (
- expr->src.inc_min && (
- expr->src.min < ov ||
- FLT_EQ(expr->src.min, ov)
- )) || (
- expr->src.min < ov
- )) {
- /* process max */
- if ((
- expr->src.exc_max && (
- ov > expr->src.max ||
- FLT_EQ(expr->src.max, ov)
- )) || (
- expr->src.inc_max && (
- ov < expr->src.max ||
- FLT_EQ(expr->src.max, ov)
- )) || (
- ov < expr->src.max
- )) {
- do_nv = 1;
- break;
- }
- }
- }
- }
- while (0);
-
- /* no expression matched, do not continue */
- if (!do_nv) continue;
- RASTER_DEBUGF(3, "Using exprset[%d] unless NODATA", i);
-
- /* converting a value from one range to another range
- OldRange = (OldMax - OldMin)
- NewRange = (NewMax - NewMin)
- NewValue = (((OldValue - OldMin) * NewRange) / OldRange) + NewMin
- */
-
- /* NODATA */
- if (hasnodata && isnodata) {
- nv = nodataval;
- }
- /*
- "src" min and max is the same, prevent division by zero
- set nv to "dst" min, which should be the same as "dst" max
- */
- else if (FLT_EQ(expr->src.max, expr->src.min)) {
- nv = expr->dst.min;
- }
- else {
- or = expr->src.max - expr->src.min;
- nr = expr->dst.max - expr->dst.min;
- nv = (((ov - expr->src.min) * nr) / or) + expr->dst.min;
-
- /* if dst range is from high to low */
- if (expr->dst.min > expr->dst.max) {
- if (nv > expr->dst.min)
- nv = expr->dst.min;
- else if (nv < expr->dst.max)
- nv = expr->dst.max;
- }
- /* if dst range is from low to high */
- else {
- if (nv < expr->dst.min)
- nv = expr->dst.min;
- else if (nv > expr->dst.max)
- nv = expr->dst.max;
- }
- }
-
- /* round the value for integers */
- switch (pixtype) {
- case PT_1BB:
- case PT_2BUI:
- case PT_4BUI:
- case PT_8BSI:
- case PT_8BUI:
- case PT_16BSI:
- case PT_16BUI:
- case PT_32BSI:
- case PT_32BUI:
- nv = round(nv);
- break;
- default:
- break;
- }
-
- RASTER_DEBUGF(4, "(%d, %d) ov: %f or: %f - %f nr: %f - %f nv: %f"
- , x
- , y
- , ov
- , (NULL != expr) ? expr->src.min : 0
- , (NULL != expr) ? expr->src.max : 0
- , (NULL != expr) ? expr->dst.min : 0
- , (NULL != expr) ? expr->dst.max : 0
- , nv
- );
- if (rt_band_set_pixel(band, x, y, nv, NULL) != ES_NONE) {
- rterror("rt_band_reclass: Could not assign value to new band");
- rt_band_destroy(band);
- rtdealloc(mem);
- return 0;
- }
-
- expr = NULL;
- }
- }
-
- return band;
-}
-
-/*- rt_raster --------------------------------------------------------*/
-
-/**
- * Construct a raster with given dimensions.
- *
- * Transform will be set to identity.
- * Will contain no bands.
- *
- * @param width : number of pixel columns
- * @param height : number of pixel rows
- *
- * @return an rt_raster or NULL if out of memory
- */
-rt_raster
-rt_raster_new(uint32_t width, uint32_t height) {
- rt_raster ret = NULL;
-
- ret = (rt_raster) rtalloc(sizeof (struct rt_raster_t));
- if (!ret) {
- rterror("rt_raster_new: Out of virtual memory creating an rt_raster");
- return NULL;
- }
-
- RASTER_DEBUGF(3, "Created rt_raster @ %p", ret);
-
- if (width > 65535 || height > 65535) {
- rterror("rt_raster_new: Dimensions requested exceed the maximum (65535 x 65535) permitted for a raster");
- rt_raster_destroy(ret);
- return NULL;
- }
-
- ret->width = width;
- ret->height = height;
- ret->scaleX = 1;
- ret->scaleY = -1;
- ret->ipX = 0.0;
- ret->ipY = 0.0;
- ret->skewX = 0.0;
- ret->skewY = 0.0;
- ret->srid = SRID_UNKNOWN;
-
- ret->numBands = 0;
- ret->bands = NULL;
-
- return ret;
-}
-
-void
-rt_raster_destroy(rt_raster raster) {
- if (raster == NULL)
- return;
-
- RASTER_DEBUGF(3, "Destroying rt_raster @ %p", raster);
-
- if (raster->bands)
- rtdealloc(raster->bands);
-
- rtdealloc(raster);
-}
-
-static void
-_rt_raster_geotransform_warn_offline_band(rt_raster raster) {
- int numband = 0;
- int i = 0;
- rt_band band = NULL;
-
- if (raster == NULL)
- return;
-
- numband = rt_raster_get_num_bands(raster);
- if (numband < 1)
- return;
-
- for (i = 0; i < numband; i++) {
- band = rt_raster_get_band(raster, i);
- if (NULL == band)
- continue;
-
- if (!rt_band_is_offline(band))
- continue;
-
- rtwarn("Changes made to raster geotransform matrix may affect out-db band data. Returned band data may be incorrect");
- break;
- }
-}
-
-uint16_t
-rt_raster_get_width(rt_raster raster) {
-
- assert(NULL != raster);
-
- return raster->width;
-}
-
-uint16_t
-rt_raster_get_height(rt_raster raster) {
-
- assert(NULL != raster);
-
- return raster->height;
-}
-
-void
-rt_raster_set_scale(
- rt_raster raster,
- double scaleX, double scaleY
-) {
- assert(NULL != raster);
-
- raster->scaleX = scaleX;
- raster->scaleY = scaleY;
-
- _rt_raster_geotransform_warn_offline_band(raster);
-}
-
-double
-rt_raster_get_x_scale(rt_raster raster) {
-
-
- assert(NULL != raster);
-
- return raster->scaleX;
-}
-
-double
-rt_raster_get_y_scale(rt_raster raster) {
-
-
- assert(NULL != raster);
-
- return raster->scaleY;
-}
-
-void
-rt_raster_set_skews(
- rt_raster raster,
- double skewX, double skewY
-) {
- assert(NULL != raster);
-
- raster->skewX = skewX;
- raster->skewY = skewY;
-
- _rt_raster_geotransform_warn_offline_band(raster);
-}
-
-double
-rt_raster_get_x_skew(rt_raster raster) {
-
-
- assert(NULL != raster);
-
- return raster->skewX;
-}
-
-double
-rt_raster_get_y_skew(rt_raster raster) {
-
-
- assert(NULL != raster);
-
- return raster->skewY;
-}
-
-void
-rt_raster_set_offsets(
- rt_raster raster,
- double x, double y
-) {
-
- assert(NULL != raster);
-
- raster->ipX = x;
- raster->ipY = y;
-
- _rt_raster_geotransform_warn_offline_band(raster);
-}
-
-double
-rt_raster_get_x_offset(rt_raster raster) {
-
-
- assert(NULL != raster);
-
- return raster->ipX;
-}
-
-double
-rt_raster_get_y_offset(rt_raster raster) {
-
-
- assert(NULL != raster);
-
- return raster->ipY;
-}
-
-void
-rt_raster_get_phys_params(rt_raster rast,
- double *i_mag, double *j_mag, double *theta_i, double *theta_ij)
-{
- double o11, o12, o21, o22 ; /* geotransform coefficients */
-
- if (rast == NULL) return ;
- if ( (i_mag==NULL) || (j_mag==NULL) || (theta_i==NULL) || (theta_ij==NULL))
- return ;
-
- /* retrieve coefficients from raster */
- o11 = rt_raster_get_x_scale(rast) ;
- o12 = rt_raster_get_x_skew(rast) ;
- o21 = rt_raster_get_y_skew(rast) ;
- o22 = rt_raster_get_y_scale(rast) ;
-
- rt_raster_calc_phys_params(o11, o12, o21, o22, i_mag, j_mag, theta_i, theta_ij);
-}
-
-void
-rt_raster_calc_phys_params(double xscale, double xskew, double yskew, double yscale,
- double *i_mag, double *j_mag, double *theta_i, double *theta_ij)
-
-{
- double theta_test ;
-
- if ( (i_mag==NULL) || (j_mag==NULL) || (theta_i==NULL) || (theta_ij==NULL))
- return ;
-
- /* pixel size in the i direction */
- *i_mag = sqrt(xscale*xscale + yskew*yskew) ;
-
- /* pixel size in the j direction */
- *j_mag = sqrt(xskew*xskew + yscale*yscale) ;
-
- /* Rotation
- * ========
- * Two steps:
- * 1] calculate the magnitude of the angle between the x axis and
- * the i basis vector.
- * 2] Calculate the sign of theta_i based on the angle between the y axis
- * and the i basis vector.
- */
- *theta_i = acos(xscale/(*i_mag)) ; /* magnitude */
- theta_test = acos(yskew/(*i_mag)) ; /* sign */
- if (theta_test < M_PI_2){
- *theta_i = -(*theta_i) ;
- }
-
-
- /* Angular separation of basis vectors
- * ===================================
- * Two steps:
- * 1] calculate the magnitude of the angle between the j basis vector and
- * the i basis vector.
- * 2] Calculate the sign of theta_ij based on the angle between the
- * perpendicular of the i basis vector and the j basis vector.
- */
- *theta_ij = acos(((xscale*xskew) + (yskew*yscale))/((*i_mag)*(*j_mag))) ;
- theta_test = acos( ((-yskew*xskew)+(xscale*yscale)) /
- ((*i_mag)*(*j_mag)));
- if (theta_test > M_PI_2) {
- *theta_ij = -(*theta_ij) ;
- }
-}
-
-void
-rt_raster_set_phys_params(rt_raster rast,double i_mag, double j_mag, double theta_i, double theta_ij)
-{
- double o11, o12, o21, o22 ; /* calculated geotransform coefficients */
- int success ;
-
- if (rast == NULL) return ;
-
- success = rt_raster_calc_gt_coeff(i_mag, j_mag, theta_i, theta_ij,
- &o11, &o12, &o21, &o22) ;
-
- if (success) {
- rt_raster_set_scale(rast, o11, o22) ;
- rt_raster_set_skews(rast, o12, o21) ;
- }
-}
-
-int
-rt_raster_calc_gt_coeff(double i_mag, double j_mag, double theta_i, double theta_ij,
- double *xscale, double *xskew, double *yskew, double *yscale)
-{
- double f ; /* reflection flag 1.0 or -1.0 */
- double k_i ; /* shearing coefficient */
- double s_i, s_j ; /* scaling coefficients */
- double cos_theta_i, sin_theta_i ;
-
- if ( (xscale==NULL) || (xskew==NULL) || (yskew==NULL) || (yscale==NULL)) {
- return 0;
- }
-
- if ( (theta_ij == 0.0) || (theta_ij == M_PI)) {
- return 0;
- }
-
- /* Reflection across the i axis */
- f=1.0 ;
- if (theta_ij < 0) {
- f = -1.0;
- }
-
- /* scaling along i axis */
- s_i = i_mag ;
-
- /* shearing parallel to i axis */
- k_i = tan(f*M_PI_2 - theta_ij) ;
-
- /* scaling along j axis */
- s_j = j_mag / (sqrt(k_i*k_i + 1)) ;
-
- /* putting it altogether */
- cos_theta_i = cos(theta_i) ;
- sin_theta_i = sin(theta_i) ;
- *xscale = s_i * cos_theta_i ;
- *xskew = k_i * s_j * f * cos_theta_i + s_j * f * sin_theta_i ;
- *yskew = -s_i * sin_theta_i ;
- *yscale = -k_i * s_j * f * sin_theta_i + s_j * f * cos_theta_i ;
- return 1;
-}
-
-int32_t
-rt_raster_get_srid(rt_raster raster) {
- assert(NULL != raster);
-
- return clamp_srid(raster->srid);
-}
-
-void
-rt_raster_set_srid(rt_raster raster, int32_t srid) {
- assert(NULL != raster);
-
- raster->srid = clamp_srid(srid);
-
- _rt_raster_geotransform_warn_offline_band(raster);
-}
-
-int
-rt_raster_get_num_bands(rt_raster raster) {
-
-
- assert(NULL != raster);
-
- return raster->numBands;
-}
-
-rt_band
-rt_raster_get_band(rt_raster raster, int n) {
- assert(NULL != raster);
-
- if (n >= raster->numBands || n < 0)
- return NULL;
-
- return raster->bands[n];
-}
-
-/**
- * Add band data to a raster.
- *
- * @param raster : the raster to add a band to
- * @param band : the band to add, ownership left to caller.
- * Band dimensions are required to match with raster ones.
- * @param index : the position where to insert the new band (0 based)
- *
- * @return identifier (position) for the just-added raster, or -1 on error
- */
-int
-rt_raster_add_band(rt_raster raster, rt_band band, int index) {
- rt_band *oldbands = NULL;
- rt_band oldband = NULL;
- rt_band tmpband = NULL;
- uint16_t i = 0;
-
-
-
- assert(NULL != raster);
- assert(NULL != band);
-
- RASTER_DEBUGF(3, "Adding band %p to raster %p", band, raster);
-
- if (band->width != raster->width || band->height != raster->height) {
- rterror("rt_raster_add_band: Can't add a %dx%d band to a %dx%d raster",
- band->width, band->height, raster->width, raster->height);
- return -1;
- }
-
- if (index > raster->numBands)
- index = raster->numBands;
-
- if (index < 0)
- index = 0;
-
- oldbands = raster->bands;
-
- RASTER_DEBUGF(3, "Oldbands at %p", oldbands);
-
- raster->bands = (rt_band*) rtrealloc(raster->bands,
- sizeof (rt_band)*(raster->numBands + 1)
- );
-
- RASTER_DEBUG(3, "Checking bands");
-
- if (NULL == raster->bands) {
- rterror("rt_raster_add_band: Out of virtual memory "
- "reallocating band pointers");
- raster->bands = oldbands;
- return -1;
- }
-
- RASTER_DEBUGF(4, "realloc returned %p", raster->bands);
-
- for (i = 0; i <= raster->numBands; ++i) {
- if (i == index) {
- oldband = raster->bands[i];
- raster->bands[i] = band;
- } else if (i > index) {
- tmpband = raster->bands[i];
- raster->bands[i] = oldband;
- oldband = tmpband;
- }
- }
-
- band->raster = raster;
-
- raster->numBands++;
-
- RASTER_DEBUGF(4, "Raster now has %d bands", raster->numBands);
-
- return index;
-}
-
-/**
- * Generate a new inline band and add it to a raster.
- * Memory is allocated in this function for band data.
- *
- * @param raster : the raster to add a band to
- * @param pixtype : the pixel type for the new band
- * @param initialvalue : initial value for pixels
- * @param hasnodata : indicates if the band has a nodata value
- * @param nodatavalue : nodata value for the new band
- * @param index : position to add the new band in the raster
- *
- * @return identifier (position) for the just-added raster, or -1 on error
- */
-int
-rt_raster_generate_new_band(
- rt_raster raster, rt_pixtype pixtype,
- double initialvalue, uint32_t hasnodata, double nodatavalue,
- int index
-) {
- rt_band band = NULL;
- int width = 0;
- int height = 0;
- int numval = 0;
- int datasize = 0;
- int oldnumbands = 0;
- int numbands = 0;
- void * mem = NULL;
- int32_t checkvalint = 0;
- uint32_t checkvaluint = 0;
- double checkvaldouble = 0;
- float checkvalfloat = 0;
- int i;
-
-
- assert(NULL != raster);
-
- /* Make sure index is in a valid range */
- oldnumbands = rt_raster_get_num_bands(raster);
- if (index < 0)
- index = 0;
- else if (index > oldnumbands + 1)
- index = oldnumbands + 1;
-
- /* Determine size of memory block to allocate and allocate it */
- width = rt_raster_get_width(raster);
- height = rt_raster_get_height(raster);
- numval = width * height;
- datasize = rt_pixtype_size(pixtype) * numval;
-
- mem = (int *)rtalloc(datasize);
- if (!mem) {
- rterror("rt_raster_generate_new_band: Could not allocate memory for band");
- return -1;
- }
-
- if (FLT_EQ(initialvalue, 0.0))
- memset(mem, 0, datasize);
- else {
- switch (pixtype)
- {
- case PT_1BB:
- {
- uint8_t *ptr = mem;
- uint8_t clamped_initval = rt_util_clamp_to_1BB(initialvalue);
- for (i = 0; i < numval; i++)
- ptr[i] = clamped_initval;
- checkvalint = ptr[0];
- break;
- }
- case PT_2BUI:
- {
- uint8_t *ptr = mem;
- uint8_t clamped_initval = rt_util_clamp_to_2BUI(initialvalue);
- for (i = 0; i < numval; i++)
- ptr[i] = clamped_initval;
- checkvalint = ptr[0];
- break;
- }
- case PT_4BUI:
- {
- uint8_t *ptr = mem;
- uint8_t clamped_initval = rt_util_clamp_to_4BUI(initialvalue);
- for (i = 0; i < numval; i++)
- ptr[i] = clamped_initval;
- checkvalint = ptr[0];
- break;
- }
- case PT_8BSI:
- {
- int8_t *ptr = mem;
- int8_t clamped_initval = rt_util_clamp_to_8BSI(initialvalue);
- for (i = 0; i < numval; i++)
- ptr[i] = clamped_initval;
- checkvalint = ptr[0];
- break;
- }
- case PT_8BUI:
- {
- uint8_t *ptr = mem;
- uint8_t clamped_initval = rt_util_clamp_to_8BUI(initialvalue);
- for (i = 0; i < numval; i++)
- ptr[i] = clamped_initval;
- checkvalint = ptr[0];
- break;
- }
- case PT_16BSI:
- {
- int16_t *ptr = mem;
- int16_t clamped_initval = rt_util_clamp_to_16BSI(initialvalue);
- for (i = 0; i < numval; i++)
- ptr[i] = clamped_initval;
- checkvalint = ptr[0];
- break;
- }
- case PT_16BUI:
- {
- uint16_t *ptr = mem;
- uint16_t clamped_initval = rt_util_clamp_to_16BUI(initialvalue);
- for (i = 0; i < numval; i++)
- ptr[i] = clamped_initval;
- checkvalint = ptr[0];
- break;
- }
- case PT_32BSI:
- {
- int32_t *ptr = mem;
- int32_t clamped_initval = rt_util_clamp_to_32BSI(initialvalue);
- for (i = 0; i < numval; i++)
- ptr[i] = clamped_initval;
- checkvalint = ptr[0];
- break;
- }
- case PT_32BUI:
- {
- uint32_t *ptr = mem;
- uint32_t clamped_initval = rt_util_clamp_to_32BUI(initialvalue);
- for (i = 0; i < numval; i++)
- ptr[i] = clamped_initval;
- checkvaluint = ptr[0];
- break;
- }
- case PT_32BF:
- {
- float *ptr = mem;
- float clamped_initval = rt_util_clamp_to_32F(initialvalue);
- for (i = 0; i < numval; i++)
- ptr[i] = clamped_initval;
- checkvalfloat = ptr[0];
- break;
- }
- case PT_64BF:
- {
- double *ptr = mem;
- for (i = 0; i < numval; i++)
- ptr[i] = initialvalue;
- checkvaldouble = ptr[0];
- break;
- }
- default:
- {
- rterror("rt_raster_generate_new_band: Unknown pixeltype %d", pixtype);
- rtdealloc(mem);
- return -1;
- }
- }
- }
-
- /* Overflow checking */
- rt_util_dbl_trunc_warning(
- initialvalue,
- checkvalint, checkvaluint,
- checkvalfloat, checkvaldouble,
- pixtype
- );
-
- band = rt_band_new_inline(width, height, pixtype, hasnodata, nodatavalue, mem);
- if (! band) {
- rterror("rt_raster_generate_new_band: Could not add band to raster. Aborting");
- rtdealloc(mem);
- return -1;
- }
- rt_band_set_ownsdata_flag(band, 1); /* we DO own this data!!! */
- index = rt_raster_add_band(raster, band, index);
- numbands = rt_raster_get_num_bands(raster);
- if (numbands == oldnumbands || index == -1) {
- rterror("rt_raster_generate_new_band: Could not add band to raster. Aborting");
- rt_band_destroy(band);
- }
-
- /* set isnodata if hasnodata = TRUE and initial value = nodatavalue */
- if (hasnodata && FLT_EQ(initialvalue, nodatavalue))
- rt_band_set_isnodata_flag(band, 1);
-
- return index;
-}
-
-/**
- * Get 6-element array of raster inverse geotransform matrix
- *
- * @param raster : the raster to get matrix of
- * @param gt : optional input parameter, 6-element geotransform matrix
- * @param igt : output parameter, 6-element inverse geotransform matrix
- *
- * @return ES_NONE if success, ES_ERROR if error
- */
-rt_errorstate rt_raster_get_inverse_geotransform_matrix(
- rt_raster raster,
- double *gt, double *igt
-) {
- double _gt[6] = {0};
-
- assert((raster != NULL || gt != NULL));
- assert(igt != NULL);
-
- if (gt == NULL)
- rt_raster_get_geotransform_matrix(raster, _gt);
- else
- memcpy(_gt, gt, sizeof(double) * 6);
-
- if (!GDALInvGeoTransform(_gt, igt)) {
- rterror("rt_raster_get_inverse_geotransform_matrix: Could not compute inverse geotransform matrix");
- return ES_ERROR;
- }
-
- return ES_NONE;
-}
-
-/**
- * Get 6-element array of raster geotransform matrix
- *
- * @param raster : the raster to get matrix of
- * @param gt : output parameter, 6-element geotransform matrix
- *
- */
-void
-rt_raster_get_geotransform_matrix(rt_raster raster,
- double *gt) {
- assert(NULL != raster);
- assert(NULL != gt);
-
- gt[0] = raster->ipX;
- gt[1] = raster->scaleX;
- gt[2] = raster->skewX;
- gt[3] = raster->ipY;
- gt[4] = raster->skewY;
- gt[5] = raster->scaleY;
-}
-
-/**
- * Set raster's geotransform using 6-element array
- *
- * @param raster : the raster to set matrix of
- * @param gt : intput parameter, 6-element geotransform matrix
- *
- */
-void
-rt_raster_set_geotransform_matrix(rt_raster raster,
- double *gt) {
- assert(NULL != raster);
- assert(NULL != gt);
-
- raster->ipX = gt[0];
- raster->scaleX = gt[1];
- raster->skewX = gt[2];
- raster->ipY = gt[3];
- raster->skewY = gt[4];
- raster->scaleY = gt[5];
-
- _rt_raster_geotransform_warn_offline_band(raster);
-}
-
-/**
- * Convert an xr, yr raster point to an xw, yw point on map
- *
- * @param raster : the raster to get info from
- * @param xr : the pixel's column
- * @param yr : the pixel's row
- * @param xw : output parameter, X ordinate of the geographical point
- * @param yw : output parameter, Y ordinate of the geographical point
- * @param gt : input/output parameter, 3x2 geotransform matrix
- *
- * @return ES_NONE if success, ES_ERROR if error
- */
-rt_errorstate
-rt_raster_cell_to_geopoint(
- rt_raster raster,
- double xr, double yr,
- double *xw, double *yw,
- double *gt
-) {
- double _gt[6] = {0};
-
- assert(NULL != raster);
- assert(NULL != xw && NULL != yw);
-
- if (NULL != gt)
- memcpy(_gt, gt, sizeof(double) * 6);
-
- /* scale of matrix is not set */
- if (
- FLT_EQ(_gt[1], 0) ||
- FLT_EQ(_gt[5], 0)
- ) {
- rt_raster_get_geotransform_matrix(raster, _gt);
- }
-
- RASTER_DEBUGF(4, "gt = (%f, %f, %f, %f, %f, %f)",
- _gt[0],
- _gt[1],
- _gt[2],
- _gt[3],
- _gt[4],
- _gt[5]
- );
-
- GDALApplyGeoTransform(_gt, xr, yr, xw, yw);
- RASTER_DEBUGF(4, "GDALApplyGeoTransform (c -> g) for (%f, %f) = (%f, %f)",
- xr, yr, *xw, *yw);
-
- return ES_NONE;
-}
-
-/**
- * Convert an xw,yw map point to a xr,yr raster point
- *
- * @param raster : the raster to get info from
- * @param xw : X ordinate of the geographical point
- * @param yw : Y ordinate of the geographical point
- * @param xr : output parameter, the pixel's column
- * @param yr : output parameter, the pixel's row
- * @param igt : input/output parameter, inverse geotransform matrix
- *
- * @return ES_NONE if success, ES_ERROR if error
- */
-rt_errorstate
-rt_raster_geopoint_to_cell(
- rt_raster raster,
- double xw, double yw,
- double *xr, double *yr,
- double *igt
-) {
- double _igt[6] = {0};
- double rnd = 0;
-
- assert(NULL != raster);
- assert(NULL != xr && NULL != yr);
-
- if (igt != NULL)
- memcpy(_igt, igt, sizeof(double) * 6);
-
- /* matrix is not set */
- if (
- FLT_EQ(_igt[0], 0.) &&
- FLT_EQ(_igt[1], 0.) &&
- FLT_EQ(_igt[2], 0.) &&
- FLT_EQ(_igt[3], 0.) &&
- FLT_EQ(_igt[4], 0.) &&
- FLT_EQ(_igt[5], 0.)
- ) {
- if (rt_raster_get_inverse_geotransform_matrix(raster, NULL, _igt) != ES_NONE) {
- rterror("rt_raster_geopoint_to_cell: Could not get inverse geotransform matrix");
- return ES_ERROR;
- }
- }
-
- GDALApplyGeoTransform(_igt, xw, yw, xr, yr);
- RASTER_DEBUGF(4, "GDALApplyGeoTransform (g -> c) for (%f, %f) = (%f, %f)",
- xw, yw, *xr, *yr);
-
- rnd = ROUND(*xr, 0);
- if (FLT_EQ(rnd, *xr))
- *xr = rnd;
- else
- *xr = floor(*xr);
-
- rnd = ROUND(*yr, 0);
- if (FLT_EQ(rnd, *yr))
- *yr = rnd;
- else
- *yr = floor(*yr);
-
- RASTER_DEBUGF(4, "Corrected GDALApplyGeoTransform (g -> c) for (%f, %f) = (%f, %f)",
- xw, yw, *xr, *yr);
-
- return ES_NONE;
-}
-
-/**
- * Returns a set of "geomval" value, one for each group of pixel
- * sharing the same value for the provided band.
- *
- * A "geomval" value is a complex type composed of a geometry
- * in LWPOLY representation (one for each group of pixel sharing
- * the same value) and the value associated with this geometry.
- *
- * @param raster : the raster to get info from.
- * @param nband : the band to polygonize. 0-based
- * @param exclude_nodata_value : if non-zero, ignore nodata values
- * to check for pixels with value
- *
- * @return A set of "geomval" values, one for each group of pixels
- * sharing the same value for the provided band. The returned values are
- * LWPOLY geometries.
- */
-rt_geomval
-rt_raster_gdal_polygonize(
- rt_raster raster, int nband,
- int exclude_nodata_value,
- int *pnElements
-) {
- CPLErr cplerr = CE_None;
- char *pszQuery;
- long j;
- OGRSFDriverH ogr_drv = NULL;
- GDALDriverH gdal_drv = NULL;
- GDALDatasetH memdataset = NULL;
- GDALRasterBandH gdal_band = NULL;
- OGRDataSourceH memdatasource = NULL;
- rt_geomval pols = NULL;
- OGRLayerH hLayer = NULL;
- OGRFeatureH hFeature = NULL;
- OGRGeometryH hGeom = NULL;
- OGRFieldDefnH hFldDfn = NULL;
- unsigned char *wkb = NULL;
- int wkbsize = 0;
- LWGEOM *lwgeom = NULL;
- int nFeatureCount = 0;
- rt_band band = NULL;
- int iPixVal = -1;
- double dValue = 0.0;
- int iBandHasNodataValue = FALSE;
- double dBandNoData = 0.0;
-
- /* for checking that a geometry is valid */
- GEOSGeometry *ggeom = NULL;
- int isValid;
- LWGEOM *lwgeomValid = NULL;
-
- uint32_t bandNums[1] = {nband};
- int excludeNodataValues[1] = {exclude_nodata_value};
-
- /* checks */
- assert(NULL != raster);
- assert(NULL != pnElements);
-
- RASTER_DEBUG(2, "In rt_raster_gdal_polygonize");
-
- *pnElements = 0;
-
- /*******************************
- * Get band
- *******************************/
- band = rt_raster_get_band(raster, nband);
- if (NULL == band) {
- rterror("rt_raster_gdal_polygonize: Error getting band %d from raster", nband);
- return NULL;
- }
-
- if (exclude_nodata_value) {
-
- /* band is NODATA */
- if (rt_band_get_isnodata_flag(band)) {
- RASTER_DEBUG(3, "Band is NODATA. Returning null");
- *pnElements = 0;
- return NULL;
- }
-
- iBandHasNodataValue = rt_band_get_hasnodata_flag(band);
- if (iBandHasNodataValue)
- rt_band_get_nodata(band, &dBandNoData);
- else
- exclude_nodata_value = FALSE;
- }
-
- /*****************************************************
- * Convert raster to GDAL MEM dataset
- *****************************************************/
- memdataset = rt_raster_to_gdal_mem(raster, NULL, bandNums, excludeNodataValues, 1, &gdal_drv);
- if (NULL == memdataset) {
- rterror("rt_raster_gdal_polygonize: Couldn't convert raster to GDAL MEM dataset");
- return NULL;
- }
-
- /*****************************
- * Register ogr mem driver
- *****************************/
- OGRRegisterAll();
-
- RASTER_DEBUG(3, "creating OGR MEM vector");
-
- /*****************************************************
- * Create an OGR in-memory vector for layers
- *****************************************************/
- ogr_drv = OGRGetDriverByName("Memory");
- memdatasource = OGR_Dr_CreateDataSource(ogr_drv, "", NULL);
- if (NULL == memdatasource) {
- rterror("rt_raster_gdal_polygonize: Couldn't create a OGR Datasource to store pols");
- GDALClose(memdataset);
- return NULL;
- }
-
- /* Can MEM driver create new layers? */
- if (!OGR_DS_TestCapability(memdatasource, ODsCCreateLayer)) {
- rterror("rt_raster_gdal_polygonize: MEM driver can't create new layers, aborting");
-
- /* xxx jorgearevalo: what should we do now? */
- GDALClose(memdataset);
- OGRReleaseDataSource(memdatasource);
-
- return NULL;
- }
-
- RASTER_DEBUG(3, "polygonizying GDAL MEM raster band");
-
- /*****************************
- * Polygonize the raster band
- *****************************/
-
- /**
- * From GDALPolygonize function header: "Polygon features will be
- * created on the output layer, with polygon geometries representing
- * the polygons". So,the WKB geometry type should be "wkbPolygon"
- **/
- hLayer = OGR_DS_CreateLayer(memdatasource, "PolygonizedLayer", NULL, wkbPolygon, NULL);
-
- if (NULL == hLayer) {
- rterror("rt_raster_gdal_polygonize: Couldn't create layer to store polygons");
-
- GDALClose(memdataset);
- OGRReleaseDataSource(memdatasource);
-
- return NULL;
- }
-
- /**
- * Create a new field in the layer, to store the px value
- */
-
- /* First, create a field definition to create the field */
- hFldDfn = OGR_Fld_Create("PixelValue", OFTReal);
-
- /* Second, create the field */
- if (OGR_L_CreateField(hLayer, hFldDfn, TRUE) != OGRERR_NONE) {
- rtwarn("Couldn't create a field in OGR Layer. The polygons generated won't be able to store the pixel value");
- iPixVal = -1;
- }
- else {
- /* Index to the new field created in the layer */
- iPixVal = 0;
- }
-
- /* Get GDAL raster band */
- gdal_band = GDALGetRasterBand(memdataset, 1);
- if (NULL == gdal_band) {
- rterror("rt_raster_gdal_polygonize: Couldn't get GDAL band to polygonize");
-
- GDALClose(memdataset);
- OGR_Fld_Destroy(hFldDfn);
- OGR_DS_DeleteLayer(memdatasource, 0);
- OGRReleaseDataSource(memdatasource);
-
- return NULL;
- }
-
- /**
- * We don't need a raster mask band. Each band has a nodata value.
- **/
-#ifdef GDALFPOLYGONIZE
- cplerr = GDALFPolygonize(gdal_band, NULL, hLayer, iPixVal, NULL, NULL, NULL);
-#else
- cplerr = GDALPolygonize(gdal_band, NULL, hLayer, iPixVal, NULL, NULL, NULL);
-#endif
-
- if (cplerr != CE_None) {
- rterror("rt_raster_gdal_polygonize: Could not polygonize GDAL band");
-
- GDALClose(memdataset);
- OGR_Fld_Destroy(hFldDfn);
- OGR_DS_DeleteLayer(memdatasource, 0);
- OGRReleaseDataSource(memdatasource);
-
- return NULL;
- }
-
- /**
- * Optimization: Apply a OGR SQL filter to the layer to select the
- * features different from NODATA value.
- *
- * Thanks to David Zwarg.
- **/
- if (iBandHasNodataValue) {
- pszQuery = (char *) rtalloc(50 * sizeof (char));
- sprintf(pszQuery, "PixelValue != %f", dBandNoData );
- OGRErr e = OGR_L_SetAttributeFilter(hLayer, pszQuery);
- if (e != OGRERR_NONE) {
- rtwarn("Error filtering NODATA values for band. All values will be treated as data values");
- }
- }
- else {
- pszQuery = NULL;
- }
-
- /*********************************************************************
- * Transform OGR layers to WKB polygons
- * XXX jorgearevalo: GDALPolygonize does not set the coordinate system
- * on the output layer. Application code should do this when the layer
- * is created, presumably matching the raster coordinate system.
- *********************************************************************/
- nFeatureCount = OGR_L_GetFeatureCount(hLayer, TRUE);
-
- /* Allocate memory for pols */
- pols = (rt_geomval) rtalloc(nFeatureCount * sizeof(struct rt_geomval_t));
-
- if (NULL == pols) {
- rterror("rt_raster_gdal_polygonize: Could not allocate memory for geomval set");
-
- GDALClose(memdataset);
- OGR_Fld_Destroy(hFldDfn);
- OGR_DS_DeleteLayer(memdatasource, 0);
- if (NULL != pszQuery)
- rtdealloc(pszQuery);
- OGRReleaseDataSource(memdatasource);
-
- return NULL;
- }
-
- /* initialize GEOS */
- initGEOS(lwnotice, lwgeom_geos_error);
-
- RASTER_DEBUGF(3, "storing polygons (%d)", nFeatureCount);
-
- /* Reset feature reading to start in the first feature */
- OGR_L_ResetReading(hLayer);
-
- for (j = 0; j < nFeatureCount; j++) {
- hFeature = OGR_L_GetNextFeature(hLayer);
- dValue = OGR_F_GetFieldAsDouble(hFeature, iPixVal);
-
- hGeom = OGR_F_GetGeometryRef(hFeature);
- wkbsize = OGR_G_WkbSize(hGeom);
-
- /* allocate wkb buffer */
- wkb = rtalloc(sizeof(unsigned char) * wkbsize);
- if (wkb == NULL) {
- rterror("rt_raster_gdal_polygonize: Could not allocate memory for WKB buffer");
-
- OGR_F_Destroy(hFeature);
- GDALClose(memdataset);
- OGR_Fld_Destroy(hFldDfn);
- OGR_DS_DeleteLayer(memdatasource, 0);
- if (NULL != pszQuery)
- rtdealloc(pszQuery);
- OGRReleaseDataSource(memdatasource);
-
- return NULL;
- }
-
- /* export WKB with LSB byte order */
- OGR_G_ExportToWkb(hGeom, wkbNDR, wkb);
-
- /* convert WKB to LWGEOM */
- lwgeom = lwgeom_from_wkb(wkb, wkbsize, LW_PARSER_CHECK_NONE);
-
-#if POSTGIS_DEBUG_LEVEL > 3
- {
- char *wkt = NULL;
- OGR_G_ExportToWkt(hGeom, &wkt);
- RASTER_DEBUGF(4, "GDAL wkt = %s", wkt);
- CPLFree(wkt);
-
- d_print_binary_hex("GDAL wkb", wkb, wkbsize);
- }
-#endif
-
- /* cleanup unnecessary stuff */
- rtdealloc(wkb);
- wkb = NULL;
- wkbsize = 0;
-
- OGR_F_Destroy(hFeature);
-
- /* specify SRID */
- lwgeom_set_srid(lwgeom, rt_raster_get_srid(raster));
-
- /*
- is geometry valid?
- if not, try to make valid
- */
- do {
-#if POSTGIS_GEOS_VERSION < 33
- /* nothing can be done if the geometry was invalid if GEOS < 3.3 */
- break;
-#endif
-
- ggeom = (GEOSGeometry *) LWGEOM2GEOS(lwgeom);
- if (ggeom == NULL) {
- rtwarn("Cannot test geometry for validity");
- break;
- }
-
- isValid = GEOSisValid(ggeom);
-
- GEOSGeom_destroy(ggeom);
- ggeom = NULL;
-
- /* geometry is valid */
- if (isValid)
- break;
-
- RASTER_DEBUG(3, "fixing invalid geometry");
-
- /* make geometry valid */
- lwgeomValid = lwgeom_make_valid(lwgeom);
- if (lwgeomValid == NULL) {
- rtwarn("Cannot fix invalid geometry");
- break;
- }
-
- lwgeom_free(lwgeom);
- lwgeom = lwgeomValid;
- }
- while (0);
-
- /* save lwgeom */
- pols[j].geom = lwgeom_as_lwpoly(lwgeom);
-
-#if POSTGIS_DEBUG_LEVEL > 3
- {
- char *wkt = lwgeom_to_wkt(lwgeom, WKT_ISO, DBL_DIG, NULL);
- RASTER_DEBUGF(4, "LWGEOM wkt = %s", wkt);
- rtdealloc(wkt);
-
- size_t lwwkbsize = 0;
- uint8_t *lwwkb = lwgeom_to_wkb(lwgeom, WKB_ISO | WKB_NDR, &lwwkbsize);
- if (lwwkbsize) {
- d_print_binary_hex("LWGEOM wkb", lwwkb, lwwkbsize);
- rtdealloc(lwwkb);
- }
- }
-#endif
-
- /* set pixel value */
- pols[j].val = dValue;
- }
-
- *pnElements = nFeatureCount;
-
- RASTER_DEBUG(3, "destroying GDAL MEM raster");
- GDALClose(memdataset);
-
- RASTER_DEBUG(3, "destroying OGR MEM vector");
- OGR_Fld_Destroy(hFldDfn);
- OGR_DS_DeleteLayer(memdatasource, 0);
- if (NULL != pszQuery) rtdealloc(pszQuery);
- OGRReleaseDataSource(memdatasource);
-
- return pols;
-}
-
-/**
- * Get raster's convex hull.
- *
- * The convex hull is typically a 4 vertices (5 to be closed)
- * single ring polygon bearing the raster's rotation and using
- * projection coordinates.
- *
- * @param raster : the raster to get info from
- * @param **hull : pointer to convex hull
- *
- * @return ES_NONE if success, ES_ERROR if error
- */
-rt_errorstate
-rt_raster_get_convex_hull(rt_raster raster, LWGEOM **hull) {
- double gt[6] = {0.0};
- int srid = SRID_UNKNOWN;
-
- POINTARRAY *pts = NULL;
- POINT4D p4d;
-
- assert(hull != NULL);
- *hull = NULL;
-
- /* raster is NULL, convex hull is NULL */
- if (raster == NULL)
- return ES_NONE;
-
- /* raster metadata */
- srid = rt_raster_get_srid(raster);
- rt_raster_get_geotransform_matrix(raster, gt);
-
- RASTER_DEBUGF(3, "rt_raster_get_convex_hull: raster is %dx%d", raster->width, raster->height);
-
- /* return point or line since at least one of the two dimensions is 0 */
- if ((!raster->width) || (!raster->height)) {
- p4d.x = gt[0];
- p4d.y = gt[3];
-
- /* return point */
- if (!raster->width && !raster->height) {
- LWPOINT *point = lwpoint_make2d(srid, p4d.x, p4d.y);
- *hull = lwpoint_as_lwgeom(point);
- }
- /* return linestring */
- else {
- LWLINE *line = NULL;
- pts = ptarray_construct_empty(0, 0, 2);
-
- /* first point of line */
- ptarray_append_point(pts, &p4d, LW_TRUE);
-
- /* second point of line */
- if (rt_raster_cell_to_geopoint(
- raster,
- rt_raster_get_width(raster), rt_raster_get_height(raster),
- &p4d.x, &p4d.y,
- gt
- ) != ES_NONE) {
- rterror("rt_raster_get_convex_hull: Could not get second point for linestring");
- return ES_ERROR;
- }
- ptarray_append_point(pts, &p4d, LW_TRUE);
- line = lwline_construct(srid, NULL, pts);
-
- *hull = lwline_as_lwgeom(line);
- }
-
- return ES_NONE;
- }
- else {
- POINTARRAY **rings = NULL;
- LWPOLY* poly = NULL;
-
- /* only one ring */
- rings = (POINTARRAY **) rtalloc(sizeof (POINTARRAY*));
- if (!rings) {
- rterror("rt_raster_get_convex_hull: Could not allocate memory for polygon ring");
- return ES_ERROR;
- }
- rings[0] = ptarray_construct(0, 0, 5);
- /* TODO: handle error on ptarray construction */
- /* XXX jorgearevalo: the error conditions aren't managed in ptarray_construct */
- if (!rings[0]) {
- rterror("rt_raster_get_convex_hull: Could not construct point array");
- return ES_ERROR;
- }
- pts = rings[0];
-
- /* Upper-left corner (first and last points) */
- p4d.x = gt[0];
- p4d.y = gt[3];
- ptarray_set_point4d(pts, 0, &p4d);
- ptarray_set_point4d(pts, 4, &p4d);
-
- /* Upper-right corner (we go clockwise) */
- rt_raster_cell_to_geopoint(
- raster,
- raster->width, 0,
- &p4d.x, &p4d.y,
- gt
- );
- ptarray_set_point4d(pts, 1, &p4d);
-
- /* Lower-right corner */
- rt_raster_cell_to_geopoint(
- raster,
- raster->width, raster->height,
- &p4d.x, &p4d.y,
- gt
- );
- ptarray_set_point4d(pts, 2, &p4d);
-
- /* Lower-left corner */
- rt_raster_cell_to_geopoint(
- raster,
- 0, raster->height,
- &p4d.x, &p4d.y,
- gt
- );
- ptarray_set_point4d(pts, 3, &p4d);
-
- poly = lwpoly_construct(srid, 0, 1, rings);
- *hull = lwpoly_as_lwgeom(poly);
- }
-
- return ES_NONE;
-}
-
-/**
- * Get raster's envelope.
- *
- * The envelope is the minimum bounding rectangle of the raster
- *
- * @param raster : the raster to get envelope of
- * @param env : pointer to rt_envelope
- *
- * @return ES_NONE if success, ES_ERROR if error
- */
-rt_errorstate rt_raster_get_envelope(
- rt_raster raster,
- rt_envelope *env
-) {
- int i;
- int rtn;
- int set = 0;
- double _r[2] = {0.};
- double _w[2] = {0.};
- double _gt[6] = {0.};
-
- assert(raster != NULL);
- assert(env != NULL);
-
- rt_raster_get_geotransform_matrix(raster, _gt);
-
- for (i = 0; i < 4; i++) {
- switch (i) {
- case 0:
- _r[0] = 0;
- _r[1] = 0;
- break;
- case 1:
- _r[0] = 0;
- _r[1] = raster->height;
- break;
- case 2:
- _r[0] = raster->width;
- _r[1] = raster->height;
- break;
- case 3:
- _r[0] = raster->width;
- _r[1] = 0;
- break;
- }
-
- rtn = rt_raster_cell_to_geopoint(
- raster,
- _r[0], _r[1],
- &(_w[0]), &(_w[1]),
- _gt
- );
- if (rtn != ES_NONE) {
- rterror("rt_raster_get_envelope: Could not compute spatial coordinates for raster pixel");
- return ES_ERROR;
- }
-
- if (!set) {
- set = 1;
- env->MinX = _w[0];
- env->MaxX = _w[0];
- env->MinY = _w[1];
- env->MaxY = _w[1];
- }
- else {
- if (_w[0] < env->MinX)
- env->MinX = _w[0];
- else if (_w[0] > env->MaxX)
- env->MaxX = _w[0];
-
- if (_w[1] < env->MinY)
- env->MinY = _w[1];
- else if (_w[1] > env->MaxY)
- env->MaxY = _w[1];
- }
- }
-
- return ES_NONE;
-}
-
-/*
- * Compute skewed extent that covers unskewed extent.
- *
- * @param envelope : unskewed extent of type rt_envelope
- * @param skew : pointer to 2-element array (x, y) of skew
- * @param scale : pointer to 2-element array (x, y) of scale
- * @param tolerance : value between 0 and 1 where the smaller the tolerance
- * results in an extent approaching the "minimum" skewed extent.
- * If value <= 0, tolerance = 0.1. If value > 1, tolerance = 1.
- *
- * @return skewed raster who's extent covers unskewed extent, NULL on error
- */
-rt_raster
-rt_raster_compute_skewed_raster(
- rt_envelope extent,
- double *skew,
- double *scale,
- double tolerance
-) {
- uint32_t run = 0;
- uint32_t max_run = 1;
- double dbl_run = 0;
-
- int rtn;
- int covers = 0;
- rt_raster raster;
- double _gt[6] = {0};
- double _igt[6] = {0};
- int _d[2] = {1, -1};
- int _dlast = 0;
- int _dlastpos = 0;
- double _w[2] = {0};
- double _r[2] = {0};
- double _xy[2] = {0};
- int i;
- int j;
- int x;
- int y;
-
- LWGEOM *geom = NULL;
- GEOSGeometry *sgeom = NULL;
- GEOSGeometry *ngeom = NULL;
-
- if (
- (tolerance < 0.) ||
- FLT_EQ(tolerance, 0.)
- ) {
- tolerance = 0.1;
- }
- else if (tolerance > 1.)
- tolerance = 1;
-
- dbl_run = tolerance;
- while (dbl_run < 10) {
- dbl_run *= 10.;
- max_run *= 10;
- }
-
- /* scale must be provided */
- if (scale == NULL)
- return NULL;
- for (i = 0; i < 2; i++) {
- if (FLT_EQ(scale[i], 0)) {
- rterror("rt_raster_compute_skewed_raster: Scale cannot be zero");
- return 0;
- }
-
- if (i < 1)
- _gt[1] = fabs(scale[i] * tolerance);
- else
- _gt[5] = fabs(scale[i] * tolerance);
- }
- /* conform scale-y to be negative */
- _gt[5] *= -1;
-
- /* skew not provided or skew is zero, return raster of correct dim and spatial attributes */
- if (
- (skew == NULL) || (
- FLT_EQ(skew[0], 0) &&
- FLT_EQ(skew[1], 0)
- )
- ) {
- int _dim[2] = {
- (int) fmax((fabs(extent.MaxX - extent.MinX) + (fabs(scale[0]) / 2.)) / fabs(scale[0]), 1),
- (int) fmax((fabs(extent.MaxY - extent.MinY) + (fabs(scale[1]) / 2.)) / fabs(scale[1]), 1)
- };
-
- raster = rt_raster_new(_dim[0], _dim[1]);
- if (raster == NULL) {
- rterror("rt_raster_compute_skewed_raster: Could not create output raster");
- return NULL;
- }
-
- rt_raster_set_offsets(raster, extent.MinX, extent.MaxY);
- rt_raster_set_scale(raster, fabs(scale[0]), -1 * fabs(scale[1]));
- rt_raster_set_skews(raster, skew[0], skew[1]);
-
- return raster;
- }
-
- /* direction to shift upper-left corner */
- if (skew[0] > 0.)
- _d[0] = -1;
- if (skew[1] < 0.)
- _d[1] = 1;
-
- /* geotransform */
- _gt[0] = extent.UpperLeftX;
- _gt[2] = skew[0] * tolerance;
- _gt[3] = extent.UpperLeftY;
- _gt[4] = skew[1] * tolerance;
-
- RASTER_DEBUGF(4, "Initial geotransform: %f, %f, %f, %f, %f, %f",
- _gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]
- );
- RASTER_DEBUGF(4, "Delta: %d, %d", _d[0], _d[1]);
-
- /* simple raster */
- if ((raster = rt_raster_new(1, 1)) == NULL) {
- rterror("rt_raster_compute_skewed_raster: Out of memory allocating extent raster");
- return NULL;
- }
- rt_raster_set_geotransform_matrix(raster, _gt);
-
- /* get inverse geotransform matrix */
- if (!GDALInvGeoTransform(_gt, _igt)) {
- rterror("rt_raster_compute_skewed_raster: Could not compute inverse geotransform matrix");
- rt_raster_destroy(raster);
- return NULL;
- }
- RASTER_DEBUGF(4, "Inverse geotransform: %f, %f, %f, %f, %f, %f",
- _igt[0], _igt[1], _igt[2], _igt[3], _igt[4], _igt[5]
- );
-
- /* shift along axis */
- for (i = 0; i < 2; i++) {
- covers = 0;
- run = 0;
-
- RASTER_DEBUGF(3, "Shifting along %s axis", i < 1 ? "X" : "Y");
-
- do {
-
- /* prevent possible infinite loop */
- if (run > max_run) {
- rterror("rt_raster_compute_skewed_raster: Could not compute skewed extent due to check preventing infinite loop");
- rt_raster_destroy(raster);
- return NULL;
- }
-
- /*
- check the four corners that they are covered along the specific axis
- pixel column should be >= 0
- */
- for (j = 0; j < 4; j++) {
- switch (j) {
- /* upper-left */
- case 0:
- _xy[0] = extent.MinX;
- _xy[1] = extent.MaxY;
- break;
- /* lower-left */
- case 1:
- _xy[0] = extent.MinX;
- _xy[1] = extent.MinY;
- break;
- /* lower-right */
- case 2:
- _xy[0] = extent.MaxX;
- _xy[1] = extent.MinY;
- break;
- /* upper-right */
- case 3:
- _xy[0] = extent.MaxX;
- _xy[1] = extent.MaxY;
- break;
- }
-
- rtn = rt_raster_geopoint_to_cell(
- raster,
- _xy[0], _xy[1],
- &(_r[0]), &(_r[1]),
- _igt
- );
- if (rtn != ES_NONE) {
- rterror("rt_raster_compute_skewed_raster: Could not compute raster pixel for spatial coordinates");
- rt_raster_destroy(raster);
- return NULL;
- }
-
- RASTER_DEBUGF(4, "Point %d at cell %d x %d", j, (int) _r[0], (int) _r[1]);
-
- /* raster doesn't cover point */
- if ((int) _r[i] < 0) {
- RASTER_DEBUGF(4, "Point outside of skewed extent: %d", j);
- covers = 0;
-
- if (_dlastpos != j) {
- _dlast = (int) _r[i];
- _dlastpos = j;
- }
- else if ((int) _r[i] < _dlast) {
- RASTER_DEBUG(4, "Point going in wrong direction. Reversing direction");
- _d[i] *= -1;
- _dlastpos = -1;
- run = 0;
- }
-
- break;
- }
-
- covers++;
- }
-
- if (!covers) {
- x = 0;
- y = 0;
- if (i < 1)
- x = _d[i] * fabs(_r[i]);
- else
- y = _d[i] * fabs(_r[i]);
-
- rtn = rt_raster_cell_to_geopoint(
- raster,
- x, y,
- &(_w[0]), &(_w[1]),
- _gt
- );
- if (rtn != ES_NONE) {
- rterror("rt_raster_compute_skewed_raster: Could not compute spatial coordinates for raster pixel");
- rt_raster_destroy(raster);
- return NULL;
- }
-
- /* adjust ul */
- if (i < 1)
- _gt[0] = _w[i];
- else
- _gt[3] = _w[i];
- rt_raster_set_geotransform_matrix(raster, _gt);
- RASTER_DEBUGF(4, "Shifted geotransform: %f, %f, %f, %f, %f, %f",
- _gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]
- );
-
- /* get inverse geotransform matrix */
- if (!GDALInvGeoTransform(_gt, _igt)) {
- rterror("rt_raster_compute_skewed_raster: Could not compute inverse geotransform matrix");
- rt_raster_destroy(raster);
- return NULL;
- }
- RASTER_DEBUGF(4, "Inverse geotransform: %f, %f, %f, %f, %f, %f",
- _igt[0], _igt[1], _igt[2], _igt[3], _igt[4], _igt[5]
- );
- }
-
- run++;
- }
- while (!covers);
- }
-
- /* covers test */
- rtn = rt_raster_geopoint_to_cell(
- raster,
- extent.MaxX, extent.MinY,
- &(_r[0]), &(_r[1]),
- _igt
- );
- if (rtn != ES_NONE) {
- rterror("rt_raster_compute_skewed_raster: Could not compute raster pixel for spatial coordinates");
- rt_raster_destroy(raster);
- return NULL;
- }
-
- RASTER_DEBUGF(4, "geopoint %f x %f at cell %d x %d", extent.MaxX, extent.MinY, (int) _r[0], (int) _r[1]);
-
- raster->width = _r[0];
- raster->height = _r[1];
-
- /* initialize GEOS */
- initGEOS(lwnotice, lwgeom_geos_error);
-
- /* create reference LWPOLY */
- {
- LWPOLY *npoly = rt_util_envelope_to_lwpoly(extent);
- if (npoly == NULL) {
- rterror("rt_raster_compute_skewed_raster: Could not build extent's geometry for covers test");
- rt_raster_destroy(raster);
- return NULL;
- }
-
- ngeom = (GEOSGeometry *) LWGEOM2GEOS(lwpoly_as_lwgeom(npoly));
- lwpoly_free(npoly);
- }
-
- do {
- covers = 0;
-
- /* construct sgeom from raster */
- if ((rt_raster_get_convex_hull(raster, &geom) != ES_NONE) || geom == NULL) {
- rterror("rt_raster_compute_skewed_raster: Could not build skewed extent's geometry for covers test");
- GEOSGeom_destroy(ngeom);
- rt_raster_destroy(raster);
- return NULL;
- }
-
- sgeom = (GEOSGeometry *) LWGEOM2GEOS(geom);
- lwgeom_free(geom);
-
- covers = GEOSRelatePattern(sgeom, ngeom, "******FF*");
- GEOSGeom_destroy(sgeom);
-
- if (covers == 2) {
- rterror("rt_raster_compute_skewed_raster: Could not run covers test");
- GEOSGeom_destroy(ngeom);
- rt_raster_destroy(raster);
- return NULL;
- }
-
- if (covers)
- break;
-
- raster->width++;
- raster->height++;
- }
- while (!covers);
-
- RASTER_DEBUGF(4, "Skewed extent does cover normal extent with dimensions %d x %d", raster->width, raster->height);
-
- raster->width = (int) ((((double) raster->width) * fabs(_gt[1]) + fabs(scale[0] / 2.)) / fabs(scale[0]));
- raster->height = (int) ((((double) raster->height) * fabs(_gt[5]) + fabs(scale[1] / 2.)) / fabs(scale[1]));
- _gt[1] = fabs(scale[0]);
- _gt[5] = -1 * fabs(scale[1]);
- _gt[2] = skew[0];
- _gt[4] = skew[1];
- rt_raster_set_geotransform_matrix(raster, _gt);
-
- /* minimize width/height */
- for (i = 0; i < 2; i++) {
- covers = 1;
- do {
- if (i < 1)
- raster->width--;
- else
- raster->height--;
-
- /* construct sgeom from raster */
- if ((rt_raster_get_convex_hull(raster, &geom) != ES_NONE) || geom == NULL) {
- rterror("rt_raster_compute_skewed_raster: Could not build skewed extent's geometry for minimizing dimensions");
- GEOSGeom_destroy(ngeom);
- rt_raster_destroy(raster);
- return NULL;
- }
-
- sgeom = (GEOSGeometry *) LWGEOM2GEOS(geom);
- lwgeom_free(geom);
-
- covers = GEOSRelatePattern(sgeom, ngeom, "******FF*");
- GEOSGeom_destroy(sgeom);
-
- if (covers == 2) {
- rterror("rt_raster_compute_skewed_raster: Could not run covers test for minimizing dimensions");
- GEOSGeom_destroy(ngeom);
- rt_raster_destroy(raster);
- return NULL;
- }
-
- if (!covers) {
- if (i < 1)
- raster->width++;
- else
- raster->height++;
-
- break;
- }
- }
- while (covers);
- }
-
- GEOSGeom_destroy(ngeom);
-
- return raster;
-}
-
-/*--------- WKB I/O ---------------------------------------------------*/
-
-static uint8_t
-isMachineLittleEndian(void) {
- static int endian_check_int = 1; /* dont modify this!!! */
- /* 0=big endian|xdr -- 1=little endian|ndr */
- return *((uint8_t *) & endian_check_int);
-}
-
-static uint8_t
-read_uint8(const uint8_t** from) {
- assert(NULL != from);
-
- return *(*from)++;
-}
-
-/* unused up to now
-static void
-write_uint8(uint8_t** from, uint8_t v)
-{
- assert(NULL != from);
-
- *(*from)++ = v;
-}
- */
-
-static int8_t
-read_int8(const uint8_t** from) {
- assert(NULL != from);
-
- return (int8_t) read_uint8(from);
-}
-
-/* unused up to now
-static void
-write_int8(uint8_t** from, int8_t v)
-{
- assert(NULL != from);
-
- *(*from)++ = v;
-}
- */
-
-static uint16_t
-read_uint16(const uint8_t** from, uint8_t littleEndian) {
- uint16_t ret = 0;
-
- assert(NULL != from);
-
- if (littleEndian) {
- ret = (*from)[0] |
- (*from)[1] << 8;
- } else {
- /* big endian */
- ret = (*from)[0] << 8 |
- (*from)[1];
- }
- *from += 2;
- return ret;
-}
-
-static void
-write_uint16(uint8_t** to, uint8_t littleEndian, uint16_t v) {
- assert(NULL != to);
-
- if (littleEndian) {
- (*to)[0] = v & 0x00FF;
- (*to)[1] = v >> 8;
- } else {
- (*to)[1] = v & 0x00FF;
- (*to)[0] = v >> 8;
- }
- *to += 2;
-}
-
-static int16_t
-read_int16(const uint8_t** from, uint8_t littleEndian) {
- assert(NULL != from);
-
- return read_uint16(from, littleEndian);
-}
-
-/* unused up to now
-static void
-write_int16(uint8_t** to, uint8_t littleEndian, int16_t v)
-{
- assert(NULL != to);
-
- if ( littleEndian )
- {
- (*to)[0] = v & 0x00FF;
- (*to)[1] = v >> 8;
- }
- else
- {
- (*to)[1] = v & 0x00FF;
- (*to)[0] = v >> 8;
- }
- *to += 2;
-}
- */
-
-static uint32_t
-read_uint32(const uint8_t** from, uint8_t littleEndian) {
- uint32_t ret = 0;
-
- assert(NULL != from);
-
- if (littleEndian) {
- ret = (uint32_t) ((*from)[0] & 0xff) |
- (uint32_t) ((*from)[1] & 0xff) << 8 |
- (uint32_t) ((*from)[2] & 0xff) << 16 |
- (uint32_t) ((*from)[3] & 0xff) << 24;
- } else {
- /* big endian */
- ret = (uint32_t) ((*from)[3] & 0xff) |
- (uint32_t) ((*from)[2] & 0xff) << 8 |
- (uint32_t) ((*from)[1] & 0xff) << 16 |
- (uint32_t) ((*from)[0] & 0xff) << 24;
- }
-
- *from += 4;
- return ret;
-}
-
-/* unused up to now
-static void
-write_uint32(uint8_t** to, uint8_t littleEndian, uint32_t v)
-{
- assert(NULL != to);
-
- if ( littleEndian )
- {
- (*to)[0] = v & 0x000000FF;
- (*to)[1] = ( v & 0x0000FF00 ) >> 8;
- (*to)[2] = ( v & 0x00FF0000 ) >> 16;
- (*to)[3] = ( v & 0xFF000000 ) >> 24;
- }
- else
- {
- (*to)[3] = v & 0x000000FF;
- (*to)[2] = ( v & 0x0000FF00 ) >> 8;
- (*to)[1] = ( v & 0x00FF0000 ) >> 16;
- (*to)[0] = ( v & 0xFF000000 ) >> 24;
- }
- *to += 4;
-}
- */
-
-static int32_t
-read_int32(const uint8_t** from, uint8_t littleEndian) {
- assert(NULL != from);
-
- return read_uint32(from, littleEndian);
-}
-
-/* unused up to now
-static void
-write_int32(uint8_t** to, uint8_t littleEndian, int32_t v)
-{
- assert(NULL != to);
-
- if ( littleEndian )
- {
- (*to)[0] = v & 0x000000FF;
- (*to)[1] = ( v & 0x0000FF00 ) >> 8;
- (*to)[2] = ( v & 0x00FF0000 ) >> 16;
- (*to)[3] = ( v & 0xFF000000 ) >> 24;
- }
- else
- {
- (*to)[3] = v & 0x000000FF;
- (*to)[2] = ( v & 0x0000FF00 ) >> 8;
- (*to)[1] = ( v & 0x00FF0000 ) >> 16;
- (*to)[0] = ( v & 0xFF000000 ) >> 24;
- }
- *to += 4;
-}
- */
-
-static float
-read_float32(const uint8_t** from, uint8_t littleEndian) {
-
- union {
- float f;
- uint32_t i;
- } ret;
-
- ret.i = read_uint32(from, littleEndian);
-
- return ret.f;
-}
-
-/* unused up to now
-static void
-write_float32(uint8_t** from, uint8_t littleEndian, float f)
-{
- union {
- float f;
- uint32_t i;
- } u;
-
- u.f = f;
- write_uint32(from, littleEndian, u.i);
-}
- */
-
-static double
-read_float64(const uint8_t** from, uint8_t littleEndian) {
-
- union {
- double d;
- uint64_t i;
- } ret;
-
- assert(NULL != from);
-
- if (littleEndian) {
- ret.i = (uint64_t) ((*from)[0] & 0xff) |
- (uint64_t) ((*from)[1] & 0xff) << 8 |
- (uint64_t) ((*from)[2] & 0xff) << 16 |
- (uint64_t) ((*from)[3] & 0xff) << 24 |
- (uint64_t) ((*from)[4] & 0xff) << 32 |
- (uint64_t) ((*from)[5] & 0xff) << 40 |
- (uint64_t) ((*from)[6] & 0xff) << 48 |
- (uint64_t) ((*from)[7] & 0xff) << 56;
- } else {
- /* big endian */
- ret.i = (uint64_t) ((*from)[7] & 0xff) |
- (uint64_t) ((*from)[6] & 0xff) << 8 |
- (uint64_t) ((*from)[5] & 0xff) << 16 |
- (uint64_t) ((*from)[4] & 0xff) << 24 |
- (uint64_t) ((*from)[3] & 0xff) << 32 |
- (uint64_t) ((*from)[2] & 0xff) << 40 |
- (uint64_t) ((*from)[1] & 0xff) << 48 |
- (uint64_t) ((*from)[0] & 0xff) << 56;
- }
-
- *from += 8;
- return ret.d;
-}
-
-/* unused up to now
-static void
-write_float64(uint8_t** to, uint8_t littleEndian, double v)
-{
- union {
- double d;
- uint64_t i;
- } u;
-
- assert(NULL != to);
-
- u.d = v;
-
- if ( littleEndian )
- {
- (*to)[0] = u.i & 0x00000000000000FFULL;
- (*to)[1] = ( u.i & 0x000000000000FF00ULL ) >> 8;
- (*to)[2] = ( u.i & 0x0000000000FF0000ULL ) >> 16;
- (*to)[3] = ( u.i & 0x00000000FF000000ULL ) >> 24;
- (*to)[4] = ( u.i & 0x000000FF00000000ULL ) >> 32;
- (*to)[5] = ( u.i & 0x0000FF0000000000ULL ) >> 40;
- (*to)[6] = ( u.i & 0x00FF000000000000ULL ) >> 48;
- (*to)[7] = ( u.i & 0xFF00000000000000ULL ) >> 56;
- }
- else
- {
- (*to)[7] = u.i & 0x00000000000000FFULL;
- (*to)[6] = ( u.i & 0x000000000000FF00ULL ) >> 8;
- (*to)[5] = ( u.i & 0x0000000000FF0000ULL ) >> 16;
- (*to)[4] = ( u.i & 0x00000000FF000000ULL ) >> 24;
- (*to)[3] = ( u.i & 0x000000FF00000000ULL ) >> 32;
- (*to)[2] = ( u.i & 0x0000FF0000000000ULL ) >> 40;
- (*to)[1] = ( u.i & 0x00FF000000000000ULL ) >> 48;
- (*to)[0] = ( u.i & 0xFF00000000000000ULL ) >> 56;
- }
- *to += 8;
-}
- */
-
-#define BANDTYPE_FLAGS_MASK 0xF0
-#define BANDTYPE_PIXTYPE_MASK 0x0F
-#define BANDTYPE_FLAG_OFFDB (1<<7)
-#define BANDTYPE_FLAG_HASNODATA (1<<6)
-#define BANDTYPE_FLAG_ISNODATA (1<<5)
-#define BANDTYPE_FLAG_RESERVED3 (1<<4)
-
-#define BANDTYPE_PIXTYPE(x) ((x)&BANDTYPE_PIXTYPE_MASK)
-#define BANDTYPE_IS_OFFDB(x) ((x)&BANDTYPE_FLAG_OFFDB)
-#define BANDTYPE_HAS_NODATA(x) ((x)&BANDTYPE_FLAG_HASNODATA)
-#define BANDTYPE_IS_NODATA(x) ((x)&BANDTYPE_FLAG_ISNODATA)
-
-/* Read band from WKB as at start of band */
-static rt_band
-rt_band_from_wkb(
- uint16_t width, uint16_t height,
- const uint8_t** ptr, const uint8_t* end,
- uint8_t littleEndian
-) {
- rt_band band = NULL;
- int pixbytes = 0;
- uint8_t type = 0;
- unsigned long sz = 0;
- uint32_t v = 0;
-
- assert(NULL != ptr);
- assert(NULL != end);
-
- band = rtalloc(sizeof (struct rt_band_t));
- if (!band) {
- rterror("rt_band_from_wkb: Out of memory allocating rt_band during WKB parsing");
- return NULL;
- }
- band->ownsdata = 0; /* assume we don't own data */
-
- if (end - *ptr < 1) {
- rterror("rt_band_from_wkb: Premature end of WKB on band reading (%s:%d)",
- __FILE__, __LINE__);
- rt_band_destroy(band);
- return NULL;
- }
- type = read_uint8(ptr);
-
- if ((type & BANDTYPE_PIXTYPE_MASK) >= PT_END) {
- rterror("rt_band_from_wkb: Invalid pixtype %d", type & BANDTYPE_PIXTYPE_MASK);
- rt_band_destroy(band);
- return NULL;
- }
-
- band->pixtype = type & BANDTYPE_PIXTYPE_MASK;
- band->offline = BANDTYPE_IS_OFFDB(type) ? 1 : 0;
- band->hasnodata = BANDTYPE_HAS_NODATA(type) ? 1 : 0;
- band->isnodata = band->hasnodata ? (BANDTYPE_IS_NODATA(type) ? 1 : 0) : 0;
- band->width = width;
- band->height = height;
-
- RASTER_DEBUGF(3, " Band pixtype:%s, offline:%d, hasnodata:%d",
- rt_pixtype_name(band->pixtype),
- band->offline,
- band->hasnodata
- );
-
- /* Check there's enough bytes to read nodata value */
- pixbytes = rt_pixtype_size(band->pixtype);
- if (((*ptr) + pixbytes) >= end) {
- rterror("rt_band_from_wkb: Premature end of WKB on band novalue reading");
- rt_band_destroy(band);
- return NULL;
- }
-
- /* Read nodata value */
- switch (band->pixtype) {
- case PT_1BB: {
- band->nodataval = ((int) read_uint8(ptr)) & 0x01;
- break;
- }
- case PT_2BUI: {
- band->nodataval = ((int) read_uint8(ptr)) & 0x03;
- break;
- }
- case PT_4BUI: {
- band->nodataval = ((int) read_uint8(ptr)) & 0x0F;
- break;
- }
- case PT_8BSI: {
- band->nodataval = read_int8(ptr);
- break;
- }
- case PT_8BUI: {
- band->nodataval = read_uint8(ptr);
- break;
- }
- case PT_16BSI: {
- band->nodataval = read_int16(ptr, littleEndian);
- break;
- }
- case PT_16BUI: {
- band->nodataval = read_uint16(ptr, littleEndian);
- break;
- }
- case PT_32BSI: {
- band->nodataval = read_int32(ptr, littleEndian);
- break;
- }
- case PT_32BUI: {
- band->nodataval = read_uint32(ptr, littleEndian);
- break;
- }
- case PT_32BF: {
- band->nodataval = read_float32(ptr, littleEndian);
- break;
- }
- case PT_64BF: {
- band->nodataval = read_float64(ptr, littleEndian);
- break;
- }
- default: {
- rterror("rt_band_from_wkb: Unknown pixeltype %d", band->pixtype);
- rt_band_destroy(band);
- return NULL;
- }
- }
-
- RASTER_DEBUGF(3, " Nodata value: %g, pixbytes: %d, ptr @ %p, end @ %p",
- band->nodataval, pixbytes, *ptr, end);
-
- if (band->offline) {
- if (((*ptr) + 1) >= end) {
- rterror("rt_band_from_wkb: Premature end of WKB on offline "
- "band data bandNum reading (%s:%d)",
- __FILE__, __LINE__
- );
- rt_band_destroy(band);
- return NULL;
- }
-
- band->data.offline.bandNum = read_int8(ptr);
- band->data.offline.mem = NULL;
-
- {
- /* check we have a NULL-termination */
- sz = 0;
- while ((*ptr)[sz] && &((*ptr)[sz]) < end) ++sz;
- if (&((*ptr)[sz]) >= end) {
- rterror("rt_band_from_wkb: Premature end of WKB on band offline path reading");
- rt_band_destroy(band);
- return NULL;
- }
-
- /* we never own offline band data */
- band->ownsdata = 0;
-
- band->data.offline.path = rtalloc(sz + 1);
- if (band->data.offline.path == NULL) {
- rterror("rt_band_from_wkb: Out of memory allocating for offline path of band");
- rt_band_destroy(band);
- return NULL;
- }
-
- memcpy(band->data.offline.path, *ptr, sz);
- band->data.offline.path[sz] = '\0';
-
- RASTER_DEBUGF(3, "OFFDB band path is %s (size is %d)",
- band->data.offline.path, sz);
-
- *ptr += sz + 1;
-
- /* TODO: How could we know if the offline band is a nodata band? */
- /* trust in the force */
- /*band->isnodata = FALSE;*/
- }
-
- return band;
- }
-
- /* This is an on-disk band */
- sz = width * height * pixbytes;
- if (((*ptr) + sz) > end) {
- rterror("rt_band_from_wkb: Premature end of WKB on band data reading (%s:%d)",
- __FILE__, __LINE__);
- rt_band_destroy(band);
- return NULL;
- }
-
- band->data.mem = rtalloc(sz);
- if (!band->data.mem) {
- rterror("rt_band_from_wkb: Out of memory during band creation in WKB parser");
- rt_band_destroy(band);
- return NULL;
- }
-
- band->ownsdata = 1; /* we DO own this data!!! */
- memcpy(band->data.mem, *ptr, sz);
- *ptr += sz;
-
- /* Should now flip values if > 8bit and
- * littleEndian != isMachineLittleEndian */
- if (pixbytes > 1) {
- if (isMachineLittleEndian() != littleEndian) {
- void (*flipper)(uint8_t*) = 0;
- uint8_t *flipme = NULL;
-
- if (pixbytes == 2)
- flipper = flip_endian_16;
- else if (pixbytes == 4)
- flipper = flip_endian_32;
- else if (pixbytes == 8)
- flipper = flip_endian_64;
- else {
- rterror("rt_band_from_wkb: Unexpected pix bytes %d", pixbytes);
- rt_band_destroy(band);
- return NULL;
- }
-
- flipme = band->data.mem;
- sz = width * height;
- for (v = 0; v < sz; ++v) {
- flipper(flipme);
- flipme += pixbytes;
- }
- }
- }
- /* And should check for invalid values for < 8bit types */
- else if (
- band->pixtype == PT_1BB ||
- band->pixtype == PT_2BUI ||
- band->pixtype == PT_4BUI
- ) {
- uint8_t maxVal = band->pixtype == PT_1BB ? 1 : (band->pixtype == PT_2BUI ? 3 : 15);
- uint8_t val;
-
- sz = width*height;
- for (v = 0; v < sz; ++v) {
- val = ((uint8_t*) band->data.mem)[v];
- if (val > maxVal) {
- rterror("rt_band_from_wkb: Invalid value %d for pixel of type %s",
- val, rt_pixtype_name(band->pixtype));
- rt_band_destroy(band);
- return NULL;
- }
- }
- }
-
- /* And we should check if the band is a nodata band */
- /* TODO: No!! This is too slow */
- /*rt_band_check_is_nodata(band);*/
-
- return band;
-}
-
-/* -4 for size, +1 for endian */
-#define RT_WKB_HDR_SZ (sizeof(struct rt_raster_serialized_t)-4+1)
-
-rt_raster
-rt_raster_from_wkb(const uint8_t* wkb, uint32_t wkbsize) {
- const uint8_t *ptr = wkb;
- const uint8_t *wkbend = NULL;
- rt_raster rast = NULL;
- uint8_t endian = 0;
- uint16_t version = 0;
- uint16_t i = 0;
- uint16_t j = 0;
-
- assert(NULL != ptr);
-
- /* Check that wkbsize is >= sizeof(rt_raster_serialized) */
- if (wkbsize < RT_WKB_HDR_SZ) {
- rterror("rt_raster_from_wkb: wkb size (%d) < min size (%d)",
- wkbsize, RT_WKB_HDR_SZ);
- return NULL;
- }
- wkbend = wkb + wkbsize;
-
- RASTER_DEBUGF(3, "Parsing header from wkb position %d (expected 0)",
- d_binptr_to_pos(ptr, wkbend, wkbsize));
-
- CHECK_BINPTR_POSITION(ptr, wkbend, wkbsize, 0);
-
- /* Read endianness */
- endian = *ptr;
- ptr += 1;
-
- /* Read version of protocol */
- version = read_uint16(&ptr, endian);
- if (version != 0) {
- rterror("rt_raster_from_wkb: WKB version %d unsupported", version);
- return NULL;
- }
-
- /* Read other components of raster header */
- rast = (rt_raster) rtalloc(sizeof (struct rt_raster_t));
- if (!rast) {
- rterror("rt_raster_from_wkb: Out of memory allocating raster for wkb input");
- return NULL;
- }
-
- rast->numBands = read_uint16(&ptr, endian);
- rast->scaleX = read_float64(&ptr, endian);
- rast->scaleY = read_float64(&ptr, endian);
- rast->ipX = read_float64(&ptr, endian);
- rast->ipY = read_float64(&ptr, endian);
- rast->skewX = read_float64(&ptr, endian);
- rast->skewY = read_float64(&ptr, endian);
- rast->srid = clamp_srid(read_int32(&ptr, endian));
- rast->width = read_uint16(&ptr, endian);
- rast->height = read_uint16(&ptr, endian);
-
- /* Consistency checking, should have been checked before */
- assert(ptr <= wkbend);
-
- RASTER_DEBUGF(3, "rt_raster_from_wkb: Raster numBands: %d",
- rast->numBands);
- RASTER_DEBUGF(3, "rt_raster_from_wkb: Raster scale: %gx%g",
- rast->scaleX, rast->scaleY);
- RASTER_DEBUGF(3, "rt_raster_from_wkb: Raster ip: %gx%g",
- rast->ipX, rast->ipY);
- RASTER_DEBUGF(3, "rt_raster_from_wkb: Raster skew: %gx%g",
- rast->skewX, rast->skewY);
- RASTER_DEBUGF(3, "rt_raster_from_wkb: Raster srid: %d",
- rast->srid);
- RASTER_DEBUGF(3, "rt_raster_from_wkb: Raster dims: %dx%d",
- rast->width, rast->height);
- RASTER_DEBUGF(3, "Parsing raster header finished at wkb position %d (expected 61)",
- d_binptr_to_pos(ptr, wkbend, wkbsize));
-
- CHECK_BINPTR_POSITION(ptr, wkbend, wkbsize, 61);
-
- /* Read all bands of raster */
- if (!rast->numBands) {
- /* Here ptr should have been left to right after last used byte */
- if (ptr < wkbend) {
- rtwarn("%d bytes of WKB remained unparsed", wkbend - ptr);
- }
- else if (ptr > wkbend) {
- /* Easier to get a segfault before I guess */
- rtwarn("We parsed %d bytes more then available!", ptr - wkbend);
- }
-
- rast->bands = NULL;
- return rast;
- }
-
- /* Now read the bands */
- rast->bands = (rt_band*) rtalloc(sizeof(rt_band) * rast->numBands);
- if (!rast->bands) {
- rterror("rt_raster_from_wkb: Out of memory allocating bands for WKB raster decoding");
- rt_raster_destroy(rast);
- return NULL;
- }
-
- /* ptr should now point to start of first band */
- /* we should have checked this before */
- assert(ptr <= wkbend);
-
- for (i = 0; i < rast->numBands; ++i) {
- RASTER_DEBUGF(3, "Parsing band %d from wkb position %d", i,
- d_binptr_to_pos(ptr, wkbend, wkbsize));
-
- rt_band band = rt_band_from_wkb(rast->width, rast->height,
- &ptr, wkbend, endian);
- if (!band) {
- rterror("rt_raster_from_wkb: Error reading WKB form of band %d", i);
- for (j = 0; j < i; j++) rt_band_destroy(rast->bands[j]);
- rt_raster_destroy(rast);
- return NULL;
- }
-
- band->raster = rast;
- rast->bands[i] = band;
- }
-
- /* Here ptr should have been left to right after last used byte */
- if (ptr < wkbend) {
- rtwarn("%d bytes of WKB remained unparsed", wkbend - ptr);
- }
- else if (ptr > wkbend) {
- /* Easier to get a segfault before I guess */
- rtwarn("We parsed %d bytes more then available!", ptr - wkbend);
- }
-
- return rast;
-}
-
-rt_raster
-rt_raster_from_hexwkb(const char* hexwkb, uint32_t hexwkbsize) {
- rt_raster ret = NULL;
- uint8_t* wkb = NULL;
- uint32_t wkbsize = 0;
- uint32_t i = 0;
-
- assert(NULL != hexwkb);
-
- RASTER_DEBUGF(3, "input wkb: %s", hexwkb);
- RASTER_DEBUGF(3, "input wkbsize: %d", hexwkbsize);
-
- if (hexwkbsize % 2) {
- rterror("rt_raster_from_hexwkb: Raster HEXWKB input must have an even number of characters");
- return NULL;
- }
- wkbsize = hexwkbsize / 2;
-
- wkb = rtalloc(wkbsize);
- if (!wkb) {
- rterror("rt_raster_from_hexwkb: Out of memory allocating memory for decoding HEXWKB");
- return NULL;
- }
-
- /* parse full hex */
- for (i = 0; i < wkbsize; ++i) {
- wkb[i] = parse_hex((char*) & (hexwkb[i * 2]));
- }
-
- ret = rt_raster_from_wkb(wkb, wkbsize);
- rtdealloc(wkb); /* as long as rt_raster_from_wkb copies memory */
-
- return ret;
-}
-
-static uint32_t
-rt_raster_wkb_size(rt_raster raster, int outasin) {
- uint32_t size = RT_WKB_HDR_SZ;
- uint16_t i = 0;
-
- assert(NULL != raster);
-
- RASTER_DEBUGF(3, "rt_raster_wkb_size: computing size for %d bands",
- raster->numBands);
-
- for (i = 0; i < raster->numBands; ++i) {
- rt_band band = raster->bands[i];
- rt_pixtype pixtype = band->pixtype;
- int pixbytes = rt_pixtype_size(pixtype);
-
- RASTER_DEBUGF(3, "rt_raster_wkb_size: adding size of band %d", i);
-
- if (pixbytes < 1) {
- rterror("rt_raster_wkb_size: Corrupted band: unknown pixtype");
- return 0;
- }
-
- /* Add space for band type */
- size += 1;
-
- /* Add space for nodata value */
- size += pixbytes;
-
- if (!outasin && band->offline) {
- /* Add space for band number */
- size += 1;
-
- /* Add space for null-terminated path */
- size += strlen(band->data.offline.path) + 1;
- }
- else {
- /* Add space for actual data */
- size += pixbytes * raster->width * raster->height;
- }
- }
-
- return size;
-}
-
-/**
- * Return this raster in WKB form
- *
- * @param raster : the raster
- * @param outasin : if TRUE, out-db bands are treated as in-db
- * @param wkbsize : will be set to the size of returned wkb form
- *
- * @return WKB of raster or NULL on error
- */
-uint8_t *
-rt_raster_to_wkb(rt_raster raster, int outasin, uint32_t *wkbsize) {
-
-#if POSTGIS_DEBUG_LEVEL > 0
- const uint8_t *wkbend = NULL;
-#endif
-
- uint8_t *wkb = NULL;
- uint8_t *ptr = NULL;
- uint16_t i = 0;
- uint8_t littleEndian = isMachineLittleEndian();
-
- assert(NULL != raster);
- assert(NULL != wkbsize);
-
- RASTER_DEBUG(2, "rt_raster_to_wkb: about to call rt_raster_wkb_size");
-
- *wkbsize = rt_raster_wkb_size(raster, outasin);
- RASTER_DEBUGF(3, "rt_raster_to_wkb: found size: %d", *wkbsize);
-
- wkb = (uint8_t*) rtalloc(*wkbsize);
- if (!wkb) {
- rterror("rt_raster_to_wkb: Out of memory allocating WKB for raster");
- return NULL;
- }
-
- ptr = wkb;
-
-#if POSTGIS_DEBUG_LEVEL > 2
- wkbend = ptr + (*wkbsize);
-#endif
- RASTER_DEBUGF(3, "Writing raster header to wkb on position %d (expected 0)",
- d_binptr_to_pos(ptr, wkbend, *wkbsize));
-
- /* Write endianness */
- *ptr = littleEndian;
- ptr += 1;
-
- /* Write version(size - (end - ptr)) */
- write_uint16(&ptr, littleEndian, 0);
-
- /* Copy header (from numBands up) */
- memcpy(ptr, &(raster->numBands), sizeof (struct rt_raster_serialized_t) - 6);
- ptr += sizeof (struct rt_raster_serialized_t) - 6;
-
- RASTER_DEBUGF(3, "Writing bands header to wkb position %d (expected 61)",
- d_binptr_to_pos(ptr, wkbend, *wkbsize));
-
- /* Serialize bands now */
- for (i = 0; i < raster->numBands; ++i) {
- rt_band band = raster->bands[i];
- rt_pixtype pixtype = band->pixtype;
- int pixbytes = rt_pixtype_size(pixtype);
-
- RASTER_DEBUGF(3, "Writing WKB for band %d", i);
- RASTER_DEBUGF(3, "Writing band pixel type to wkb position %d",
- d_binptr_to_pos(ptr, wkbend, *wkbsize));
-
- if (pixbytes < 1) {
- rterror("rt_raster_to_wkb: Corrupted band: unknown pixtype");
- rtdealloc(wkb);
- return NULL;
- }
-
- /* Add band type */
- *ptr = band->pixtype;
- if (!outasin && band->offline) *ptr |= BANDTYPE_FLAG_OFFDB;
- if (band->hasnodata) *ptr |= BANDTYPE_FLAG_HASNODATA;
- if (band->isnodata) *ptr |= BANDTYPE_FLAG_ISNODATA;
- ptr += 1;
-
-#if 0
- /* no padding required for WKB */
- /* Add padding (if needed) */
- if (pixbytes > 1) {
- memset(ptr, '\0', pixbytes - 1);
- ptr += pixbytes - 1;
- }
- /* Consistency checking (ptr is pixbytes-aligned) */
- assert(!(((uint64_t) ptr) % pixbytes));
-#endif
-
- RASTER_DEBUGF(3, "Writing band nodata to wkb position %d",
- d_binptr_to_pos(ptr, wkbend, *wkbsize));
-
- /* Add nodata value */
- switch (pixtype) {
- case PT_1BB:
- case PT_2BUI:
- case PT_4BUI:
- case PT_8BUI: {
- uint8_t v = band->nodataval;
- *ptr = v;
- ptr += 1;
- break;
- }
- case PT_8BSI: {
- int8_t v = band->nodataval;
- *ptr = v;
- ptr += 1;
- break;
- }
- case PT_16BSI:
- case PT_16BUI: {
- uint16_t v = band->nodataval;
- memcpy(ptr, &v, 2);
- ptr += 2;
- break;
- }
- case PT_32BSI:
- case PT_32BUI: {
- uint32_t v = band->nodataval;
- memcpy(ptr, &v, 4);
- ptr += 4;
- break;
- }
- case PT_32BF: {
- float v = band->nodataval;
- memcpy(ptr, &v, 4);
- ptr += 4;
- break;
- }
- case PT_64BF: {
- memcpy(ptr, &band->nodataval, 8);
- ptr += 8;
- break;
- }
- default:
- rterror("rt_raster_to_wkb: Fatal error caused by unknown pixel type. Aborting.");
- rtdealloc(wkb);
- abort(); /* shoudn't happen */
- return 0;
- }
-
-#if 0
- /* no padding for WKB */
- /* Consistency checking (ptr is pixbytes-aligned) */
- assert(!((uint64_t) ptr % pixbytes));
-#endif
-
- if (!outasin && band->offline) {
- /* Write band number */
- *ptr = band->data.offline.bandNum;
- ptr += 1;
-
- /* Write path */
- strcpy((char*) ptr, band->data.offline.path);
- ptr += strlen(band->data.offline.path) + 1;
- }
- else {
- /* Write data */
- uint32_t datasize = raster->width * raster->height * pixbytes;
- RASTER_DEBUGF(4, "rt_raster_to_wkb: Copying %d bytes", datasize);
-
- memcpy(ptr, rt_band_get_data(band), datasize);
-
- ptr += datasize;
- }
-
-#if 0
- /* no padding for WKB */
- /* Pad up to 8-bytes boundary */
- while ((uint64_t) ptr % 8) {
- *ptr = 0;
- ++ptr;
- }
-
- /* Consistency checking (ptr is pixbytes-aligned) */
- assert(!((uint64_t) ptr % pixbytes));
-#endif
- }
-
- return wkb;
-}
-
-char *
-rt_raster_to_hexwkb(rt_raster raster, int outasin, uint32_t *hexwkbsize) {
- uint8_t *wkb = NULL;
- char* hexwkb = NULL;
- uint32_t i = 0;
- uint32_t wkbsize = 0;
-
- assert(NULL != raster);
- assert(NULL != hexwkbsize);
-
- RASTER_DEBUG(2, "rt_raster_to_hexwkb: calling rt_raster_to_wkb");
-
- wkb = rt_raster_to_wkb(raster, outasin, &wkbsize);
-
- RASTER_DEBUG(3, "rt_raster_to_hexwkb: rt_raster_to_wkb returned");
-
- *hexwkbsize = wkbsize * 2; /* hex is 2 times bytes */
- hexwkb = (char*) rtalloc((*hexwkbsize) + 1);
- if (!hexwkb) {
- rterror("rt_raster_to_hexwkb: Out of memory hexifying raster WKB");
- rtdealloc(wkb);
- return NULL;
- }
- hexwkb[*hexwkbsize] = '\0'; /* Null-terminate */
-
- for (i = 0; i < wkbsize; ++i) {
- deparse_hex(wkb[i], &(hexwkb[2 * i]));
- }
-
- rtdealloc(wkb); /* we don't need this anymore */
-
- RASTER_DEBUGF(3, "rt_raster_to_hexwkb: output wkb: %s", hexwkb);
- return hexwkb;
-}
-
-/*--------- Serializer/Deserializer --------------------------------------*/
-
-static uint32_t
-rt_raster_serialized_size(rt_raster raster) {
- uint32_t size = sizeof (struct rt_raster_serialized_t);
- uint16_t i = 0;
-
- assert(NULL != raster);
-
- RASTER_DEBUGF(3, "Serialized size with just header:%d - now adding size of %d bands",
- size, raster->numBands);
-
- for (i = 0; i < raster->numBands; ++i) {
- rt_band band = raster->bands[i];
- rt_pixtype pixtype = band->pixtype;
- int pixbytes = rt_pixtype_size(pixtype);
-
- if (pixbytes < 1) {
- rterror("rt_raster_serialized_size: Corrupted band: unknown pixtype");
- return 0;
- }
-
- /* Add space for band type, hasnodata flag and data padding */
- size += pixbytes;
-
- /* Add space for nodata value */
- size += pixbytes;
-
- if (band->offline) {
- /* Add space for band number */
- size += 1;
-
- /* Add space for null-terminated path */
- size += strlen(band->data.offline.path) + 1;
- }
- else {
- /* Add space for raster band data */
- size += pixbytes * raster->width * raster->height;
- }
-
- RASTER_DEBUGF(3, "Size before alignment is %d", size);
-
- /* Align size to 8-bytes boundary (trailing padding) */
- /* XXX jorgearevalo: bug here. If the size is actually 8-bytes aligned,
- this line will add 8 bytes trailing padding, and it's not necessary */
- /*size += 8 - (size % 8);*/
- if (size % 8)
- size += 8 - (size % 8);
-
- RASTER_DEBUGF(3, "Size after alignment is %d", size);
- }
-
- return size;
-}
-
-/**
- * Return this raster in serialized form.
- * Memory (band data included) is copied from rt_raster.
- *
- * Serialized form is documented in doc/RFC1-SerializedFormat.
- */
-void*
-rt_raster_serialize(rt_raster raster) {
- uint32_t size = 0;
- uint8_t* ret = NULL;
- uint8_t* ptr = NULL;
- uint16_t i = 0;
-
- assert(NULL != raster);
-
- size = rt_raster_serialized_size(raster);
- ret = (uint8_t*) rtalloc(size);
- if (!ret) {
- rterror("rt_raster_serialize: Out of memory allocating %d bytes for serializing a raster", size);
- return NULL;
- }
- memset(ret, '-', size);
- ptr = ret;
-
- RASTER_DEBUGF(3, "sizeof(struct rt_raster_serialized_t):%u",
- sizeof (struct rt_raster_serialized_t));
- RASTER_DEBUGF(3, "sizeof(struct rt_raster_t):%u",
- sizeof (struct rt_raster_t));
- RASTER_DEBUGF(3, "serialized size:%lu", (long unsigned) size);
-
- /* Set size */
- /* NOTE: Value of rt_raster.size may be updated in
- * returned object, for instance, by rt_pg layer to
- * store value calculated by SET_VARSIZE.
- */
- raster->size = size;
-
- /* Set version */
- raster->version = 0;
-
- /* Copy header */
- memcpy(ptr, raster, sizeof (struct rt_raster_serialized_t));
-
- RASTER_DEBUG(3, "Start hex dump of raster being serialized using 0x2D to mark non-written bytes");
-
-#if POSTGIS_DEBUG_LEVEL > 2
- uint8_t* dbg_ptr = ptr;
- d_print_binary_hex("HEADER", dbg_ptr, size);
-#endif
-
- ptr += sizeof (struct rt_raster_serialized_t);
-
- /* Serialize bands now */
- for (i = 0; i < raster->numBands; ++i) {
- rt_band band = raster->bands[i];
- assert(NULL != band);
-
- rt_pixtype pixtype = band->pixtype;
- int pixbytes = rt_pixtype_size(pixtype);
- if (pixbytes < 1) {
- rterror("rt_raster_serialize: Corrupted band: unknown pixtype");
- rtdealloc(ret);
- return NULL;
- }
-
- /* Add band type */
- *ptr = band->pixtype;
- if (band->offline) {
- *ptr |= BANDTYPE_FLAG_OFFDB;
- }
- if (band->hasnodata) {
- *ptr |= BANDTYPE_FLAG_HASNODATA;
- }
-
- if (band->isnodata) {
- *ptr |= BANDTYPE_FLAG_ISNODATA;
- }
-
-#if POSTGIS_DEBUG_LEVEL > 2
- d_print_binary_hex("PIXTYPE", dbg_ptr, size);
-#endif
-
- ptr += 1;
-
- /* Add padding (if needed) */
- if (pixbytes > 1) {
- memset(ptr, '\0', pixbytes - 1);
- ptr += pixbytes - 1;
- }
-
-#if POSTGIS_DEBUG_LEVEL > 2
- d_print_binary_hex("PADDING", dbg_ptr, size);
-#endif
-
- /* Consistency checking (ptr is pixbytes-aligned) */
- assert(!((ptr - ret) % pixbytes));
-
- /* Add nodata value */
- switch (pixtype) {
- case PT_1BB:
- case PT_2BUI:
- case PT_4BUI:
- case PT_8BUI: {
- uint8_t v = band->nodataval;
- *ptr = v;
- ptr += 1;
- break;
- }
- case PT_8BSI: {
- int8_t v = band->nodataval;
- *ptr = v;
- ptr += 1;
- break;
- }
- case PT_16BSI:
- case PT_16BUI: {
- uint16_t v = band->nodataval;
- memcpy(ptr, &v, 2);
- ptr += 2;
- break;
- }
- case PT_32BSI:
- case PT_32BUI: {
- uint32_t v = band->nodataval;
- memcpy(ptr, &v, 4);
- ptr += 4;
- break;
- }
- case PT_32BF: {
- float v = band->nodataval;
- memcpy(ptr, &v, 4);
- ptr += 4;
- break;
- }
- case PT_64BF: {
- memcpy(ptr, &band->nodataval, 8);
- ptr += 8;
- break;
- }
- default:
- rterror("rt_raster_serialize: Fatal error caused by unknown pixel type. Aborting.");
- rtdealloc(ret);
- return NULL;
- }
-
- /* Consistency checking (ptr is pixbytes-aligned) */
- assert(!((ptr - ret) % pixbytes));
-
-#if POSTGIS_DEBUG_LEVEL > 2
- d_print_binary_hex("nodata", dbg_ptr, size);
-#endif
-
- if (band->offline) {
- /* Write band number */
- *ptr = band->data.offline.bandNum;
- ptr += 1;
-
- /* Write path */
- strcpy((char*) ptr, band->data.offline.path);
- ptr += strlen(band->data.offline.path) + 1;
- }
- else {
- /* Write data */
- uint32_t datasize = raster->width * raster->height * pixbytes;
- memcpy(ptr, band->data.mem, datasize);
- ptr += datasize;
- }
-
-#if POSTGIS_DEBUG_LEVEL > 2
- d_print_binary_hex("BAND", dbg_ptr, size);
-#endif
-
- /* Pad up to 8-bytes boundary */
- while ((uintptr_t) ptr % 8) {
- *ptr = 0;
- ++ptr;
-
- RASTER_DEBUGF(3, "PAD at %d", (uintptr_t) ptr % 8);
- }
-
- /* Consistency checking (ptr is pixbytes-aligned) */
- assert(!((ptr - ret) % pixbytes));
- } /* for-loop over bands */
-
-#if POSTGIS_DEBUG_LEVEL > 2
- d_print_binary_hex("SERIALIZED RASTER", dbg_ptr, size);
-#endif
- return ret;
-}
-
-/**
- * Return a raster from a serialized form.
- *
- * Serialized form is documented in doc/RFC1-SerializedFormat.
- *
- * NOTE: the raster will contain pointer to the serialized
- * form (including band data), which must be kept alive.
- */
-rt_raster
-rt_raster_deserialize(void* serialized, int header_only) {
- rt_raster rast = NULL;
- const uint8_t *ptr = NULL;
- const uint8_t *beg = NULL;
- uint16_t i = 0;
- uint16_t j = 0;
- uint8_t littleEndian = isMachineLittleEndian();
-
- assert(NULL != serialized);
-
- RASTER_DEBUG(2, "rt_raster_deserialize: Entering...");
-
- /* NOTE: Value of rt_raster.size may be different
- * than actual size of raster data being read.
- * See note on SET_VARSIZE in rt_raster_serialize function above.
- */
-
- /* Allocate memory for deserialized raster header */
- RASTER_DEBUG(3, "rt_raster_deserialize: Allocating memory for deserialized raster header");
- rast = (rt_raster) rtalloc(sizeof (struct rt_raster_t));
- if (!rast) {
- rterror("rt_raster_deserialize: Out of memory allocating raster for deserialization");
- return NULL;
- }
-
- /* Deserialize raster header */
- RASTER_DEBUG(3, "rt_raster_deserialize: Deserialize raster header");
- memcpy(rast, serialized, sizeof (struct rt_raster_serialized_t));
-
- if (0 == rast->numBands || header_only) {
- rast->bands = 0;
- return rast;
- }
-
- beg = (const uint8_t*) serialized;
-
- /* Allocate registry of raster bands */
- RASTER_DEBUG(3, "rt_raster_deserialize: Allocating memory for bands");
- rast->bands = rtalloc(rast->numBands * sizeof (rt_band));
- if (rast->bands == NULL) {
- rterror("rt_raster_deserialize: Out of memory allocating bands");
- rtdealloc(rast);
- return NULL;
- }
-
- RASTER_DEBUGF(3, "rt_raster_deserialize: %d bands", rast->numBands);
-
- /* Move to the beginning of first band */
- ptr = beg;
- ptr += sizeof (struct rt_raster_serialized_t);
-
- /* Deserialize bands now */
- for (i = 0; i < rast->numBands; ++i) {
- rt_band band = NULL;
- uint8_t type = 0;
- int pixbytes = 0;
-
- band = rtalloc(sizeof(struct rt_band_t));
- if (!band) {
- rterror("rt_raster_deserialize: Out of memory allocating rt_band during deserialization");
- for (j = 0; j < i; j++) rt_band_destroy(rast->bands[j]);
- rt_raster_destroy(rast);
- return NULL;
- }
-
- rast->bands[i] = band;
-
- type = *ptr;
- ptr++;
- band->pixtype = type & BANDTYPE_PIXTYPE_MASK;
-
- RASTER_DEBUGF(3, "rt_raster_deserialize: band %d with pixel type %s", i, rt_pixtype_name(band->pixtype));
-
- band->offline = BANDTYPE_IS_OFFDB(type) ? 1 : 0;
- band->hasnodata = BANDTYPE_HAS_NODATA(type) ? 1 : 0;
- band->isnodata = band->hasnodata ? (BANDTYPE_IS_NODATA(type) ? 1 : 0) : 0;
- band->width = rast->width;
- band->height = rast->height;
- band->ownsdata = 0; /* we do NOT own this data!!! */
- band->raster = rast;
-
- /* Advance by data padding */
- pixbytes = rt_pixtype_size(band->pixtype);
- ptr += pixbytes - 1;
-
- /* Read nodata value */
- switch (band->pixtype) {
- case PT_1BB: {
- band->nodataval = ((int) read_uint8(&ptr)) & 0x01;
- break;
- }
- case PT_2BUI: {
- band->nodataval = ((int) read_uint8(&ptr)) & 0x03;
- break;
- }
- case PT_4BUI: {
- band->nodataval = ((int) read_uint8(&ptr)) & 0x0F;
- break;
- }
- case PT_8BSI: {
- band->nodataval = read_int8(&ptr);
- break;
- }
- case PT_8BUI: {
- band->nodataval = read_uint8(&ptr);
- break;
- }
- case PT_16BSI: {
- band->nodataval = read_int16(&ptr, littleEndian);
- break;
- }
- case PT_16BUI: {
- band->nodataval = read_uint16(&ptr, littleEndian);
- break;
- }
- case PT_32BSI: {
- band->nodataval = read_int32(&ptr, littleEndian);
- break;
- }
- case PT_32BUI: {
- band->nodataval = read_uint32(&ptr, littleEndian);
- break;
- }
- case PT_32BF: {
- band->nodataval = read_float32(&ptr, littleEndian);
- break;
- }
- case PT_64BF: {
- band->nodataval = read_float64(&ptr, littleEndian);
- break;
- }
- default: {
- rterror("rt_raster_deserialize: Unknown pixeltype %d", band->pixtype);
- for (j = 0; j <= i; j++) rt_band_destroy(rast->bands[j]);
- rt_raster_destroy(rast);
- return NULL;
- }
- }
-
- RASTER_DEBUGF(3, "rt_raster_deserialize: has nodata flag %d", band->hasnodata);
- RASTER_DEBUGF(3, "rt_raster_deserialize: nodata value %g", band->nodataval);
-
- /* Consistency checking (ptr is pixbytes-aligned) */
- assert(!((ptr - beg) % pixbytes));
-
- if (band->offline) {
- int pathlen = 0;
-
- /* Read band number */
- band->data.offline.bandNum = *ptr;
- ptr += 1;
-
- /* Register path */
- pathlen = strlen((char*) ptr);
- band->data.offline.path = rtalloc(sizeof(char) * (pathlen + 1));
- if (band->data.offline.path == NULL) {
- rterror("rt_raster_deserialize: Could not allocate momory for offline band path");
- for (j = 0; j <= i; j++) rt_band_destroy(rast->bands[j]);
- rt_raster_destroy(rast);
- return NULL;
- }
-
- memcpy(band->data.offline.path, ptr, pathlen);
- band->data.offline.path[pathlen] = '\0';
- ptr += pathlen + 1;
-
- band->data.offline.mem = NULL;
- }
- else {
- /* Register data */
- const uint32_t datasize = rast->width * rast->height * pixbytes;
- band->data.mem = (uint8_t*) ptr;
- ptr += datasize;
- }
-
- /* Skip bytes of padding up to 8-bytes boundary */
-#if POSTGIS_DEBUG_LEVEL > 0
- const uint8_t *padbeg = ptr;
-#endif
- while (0 != ((ptr - beg) % 8)) {
- ++ptr;
- }
-
- RASTER_DEBUGF(3, "rt_raster_deserialize: skip %d bytes of 8-bytes boundary padding", ptr - padbeg);
-
- /* Consistency checking (ptr is pixbytes-aligned) */
- assert(!((ptr - beg) % pixbytes));
- }
-
- return rast;
-}
-
-/**
- * Return TRUE if the raster is empty. i.e. is NULL, width = 0 or height = 0
- *
- * @param raster : the raster to get info from
- *
- * @return TRUE if the raster is empty, FALSE otherwise
- */
-int
-rt_raster_is_empty(rt_raster raster) {
- return (NULL == raster || raster->height <= 0 || raster->width <= 0);
-}
-
-/**
- * Return TRUE if the raster has a band of this number.
- *
- * @param raster : the raster to get info from
- * @param nband : the band number. 0-based
- *
- * @return TRUE if the raster has a band of this number, FALSE otherwise
- */
-int
-rt_raster_has_band(rt_raster raster, int nband) {
- return !(NULL == raster || nband >= raster->numBands || nband < 0);
-}
-
-/**
- * Copy one band from one raster to another. Bands are duplicated from
- * fromrast to torast using rt_band_duplicate. The caller will need
- * to ensure that the copied band's data or path remains allocated
- * for the lifetime of the copied bands.
- *
- * @param torast : raster to copy band to
- * @param fromrast : raster to copy band from
- * @param fromindex : index of band in source raster, 0-based
- * @param toindex : index of new band in destination raster, 0-based
- *
- * @return The band index of the second raster where the new band is copied.
- * -1 if error
- */
-int
-rt_raster_copy_band(
- rt_raster torast, rt_raster fromrast,
- int fromindex, int toindex
-) {
- rt_band srcband = NULL;
- rt_band dstband = NULL;
-
- assert(NULL != torast);
- assert(NULL != fromrast);
-
- /* Check raster dimensions */
- if (torast->height != fromrast->height || torast->width != fromrast->width) {
- rtwarn("rt_raster_copy_band: Attempting to add a band with different width or height");
- return -1;
- }
-
- /* Check bands limits */
- if (fromrast->numBands < 1) {
- rtwarn("rt_raster_copy_band: Second raster has no band");
- return -1;
- }
- else if (fromindex < 0) {
- rtwarn("rt_raster_copy_band: Band index for second raster < 0. Defaulted to 0");
- fromindex = 0;
- }
- else if (fromindex >= fromrast->numBands) {
- rtwarn("rt_raster_copy_band: Band index for second raster > number of bands, truncated from %u to %u", fromindex, fromrast->numBands - 1);
- fromindex = fromrast->numBands - 1;
- }
-
- if (toindex < 0) {
- rtwarn("rt_raster_copy_band: Band index for first raster < 0. Defaulted to 0");
- toindex = 0;
- }
- else if (toindex > torast->numBands) {
- rtwarn("rt_raster_copy_band: Band index for first raster > number of bands, truncated from %u to %u", toindex, torast->numBands);
- toindex = torast->numBands;
- }
-
- /* Get band from source raster */
- srcband = rt_raster_get_band(fromrast, fromindex);
-
- /* duplicate band */
- dstband = rt_band_duplicate(srcband);
-
- /* Add band to the second raster */
- return rt_raster_add_band(torast, dstband, toindex);
-}
-
-/**
- * Construct a new rt_raster from an existing rt_raster and an array
- * of band numbers
- *
- * @param raster : the source raster
- * @param bandNums : array of band numbers to extract from source raster
- * and add to the new raster (0 based)
- * @param count : number of elements in bandNums
- *
- * @return a new rt_raster or NULL on error
- */
-rt_raster
-rt_raster_from_band(rt_raster raster, uint32_t *bandNums, int count) {
- rt_raster rast = NULL;
- int i = 0;
- int j = 0;
- int idx;
- int32_t flag;
- double gt[6] = {0.};
-
- assert(NULL != raster);
- assert(NULL != bandNums);
-
- RASTER_DEBUGF(3, "rt_raster_from_band: source raster has %d bands",
- rt_raster_get_num_bands(raster));
-
- /* create new raster */
- rast = rt_raster_new(raster->width, raster->height);
- if (NULL == rast) {
- rterror("rt_raster_from_band: Out of memory allocating new raster");
- return NULL;
- }
-
- /* copy raster attributes */
- rt_raster_get_geotransform_matrix(raster, gt);
- rt_raster_set_geotransform_matrix(rast, gt);
-
- /* srid */
- rt_raster_set_srid(rast, raster->srid);
-
- /* copy bands */
- for (i = 0; i < count; i++) {
- idx = bandNums[i];
- flag = rt_raster_copy_band(rast, raster, idx, i);
-
- if (flag < 0) {
- rterror("rt_raster_from_band: Could not copy band");
- for (j = 0; j < i; j++) rt_band_destroy(rast->bands[j]);
- rt_raster_destroy(rast);
- return NULL;
- }
-
- RASTER_DEBUGF(3, "rt_raster_from_band: band created at index %d",
- flag);
- }
-
- RASTER_DEBUGF(3, "rt_raster_from_band: new raster has %d bands",
- rt_raster_get_num_bands(rast));
- return rast;
-}
-
-/**
- * Replace band at provided index with new band
- *
- * @param raster: raster of band to be replaced
- * @param band : new band to add to raster
- * @param index : index of band to replace (0-based)
- *
- * @return NULL on error or replaced band
- */
-rt_band
-rt_raster_replace_band(rt_raster raster, rt_band band, int index) {
- rt_band oldband = NULL;
- assert(NULL != raster);
- assert(NULL != band);
-
- if (band->width != raster->width || band->height != raster->height) {
- rterror("rt_raster_replace_band: Band does not match raster's dimensions: %dx%d band to %dx%d raster",
- band->width, band->height, raster->width, raster->height);
- return 0;
- }
-
- if (index >= raster->numBands || index < 0) {
- rterror("rt_raster_replace_band: Band index is not valid");
- return 0;
- }
-
- oldband = rt_raster_get_band(raster, index);
- RASTER_DEBUGF(3, "rt_raster_replace_band: old band at %p", oldband);
- RASTER_DEBUGF(3, "rt_raster_replace_band: new band at %p", band);
-
- raster->bands[index] = band;
- RASTER_DEBUGF(3, "rt_raster_replace_band: new band at %p", raster->bands[index]);
-
- band->raster = raster;
- oldband->raster = NULL;
-
- return oldband;
-}
-
-/**
- * Clone an existing raster
- *
- * @param raster : raster to clone
- * @param deep : flag indicating if bands should be cloned
- *
- * @return a new rt_raster or NULL on error
- */
-rt_raster
-rt_raster_clone(rt_raster raster, uint8_t deep) {
- rt_raster rtn = NULL;
- double gt[6] = {0};
-
- assert(NULL != raster);
-
- if (deep) {
- int numband = rt_raster_get_num_bands(raster);
- uint32_t *nband = NULL;
- int i = 0;
-
- nband = rtalloc(sizeof(uint32_t) * numband);
- if (nband == NULL) {
- rterror("rt_raster_clone: Could not allocate memory for deep clone");
- return NULL;
- }
- for (i = 0; i < numband; i++)
- nband[i] = i;
-
- rtn = rt_raster_from_band(raster, nband, numband);
- rtdealloc(nband);
-
- return rtn;
- }
-
- rtn = rt_raster_new(
- rt_raster_get_width(raster),
- rt_raster_get_height(raster)
- );
- if (rtn == NULL) {
- rterror("rt_raster_clone: Could not create cloned raster");
- return NULL;
- }
-
- rt_raster_get_geotransform_matrix(raster, gt);
- rt_raster_set_geotransform_matrix(rtn, gt);
- rt_raster_set_srid(rtn, rt_raster_get_srid(raster));
-
- return rtn;
-}
-
-/**
- * Return formatted GDAL raster from raster
- *
- * @param raster : the raster to convert
- * @param srs : the raster's coordinate system in OGC WKT
- * @param format : format to convert to. GDAL driver short name
- * @param options : list of format creation options. array of strings
- * @param gdalsize : will be set to the size of returned bytea
- *
- * @return formatted GDAL raster. the calling function is responsible
- * for freeing the returned data using CPLFree()
- */
-uint8_t*
-rt_raster_to_gdal(rt_raster raster, const char *srs,
- char *format, char **options, uint64_t *gdalsize) {
- GDALDriverH src_drv = NULL;
- GDALDatasetH src_ds = NULL;
-
- vsi_l_offset rtn_lenvsi;
- uint64_t rtn_len = 0;
-
- GDALDriverH rtn_drv = NULL;
- GDALDatasetH rtn_ds = NULL;
- uint8_t *rtn = NULL;
-
- assert(NULL != raster);
- assert(NULL != gdalsize);
-
- /* any supported format is possible */
- rt_util_gdal_register_all();
- RASTER_DEBUG(3, "loaded all supported GDAL formats");
-
- /* output format not specified */
- if (format == NULL || !strlen(format))
- format = "GTiff";
- RASTER_DEBUGF(3, "output format is %s", format);
-
- /* load raster into a GDAL MEM raster */
- src_ds = rt_raster_to_gdal_mem(raster, srs, NULL, NULL, 0, &src_drv);
- if (NULL == src_ds) {
- rterror("rt_raster_to_gdal: Could not convert raster to GDAL MEM format");
- return 0;
- }
-
- /* load driver */
- rtn_drv = GDALGetDriverByName(format);
- if (NULL == rtn_drv) {
- rterror("rt_raster_to_gdal: Could not load the output GDAL driver");
- GDALClose(src_ds);
- return 0;
- }
- RASTER_DEBUG(3, "Output driver loaded");
-
- /* convert GDAL MEM raster to output format */
- RASTER_DEBUG(3, "Copying GDAL MEM raster to memory file in output format");
- rtn_ds = GDALCreateCopy(
- rtn_drv,
- "/vsimem/out.dat", /* should be fine assuming this is in a process */
- src_ds,
- FALSE, /* should copy be strictly equivelent? */
- options, /* format options */
- NULL, /* progress function */
- NULL /* progress data */
- );
- if (NULL == rtn_ds) {
- rterror("rt_raster_to_gdal: Could not create the output GDAL dataset");
- GDALClose(src_ds);
- return 0;
- }
-
- /* close source dataset */
- GDALClose(src_ds);
- RASTER_DEBUG(3, "Closed GDAL MEM raster");
-
- RASTER_DEBUGF(4, "dataset SRS: %s", GDALGetProjectionRef(rtn_ds));
-
- /* close dataset, this also flushes any pending writes */
- GDALClose(rtn_ds);
- RASTER_DEBUG(3, "Closed GDAL output raster");
-
- RASTER_DEBUG(3, "Done copying GDAL MEM raster to memory file in output format");
-
- /* from memory file to buffer */
- RASTER_DEBUG(3, "Copying GDAL memory file to buffer");
- rtn = VSIGetMemFileBuffer("/vsimem/out.dat", &rtn_lenvsi, TRUE);
- RASTER_DEBUG(3, "Done copying GDAL memory file to buffer");
- if (NULL == rtn) {
- rterror("rt_raster_to_gdal: Could not create the output GDAL raster");
- return 0;
- }
-
- rtn_len = (uint64_t) rtn_lenvsi;
- *gdalsize = rtn_len;
-
- return rtn;
-}
-
-/**
- * Returns a set of available GDAL drivers
- *
- * @param drv_count : number of GDAL drivers available
- * @param cancc : if non-zero, filter drivers to only those
- * with support for CreateCopy and VirtualIO
- *
- * @return set of "gdaldriver" values of available GDAL drivers
- */
-rt_gdaldriver
-rt_raster_gdal_drivers(uint32_t *drv_count, uint8_t cancc) {
- const char *state;
- const char *txt;
- int txt_len;
- GDALDriverH *drv = NULL;
- rt_gdaldriver rtn = NULL;
- int count;
- int i;
- uint32_t j;
-
- assert(drv_count != NULL);
-
- rt_util_gdal_register_all();
- count = GDALGetDriverCount();
- rtn = (rt_gdaldriver) rtalloc(count * sizeof(struct rt_gdaldriver_t));
- if (NULL == rtn) {
- rterror("rt_raster_gdal_drivers: Could not allocate memory for gdaldriver structure");
- return 0;
- }
-
- for (i = 0, j = 0; i < count; i++) {
- drv = GDALGetDriver(i);
-
- if (cancc) {
- /* CreateCopy support */
- state = GDALGetMetadataItem(drv, GDAL_DCAP_CREATECOPY, NULL);
- if (state == NULL) continue;
-
- /* VirtualIO support */
- state = GDALGetMetadataItem(drv, GDAL_DCAP_VIRTUALIO, NULL);
- if (state == NULL) continue;
- }
-
- /* index of driver */
- rtn[j].idx = i;
-
- /* short name */
- txt = GDALGetDriverShortName(drv);
- txt_len = strlen(txt);
-
- if (cancc) {
- RASTER_DEBUGF(3, "rt_raster_gdal_driver: driver %s (%d) supports CreateCopy() and VirtualIO()", txt, i);
- }
-
- txt_len = (txt_len + 1) * sizeof(char);
- rtn[j].short_name = (char *) rtalloc(txt_len);
- memcpy(rtn[j].short_name, txt, txt_len);
-
- /* long name */
- txt = GDALGetDriverLongName(drv);
- txt_len = strlen(txt);
-
- txt_len = (txt_len + 1) * sizeof(char);
- rtn[j].long_name = (char *) rtalloc(txt_len);
- memcpy(rtn[j].long_name, txt, txt_len);
-
- /* creation options */
- txt = GDALGetDriverCreationOptionList(drv);
- txt_len = strlen(txt);
-
- txt_len = (txt_len + 1) * sizeof(char);
- rtn[j].create_options = (char *) rtalloc(txt_len);
- memcpy(rtn[j].create_options, txt, txt_len);
-
- j++;
- }
-
- /* free unused memory */
- rtn = rtrealloc(rtn, j * sizeof(struct rt_gdaldriver_t));
- *drv_count = j;
-
- return rtn;
-}
-
-/**
- * Return GDAL dataset using GDAL MEM driver from raster
- *
- * @param raster : raster to convert to GDAL MEM
- * @param srs : the raster's coordinate system in OGC WKT
- * @param bandNums : array of band numbers to extract from raster
- * and include in the GDAL dataset (0 based)
- * @param excludeNodataValues : array of zero, nonzero where if non-zero,
- * ignore nodata values for the band
- * @param count : number of elements in bandNums
- * @param rtn_drv : is set to the GDAL driver object
- *
- * @return GDAL dataset using GDAL MEM driver
- */
-GDALDatasetH
-rt_raster_to_gdal_mem(
- rt_raster raster,
- const char *srs,
- uint32_t *bandNums,
- int *excludeNodataValues,
- int count,
- GDALDriverH *rtn_drv
-) {
- GDALDriverH drv = NULL;
- GDALDatasetH ds = NULL;
- double gt[6] = {0.0};
- CPLErr cplerr;
- GDALDataType gdal_pt = GDT_Unknown;
- GDALRasterBandH band;
- void *pVoid;
- char *pszDataPointer;
- char szGDALOption[50];
- char *apszOptions[4];
- double nodata = 0.0;
- int allocBandNums = 0;
- int allocNodataValues = 0;
-
- int i;
- int numBands;
- uint32_t width = 0;
- uint32_t height = 0;
- rt_band rtband = NULL;
- rt_pixtype pt = PT_END;
-
- assert(NULL != raster);
- assert(NULL != rtn_drv);
-
- /* store raster in GDAL MEM raster */
- if (!rt_util_gdal_driver_registered("MEM"))
- GDALRegister_MEM();
- drv = GDALGetDriverByName("MEM");
- if (NULL == drv) {
- rterror("rt_raster_to_gdal_mem: Could not load the MEM GDAL driver");
- return 0;
- }
- *rtn_drv = drv;
-
- width = rt_raster_get_width(raster);
- height = rt_raster_get_height(raster);
- ds = GDALCreate(
- drv, "",
- width, height,
- 0, GDT_Byte, NULL
- );
- if (NULL == ds) {
- rterror("rt_raster_to_gdal_mem: Could not create a GDALDataset to convert into");
- return 0;
- }
-
- /* add geotransform */
- rt_raster_get_geotransform_matrix(raster, gt);
- cplerr = GDALSetGeoTransform(ds, gt);
- if (cplerr != CE_None) {
- rterror("rt_raster_to_gdal_mem: Could not set geotransformation");
- GDALClose(ds);
- return 0;
- }
-
- /* set spatial reference */
- if (NULL != srs && strlen(srs)) {
- char *_srs = rt_util_gdal_convert_sr(srs, 0);
- if (_srs == NULL) {
- rterror("rt_raster_to_gdal_mem: Could not convert srs to GDAL accepted format");
- GDALClose(ds);
- return 0;
- }
-
- cplerr = GDALSetProjection(ds, _srs);
- CPLFree(_srs);
- if (cplerr != CE_None) {
- rterror("rt_raster_to_gdal_mem: Could not set projection");
- GDALClose(ds);
- return 0;
- }
- RASTER_DEBUGF(3, "Projection set to: %s", GDALGetProjectionRef(ds));
- }
-
- /* process bandNums */
- numBands = rt_raster_get_num_bands(raster);
- if (NULL != bandNums && count > 0) {
- for (i = 0; i < count; i++) {
- if (bandNums[i] >= numBands) {
- rterror("rt_raster_to_gdal_mem: The band index %d is invalid", bandNums[i]);
- GDALClose(ds);
- return 0;
- }
- }
- }
- else {
- count = numBands;
- bandNums = (uint32_t *) rtalloc(sizeof(uint32_t) * count);
- if (NULL == bandNums) {
- rterror("rt_raster_to_gdal_mem: Could not allocate memory for band indices");
- GDALClose(ds);
- return 0;
- }
- allocBandNums = 1;
- for (i = 0; i < count; i++) bandNums[i] = i;
- }
-
- /* process exclude_nodata_values */
- if (NULL == excludeNodataValues) {
- excludeNodataValues = (int *) rtalloc(sizeof(int) * count);
- if (NULL == excludeNodataValues) {
- rterror("rt_raster_to_gdal_mem: Could not allocate memory for NODATA flags");
- GDALClose(ds);
- return 0;
- }
- allocNodataValues = 1;
- for (i = 0; i < count; i++) excludeNodataValues[i] = 1;
- }
-
- /* add band(s) */
- for (i = 0; i < count; i++) {
- rtband = rt_raster_get_band(raster, bandNums[i]);
- if (NULL == rtband) {
- rterror("rt_raster_to_gdal_mem: Could not get requested band index %d", bandNums[i]);
- if (allocBandNums) rtdealloc(bandNums);
- if (allocNodataValues) rtdealloc(excludeNodataValues);
- GDALClose(ds);
- return 0;
- }
-
- pt = rt_band_get_pixtype(rtband);
- gdal_pt = rt_util_pixtype_to_gdal_datatype(pt);
- if (gdal_pt == GDT_Unknown)
- rtwarn("rt_raster_to_gdal_mem: Unknown pixel type for band");
-
- /*
- For all pixel types other than PT_8BSI, set pointer to start of data
- */
- if (pt != PT_8BSI) {
- pVoid = rt_band_get_data(rtband);
- RASTER_DEBUGF(4, "Band data is at pos %p", pVoid);
-
- pszDataPointer = (char *) rtalloc(20 * sizeof (char));
- sprintf(pszDataPointer, "%p", pVoid);
- RASTER_DEBUGF(4, "rt_raster_to_gdal_mem: szDatapointer is %p",
- pszDataPointer);
-
- if (strnicmp(pszDataPointer, "0x", 2) == 0)
- sprintf(szGDALOption, "DATAPOINTER=%s", pszDataPointer);
- else
- sprintf(szGDALOption, "DATAPOINTER=0x%s", pszDataPointer);
-
- RASTER_DEBUG(3, "Storing info for GDAL MEM raster band");
-
- apszOptions[0] = szGDALOption;
- apszOptions[1] = NULL; /* pixel offset, not needed */
- apszOptions[2] = NULL; /* line offset, not needed */
- apszOptions[3] = NULL;
-
- /* free */
- rtdealloc(pszDataPointer);
-
- /* add band */
- if (GDALAddBand(ds, gdal_pt, apszOptions) == CE_Failure) {
- rterror("rt_raster_to_gdal_mem: Could not add GDAL raster band");
- if (allocBandNums) rtdealloc(bandNums);
- GDALClose(ds);
- return 0;
- }
- }
- /*
- PT_8BSI is special as GDAL has no equivalent pixel type.
- Must convert 8BSI to 16BSI so create basic band
- */
- else {
- /* add band */
- if (GDALAddBand(ds, gdal_pt, NULL) == CE_Failure) {
- rterror("rt_raster_to_gdal_mem: Could not add GDAL raster band");
- if (allocBandNums) rtdealloc(bandNums);
- if (allocNodataValues) rtdealloc(excludeNodataValues);
- GDALClose(ds);
- return 0;
- }
- }
-
- /* check band count */
- if (GDALGetRasterCount(ds) != i + 1) {
- rterror("rt_raster_to_gdal_mem: Error creating GDAL MEM raster band");
- if (allocBandNums) rtdealloc(bandNums);
- if (allocNodataValues) rtdealloc(excludeNodataValues);
- GDALClose(ds);
- return 0;
- }
-
- /* get new band */
- band = NULL;
- band = GDALGetRasterBand(ds, i + 1);
- if (NULL == band) {
- rterror("rt_raster_to_gdal_mem: Could not get GDAL band for additional processing");
- if (allocBandNums) rtdealloc(bandNums);
- if (allocNodataValues) rtdealloc(excludeNodataValues);
- GDALClose(ds);
- return 0;
- }
-
- /* PT_8BSI requires manual setting of pixels */
- if (pt == PT_8BSI) {
- int nXBlocks, nYBlocks;
- int nXBlockSize, nYBlockSize;
- int iXBlock, iYBlock;
- int nXValid, nYValid;
- int iX, iY;
- int iXMax, iYMax;
-
- int x, y, z;
- uint32_t valueslen = 0;
- int16_t *values = NULL;
- double value = 0.;
-
- /* this makes use of GDAL's "natural" blocks */
- GDALGetBlockSize(band, &nXBlockSize, &nYBlockSize);
- nXBlocks = (width + nXBlockSize - 1) / nXBlockSize;
- nYBlocks = (height + nYBlockSize - 1) / nYBlockSize;
- RASTER_DEBUGF(4, "(nXBlockSize, nYBlockSize) = (%d, %d)", nXBlockSize, nYBlockSize);
- RASTER_DEBUGF(4, "(nXBlocks, nYBlocks) = (%d, %d)", nXBlocks, nYBlocks);
-
- /* length is for the desired pixel type */
- valueslen = rt_pixtype_size(PT_16BSI) * nXBlockSize * nYBlockSize;
- values = rtalloc(valueslen);
- if (NULL == values) {
- rterror("rt_raster_to_gdal_mem: Could not allocate memory for GDAL band pixel values");
- if (allocBandNums) rtdealloc(bandNums);
- if (allocNodataValues) rtdealloc(excludeNodataValues);
- GDALClose(ds);
- return 0;
- }
-
- for (iYBlock = 0; iYBlock < nYBlocks; iYBlock++) {
- for (iXBlock = 0; iXBlock < nXBlocks; iXBlock++) {
- memset(values, 0, valueslen);
-
- x = iXBlock * nXBlockSize;
- y = iYBlock * nYBlockSize;
- RASTER_DEBUGF(4, "(iXBlock, iYBlock) = (%d, %d)", iXBlock, iYBlock);
- RASTER_DEBUGF(4, "(x, y) = (%d, %d)", x, y);
-
- /* valid block width */
- if ((iXBlock + 1) * nXBlockSize > width)
- nXValid = width - (iXBlock * nXBlockSize);
- else
- nXValid = nXBlockSize;
-
- /* valid block height */
- if ((iYBlock + 1) * nYBlockSize > height)
- nYValid = height - (iYBlock * nYBlockSize);
- else
- nYValid = nYBlockSize;
-
- RASTER_DEBUGF(4, "(nXValid, nYValid) = (%d, %d)", nXValid, nYValid);
-
- /* convert 8BSI values to 16BSI */
- z = 0;
- iYMax = y + nYValid;
- iXMax = x + nXValid;
- for (iY = y; iY < iYMax; iY++) {
- for (iX = x; iX < iXMax; iX++) {
- if (rt_band_get_pixel(rtband, iX, iY, &value, NULL) != ES_NONE) {
- rterror("rt_raster_to_gdal_mem: Could not get pixel value to convert from 8BSI to 16BSI");
- rtdealloc(values);
- if (allocBandNums) rtdealloc(bandNums);
- if (allocNodataValues) rtdealloc(excludeNodataValues);
- GDALClose(ds);
- return 0;
- }
-
- values[z++] = rt_util_clamp_to_16BSI(value);
- }
- }
-
- /* burn values */
- if (GDALRasterIO(
- band, GF_Write,
- x, y,
- nXValid, nYValid,
- values, nXValid, nYValid,
- gdal_pt,
- 0, 0
- ) != CE_None) {
- rterror("rt_raster_to_gdal_mem: Could not write converted 8BSI to 16BSI values to GDAL band");
- rtdealloc(values);
- if (allocBandNums) rtdealloc(bandNums);
- if (allocNodataValues) rtdealloc(excludeNodataValues);
- GDALClose(ds);
- return 0;
- }
- }
- }
-
- rtdealloc(values);
- }
-
- /* Add nodata value for band */
- if (rt_band_get_hasnodata_flag(rtband) != FALSE && excludeNodataValues[i]) {
- rt_band_get_nodata(rtband, &nodata);
- if (GDALSetRasterNoDataValue(band, nodata) != CE_None)
- rtwarn("rt_raster_to_gdal_mem: Could not set nodata value for band");
- RASTER_DEBUGF(3, "nodata value set to %f", GDALGetRasterNoDataValue(band, NULL));
- }
-
-#if POSTGIS_DEBUG_LEVEL > 3
- {
- GDALRasterBandH _grb = NULL;
- double _min;
- double _max;
- double _mean;
- double _stddev;
-
- _grb = GDALGetRasterBand(ds, i + 1);
- GDALComputeRasterStatistics(_grb, FALSE, &_min, &_max, &_mean, &_stddev, NULL, NULL);
- RASTER_DEBUGF(4, "GDAL Band %d stats: %f, %f, %f, %f", i + 1, _min, _max, _mean, _stddev);
- }
-#endif
-
- }
-
- /* necessary??? */
- GDALFlushCache(ds);
-
- if (allocBandNums) rtdealloc(bandNums);
- if (allocNodataValues) rtdealloc(excludeNodataValues);
-
- return ds;
-}
-
-/**
- * Return a raster from a GDAL dataset
- *
- * @param ds : the GDAL dataset to convert to a raster
- *
- * @return raster or NULL
- */
-rt_raster
-rt_raster_from_gdal_dataset(GDALDatasetH ds) {
- rt_raster rast = NULL;
- double gt[6] = {0};
- CPLErr cplerr;
- uint32_t width = 0;
- uint32_t height = 0;
- uint32_t numBands = 0;
- int i = 0;
- char *authname = NULL;
- char *authcode = NULL;
-
- GDALRasterBandH gdband = NULL;
- GDALDataType gdpixtype = GDT_Unknown;
- rt_band band;
- int32_t idx;
- rt_pixtype pt = PT_END;
- uint32_t ptlen = 0;
- int hasnodata = 0;
- double nodataval;
-
- int x;
- int y;
-
- int nXBlocks, nYBlocks;
- int nXBlockSize, nYBlockSize;
- int iXBlock, iYBlock;
- int nXValid, nYValid;
- int iY;
-
- void *values = NULL;
- uint32_t valueslen = 0;
- void *ptr = NULL;
-
- assert(NULL != ds);
-
- /* raster size */
- width = GDALGetRasterXSize(ds);
- height = GDALGetRasterYSize(ds);
- RASTER_DEBUGF(3, "Raster dimensions (width x height): %d x %d", width, height);
-
- /* create new raster */
- RASTER_DEBUG(3, "Creating new raster");
- rast = rt_raster_new(width, height);
- if (NULL == rast) {
- rterror("rt_raster_from_gdal_dataset: Out of memory allocating new raster");
- return NULL;
- }
- RASTER_DEBUGF(3, "Created raster dimensions (width x height): %d x %d", rast->width, rast->height);
-
- /* get raster attributes */
- cplerr = GDALGetGeoTransform(ds, gt);
- if (GDALGetGeoTransform(ds, gt) != CE_None) {
- RASTER_DEBUG(4, "Using default geotransform matrix (0, 1, 0, 0, 0, -1)");
- gt[0] = 0;
- gt[1] = 1;
- gt[2] = 0;
- gt[3] = 0;
- gt[4] = 0;
- gt[5] = -1;
- }
-
- /* apply raster attributes */
- rt_raster_set_geotransform_matrix(rast, gt);
-
- RASTER_DEBUGF(3, "Raster geotransform (%f, %f, %f, %f, %f, %f)",
- gt[0], gt[1], gt[2], gt[3], gt[4], gt[5]);
-
- /* srid */
- if (rt_util_gdal_sr_auth_info(ds, &authname, &authcode) == ES_NONE) {
- if (
- authname != NULL &&
- strcmp(authname, "EPSG") == 0 &&
- authcode != NULL
- ) {
- rt_raster_set_srid(rast, atoi(authcode));
- RASTER_DEBUGF(3, "New raster's SRID = %d", rast->srid);
- }
-
- if (authname != NULL)
- rtdealloc(authname);
- if (authcode != NULL)
- rtdealloc(authcode);
- }
-
- numBands = GDALGetRasterCount(ds);
-
-#if POSTGIS_DEBUG_LEVEL > 3
- for (i = 1; i <= numBands; i++) {
- GDALRasterBandH _grb = NULL;
- double _min;
- double _max;
- double _mean;
- double _stddev;
-
- _grb = GDALGetRasterBand(ds, i);
- GDALComputeRasterStatistics(_grb, FALSE, &_min, &_max, &_mean, &_stddev, NULL, NULL);
- RASTER_DEBUGF(4, "GDAL Band %d stats: %f, %f, %f, %f", i, _min, _max, _mean, _stddev);
- }
-#endif
-
- /* copy bands */
- for (i = 1; i <= numBands; i++) {
- RASTER_DEBUGF(3, "Processing band %d of %d", i, numBands);
- gdband = NULL;
- gdband = GDALGetRasterBand(ds, i);
- if (NULL == gdband) {
- rterror("rt_raster_from_gdal_dataset: Could not get GDAL band");
- rt_raster_destroy(rast);
- return NULL;
- }
- RASTER_DEBUGF(4, "gdband @ %p", gdband);
-
- /* pixtype */
- gdpixtype = GDALGetRasterDataType(gdband);
- RASTER_DEBUGF(4, "gdpixtype, size = %s, %d", GDALGetDataTypeName(gdpixtype), GDALGetDataTypeSize(gdpixtype) / 8);
- pt = rt_util_gdal_datatype_to_pixtype(gdpixtype);
- if (pt == PT_END) {
- rterror("rt_raster_from_gdal_dataset: Unknown pixel type for GDAL band");
- rt_raster_destroy(rast);
- return NULL;
- }
- ptlen = rt_pixtype_size(pt);
-
- /* size: width and height */
- width = GDALGetRasterBandXSize(gdband);
- height = GDALGetRasterBandYSize(gdband);
- RASTER_DEBUGF(3, "GDAL band dimensions (width x height): %d x %d", width, height);
-
- /* nodata */
- nodataval = GDALGetRasterNoDataValue(gdband, &hasnodata);
- RASTER_DEBUGF(3, "(hasnodata, nodataval) = (%d, %f)", hasnodata, nodataval);
-
- /* create band object */
- idx = rt_raster_generate_new_band(
- rast, pt,
- (hasnodata ? nodataval : 0),
- hasnodata, nodataval, rt_raster_get_num_bands(rast)
- );
- if (idx < 0) {
- rterror("rt_raster_from_gdal_dataset: Could not allocate memory for raster band");
- rt_raster_destroy(rast);
- return NULL;
- }
- band = rt_raster_get_band(rast, idx);
- RASTER_DEBUGF(3, "Created band of dimension (width x height): %d x %d", band->width, band->height);
-
- /* this makes use of GDAL's "natural" blocks */
- GDALGetBlockSize(gdband, &nXBlockSize, &nYBlockSize);
- nXBlocks = (width + nXBlockSize - 1) / nXBlockSize;
- nYBlocks = (height + nYBlockSize - 1) / nYBlockSize;
- RASTER_DEBUGF(4, "(nXBlockSize, nYBlockSize) = (%d, %d)", nXBlockSize, nYBlockSize);
- RASTER_DEBUGF(4, "(nXBlocks, nYBlocks) = (%d, %d)", nXBlocks, nYBlocks);
-
- /* allocate memory for values */
- valueslen = ptlen * nXBlockSize * nYBlockSize;
- switch (gdpixtype) {
- case GDT_Byte:
- values = (uint8_t *) rtalloc(valueslen);
- break;
- case GDT_UInt16:
- values = (uint16_t *) rtalloc(valueslen);
- break;
- case GDT_Int16:
- values = (int16_t *) rtalloc(valueslen);
- break;
- case GDT_UInt32:
- values = (uint32_t *) rtalloc(valueslen);
- break;
- case GDT_Int32:
- values = (int32_t *) rtalloc(valueslen);
- break;
- case GDT_Float32:
- values = (float *) rtalloc(valueslen);
- break;
- case GDT_Float64:
- values = (double *) rtalloc(valueslen);
- break;
- default:
- /* should NEVER get here */
- rterror("rt_raster_from_gdal_dataset: Could not allocate memory for unknown pixel type");
- rt_raster_destroy(rast);
- return NULL;
- }
- if (values == NULL) {
- rterror("rt_raster_from_gdal_dataset: Could not allocate memory for GDAL band pixel values");
- rt_raster_destroy(rast);
- return NULL;
- }
- RASTER_DEBUGF(3, "values @ %p of length = %d", values, valueslen);
-
- for (iYBlock = 0; iYBlock < nYBlocks; iYBlock++) {
- for (iXBlock = 0; iXBlock < nXBlocks; iXBlock++) {
- x = iXBlock * nXBlockSize;
- y = iYBlock * nYBlockSize;
- RASTER_DEBUGF(4, "(iXBlock, iYBlock) = (%d, %d)", iXBlock, iYBlock);
- RASTER_DEBUGF(4, "(x, y) = (%d, %d)", x, y);
-
- memset(values, 0, valueslen);
-
- /* valid block width */
- if ((iXBlock + 1) * nXBlockSize > width)
- nXValid = width - (iXBlock * nXBlockSize);
- else
- nXValid = nXBlockSize;
-
- /* valid block height */
- if ((iYBlock + 1) * nYBlockSize > height)
- nYValid = height - (iYBlock * nYBlockSize);
- else
- nYValid = nYBlockSize;
-
- RASTER_DEBUGF(4, "(nXValid, nYValid) = (%d, %d)", nXValid, nYValid);
-
- cplerr = GDALRasterIO(
- gdband, GF_Read,
- x, y,
- nXValid, nYValid,
- values, nXValid, nYValid,
- gdpixtype,
- 0, 0
- );
- if (cplerr != CE_None) {
- rterror("rt_raster_from_gdal_dataset: Could not get data from GDAL raster");
- rtdealloc(values);
- rt_raster_destroy(rast);
- return NULL;
- }
-
- /* if block width is same as raster width, shortcut */
- if (nXBlocks == 1 && nYBlockSize > 1 && nXValid == width) {
- x = 0;
- y = nYBlockSize * iYBlock;
-
- RASTER_DEBUGF(4, "Setting set of pixel lines at (%d, %d) for %d pixels", x, y, nXValid * nYValid);
- rt_band_set_pixel_line(band, x, y, values, nXValid * nYValid);
- }
- else {
- ptr = values;
- x = nXBlockSize * iXBlock;
- for (iY = 0; iY < nYValid; iY++) {
- y = iY + (nYBlockSize * iYBlock);
-
- RASTER_DEBUGF(4, "Setting pixel line at (%d, %d) for %d pixels", x, y, nXValid);
- rt_band_set_pixel_line(band, x, y, ptr, nXValid);
- ptr += (nXValid * ptlen);
- }
- }
- }
- }
-
- /* free memory */
- rtdealloc(values);
- }
-
- return rast;
-}
-
-/******************************************************************************
-* rt_raster_gdal_warp()
-******************************************************************************/
-
-typedef struct _rti_warp_arg_t* _rti_warp_arg;
-struct _rti_warp_arg_t {
-
- struct {
- GDALDriverH drv;
- GDALDatasetH ds;
- char *srs;
- } src, dst;
-
- GDALWarpOptions *wopts;
-
- struct {
- struct {
- char **item;
- int len;
- } option;
-
- struct {
- void *transform;
- void *imgproj;
- void *approx;
- } arg;
-
- GDALTransformerFunc func;
- } transform;
-
-};
-
-static _rti_warp_arg
-_rti_warp_arg_init() {
- _rti_warp_arg arg = NULL;
-
- arg = rtalloc(sizeof(struct _rti_warp_arg_t));
- if (arg == NULL) {
- rterror("_rti_warp_arg_init: Could not allocate memory for _rti_warp_arg");
- return NULL;
- }
-
- arg->src.drv = NULL;
- arg->src.ds = NULL;
- arg->src.srs = NULL;
-
- arg->dst.drv = NULL;
- arg->dst.ds = NULL;
- arg->dst.srs = NULL;
-
- arg->wopts = NULL;
-
- arg->transform.option.item = NULL;
- arg->transform.option.len = 0;
-
- arg->transform.arg.transform = NULL;
- arg->transform.arg.imgproj = NULL;
- arg->transform.arg.approx = NULL;
-
- arg->transform.func = NULL;
-
- return arg;
-}
-
-static void
-_rti_warp_arg_destroy(_rti_warp_arg arg) {
- int i = 0;
-
- if (arg->dst.ds != NULL)
- GDALClose(arg->dst.ds);
- if (arg->dst.srs != NULL)
- CPLFree(arg->dst.srs);
-
- if (arg->src.ds != NULL)
- GDALClose(arg->src.ds);
- if (arg->src.srs != NULL)
- CPLFree(arg->src.srs);
-
- if (arg->transform.func == GDALApproxTransform) {
- if (arg->transform.arg.imgproj != NULL)
- GDALDestroyGenImgProjTransformer(arg->transform.arg.imgproj);
- }
-
- if (arg->wopts != NULL)
- GDALDestroyWarpOptions(arg->wopts);
-
- if (arg->transform.option.len > 0 && arg->transform.option.item != NULL) {
- for (i = 0; i < arg->transform.option.len; i++) {
- if (arg->transform.option.item[i] != NULL)
- rtdealloc(arg->transform.option.item[i]);
- }
- rtdealloc(arg->transform.option.item);
- }
-
- rtdealloc(arg);
- arg = NULL;
-}
-
-/**
- * Return a warped raster using GDAL Warp API
- *
- * @param raster : raster to transform
- * @param src_srs : the raster's coordinate system in OGC WKT
- * @param dst_srs : the warped raster's coordinate system in OGC WKT
- * @param scale_x : the x size of pixels of the warped raster's pixels in
- * units of dst_srs
- * @param scale_y : the y size of pixels of the warped raster's pixels in
- * units of dst_srs
- * @param width : the number of columns of the warped raster. note that
- * width/height CANNOT be used with scale_x/scale_y
- * @param height : the number of rows of the warped raster. note that
- * width/height CANNOT be used with scale_x/scale_y
- * @param ul_xw : the X value of upper-left corner of the warped raster in
- * units of dst_srs
- * @param ul_yw : the Y value of upper-left corner of the warped raster in
- * units of dst_srs
- * @param grid_xw : the X value of point on a grid to align warped raster
- * to in units of dst_srs
- * @param grid_yw : the Y value of point on a grid to align warped raster
- * to in units of dst_srs
- * @param skew_x : the X skew of the warped raster in units of dst_srs
- * @param skew_y : the Y skew of the warped raster in units of dst_srs
- * @param resample_alg : the resampling algorithm
- * @param max_err : maximum error measured in input pixels permitted
- * (0.0 for exact calculations)
- *
- * @return the warped raster or NULL
- */
-rt_raster rt_raster_gdal_warp(
- rt_raster raster,
- const char *src_srs, const char *dst_srs,
- double *scale_x, double *scale_y,
- int *width, int *height,
- double *ul_xw, double *ul_yw,
- double *grid_xw, double *grid_yw,
- double *skew_x, double *skew_y,
- GDALResampleAlg resample_alg, double max_err
-) {
- CPLErr cplerr;
- char *dst_options[] = {"SUBCLASS=VRTWarpedDataset", NULL};
- _rti_warp_arg arg = NULL;
-
- int hasnodata = 0;
-
- GDALRasterBandH band;
- rt_band rtband = NULL;
- rt_pixtype pt = PT_END;
- GDALDataType gdal_pt = GDT_Unknown;
- double nodata = 0.0;
-
- double _gt[6] = {0};
- double dst_extent[4];
- rt_envelope extent;
-
- int _dim[2] = {0};
- double _skew[2] = {0};
- double _scale[2] = {0};
- int ul_user = 0;
-
- rt_raster rast = NULL;
- int i = 0;
- int numBands = 0;
-
- int subgt = 0;
-
- RASTER_DEBUG(3, "starting");
-
- assert(NULL != raster);
-
- /* internal variables */
- arg = _rti_warp_arg_init();
- if (arg == NULL) {
- rterror("rt_raster_gdal_warp: Could not initialize internal variables");
- return NULL;
- }
-
- /*
- max_err must be gte zero
-
- the value 0.125 is the default used in gdalwarp.cpp on line 283
- */
- if (max_err < 0.) max_err = 0.125;
- RASTER_DEBUGF(4, "max_err = %f", max_err);
-
- /* handle srs */
- if (src_srs != NULL) {
- /* reprojection taking place */
- if (dst_srs != NULL && strcmp(src_srs, dst_srs) != 0) {
- RASTER_DEBUG(4, "Warp operation does include a reprojection");
- arg->src.srs = rt_util_gdal_convert_sr(src_srs, 0);
- arg->dst.srs = rt_util_gdal_convert_sr(dst_srs, 0);
-
- if (arg->src.srs == NULL || arg->dst.srs == NULL) {
- rterror("rt_raster_gdal_warp: Could not convert srs values to GDAL accepted format");
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
- }
- /* no reprojection, a stub just for clarity */
- else {
- RASTER_DEBUG(4, "Warp operation does NOT include reprojection");
- }
- }
- else if (dst_srs != NULL) {
- /* dst_srs provided but not src_srs */
- rterror("rt_raster_gdal_warp: SRS required for input raster if SRS provided for warped raster");
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- /* load raster into a GDAL MEM dataset */
- arg->src.ds = rt_raster_to_gdal_mem(raster, arg->src.srs, NULL, NULL, 0, &(arg->src.drv));
- if (NULL == arg->src.ds) {
- rterror("rt_raster_gdal_warp: Could not convert raster to GDAL MEM format");
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
- RASTER_DEBUG(3, "raster loaded into GDAL MEM dataset");
-
- /* special case when src_srs and dst_srs is NULL and raster's geotransform matrix is default */
- if (src_srs == NULL && dst_srs == NULL && rt_raster_get_srid(raster) == SRID_UNKNOWN) {
- double gt[6];
-
-#if POSTGIS_DEBUG_LEVEL > 3
- GDALGetGeoTransform(arg->src.ds, gt);
- RASTER_DEBUGF(3, "GDAL MEM geotransform: %f, %f, %f, %f, %f, %f",
- gt[0], gt[1], gt[2], gt[3], gt[4], gt[5]);
-#endif
-
- /* default geotransform */
- rt_raster_get_geotransform_matrix(raster, gt);
- RASTER_DEBUGF(3, "raster geotransform: %f, %f, %f, %f, %f, %f",
- gt[0], gt[1], gt[2], gt[3], gt[4], gt[5]);
-
- if (
- FLT_EQ(gt[0], 0) &&
- FLT_EQ(gt[1], 1) &&
- FLT_EQ(gt[2], 0) &&
- FLT_EQ(gt[3], 0) &&
- FLT_EQ(gt[4], 0) &&
- FLT_EQ(gt[5], -1)
- ) {
- double ngt[6] = {0, 10, 0, 0, 0, -10};
-
- rtinfo("Raster has default geotransform. Adjusting metadata for use of GDAL Warp API");
-
- GDALSetGeoTransform(arg->src.ds, ngt);
- GDALFlushCache(arg->src.ds);
-
- subgt = 1;
-
-#if POSTGIS_DEBUG_LEVEL > 3
- GDALGetGeoTransform(arg->src.ds, gt);
- RASTER_DEBUGF(3, "GDAL MEM geotransform: %f, %f, %f, %f, %f, %f",
- gt[0], gt[1], gt[2], gt[3], gt[4], gt[5]);
-#endif
- }
- }
-
- /* set transform options */
- if (arg->src.srs != NULL || arg->dst.srs != NULL) {
- arg->transform.option.len = 2;
- arg->transform.option.item = rtalloc(sizeof(char *) * (arg->transform.option.len + 1));
- if (NULL == arg->transform.option.item) {
- rterror("rt_raster_gdal_warp: Could not allocation memory for transform options");
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
- memset(arg->transform.option.item, 0, sizeof(char *) * (arg->transform.option.len + 1));
-
- for (i = 0; i < arg->transform.option.len; i++) {
- switch (i) {
- case 1:
- if (arg->dst.srs != NULL)
- arg->transform.option.item[i] = (char *) rtalloc(sizeof(char) * (strlen("DST_SRS=") + strlen(arg->dst.srs) + 1));
- else
- arg->transform.option.item[i] = (char *) rtalloc(sizeof(char) * (strlen("DST_SRS=") + 1));
- break;
- case 0:
- if (arg->src.srs != NULL)
- arg->transform.option.item[i] = (char *) rtalloc(sizeof(char) * (strlen("SRC_SRS=") + strlen(arg->src.srs) + 1));
- else
- arg->transform.option.item[i] = (char *) rtalloc(sizeof(char) * (strlen("SRC_SRS=") + 1));
- break;
- }
- if (NULL == arg->transform.option.item[i]) {
- rterror("rt_raster_gdal_warp: Could not allocation memory for transform options");
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- switch (i) {
- case 1:
- if (arg->dst.srs != NULL) {
- snprintf(
- arg->transform.option.item[i],
- sizeof(char) * (strlen("DST_SRS=") + strlen(arg->dst.srs) + 1),
- "DST_SRS=%s",
- arg->dst.srs
- );
- }
- else
- sprintf(arg->transform.option.item[i], "%s", "DST_SRS=");
- break;
- case 0:
- if (arg->src.srs != NULL) {
- snprintf(
- arg->transform.option.item[i],
- sizeof(char) * (strlen("SRC_SRS=") + strlen(arg->src.srs) + 1),
- "SRC_SRS=%s",
- arg->src.srs
- );
- }
- else
- sprintf(arg->transform.option.item[i], "%s", "SRC_SRS=");
- break;
- }
- RASTER_DEBUGF(4, "arg->transform.option.item[%d] = %s", i, arg->transform.option.item[i]);
- }
- }
- else
- arg->transform.option.len = 0;
-
- /* transformation object for building dst dataset */
- arg->transform.arg.transform = GDALCreateGenImgProjTransformer2(arg->src.ds, NULL, arg->transform.option.item);
- if (NULL == arg->transform.arg.transform) {
- rterror("rt_raster_gdal_warp: Could not create GDAL transformation object for output dataset creation");
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- /* get approximate output georeferenced bounds and resolution */
- cplerr = GDALSuggestedWarpOutput2(
- arg->src.ds, GDALGenImgProjTransform,
- arg->transform.arg.transform, _gt, &(_dim[0]), &(_dim[1]), dst_extent, 0);
- if (cplerr != CE_None) {
- rterror("rt_raster_gdal_warp: Could not get GDAL suggested warp output for output dataset creation");
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
- GDALDestroyGenImgProjTransformer(arg->transform.arg.transform);
- arg->transform.arg.transform = NULL;
-
- /*
- don't use suggested dimensions as use of suggested scales
- on suggested extent will result in suggested dimensions
- */
- _dim[0] = 0;
- _dim[1] = 0;
-
- RASTER_DEBUGF(3, "Suggested geotransform: %f, %f, %f, %f, %f, %f",
- _gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]);
-
- /* store extent in easier-to-use object */
- extent.MinX = dst_extent[0];
- extent.MinY = dst_extent[1];
- extent.MaxX = dst_extent[2];
- extent.MaxY = dst_extent[3];
-
- extent.UpperLeftX = dst_extent[0];
- extent.UpperLeftY = dst_extent[3];
-
- RASTER_DEBUGF(3, "Suggested extent: %f, %f, %f, %f",
- extent.MinX, extent.MinY, extent.MaxX, extent.MaxY);
-
- /* scale and width/height are mutually exclusive */
- if (
- ((NULL != scale_x) || (NULL != scale_y)) &&
- ((NULL != width) || (NULL != height))
- ) {
- rterror("rt_raster_gdal_warp: Scale X/Y and width/height are mutually exclusive. Only provide one");
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- /* user-defined width */
- if (NULL != width) {
- _dim[0] = abs(*width);
- _scale[0] = fabs((extent.MaxX - extent.MinX) / ((double) _dim[0]));
- }
- /* user-defined height */
- if (NULL != height) {
- _dim[1] = abs(*height);
- _scale[1] = fabs((extent.MaxY - extent.MinY) / ((double) _dim[1]));
- }
-
- /* user-defined scale */
- if (
- ((NULL != scale_x) && (FLT_NEQ(*scale_x, 0.0))) &&
- ((NULL != scale_y) && (FLT_NEQ(*scale_y, 0.0)))
- ) {
- _scale[0] = fabs(*scale_x);
- _scale[1] = fabs(*scale_y);
-
- /* special override */
- if (subgt) {
- _scale[0] *= 10;
- _scale[1] *= 10;
- }
- }
- else if (
- ((NULL != scale_x) && (NULL == scale_y)) ||
- ((NULL == scale_x) && (NULL != scale_y))
- ) {
- rterror("rt_raster_gdal_warp: Both X and Y scale values must be provided for scale");
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- /* scale not defined, use suggested */
- if (FLT_EQ(_scale[0], 0) && FLT_EQ(_scale[1], 0)) {
- _scale[0] = fabs(_gt[1]);
- _scale[1] = fabs(_gt[5]);
- }
-
- RASTER_DEBUGF(4, "Using scale: %f x %f", _scale[0], -1 * _scale[1]);
-
- /* user-defined skew */
- if (NULL != skew_x) {
- _skew[0] = *skew_x;
-
- /*
- negative scale-x affects skew
- for now, force skew to be in left-right, top-down orientation
- */
- if (
- NULL != scale_x &&
- *scale_x < 0.
- ) {
- _skew[0] *= -1;
- }
- }
- if (NULL != skew_y) {
- _skew[1] = *skew_y;
-
- /*
- positive scale-y affects skew
- for now, force skew to be in left-right, top-down orientation
- */
- if (
- NULL != scale_y &&
- *scale_y > 0.
- ) {
- _skew[1] *= -1;
- }
- }
-
- RASTER_DEBUGF(4, "Using skew: %f x %f", _skew[0], _skew[1]);
-
- /* reprocess extent if skewed */
- if (
- FLT_NEQ(_skew[0], 0) ||
- FLT_NEQ(_skew[1], 0)
- ) {
- rt_raster skewedrast;
-
- RASTER_DEBUG(3, "Computing skewed extent's envelope");
-
- skewedrast = rt_raster_compute_skewed_raster(
- extent,
- _skew,
- _scale,
- 0.01
- );
- if (skewedrast == NULL) {
- rterror("rt_raster_gdal_warp: Could not compute skewed raster");
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- if (_dim[0] == 0)
- _dim[0] = skewedrast->width;
- if (_dim[1] == 0)
- _dim[1] = skewedrast->height;
-
- extent.UpperLeftX = skewedrast->ipX;
- extent.UpperLeftY = skewedrast->ipY;
-
- rt_raster_destroy(skewedrast);
- }
-
- /* dimensions not defined, compute */
- if (!_dim[0])
- _dim[0] = (int) fmax((fabs(extent.MaxX - extent.MinX) + (_scale[0] / 2.)) / _scale[0], 1);
- if (!_dim[1])
- _dim[1] = (int) fmax((fabs(extent.MaxY - extent.MinY) + (_scale[1] / 2.)) / _scale[1], 1);
-
- /* temporary raster */
- rast = rt_raster_new(_dim[0], _dim[1]);
- if (rast == NULL) {
- rterror("rt_raster_gdal_warp: Out of memory allocating temporary raster");
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- /* set raster's spatial attributes */
- rt_raster_set_offsets(rast, extent.UpperLeftX, extent.UpperLeftY);
- rt_raster_set_scale(rast, _scale[0], -1 * _scale[1]);
- rt_raster_set_skews(rast, _skew[0], _skew[1]);
-
- rt_raster_get_geotransform_matrix(rast, _gt);
- RASTER_DEBUGF(3, "Temp raster's geotransform: %f, %f, %f, %f, %f, %f",
- _gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]);
- RASTER_DEBUGF(3, "Temp raster's dimensions (width x height): %d x %d",
- _dim[0], _dim[1]);
-
- /* user-defined upper-left corner */
- if (
- NULL != ul_xw &&
- NULL != ul_yw
- ) {
- ul_user = 1;
-
- RASTER_DEBUGF(4, "Using user-specified upper-left corner: %f, %f", *ul_xw, *ul_yw);
-
- /* set upper-left corner */
- rt_raster_set_offsets(rast, *ul_xw, *ul_yw);
- extent.UpperLeftX = *ul_xw;
- extent.UpperLeftY = *ul_yw;
- }
- else if (
- ((NULL != ul_xw) && (NULL == ul_yw)) ||
- ((NULL == ul_xw) && (NULL != ul_yw))
- ) {
- rterror("rt_raster_gdal_warp: Both X and Y upper-left corner values must be provided");
- rt_raster_destroy(rast);
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- /* alignment only considered if upper-left corner not provided */
- if (
- !ul_user && (
- (NULL != grid_xw) || (NULL != grid_yw)
- )
- ) {
-
- if (
- ((NULL != grid_xw) && (NULL == grid_yw)) ||
- ((NULL == grid_xw) && (NULL != grid_yw))
- ) {
- rterror("rt_raster_gdal_warp: Both X and Y alignment values must be provided");
- rt_raster_destroy(rast);
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- RASTER_DEBUGF(4, "Aligning extent to user-specified grid: %f, %f", *grid_xw, *grid_yw);
-
- do {
- double _r[2] = {0};
- double _w[2] = {0};
-
- /* raster is already aligned */
- if (FLT_EQ(*grid_xw, extent.UpperLeftX) && FLT_EQ(*grid_yw, extent.UpperLeftY)) {
- RASTER_DEBUG(3, "Skipping raster alignment as it is already aligned to grid");
- break;
- }
-
- extent.UpperLeftX = rast->ipX;
- extent.UpperLeftY = rast->ipY;
- rt_raster_set_offsets(rast, *grid_xw, *grid_yw);
-
- /* process upper-left corner */
- if (rt_raster_geopoint_to_cell(
- rast,
- extent.UpperLeftX, extent.UpperLeftY,
- &(_r[0]), &(_r[1]),
- NULL
- ) != ES_NONE) {
- rterror("rt_raster_gdal_warp: Could not compute raster pixel for spatial coordinates");
- rt_raster_destroy(rast);
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- if (rt_raster_cell_to_geopoint(
- rast,
- _r[0], _r[1],
- &(_w[0]), &(_w[1]),
- NULL
- ) != ES_NONE) {
- rterror("rt_raster_gdal_warp: Could not compute spatial coordinates for raster pixel");
-
- rt_raster_destroy(rast);
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- /* shift occurred */
- if (FLT_NEQ(_w[0], extent.UpperLeftX)) {
- if (NULL == width)
- rast->width++;
- else if (NULL == scale_x) {
- double _c[2] = {0};
-
- rt_raster_set_offsets(rast, extent.UpperLeftX, extent.UpperLeftY);
-
- /* get upper-right corner */
- if (rt_raster_cell_to_geopoint(
- rast,
- rast->width, 0,
- &(_c[0]), &(_c[1]),
- NULL
- ) != ES_NONE) {
- rterror("rt_raster_gdal_warp: Could not compute spatial coordinates for raster pixel");
- rt_raster_destroy(rast);
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- rast->scaleX = fabs((_c[0] - _w[0]) / ((double) rast->width));
- }
- }
- if (FLT_NEQ(_w[1], extent.UpperLeftY)) {
- if (NULL == height)
- rast->height++;
- else if (NULL == scale_y) {
- double _c[2] = {0};
-
- rt_raster_set_offsets(rast, extent.UpperLeftX, extent.UpperLeftY);
-
- /* get upper-right corner */
- if (rt_raster_cell_to_geopoint(
- rast,
- 0, rast->height,
- &(_c[0]), &(_c[1]),
- NULL
- ) != ES_NONE) {
- rterror("rt_raster_gdal_warp: Could not compute spatial coordinates for raster pixel");
-
- rt_raster_destroy(rast);
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- rast->scaleY = -1 * fabs((_c[1] - _w[1]) / ((double) rast->height));
- }
- }
-
- rt_raster_set_offsets(rast, _w[0], _w[1]);
- RASTER_DEBUGF(4, "aligned offsets: %f x %f", _w[0], _w[1]);
- }
- while (0);
- }
-
- /*
- after this point, rt_envelope extent is no longer used
- */
-
- /* get key attributes from rast */
- _dim[0] = rast->width;
- _dim[1] = rast->height;
- rt_raster_get_geotransform_matrix(rast, _gt);
-
- /* scale-x is negative or scale-y is positive */
- if ((
- (NULL != scale_x) && (*scale_x < 0.)
- ) || (
- (NULL != scale_y) && (*scale_y > 0)
- )) {
- double _w[2] = {0};
-
- /* negative scale-x */
- if (
- (NULL != scale_x) &&
- (*scale_x < 0.)
- ) {
- if (rt_raster_cell_to_geopoint(
- rast,
- rast->width, 0,
- &(_w[0]), &(_w[1]),
- NULL
- ) != ES_NONE) {
- rterror("rt_raster_gdal_warp: Could not compute spatial coordinates for raster pixel");
- rt_raster_destroy(rast);
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- _gt[0] = _w[0];
- _gt[1] = *scale_x;
-
- /* check for skew */
- if (NULL != skew_x && FLT_NEQ(*skew_x, 0))
- _gt[2] = *skew_x;
- }
- /* positive scale-y */
- if (
- (NULL != scale_y) &&
- (*scale_y > 0)
- ) {
- if (rt_raster_cell_to_geopoint(
- rast,
- 0, rast->height,
- &(_w[0]), &(_w[1]),
- NULL
- ) != ES_NONE) {
- rterror("rt_raster_gdal_warp: Could not compute spatial coordinates for raster pixel");
- rt_raster_destroy(rast);
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- _gt[3] = _w[1];
- _gt[5] = *scale_y;
-
- /* check for skew */
- if (NULL != skew_y && FLT_NEQ(*skew_y, 0))
- _gt[4] = *skew_y;
- }
- }
-
- rt_raster_destroy(rast);
- rast = NULL;
-
- RASTER_DEBUGF(3, "Applied geotransform: %f, %f, %f, %f, %f, %f",
- _gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]);
- RASTER_DEBUGF(3, "Raster dimensions (width x height): %d x %d",
- _dim[0], _dim[1]);
-
- if (FLT_EQ(_dim[0], 0) || FLT_EQ(_dim[1], 0)) {
- rterror("rt_raster_gdal_warp: The width (%f) or height (%f) of the warped raster is zero", _dim[0], _dim[1]);
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- /* load VRT driver */
- if (!rt_util_gdal_driver_registered("VRT"))
- GDALRegister_VRT();
- arg->dst.drv = GDALGetDriverByName("VRT");
- if (NULL == arg->dst.drv) {
- rterror("rt_raster_gdal_warp: Could not load the output GDAL VRT driver");
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- /* create dst dataset */
- arg->dst.ds = GDALCreate(arg->dst.drv, "", _dim[0], _dim[1], 0, GDT_Byte, dst_options);
- if (NULL == arg->dst.ds) {
- rterror("rt_raster_gdal_warp: Could not create GDAL VRT dataset");
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- /* set dst srs */
- if (arg->dst.srs != NULL) {
- cplerr = GDALSetProjection(arg->dst.ds, arg->dst.srs);
- if (cplerr != CE_None) {
- rterror("rt_raster_gdal_warp: Could not set projection");
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
- RASTER_DEBUGF(3, "Applied SRS: %s", GDALGetProjectionRef(arg->dst.ds));
- }
-
- /* set dst geotransform */
- cplerr = GDALSetGeoTransform(arg->dst.ds, _gt);
- if (cplerr != CE_None) {
- rterror("rt_raster_gdal_warp: Could not set geotransform");
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- /* add bands to dst dataset */
- numBands = rt_raster_get_num_bands(raster);
- for (i = 0; i < numBands; i++) {
- rtband = rt_raster_get_band(raster, i);
- if (NULL == rtband) {
- rterror("rt_raster_gdal_warp: Could not get band %d for adding to VRT dataset", i);
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- pt = rt_band_get_pixtype(rtband);
- gdal_pt = rt_util_pixtype_to_gdal_datatype(pt);
- if (gdal_pt == GDT_Unknown)
- rtwarn("rt_raster_gdal_warp: Unknown pixel type for band %d", i);
-
- cplerr = GDALAddBand(arg->dst.ds, gdal_pt, NULL);
- if (cplerr != CE_None) {
- rterror("rt_raster_gdal_warp: Could not add band to VRT dataset");
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- /* get band to write data to */
- band = NULL;
- band = GDALGetRasterBand(arg->dst.ds, i + 1);
- if (NULL == band) {
- rterror("rt_raster_gdal_warp: Could not get GDAL band for additional processing");
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- /* set nodata */
- if (rt_band_get_hasnodata_flag(rtband) != FALSE) {
- hasnodata = 1;
- rt_band_get_nodata(rtband, &nodata);
- if (GDALSetRasterNoDataValue(band, nodata) != CE_None)
- rtwarn("rt_raster_gdal_warp: Could not set nodata value for band %d", i);
- RASTER_DEBUGF(3, "nodata value set to %f", GDALGetRasterNoDataValue(band, NULL));
- }
- }
-
- /* create transformation object */
- arg->transform.arg.transform = arg->transform.arg.imgproj = GDALCreateGenImgProjTransformer2(
- arg->src.ds, arg->dst.ds,
- arg->transform.option.item
- );
- if (NULL == arg->transform.arg.transform) {
- rterror("rt_raster_gdal_warp: Could not create GDAL transformation object");
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
- arg->transform.func = GDALGenImgProjTransform;
-
- /* use approximate transformation object */
- if (max_err > 0.0) {
- arg->transform.arg.transform = arg->transform.arg.approx = GDALCreateApproxTransformer(
- GDALGenImgProjTransform,
- arg->transform.arg.imgproj, max_err
- );
- if (NULL == arg->transform.arg.transform) {
- rterror("rt_raster_gdal_warp: Could not create GDAL approximate transformation object");
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- arg->transform.func = GDALApproxTransform;
- }
-
- /* warp options */
- arg->wopts = GDALCreateWarpOptions();
- if (NULL == arg->wopts) {
- rterror("rt_raster_gdal_warp: Could not create GDAL warp options object");
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- /* set options */
- arg->wopts->eResampleAlg = resample_alg;
- arg->wopts->hSrcDS = arg->src.ds;
- arg->wopts->hDstDS = arg->dst.ds;
- arg->wopts->pfnTransformer = arg->transform.func;
- arg->wopts->pTransformerArg = arg->transform.arg.transform;
- arg->wopts->papszWarpOptions = (char **) CPLMalloc(sizeof(char *) * 2);
- arg->wopts->papszWarpOptions[0] = (char *) CPLMalloc(sizeof(char) * (strlen("INIT_DEST=NO_DATA") + 1));
- strcpy(arg->wopts->papszWarpOptions[0], "INIT_DEST=NO_DATA");
- arg->wopts->papszWarpOptions[1] = NULL;
-
- /* set band mapping */
- arg->wopts->nBandCount = numBands;
- arg->wopts->panSrcBands = (int *) CPLMalloc(sizeof(int) * arg->wopts->nBandCount);
- arg->wopts->panDstBands = (int *) CPLMalloc(sizeof(int) * arg->wopts->nBandCount);
- for (i = 0; i < arg->wopts->nBandCount; i++)
- arg->wopts->panDstBands[i] = arg->wopts->panSrcBands[i] = i + 1;
-
- /* set nodata mapping */
- if (hasnodata) {
- RASTER_DEBUG(3, "Setting nodata mapping");
- arg->wopts->padfSrcNoDataReal = (double *) CPLMalloc(numBands * sizeof(double));
- arg->wopts->padfDstNoDataReal = (double *) CPLMalloc(numBands * sizeof(double));
- arg->wopts->padfSrcNoDataImag = (double *) CPLMalloc(numBands * sizeof(double));
- arg->wopts->padfDstNoDataImag = (double *) CPLMalloc(numBands * sizeof(double));
- if (
- NULL == arg->wopts->padfSrcNoDataReal ||
- NULL == arg->wopts->padfDstNoDataReal ||
- NULL == arg->wopts->padfSrcNoDataImag ||
- NULL == arg->wopts->padfDstNoDataImag
- ) {
- rterror("rt_raster_gdal_warp: Out of memory allocating nodata mapping");
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
- for (i = 0; i < numBands; i++) {
- band = rt_raster_get_band(raster, i);
- if (!band) {
- rterror("rt_raster_gdal_warp: Could not process bands for nodata values");
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
-
- if (!rt_band_get_hasnodata_flag(band)) {
- /*
- based on line 1004 of gdalwarp.cpp
- the problem is that there is a chance that this number is a legitimate value
- */
- arg->wopts->padfSrcNoDataReal[i] = -123456.789;
- }
- else {
- rt_band_get_nodata(band, &(arg->wopts->padfSrcNoDataReal[i]));
- }
-
- arg->wopts->padfDstNoDataReal[i] = arg->wopts->padfSrcNoDataReal[i];
- arg->wopts->padfDstNoDataImag[i] = arg->wopts->padfSrcNoDataImag[i] = 0.0;
- RASTER_DEBUGF(4, "Mapped nodata value for band %d: %f (%f) => %f (%f)",
- i,
- arg->wopts->padfSrcNoDataReal[i], arg->wopts->padfSrcNoDataImag[i],
- arg->wopts->padfDstNoDataReal[i], arg->wopts->padfDstNoDataImag[i]
- );
- }
- }
-
- /* warp raster */
- RASTER_DEBUG(3, "Warping raster");
- cplerr = GDALInitializeWarpedVRT(arg->dst.ds, arg->wopts);
- if (cplerr != CE_None) {
- rterror("rt_raster_gdal_warp: Could not warp raster");
- _rti_warp_arg_destroy(arg);
- return NULL;
- }
- /*
- GDALSetDescription(arg->dst.ds, "/tmp/warped.vrt");
- */
- GDALFlushCache(arg->dst.ds);
- RASTER_DEBUG(3, "Raster warped");
-
- /* convert gdal dataset to raster */
- RASTER_DEBUG(3, "Converting GDAL dataset to raster");
- rast = rt_raster_from_gdal_dataset(arg->dst.ds);
-
- _rti_warp_arg_destroy(arg);
-
- if (NULL == rast) {
- rterror("rt_raster_gdal_warp: Could not warp raster");
- return NULL;
- }
-
- /* substitute geotransform matrix, reset back to default */
- if (subgt) {
- double gt[6] = {0, 1, 0, 0, 0, -1};
-
- rt_raster_set_geotransform_matrix(rast, gt);
- }
-
- RASTER_DEBUG(3, "done");
-
- return rast;
-}
-
-/******************************************************************************
-* rt_raster_gdal_rasterize()
-******************************************************************************/
-
-typedef struct _rti_rasterize_arg_t* _rti_rasterize_arg;
-struct _rti_rasterize_arg_t {
- uint8_t noband;
-
- uint32_t numbands;
-
- rt_pixtype *pixtype;
- double *init;
- double *nodata;
- uint8_t *hasnodata;
- double *value;
- int *bandlist;
-};
-
-static _rti_rasterize_arg
-_rti_rasterize_arg_init() {
- _rti_rasterize_arg arg = NULL;
-
- arg = rtalloc(sizeof(struct _rti_rasterize_arg_t));
- if (arg == NULL) {
- rterror("_rti_rasterize_arg_init: Could not allocate memory for _rti_rasterize_arg");
- return NULL;
- }
-
- arg->noband = 0;
-
- arg->numbands = 0;
- arg->pixtype = NULL;
- arg->init = NULL;
- arg->nodata = NULL;
- arg->hasnodata = NULL;
- arg->value = NULL;
- arg->bandlist = NULL;
-
- return arg;
-}
-
-static void
-_rti_rasterize_arg_destroy(_rti_rasterize_arg arg) {
- if (arg->noband) {
- if (arg->pixtype != NULL)
- rtdealloc(arg->pixtype);
- if (arg->init != NULL)
- rtdealloc(arg->init);
- if (arg->nodata != NULL)
- rtdealloc(arg->nodata);
- if (arg->hasnodata != NULL)
- rtdealloc(arg->hasnodata);
- if (arg->value != NULL)
- rtdealloc(arg->value);
- }
-
- if (arg->bandlist != NULL)
- rtdealloc(arg->bandlist);
-
- rtdealloc(arg);
-}
-
-/**
- * Return a raster of the provided geometry
- *
- * @param wkb : WKB representation of the geometry to convert
- * @param wkb_len : length of the WKB representation of the geometry
- * @param srs : the geometry's coordinate system in OGC WKT
- * @param num_bands : number of bands in the output raster
- * @param pixtype : data type of each band
- * @param init : array of values to initialize each band with
- * @param value : array of values for pixels of geometry
- * @param nodata : array of nodata values for each band
- * @param hasnodata : array flagging the presence of nodata for each band
- * @param width : the number of columns of the raster
- * @param height : the number of rows of the raster
- * @param scale_x : the pixel width of the raster
- * @param scale_y : the pixel height of the raster
- * @param ul_xw : the X value of upper-left corner of the raster
- * @param ul_yw : the Y value of upper-left corner of the raster
- * @param grid_xw : the X value of point on grid to align raster to
- * @param grid_yw : the Y value of point on grid to align raster to
- * @param skew_x : the X skew of the raster
- * @param skew_y : the Y skew of the raster
- * @param options : array of options. only option is "ALL_TOUCHED"
- *
- * @return the raster of the provided geometry or NULL
- */
-rt_raster
-rt_raster_gdal_rasterize(
- const unsigned char *wkb, uint32_t wkb_len,
- const char *srs,
- uint32_t num_bands, rt_pixtype *pixtype,
- double *init, double *value,
- double *nodata, uint8_t *hasnodata,
- int *width, int *height,
- double *scale_x, double *scale_y,
- double *ul_xw, double *ul_yw,
- double *grid_xw, double *grid_yw,
- double *skew_x, double *skew_y,
- char **options
-) {
- rt_raster rast = NULL;
- int i = 0;
- int err = 0;
-
- _rti_rasterize_arg arg = NULL;
-
- int _dim[2] = {0};
- double _scale[2] = {0};
- double _skew[2] = {0};
-
- OGRErr ogrerr;
- OGRSpatialReferenceH src_sr = NULL;
- OGRGeometryH src_geom;
- OGREnvelope src_env;
- rt_envelope extent;
- OGRwkbGeometryType wkbtype = wkbUnknown;
-
- int ul_user = 0;
-
- CPLErr cplerr;
- double _gt[6] = {0};
- GDALDriverH _drv = NULL;
- GDALDatasetH _ds = NULL;
- GDALRasterBandH _band = NULL;
-
- uint16_t _width = 0;
- uint16_t _height = 0;
-
- RASTER_DEBUG(3, "starting");
-
- assert(NULL != wkb);
- assert(0 != wkb_len);
-
- /* internal variables */
- arg = _rti_rasterize_arg_init();
- if (arg == NULL) {
- rterror("rt_raster_gdal_rasterize: Could not initialize internal variables");
- return NULL;
- }
-
- /* no bands, raster is a mask */
- if (num_bands < 1) {
- arg->noband = 1;
- arg->numbands = 1;
-
- arg->pixtype = (rt_pixtype *) rtalloc(sizeof(rt_pixtype));
- arg->pixtype[0] = PT_8BUI;
-
- arg->init = (double *) rtalloc(sizeof(double));
- arg->init[0] = 0;
-
- arg->nodata = (double *) rtalloc(sizeof(double));
- arg->nodata[0] = 0;
-
- arg->hasnodata = (uint8_t *) rtalloc(sizeof(uint8_t));
- arg->hasnodata[0] = 1;
-
- arg->value = (double *) rtalloc(sizeof(double));
- arg->value[0] = 1;
- }
- else {
- arg->noband = 0;
- arg->numbands = num_bands;
-
- arg->pixtype = pixtype;
- arg->init = init;
- arg->nodata = nodata;
- arg->hasnodata = hasnodata;
- arg->value = value;
- }
-
- /* OGR spatial reference */
- if (NULL != srs && strlen(srs)) {
- src_sr = OSRNewSpatialReference(NULL);
- if (OSRSetFromUserInput(src_sr, srs) != OGRERR_NONE) {
- rterror("rt_raster_gdal_rasterize: Could not create OSR spatial reference using the provided srs: %s", srs);
- _rti_rasterize_arg_destroy(arg);
- return NULL;
- }
- }
-
- /* convert WKB to OGR Geometry */
- ogrerr = OGR_G_CreateFromWkb((unsigned char *) wkb, src_sr, &src_geom, wkb_len);
- if (ogrerr != OGRERR_NONE) {
- rterror("rt_raster_gdal_rasterize: Could not create OGR Geometry from WKB");
-
- _rti_rasterize_arg_destroy(arg);
-
- if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
- /* OGRCleanupAll(); */
-
- return NULL;
- }
-
- /* OGR Geometry is empty */
- if (OGR_G_IsEmpty(src_geom)) {
- rtinfo("Geometry provided is empty. Returning empty raster");
-
- _rti_rasterize_arg_destroy(arg);
-
- OGR_G_DestroyGeometry(src_geom);
- if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
- /* OGRCleanupAll(); */
-
- return rt_raster_new(0, 0);
- }
-
- /* get envelope */
- OGR_G_GetEnvelope(src_geom, &src_env);
- rt_util_from_ogr_envelope(src_env, &extent);
-
- RASTER_DEBUGF(3, "Suggested raster envelope: %f, %f, %f, %f",
- extent.MinX, extent.MinY, extent.MaxX, extent.MaxY);
-
- /* user-defined scale */
- if (
- (NULL != scale_x) &&
- (NULL != scale_y) &&
- (FLT_NEQ(*scale_x, 0.0)) &&
- (FLT_NEQ(*scale_y, 0.0))
- ) {
- /* for now, force scale to be in left-right, top-down orientation */
- _scale[0] = fabs(*scale_x);
- _scale[1] = fabs(*scale_y);
- }
- /* user-defined width/height */
- else if (
- (NULL != width) &&
- (NULL != height) &&
- (FLT_NEQ(*width, 0.0)) &&
- (FLT_NEQ(*height, 0.0))
- ) {
- _dim[0] = fabs(*width);
- _dim[1] = fabs(*height);
-
- if (FLT_NEQ(extent.MaxX, extent.MinX))
- _scale[0] = fabs((extent.MaxX - extent.MinX) / _dim[0]);
- else
- _scale[0] = 1.;
-
- if (FLT_NEQ(extent.MaxY, extent.MinY))
- _scale[1] = fabs((extent.MaxY - extent.MinY) / _dim[1]);
- else
- _scale[1] = 1.;
- }
- else {
- rterror("rt_raster_gdal_rasterize: Values must be provided for width and height or X and Y of scale");
-
- _rti_rasterize_arg_destroy(arg);
-
- OGR_G_DestroyGeometry(src_geom);
- if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
- /* OGRCleanupAll(); */
-
- return NULL;
- }
- RASTER_DEBUGF(3, "scale (x, y) = %f, %f", _scale[0], -1 * _scale[1]);
- RASTER_DEBUGF(3, "dim (x, y) = %d, %d", _dim[0], _dim[1]);
-
- /* user-defined skew */
- if (NULL != skew_x) {
- _skew[0] = *skew_x;
-
- /*
- negative scale-x affects skew
- for now, force skew to be in left-right, top-down orientation
- */
- if (
- NULL != scale_x &&
- *scale_x < 0.
- ) {
- _skew[0] *= -1;
- }
- }
- if (NULL != skew_y) {
- _skew[1] = *skew_y;
-
- /*
- positive scale-y affects skew
- for now, force skew to be in left-right, top-down orientation
- */
- if (
- NULL != scale_y &&
- *scale_y > 0.
- ) {
- _skew[1] *= -1;
- }
- }
-
- /*
- if geometry is a point, a linestring or set of either and bounds not set,
- increase extent by a pixel to avoid missing points on border
-
- a whole pixel is used instead of half-pixel due to backward
- compatibility with GDAL 1.6, 1.7 and 1.8. 1.9+ works fine with half-pixel.
- */
- wkbtype = wkbFlatten(OGR_G_GetGeometryType(src_geom));
- if ((
- (wkbtype == wkbPoint) ||
- (wkbtype == wkbMultiPoint) ||
- (wkbtype == wkbLineString) ||
- (wkbtype == wkbMultiLineString)
- ) &&
- FLT_EQ(_dim[0], 0) &&
- FLT_EQ(_dim[1], 0)
- ) {
- int result;
- LWPOLY *epoly = NULL;
- LWGEOM *lwgeom = NULL;
- GEOSGeometry *egeom = NULL;
- GEOSGeometry *geom = NULL;
-
- RASTER_DEBUG(3, "Testing geometry is properly contained by extent");
-
- /*
- see if geometry is properly contained by extent
- all parts of geometry lies within extent
- */
-
- /* initialize GEOS */
- initGEOS(lwnotice, lwgeom_geos_error);
-
- /* convert envelope to geometry */
- RASTER_DEBUG(4, "Converting envelope to geometry");
- epoly = rt_util_envelope_to_lwpoly(extent);
- if (epoly == NULL) {
- rterror("rt_raster_gdal_rasterize: Could not create envelope's geometry to test if geometry is properly contained by extent");
-
- _rti_rasterize_arg_destroy(arg);
-
- OGR_G_DestroyGeometry(src_geom);
- if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
- /* OGRCleanupAll(); */
-
- return NULL;
- }
-
- egeom = (GEOSGeometry *) LWGEOM2GEOS(lwpoly_as_lwgeom(epoly));
- lwpoly_free(epoly);
-
- /* convert WKB to geometry */
- RASTER_DEBUG(4, "Converting WKB to geometry");
- lwgeom = lwgeom_from_wkb(wkb, wkb_len, LW_PARSER_CHECK_NONE);
- geom = (GEOSGeometry *) LWGEOM2GEOS(lwgeom);
- lwgeom_free(lwgeom);
-
- result = GEOSRelatePattern(egeom, geom, "T**FF*FF*");
- GEOSGeom_destroy(geom);
- GEOSGeom_destroy(egeom);
-
- if (result == 2) {
- rterror("rt_raster_gdal_rasterize: Could not test if geometry is properly contained by extent for geometry within extent");
-
- _rti_rasterize_arg_destroy(arg);
-
- OGR_G_DestroyGeometry(src_geom);
- if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
- /* OGRCleanupAll(); */
-
- return NULL;
- }
-
- /* geometry NOT properly contained by extent */
- if (!result) {
-
-#if POSTGIS_GDAL_VERSION > 18
-
- /* check alignment flag: grid_xw */
- if (
- (NULL == ul_xw && NULL == ul_yw) &&
- (NULL != grid_xw && NULL != grid_xw) &&
- FLT_NEQ(*grid_xw, extent.MinX)
- ) {
- // do nothing
- RASTER_DEBUG(3, "Skipping extent adjustment on X-axis due to upcoming alignment");
- }
- else {
- RASTER_DEBUG(3, "Adjusting extent for GDAL > 1.8 by half the scale on X-axis");
- extent.MinX -= (_scale[0] / 2.);
- extent.MaxX += (_scale[0] / 2.);
- }
-
- /* check alignment flag: grid_yw */
- if (
- (NULL == ul_xw && NULL == ul_yw) &&
- (NULL != grid_xw && NULL != grid_xw) &&
- FLT_NEQ(*grid_yw, extent.MaxY)
- ) {
- // do nothing
- RASTER_DEBUG(3, "Skipping extent adjustment on Y-axis due to upcoming alignment");
- }
- else {
- RASTER_DEBUG(3, "Adjusting extent for GDAL > 1.8 by half the scale on Y-axis");
- extent.MinY -= (_scale[1] / 2.);
- extent.MaxY += (_scale[1] / 2.);
- }
-
-#else
-
- /* check alignment flag: grid_xw */
- if (
- (NULL == ul_xw && NULL == ul_yw) &&
- (NULL != grid_xw && NULL != grid_xw) &&
- FLT_NEQ(*grid_xw, extent.MinX)
- ) {
- // do nothing
- RASTER_DEBUG(3, "Skipping extent adjustment on X-axis due to upcoming alignment");
- }
- else {
- RASTER_DEBUG(3, "Adjusting extent for GDAL <= 1.8 by the scale on X-axis");
- extent.MinX -= _scale[0];
- extent.MaxX += _scale[0];
- }
-
-
- /* check alignment flag: grid_yw */
- if (
- (NULL == ul_xw && NULL == ul_yw) &&
- (NULL != grid_xw && NULL != grid_xw) &&
- FLT_NEQ(*grid_yw, extent.MaxY)
- ) {
- // do nothing
- RASTER_DEBUG(3, "Skipping extent adjustment on Y-axis due to upcoming alignment");
- }
- else {
- RASTER_DEBUG(3, "Adjusting extent for GDAL <= 1.8 by the scale on Y-axis");
- extent.MinY -= _scale[1];
- extent.MaxY += _scale[1];
- }
-
-#endif
-
- }
-
- RASTER_DEBUGF(3, "Adjusted extent: %f, %f, %f, %f",
- extent.MinX, extent.MinY, extent.MaxX, extent.MaxY);
-
- extent.UpperLeftX = extent.MinX;
- extent.UpperLeftY = extent.MaxY;
- }
-
- /* reprocess extent if skewed */
- if (
- FLT_NEQ(_skew[0], 0) ||
- FLT_NEQ(_skew[1], 0)
- ) {
- rt_raster skewedrast;
-
- RASTER_DEBUG(3, "Computing skewed extent's envelope");
-
- skewedrast = rt_raster_compute_skewed_raster(
- extent,
- _skew,
- _scale,
- 0.01
- );
- if (skewedrast == NULL) {
- rterror("rt_raster_gdal_rasterize: Could not compute skewed raster");
-
- _rti_rasterize_arg_destroy(arg);
-
- OGR_G_DestroyGeometry(src_geom);
- if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
- /* OGRCleanupAll(); */
-
- return NULL;
- }
-
- _dim[0] = skewedrast->width;
- _dim[1] = skewedrast->height;
-
- extent.UpperLeftX = skewedrast->ipX;
- extent.UpperLeftY = skewedrast->ipY;
-
- rt_raster_destroy(skewedrast);
- }
-
- /* raster dimensions */
- if (!_dim[0])
- _dim[0] = (int) fmax((fabs(extent.MaxX - extent.MinX) + (_scale[0] / 2.)) / _scale[0], 1);
- if (!_dim[1])
- _dim[1] = (int) fmax((fabs(extent.MaxY - extent.MinY) + (_scale[1] / 2.)) / _scale[1], 1);
-
- /* temporary raster */
- rast = rt_raster_new(_dim[0], _dim[1]);
- if (rast == NULL) {
- rterror("rt_raster_gdal_rasterize: Out of memory allocating temporary raster");
-
- _rti_rasterize_arg_destroy(arg);
-
- OGR_G_DestroyGeometry(src_geom);
- if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
- /* OGRCleanupAll(); */
-
- return NULL;
- }
-
- /* set raster's spatial attributes */
- rt_raster_set_offsets(rast, extent.UpperLeftX, extent.UpperLeftY);
- rt_raster_set_scale(rast, _scale[0], -1 * _scale[1]);
- rt_raster_set_skews(rast, _skew[0], _skew[1]);
-
- rt_raster_get_geotransform_matrix(rast, _gt);
- RASTER_DEBUGF(3, "Temp raster's geotransform: %f, %f, %f, %f, %f, %f",
- _gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]);
- RASTER_DEBUGF(3, "Temp raster's dimensions (width x height): %d x %d",
- _dim[0], _dim[1]);
-
- /* user-specified upper-left corner */
- if (
- NULL != ul_xw &&
- NULL != ul_yw
- ) {
- ul_user = 1;
-
- RASTER_DEBUGF(4, "Using user-specified upper-left corner: %f, %f", *ul_xw, *ul_yw);
-
- /* set upper-left corner */
- rt_raster_set_offsets(rast, *ul_xw, *ul_yw);
- extent.UpperLeftX = *ul_xw;
- extent.UpperLeftY = *ul_yw;
- }
- else if (
- ((NULL != ul_xw) && (NULL == ul_yw)) ||
- ((NULL == ul_xw) && (NULL != ul_yw))
- ) {
- rterror("rt_raster_gdal_rasterize: Both X and Y upper-left corner values must be provided");
-
- rt_raster_destroy(rast);
- _rti_rasterize_arg_destroy(arg);
-
- OGR_G_DestroyGeometry(src_geom);
- if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
- /* OGRCleanupAll(); */
-
- return NULL;
- }
-
- /* alignment only considered if upper-left corner not provided */
- if (
- !ul_user && (
- (NULL != grid_xw) || (NULL != grid_yw)
- )
- ) {
-
- if (
- ((NULL != grid_xw) && (NULL == grid_yw)) ||
- ((NULL == grid_xw) && (NULL != grid_yw))
- ) {
- rterror("rt_raster_gdal_rasterize: Both X and Y alignment values must be provided");
-
- rt_raster_destroy(rast);
- _rti_rasterize_arg_destroy(arg);
-
- OGR_G_DestroyGeometry(src_geom);
- if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
- /* OGRCleanupAll(); */
-
- return NULL;
- }
-
- RASTER_DEBUGF(4, "Aligning extent to user-specified grid: %f, %f", *grid_xw, *grid_yw);
-
- do {
- double _r[2] = {0};
- double _w[2] = {0};
-
- /* raster is already aligned */
- if (FLT_EQ(*grid_xw, extent.UpperLeftX) && FLT_EQ(*grid_yw, extent.UpperLeftY)) {
- RASTER_DEBUG(3, "Skipping raster alignment as it is already aligned to grid");
- break;
- }
-
- extent.UpperLeftX = rast->ipX;
- extent.UpperLeftY = rast->ipY;
- rt_raster_set_offsets(rast, *grid_xw, *grid_yw);
-
- /* process upper-left corner */
- if (rt_raster_geopoint_to_cell(
- rast,
- extent.UpperLeftX, extent.UpperLeftY,
- &(_r[0]), &(_r[1]),
- NULL
- ) != ES_NONE) {
- rterror("rt_raster_gdal_rasterize: Could not compute raster pixel for spatial coordinates");
-
- rt_raster_destroy(rast);
- _rti_rasterize_arg_destroy(arg);
-
- OGR_G_DestroyGeometry(src_geom);
- if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
- /* OGRCleanupAll(); */
-
- return NULL;
- }
-
- if (rt_raster_cell_to_geopoint(
- rast,
- _r[0], _r[1],
- &(_w[0]), &(_w[1]),
- NULL
- ) != ES_NONE) {
- rterror("rt_raster_gdal_rasterize: Could not compute spatial coordinates for raster pixel");
-
- rt_raster_destroy(rast);
- _rti_rasterize_arg_destroy(arg);
-
- OGR_G_DestroyGeometry(src_geom);
- if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
- /* OGRCleanupAll(); */
-
- return NULL;
- }
-
- /* shift occurred */
- if (FLT_NEQ(_w[0], extent.UpperLeftX)) {
- if (NULL == width)
- rast->width++;
- else if (NULL == scale_x) {
- double _c[2] = {0};
-
- rt_raster_set_offsets(rast, extent.UpperLeftX, extent.UpperLeftY);
-
- /* get upper-right corner */
- if (rt_raster_cell_to_geopoint(
- rast,
- rast->width, 0,
- &(_c[0]), &(_c[1]),
- NULL
- ) != ES_NONE) {
- rterror("rt_raster_gdal_rasterize: Could not compute spatial coordinates for raster pixel");
-
- rt_raster_destroy(rast);
- _rti_rasterize_arg_destroy(arg);
-
- OGR_G_DestroyGeometry(src_geom);
- if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
- /* OGRCleanupAll(); */
-
- return NULL;
- }
-
- rast->scaleX = fabs((_c[0] - _w[0]) / ((double) rast->width));
- }
- }
- if (FLT_NEQ(_w[1], extent.UpperLeftY)) {
- if (NULL == height)
- rast->height++;
- else if (NULL == scale_y) {
- double _c[2] = {0};
-
- rt_raster_set_offsets(rast, extent.UpperLeftX, extent.UpperLeftY);
-
- /* get upper-right corner */
- if (rt_raster_cell_to_geopoint(
- rast,
- 0, rast->height,
- &(_c[0]), &(_c[1]),
- NULL
- ) != ES_NONE) {
- rterror("rt_raster_gdal_rasterize: Could not compute spatial coordinates for raster pixel");
-
- rt_raster_destroy(rast);
- _rti_rasterize_arg_destroy(arg);
-
- OGR_G_DestroyGeometry(src_geom);
- if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
- /* OGRCleanupAll(); */
-
- return NULL;
- }
-
- rast->scaleY = -1 * fabs((_c[1] - _w[1]) / ((double) rast->height));
- }
- }
-
- rt_raster_set_offsets(rast, _w[0], _w[1]);
- }
- while (0);
- }
-
- /*
- after this point, rt_envelope extent is no longer used
- */
-
- /* get key attributes from rast */
- _dim[0] = rast->width;
- _dim[1] = rast->height;
- rt_raster_get_geotransform_matrix(rast, _gt);
-
- /* scale-x is negative or scale-y is positive */
- if ((
- (NULL != scale_x) && (*scale_x < 0.)
- ) || (
- (NULL != scale_y) && (*scale_y > 0)
- )) {
- double _w[2] = {0};
-
- /* negative scale-x */
- if (
- (NULL != scale_x) &&
- (*scale_x < 0.)
- ) {
- RASTER_DEBUG(3, "Processing negative scale-x");
-
- if (rt_raster_cell_to_geopoint(
- rast,
- _dim[0], 0,
- &(_w[0]), &(_w[1]),
- NULL
- ) != ES_NONE) {
- rterror("rt_raster_gdal_rasterize: Could not compute spatial coordinates for raster pixel");
-
- rt_raster_destroy(rast);
- _rti_rasterize_arg_destroy(arg);
-
- OGR_G_DestroyGeometry(src_geom);
- if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
- /* OGRCleanupAll(); */
-
- return NULL;
- }
-
- _gt[0] = _w[0];
- _gt[1] = *scale_x;
-
- /* check for skew */
- if (NULL != skew_x && FLT_NEQ(*skew_x, 0))
- _gt[2] = *skew_x;
- }
- /* positive scale-y */
- if (
- (NULL != scale_y) &&
- (*scale_y > 0)
- ) {
- RASTER_DEBUG(3, "Processing positive scale-y");
-
- if (rt_raster_cell_to_geopoint(
- rast,
- 0, _dim[1],
- &(_w[0]), &(_w[1]),
- NULL
- ) != ES_NONE) {
- rterror("rt_raster_gdal_rasterize: Could not compute spatial coordinates for raster pixel");
-
- rt_raster_destroy(rast);
- _rti_rasterize_arg_destroy(arg);
-
- OGR_G_DestroyGeometry(src_geom);
- if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
- /* OGRCleanupAll(); */
-
- return NULL;
- }
-
- _gt[3] = _w[1];
- _gt[5] = *scale_y;
-
- /* check for skew */
- if (NULL != skew_y && FLT_NEQ(*skew_y, 0))
- _gt[4] = *skew_y;
- }
- }
-
- rt_raster_destroy(rast);
- rast = NULL;
-
- RASTER_DEBUGF(3, "Applied geotransform: %f, %f, %f, %f, %f, %f",
- _gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]);
- RASTER_DEBUGF(3, "Raster dimensions (width x height): %d x %d",
- _dim[0], _dim[1]);
-
- /* load GDAL mem */
- if (!rt_util_gdal_driver_registered("MEM"))
- GDALRegister_MEM();
- _drv = GDALGetDriverByName("MEM");
- if (NULL == _drv) {
- rterror("rt_raster_gdal_rasterize: Could not load the MEM GDAL driver");
-
- _rti_rasterize_arg_destroy(arg);
-
- OGR_G_DestroyGeometry(src_geom);
- if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
- /* OGRCleanupAll(); */
-
- return NULL;
- }
-
- _ds = GDALCreate(_drv, "", _dim[0], _dim[1], 0, GDT_Byte, NULL);
- if (NULL == _ds) {
- rterror("rt_raster_gdal_rasterize: Could not create a GDALDataset to rasterize the geometry into");
-
- _rti_rasterize_arg_destroy(arg);
-
- OGR_G_DestroyGeometry(src_geom);
- if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
- /* OGRCleanupAll(); */
-
- return NULL;
- }
-
- /* set geotransform */
- cplerr = GDALSetGeoTransform(_ds, _gt);
- if (cplerr != CE_None) {
- rterror("rt_raster_gdal_rasterize: Could not set geotransform on GDALDataset");
-
- _rti_rasterize_arg_destroy(arg);
-
- OGR_G_DestroyGeometry(src_geom);
- if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
- /* OGRCleanupAll(); */
-
- GDALClose(_ds);
-
- return NULL;
- }
-
- /* set SRS */
- if (NULL != src_sr) {
- char *_srs = NULL;
- OSRExportToWkt(src_sr, &_srs);
-
- cplerr = GDALSetProjection(_ds, _srs);
- CPLFree(_srs);
- if (cplerr != CE_None) {
- rterror("rt_raster_gdal_rasterize: Could not set projection on GDALDataset");
-
- _rti_rasterize_arg_destroy(arg);
-
- OGR_G_DestroyGeometry(src_geom);
- OSRDestroySpatialReference(src_sr);
- /* OGRCleanupAll(); */
-
- GDALClose(_ds);
-
- return NULL;
- }
- }
-
- /* set bands */
- for (i = 0; i < arg->numbands; i++) {
- err = 0;
-
- do {
- /* add band */
- cplerr = GDALAddBand(_ds, rt_util_pixtype_to_gdal_datatype(arg->pixtype[i]), NULL);
- if (cplerr != CE_None) {
- rterror("rt_raster_gdal_rasterize: Could not add band to GDALDataset");
- err = 1;
- break;
- }
-
- _band = GDALGetRasterBand(_ds, i + 1);
- if (NULL == _band) {
- rterror("rt_raster_gdal_rasterize: Could not get band %d from GDALDataset", i + 1);
- err = 1;
- break;
- }
-
- /* nodata value */
- if (arg->hasnodata[i]) {
- RASTER_DEBUGF(4, "Setting NODATA value of band %d to %f", i, arg->nodata[i]);
- cplerr = GDALSetRasterNoDataValue(_band, arg->nodata[i]);
- if (cplerr != CE_None) {
- rterror("rt_raster_gdal_rasterize: Could not set nodata value");
- err = 1;
- break;
- }
- RASTER_DEBUGF(4, "NODATA value set to %f", GDALGetRasterNoDataValue(_band, NULL));
- }
-
- /* initial value */
- cplerr = GDALFillRaster(_band, arg->init[i], 0);
- if (cplerr != CE_None) {
- rterror("rt_raster_gdal_rasterize: Could not set initial value");
- err = 1;
- break;
- }
- }
- while (0);
-
- if (err) {
- _rti_rasterize_arg_destroy(arg);
-
- OGR_G_DestroyGeometry(src_geom);
- if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
- /* OGRCleanupAll(); */
-
- GDALClose(_ds);
-
- return NULL;
- }
- }
-
- arg->bandlist = (int *) rtalloc(sizeof(int) * arg->numbands);
- for (i = 0; i < arg->numbands; i++) arg->bandlist[i] = i + 1;
-
-
- /* burn geometry */
- cplerr = GDALRasterizeGeometries(
- _ds,
- arg->numbands, arg->bandlist,
- 1, &src_geom,
- NULL, NULL,
- arg->value,
- options,
- NULL, NULL
- );
- if (cplerr != CE_None) {
- rterror("rt_raster_gdal_rasterize: Could not rasterize geometry");
-
- _rti_rasterize_arg_destroy(arg);
-
- OGR_G_DestroyGeometry(src_geom);
- if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
- /* OGRCleanupAll(); */
-
- GDALClose(_ds);
-
- return NULL;
- }
-
- /* convert gdal dataset to raster */
- GDALFlushCache(_ds);
- RASTER_DEBUG(3, "Converting GDAL dataset to raster");
- rast = rt_raster_from_gdal_dataset(_ds);
-
- OGR_G_DestroyGeometry(src_geom);
- if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
- /* OGRCleanupAll(); */
-
- GDALClose(_ds);
-
- if (NULL == rast) {
- rterror("rt_raster_gdal_rasterize: Could not rasterize geometry");
- return NULL;
- }
-
- /* width, height */
- _width = rt_raster_get_width(rast);
- _height = rt_raster_get_height(rast);
-
- /* check each band for pixtype */
- for (i = 0; i < arg->numbands; i++) {
- uint8_t *data = NULL;
- rt_band band = NULL;
- rt_band oldband = NULL;
-
- double val = 0;
- int nodata = 0;
- int hasnodata = 0;
- double nodataval = 0;
- int x = 0;
- int y = 0;
-
- oldband = rt_raster_get_band(rast, i);
- if (oldband == NULL) {
- rterror("rt_raster_gdal_rasterize: Could not get band %d of output raster", i);
- _rti_rasterize_arg_destroy(arg);
- rt_raster_destroy(rast);
- return NULL;
- }
-
- /* band is of user-specified type */
- if (rt_band_get_pixtype(oldband) == arg->pixtype[i])
- continue;
-
- /* hasnodata, nodataval */
- hasnodata = rt_band_get_hasnodata_flag(oldband);
- if (hasnodata)
- rt_band_get_nodata(oldband, &nodataval);
-
- /* allocate data */
- data = rtalloc(rt_pixtype_size(arg->pixtype[i]) * _width * _height);
- if (data == NULL) {
- rterror("rt_raster_gdal_rasterize: Could not allocate memory for band data");
- _rti_rasterize_arg_destroy(arg);
- rt_raster_destroy(rast);
- return NULL;
- }
- memset(data, 0, rt_pixtype_size(arg->pixtype[i]) * _width * _height);
-
- /* create new band of correct type */
- band = rt_band_new_inline(
- _width, _height,
- arg->pixtype[i],
- hasnodata, nodataval,
- data
- );
- if (band == NULL) {
- rterror("rt_raster_gdal_rasterize: Could not create band");
- rtdealloc(data);
- _rti_rasterize_arg_destroy(arg);
- rt_raster_destroy(rast);
- return NULL;
- }
-
- /* give ownership of data to band */
- rt_band_set_ownsdata_flag(band, 1);
-
- /* copy pixel by pixel */
- for (x = 0; x < _width; x++) {
- for (y = 0; y < _height; y++) {
- err = rt_band_get_pixel(oldband, x, y, &val, &nodata);
- if (err != ES_NONE) {
- rterror("rt_raster_gdal_rasterize: Could not get pixel value");
- _rti_rasterize_arg_destroy(arg);
- rt_raster_destroy(rast);
- rt_band_destroy(band);
- return NULL;
- }
-
- if (nodata)
- val = nodataval;
-
- err = rt_band_set_pixel(band, x, y, val, NULL);
- if (err != ES_NONE) {
- rterror("rt_raster_gdal_rasterize: Could not set pixel value");
- _rti_rasterize_arg_destroy(arg);
- rt_raster_destroy(rast);
- rt_band_destroy(band);
- return NULL;
- }
- }
- }
-
- /* replace band */
- oldband = rt_raster_replace_band(rast, band, i);
- if (oldband == NULL) {
- rterror("rt_raster_gdal_rasterize: Could not replace band %d of output raster", i);
- _rti_rasterize_arg_destroy(arg);
- rt_raster_destroy(rast);
- rt_band_destroy(band);
- return NULL;
- }
-
- /* free oldband */
- rt_band_destroy(oldband);
- }
-
- _rti_rasterize_arg_destroy(arg);
-
- RASTER_DEBUG(3, "done");
-
- return rast;
-}
-
-/******************************************************************************
-* rt_raster_intersects()
-******************************************************************************/
-
-static
-int rt_raster_intersects_algorithm(
- rt_raster rast1, rt_raster rast2,
- rt_band band1, rt_band band2,
- int hasnodata1, int hasnodata2,
- double nodata1, double nodata2
-) {
- int i;
- int byHeight = 1;
- uint32_t dimValue;
-
- uint32_t row;
- uint32_t rowoffset;
- uint32_t col;
- uint32_t coloffset;
-
- enum line_points {X1, Y1, X2, Y2};
- enum point {pX, pY};
- double line1[4] = {0.};
- double line2[4] = {0.};
- double P[2] = {0.};
- double Qw[2] = {0.};
- double Qr[2] = {0.};
- double gt1[6] = {0.};
- double gt2[6] = {0.};
- double igt1[6] = {0};
- double igt2[6] = {0};
- double d;
- double val1;
- int noval1;
- int isnodata1;
- double val2;
- int noval2;
- int isnodata2;
- uint32_t adjacent[8] = {0};
-
- double xscale;
- double yscale;
-
- uint16_t width1;
- uint16_t height1;
- uint16_t width2;
- uint16_t height2;
-
- width1 = rt_raster_get_width(rast1);
- height1 = rt_raster_get_height(rast1);
- width2 = rt_raster_get_width(rast2);
- height2 = rt_raster_get_height(rast2);
-
- /* sampling scale */
- xscale = fmin(rt_raster_get_x_scale(rast1), rt_raster_get_x_scale(rast2)) / 10.;
- yscale = fmin(rt_raster_get_y_scale(rast1), rt_raster_get_y_scale(rast2)) / 10.;
-
- /* see if skew made rast2's rows are parallel to rast1's cols */
- rt_raster_cell_to_geopoint(
- rast1,
- 0, 0,
- &(line1[X1]), &(line1[Y1]),
- gt1
- );
-
- rt_raster_cell_to_geopoint(
- rast1,
- 0, height1,
- &(line1[X2]), &(line1[Y2]),
- gt1
- );
-
- rt_raster_cell_to_geopoint(
- rast2,
- 0, 0,
- &(line2[X1]), &(line2[Y1]),
- gt2
- );
-
- rt_raster_cell_to_geopoint(
- rast2,
- width2, 0,
- &(line2[X2]), &(line2[Y2]),
- gt2
- );
-
- /* parallel vertically */
- if (FLT_EQ(line1[X2] - line1[X1], 0.) && FLT_EQ(line2[X2] - line2[X1], 0.))
- byHeight = 0;
- /* parallel */
- else if (FLT_EQ(((line1[Y2] - line1[Y1]) / (line1[X2] - line1[X1])), ((line2[Y2] - line2[Y1]) / (line2[X2] - line2[X1]))))
- byHeight = 0;
-
- if (byHeight)
- dimValue = height2;
- else
- dimValue = width2;
- RASTER_DEBUGF(4, "byHeight: %d, dimValue: %d", byHeight, dimValue);
-
- /* 3 x 3 search */
- for (coloffset = 0; coloffset < 3; coloffset++) {
- for (rowoffset = 0; rowoffset < 3; rowoffset++) {
- /* smaller raster */
- for (col = coloffset; col <= width1; col += 3) {
-
- rt_raster_cell_to_geopoint(
- rast1,
- col, 0,
- &(line1[X1]), &(line1[Y1]),
- gt1
- );
-
- rt_raster_cell_to_geopoint(
- rast1,
- col, height1,
- &(line1[X2]), &(line1[Y2]),
- gt1
- );
-
- /* larger raster */
- for (row = rowoffset; row <= dimValue; row += 3) {
-
- if (byHeight) {
- rt_raster_cell_to_geopoint(
- rast2,
- 0, row,
- &(line2[X1]), &(line2[Y1]),
- gt2
- );
-
- rt_raster_cell_to_geopoint(
- rast2,
- width2, row,
- &(line2[X2]), &(line2[Y2]),
- gt2
- );
- }
- else {
- rt_raster_cell_to_geopoint(
- rast2,
- row, 0,
- &(line2[X1]), &(line2[Y1]),
- gt2
- );
-
- rt_raster_cell_to_geopoint(
- rast2,
- row, height2,
- &(line2[X2]), &(line2[Y2]),
- gt2
- );
- }
-
- RASTER_DEBUGF(4, "(col, row) = (%d, %d)", col, row);
- RASTER_DEBUGF(4, "line1(x1, y1, x2, y2) = (%f, %f, %f, %f)",
- line1[X1], line1[Y1], line1[X2], line1[Y2]);
- RASTER_DEBUGF(4, "line2(x1, y1, x2, y2) = (%f, %f, %f, %f)",
- line2[X1], line2[Y1], line2[X2], line2[Y2]);
-
- /* intersection */
- /* http://en.wikipedia.org/wiki/Line-line_intersection */
- d = ((line1[X1] - line1[X2]) * (line2[Y1] - line2[Y2])) - ((line1[Y1] - line1[Y2]) * (line2[X1] - line2[X2]));
- /* no intersection */
- if (FLT_EQ(d, 0.)) {
- continue;
- }
-
- P[pX] = (((line1[X1] * line1[Y2]) - (line1[Y1] * line1[X2])) * (line2[X1] - line2[X2])) -
- ((line1[X1] - line1[X2]) * ((line2[X1] * line2[Y2]) - (line2[Y1] * line2[X2])));
- P[pX] = P[pX] / d;
-
- P[pY] = (((line1[X1] * line1[Y2]) - (line1[Y1] * line1[X2])) * (line2[Y1] - line2[Y2])) -
- ((line1[Y1] - line1[Y2]) * ((line2[X1] * line2[Y2]) - (line2[Y1] * line2[X2])));
- P[pY] = P[pY] / d;
-
- RASTER_DEBUGF(4, "P(x, y) = (%f, %f)", P[pX], P[pY]);
-
- /* intersection within bounds */
- if ((
- (FLT_EQ(P[pX], line1[X1]) || FLT_EQ(P[pX], line1[X2])) ||
- (P[pX] > fmin(line1[X1], line1[X2]) && (P[pX] < fmax(line1[X1], line1[X2])))
- ) && (
- (FLT_EQ(P[pY], line1[Y1]) || FLT_EQ(P[pY], line1[Y2])) ||
- (P[pY] > fmin(line1[Y1], line1[Y2]) && (P[pY] < fmax(line1[Y1], line1[Y2])))
- ) && (
- (FLT_EQ(P[pX], line2[X1]) || FLT_EQ(P[pX], line2[X2])) ||
- (P[pX] > fmin(line2[X1], line2[X2]) && (P[pX] < fmax(line2[X1], line2[X2])))
- ) && (
- (FLT_EQ(P[pY], line2[Y1]) || FLT_EQ(P[pY], line2[Y2])) ||
- (P[pY] > fmin(line2[Y1], line2[Y2]) && (P[pY] < fmax(line2[Y1], line2[Y2])))
- )) {
- RASTER_DEBUG(4, "within bounds");
-
- for (i = 0; i < 8; i++) adjacent[i] = 0;
-
- /* test points around intersection */
- for (i = 0; i < 8; i++) {
- switch (i) {
- case 7:
- Qw[pX] = P[pX] - xscale;
- Qw[pY] = P[pY] + yscale;
- break;
- /* 270 degrees = 09:00 */
- case 6:
- Qw[pX] = P[pX] - xscale;
- Qw[pY] = P[pY];
- break;
- case 5:
- Qw[pX] = P[pX] - xscale;
- Qw[pY] = P[pY] - yscale;
- break;
- /* 180 degrees = 06:00 */
- case 4:
- Qw[pX] = P[pX];
- Qw[pY] = P[pY] - yscale;
- break;
- case 3:
- Qw[pX] = P[pX] + xscale;
- Qw[pY] = P[pY] - yscale;
- break;
- /* 90 degrees = 03:00 */
- case 2:
- Qw[pX] = P[pX] + xscale;
- Qw[pY] = P[pY];
- break;
- /* 45 degrees */
- case 1:
- Qw[pX] = P[pX] + xscale;
- Qw[pY] = P[pY] + yscale;
- break;
- /* 0 degrees = 00:00 */
- case 0:
- Qw[pX] = P[pX];
- Qw[pY] = P[pY] + yscale;
- break;
- }
-
- /* unable to convert point to cell */
- noval1 = 0;
- if (rt_raster_geopoint_to_cell(
- rast1,
- Qw[pX], Qw[pY],
- &(Qr[pX]), &(Qr[pY]),
- igt1
- ) != ES_NONE) {
- noval1 = 1;
- }
- /* cell is outside bounds of grid */
- else if (
- (Qr[pX] < 0 || Qr[pX] > width1 || FLT_EQ(Qr[pX], width1)) ||
- (Qr[pY] < 0 || Qr[pY] > height1 || FLT_EQ(Qr[pY], height1))
- ) {
- noval1 = 1;
- }
- else if (hasnodata1 == FALSE)
- val1 = 1;
- /* unable to get value at cell */
- else if (rt_band_get_pixel(band1, Qr[pX], Qr[pY], &val1, &isnodata1) != ES_NONE)
- noval1 = 1;
-
- /* unable to convert point to cell */
- noval2 = 0;
- if (rt_raster_geopoint_to_cell(
- rast2,
- Qw[pX], Qw[pY],
- &(Qr[pX]), &(Qr[pY]),
- igt2
- ) != ES_NONE) {
- noval2 = 1;
- }
- /* cell is outside bounds of grid */
- else if (
- (Qr[pX] < 0 || Qr[pX] > width2 || FLT_EQ(Qr[pX], width2)) ||
- (Qr[pY] < 0 || Qr[pY] > height2 || FLT_EQ(Qr[pY], height2))
- ) {
- noval2 = 1;
- }
- else if (hasnodata2 == FALSE)
- val2 = 1;
- /* unable to get value at cell */
- else if (rt_band_get_pixel(band2, Qr[pX], Qr[pY], &val2, &isnodata2) != ES_NONE)
- noval2 = 1;
-
- if (!noval1) {
- RASTER_DEBUGF(4, "val1 = %f", val1);
- }
- if (!noval2) {
- RASTER_DEBUGF(4, "val2 = %f", val2);
- }
-
- /* pixels touch */
- if (!noval1 && (
- (hasnodata1 == FALSE) || !isnodata1
- )) {
- adjacent[i]++;
- }
- if (!noval2 && (
- (hasnodata2 == FALSE) || !isnodata2
- )) {
- adjacent[i] += 3;
- }
-
- /* two pixel values not present */
- if (noval1 || noval2) {
- RASTER_DEBUGF(4, "noval1 = %d, noval2 = %d", noval1, noval2);
- continue;
- }
-
- /* pixels valid, so intersect */
- if (
- ((hasnodata1 == FALSE) || !isnodata1) &&
- ((hasnodata2 == FALSE) || !isnodata2)
- ) {
- RASTER_DEBUG(3, "The two rasters do intersect");
-
- return 1;
- }
- }
-
- /* pixels touch */
- for (i = 0; i < 4; i++) {
- RASTER_DEBUGF(4, "adjacent[%d] = %d, adjacent[%d] = %d"
- , i, adjacent[i], i + 4, adjacent[i + 4]);
- if (adjacent[i] == 0) continue;
-
- if (adjacent[i] + adjacent[i + 4] == 4) {
- RASTER_DEBUG(3, "The two rasters touch");
-
- return 1;
- }
- }
- }
- else {
- RASTER_DEBUG(4, "outside of bounds");
- }
- }
- }
- }
- }
-
- return 0;
-}
-
-/**
- * Return zero if error occurred in function.
- * Parameter intersects returns non-zero if two rasters intersect
- *
- * @param rast1 : the first raster whose band will be tested
- * @param nband1 : the 0-based band of raster rast1 to use
- * if value is less than zero, bands are ignored.
- * if nband1 gte zero, nband2 must be gte zero
- * @param rast2 : the second raster whose band will be tested
- * @param nband2 : the 0-based band of raster rast2 to use
- * if value is less than zero, bands are ignored
- * if nband2 gte zero, nband1 must be gte zero
- * @param intersects : non-zero value if the two rasters' bands intersects
- *
- * @return ES_NONE if success, ES_ERROR if error
- */
-rt_errorstate
-rt_raster_intersects(
- rt_raster rast1, int nband1,
- rt_raster rast2, int nband2,
- int *intersects
-) {
- int i;
- int j;
- int within = 0;
-
- LWGEOM *hull[2] = {NULL};
- GEOSGeometry *ghull[2] = {NULL};
-
- uint16_t width1;
- uint16_t height1;
- uint16_t width2;
- uint16_t height2;
- double area1;
- double area2;
- double pixarea1;
- double pixarea2;
- rt_raster rastS = NULL;
- rt_raster rastL = NULL;
- uint16_t *widthS = NULL;
- uint16_t *heightS = NULL;
- uint16_t *widthL = NULL;
- uint16_t *heightL = NULL;
- int nbandS;
- int nbandL;
- rt_band bandS = NULL;
- rt_band bandL = NULL;
- int hasnodataS = FALSE;
- int hasnodataL = FALSE;
- double nodataS = 0;
- double nodataL = 0;
- int isnodataS = 0;
- int isnodataL = 0;
- double gtS[6] = {0};
- double igtL[6] = {0};
-
- uint32_t row;
- uint32_t rowoffset;
- uint32_t col;
- uint32_t coloffset;
-
- enum line_points {X1, Y1, X2, Y2};
- enum point {pX, pY};
- double lineS[4];
- double Qr[2];
- double valS;
- double valL;
-
- RASTER_DEBUG(3, "Starting");
-
- assert(NULL != rast1);
- assert(NULL != rast2);
- assert(NULL != intersects);
-
- if (nband1 < 0 && nband2 < 0) {
- nband1 = -1;
- nband2 = -1;
- }
- else {
- assert(nband1 >= 0 && nband1 < rt_raster_get_num_bands(rast1));
- assert(nband2 >= 0 && nband2 < rt_raster_get_num_bands(rast2));
- }
-
- /* same srid */
- if (rt_raster_get_srid(rast1) != rt_raster_get_srid(rast2)) {
- rterror("rt_raster_intersects: The two rasters provided have different SRIDs");
- *intersects = 0;
- return ES_ERROR;
- }
-
- /* raster extents need to intersect */
- do {
- int rtn;
-
- initGEOS(lwnotice, lwgeom_geos_error);
-
- rtn = 1;
- for (i = 0; i < 2; i++) {
- if ((rt_raster_get_convex_hull(i < 1 ? rast1 : rast2, &(hull[i])) != ES_NONE) || NULL == hull[i]) {
- for (j = 0; j < i; j++) {
- GEOSGeom_destroy(ghull[j]);
- lwgeom_free(hull[j]);
- }
- rtn = 0;
- break;
- }
- ghull[i] = (GEOSGeometry *) LWGEOM2GEOS(hull[i]);
- if (NULL == ghull[i]) {
- for (j = 0; j < i; j++) {
- GEOSGeom_destroy(ghull[j]);
- lwgeom_free(hull[j]);
- }
- lwgeom_free(hull[i]);
- rtn = 0;
- break;
- }
- }
- if (!rtn) break;
-
- /* test to see if raster within the other */
- within = 0;
- if (GEOSWithin(ghull[0], ghull[1]) == 1)
- within = -1;
- else if (GEOSWithin(ghull[1], ghull[0]) == 1)
- within = 1;
-
- if (within != 0)
- rtn = 1;
- else
- rtn = GEOSIntersects(ghull[0], ghull[1]);
-
- for (i = 0; i < 2; i++) {
- GEOSGeom_destroy(ghull[i]);
- lwgeom_free(hull[i]);
- }
-
- if (rtn != 2) {
- RASTER_DEBUGF(4, "convex hulls of rasters do %sintersect", rtn != 1 ? "NOT " : "");
- if (rtn != 1) {
- *intersects = 0;
- return ES_NONE;
- }
- /* band isn't specified */
- else if (nband1 < 0) {
- *intersects = 1;
- return ES_NONE;
- }
- }
- else {
- RASTER_DEBUG(4, "GEOSIntersects() returned a 2!!!!");
- }
- }
- while (0);
-
- /* smaller raster by area or width */
- width1 = rt_raster_get_width(rast1);
- height1 = rt_raster_get_height(rast1);
- width2 = rt_raster_get_width(rast2);
- height2 = rt_raster_get_height(rast2);
- pixarea1 = fabs(rt_raster_get_x_scale(rast1) * rt_raster_get_y_scale(rast1));
- pixarea2 = fabs(rt_raster_get_x_scale(rast2) * rt_raster_get_y_scale(rast2));
- area1 = fabs(width1 * height1 * pixarea1);
- area2 = fabs(width2 * height2 * pixarea2);
- RASTER_DEBUGF(4, "pixarea1, pixarea2, area1, area2 = %f, %f, %f, %f",
- pixarea1, pixarea2, area1, area2);
- if (
- (within <= 0) ||
- (area1 < area2) ||
- FLT_EQ(area1, area2) ||
- (area1 < pixarea2) || /* area of rast1 smaller than pixel area of rast2 */
- FLT_EQ(area1, pixarea2)
- ) {
- rastS = rast1;
- nbandS = nband1;
- widthS = &width1;
- heightS = &height1;
-
- rastL = rast2;
- nbandL = nband2;
- widthL = &width2;
- heightL = &height2;
- }
- else {
- rastS = rast2;
- nbandS = nband2;
- widthS = &width2;
- heightS = &height2;
-
- rastL = rast1;
- nbandL = nband1;
- widthL = &width1;
- heightL = &height1;
- }
-
- /* no band to use, set band to zero */
- if (nband1 < 0) {
- nbandS = 0;
- nbandL = 0;
- }
-
- RASTER_DEBUGF(4, "rast1 @ %p", rast1);
- RASTER_DEBUGF(4, "rast2 @ %p", rast2);
- RASTER_DEBUGF(4, "rastS @ %p", rastS);
- RASTER_DEBUGF(4, "rastL @ %p", rastL);
-
- /* load band of smaller raster */
- bandS = rt_raster_get_band(rastS, nbandS);
- if (NULL == bandS) {
- rterror("rt_raster_intersects: Could not get band %d of the first raster", nbandS);
- *intersects = 0;
- return ES_ERROR;
- }
-
- hasnodataS = rt_band_get_hasnodata_flag(bandS);
- if (hasnodataS != FALSE)
- rt_band_get_nodata(bandS, &nodataS);
-
- /* load band of larger raster */
- bandL = rt_raster_get_band(rastL, nbandL);
- if (NULL == bandL) {
- rterror("rt_raster_intersects: Could not get band %d of the first raster", nbandL);
- *intersects = 0;
- return ES_ERROR;
- }
-
- hasnodataL = rt_band_get_hasnodata_flag(bandL);
- if (hasnodataL != FALSE)
- rt_band_get_nodata(bandL, &nodataL);
-
- /* no band to use, ignore nodata */
- if (nband1 < 0) {
- hasnodataS = FALSE;
- hasnodataL = FALSE;
- }
-
- /* hasnodata(S|L) = TRUE and one of the two bands is isnodata */
- if (
- (hasnodataS && rt_band_get_isnodata_flag(bandS)) ||
- (hasnodataL && rt_band_get_isnodata_flag(bandL))
- ) {
- RASTER_DEBUG(3, "One of the two raster bands is NODATA. The two rasters do not intersect");
- *intersects = 0;
- return ES_NONE;
- }
-
- /* special case where a raster can fit inside another raster's pixel */
- if (within != 0 && ((pixarea1 > area2) || (pixarea2 > area1))) {
- RASTER_DEBUG(4, "Using special case of raster fitting into another raster's pixel");
- /* 3 x 3 search */
- for (coloffset = 0; coloffset < 3; coloffset++) {
- for (rowoffset = 0; rowoffset < 3; rowoffset++) {
- for (col = coloffset; col < *widthS; col += 3) {
- for (row = rowoffset; row < *heightS; row += 3) {
- if (hasnodataS == FALSE)
- valS = 1;
- else if (rt_band_get_pixel(bandS, col, row, &valS, &isnodataS) != ES_NONE)
- continue;
-
- if ((hasnodataS == FALSE) || !isnodataS) {
- rt_raster_cell_to_geopoint(
- rastS,
- col, row,
- &(lineS[X1]), &(lineS[Y1]),
- gtS
- );
-
- if (rt_raster_geopoint_to_cell(
- rastL,
- lineS[X1], lineS[Y1],
- &(Qr[pX]), &(Qr[pY]),
- igtL
- ) != ES_NONE) {
- continue;
- }
-
- if (
- (Qr[pX] < 0 || Qr[pX] > *widthL || FLT_EQ(Qr[pX], *widthL)) ||
- (Qr[pY] < 0 || Qr[pY] > *heightL || FLT_EQ(Qr[pY], *heightL))
- ) {
- continue;
- }
-
- if (hasnodataS == FALSE)
- valL = 1;
- else if (rt_band_get_pixel(bandL, Qr[pX], Qr[pY], &valL, &isnodataL) != ES_NONE)
- continue;
-
- if ((hasnodataL == FALSE) || !isnodataL) {
- RASTER_DEBUG(3, "The two rasters do intersect");
- *intersects = 1;
- return ES_NONE;
- }
- }
- }
- }
- }
- }
- RASTER_DEBUG(4, "Smaller raster not in the other raster's pixel. Continuing");
- }
-
- RASTER_DEBUG(4, "Testing smaller raster vs larger raster");
- *intersects = rt_raster_intersects_algorithm(
- rastS, rastL,
- bandS, bandL,
- hasnodataS, hasnodataL,
- nodataS, nodataL
- );
-
- if (*intersects) return ES_NONE;
-
- RASTER_DEBUG(4, "Testing larger raster vs smaller raster");
- *intersects = rt_raster_intersects_algorithm(
- rastL, rastS,
- bandL, bandS,
- hasnodataL, hasnodataS,
- nodataL, nodataS
- );
-
- if (*intersects) return ES_NONE;
-
- RASTER_DEBUG(3, "The two rasters do not intersect");
-
- *intersects = 0;
- return ES_NONE;
-}
-
-/******************************************************************************
-* GEOS-based spatial relationship tests
-******************************************************************************/
-
-static
-rt_errorstate rt_raster_geos_spatial_relationship(
- rt_raster rast1, int nband1,
- rt_raster rast2, int nband2,
- rt_geos_spatial_test testtype,
- int *testresult
-) {
- LWMPOLY *surface1 = NULL;
- LWMPOLY *surface2 = NULL;
- GEOSGeometry *geom1 = NULL;
- GEOSGeometry *geom2 = NULL;
- int rtn = 0;
- int flag = 0;
-
- RASTER_DEBUG(3, "Starting");
-
- assert(NULL != rast1);
- assert(NULL != rast2);
- assert(NULL != testresult);
-
- if (nband1 < 0 && nband2 < 0) {
- nband1 = -1;
- nband2 = -1;
- }
- else {
- assert(nband1 >= 0 && nband1 < rt_raster_get_num_bands(rast1));
- assert(nband2 >= 0 && nband2 < rt_raster_get_num_bands(rast2));
- }
-
- /* initialize to zero, false result of spatial relationship test */
- *testresult = 0;
-
- /* same srid */
- if (rt_raster_get_srid(rast1) != rt_raster_get_srid(rast2)) {
- rterror("rt_raster_geos_spatial_relationship: The two rasters provided have different SRIDs");
- return ES_ERROR;
- }
-
- initGEOS(lwnotice, lwgeom_geos_error);
-
- /* get LWMPOLY of each band */
- if (rt_raster_surface(rast1, nband1, &surface1) != ES_NONE) {
- rterror("rt_raster_geos_spatial_relationship: Could not get surface of the specified band from the first raster");
- return ES_ERROR;
- }
- if (rt_raster_surface(rast2, nband2, &surface2) != ES_NONE) {
- rterror("rt_raster_geos_spatial_relationship: Could not get surface of the specified band from the second raster");
- lwmpoly_free(surface1);
- return ES_ERROR;
- }
-
- /* either surface is NULL, spatial relationship test is false */
- if (surface1 == NULL || surface2 == NULL) {
- if (surface1 != NULL) lwmpoly_free(surface1);
- if (surface2 != NULL) lwmpoly_free(surface2);
- return ES_NONE;
- }
-
- /* convert LWMPOLY to GEOSGeometry */
- geom1 = LWGEOM2GEOS(lwmpoly_as_lwgeom(surface1));
- lwmpoly_free(surface1);
- if (geom1 == NULL) {
- rterror("rt_raster_geos_spatial_relationship: Could not convert surface of the specified band from the first raster to a GEOSGeometry");
- lwmpoly_free(surface2);
- return ES_ERROR;
- }
-
- geom2 = LWGEOM2GEOS(lwmpoly_as_lwgeom(surface2));
- lwmpoly_free(surface2);
- if (geom2 == NULL) {
- rterror("rt_raster_geos_spatial_relationship: Could not convert surface of the specified band from the second raster to a GEOSGeometry");
- return ES_ERROR;
- }
-
- flag = 0;
- switch (testtype) {
- case GSR_OVERLAPS:
- rtn = GEOSOverlaps(geom1, geom2);
- break;
- case GSR_TOUCHES:
- rtn = GEOSTouches(geom1, geom2);
- break;
- case GSR_CONTAINS:
- rtn = GEOSContains(geom1, geom2);
- break;
- case GSR_CONTAINSPROPERLY:
- rtn = GEOSRelatePattern(geom1, geom2, "T**FF*FF*");
- break;
- case GSR_COVERS:
- rtn = GEOSRelatePattern(geom1, geom2, "******FF*");
- break;
- case GSR_COVEREDBY:
- rtn = GEOSRelatePattern(geom1, geom2, "**F**F***");
- break;
- default:
- rterror("rt_raster_geos_spatial_relationship: Unknown or unsupported GEOS spatial relationship test");
- flag = -1;
- break;
- }
- GEOSGeom_destroy(geom1);
- GEOSGeom_destroy(geom2);
-
- /* something happened in the spatial relationship test */
- if (rtn == 2) {
- rterror("rt_raster_geos_spatial_relationship: Could not run the appropriate GEOS spatial relationship test");
- flag = ES_ERROR;
- }
- /* spatial relationship test ran fine */
- else if (flag >= 0) {
- if (rtn != 0)
- *testresult = 1;
- flag = ES_NONE;
- }
- /* flag < 0 for when testtype is unknown */
- else
- flag = ES_ERROR;
-
- return flag;
-}
-
-/**
- * Return ES_ERROR if error occurred in function.
- * Parameter overlaps returns non-zero if two rasters overlap
- *
- * @param rast1 : the first raster whose band will be tested
- * @param nband1 : the 0-based band of raster rast1 to use
- * if value is less than zero, bands are ignored.
- * if nband1 gte zero, nband2 must be gte zero
- * @param rast2 : the second raster whose band will be tested
- * @param nband2 : the 0-based band of raster rast2 to use
- * if value is less than zero, bands are ignored
- * if nband2 gte zero, nband1 must be gte zero
- * @param overlaps : non-zero value if the two rasters' bands overlaps
- *
- * @return ES_NONE if success, ES_ERROR if error
- */
-rt_errorstate rt_raster_overlaps(
- rt_raster rast1, int nband1,
- rt_raster rast2, int nband2,
- int *overlaps
-) {
- RASTER_DEBUG(3, "Starting");
-
- return rt_raster_geos_spatial_relationship(
- rast1, nband1,
- rast2, nband2,
- GSR_OVERLAPS,
- overlaps
- );
-}
-
-/**
- * Return ES_ERROR if error occurred in function.
- * Parameter touches returns non-zero if two rasters touch
- *
- * @param rast1 : the first raster whose band will be tested
- * @param nband1 : the 0-based band of raster rast1 to use
- * if value is less than zero, bands are ignored.
- * if nband1 gte zero, nband2 must be gte zero
- * @param rast2 : the second raster whose band will be tested
- * @param nband2 : the 0-based band of raster rast2 to use
- * if value is less than zero, bands are ignored
- * if nband2 gte zero, nband1 must be gte zero
- * @param touches : non-zero value if the two rasters' bands touch
- *
- * @return ES_NONE if success, ES_ERROR if error
- */
-rt_errorstate rt_raster_touches(
- rt_raster rast1, int nband1,
- rt_raster rast2, int nband2,
- int *touches
-) {
- RASTER_DEBUG(3, "Starting");
-
- return rt_raster_geos_spatial_relationship(
- rast1, nband1,
- rast2, nband2,
- GSR_TOUCHES,
- touches
- );
-}
-
-/**
- * Return ES_ERROR if error occurred in function.
- * Parameter contains returns non-zero if rast1 contains rast2
- *
- * @param rast1 : the first raster whose band will be tested
- * @param nband1 : the 0-based band of raster rast1 to use
- * if value is less than zero, bands are ignored.
- * if nband1 gte zero, nband2 must be gte zero
- * @param rast2 : the second raster whose band will be tested
- * @param nband2 : the 0-based band of raster rast2 to use
- * if value is less than zero, bands are ignored
- * if nband2 gte zero, nband1 must be gte zero
- * @param contains : non-zero value if rast1 contains rast2
- *
- * @return ES_NONE if success, ES_ERROR if error
- */
-rt_errorstate rt_raster_contains(
- rt_raster rast1, int nband1,
- rt_raster rast2, int nband2,
- int *contains
-) {
- RASTER_DEBUG(3, "Starting");
-
- return rt_raster_geos_spatial_relationship(
- rast1, nband1,
- rast2, nband2,
- GSR_CONTAINS,
- contains
- );
-}
-
-/**
- * Return ES_ERROR if error occurred in function.
- * Parameter contains returns non-zero if rast1 contains properly rast2
- *
- * @param rast1 : the first raster whose band will be tested
- * @param nband1 : the 0-based band of raster rast1 to use
- * if value is less than zero, bands are ignored.
- * if nband1 gte zero, nband2 must be gte zero
- * @param rast2 : the second raster whose band will be tested
- * @param nband2 : the 0-based band of raster rast2 to use
- * if value is less than zero, bands are ignored
- * if nband2 gte zero, nband1 must be gte zero
- * @param contains : non-zero value if rast1 contains properly rast2
- *
- * @return ES_NONE if success, ES_ERROR if error
- */
-rt_errorstate rt_raster_contains_properly(
- rt_raster rast1, int nband1,
- rt_raster rast2, int nband2,
- int *contains
-) {
- RASTER_DEBUG(3, "Starting");
-
- return rt_raster_geos_spatial_relationship(
- rast1, nband1,
- rast2, nband2,
- GSR_CONTAINSPROPERLY,
- contains
- );
-}
-
-/**
- * Return ES_ERROR if error occurred in function.
- * Parameter covers returns non-zero if rast1 covers rast2
- *
- * @param rast1 : the first raster whose band will be tested
- * @param nband1 : the 0-based band of raster rast1 to use
- * if value is less than zero, bands are ignored.
- * if nband1 gte zero, nband2 must be gte zero
- * @param rast2 : the second raster whose band will be tested
- * @param nband2 : the 0-based band of raster rast2 to use
- * if value is less than zero, bands are ignored
- * if nband2 gte zero, nband1 must be gte zero
- * @param covers : non-zero value if rast1 covers rast2
- *
- * @return ES_NONE if success, ES_ERROR if error
- */
-rt_errorstate rt_raster_covers(
- rt_raster rast1, int nband1,
- rt_raster rast2, int nband2,
- int *covers
-) {
- RASTER_DEBUG(3, "Starting");
-
- return rt_raster_geos_spatial_relationship(
- rast1, nband1,
- rast2, nband2,
- GSR_COVERS,
- covers
- );
-}
-
-/**
- * Return ES_ERROR if error occurred in function.
- * Parameter coveredby returns non-zero if rast1 is covered by rast2
- *
- * @param rast1 : the first raster whose band will be tested
- * @param nband1 : the 0-based band of raster rast1 to use
- * if value is less than zero, bands are ignored.
- * if nband1 gte zero, nband2 must be gte zero
- * @param rast2 : the second raster whose band will be tested
- * @param nband2 : the 0-based band of raster rast2 to use
- * if value is less than zero, bands are ignored
- * if nband2 gte zero, nband1 must be gte zero
- * @param coveredby : non-zero value if rast1 is covered by rast2
- *
- * @return ES_NONE if success, ES_ERROR if error
- */
-rt_errorstate rt_raster_coveredby(
- rt_raster rast1, int nband1,
- rt_raster rast2, int nband2,
- int *coveredby
-) {
- RASTER_DEBUG(3, "Starting");
-
- return rt_raster_geos_spatial_relationship(
- rast1, nband1,
- rast2, nband2,
- GSR_COVEREDBY,
- coveredby
- );
-}
-
-/**
- * Return ES_ERROR if error occurred in function.
- * Parameter dwithin returns non-zero if rast1 is within the specified
- * distance of rast2
- *
- * @param rast1 : the first raster whose band will be tested
- * @param nband1 : the 0-based band of raster rast1 to use
- * if value is less than zero, bands are ignored.
- * if nband1 gte zero, nband2 must be gte zero
- * @param rast2 : the second raster whose band will be tested
- * @param nband2 : the 0-based band of raster rast2 to use
- * if value is less than zero, bands are ignored
- * if nband2 gte zero, nband1 must be gte zero
- * @param dwithin : non-zero value if rast1 is within the specified distance
- * of rast2
- *
- * @return ES_NONE if success, ES_ERROR if error
- */
-rt_errorstate rt_raster_within_distance(
- rt_raster rast1, int nband1,
- rt_raster rast2, int nband2,
- double distance,
- int *dwithin
-) {
- LWMPOLY *surface = NULL;
- LWGEOM *surface1 = NULL;
- LWGEOM *surface2 = NULL;
- double mindist = 0;
-
- RASTER_DEBUG(3, "Starting");
-
- assert(NULL != rast1);
- assert(NULL != rast2);
- assert(NULL != dwithin);
-
- if (nband1 < 0 && nband2 < 0) {
- nband1 = -1;
- nband2 = -1;
- }
- else {
- assert(nband1 >= 0 && nband1 < rt_raster_get_num_bands(rast1));
- assert(nband2 >= 0 && nband2 < rt_raster_get_num_bands(rast2));
- }
-
- /* initialize to zero, false result */
- *dwithin = 0;
-
- /* same srid */
- if (rt_raster_get_srid(rast1) != rt_raster_get_srid(rast2)) {
- rterror("rt_raster_distance_within: The two rasters provided have different SRIDs");
- return ES_ERROR;
- }
-
- /* distance cannot be less than zero */
- if (distance < 0) {
- rterror("rt_raster_distance_within: Distance cannot be less than zero");
- return ES_ERROR;
- }
-
- /* get LWMPOLY of each band */
- if (rt_raster_surface(rast1, nband1, &surface) != ES_NONE) {
- rterror("rt_raster_distance_within: Could not get surface of the specified band from the first raster");
- return ES_ERROR;
- }
- surface1 = lwmpoly_as_lwgeom(surface);
-
- if (rt_raster_surface(rast2, nband2, &surface) != ES_NONE) {
- rterror("rt_raster_distance_within: Could not get surface of the specified band from the second raster");
- lwgeom_free(surface1);
- return ES_ERROR;
- }
- surface2 = lwmpoly_as_lwgeom(surface);
-
- /* either surface is NULL, test is false */
- if (surface1 == NULL || surface2 == NULL) {
- if (surface1 != NULL) lwgeom_free(surface1);
- if (surface2 != NULL) lwgeom_free(surface2);
- return ES_NONE;
- }
-
- /* get the min distance between the two surfaces */
- mindist = lwgeom_mindistance2d_tolerance(surface1, surface2, distance);
-
- lwgeom_free(surface1);
- lwgeom_free(surface2);
-
- /* if distance >= mindist, true */
- if (FLT_EQ(mindist, distance) || distance > mindist)
- *dwithin = 1;
-
- RASTER_DEBUGF(3, "(mindist, distance) = (%f, %f, %d)", mindist, distance, *dwithin);
-
- return ES_NONE;
-}
-
-/**
- * Return ES_ERROR if error occurred in function.
- * Parameter dfwithin returns non-zero if rast1 is fully within the specified
- * distance of rast2
- *
- * @param rast1 : the first raster whose band will be tested
- * @param nband1 : the 0-based band of raster rast1 to use
- * if value is less than zero, bands are ignored.
- * if nband1 gte zero, nband2 must be gte zero
- * @param rast2 : the second raster whose band will be tested
- * @param nband2 : the 0-based band of raster rast2 to use
- * if value is less than zero, bands are ignored
- * if nband2 gte zero, nband1 must be gte zero
- * @param dfwithin : non-zero value if rast1 is fully within the specified
- * distance of rast2
- *
- * @return ES_NONE if success, ES_ERROR if error
- */
-rt_errorstate rt_raster_fully_within_distance(
- rt_raster rast1, int nband1,
- rt_raster rast2, int nband2,
- double distance,
- int *dfwithin
-) {
- LWMPOLY *surface = NULL;
- LWGEOM *surface1 = NULL;
- LWGEOM *surface2 = NULL;
- double maxdist = 0;
-
- RASTER_DEBUG(3, "Starting");
-
- assert(NULL != rast1);
- assert(NULL != rast2);
- assert(NULL != dfwithin);
-
- if (nband1 < 0 && nband2 < 0) {
- nband1 = -1;
- nband2 = -1;
- }
- else {
- assert(nband1 >= 0 && nband1 < rt_raster_get_num_bands(rast1));
- assert(nband2 >= 0 && nband2 < rt_raster_get_num_bands(rast2));
- }
-
- /* initialize to zero, false result */
- *dfwithin = 0;
-
- /* same srid */
- if (rt_raster_get_srid(rast1) != rt_raster_get_srid(rast2)) {
- rterror("rt_raster_fully_within_distance: The two rasters provided have different SRIDs");
- return ES_ERROR;
- }
-
- /* distance cannot be less than zero */
- if (distance < 0) {
- rterror("rt_raster_fully_within_distance: Distance cannot be less than zero");
- return ES_ERROR;
- }
-
- /* get LWMPOLY of each band */
- if (rt_raster_surface(rast1, nband1, &surface) != ES_NONE) {
- rterror("rt_raster_fully_within_distance: Could not get surface of the specified band from the first raster");
- return ES_ERROR;
- }
- surface1 = lwmpoly_as_lwgeom(surface);
-
- if (rt_raster_surface(rast2, nband2, &surface) != ES_NONE) {
- rterror("rt_raster_fully_within_distance: Could not get surface of the specified band from the second raster");
- lwgeom_free(surface1);
- return ES_ERROR;
- }
- surface2 = lwmpoly_as_lwgeom(surface);
-
- /* either surface is NULL, test is false */
- if (surface1 == NULL || surface2 == NULL) {
- if (surface1 != NULL) lwgeom_free(surface1);
- if (surface2 != NULL) lwgeom_free(surface2);
- return ES_NONE;
- }
-
- /* get the maximum distance between the two surfaces */
- maxdist = lwgeom_maxdistance2d_tolerance(surface1, surface2, distance);
-
- lwgeom_free(surface1);
- lwgeom_free(surface2);
-
- /* if distance >= maxdist, true */
- if (FLT_EQ(maxdist, distance) || distance > maxdist)
- *dfwithin = 1;
-
- RASTER_DEBUGF(3, "(maxdist, distance, dfwithin) = (%f, %f, %d)", maxdist, distance, *dfwithin);
-
- return ES_NONE;
-}
-
-/*
- * Return ES_ERROR if error occurred in function.
- * Paramter aligned returns non-zero if two rasters are aligned
- *
- * @param rast1 : the first raster for alignment test
- * @param rast2 : the second raster for alignment test
- * @param *aligned : non-zero value if the two rasters are aligned
- * @param *reason : reason why rasters are not aligned
- *
- * @return ES_NONE if success, ES_ERROR if error
- */
-rt_errorstate
-rt_raster_same_alignment(
- rt_raster rast1,
- rt_raster rast2,
- int *aligned, char **reason
-) {
- double xr;
- double yr;
- double xw;
- double yw;
- int err = 0;
-
- assert(NULL != rast1);
- assert(NULL != rast2);
- assert(NULL != aligned);
-
- err = 0;
- /* same srid */
- if (rt_raster_get_srid(rast1) != rt_raster_get_srid(rast2)) {
- if (reason != NULL) *reason = "The rasters have different SRIDs";
- err = 1;
- }
- /* scales must match */
- else if (FLT_NEQ(fabs(rast1->scaleX), fabs(rast2->scaleX))) {
- if (reason != NULL) *reason = "The rasters have different scales on the X axis";
- err = 1;
- }
- else if (FLT_NEQ(fabs(rast1->scaleY), fabs(rast2->scaleY))) {
- if (reason != NULL) *reason = "The rasters have different scales on the Y axis";
- err = 1;
- }
- /* skews must match */
- else if (FLT_NEQ(rast1->skewX, rast2->skewX)) {
- if (reason != NULL) *reason = "The rasters have different skews on the X axis";
- err = 1;
- }
- else if (FLT_NEQ(rast1->skewY, rast2->skewY)) {
- if (reason != NULL) *reason = "The rasters have different skews on the Y axis";
- err = 1;
- }
-
- if (err) {
- *aligned = 0;
- return ES_NONE;
- }
-
- /* raster coordinates in context of second raster of first raster's upper-left corner */
- if (rt_raster_geopoint_to_cell(
- rast2,
- rast1->ipX, rast1->ipY,
- &xr, &yr,
- NULL
- ) != ES_NONE) {
- rterror("rt_raster_same_alignment: Could not get raster coordinates of second raster from first raster's spatial coordinates");
- *aligned = 0;
- return ES_ERROR;
- }
-
- /* spatial coordinates of raster coordinates from above */
- if (rt_raster_cell_to_geopoint(
- rast2,
- xr, yr,
- &xw, &yw,
- NULL
- ) != ES_NONE) {
- rterror("rt_raster_same_alignment: Could not get spatial coordinates of second raster from raster coordinates");
- *aligned = 0;
- return ES_ERROR;
- }
-
- RASTER_DEBUGF(4, "rast1(ipX, ipxY) = (%f, %f)", rast1->ipX, rast1->ipY);
- RASTER_DEBUGF(4, "rast2(xr, yr) = (%f, %f)", xr, yr);
- RASTER_DEBUGF(4, "rast2(xw, yw) = (%f, %f)", xw, yw);
-
- /* spatial coordinates are identical to that of first raster's upper-left corner */
- if (FLT_EQ(xw, rast1->ipX) && FLT_EQ(yw, rast1->ipY)) {
- if (reason != NULL) *reason = "The rasters are aligned";
- *aligned = 1;
- return ES_NONE;
- }
-
- /* no alignment */
- if (reason != NULL) *reason = "The rasters (pixel corner coordinates) are not aligned";
-
- *aligned = 0;
- return ES_NONE;
-}
-
-/*
- * Return raster of computed extent specified extenttype applied
- * on two input rasters. The raster returned should be freed by
- * the caller
- *
- * @param rast1 : the first raster
- * @param rast2 : the second raster
- * @param extenttype : type of extent for the output raster
- * @param *rtnraster : raster of computed extent
- * @param *offset : 4-element array indicating the X,Y offsets
- * for each raster. 0,1 for rast1 X,Y. 2,3 for rast2 X,Y.
- *
- * @return ES_NONE if success, ES_ERROR if error
- */
-rt_errorstate
-rt_raster_from_two_rasters(
- rt_raster rast1, rt_raster rast2,
- rt_extenttype extenttype,
- rt_raster *rtnraster, double *offset
-) {
- int i;
-
- rt_raster _rast[2] = {rast1, rast2};
- double _offset[2][4] = {{0.}};
- uint16_t _dim[2][2] = {{0}};
-
- rt_raster raster = NULL;
- int aligned = 0;
- int dim[2] = {0};
- double gt[6] = {0.};
-
- assert(NULL != rast1);
- assert(NULL != rast2);
- assert(NULL != rtnraster);
-
- /* set rtnraster to NULL */
- *rtnraster = NULL;
-
- /* rasters must be aligned */
- if (rt_raster_same_alignment(rast1, rast2, &aligned, NULL) != ES_NONE) {
- rterror("rt_raster_from_two_rasters: Could not test for alignment on the two rasters");
- return ES_ERROR;
- }
- if (!aligned) {
- rterror("rt_raster_from_two_rasters: The two rasters provided do not have the same alignment");
- return ES_ERROR;
- }
-
- /* dimensions */
- _dim[0][0] = rast1->width;
- _dim[0][1] = rast1->height;
- _dim[1][0] = rast2->width;
- _dim[1][1] = rast2->height;
-
- /* get raster offsets */
- if (rt_raster_geopoint_to_cell(
- _rast[1],
- _rast[0]->ipX, _rast[0]->ipY,
- &(_offset[1][0]), &(_offset[1][1]),
- NULL
- ) != ES_NONE) {
- rterror("rt_raster_from_two_rasters: Could not compute offsets of the second raster relative to the first raster");
- return ES_ERROR;
- }
- _offset[1][0] = -1 * _offset[1][0];
- _offset[1][1] = -1 * _offset[1][1];
- _offset[1][2] = _offset[1][0] + _dim[1][0] - 1;
- _offset[1][3] = _offset[1][1] + _dim[1][1] - 1;
-
- i = -1;
- switch (extenttype) {
- case ET_FIRST:
- i = 0;
- _offset[0][0] = 0.;
- _offset[0][1] = 0.;
- case ET_LAST:
- case ET_SECOND:
- if (i < 0) {
- i = 1;
- _offset[0][0] = -1 * _offset[1][0];
- _offset[0][1] = -1 * _offset[1][1];
- _offset[1][0] = 0.;
- _offset[1][1] = 0.;
- }
-
- dim[0] = _dim[i][0];
- dim[1] = _dim[i][1];
- raster = rt_raster_new(
- dim[0],
- dim[1]
- );
- if (raster == NULL) {
- rterror("rt_raster_from_two_rasters: Could not create output raster");
- return ES_ERROR;
- }
- rt_raster_set_srid(raster, _rast[i]->srid);
- rt_raster_get_geotransform_matrix(_rast[i], gt);
- rt_raster_set_geotransform_matrix(raster, gt);
- break;
- case ET_UNION: {
- double off[4] = {0};
-
- rt_raster_get_geotransform_matrix(_rast[0], gt);
- RASTER_DEBUGF(4, "gt = (%f, %f, %f, %f, %f, %f)",
- gt[0],
- gt[1],
- gt[2],
- gt[3],
- gt[4],
- gt[5]
- );
-
- /* new raster upper left offset */
- off[0] = 0;
- if (_offset[1][0] < 0)
- off[0] = _offset[1][0];
- off[1] = 0;
- if (_offset[1][1] < 0)
- off[1] = _offset[1][1];
-
- /* new raster lower right offset */
- off[2] = _dim[0][0] - 1;
- if ((int) _offset[1][2] >= _dim[0][0])
- off[2] = _offset[1][2];
- off[3] = _dim[0][1] - 1;
- if ((int) _offset[1][3] >= _dim[0][1])
- off[3] = _offset[1][3];
-
- /* upper left corner */
- if (rt_raster_cell_to_geopoint(
- _rast[0],
- off[0], off[1],
- &(gt[0]), &(gt[3]),
- NULL
- ) != ES_NONE) {
- rterror("rt_raster_from_two_rasters: Could not get spatial coordinates of upper-left pixel of output raster");
- return ES_ERROR;
- }
-
- dim[0] = off[2] - off[0] + 1;
- dim[1] = off[3] - off[1] + 1;
- RASTER_DEBUGF(4, "off = (%f, %f, %f, %f)",
- off[0],
- off[1],
- off[2],
- off[3]
- );
- RASTER_DEBUGF(4, "dim = (%d, %d)", dim[0], dim[1]);
-
- raster = rt_raster_new(
- dim[0],
- dim[1]
- );
- if (raster == NULL) {
- rterror("rt_raster_from_two_rasters: Could not create output raster");
- return ES_ERROR;
- }
- rt_raster_set_srid(raster, _rast[0]->srid);
- rt_raster_set_geotransform_matrix(raster, gt);
- RASTER_DEBUGF(4, "gt = (%f, %f, %f, %f, %f, %f)",
- gt[0],
- gt[1],
- gt[2],
- gt[3],
- gt[4],
- gt[5]
- );
-
- /* get offsets */
- if (rt_raster_geopoint_to_cell(
- _rast[0],
- gt[0], gt[3],
- &(_offset[0][0]), &(_offset[0][1]),
- NULL
- ) != ES_NONE) {
- rterror("rt_raster_from_two_rasters: Could not get offsets of the FIRST raster relative to the output raster");
- rt_raster_destroy(raster);
- return ES_ERROR;
- }
- _offset[0][0] *= -1;
- _offset[0][1] *= -1;
-
- if (rt_raster_geopoint_to_cell(
- _rast[1],
- gt[0], gt[3],
- &(_offset[1][0]), &(_offset[1][1]),
- NULL
- ) != ES_NONE) {
- rterror("rt_raster_from_two_rasters: Could not get offsets of the SECOND raster relative to the output raster");
- rt_raster_destroy(raster);
- return ES_ERROR;
- }
- _offset[1][0] *= -1;
- _offset[1][1] *= -1;
- break;
- }
- case ET_INTERSECTION: {
- double off[4] = {0};
-
- /* no intersection */
- if (
- (_offset[1][2] < 0 || _offset[1][0] > (_dim[0][0] - 1)) ||
- (_offset[1][3] < 0 || _offset[1][1] > (_dim[0][1] - 1))
- ) {
- RASTER_DEBUG(3, "The two rasters provided have no intersection. Returning no band raster");
-
- raster = rt_raster_new(0, 0);
- if (raster == NULL) {
- rterror("rt_raster_from_two_rasters: Could not create output raster");
- return ES_ERROR;
- }
- rt_raster_set_srid(raster, _rast[0]->srid);
- rt_raster_set_scale(raster, 0, 0);
-
- /* set offsets if provided */
- if (NULL != offset) {
- for (i = 0; i < 4; i++)
- offset[i] = _offset[i / 2][i % 2];
- }
-
- *rtnraster = raster;
- return ES_NONE;
- }
-
- if (_offset[1][0] > 0)
- off[0] = _offset[1][0];
- if (_offset[1][1] > 0)
- off[1] = _offset[1][1];
-
- off[2] = _dim[0][0] - 1;
- if (_offset[1][2] < _dim[0][0])
- off[2] = _offset[1][2];
- off[3] = _dim[0][1] - 1;
- if (_offset[1][3] < _dim[0][1])
- off[3] = _offset[1][3];
-
- dim[0] = off[2] - off[0] + 1;
- dim[1] = off[3] - off[1] + 1;
- raster = rt_raster_new(
- dim[0],
- dim[1]
- );
- if (raster == NULL) {
- rterror("rt_raster_from_two_rasters: Could not create output raster");
- return ES_ERROR;
- }
- rt_raster_set_srid(raster, _rast[0]->srid);
-
- /* get upper-left corner */
- rt_raster_get_geotransform_matrix(_rast[0], gt);
- if (rt_raster_cell_to_geopoint(
- _rast[0],
- off[0], off[1],
- &(gt[0]), &(gt[3]),
- gt
- ) != ES_NONE) {
- rterror("rt_raster_from_two_rasters: Could not get spatial coordinates of upper-left pixel of output raster");
- rt_raster_destroy(raster);
- return ES_ERROR;
- }
-
- rt_raster_set_geotransform_matrix(raster, gt);
-
- /* get offsets */
- if (rt_raster_geopoint_to_cell(
- _rast[0],
- gt[0], gt[3],
- &(_offset[0][0]), &(_offset[0][1]),
- NULL
- ) != ES_NONE) {
- rterror("rt_raster_from_two_rasters: Could not get pixel coordinates to compute the offsets of the FIRST raster relative to the output raster");
- rt_raster_destroy(raster);
- return ES_ERROR;
- }
- _offset[0][0] *= -1;
- _offset[0][1] *= -1;
-
- if (rt_raster_geopoint_to_cell(
- _rast[1],
- gt[0], gt[3],
- &(_offset[1][0]), &(_offset[1][1]),
- NULL
- ) != ES_NONE) {
- rterror("rt_raster_from_two_rasters: Could not get pixel coordinates to compute the offsets of the SECOND raster relative to the output raster");
- rt_raster_destroy(raster);
- return ES_ERROR;
- }
- _offset[1][0] *= -1;
- _offset[1][1] *= -1;
- break;
- }
- case ET_CUSTOM:
- rterror("rt_raster_from_two_rasters: Extent type ET_CUSTOM is not supported by this function");
- break;
- }
-
- /* set offsets if provided */
- if (NULL != offset) {
- for (i = 0; i < 4; i++)
- offset[i] = _offset[i / 2][i % 2];
- }
-
- *rtnraster = raster;
- return ES_NONE;
-}
-
-/**
- * Get a raster pixel as a polygon.
- *
- * The pixel shape is a 4 vertices (5 to be closed) single
- * ring polygon bearing the raster's rotation
- * and using projection coordinates
- *
- * @param raster : the raster to get pixel from
- * @param x : the column number
- * @param y : the row number
- *
- * @return the pixel polygon, or NULL on error.
- *
- */
-LWPOLY*
-rt_raster_pixel_as_polygon(rt_raster rast, int x, int y)
-{
- double scale_x, scale_y;
- double skew_x, skew_y;
- double ul_x, ul_y;
- int srid;
- POINTARRAY **points;
- POINT4D p, p0;
- LWPOLY *poly;
-
- assert(rast != NULL);
-
- scale_x = rt_raster_get_x_scale(rast);
- scale_y = rt_raster_get_y_scale(rast);
- skew_x = rt_raster_get_x_skew(rast);
- skew_y = rt_raster_get_y_skew(rast);
- ul_x = rt_raster_get_x_offset(rast);
- ul_y = rt_raster_get_y_offset(rast);
- srid = rt_raster_get_srid(rast);
-
- points = rtalloc(sizeof(POINTARRAY *)*1);
- points[0] = ptarray_construct(0, 0, 5);
-
- p0.x = scale_x * x + skew_x * y + ul_x;
- p0.y = scale_y * y + skew_y * x + ul_y;
- ptarray_set_point4d(points[0], 0, &p0);
-
- p.x = p0.x + scale_x;
- p.y = p0.y + skew_y;
- ptarray_set_point4d(points[0], 1, &p);
-
- p.x = p0.x + scale_x + skew_x;
- p.y = p0.y + scale_y + skew_y;
- ptarray_set_point4d(points[0], 2, &p);
-
- p.x = p0.x + skew_x;
- p.y = p0.y + scale_y;
- ptarray_set_point4d(points[0], 3, &p);
-
- /* close it */
- ptarray_set_point4d(points[0], 4, &p0);
-
- poly = lwpoly_construct(srid, NULL, 1, points);
-
- return poly;
-}
-
-/**
- * Get a raster as a surface (multipolygon). If a band is specified,
- * those pixels with value (not NODATA) contribute to the area
- * of the output multipolygon.
- *
- * @param raster : the raster to convert to a multipolygon
- * @param nband : the 0-based band of raster rast to use
- * if value is less than zero, bands are ignored.
- * @param *surface : raster as a surface (multipolygon).
- * if all pixels are NODATA, NULL is set
- *
- * @return ES_NONE on success, ES_ERROR on error
- */
-rt_errorstate rt_raster_surface(rt_raster raster, int nband, LWMPOLY **surface) {
- rt_band band = NULL;
- LWGEOM *mpoly = NULL;
- LWGEOM *tmp = NULL;
- LWGEOM *clone = NULL;
- rt_geomval gv = NULL;
- int gvcount = 0;
- GEOSGeometry *gc = NULL;
- GEOSGeometry *gunion = NULL;
- GEOSGeometry **geoms = NULL;
- int geomscount = 0;
- int i = 0;
-
- assert(surface != NULL);
-
- /* init *surface to NULL */
- *surface = NULL;
-
- /* raster is empty, surface = NULL */
- if (rt_raster_is_empty(raster))
- return ES_NONE;
-
- /* if nband < 0, return the convex hull as a multipolygon */
- if (nband < 0) {
- /*
- lwgeom_as_multi() only does a shallow clone internally
- so input and output geometries may share memory
- hence the deep clone of the output geometry for returning
- is the only way to guarentee the memory isn't shared
- */
- if (rt_raster_get_convex_hull(raster, &tmp) != ES_NONE) {
- rterror("rt_raster_surface: Could not get convex hull of raster");
- return ES_ERROR;
- }
- mpoly = lwgeom_as_multi(tmp);
- clone = lwgeom_clone_deep(mpoly);
- lwgeom_free(tmp);
- lwgeom_free(mpoly);
-
- *surface = lwgeom_as_lwmpoly(clone);
- return ES_NONE;
- }
- /* check that nband is valid */
- else if (nband >= rt_raster_get_num_bands(raster)) {
- rterror("rt_raster_surface: The band index %d is invalid", nband);
- return ES_ERROR;
- }
-
- /* get band */
- band = rt_raster_get_band(raster, nband);
- if (band == NULL) {
- rterror("rt_raster_surface: Error getting band %d from raster", nband);
- return ES_ERROR;
- }
-
- /* band does not have a NODATA flag, return convex hull */
- if (!rt_band_get_hasnodata_flag(band)) {
- /*
- lwgeom_as_multi() only does a shallow clone internally
- so input and output geometries may share memory
- hence the deep clone of the output geometry for returning
- is the only way to guarentee the memory isn't shared
- */
- if (rt_raster_get_convex_hull(raster, &tmp) != ES_NONE) {
- rterror("rt_raster_surface: Could not get convex hull of raster");
- return ES_ERROR;
- }
- mpoly = lwgeom_as_multi(tmp);
- clone = lwgeom_clone_deep(mpoly);
- lwgeom_free(tmp);
- lwgeom_free(mpoly);
-
- *surface = lwgeom_as_lwmpoly(clone);
- return ES_NONE;
- }
- /* band is NODATA, return NULL */
- else if (rt_band_get_isnodata_flag(band)) {
- RASTER_DEBUG(3, "Band is NODATA. Returning NULL");
- return ES_NONE;
- }
-
- /* initialize GEOS */
- initGEOS(lwnotice, lwgeom_geos_error);
-
- /* use gdal polygonize */
- gv = rt_raster_gdal_polygonize(raster, nband, 1, &gvcount);
- /* no polygons returned */
- if (gvcount < 1) {
- RASTER_DEBUG(3, "All pixels of band are NODATA. Returning NULL");
- if (gv != NULL) rtdealloc(gv);
- return ES_NONE;
- }
- /* more than 1 polygon */
- else if (gvcount > 1) {
- /* convert LWPOLY to GEOSGeometry */
- geomscount = gvcount;
- geoms = rtalloc(sizeof(GEOSGeometry *) * geomscount);
- if (geoms == NULL) {
- rterror("rt_raster_surface: Could not allocate memory for pixel polygons as GEOSGeometry");
- for (i = 0; i < gvcount; i++) lwpoly_free(gv[i].geom);
- rtdealloc(gv);
- return ES_ERROR;
- }
- for (i = 0; i < gvcount; i++) {
-#if POSTGIS_DEBUG_LEVEL > 3
- {
- char *wkt = lwgeom_to_wkt(lwpoly_as_lwgeom(gv[i].geom), WKT_ISO, DBL_DIG, NULL);
- RASTER_DEBUGF(4, "geom %d = %s", i, wkt);
- rtdealloc(wkt);
- }
-#endif
-
- geoms[i] = LWGEOM2GEOS(lwpoly_as_lwgeom(gv[i].geom));
- lwpoly_free(gv[i].geom);
- }
- rtdealloc(gv);
-
- /* create geometry collection */
-#if POSTGIS_GEOS_VERSION >= 33
- gc = GEOSGeom_createCollection(GEOS_GEOMETRYCOLLECTION, geoms, geomscount);
-#else
- gc = GEOSGeom_createCollection(GEOS_MULTIPOLYGON, geoms, geomscount);
-#endif
-
- if (gc == NULL) {
-#if POSTGIS_GEOS_VERSION >= 33
- rterror("rt_raster_surface: Could not create GEOS GEOMETRYCOLLECTION from set of pixel polygons");
-#else
- rterror("rt_raster_surface: Could not create GEOS MULTIPOLYGON from set of pixel polygons");
-#endif
-
- for (i = 0; i < geomscount; i++)
- GEOSGeom_destroy(geoms[i]);
- rtdealloc(geoms);
- return ES_ERROR;
- }
-
- /* run the union */
-#if POSTGIS_GEOS_VERSION >= 33
- gunion = GEOSUnaryUnion(gc);
-#else
- gunion = GEOSUnionCascaded(gc);
-#endif
- GEOSGeom_destroy(gc);
- rtdealloc(geoms);
-
- if (gunion == NULL) {
-#if POSTGIS_GEOS_VERSION >= 33
- rterror("rt_raster_surface: Could not union the pixel polygons using GEOSUnaryUnion()");
-#else
- rterror("rt_raster_surface: Could not union the pixel polygons using GEOSUnionCascaded()");
-#endif
- return ES_ERROR;
- }
-
- /* convert union result from GEOSGeometry to LWGEOM */
- mpoly = GEOS2LWGEOM(gunion, 0);
-
- /*
- is geometry valid?
- if not, try to make valid
- */
- do {
- LWGEOM *mpolyValid = NULL;
-
-#if POSTGIS_GEOS_VERSION < 33
- break;
-#endif
-
- if (GEOSisValid(gunion))
- break;
-
- /* make geometry valid */
- mpolyValid = lwgeom_make_valid(mpoly);
- if (mpolyValid == NULL) {
- rtwarn("Cannot fix invalid geometry");
- break;
- }
-
- lwgeom_free(mpoly);
- mpoly = mpolyValid;
- }
- while (0);
-
- GEOSGeom_destroy(gunion);
- }
- else {
- mpoly = lwpoly_as_lwgeom(gv[0].geom);
- rtdealloc(gv);
-
-#if POSTGIS_DEBUG_LEVEL > 3
- {
- char *wkt = lwgeom_to_wkt(mpoly, WKT_ISO, DBL_DIG, NULL);
- RASTER_DEBUGF(4, "geom 0 = %s", wkt);
- rtdealloc(wkt);
- }
-#endif
- }
-
- /* specify SRID */
- lwgeom_set_srid(mpoly, rt_raster_get_srid(raster));
-
- if (mpoly != NULL) {
- /* convert to multi */
- if (!lwgeom_is_collection(mpoly)) {
- tmp = mpoly;
-
-#if POSTGIS_DEBUG_LEVEL > 3
- {
- char *wkt = lwgeom_to_wkt(mpoly, WKT_ISO, DBL_DIG, NULL);
- RASTER_DEBUGF(4, "before multi = %s", wkt);
- rtdealloc(wkt);
- }
-#endif
-
- RASTER_DEBUGF(4, "mpoly @ %p", mpoly);
-
- /*
- lwgeom_as_multi() only does a shallow clone internally
- so input and output geometries may share memory
- hence the deep clone of the output geometry for returning
- is the only way to guarentee the memory isn't shared
- */
- mpoly = lwgeom_as_multi(tmp);
- clone = lwgeom_clone_deep(mpoly);
- lwgeom_free(tmp);
- lwgeom_free(mpoly);
- mpoly = clone;
-
- RASTER_DEBUGF(4, "mpoly @ %p", mpoly);
-
-#if POSTGIS_DEBUG_LEVEL > 3
- {
- char *wkt = lwgeom_to_wkt(mpoly, WKT_ISO, DBL_DIG, NULL);
- RASTER_DEBUGF(4, "after multi = %s", wkt);
- rtdealloc(wkt);
- }
-#endif
- }
-
-#if POSTGIS_DEBUG_LEVEL > 3
- {
- char *wkt = lwgeom_to_wkt(mpoly, WKT_ISO, DBL_DIG, NULL);
- RASTER_DEBUGF(4, "returning geometry = %s", wkt);
- rtdealloc(wkt);
- }
-#endif
-
- *surface = lwgeom_as_lwmpoly(mpoly);
- return ES_NONE;
- }
-
- return ES_NONE;
-}
-
-/******************************************************************************
-* rt_raster_iterator()
-******************************************************************************/
-
-typedef struct _rti_iterator_arg_t* _rti_iterator_arg;
-struct _rti_iterator_arg_t {
- int count;
-
- rt_raster *raster;
- int *isempty;
- double **offset;
- int *width;
- int *height;
-
- struct {
- rt_band *rtband;
- int *hasnodata;
- int *isnodata;
- double *nodataval;
- double *minval;
- } band;
-
- struct {
- uint16_t x;
- uint16_t y;
- } distance;
-
- struct {
- uint32_t rows;
- uint32_t columns;
- } dimension;
-
- struct {
- double **values;
- int **nodata;
- } empty;
-
- rt_iterator_arg arg;
-};
-
-static _rti_iterator_arg
-_rti_iterator_arg_init() {
- _rti_iterator_arg _param;
-
- _param = rtalloc(sizeof(struct _rti_iterator_arg_t));
- if (_param == NULL) {
- rterror("_rti_iterator_arg_init: Could not allocate memory for _rti_iterator_arg");
- return NULL;
- }
-
- _param->count = 0;
-
- _param->raster = NULL;
- _param->isempty = NULL;
- _param->offset = NULL;
- _param->width = NULL;
- _param->height = NULL;
-
- _param->band.rtband = NULL;
- _param->band.hasnodata = NULL;
- _param->band.isnodata = NULL;
- _param->band.nodataval = NULL;
- _param->band.minval = NULL;
-
- _param->distance.x = 0;
- _param->distance.y = 0;
-
- _param->dimension.rows = 0;
- _param->dimension.columns = 0;
-
- _param->empty.values = NULL;
- _param->empty.nodata = NULL;
-
- _param->arg = NULL;
-
- return _param;
-}
-
-static void
-_rti_iterator_arg_destroy(_rti_iterator_arg _param) {
- int i = 0;
-
- if (_param->raster != NULL)
- rtdealloc(_param->raster);
- if (_param->isempty != NULL)
- rtdealloc(_param->isempty);
- if (_param->width != NULL)
- rtdealloc(_param->width);
- if (_param->height != NULL)
- rtdealloc(_param->height);
-
- if (_param->band.rtband != NULL)
- rtdealloc(_param->band.rtband);
- if (_param->band.hasnodata != NULL)
- rtdealloc(_param->band.hasnodata);
- if (_param->band.isnodata != NULL)
- rtdealloc(_param->band.isnodata);
- if (_param->band.nodataval != NULL)
- rtdealloc(_param->band.nodataval);
- if (_param->band.minval != NULL)
- rtdealloc(_param->band.minval);
-
- if (_param->offset != NULL) {
- for (i = 0; i < _param->count; i++) {
- if (_param->offset[i] == NULL)
- continue;
- rtdealloc(_param->offset[i]);
- }
- rtdealloc(_param->offset);
- }
-
- if (_param->empty.values != NULL) {
- for (i = 0; i < _param->dimension.rows; i++) {
- if (_param->empty.values[i] == NULL)
- continue;
- rtdealloc(_param->empty.values[i]);
- }
- rtdealloc(_param->empty.values);
- }
- if (_param->empty.nodata != NULL) {
- for (i = 0; i < _param->dimension.rows; i++) {
- if (_param->empty.nodata[i] == NULL)
- continue;
- rtdealloc(_param->empty.nodata[i]);
- }
- rtdealloc(_param->empty.nodata);
- }
-
- if (_param->arg != NULL) {
- if (_param->arg->values != NULL)
- rtdealloc(_param->arg->values);
- if (_param->arg->nodata != NULL)
- rtdealloc(_param->arg->nodata);
- if (_param->arg->src_pixel != NULL) {
- for (i = 0; i < _param->count; i++) {
- if (_param->arg->src_pixel[i] == NULL)
- continue;
- rtdealloc(_param->arg->src_pixel[i]);
- }
-
- rtdealloc(_param->arg->src_pixel);
- }
-
- rtdealloc(_param->arg);
- }
-
- rtdealloc(_param);
-}
-
-static int
-_rti_iterator_arg_populate(
- _rti_iterator_arg _param,
- rt_iterator itrset, uint16_t itrcount,
- uint16_t distancex, uint16_t distancey,
- int *allnull, int *allempty
-) {
- int i = 0;
- int hasband = 0;
-
- _param->count = itrcount;
- _param->distance.x = distancex;
- _param->distance.y = distancey;
- _param->dimension.columns = distancex * 2 + 1;
- _param->dimension.rows = distancey * 2 + 1;
-
- /* allocate memory for children */
- _param->raster = rtalloc(sizeof(rt_raster) * itrcount);
- _param->isempty = rtalloc(sizeof(int) * itrcount);
- _param->width = rtalloc(sizeof(int) * itrcount);
- _param->height = rtalloc(sizeof(int) * itrcount);
-
- _param->offset = rtalloc(sizeof(double *) * itrcount);
-
- _param->band.rtband = rtalloc(sizeof(rt_band) * itrcount);
- _param->band.hasnodata = rtalloc(sizeof(int) * itrcount);
- _param->band.isnodata = rtalloc(sizeof(int) * itrcount);
- _param->band.nodataval = rtalloc(sizeof(double) * itrcount);
- _param->band.minval = rtalloc(sizeof(double) * itrcount);
-
- if (
- _param->raster == NULL ||
- _param->isempty == NULL ||
- _param->width == NULL ||
- _param->height == NULL ||
- _param->offset == NULL ||
- _param->band.rtband == NULL ||
- _param->band.hasnodata == NULL ||
- _param->band.isnodata == NULL ||
- _param->band.nodataval == NULL ||
- _param->band.minval == NULL
- ) {
- rterror("_rti_iterator_arg_populate: Could not allocate memory for children of _rti_iterator_arg");
- return 0;
- }
-
- *allnull = 0;
- *allempty = 0;
-
- /*
- check input rasters
- not empty, band # is valid
- copy raster pointers and set flags
- */
- for (i = 0; i < itrcount; i++) {
- /* initialize elements */
- _param->raster[i] = NULL;
- _param->isempty[i] = 0;
- _param->width[i] = 0;
- _param->height[i] = 0;
-
- _param->offset[i] = NULL;
-
- _param->band.rtband[i] = NULL;
- _param->band.hasnodata[i] = 0;
- _param->band.isnodata[i] = 0;
- _param->band.nodataval[i] = 0;
- _param->band.minval[i] = 0;
-
- /* set isempty */
- if (itrset[i].raster == NULL) {
- _param->isempty[i] = 1;
-
- (*allnull)++;
- (*allempty)++;
-
- continue;
- }
- else if (rt_raster_is_empty(itrset[i].raster)) {
- _param->isempty[i] = 1;
-
- (*allempty)++;
-
- continue;
- }
-
- /* check band number */
- hasband = rt_raster_has_band(itrset[i].raster, itrset[i].nband);
- if (!hasband) {
- if (!itrset[i].nbnodata) {
- rterror("_rti_iterator_arg_populate: Band %d not found for raster %d", itrset[i].nband, i);
- return 0;
- }
- else {
- RASTER_DEBUGF(4, "Band %d not found for raster %d. Using NODATA", itrset[i].nband, i);
- }
- }
-
- _param->raster[i] = itrset[i].raster;
- if (hasband) {
- _param->band.rtband[i] = rt_raster_get_band(itrset[i].raster, itrset[i].nband);
- if (_param->band.rtband[i] == NULL) {
- rterror("_rti_iterator_arg_populate: Could not get band %d for raster %d", itrset[i].nband, i);
- return 0;
- }
-
- /* hasnodata */
- _param->band.hasnodata[i] = rt_band_get_hasnodata_flag(_param->band.rtband[i]);
-
- /* hasnodata = TRUE */
- if (_param->band.hasnodata[i]) {
- /* nodataval */
- rt_band_get_nodata(_param->band.rtband[i], &(_param->band.nodataval[i]));
-
- /* isnodata */
- _param->band.isnodata[i] = rt_band_get_isnodata_flag(_param->band.rtband[i]);
- }
- /* hasnodata = FALSE */
- else {
- /* minval */
- _param->band.minval[i] = rt_band_get_min_value(_param->band.rtband[i]);
- }
- }
-
- /* width, height */
- _param->width[i] = rt_raster_get_width(_param->raster[i]);
- _param->height[i] = rt_raster_get_height(_param->raster[i]);
-
- /* init offset */
- _param->offset[i] = rtalloc(sizeof(double) * 2);
- if (_param->offset[i] == NULL) {
- rterror("_rti_iterator_arg_populate: Could not allocate memory for offsets");
- return 0;
- }
- }
-
- return 1;
-}
-
-static int
-_rti_iterator_arg_empty_init(_rti_iterator_arg _param) {
- int x = 0;
- int y = 0;
-
- _param->empty.values = rtalloc(sizeof(double *) * _param->dimension.rows);
- _param->empty.nodata = rtalloc(sizeof(int *) * _param->dimension.rows);
- if (_param->empty.values == NULL || _param->empty.nodata == NULL) {
- rterror("_rti_iterator_arg_empty_init: Could not allocate memory for empty values and NODATA");
- return 0;
- }
-
- for (y = 0; y < _param->dimension.rows; y++) {
- _param->empty.values[y] = rtalloc(sizeof(double) * _param->dimension.columns);
- _param->empty.nodata[y] = rtalloc(sizeof(int) * _param->dimension.columns);
-
- if (_param->empty.values[y] == NULL || _param->empty.nodata[y] == NULL) {
- rterror("_rti_iterator_arg_empty_init: Could not allocate memory for elements of empty values and NODATA");
- return 0;
- }
-
- for (x = 0; x < _param->dimension.columns; x++) {
- _param->empty.values[y][x] = 0;
- _param->empty.nodata[y][x] = 1;
- }
- }
-
- return 1;
-}
-
-static int
-_rti_iterator_arg_callback_init(_rti_iterator_arg _param) {
- int i = 0;
-
- _param->arg = rtalloc(sizeof(struct rt_iterator_arg_t));
- if (_param->arg == NULL) {
- rterror("_rti_iterator_arg_callback_init: Could not allocate memory for rt_iterator_arg");
- return 0;
- }
-
- _param->arg->values = NULL;
- _param->arg->nodata = NULL;
- _param->arg->src_pixel = NULL;
-
- /* initialize argument components */
- _param->arg->values = rtalloc(sizeof(double **) * _param->count);
- _param->arg->nodata = rtalloc(sizeof(int **) * _param->count);
- _param->arg->src_pixel = rtalloc(sizeof(int *) * _param->count);
- if (_param->arg->values == NULL || _param->arg->nodata == NULL || _param->arg->src_pixel == NULL) {
- rterror("_rti_iterator_arg_callback_init: Could not allocate memory for element of rt_iterator_arg");
- return 0;
- }
- memset(_param->arg->values, 0, sizeof(double **) * _param->count);
- memset(_param->arg->nodata, 0, sizeof(int **) * _param->count);
-
- /* initialize pos */
- for (i = 0; i < _param->count; i++) {
-
- _param->arg->src_pixel[i] = rtalloc(sizeof(int) * 2);
- if (_param->arg->src_pixel[i] == NULL) {
- rterror("_rti_iterator_arg_callback_init: Could not allocate memory for position elements of rt_iterator_arg");
- return 0;
- }
- memset(_param->arg->src_pixel[i], 0, sizeof(int) * 2);
- }
-
- _param->arg->rasters = _param->count;
- _param->arg->rows = _param->dimension.rows;
- _param->arg->columns = _param->dimension.columns;
-
- _param->arg->dst_pixel[0] = 0;
- _param->arg->dst_pixel[1] = 0;
-
- return 1;
-}
-
-static void
-_rti_iterator_arg_callback_clean(_rti_iterator_arg _param) {
- int i = 0;
- int y = 0;
-
- for (i = 0; i < _param->count; i++) {
- RASTER_DEBUGF(5, "empty at @ %p", _param->empty.values);
- RASTER_DEBUGF(5, "values at @ %p", _param->arg->values[i]);
-
- if (_param->arg->values[i] == _param->empty.values) {
- _param->arg->values[i] = NULL;
- _param->arg->nodata[i] = NULL;
-
- continue;
- }
-
- for (y = 0; y < _param->dimension.rows; y++) {
- rtdealloc(_param->arg->values[i][y]);
- rtdealloc(_param->arg->nodata[i][y]);
- }
-
- rtdealloc(_param->arg->values[i]);
- rtdealloc(_param->arg->nodata[i]);
-
- _param->arg->values[i] = NULL;
- _param->arg->nodata[i] = NULL;
- }
-}
-
-/**
- * n-raster iterator.
- * The raster returned should be freed by the caller
- *
- * @param itrset : set of rt_iterator objects.
- * @param itrcount : number of objects in itrset.
- * @param extenttype : type of extent for the output raster.
- * @param customextent : raster specifying custom extent.
- * is only used if extenttype is ET_CUSTOM.
- * @param pixtype : the desired pixel type of the output raster's band.
- * @param hasnodata : indicates if the band has nodata value
- * @param nodataval : the nodata value, will be appropriately
- * truncated to fit the pixtype size.
- * @param distancex : the number of pixels around the specified pixel
- * along the X axis
- * @param distancey : the number of pixels around the specified pixel
- * along the Y axis
- * @param userarg : pointer to any argument that is passed as-is to callback.
- * @param callback : callback function for actual processing of pixel values.
- * @param *rtnraster : return one band raster from iterator process
- *
- * The callback function _must_ have the following signature.
- *
- * int FNAME(rt_iterator_arg arg, void *userarg, double *value, int *nodata)
- *
- * The callback function _must_ return zero (error) or non-zero (success)
- * indicating whether the function ran successfully.
- * The parameters passed to the callback function are as follows.
- *
- * - rt_iterator_arg arg: struct containing pixel values, NODATA flags and metadata
- * - void *userarg: NULL or calling function provides to rt_raster_iterator() for use by callback function
- * - double *value: value of pixel to be burned by rt_raster_iterator()
- * - int *nodata: flag (0 or 1) indicating that pixel to be burned is NODATA
- *
- * @return ES_NONE on success, ES_ERROR on error
- */
-rt_errorstate
-rt_raster_iterator(
- rt_iterator itrset, uint16_t itrcount,
- rt_extenttype extenttype, rt_raster customextent,
- rt_pixtype pixtype,
- uint8_t hasnodata, double nodataval,
- uint16_t distancex, uint16_t distancey,
- void *userarg,
- int (*callback)(
- rt_iterator_arg arg,
- void *userarg,
- double *value,
- int *nodata
- ),
- rt_raster *rtnraster
-) {
- /* output raster */
- rt_raster rtnrast = NULL;
- /* output raster's band */
- rt_band rtnband = NULL;
-
- /* working raster */
- rt_raster rast = NULL;
-
- _rti_iterator_arg _param = NULL;
- int allnull = 0;
- int allempty = 0;
- int aligned = 0;
- double offset[4] = {0.};
- rt_pixel npixels;
-
- int i = 0;
- int status = 0;
- int inextent = 0;
- int x = 0;
- int y = 0;
- int _x = 0;
- int _y = 0;
-
- int _width = 0;
- int _height = 0;
-
- double minval;
- double value;
- int isnodata;
- int nodata;
-
- RASTER_DEBUG(3, "Starting...");
-
- assert(itrset != NULL && itrcount > 0);
- assert(rtnraster != NULL);
-
- /* init rtnraster to NULL */
- *rtnraster = NULL;
-
- /* check that callback function is not NULL */
- if (callback == NULL) {
- rterror("rt_raster_iterator: Callback function not provided");
- return ES_ERROR;
- }
-
- /* check that custom extent is provided if extenttype = ET_CUSTOM */
- if (extenttype == ET_CUSTOM && rt_raster_is_empty(customextent)) {
- rterror("rt_raster_iterator: Custom extent cannot be empty if extent type is ET_CUSTOM");
- return ES_ERROR;
- }
-
- /* check that pixtype != PT_END */
- if (pixtype == PT_END) {
- rterror("rt_raster_iterator: Pixel type cannot be PT_END");
- return ES_ERROR;
- }
-
- /* initialize _param */
- if ((_param = _rti_iterator_arg_init()) == NULL) {
- rterror("rt_raster_iterator: Could not initialize internal variables");
- return ES_ERROR;
- }
-
- /* fill _param */
- if (!_rti_iterator_arg_populate(_param, itrset, itrcount, distancex, distancey, &allnull, &allempty)) {
- rterror("rt_raster_iterator: Could not populate for internal variables");
- _rti_iterator_arg_destroy(_param);
- return ES_ERROR;
- }
-
- /* shortcut if all null, return NULL */
- if (allnull == itrcount) {
- RASTER_DEBUG(3, "all rasters are NULL, returning NULL");
-
- _rti_iterator_arg_destroy(_param);
-
- return ES_NONE;
- }
- /* shortcut if all empty, return empty raster */
- else if (allempty == itrcount) {
- RASTER_DEBUG(3, "all rasters are empty, returning empty raster");
-
- _rti_iterator_arg_destroy(_param);
-
- rtnrast = rt_raster_new(0, 0);
- if (rtnrast == NULL) {
- rterror("rt_raster_iterator: Could not create empty raster");
- return ES_ERROR;
- }
- rt_raster_set_scale(rtnrast, 0, 0);
-
- *rtnraster = rtnrast;
- return ES_NONE;
- }
-
- /* check that all rasters are aligned */
- RASTER_DEBUG(3, "checking alignment of all rasters");
- rast = NULL;
-
- /* find raster to use as reference */
- /* use custom if provided */
- if (extenttype == ET_CUSTOM) {
- RASTER_DEBUG(4, "using custom extent as reference raster");
- rast = customextent;
- }
- /* use first valid one in _param->raster */
- else {
- for (i = 0; i < itrcount; i++) {
- if (!_param->isempty[i]) {
- RASTER_DEBUGF(4, "using raster at index %d as reference raster", i);
- rast = _param->raster[i];
- break;
- }
- }
- }
-
- /* no rasters found, SHOULD NEVER BE HERE! */
- if (rast == NULL) {
- rterror("rt_raster_iterator: Could not find reference raster to use for alignment tests");
-
- _rti_iterator_arg_destroy(_param);
-
- return ES_ERROR;
- }
-
- do {
- aligned = 1;
-
- /* check custom first if set. also skip if rasters are the same */
- if (extenttype == ET_CUSTOM && rast != customextent) {
- if (rt_raster_same_alignment(rast, customextent, &aligned, NULL) != ES_NONE) {
- rterror("rt_raster_iterator: Could not test for alignment between reference raster and custom extent");
-
- _rti_iterator_arg_destroy(_param);
-
- return ES_ERROR;
- }
-
- RASTER_DEBUGF(5, "custom extent alignment: %d", aligned);
- if (!aligned)
- break;
- }
-
- for (i = 0; i < itrcount; i++) {
- /* skip NULL rasters and if rasters are the same */
- if (_param->isempty[i] || rast == _param->raster[i])
- continue;
-
- if (rt_raster_same_alignment(rast, _param->raster[i], &aligned, NULL) != ES_NONE) {
- rterror("rt_raster_iterator: Could not test for alignment between reference raster and raster %d", i);
-
- _rti_iterator_arg_destroy(_param);
-
- return ES_ERROR;
- }
- RASTER_DEBUGF(5, "raster at index %d alignment: %d", i, aligned);
-
- /* abort checking since a raster isn't aligned */
- if (!aligned)
- break;
- }
- }
- while (0);
-
- /* not aligned, error */
- if (!aligned) {
- rterror("rt_raster_iterator: The set of rasters provided (custom extent included, if appropriate) do not have the same alignment");
-
- _rti_iterator_arg_destroy(_param);
-
- return ES_ERROR;
- }
-
- /* use extenttype to build output raster (no bands though) */
- i = -1;
- switch (extenttype) {
- case ET_INTERSECTION:
- case ET_UNION:
- /* make copy of first "real" raster */
- rtnrast = rtalloc(sizeof(struct rt_raster_t));
- if (rtnrast == NULL) {
- rterror("rt_raster_iterator: Could not allocate memory for output raster");
-
- _rti_iterator_arg_destroy(_param);
-
- return ES_ERROR;
- }
-
- for (i = 0; i < itrcount; i++) {
- if (!_param->isempty[i]) {
- memcpy(rtnrast, _param->raster[i], sizeof(struct rt_raster_serialized_t));
- break;
- }
- }
- rtnrast->numBands = 0;
- rtnrast->bands = NULL;
-
- /* get extent of output raster */
- rast = NULL;
- for (i = i + 1; i < itrcount; i++) {
- if (_param->isempty[i])
- continue;
-
- status = rt_raster_from_two_rasters(rtnrast, _param->raster[i], extenttype, &rast, NULL);
- rtdealloc(rtnrast);
-
- if (rast == NULL || status != ES_NONE) {
- rterror("rt_raster_iterator: Could not compute %s extent of rasters",
- extenttype == ET_UNION ? "union" : "intersection"
- );
-
- _rti_iterator_arg_destroy(_param);
-
- return ES_ERROR;
- }
- else if (rt_raster_is_empty(rast)) {
- rtinfo("rt_raster_iterator: Computed raster for %s extent is empty",
- extenttype == ET_UNION ? "union" : "intersection"
- );
-
- _rti_iterator_arg_destroy(_param);
-
- *rtnraster = rast;
- return ES_NONE;
- }
-
- rtnrast = rast;
- rast = NULL;
- }
-
- break;
- /*
- first, second and last have similar checks
- and continue into custom
- */
- case ET_FIRST:
- i = 0;
- case ET_SECOND:
- if (i < 0) {
- if (itrcount < 2)
- i = 0;
- else
- i = 1;
- }
- case ET_LAST:
- if (i < 0) i = itrcount - 1;
-
- /* input raster is null, return NULL */
- if (_param->raster[i] == NULL) {
- RASTER_DEBUGF(3, "returning NULL as %s raster is NULL and extent type is ET_%s",
- (i == 0 ? "first" : (i == 1 ? "second" : "last")),
- (i == 0 ? "FIRST" : (i == 1 ? "SECOND" : "LAST"))
- );
-
- _rti_iterator_arg_destroy(_param);
-
- return ES_NONE;
- }
- /* input raster is empty, return empty raster */
- else if (_param->isempty[i]) {
- RASTER_DEBUGF(3, "returning empty raster as %s raster is empty and extent type is ET_%s",
- (i == 0 ? "first" : (i == 1 ? "second" : "last")),
- (i == 0 ? "FIRST" : (i == 1 ? "SECOND" : "LAST"))
- );
-
- _rti_iterator_arg_destroy(_param);
-
- rtnrast = rt_raster_new(0, 0);
- if (rtnrast == NULL) {
- rterror("rt_raster_iterator: Could not create empty raster");
- return ES_ERROR;
- }
- rt_raster_set_scale(rtnrast, 0, 0);
-
- *rtnraster = rtnrast;
- return ES_NONE;
- }
- /* copy the custom extent raster */
- case ET_CUSTOM:
- rtnrast = rtalloc(sizeof(struct rt_raster_t));
- if (rtnrast == NULL) {
- rterror("rt_raster_iterator: Could not allocate memory for output raster");
-
- _rti_iterator_arg_destroy(_param);
-
- return ES_ERROR;
- }
-
- switch (extenttype) {
- case ET_CUSTOM:
- memcpy(rtnrast, customextent, sizeof(struct rt_raster_serialized_t));
- break;
- /* first, second, last */
- default:
- memcpy(rtnrast, _param->raster[i], sizeof(struct rt_raster_serialized_t));
- break;
- }
- rtnrast->numBands = 0;
- rtnrast->bands = NULL;
- break;
- }
-
- _width = rt_raster_get_width(rtnrast);
- _height = rt_raster_get_height(rtnrast);
-
- RASTER_DEBUGF(4, "rtnrast (width, height, ulx, uly, scalex, scaley, skewx, skewy, srid) = (%d, %d, %f, %f, %f, %f, %f, %f, %d)",
- _width,
- _height,
- rt_raster_get_x_offset(rtnrast),
- rt_raster_get_y_offset(rtnrast),
- rt_raster_get_x_scale(rtnrast),
- rt_raster_get_y_scale(rtnrast),
- rt_raster_get_x_skew(rtnrast),
- rt_raster_get_y_skew(rtnrast),
- rt_raster_get_srid(rtnrast)
- );
-
- /* init values and NODATA for use with empty rasters */
- if (!_rti_iterator_arg_empty_init(_param)) {
- rterror("rt_raster_iterator: Could not initialize empty values and NODATA");
-
- _rti_iterator_arg_destroy(_param);
- rt_raster_destroy(rtnrast);
-
- return ES_ERROR;
- }
-
- /* create output band */
- if (rt_raster_generate_new_band(
- rtnrast,
- pixtype,
- nodataval,
- hasnodata, nodataval,
- 0
- ) < 0) {
- rterror("rt_raster_iterator: Could not add new band to output raster");
-
- _rti_iterator_arg_destroy(_param);
- rt_raster_destroy(rtnrast);
-
- return ES_ERROR;
- }
-
- /* get output band */
- rtnband = rt_raster_get_band(rtnrast, 0);
- if (rtnband == NULL) {
- rterror("rt_raster_iterator: Could not get new band from output raster");
-
- _rti_iterator_arg_destroy(_param);
- rt_raster_destroy(rtnrast);
-
- return ES_ERROR;
- }
-
- /* output band's minimum value */
- minval = rt_band_get_min_value(rtnband);
-
- /* initialize argument for callback function */
- if (!_rti_iterator_arg_callback_init(_param)) {
- rterror("rt_raster_iterator: Could not initialize callback function argument");
-
- _rti_iterator_arg_destroy(_param);
- rt_band_destroy(rtnband);
- rt_raster_destroy(rtnrast);
-
- return ES_ERROR;
- }
-
- /* fill _param->offset */
- for (i = 0; i < itrcount; i++) {
- if (_param->isempty[i])
- continue;
-
- status = rt_raster_from_two_rasters(rtnrast, _param->raster[i], ET_FIRST, &rast, offset);
- rtdealloc(rast);
- if (status != ES_NONE) {
- rterror("rt_raster_iterator: Could not compute raster offsets");
-
- _rti_iterator_arg_destroy(_param);
- rt_band_destroy(rtnband);
- rt_raster_destroy(rtnrast);
-
- return ES_ERROR;
- }
-
- _param->offset[i][0] = offset[2];
- _param->offset[i][1] = offset[3];
- RASTER_DEBUGF(4, "rast %d offset: %f %f", i, offset[2], offset[3]);
- }
-
- /* loop over each pixel (POI) of output raster */
- /* _x,_y are for output raster */
- /* x,y are for input raster */
- for (_y = 0; _y < _height; _y++) {
- for (_x = 0; _x < _width; _x++) {
- RASTER_DEBUGF(4, "iterating output pixel (x, y) = (%d, %d)", _x, _y);
- _param->arg->dst_pixel[0] = _x;
- _param->arg->dst_pixel[1] = _y;
-
- /* loop through each input raster */
- for (i = 0; i < itrcount; i++) {
- RASTER_DEBUGF(4, "raster %d", i);
-
- /*
- empty raster
- OR band does not exist and flag set to use NODATA
- OR band is NODATA
- */
- if (
- _param->isempty[i] ||
- (_param->band.rtband[i] == NULL && itrset[i].nbnodata) ||
- _param->band.isnodata[i]
- ) {
- RASTER_DEBUG(4, "empty raster, band does not exist or band is NODATA. using empty values and NODATA");
-
- x = _x;
- y = _y;
-
- _param->arg->values[i] = _param->empty.values;
- _param->arg->nodata[i] = _param->empty.nodata;
-
- continue;
- }
-
- /* input raster's X,Y */
- x = _x - (int) _param->offset[i][0];
- y = _y - (int) _param->offset[i][1];
- RASTER_DEBUGF(4, "source pixel (x, y) = (%d, %d)", x, y);
-
- _param->arg->src_pixel[i][0] = x;
- _param->arg->src_pixel[i][1] = y;
-
- /* neighborhood */
- npixels = NULL;
- status = 0;
- if (distancex > 0 && distancey > 0) {
- RASTER_DEBUG(4, "getting neighborhood");
-
- status = rt_band_get_nearest_pixel(
- _param->band.rtband[i],
- x, y,
- distancex, distancey,
- 1,
- &npixels
- );
- if (status < 0) {
- rterror("rt_raster_iterator: Could not get pixel neighborhood");
-
- _rti_iterator_arg_destroy(_param);
- rt_band_destroy(rtnband);
- rt_raster_destroy(rtnrast);
-
- return ES_ERROR;
- }
- }
-
- /* get value of POI */
- /* get pixel's value */
- if (
- (x >= 0 && x < _param->width[i]) &&
- (y >= 0 && y < _param->height[i])
- ) {
- RASTER_DEBUG(4, "getting value of POI");
- if (rt_band_get_pixel(
- _param->band.rtband[i],
- x, y,
- &value,
- &isnodata
- ) != ES_NONE) {
- rterror("rt_raster_iterator: Could not get the pixel value of band");
-
- _rti_iterator_arg_destroy(_param);
- rt_band_destroy(rtnband);
- rt_raster_destroy(rtnrast);
-
- return ES_ERROR;
- }
- inextent = 1;
- }
- /* outside band extent, set to NODATA */
- else {
- RASTER_DEBUG(4, "Outside band extent, setting value to NODATA");
- /* has NODATA, use NODATA */
- if (_param->band.hasnodata[i])
- value = _param->band.nodataval[i];
- /* no NODATA, use min possible value */
- else
- value = _param->band.minval[i];
-
- inextent = 0;
- isnodata = 1;
- }
-
- /* add pixel to neighborhood */
- status++;
- if (status > 1)
- npixels = (rt_pixel) rtrealloc(npixels, sizeof(struct rt_pixel_t) * status);
- else
- npixels = (rt_pixel) rtalloc(sizeof(struct rt_pixel_t));
-
- if (npixels == NULL) {
- rterror("rt_raster_iterator: Could not reallocate memory for neighborhood");
-
- _rti_iterator_arg_destroy(_param);
- rt_band_destroy(rtnband);
- rt_raster_destroy(rtnrast);
-
- return ES_ERROR;
- }
-
- npixels[status - 1].x = x;
- npixels[status - 1].y = y;
- npixels[status - 1].nodata = 1;
- npixels[status - 1].value = value;
-
- /* set nodata flag */
- if ((!_param->band.hasnodata[i] && inextent) || !isnodata) {
- npixels[status - 1].nodata = 0;
- }
- RASTER_DEBUGF(4, "value, nodata: %f, %d", value, npixels[status - 1].nodata);
-
- /* convert set of rt_pixel to 2D array */
- status = rt_pixel_set_to_array(
- npixels, status,
- x, y,
- distancex, distancey,
- &(_param->arg->values[i]),
- &(_param->arg->nodata[i]),
- NULL, NULL
- );
- rtdealloc(npixels);
- if (status != ES_NONE) {
- rterror("rt_raster_iterator: Could not create 2D array of neighborhood");
-
- _rti_iterator_arg_destroy(_param);
- rt_band_destroy(rtnband);
- rt_raster_destroy(rtnrast);
-
- return ES_ERROR;
- }
- }
-
- /* callback */
- RASTER_DEBUG(4, "calling callback function");
- value = 0;
- nodata = 0;
- status = callback(_param->arg, userarg, &value, &nodata);
-
- /* free memory from callback */
- _rti_iterator_arg_callback_clean(_param);
-
- /* handle callback status */
- if (status == 0) {
- rterror("rt_raster_iterator: Callback function returned an error");
-
- _rti_iterator_arg_destroy(_param);
- rt_band_destroy(rtnband);
- rt_raster_destroy(rtnrast);
-
- return ES_ERROR;
- }
-
- /* burn value to pixel */
- status = 0;
- if (!nodata) {
- status = rt_band_set_pixel(rtnband, _x, _y, value, NULL);
- RASTER_DEBUGF(4, "burning pixel (%d, %d) with value: %f", _x, _y, value);
- }
- else if (!hasnodata) {
- status = rt_band_set_pixel(rtnband, _x, _y, minval, NULL);
- RASTER_DEBUGF(4, "burning pixel (%d, %d) with minval: %f", _x, _y, minval);
- }
- else {
- RASTER_DEBUGF(4, "NOT burning pixel (%d, %d)", _x, _y);
- }
- if (status != ES_NONE) {
- rterror("rt_raster_iterator: Could not set pixel value");
-
- _rti_iterator_arg_destroy(_param);
- rt_band_destroy(rtnband);
- rt_raster_destroy(rtnrast);
-
- return ES_ERROR;
- }
- }
- }
-
- /* lots of cleanup */
- _rti_iterator_arg_destroy(_param);
-
- *rtnraster = rtnrast;
- return ES_NONE;
-}
-
-/******************************************************************************
-* rt_raster_perimeter()
-******************************************************************************/
-static rt_errorstate
-_rti_raster_get_band_perimeter(rt_band band, uint16_t *trim) {
- uint16_t width = 0;
- uint16_t height = 0;
- int x = 0;
- int y = 0;
- int offset = 0;
- int done[4] = {0};
- double value = 0;
- int nodata = 0;
-
- assert(band != NULL);
- assert(band->raster != NULL);
- assert(trim != NULL);
-
- memset(trim, 0, sizeof(uint16_t) * 4);
-
- width = rt_band_get_width(band);
- height = rt_band_get_height(band);
-
- /* top */
- for (y = 0; y < height; y++) {
- for (offset = 0; offset < 3; offset++) {
- /* every third pixel */
- for (x = offset; x < width; x += 3) {
- if (rt_band_get_pixel(band, x, y, &value, &nodata) != ES_NONE) {
- rterror("_rti_raster_get_band_perimeter: Could not get band pixel");
- return ES_ERROR;
- }
-
- RASTER_DEBUGF(4, "top (x, y, value, nodata) = (%d, %d, %f, %d)", x, y, value, nodata);
- if (!nodata) {
- trim[0] = y;
- done[0] = 1;
- break;
- }
- }
-
- if (done[0])
- break;
- }
-
- if (done[0])
- break;
- }
-
- /* right */
- for (x = width - 1; x >= 0; x--) {
- for (offset = 0; offset < 3; offset++) {
- /* every third pixel */
- for (y = offset; y < height; y += 3) {
- if (rt_band_get_pixel(band, x, y, &value, &nodata) != ES_NONE) {
- rterror("_rti_raster_get_band_perimeter: Could not get band pixel");
- return ES_ERROR;
- }
-
- RASTER_DEBUGF(4, "right (x, y, value, nodata) = (%d, %d, %f, %d)", x, y, value, nodata);
- if (!nodata) {
- trim[1] = width - (x + 1);
- done[1] = 1;
- break;
- }
- }
-
- if (done[1])
- break;
- }
-
- if (done[1])
- break;
- }
-
- /* bottom */
- for (y = height - 1; y >= 0; y--) {
- for (offset = 0; offset < 3; offset++) {
- /* every third pixel */
- for (x = offset; x < width; x += 3) {
- if (rt_band_get_pixel(band, x, y, &value, &nodata) != ES_NONE) {
- rterror("_rti_raster_get_band_perimeter: Could not get band pixel");
- return ES_ERROR;
- }
-
- RASTER_DEBUGF(4, "bottom (x, y, value, nodata) = (%d, %d, %f, %d)", x, y, value, nodata);
- if (!nodata) {
- trim[2] = height - (y + 1);
- done[2] = 1;
- break;
- }
- }
-
- if (done[2])
- break;
- }
-
- if (done[2])
- break;
- }
-
- /* left */
- for (x = 0; x < width; x++) {
- for (offset = 0; offset < 3; offset++) {
- /* every third pixel */
- for (y = offset; y < height; y += 3) {
- if (rt_band_get_pixel(band, x, y, &value, &nodata) != ES_NONE) {
- rterror("_rti_raster_get_band_perimeter: Could not get band pixel");
- return ES_ERROR;
- }
-
- RASTER_DEBUGF(4, "left (x, , value, nodata) = (%d, %d, %f, %d)", x, y, value, nodata);
- if (!nodata) {
- trim[3] = x;
- done[3] = 1;
- break;
- }
- }
-
- if (done[3])
- break;
- }
-
- if (done[3])
- break;
- }
-
- RASTER_DEBUGF(4, "trim = (%d, %d, %d, %d)",
- trim[0], trim[1], trim[2], trim[3]);
-
- return ES_NONE;
-}
-
-/**
- * Get raster perimeter
- *
- * The perimeter is a 4 vertices (5 to be closed)
- * single ring polygon bearing the raster's rotation and using
- * projection coordinates.
- *
- * @param raster : the raster to get info from
- * @param nband : the band for the perimeter. 0-based
- * value less than zero means all bands
- * @param **perimeter : pointer to perimeter
- *
- * @return ES_NONE if success, ES_ERROR if error
- */
-rt_errorstate rt_raster_get_perimeter(
- rt_raster raster, int nband,
- LWGEOM **perimeter
-) {
- rt_band band = NULL;
- int numband = 0;
- uint16_t *_nband = NULL;
- int i = 0;
- int j = 0;
- uint16_t _trim[4] = {0};
- uint16_t trim[4] = {0}; /* top, right, bottom, left */
- int isset[4] = {0};
- double gt[6] = {0.0};
- int srid = SRID_UNKNOWN;
-
- POINTARRAY *pts = NULL;
- POINT4D p4d;
- POINTARRAY **rings = NULL;
- LWPOLY* poly = NULL;
-
- assert(perimeter != NULL);
-
- *perimeter = NULL;
-
- /* empty raster, no perimeter */
- if (rt_raster_is_empty(raster))
- return ES_NONE;
-
- /* raster metadata */
- srid = rt_raster_get_srid(raster);
- rt_raster_get_geotransform_matrix(raster, gt);
- numband = rt_raster_get_num_bands(raster);
-
- RASTER_DEBUGF(3, "rt_raster_get_perimeter: raster is %dx%d", raster->width, raster->height);
-
- /* nband < 0 means all bands */
- if (nband >= 0) {
- if (nband >= numband) {
- rterror("rt_raster_get_boundary: Band %d not found for raster", nband);
- return ES_ERROR;
- }
-
- numband = 1;
- }
- else
- nband = -1;
-
- RASTER_DEBUGF(3, "rt_raster_get_perimeter: nband, numband = %d, %d", nband, numband);
-
- _nband = rtalloc(sizeof(uint16_t) * numband);
- if (_nband == NULL) {
- rterror("rt_raster_get_boundary: Could not allocate memory for band indices");
- return ES_ERROR;
- }
-
- if (nband < 0) {
- for (i = 0; i < numband; i++)
- _nband[i] = i;
- }
- else
- _nband[0] = nband;
-
- for (i = 0; i < numband; i++) {
- band = rt_raster_get_band(raster, _nband[i]);
- if (band == NULL) {
- rterror("rt_raster_get_boundary: Could not get band at index %d", _nband[i]);
- rtdealloc(_nband);
- return ES_ERROR;
- }
-
- /* band is nodata */
- if (rt_band_get_isnodata_flag(band) != 0)
- continue;
-
- if (_rti_raster_get_band_perimeter(band, trim) != ES_NONE) {
- rterror("rt_raster_get_boundary: Could not get band perimeter");
- rtdealloc(_nband);
- return ES_ERROR;
- }
-
- for (j = 0; j < 4; j++) {
- if (!isset[j] || trim[j] < _trim[j]) {
- _trim[j] = trim[j];
- isset[j] = 1;
- }
- }
- }
-
- /* no longer needed */
- rtdealloc(_nband);
-
- /* check isset, just need to check one element */
- if (!isset[0]) {
- /* return NULL as bands are empty */
- return ES_NONE;
- }
-
- RASTER_DEBUGF(4, "trim = (%d, %d, %d, %d)",
- trim[0], trim[1], trim[2], trim[3]);
-
- /* only one ring */
- rings = (POINTARRAY **) rtalloc(sizeof (POINTARRAY*));
- if (!rings) {
- rterror("rt_raster_get_perimeter: Could not allocate memory for polygon ring");
- return ES_ERROR;
- }
- rings[0] = ptarray_construct(0, 0, 5);
- if (!rings[0]) {
- rterror("rt_raster_get_perimeter: Could not construct point array");
- return ES_ERROR;
- }
- pts = rings[0];
-
- /* Upper-left corner (first and last points) */
- rt_raster_cell_to_geopoint(
- raster,
- _trim[3], _trim[0],
- &p4d.x, &p4d.y,
- gt
- );
- ptarray_set_point4d(pts, 0, &p4d);
- ptarray_set_point4d(pts, 4, &p4d);
-
- /* Upper-right corner (we go clockwise) */
- rt_raster_cell_to_geopoint(
- raster,
- raster->width - _trim[1], _trim[0],
- &p4d.x, &p4d.y,
- gt
- );
- ptarray_set_point4d(pts, 1, &p4d);
-
- /* Lower-right corner */
- rt_raster_cell_to_geopoint(
- raster,
- raster->width - _trim[1], raster->height - _trim[2],
- &p4d.x, &p4d.y,
- gt
- );
- ptarray_set_point4d(pts, 2, &p4d);
-
- /* Lower-left corner */
- rt_raster_cell_to_geopoint(
- raster,
- _trim[3], raster->height - _trim[2],
- &p4d.x, &p4d.y,
- gt
- );
- ptarray_set_point4d(pts, 3, &p4d);
-
- poly = lwpoly_construct(srid, 0, 1, rings);
- *perimeter = lwpoly_as_lwgeom(poly);
-
- return ES_NONE;
-}
-
-/******************************************************************************
-* rt_raster_colormap()
-******************************************************************************/
-
-typedef struct _rti_colormap_arg_t* _rti_colormap_arg;
-struct _rti_colormap_arg_t {
- rt_raster raster;
- rt_band band;
-
- rt_colormap_entry nodataentry;
- int hasnodata;
- double nodataval;
-
- int nexpr;
- rt_reclassexpr *expr;
-
- int npos;
- uint16_t *pos;
-
-};
-
-static _rti_colormap_arg
-_rti_colormap_arg_init(rt_raster raster) {
- _rti_colormap_arg arg = NULL;
-
- arg = rtalloc(sizeof(struct _rti_colormap_arg_t));
- if (arg == NULL) {
- rterror("_rti_colormap_arg_init: Could not allocate memory for _rti_color_arg");
- return NULL;
- }
-
- arg->band = NULL;
- arg->nodataentry = NULL;
- arg->hasnodata = 0;
- arg->nodataval = 0;
-
- if (raster == NULL)
- arg->raster = NULL;
- /* raster provided */
- else {
- arg->raster = rt_raster_clone(raster, 0);
- if (arg->raster == NULL) {
- rterror("_rti_colormap_arg_init: Could not create output raster");
- return NULL;
- }
- }
-
- arg->nexpr = 0;
- arg->expr = NULL;
-
- arg->npos = 0;
- arg->pos = NULL;
-
- return arg;
-}
-
-static void
-_rti_colormap_arg_destroy(_rti_colormap_arg arg) {
- int i = 0;
-
- if (arg->raster != NULL) {
- rt_band band = NULL;
-
- for (i = rt_raster_get_num_bands(arg->raster) - 1; i >= 0; i--) {
- band = rt_raster_get_band(arg->raster, i);
- if (band != NULL)
- rt_band_destroy(band);
- }
-
- rt_raster_destroy(arg->raster);
- }
-
- if (arg->nexpr) {
- for (i = 0; i < arg->nexpr; i++) {
- if (arg->expr[i] != NULL)
- rtdealloc(arg->expr[i]);
- }
- rtdealloc(arg->expr);
- }
-
- if (arg->npos)
- rtdealloc(arg->pos);
-
- rtdealloc(arg);
- arg = NULL;
-}
-
-/**
- * Returns a new raster with up to four 8BUI bands (RGBA) from
- * applying a colormap to the user-specified band of the
- * input raster.
- *
- * @param raster: input raster
- * @param nband: 0-based index of the band to process with colormap
- * @param colormap: rt_colormap object of colormap to apply to band
- *
- * @return new raster or NULL on error
- */
-rt_raster rt_raster_colormap(
- rt_raster raster, int nband,
- rt_colormap colormap
-) {
- _rti_colormap_arg arg = NULL;
- rt_raster rtnraster = NULL;
- rt_band band = NULL;
- int i = 0;
- int j = 0;
- int k = 0;
-
- assert(colormap != NULL);
-
- /* empty raster */
- if (rt_raster_is_empty(raster))
- return NULL;
-
- /* no colormap entries */
- if (colormap->nentry < 1) {
- rterror("rt_raster_colormap: colormap must have at least one entry");
- return NULL;
- }
-
- /* nband is valid */
- if (!rt_raster_has_band(raster, nband)) {
- rterror("rt_raster_colormap: raster has no band at index %d", nband);
- return NULL;
- }
-
- band = rt_raster_get_band(raster, nband);
- if (band == NULL) {
- rterror("rt_raster_colormap: Could not get band at index %d", nband);
- return NULL;
- }
-
- /* init internal variables */
- arg = _rti_colormap_arg_init(raster);
- if (arg == NULL) {
- rterror("rt_raster_colormap: Could not initialize internal variables");
- return NULL;
- }
-
- /* handle NODATA */
- if (rt_band_get_hasnodata_flag(band)) {
- arg->hasnodata = 1;
- rt_band_get_nodata(band, &(arg->nodataval));
- }
-
- /* # of colors */
- if (colormap->ncolor < 1) {
- rterror("rt_raster_colormap: At least one color must be provided");
- _rti_colormap_arg_destroy(arg);
- return NULL;
- }
- else if (colormap->ncolor > 4) {
- rtinfo("More than four colors indicated. Using only the first four colors");
- colormap->ncolor = 4;
- }
-
- /* find non-NODATA entries */
- arg->npos = 0;
- arg->pos = rtalloc(sizeof(uint16_t) * colormap->nentry);
- if (arg->pos == NULL) {
- rterror("rt_raster_colormap: Could not allocate memory for valid entries");
- _rti_colormap_arg_destroy(arg);
- return NULL;
- }
- for (i = 0, j = 0; i < colormap->nentry; i++) {
- /* special handling for NODATA entries */
- if (colormap->entry[i].isnodata) {
- /* first NODATA entry found, use it */
- if (arg->nodataentry == NULL)
- arg->nodataentry = &(colormap->entry[i]);
- else
- rtwarn("More than one colormap entry found for NODATA value. Only using first NOTDATA entry");
-
- continue;
- }
-
- (arg->npos)++;
- arg->pos[j++] = i;
- }
-
- /* INTERPOLATE and only one non-NODATA entry */
- if (colormap->method == CM_INTERPOLATE && arg->npos < 2) {
- rtinfo("Method INTERPOLATE requires at least two non-NODATA colormap entries. Using NEAREST instead");
- colormap->method = CM_NEAREST;
- }
-
- /* NODATA entry but band has no NODATA value */
- if (!arg->hasnodata && arg->nodataentry != NULL) {
- rtinfo("Band at index %d has no NODATA value. Ignoring NODATA entry", nband);
- arg->nodataentry = NULL;
- }
-
- /* allocate expr */
- arg->nexpr = arg->npos;
-
- /* INTERPOLATE needs one less than the number of entries */
- if (colormap->method == CM_INTERPOLATE)
- arg->nexpr -= 1;
- /* EXACT requires a no matching expression */
- else if (colormap->method == CM_EXACT)
- arg->nexpr += 1;
-
- /* NODATA entry exists, add expression */
- if (arg->nodataentry != NULL)
- arg->nexpr += 1;
- arg->expr = rtalloc(sizeof(rt_reclassexpr) * arg->nexpr);
- if (arg->expr == NULL) {
- rterror("rt_raster_colormap: Could not allocate memory for reclass expressions");
- _rti_colormap_arg_destroy(arg);
- return NULL;
- }
- RASTER_DEBUGF(4, "nexpr = %d", arg->nexpr);
- RASTER_DEBUGF(4, "expr @ %p", arg->expr);
-
- for (i = 0; i < arg->nexpr; i++) {
- arg->expr[i] = rtalloc(sizeof(struct rt_reclassexpr_t));
- if (arg->expr[i] == NULL) {
- rterror("rt_raster_colormap: Could not allocate memory for reclass expression");
- _rti_colormap_arg_destroy(arg);
- return NULL;
- }
- }
-
- /* reclassify bands */
- /* by # of colors */
- for (i = 0; i < colormap->ncolor; i++) {
- k = 0;
-
- /* handle NODATA entry first */
- if (arg->nodataentry != NULL) {
- arg->expr[k]->src.min = arg->nodataentry->value;
- arg->expr[k]->src.max = arg->nodataentry->value;
- arg->expr[k]->src.inc_min = 1;
- arg->expr[k]->src.inc_max = 1;
- arg->expr[k]->src.exc_min = 0;
- arg->expr[k]->src.exc_max = 0;
-
- arg->expr[k]->dst.min = arg->nodataentry->color[i];
- arg->expr[k]->dst.max = arg->nodataentry->color[i];
-
- arg->expr[k]->dst.inc_min = 1;
- arg->expr[k]->dst.inc_max = 1;
- arg->expr[k]->dst.exc_min = 0;
- arg->expr[k]->dst.exc_max = 0;
-
- RASTER_DEBUGF(4, "NODATA expr[%d]->src (min, max, in, ix, en, ex) = (%f, %f, %d, %d, %d, %d)",
- k,
- arg->expr[k]->src.min,
- arg->expr[k]->src.max,
- arg->expr[k]->src.inc_min,
- arg->expr[k]->src.inc_max,
- arg->expr[k]->src.exc_min,
- arg->expr[k]->src.exc_max
- );
- RASTER_DEBUGF(4, "NODATA expr[%d]->dst (min, max, in, ix, en, ex) = (%f, %f, %d, %d, %d, %d)",
- k,
- arg->expr[k]->dst.min,
- arg->expr[k]->dst.max,
- arg->expr[k]->dst.inc_min,
- arg->expr[k]->dst.inc_max,
- arg->expr[k]->dst.exc_min,
- arg->expr[k]->dst.exc_max
- );
-
- k++;
- }
-
- /* by non-NODATA entry */
- for (j = 0; j < arg->npos; j++) {
- if (colormap->method == CM_INTERPOLATE) {
- if (j == arg->npos - 1)
- continue;
-
- arg->expr[k]->src.min = colormap->entry[arg->pos[j + 1]].value;
- arg->expr[k]->src.inc_min = 1;
- arg->expr[k]->src.exc_min = 0;
-
- arg->expr[k]->src.max = colormap->entry[arg->pos[j]].value;
- arg->expr[k]->src.inc_max = 1;
- arg->expr[k]->src.exc_max = 0;
-
- arg->expr[k]->dst.min = colormap->entry[arg->pos[j + 1]].color[i];
- arg->expr[k]->dst.max = colormap->entry[arg->pos[j]].color[i];
-
- arg->expr[k]->dst.inc_min = 1;
- arg->expr[k]->dst.exc_min = 0;
-
- arg->expr[k]->dst.inc_max = 1;
- arg->expr[k]->dst.exc_max = 0;
- }
- else if (colormap->method == CM_NEAREST) {
-
- /* NOT last entry */
- if (j != arg->npos - 1) {
- arg->expr[k]->src.min = ((colormap->entry[arg->pos[j]].value - colormap->entry[arg->pos[j + 1]].value) / 2.) + colormap->entry[arg->pos[j + 1]].value;
- arg->expr[k]->src.inc_min = 0;
- arg->expr[k]->src.exc_min = 0;
- }
- /* last entry */
- else {
- arg->expr[k]->src.min = colormap->entry[arg->pos[j]].value;
- arg->expr[k]->src.inc_min = 1;
- arg->expr[k]->src.exc_min = 1;
- }
-
- /* NOT first entry */
- if (j > 0) {
- arg->expr[k]->src.max = arg->expr[k - 1]->src.min;
- arg->expr[k]->src.inc_max = 1;
- arg->expr[k]->src.exc_max = 0;
- }
- /* first entry */
- else {
- arg->expr[k]->src.max = colormap->entry[arg->pos[j]].value;
- arg->expr[k]->src.inc_max = 1;
- arg->expr[k]->src.exc_max = 1;
- }
-
- arg->expr[k]->dst.min = colormap->entry[arg->pos[j]].color[i];
- arg->expr[k]->dst.inc_min = 1;
- arg->expr[k]->dst.exc_min = 0;
-
- arg->expr[k]->dst.max = colormap->entry[arg->pos[j]].color[i];
- arg->expr[k]->dst.inc_max = 1;
- arg->expr[k]->dst.exc_max = 0;
- }
- else if (colormap->method == CM_EXACT) {
- arg->expr[k]->src.min = colormap->entry[arg->pos[j]].value;
- arg->expr[k]->src.inc_min = 1;
- arg->expr[k]->src.exc_min = 0;
-
- arg->expr[k]->src.max = colormap->entry[arg->pos[j]].value;
- arg->expr[k]->src.inc_max = 1;
- arg->expr[k]->src.exc_max = 0;
-
- arg->expr[k]->dst.min = colormap->entry[arg->pos[j]].color[i];
- arg->expr[k]->dst.inc_min = 1;
- arg->expr[k]->dst.exc_min = 0;
-
- arg->expr[k]->dst.max = colormap->entry[arg->pos[j]].color[i];
- arg->expr[k]->dst.inc_max = 1;
- arg->expr[k]->dst.exc_max = 0;
- }
-
- RASTER_DEBUGF(4, "expr[%d]->src (min, max, in, ix, en, ex) = (%f, %f, %d, %d, %d, %d)",
- k,
- arg->expr[k]->src.min,
- arg->expr[k]->src.max,
- arg->expr[k]->src.inc_min,
- arg->expr[k]->src.inc_max,
- arg->expr[k]->src.exc_min,
- arg->expr[k]->src.exc_max
- );
-
- RASTER_DEBUGF(4, "expr[%d]->dst (min, max, in, ix, en, ex) = (%f, %f, %d, %d, %d, %d)",
- k,
- arg->expr[k]->dst.min,
- arg->expr[k]->dst.max,
- arg->expr[k]->dst.inc_min,
- arg->expr[k]->dst.inc_max,
- arg->expr[k]->dst.exc_min,
- arg->expr[k]->dst.exc_max
- );
-
- k++;
- }
-
- /* EXACT has one last expression for catching all uncaught values */
- if (colormap->method == CM_EXACT) {
- arg->expr[k]->src.min = 0;
- arg->expr[k]->src.inc_min = 1;
- arg->expr[k]->src.exc_min = 1;
-
- arg->expr[k]->src.max = 0;
- arg->expr[k]->src.inc_max = 1;
- arg->expr[k]->src.exc_max = 1;
-
- arg->expr[k]->dst.min = 0;
- arg->expr[k]->dst.inc_min = 1;
- arg->expr[k]->dst.exc_min = 0;
-
- arg->expr[k]->dst.max = 0;
- arg->expr[k]->dst.inc_max = 1;
- arg->expr[k]->dst.exc_max = 0;
-
- RASTER_DEBUGF(4, "expr[%d]->src (min, max, in, ix, en, ex) = (%f, %f, %d, %d, %d, %d)",
- k,
- arg->expr[k]->src.min,
- arg->expr[k]->src.max,
- arg->expr[k]->src.inc_min,
- arg->expr[k]->src.inc_max,
- arg->expr[k]->src.exc_min,
- arg->expr[k]->src.exc_max
- );
-
- RASTER_DEBUGF(4, "expr[%d]->dst (min, max, in, ix, en, ex) = (%f, %f, %d, %d, %d, %d)",
- k,
- arg->expr[k]->dst.min,
- arg->expr[k]->dst.max,
- arg->expr[k]->dst.inc_min,
- arg->expr[k]->dst.inc_max,
- arg->expr[k]->dst.exc_min,
- arg->expr[k]->dst.exc_max
- );
-
- k++;
- }
-
- /* call rt_band_reclass */
- arg->band = rt_band_reclass(band, PT_8BUI, 0, 0, arg->expr, arg->nexpr);
- if (arg->band == NULL) {
- rterror("rt_raster_colormap: Could not reclassify band");
- _rti_colormap_arg_destroy(arg);
- return NULL;
- }
-
- /* add reclassified band to raster */
- if (rt_raster_add_band(arg->raster, arg->band, rt_raster_get_num_bands(arg->raster)) < 0) {
- rterror("rt_raster_colormap: Could not add reclassified band to output raster");
- _rti_colormap_arg_destroy(arg);
- return NULL;
- }
- }
-
- rtnraster = arg->raster;
- arg->raster = NULL;
- _rti_colormap_arg_destroy(arg);
-
- return rtnraster;
-}
--- /dev/null
+/*
+ * $Id$
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "librtcore.h"
+#include "librtcore_internal.h"
+
+#include "gdal_vrt.h"
+
+/**
+ * Create an in-db rt_band with no data
+ *
+ * @param width : number of pixel columns
+ * @param height : number of pixel rows
+ * @param pixtype : pixel type for the band
+ * @param hasnodata : indicates if the band has nodata value
+ * @param nodataval : the nodata value, will be appropriately
+ * truncated to fit the pixtype size.
+ * @param data : pointer to actual band data, required to
+ * be aligned accordingly to
+ * rt_pixtype_aligment(pixtype) and big enough
+ * to hold raster width*height values.
+ * Data will NOT be copied, ownership is left
+ * to caller which is responsible to keep it
+ * allocated for the whole lifetime of the returned
+ * rt_band.
+ *
+ * @return an rt_band, or 0 on failure
+ */
+rt_band
+rt_band_new_inline(
+ uint16_t width, uint16_t height,
+ rt_pixtype pixtype,
+ uint32_t hasnodata, double nodataval,
+ uint8_t* data
+) {
+ rt_band band = NULL;
+
+ assert(NULL != data);
+
+ band = rtalloc(sizeof(struct rt_band_t));
+ if (band == NULL) {
+ rterror("rt_band_new_inline: Out of memory allocating rt_band");
+ return NULL;
+ }
+
+ RASTER_DEBUGF(3, "Created rt_band @ %p with pixtype %s", band, rt_pixtype_name(pixtype));
+
+ band->pixtype = pixtype;
+ band->offline = 0;
+ band->width = width;
+ band->height = height;
+ band->hasnodata = hasnodata ? 1 : 0;
+ band->isnodata = FALSE; /* we don't know what is in data, so must be FALSE */
+ band->nodataval = 0;
+ band->data.mem = data;
+ band->ownsdata = 0; /* we do NOT own this data!!! */
+ band->raster = NULL;
+
+ RASTER_DEBUGF(3, "Created rt_band with dimensions %d x %d", band->width, band->height);
+
+ /* properly set nodataval as it may need to be constrained to the data type */
+ if (hasnodata && rt_band_set_nodata(band, nodataval, NULL) != ES_NONE) {
+ rterror("rt_band_new_inline: Could not set NODATA value");
+ rt_band_destroy(band);
+ return NULL;
+ }
+
+ return band;
+}
+
+/**
+ * Create an out-db rt_band
+ *
+ * @param width : number of pixel columns
+ * @param height : number of pixel rows
+ * @param pixtype : pixel type for the band
+ * @param hasnodata : indicates if the band has nodata value
+ * @param nodataval : the nodata value, will be appropriately
+ * truncated to fit the pixtype size.
+ * @param bandNum : 0-based band number in the external file
+ * to associate this band with.
+ * @param path : NULL-terminated path string pointing to the file
+ * containing band data. The string will NOT be
+ * copied, ownership is left to caller which is
+ * responsible to keep it allocated for the whole
+ * lifetime of the returned rt_band.
+ *
+ * @return an rt_band, or 0 on failure
+ */
+rt_band
+rt_band_new_offline(
+ uint16_t width, uint16_t height,
+ rt_pixtype pixtype,
+ uint32_t hasnodata, double nodataval,
+ uint8_t bandNum, const char* path
+) {
+ rt_band band = NULL;
+ int pathlen = 0;
+
+ assert(NULL != path);
+
+ band = rtalloc(sizeof(struct rt_band_t));
+ if (band == NULL) {
+ rterror("rt_band_new_offline: Out of memory allocating rt_band");
+ return NULL;
+ }
+
+ RASTER_DEBUGF(3, "Created rt_band @ %p with pixtype %s",
+ band, rt_pixtype_name(pixtype)
+ );
+
+ band->pixtype = pixtype;
+ band->offline = 1;
+ band->width = width;
+ band->height = height;
+ band->hasnodata = hasnodata ? 1 : 0;
+ band->nodataval = 0;
+ band->isnodata = FALSE; /* we don't know if the offline band is NODATA */
+ band->ownsdata = 0; /* offline, flag is useless as all offline data cache is owned internally */
+ band->raster = NULL;
+
+ /* properly set nodataval as it may need to be constrained to the data type */
+ if (hasnodata && rt_band_set_nodata(band, nodataval, NULL) != ES_NONE) {
+ rterror("rt_band_new_offline: Could not set NODATA value");
+ rt_band_destroy(band);
+ return NULL;
+ }
+
+ band->data.offline.bandNum = bandNum;
+
+ /* memory for data.offline.path is managed internally */
+ pathlen = strlen(path);
+ band->data.offline.path = rtalloc(sizeof(char) * (pathlen + 1));
+ if (band->data.offline.path == NULL) {
+ rterror("rt_band_new_offline: Out of memory allocating offline path");
+ rt_band_destroy(band);
+ return NULL;
+ }
+ memcpy(band->data.offline.path, path, pathlen);
+ band->data.offline.path[pathlen] = '\0';
+
+ band->data.offline.mem = NULL;
+
+ return band;
+}
+
+/**
+ * Create a new band duplicated from source band. Memory is allocated
+ * for band path (if band is offline) or band data (if band is online).
+ * The caller is responsible for freeing the memory when the returned
+ * rt_band is destroyed.
+ *
+ * @param : the band to copy
+ *
+ * @return an rt_band or NULL on failure
+ */
+rt_band
+rt_band_duplicate(rt_band band) {
+ rt_band rtn = NULL;
+
+ assert(band != NULL);
+
+ /* offline */
+ if (band->offline) {
+ rtn = rt_band_new_offline(
+ band->width, band->height,
+ band->pixtype,
+ band->hasnodata, band->nodataval,
+ band->data.offline.bandNum, (const char *) band->data.offline.path
+ );
+ }
+ /* online */
+ else {
+ uint8_t *data = NULL;
+ data = rtalloc(rt_pixtype_size(band->pixtype) * band->width * band->height);
+ if (data == NULL) {
+ rterror("rt_band_duplicate: Out of memory allocating online band data");
+ return NULL;
+ }
+ memcpy(data, band->data.mem, rt_pixtype_size(band->pixtype) * band->width * band->height);
+
+ rtn = rt_band_new_inline(
+ band->width, band->height,
+ band->pixtype,
+ band->hasnodata, band->nodataval,
+ data
+ );
+ rt_band_set_ownsdata_flag(rtn, 1); /* we DO own this data!!! */
+ }
+
+ if (rtn == NULL) {
+ rterror("rt_band_duplicate: Could not copy band");
+ return NULL;
+ }
+
+ return rtn;
+}
+
+int
+rt_band_is_offline(rt_band band) {
+
+ assert(NULL != band);
+
+
+ return band->offline ? 1 : 0;
+}
+
+/**
+ * Destroy a raster band
+ *
+ * @param band : the band to destroy
+ */
+void
+rt_band_destroy(rt_band band) {
+ if (band == NULL)
+ return;
+
+ RASTER_DEBUGF(3, "Destroying rt_band @ %p", band);
+
+ /* offline band */
+ if (band->offline) {
+ /* memory cache */
+ if (band->data.offline.mem != NULL)
+ rtdealloc(band->data.offline.mem);
+ /* offline file path */
+ if (band->data.offline.path != NULL)
+ rtdealloc(band->data.offline.path);
+ }
+ /* inline band and band owns the data */
+ else if (band->data.mem != NULL && band->ownsdata)
+ rtdealloc(band->data.mem);
+
+ rtdealloc(band);
+}
+
+const char*
+rt_band_get_ext_path(rt_band band) {
+
+ assert(NULL != band);
+
+
+ if (!band->offline) {
+ RASTER_DEBUG(3, "rt_band_get_ext_path: Band is not offline");
+ return NULL;
+ }
+ return band->data.offline.path;
+}
+
+rt_errorstate
+rt_band_get_ext_band_num(rt_band band, uint8_t *bandnum) {
+ assert(NULL != band);
+ assert(NULL != bandnum);
+
+ *bandnum = 0;
+
+ if (!band->offline) {
+ RASTER_DEBUG(3, "rt_band_get_ext_band_num: Band is not offline");
+ return ES_ERROR;
+ }
+
+ *bandnum = band->data.offline.bandNum;
+
+ return ES_NONE;
+}
+
+/**
+ * Get pointer to raster band data
+ *
+ * @param band : the band who's data to get
+ *
+ * @return pointer to band data or NULL if error
+ */
+void *
+rt_band_get_data(rt_band band) {
+ assert(NULL != band);
+
+ if (band->offline) {
+ if (band->data.offline.mem != NULL)
+ return band->data.offline.mem;
+
+ if (rt_band_load_offline_data(band) != ES_NONE)
+ return NULL;
+ else
+ return band->data.offline.mem;
+ }
+ else
+ return band->data.mem;
+}
+
+/**
+ * Load offline band's data. Loaded data is internally owned
+ * and should not be released by the caller. Data will be
+ * released when band is destroyed with rt_band_destroy().
+ *
+ * @param band : the band who's data to get
+ *
+ * @return ES_NONE if success, ES_ERROR if failure
+ */
+rt_errorstate
+rt_band_load_offline_data(rt_band band) {
+ GDALDatasetH hdsSrc = NULL;
+ int nband = 0;
+ VRTDatasetH hdsDst = NULL;
+ VRTSourcedRasterBandH hbandDst = NULL;
+ double gt[6] = {0.};
+ double ogt[6] = {0};
+ double offset[2] = {0};
+
+ rt_raster _rast = NULL;
+ rt_band _band = NULL;
+ int aligned = 0;
+ int err = ES_NONE;
+
+ assert(band != NULL);
+ assert(band->raster != NULL);
+
+ if (!band->offline) {
+ rterror("rt_band_load_offline_data: Band is not offline");
+ return ES_ERROR;
+ }
+ else if (!strlen(band->data.offline.path)) {
+ rterror("rt_band_load_offline_data: Offline band does not a have a specified file");
+ return ES_ERROR;
+ }
+
+ rt_util_gdal_register_all();
+ hdsSrc = GDALOpenShared(band->data.offline.path, GA_ReadOnly);
+ if (hdsSrc == NULL) {
+ rterror("rt_band_load_offline_data: Cannot open offline raster: %s", band->data.offline.path);
+ return ES_ERROR;
+ }
+
+ /* # of bands */
+ nband = GDALGetRasterCount(hdsSrc);
+ if (!nband) {
+ rterror("rt_band_load_offline_data: No bands found in offline raster: %s", band->data.offline.path);
+ GDALClose(hdsSrc);
+ return ES_ERROR;
+ }
+ /* bandNum is 0-based */
+ else if (band->data.offline.bandNum + 1 > nband) {
+ rterror("rt_band_load_offline_data: Specified band %d not found in offline raster: %s", band->data.offline.bandNum, band->data.offline.path);
+ GDALClose(hdsSrc);
+ return ES_ERROR;
+ }
+
+ /* get raster's geotransform */
+ rt_raster_get_geotransform_matrix(band->raster, gt);
+ RASTER_DEBUGF(3, "Raster geotransform (%f, %f, %f, %f, %f, %f)",
+ gt[0], gt[1], gt[2], gt[3], gt[4], gt[5]);
+
+ /* get offline raster's geotransform */
+ if (GDALGetGeoTransform(hdsSrc, ogt) != CE_None) {
+ RASTER_DEBUG(4, "Using default geotransform matrix (0, 1, 0, 0, 0, -1)");
+ ogt[0] = 0;
+ ogt[1] = 1;
+ ogt[2] = 0;
+ ogt[3] = 0;
+ ogt[4] = 0;
+ ogt[5] = -1;
+ }
+ RASTER_DEBUGF(3, "Offline geotransform (%f, %f, %f, %f, %f, %f)",
+ ogt[0], ogt[1], ogt[2], ogt[3], ogt[4], ogt[5]);
+
+ /* are rasters aligned? */
+ _rast = rt_raster_new(1, 1);
+ rt_raster_set_geotransform_matrix(_rast, ogt);
+ rt_raster_set_srid(_rast, band->raster->srid);
+ err = rt_raster_same_alignment(band->raster, _rast, &aligned, NULL);
+ rt_raster_destroy(_rast);
+
+ if (err != ES_NONE) {
+ rterror("rt_band_load_offline_data: Could not test alignment of in-db representation of out-db raster");
+ GDALClose(hdsSrc);
+ return ES_ERROR;
+ }
+ else if (!aligned) {
+ rtwarn("The in-db representation of the out-db raster is not aligned. Band data may be incorrect");
+ }
+
+ /* get offsets */
+ rt_raster_geopoint_to_cell(
+ band->raster,
+ ogt[0], ogt[3],
+ &(offset[0]), &(offset[1]),
+ NULL
+ );
+
+ RASTER_DEBUGF(4, "offsets: (%f, %f)", offset[0], offset[1]);
+
+ /* create VRT dataset */
+ hdsDst = VRTCreate(band->width, band->height);
+ GDALSetGeoTransform(hdsDst, gt);
+ /*
+ GDALSetDescription(hdsDst, "/tmp/offline.vrt");
+ */
+
+ /* add band as simple sources */
+ GDALAddBand(hdsDst, rt_util_pixtype_to_gdal_datatype(band->pixtype), NULL);
+ hbandDst = (VRTSourcedRasterBandH) GDALGetRasterBand(hdsDst, 1);
+
+ if (band->hasnodata)
+ GDALSetRasterNoDataValue(hbandDst, band->nodataval);
+
+ VRTAddSimpleSource(
+ hbandDst, GDALGetRasterBand(hdsSrc, band->data.offline.bandNum + 1),
+ abs(offset[0]), abs(offset[1]),
+ band->width, band->height,
+ 0, 0,
+ band->width, band->height,
+ "near", VRT_NODATA_UNSET
+ );
+
+ /* make sure VRT reflects all changes */
+ VRTFlushCache(hdsDst);
+
+ /* convert VRT dataset to rt_raster */
+ _rast = rt_raster_from_gdal_dataset(hdsDst);
+
+ GDALClose(hdsDst);
+ /* XXX: need to find a way to clean up the GDALOpenShared datasets at end of transaction */
+ /* GDALClose(hdsSrc); */
+
+ if (_rast == NULL) {
+ rterror("rt_band_load_offline_data: Cannot load data from offline raster: %s", band->data.offline.path);
+ return ES_ERROR;
+ }
+
+ _band = rt_raster_get_band(_rast, 0);
+ if (_band == NULL) {
+ rterror("rt_band_load_offline_data: Cannot load data from offline raster: %s", band->data.offline.path);
+ rt_raster_destroy(_rast);
+ return ES_ERROR;
+ }
+
+ /* band->data.offline.mem not NULL, free first */
+ if (band->data.offline.mem != NULL) {
+ rtdealloc(band->data.offline.mem);
+ band->data.offline.mem = NULL;
+ }
+
+ band->data.offline.mem = _band->data.mem;
+
+ rtdealloc(_band); /* cannot use rt_band_destroy */
+ rt_raster_destroy(_rast);
+
+ return ES_NONE;
+}
+
+rt_pixtype
+rt_band_get_pixtype(rt_band band) {
+
+ assert(NULL != band);
+
+
+ return band->pixtype;
+}
+
+uint16_t
+rt_band_get_width(rt_band band) {
+
+ assert(NULL != band);
+
+
+ return band->width;
+}
+
+uint16_t
+rt_band_get_height(rt_band band) {
+
+ assert(NULL != band);
+
+
+ return band->height;
+}
+
+/* Get ownsdata flag */
+int
+rt_band_get_ownsdata_flag(rt_band band) {
+ assert(NULL != band);
+
+ return band->ownsdata ? 1 : 0;
+}
+
+/* set ownsdata flag */
+void
+rt_band_set_ownsdata_flag(rt_band band, int flag) {
+ assert(NULL != band);
+
+ band->ownsdata = flag ? 1 : 0;
+}
+
+int
+rt_band_get_hasnodata_flag(rt_band band) {
+ assert(NULL != band);
+
+ return band->hasnodata ? 1 : 0;
+}
+
+void
+rt_band_set_hasnodata_flag(rt_band band, int flag) {
+
+ assert(NULL != band);
+
+ band->hasnodata = (flag) ? 1 : 0;
+
+ /* isnodata depends on hasnodata */
+ if (!band->hasnodata && band->isnodata) {
+ RASTER_DEBUG(3, "Setting isnodata to FALSE as band no longer has NODATA");
+ band->isnodata = 0;
+ }
+}
+
+rt_errorstate
+rt_band_set_isnodata_flag(rt_band band, int flag) {
+ assert(NULL != band);
+
+ if (!band->hasnodata) {
+ /* silently permit setting isnodata flag to FALSE */
+ if (!flag)
+ band->isnodata = 0;
+ else {
+ rterror("rt_band_set_isnodata_flag: Cannot set isnodata flag as band has no NODATA");
+ return ES_ERROR;
+ }
+ }
+ else
+ band->isnodata = (flag) ? 1 : 0;
+
+ return ES_NONE;
+}
+
+int
+rt_band_get_isnodata_flag(rt_band band) {
+ assert(NULL != band);
+
+ if (band->hasnodata)
+ return band->isnodata ? 1 : 0;
+ else
+ return 0;
+}
+
+/**
+ * Set nodata value
+ *
+ * @param band : the band to set nodata value to
+ * @param val : the nodata value
+ * @param converted : if non-zero, value was truncated/clamped/coverted
+ *
+ * @return ES_NONE or ES_ERROR
+ */
+rt_errorstate
+rt_band_set_nodata(rt_band band, double val, int *converted) {
+ rt_pixtype pixtype = PT_END;
+ int32_t checkvalint = 0;
+ uint32_t checkvaluint = 0;
+ float checkvalfloat = 0;
+ double checkvaldouble = 0;
+
+ assert(NULL != band);
+
+ if (converted != NULL)
+ *converted = 0;
+
+ pixtype = band->pixtype;
+
+ RASTER_DEBUGF(3, "rt_band_set_nodata: setting nodata value %g with band type %s", val, rt_pixtype_name(pixtype));
+
+ /* return -1 on out of range */
+ switch (pixtype) {
+ case PT_1BB: {
+ band->nodataval = rt_util_clamp_to_1BB(val);
+ checkvalint = band->nodataval;
+ break;
+ }
+ case PT_2BUI: {
+ band->nodataval = rt_util_clamp_to_2BUI(val);
+ checkvalint = band->nodataval;
+ break;
+ }
+ case PT_4BUI: {
+ band->nodataval = rt_util_clamp_to_4BUI(val);
+ checkvalint = band->nodataval;
+ break;
+ }
+ case PT_8BSI: {
+ band->nodataval = rt_util_clamp_to_8BSI(val);
+ checkvalint = band->nodataval;
+ break;
+ }
+ case PT_8BUI: {
+ band->nodataval = rt_util_clamp_to_8BUI(val);
+ checkvalint = band->nodataval;
+ break;
+ }
+ case PT_16BSI: {
+ band->nodataval = rt_util_clamp_to_16BSI(val);
+ checkvalint = band->nodataval;
+ break;
+ }
+ case PT_16BUI: {
+ band->nodataval = rt_util_clamp_to_16BUI(val);
+ checkvalint = band->nodataval;
+ break;
+ }
+ case PT_32BSI: {
+ band->nodataval = rt_util_clamp_to_32BSI(val);
+ checkvalint = band->nodataval;
+ break;
+ }
+ case PT_32BUI: {
+ band->nodataval = rt_util_clamp_to_32BUI(val);
+ checkvaluint = band->nodataval;
+ break;
+ }
+ case PT_32BF: {
+ band->nodataval = rt_util_clamp_to_32F(val);
+ checkvalfloat = band->nodataval;
+ break;
+ }
+ case PT_64BF: {
+ band->nodataval = val;
+ checkvaldouble = band->nodataval;
+ break;
+ }
+ default: {
+ rterror("rt_band_set_nodata: Unknown pixeltype %d", pixtype);
+ band->hasnodata = 0;
+ return ES_ERROR;
+ }
+ }
+
+ RASTER_DEBUGF(3, "rt_band_set_nodata: band->hasnodata = %d", band->hasnodata);
+ RASTER_DEBUGF(3, "rt_band_set_nodata: band->nodataval = %f", band->nodataval);
+ /* the nodata value was just set, so this band has NODATA */
+ band->hasnodata = 1;
+
+ /* also set isnodata flag to false */
+ band->isnodata = 0;
+
+ if (rt_util_dbl_trunc_warning(
+ val,
+ checkvalint, checkvaluint,
+ checkvalfloat, checkvaldouble,
+ pixtype
+ ) && converted != NULL) {
+ *converted = 1;
+ }
+
+ return ES_NONE;
+}
+
+/**
+ * Set values of multiple pixels. Unlike rt_band_set_pixel,
+ * values in vals are expected to be of the band's pixel type
+ * as this function uses memcpy.
+ *
+ * It is important to be careful when using this function as
+ * the number of values being set may exceed a pixel "row".
+ * Remember that the band values are stored in a stream (1-D array)
+ * regardless of what the raster's width and height might be.
+ * So, setting a number of values may cross multiple pixel "rows".
+ *
+ * @param band : the band to set value to
+ * @param x : X coordinate (0-based)
+ * @param y : Y coordinate (0-based)
+ * @param vals : the pixel values to apply
+ * @param len : # of elements in vals
+ *
+ * @return ES_NONE on success, ES_ERROR on error
+ */
+rt_errorstate
+rt_band_set_pixel_line(
+ rt_band band,
+ int x, int y,
+ void *vals, uint32_t len
+) {
+ rt_pixtype pixtype = PT_END;
+ int size = 0;
+ uint8_t *data = NULL;
+ uint32_t offset = 0;
+
+ assert(NULL != band);
+ assert(vals != NULL && len > 0);
+
+ RASTER_DEBUGF(3, "length of values = %d", len);
+
+ if (band->offline) {
+ rterror("rt_band_set_pixel_line not implemented yet for OFFDB bands");
+ return ES_ERROR;
+ }
+
+ pixtype = band->pixtype;
+ size = rt_pixtype_size(pixtype);
+
+ if (
+ x < 0 || x >= band->width ||
+ y < 0 || y >= band->height
+ ) {
+ rterror("rt_band_set_pixel_line: Coordinates out of range (%d, %d) vs (%d, %d)", x, y, band->width, band->height);
+ return ES_ERROR;
+ }
+
+ data = rt_band_get_data(band);
+ offset = x + (y * band->width);
+ RASTER_DEBUGF(4, "offset = %d", offset);
+
+ /* make sure len of values to copy don't exceed end of data */
+ if (len > (band->width * band->height) - offset) {
+ rterror("rt_band_set_pixel_line: Could not apply pixels as values length exceeds end of data");
+ return ES_ERROR;
+ }
+
+ switch (pixtype) {
+ case PT_1BB:
+ case PT_2BUI:
+ case PT_4BUI:
+ case PT_8BUI:
+ case PT_8BSI: {
+ uint8_t *ptr = data;
+ ptr += offset;
+ memcpy(ptr, vals, size * len);
+ break;
+ }
+ case PT_16BUI: {
+ uint16_t *ptr = (uint16_t *) data;
+ ptr += offset;
+ memcpy(ptr, vals, size * len);
+ break;
+ }
+ case PT_16BSI: {
+ int16_t *ptr = (int16_t *) data;
+ ptr += offset;
+ memcpy(ptr, vals, size * len);
+ break;
+ }
+ case PT_32BUI: {
+ uint32_t *ptr = (uint32_t *) data;
+ ptr += offset;
+ memcpy(ptr, vals, size * len);
+ break;
+ }
+ case PT_32BSI: {
+ int32_t *ptr = (int32_t *) data;
+ ptr += offset;
+ memcpy(ptr, vals, size * len);
+ break;
+ }
+ case PT_32BF: {
+ float *ptr = (float *) data;
+ ptr += offset;
+ memcpy(ptr, vals, size * len);
+ break;
+ }
+ case PT_64BF: {
+ double *ptr = (double *) data;
+ ptr += offset;
+ memcpy(ptr, vals, size * len);
+ break;
+ }
+ default: {
+ rterror("rt_band_set_pixel_line: Unknown pixeltype %d", pixtype);
+ return ES_ERROR;
+ }
+ }
+
+#if POSTGIS_DEBUG_LEVEL > 0
+ {
+ double value;
+ rt_band_get_pixel(band, x, y, &value, NULL);
+ RASTER_DEBUGF(4, "pixel at (%d, %d) = %f", x, y, value);
+ }
+#endif
+
+ /* set band's isnodata flag to FALSE */
+ if (rt_band_get_hasnodata_flag(band))
+ rt_band_set_isnodata_flag(band, 0);
+
+ return ES_NONE;
+}
+
+/**
+ * Set single pixel's value
+ *
+ * @param band : the band to set value to
+ * @param x : x ordinate (0-based)
+ * @param y : y ordinate (0-based)
+ * @param val : the pixel value
+ * @param converted : (optional) non-zero if value truncated/clamped/converted
+ *
+ * @return ES_NONE on success, ES_ERROR on error
+ */
+rt_errorstate
+rt_band_set_pixel(
+ rt_band band,
+ int x, int y,
+ double val,
+ int *converted
+) {
+ rt_pixtype pixtype = PT_END;
+ unsigned char* data = NULL;
+ uint32_t offset = 0;
+
+ int32_t checkvalint = 0;
+ uint32_t checkvaluint = 0;
+ float checkvalfloat = 0;
+ double checkvaldouble = 0;
+
+ assert(NULL != band);
+
+ if (converted != NULL)
+ *converted = 0;
+
+ if (band->offline) {
+ rterror("rt_band_set_pixel not implemented yet for OFFDB bands");
+ return ES_ERROR;
+ }
+
+ pixtype = band->pixtype;
+
+ if (
+ x < 0 || x >= band->width ||
+ y < 0 || y >= band->height
+ ) {
+ rterror("rt_band_set_pixel: Coordinates out of range");
+ return ES_ERROR;
+ }
+
+ /* check that clamped value isn't clamped NODATA */
+ if (band->hasnodata && pixtype != PT_64BF) {
+ double newval;
+ int corrected;
+
+ rt_band_corrected_clamped_value(band, val, &newval, &corrected);
+
+ if (corrected) {
+#if POSTGIS_RASTER_WARN_ON_TRUNCATION > 0
+ rtwarn("Value for pixel %d x %d has been corrected as clamped value becomes NODATA", x, y);
+#endif
+ val = newval;
+
+ if (converted != NULL)
+ *converted = 1;
+ }
+ }
+
+ data = rt_band_get_data(band);
+ offset = x + (y * band->width);
+
+ switch (pixtype) {
+ case PT_1BB: {
+ data[offset] = rt_util_clamp_to_1BB(val);
+ checkvalint = data[offset];
+ break;
+ }
+ case PT_2BUI: {
+ data[offset] = rt_util_clamp_to_2BUI(val);
+ checkvalint = data[offset];
+ break;
+ }
+ case PT_4BUI: {
+ data[offset] = rt_util_clamp_to_4BUI(val);
+ checkvalint = data[offset];
+ break;
+ }
+ case PT_8BSI: {
+ data[offset] = rt_util_clamp_to_8BSI(val);
+ checkvalint = (int8_t) data[offset];
+ break;
+ }
+ case PT_8BUI: {
+ data[offset] = rt_util_clamp_to_8BUI(val);
+ checkvalint = data[offset];
+ break;
+ }
+ case PT_16BSI: {
+ int16_t *ptr = (int16_t*) data; /* we assume correct alignment */
+ ptr[offset] = rt_util_clamp_to_16BSI(val);
+ checkvalint = (int16_t) ptr[offset];
+ break;
+ }
+ case PT_16BUI: {
+ uint16_t *ptr = (uint16_t*) data; /* we assume correct alignment */
+ ptr[offset] = rt_util_clamp_to_16BUI(val);
+ checkvalint = ptr[offset];
+ break;
+ }
+ case PT_32BSI: {
+ int32_t *ptr = (int32_t*) data; /* we assume correct alignment */
+ ptr[offset] = rt_util_clamp_to_32BSI(val);
+ checkvalint = (int32_t) ptr[offset];
+ break;
+ }
+ case PT_32BUI: {
+ uint32_t *ptr = (uint32_t*) data; /* we assume correct alignment */
+ ptr[offset] = rt_util_clamp_to_32BUI(val);
+ checkvaluint = ptr[offset];
+ break;
+ }
+ case PT_32BF: {
+ float *ptr = (float*) data; /* we assume correct alignment */
+ ptr[offset] = rt_util_clamp_to_32F(val);
+ checkvalfloat = ptr[offset];
+ break;
+ }
+ case PT_64BF: {
+ double *ptr = (double*) data; /* we assume correct alignment */
+ ptr[offset] = val;
+ checkvaldouble = ptr[offset];
+ break;
+ }
+ default: {
+ rterror("rt_band_set_pixel: Unknown pixeltype %d", pixtype);
+ return ES_ERROR;
+ }
+ }
+
+ /* If the stored value is not NODATA, reset the isnodata flag */
+ if (!rt_band_clamped_value_is_nodata(band, val)) {
+ RASTER_DEBUG(3, "Band has a value that is not NODATA. Setting isnodata to FALSE");
+ band->isnodata = FALSE;
+ }
+
+ /* Overflow checking */
+ if (rt_util_dbl_trunc_warning(
+ val,
+ checkvalint, checkvaluint,
+ checkvalfloat, checkvaldouble,
+ pixtype
+ ) && converted != NULL) {
+ *converted = 1;
+ }
+
+ return ES_NONE;
+}
+
+/**
+ * Get values of multiple pixels. Unlike rt_band_get_pixel,
+ * values in vals are of the band's pixel type so cannot be
+ * assumed to be double. Function uses memcpy.
+ *
+ * It is important to be careful when using this function as
+ * the number of values being fetched may exceed a pixel "row".
+ * Remember that the band values are stored in a stream (1-D array)
+ * regardless of what the raster's width and height might be.
+ * So, getting a number of values may cross multiple pixel "rows".
+ *
+ * @param band : the band to get pixel value from
+ * @param x : pixel column (0-based)
+ * @param y : pixel row (0-based)
+ * @param len : the number of pixels to get
+ * @param **vals : the pixel values
+ * @param *nvals : the number of pixel values being returned
+ *
+ * @return ES_NONE on success, ES_ERROR on error
+ */
+rt_errorstate rt_band_get_pixel_line(
+ rt_band band,
+ int x, int y,
+ uint16_t len,
+ void **vals, uint16_t *nvals
+) {
+ uint8_t *_vals = NULL;
+ int pixsize = 0;
+ uint8_t *data = NULL;
+ uint32_t offset = 0;
+ uint16_t _nvals = 0;
+ int maxlen = 0;
+ uint8_t *ptr = NULL;
+
+ assert(NULL != band);
+ assert(vals != NULL && nvals != NULL);
+
+ /* initialize to no values */
+ *nvals = 0;
+
+ if (
+ x < 0 || x >= band->width ||
+ y < 0 || y >= band->height
+ ) {
+ rtwarn("Attempting to get pixel values with out of range raster coordinates: (%d, %d)", x, y);
+ return ES_ERROR;
+ }
+
+ if (len < 1)
+ return ES_NONE;
+
+ data = rt_band_get_data(band);
+ if (data == NULL) {
+ rterror("rt_band_get_pixel_line: Cannot get band data");
+ return ES_ERROR;
+ }
+
+ /* +1 for the nodata value */
+ offset = x + (y * band->width);
+ RASTER_DEBUGF(4, "offset = %d", offset);
+
+ pixsize = rt_pixtype_size(band->pixtype);
+ RASTER_DEBUGF(4, "pixsize = %d", pixsize);
+
+ /* cap _nvals so that it doesn't overflow */
+ _nvals = len;
+ maxlen = band->width * band->height;
+
+ if (((int) (offset + _nvals)) > maxlen) {
+ _nvals = maxlen - offset;
+ rtwarn("Limiting returning number values to %d", _nvals);
+ }
+ RASTER_DEBUGF(4, "_nvals = %d", _nvals);
+
+ ptr = data + (offset * pixsize);
+
+ _vals = rtalloc(_nvals * pixsize);
+ if (_vals == NULL) {
+ rterror("rt_band_get_pixel_line: Could not allocate memory for pixel values");
+ return ES_ERROR;
+ }
+
+ /* copy pixels */
+ memcpy(_vals, ptr, _nvals * pixsize);
+
+ *vals = _vals;
+ *nvals = _nvals;
+
+ return ES_NONE;
+}
+
+/**
+ * Get pixel value. If band's isnodata flag is TRUE, value returned
+ * will be the band's NODATA value
+ *
+ * @param band : the band to set nodata value to
+ * @param x : x ordinate (0-based)
+ * @param y : x ordinate (0-based)
+ * @param *value : pixel value
+ * @param *nodata : 0 if pixel is not NODATA
+ *
+ * @return 0 on success, -1 on error (value out of valid range).
+ */
+rt_errorstate
+rt_band_get_pixel(
+ rt_band band,
+ int x, int y,
+ double *value,
+ int *nodata
+) {
+ rt_pixtype pixtype = PT_END;
+ uint8_t* data = NULL;
+ uint32_t offset = 0;
+
+ assert(NULL != band);
+ assert(NULL != value);
+
+ /* set nodata to 0 */
+ if (nodata != NULL)
+ *nodata = 0;
+
+ if (
+ x < 0 || x >= band->width ||
+ y < 0 || y >= band->height
+ ) {
+ rtwarn("Attempting to get pixel value with out of range raster coordinates: (%d, %d)", x, y);
+ return ES_ERROR;
+ }
+
+ /* band is NODATA */
+ if (band->isnodata) {
+ RASTER_DEBUG(3, "Band's isnodata flag is TRUE. Returning NODATA value");
+ *value = band->nodataval;
+ if (nodata != NULL) *nodata = 1;
+ return ES_NONE;
+ }
+
+ data = rt_band_get_data(band);
+ if (data == NULL) {
+ rterror("rt_band_get_pixel: Cannot get band data");
+ return ES_ERROR;
+ }
+
+ /* +1 for the nodata value */
+ offset = x + (y * band->width);
+
+ pixtype = band->pixtype;
+
+ switch (pixtype) {
+ case PT_1BB:
+#ifdef OPTIMIZE_SPACE
+ {
+ int byteOffset = offset / 8;
+ int bitOffset = offset % 8;
+ data += byteOffset;
+
+ /* Bit to set is bitOffset into data */
+ *value = getBits(data, val, 1, bitOffset);
+ break;
+ }
+#endif
+ case PT_2BUI:
+#ifdef OPTIMIZE_SPACE
+ {
+ int byteOffset = offset / 4;
+ int bitOffset = offset % 4;
+ data += byteOffset;
+
+ /* Bits to set start at bitOffset into data */
+ *value = getBits(data, val, 2, bitOffset);
+ break;
+ }
+#endif
+ case PT_4BUI:
+#ifdef OPTIMIZE_SPACE
+ {
+ int byteOffset = offset / 2;
+ int bitOffset = offset % 2;
+ data += byteOffset;
+
+ /* Bits to set start at bitOffset into data */
+ *value = getBits(data, val, 2, bitOffset);
+ break;
+ }
+#endif
+ case PT_8BSI: {
+ int8_t val = data[offset];
+ *value = val;
+ break;
+ }
+ case PT_8BUI: {
+ uint8_t val = data[offset];
+ *value = val;
+ break;
+ }
+ case PT_16BSI: {
+ int16_t *ptr = (int16_t*) data; /* we assume correct alignment */
+ *value = ptr[offset];
+ break;
+ }
+ case PT_16BUI: {
+ uint16_t *ptr = (uint16_t*) data; /* we assume correct alignment */
+ *value = ptr[offset];
+ break;
+ }
+ case PT_32BSI: {
+ int32_t *ptr = (int32_t*) data; /* we assume correct alignment */
+ *value = ptr[offset];
+ break;
+ }
+ case PT_32BUI: {
+ uint32_t *ptr = (uint32_t*) data; /* we assume correct alignment */
+ *value = ptr[offset];
+ break;
+ }
+ case PT_32BF: {
+ float *ptr = (float*) data; /* we assume correct alignment */
+ *value = ptr[offset];
+ break;
+ }
+ case PT_64BF: {
+ double *ptr = (double*) data; /* we assume correct alignment */
+ *value = ptr[offset];
+ break;
+ }
+ default: {
+ rterror("rt_band_get_pixel: Unknown pixeltype %d", pixtype);
+ return ES_ERROR;
+ }
+ }
+
+ /* set NODATA flag */
+ if (band->hasnodata && nodata != NULL) {
+ if (rt_band_clamped_value_is_nodata(band, *value))
+ *nodata = 1;
+ }
+
+ return ES_NONE;
+}
+
+/**
+ * Get nearest pixel(s) with value (not NODATA) to specified pixel
+ *
+ * @param band : the band to get nearest pixel(s) from
+ * @param x : the column of the pixel (0-based)
+ * @param y : the line of the pixel (0-based)
+ * @param distancex : the number of pixels around the specified pixel
+ * along the X axis
+ * @param distancey : the number of pixels around the specified pixel
+ * along the Y axis
+ * @param exclude_nodata_value : if non-zero, ignore nodata values
+ * to check for pixels with value
+ * @param npixels : return set of rt_pixel object or NULL
+ *
+ * @return -1 on error, otherwise the number of rt_pixel objects
+ * in npixels
+ */
+int rt_band_get_nearest_pixel(
+ rt_band band,
+ int x, int y,
+ uint16_t distancex, uint16_t distancey,
+ int exclude_nodata_value,
+ rt_pixel *npixels
+) {
+ rt_pixel npixel = NULL;
+ int extent[4] = {0};
+ int max_extent[4] = {0};
+ int d0 = 0;
+ int distance[2] = {0};
+ uint32_t _d[2] = {0};
+ uint32_t i = 0;
+ uint32_t j = 0;
+ uint32_t k = 0;
+ int _max = 0;
+ int _x = 0;
+ int _y = 0;
+ int *_min = NULL;
+ double pixval = 0;
+ double minval = 0;
+ uint32_t count = 0;
+ int isnodata = 0;
+
+ int inextent = 0;
+
+ assert(NULL != band);
+ assert(NULL != npixels);
+
+ RASTER_DEBUG(3, "Starting");
+
+ /* process distance */
+ distance[0] = distancex;
+ distance[1] = distancey;
+
+ /* no distance, means get nearest pixels and return */
+ if (!distance[0] && !distance[1])
+ d0 = 1;
+
+ RASTER_DEBUGF(4, "Selected pixel: %d x %d", x, y);
+ RASTER_DEBUGF(4, "Distances: %d x %d", distance[0], distance[1]);
+
+ /* shortcuts if outside band extent */
+ if (
+ exclude_nodata_value && (
+ (x < 0 || x > band->width) ||
+ (y < 0 || y > band->height)
+ )
+ ) {
+ /* no distances specified, jump to pixel close to extent */
+ if (d0) {
+ if (x < 0)
+ x = -1;
+ else if (x > band->width)
+ x = band->width;
+
+ if (y < 0)
+ y = -1;
+ else if (y > band->height)
+ y = band->height;
+
+ RASTER_DEBUGF(4, "Moved selected pixel: %d x %d", x, y);
+ }
+ /*
+ distances specified
+ if distances won't capture extent of band, return 0
+ */
+ else if (
+ ((x < 0 && abs(x) > distance[0]) || (x - band->width >= distance[0])) ||
+ ((y < 0 && abs(y) > distance[1]) || (y - band->height >= distance[1]))
+ ) {
+ RASTER_DEBUG(4, "No nearest pixels possible for provided pixel and distances");
+ return 0;
+ }
+ }
+
+ /* no NODATA, exclude is FALSE */
+ if (!band->hasnodata)
+ exclude_nodata_value = FALSE;
+ /* band is NODATA and excluding NODATA */
+ else if (exclude_nodata_value && band->isnodata) {
+ RASTER_DEBUG(4, "No nearest pixels possible as band is NODATA and excluding NODATA values");
+ return 0;
+ }
+
+ /* determine the maximum distance to prevent an infinite loop */
+ if (d0) {
+ int a, b;
+
+ /* X axis */
+ a = abs(x);
+ b = abs(x - band->width);
+
+ if (a > b)
+ distance[0] = a;
+ else
+ distance[0] = b;
+
+ /* Y axis */
+ a = abs(y);
+ b = abs(y - band->height);
+ if (a > b)
+ distance[1] = a;
+ else
+ distance[1] = b;
+
+ RASTER_DEBUGF(4, "Maximum distances: %d x %d", distance[0], distance[1]);
+ }
+
+ /* minimum possible value for pixel type */
+ minval = rt_pixtype_get_min_value(band->pixtype);
+ RASTER_DEBUGF(4, "pixtype: %s", rt_pixtype_name(band->pixtype));
+ RASTER_DEBUGF(4, "minval: %f", minval);
+
+ /* set variables */
+ count = 0;
+ *npixels = NULL;
+
+ /* maximum extent */
+ max_extent[0] = x - distance[0]; /* min X */
+ max_extent[1] = y - distance[1]; /* min Y */
+ max_extent[2] = x + distance[0]; /* max X */
+ max_extent[3] = y + distance[1]; /* max Y */
+ RASTER_DEBUGF(4, "Maximum Extent: (%d, %d, %d, %d)",
+ max_extent[0], max_extent[1], max_extent[2], max_extent[3]);
+
+ _d[0] = 0;
+ _d[1] = 0;
+ do {
+ _d[0]++;
+ _d[1]++;
+
+ extent[0] = x - _d[0]; /* min x */
+ extent[1] = y - _d[1]; /* min y */
+ extent[2] = x + _d[0]; /* max x */
+ extent[3] = y + _d[1]; /* max y */
+
+ RASTER_DEBUGF(4, "Processing distances: %d x %d", _d[0], _d[1]);
+ RASTER_DEBUGF(4, "Extent: (%d, %d, %d, %d)",
+ extent[0], extent[1], extent[2], extent[3]);
+
+ for (i = 0; i < 2; i++) {
+
+ /* by row */
+ if (i < 1)
+ _max = extent[2] - extent[0] + 1;
+ /* by column */
+ else
+ _max = extent[3] - extent[1] + 1;
+ _max = abs(_max);
+
+ for (j = 0; j < 2; j++) {
+ /* by row */
+ if (i < 1) {
+ _x = extent[0];
+ _min = &_x;
+
+ /* top row */
+ if (j < 1)
+ _y = extent[1];
+ /* bottom row */
+ else
+ _y = extent[3];
+ }
+ /* by column */
+ else {
+ _y = extent[1] + 1;
+ _min = &_y;
+
+ /* left column */
+ if (j < 1) {
+ _x = extent[0];
+ _max -= 2;
+ }
+ /* right column */
+ else
+ _x = extent[2];
+ }
+
+ RASTER_DEBUGF(4, "_min, _max: %d, %d", *_min, _max);
+ for (k = 0; k < _max; k++) {
+ /* check that _x and _y are not outside max extent */
+ if (
+ _x < max_extent[0] || _x > max_extent[2] ||
+ _y < max_extent[1] || _y > max_extent[3]
+ ) {
+ (*_min)++;
+ continue;
+ }
+
+ /* outside band extent, set to NODATA */
+ if (
+ (_x < 0 || _x >= band->width) ||
+ (_y < 0 || _y >= band->height)
+ ) {
+ /* no NODATA, set to minimum possible value */
+ if (!band->hasnodata)
+ pixval = minval;
+ /* has NODATA, use NODATA */
+ else
+ pixval = band->nodataval;
+ RASTER_DEBUGF(4, "NODATA pixel outside band extent: (x, y, val) = (%d, %d, %f)", _x, _y, pixval);
+ inextent = 0;
+ isnodata = 1;
+ }
+ else {
+ if (rt_band_get_pixel(
+ band,
+ _x, _y,
+ &pixval,
+ &isnodata
+ ) != ES_NONE) {
+ rterror("rt_band_get_nearest_pixel: Could not get pixel value");
+ if (count) rtdealloc(*npixels);
+ return -1;
+ }
+ RASTER_DEBUGF(4, "Pixel: (x, y, val) = (%d, %d, %f)", _x, _y, pixval);
+ inextent = 1;
+ }
+
+ /* use pixval? */
+ if (!exclude_nodata_value || (exclude_nodata_value && !isnodata)) {
+ /* add pixel to result set */
+ RASTER_DEBUGF(4, "Adding pixel to set of nearest pixels: (x, y, val) = (%d, %d, %f)", _x, _y, pixval);
+ count++;
+
+ if (*npixels == NULL)
+ *npixels = (rt_pixel) rtalloc(sizeof(struct rt_pixel_t) * count);
+ else
+ *npixels = (rt_pixel) rtrealloc(*npixels, sizeof(struct rt_pixel_t) * count);
+ if (*npixels == NULL) {
+ rterror("rt_band_get_nearest_pixel: Could not allocate memory for nearest pixel(s)");
+ return -1;
+ }
+
+ npixel = &((*npixels)[count - 1]);
+ npixel->x = _x;
+ npixel->y = _y;
+ npixel->value = pixval;
+
+ /* special case for when outside band extent */
+ if (!inextent && !band->hasnodata)
+ npixel->nodata = 1;
+ else
+ npixel->nodata = 0;
+ }
+
+ (*_min)++;
+ }
+ }
+ }
+
+ /* distance threshholds met */
+ if (_d[0] >= distance[0] && _d[1] >= distance[1])
+ break;
+ else if (d0 && count)
+ break;
+ }
+ while (1);
+
+ RASTER_DEBUGF(3, "Nearest pixels in return: %d", count);
+
+ return count;
+}
+
+/**
+ * Search band for pixel(s) with search values
+ *
+ * @param band : the band to query for minimum and maximum pixel values
+ * @param exclude_nodata_value : if non-zero, ignore nodata values
+ * @param searchset : array of values to count
+ * @param searchcount : the number of search values
+ * @param pixels : pixels with the search value
+ *
+ * @return -1 on error, otherwise number of pixels
+ */
+int
+rt_band_get_pixel_of_value(
+ rt_band band, int exclude_nodata_value,
+ double *searchset, int searchcount,
+ rt_pixel *pixels
+) {
+ int x;
+ int y;
+ int i;
+ double pixval;
+ int err;
+ int count = 0;
+ int isnodata = 0;
+ int isequal = 0;
+
+ rt_pixel pixel = NULL;
+
+ assert(NULL != band);
+ assert(NULL != pixels);
+ assert(NULL != searchset && searchcount > 0);
+
+ if (!band->hasnodata)
+ exclude_nodata_value = FALSE;
+ /* band is NODATA and exclude_nodata_value = TRUE, nothing to search */
+ else if (exclude_nodata_value && band->isnodata) {
+ RASTER_DEBUG(4, "Pixels cannot be searched as band is NODATA and excluding NODATA values");
+ return 0;
+ }
+
+ for (x = 0; x < band->width; x++) {
+ for (y = 0; y < band->height; y++) {
+ err = rt_band_get_pixel(band, x, y, &pixval, &isnodata);
+ if (err != ES_NONE) {
+ rterror("rt_band_get_pixel_of_value: Cannot get band pixel");
+ return -1;
+ }
+ else if (exclude_nodata_value && isnodata)
+ continue;
+
+ for (i = 0; i < searchcount; i++) {
+ if (rt_pixtype_compare_clamped_values(band->pixtype, searchset[i], pixval, &isequal) != ES_NONE) {
+ continue;
+ }
+
+ if (FLT_NEQ(pixval, searchset[i]) || !isequal)
+ continue;
+
+ /* match found */
+ count++;
+ if (*pixels == NULL)
+ *pixels = (rt_pixel) rtalloc(sizeof(struct rt_pixel_t) * count);
+ else
+ *pixels = (rt_pixel) rtrealloc(*pixels, sizeof(struct rt_pixel_t) * count);
+ if (*pixels == NULL) {
+ rterror("rt_band_get_pixel_of_value: Could not allocate memory for pixel(s)");
+ return -1;
+ }
+
+ pixel = &((*pixels)[count - 1]);
+ pixel->x = x;
+ pixel->y = y;
+ pixel->nodata = 0;
+ pixel->value = pixval;
+ }
+ }
+ }
+
+ return count;
+}
+
+/**
+ * Get NODATA value
+ *
+ * @param band : the band whose NODATA value will be returned
+ * @param nodata : the band's NODATA value
+ *
+ * @return ES_NONE or ES_ERROR
+ */
+rt_errorstate
+rt_band_get_nodata(rt_band band, double *nodata) {
+ assert(NULL != band);
+ assert(NULL != nodata);
+
+ *nodata = band->nodataval;
+
+ if (!band->hasnodata) {
+ rterror("rt_band_get_nodata: Band has no NODATA value");
+ return ES_ERROR;
+ }
+
+ return ES_NONE;
+}
+
+double
+rt_band_get_min_value(rt_band band) {
+ assert(NULL != band);
+
+ return rt_pixtype_get_min_value(band->pixtype);
+}
+
+int
+rt_band_check_is_nodata(rt_band band) {
+ int i, j, err;
+ double pxValue;
+ int isnodata = 0;
+
+ assert(NULL != band);
+
+ /* Check if band has nodata value */
+ if (!band->hasnodata) {
+ RASTER_DEBUG(3, "Band has no NODATA value");
+ band->isnodata = FALSE;
+ return FALSE;
+ }
+
+ pxValue = band->nodataval;
+
+ /* Check all pixels */
+ for (i = 0; i < band->width; i++) {
+ for (j = 0; j < band->height; j++) {
+ err = rt_band_get_pixel(band, i, j, &pxValue, &isnodata);
+ if (err != ES_NONE) {
+ rterror("rt_band_check_is_nodata: Cannot get band pixel");
+ return FALSE;
+ }
+ else if (!isnodata) {
+ band->isnodata = FALSE;
+ return FALSE;
+ }
+ }
+ }
+
+ band->isnodata = TRUE;
+ return TRUE;
+}
+
+/**
+ * Compare clamped value to band's clamped NODATA value.
+ *
+ * @param band : the band whose NODATA value will be used for comparison
+ * @param val : the value to compare to the NODATA value
+ *
+ * @return 2 if unclamped value is unclamped NODATA
+ * 1 if clamped value is clamped NODATA
+ * 0 if clamped value is NOT clamped NODATA
+ */
+int
+rt_band_clamped_value_is_nodata(rt_band band, double val) {
+ int isequal = 0;
+
+ assert(NULL != band);
+
+ /* no NODATA, so never equal */
+ if (!band->hasnodata)
+ return 0;
+
+ /* value is exactly NODATA */
+ if (FLT_EQ(val, band->nodataval))
+ return 2;
+
+ /* ignore error from rt_pixtype_compare_clamped_values */
+ rt_pixtype_compare_clamped_values(
+ band->pixtype,
+ val, band->nodataval,
+ &isequal
+ );
+
+ return isequal ? 1 : 0;
+}
+
+/**
+ * Correct value when clamped value is equal to clamped NODATA value.
+ * Correction does NOT occur if unclamped value is exactly unclamped
+ * NODATA value.
+ *
+ * @param band : the band whose NODATA value will be used for comparison
+ * @param val : the value to compare to the NODATA value and correct
+ * @param *newval : pointer to corrected value
+ * @param *corrected : (optional) non-zero if val was corrected
+ *
+ * @return ES_NONE if success, ES_ERROR if error
+ */
+rt_errorstate
+rt_band_corrected_clamped_value(
+ rt_band band,
+ double val,
+ double *newval, int *corrected
+) {
+ double minval = 0.;
+
+ assert(NULL != band);
+ assert(NULL != newval);
+
+ if (corrected != NULL)
+ *corrected = 0;
+
+ /* no need to correct if clamped values IS NOT clamped NODATA */
+ if (rt_band_clamped_value_is_nodata(band, val) != 1) {
+ *newval = val;
+ return ES_NONE;
+ }
+
+ minval = rt_pixtype_get_min_value(band->pixtype);
+ *newval = val;
+
+ switch (band->pixtype) {
+ case PT_1BB:
+ *newval = !band->nodataval;
+ break;
+ case PT_2BUI:
+ if (rt_util_clamp_to_2BUI(val) == rt_util_clamp_to_2BUI(minval))
+ (*newval)++;
+ else
+ (*newval)--;
+ break;
+ case PT_4BUI:
+ if (rt_util_clamp_to_4BUI(val) == rt_util_clamp_to_4BUI(minval))
+ (*newval)++;
+ else
+ (*newval)--;
+ break;
+ case PT_8BSI:
+ if (rt_util_clamp_to_8BSI(val) == rt_util_clamp_to_8BSI(minval))
+ (*newval)++;
+ else
+ (*newval)--;
+ break;
+ case PT_8BUI:
+ if (rt_util_clamp_to_8BUI(val) == rt_util_clamp_to_8BUI(minval))
+ (*newval)++;
+ else
+ (*newval)--;
+ break;
+ case PT_16BSI:
+ if (rt_util_clamp_to_16BSI(val) == rt_util_clamp_to_16BSI(minval))
+ (*newval)++;
+ else
+ (*newval)--;
+ break;
+ case PT_16BUI:
+ if (rt_util_clamp_to_16BUI(val) == rt_util_clamp_to_16BUI(minval))
+ (*newval)++;
+ else
+ (*newval)--;
+ break;
+ case PT_32BSI:
+ if (rt_util_clamp_to_32BSI(val) == rt_util_clamp_to_32BSI(minval))
+ (*newval)++;
+ else
+ (*newval)--;
+ break;
+ case PT_32BUI:
+ if (rt_util_clamp_to_32BUI(val) == rt_util_clamp_to_32BUI(minval))
+ (*newval)++;
+ else
+ (*newval)--;
+ break;
+ case PT_32BF:
+ if (FLT_EQ(rt_util_clamp_to_32F(val), rt_util_clamp_to_32F(minval)))
+ *newval += FLT_EPSILON;
+ else
+ *newval -= FLT_EPSILON;
+ break;
+ case PT_64BF:
+ break;
+ default:
+ rterror("rt_band_corrected_clamped_value: Unknown pixeltype %d", band->pixtype);
+ return ES_ERROR;
+ }
+
+ if (corrected != NULL)
+ *corrected = 1;
+
+ return ES_NONE;
+}
+
--- /dev/null
+/*
+ * $Id$
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdarg.h> /* for va_list, va_start etc */
+
+#include "librtcore.h"
+#include "librtcore_internal.h"
+
+/******************************************************************************
+* rt_context
+******************************************************************************/
+
+/* Functions definitions */
+void * init_rt_allocator(size_t size);
+void * init_rt_reallocator(void * mem, size_t size);
+void init_rt_deallocator(void * mem);
+void init_rt_errorreporter(const char * fmt, va_list ap);
+void init_rt_warnreporter(const char * fmt, va_list ap);
+void init_rt_inforeporter(const char * fmt, va_list ap);
+
+/*
+ * Default allocators
+ *
+ * We include some default allocators that use malloc/free/realloc
+ * along with stdout/stderr since this is the most common use case
+ *
+ */
+void *
+default_rt_allocator(size_t size)
+{
+ void *mem = malloc(size);
+ return mem;
+}
+
+void *
+default_rt_reallocator(void *mem, size_t size)
+{
+ void *ret = realloc(mem, size);
+ return ret;
+}
+
+void
+default_rt_deallocator(void *mem)
+{
+ free(mem);
+}
+
+void
+default_rt_error_handler(const char *fmt, va_list ap) {
+
+ static const char *label = "ERROR: ";
+ char newfmt[1024] = {0};
+ snprintf(newfmt, 1024, "%s%s\n", label, fmt);
+ newfmt[1023] = '\0';
+
+ vprintf(newfmt, ap);
+
+ va_end(ap);
+}
+
+void
+default_rt_warning_handler(const char *fmt, va_list ap) {
+
+ static const char *label = "WARNING: ";
+ char newfmt[1024] = {0};
+ snprintf(newfmt, 1024, "%s%s\n", label, fmt);
+ newfmt[1023] = '\0';
+
+ vprintf(newfmt, ap);
+
+ va_end(ap);
+}
+
+void
+default_rt_info_handler(const char *fmt, va_list ap) {
+
+ static const char *label = "INFO: ";
+ char newfmt[1024] = {0};
+ snprintf(newfmt, 1024, "%s%s\n", label, fmt);
+ newfmt[1023] = '\0';
+
+ vprintf(newfmt, ap);
+
+ va_end(ap);
+}
+
+/**
+ * Struct definition here
+ */
+struct rt_context_t {
+ rt_allocator alloc;
+ rt_reallocator realloc;
+ rt_deallocator dealloc;
+ rt_message_handler err;
+ rt_message_handler warn;
+ rt_message_handler info;
+};
+
+/* Static variable, to be used for all rt_core functions */
+static struct rt_context_t ctx_t = {
+ .alloc = init_rt_allocator,
+ .realloc = init_rt_reallocator,
+ .dealloc = init_rt_deallocator,
+ .err = init_rt_errorreporter,
+ .warn = init_rt_warnreporter,
+ .info = init_rt_inforeporter
+};
+
+
+/**
+ * This function is normally called by rt_init_allocators when no special memory
+ * management is needed. Useful in raster core testing and in the (future)
+ * loader, when we need to use raster core functions but we don't have
+ * PostgreSQL backend behind. We must take care of memory by ourselves in those
+ * situations
+ */
+void
+rt_install_default_allocators(void)
+{
+ ctx_t.alloc = default_rt_allocator;
+ ctx_t.realloc = default_rt_reallocator;
+ ctx_t.dealloc = default_rt_deallocator;
+ ctx_t.err = default_rt_error_handler;
+ ctx_t.info = default_rt_info_handler;
+ ctx_t.warn = default_rt_warning_handler;
+}
+
+
+/**
+ * This function is called by rt_init_allocators when the PostgreSQL backend is
+ * taking care of the memory and we want to use palloc family
+ */
+void
+rt_set_handlers(rt_allocator allocator, rt_reallocator reallocator,
+ rt_deallocator deallocator, rt_message_handler error_handler,
+ rt_message_handler info_handler, rt_message_handler warning_handler) {
+
+ ctx_t.alloc = allocator;
+ ctx_t.realloc = reallocator;
+ ctx_t.dealloc = deallocator;
+
+ ctx_t.err = error_handler;
+ ctx_t.info = info_handler;
+ ctx_t.warn = warning_handler;
+}
+
+/**
+ * Initialisation allocators
+ *
+ * These are used the first time any of the allocators are called to enable
+ * executables/libraries that link into raster to be able to set up their own
+ * allocators. This is mainly useful for older PostgreSQL versions that don't
+ * have functions that are called upon startup.
+ **/
+void *
+init_rt_allocator(size_t size)
+{
+ rt_init_allocators();
+
+ return ctx_t.alloc(size);
+}
+
+void
+init_rt_deallocator(void *mem)
+{
+ rt_init_allocators();
+
+ ctx_t.dealloc(mem);
+}
+
+
+void *
+init_rt_reallocator(void *mem, size_t size)
+{
+ rt_init_allocators();
+
+ return ctx_t.realloc(mem, size);
+}
+
+void
+init_rt_inforeporter(const char *fmt, va_list ap)
+{
+ rt_init_allocators();
+
+ (*ctx_t.info)(fmt, ap);
+}
+
+void
+init_rt_warnreporter(const char *fmt, va_list ap)
+{
+ rt_init_allocators();
+
+ (*ctx_t.warn)(fmt, ap);
+}
+
+
+void
+init_rt_errorreporter(const char *fmt, va_list ap)
+{
+ rt_init_allocators();
+
+ (*ctx_t.err)(fmt, ap);
+
+}
+
+
+
+/**
+ * Raster core memory management functions.
+ *
+ * They use the functions defined by the caller.
+ */
+void *
+rtalloc(size_t size) {
+ void * mem = ctx_t.alloc(size);
+ RASTER_DEBUGF(5, "rtalloc called: %d@%p", size, mem);
+ return mem;
+}
+
+
+void *
+rtrealloc(void * mem, size_t size) {
+ void * result = ctx_t.realloc(mem, size);
+ RASTER_DEBUGF(5, "rtrealloc called: %d@%p", size, result);
+ return result;
+}
+
+void
+rtdealloc(void * mem) {
+ ctx_t.dealloc(mem);
+ RASTER_DEBUG(5, "rtdealloc called");
+}
+
+/**
+ * Raster core error and info handlers
+ *
+ * Since variadic functions cannot pass their parameters directly, we need
+ * wrappers for these functions to convert the arguments into a va_list
+ * structure.
+ */
+void
+rterror(const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ /* Call the supplied function */
+ (*ctx_t.err)(fmt, ap);
+
+ va_end(ap);
+}
+
+void
+rtinfo(const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ /* Call the supplied function */
+ (*ctx_t.info)(fmt, ap);
+
+ va_end(ap);
+}
+
+
+void
+rtwarn(const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ /* Call the supplied function */
+ (*ctx_t.warn)(fmt, ap);
+
+ va_end(ap);
+}
--- /dev/null
+/*
+ * $Id$
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "librtcore.h"
+#include "librtcore_internal.h"
+
+/******************************************************************************
+* rt_raster_perimeter()
+******************************************************************************/
+
+static rt_errorstate
+_rti_raster_get_band_perimeter(rt_band band, uint16_t *trim) {
+ uint16_t width = 0;
+ uint16_t height = 0;
+ int x = 0;
+ int y = 0;
+ int offset = 0;
+ int done[4] = {0};
+ double value = 0;
+ int nodata = 0;
+
+ assert(band != NULL);
+ assert(band->raster != NULL);
+ assert(trim != NULL);
+
+ memset(trim, 0, sizeof(uint16_t) * 4);
+
+ width = rt_band_get_width(band);
+ height = rt_band_get_height(band);
+
+ /* top */
+ for (y = 0; y < height; y++) {
+ for (offset = 0; offset < 3; offset++) {
+ /* every third pixel */
+ for (x = offset; x < width; x += 3) {
+ if (rt_band_get_pixel(band, x, y, &value, &nodata) != ES_NONE) {
+ rterror("_rti_raster_get_band_perimeter: Could not get band pixel");
+ return ES_ERROR;
+ }
+
+ RASTER_DEBUGF(4, "top (x, y, value, nodata) = (%d, %d, %f, %d)", x, y, value, nodata);
+ if (!nodata) {
+ trim[0] = y;
+ done[0] = 1;
+ break;
+ }
+ }
+
+ if (done[0])
+ break;
+ }
+
+ if (done[0])
+ break;
+ }
+
+ /* right */
+ for (x = width - 1; x >= 0; x--) {
+ for (offset = 0; offset < 3; offset++) {
+ /* every third pixel */
+ for (y = offset; y < height; y += 3) {
+ if (rt_band_get_pixel(band, x, y, &value, &nodata) != ES_NONE) {
+ rterror("_rti_raster_get_band_perimeter: Could not get band pixel");
+ return ES_ERROR;
+ }
+
+ RASTER_DEBUGF(4, "right (x, y, value, nodata) = (%d, %d, %f, %d)", x, y, value, nodata);
+ if (!nodata) {
+ trim[1] = width - (x + 1);
+ done[1] = 1;
+ break;
+ }
+ }
+
+ if (done[1])
+ break;
+ }
+
+ if (done[1])
+ break;
+ }
+
+ /* bottom */
+ for (y = height - 1; y >= 0; y--) {
+ for (offset = 0; offset < 3; offset++) {
+ /* every third pixel */
+ for (x = offset; x < width; x += 3) {
+ if (rt_band_get_pixel(band, x, y, &value, &nodata) != ES_NONE) {
+ rterror("_rti_raster_get_band_perimeter: Could not get band pixel");
+ return ES_ERROR;
+ }
+
+ RASTER_DEBUGF(4, "bottom (x, y, value, nodata) = (%d, %d, %f, %d)", x, y, value, nodata);
+ if (!nodata) {
+ trim[2] = height - (y + 1);
+ done[2] = 1;
+ break;
+ }
+ }
+
+ if (done[2])
+ break;
+ }
+
+ if (done[2])
+ break;
+ }
+
+ /* left */
+ for (x = 0; x < width; x++) {
+ for (offset = 0; offset < 3; offset++) {
+ /* every third pixel */
+ for (y = offset; y < height; y += 3) {
+ if (rt_band_get_pixel(band, x, y, &value, &nodata) != ES_NONE) {
+ rterror("_rti_raster_get_band_perimeter: Could not get band pixel");
+ return ES_ERROR;
+ }
+
+ RASTER_DEBUGF(4, "left (x, , value, nodata) = (%d, %d, %f, %d)", x, y, value, nodata);
+ if (!nodata) {
+ trim[3] = x;
+ done[3] = 1;
+ break;
+ }
+ }
+
+ if (done[3])
+ break;
+ }
+
+ if (done[3])
+ break;
+ }
+
+ RASTER_DEBUGF(4, "trim = (%d, %d, %d, %d)",
+ trim[0], trim[1], trim[2], trim[3]);
+
+ return ES_NONE;
+}
+
+/**
+ * Get raster perimeter
+ *
+ * The perimeter is a 4 vertices (5 to be closed)
+ * single ring polygon bearing the raster's rotation and using
+ * projection coordinates.
+ *
+ * @param raster : the raster to get info from
+ * @param nband : the band for the perimeter. 0-based
+ * value less than zero means all bands
+ * @param **perimeter : pointer to perimeter
+ *
+ * @return ES_NONE if success, ES_ERROR if error
+ */
+rt_errorstate rt_raster_get_perimeter(
+ rt_raster raster, int nband,
+ LWGEOM **perimeter
+) {
+ rt_band band = NULL;
+ int numband = 0;
+ uint16_t *_nband = NULL;
+ int i = 0;
+ int j = 0;
+ uint16_t _trim[4] = {0};
+ uint16_t trim[4] = {0}; /* top, right, bottom, left */
+ int isset[4] = {0};
+ double gt[6] = {0.0};
+ int srid = SRID_UNKNOWN;
+
+ POINTARRAY *pts = NULL;
+ POINT4D p4d;
+ POINTARRAY **rings = NULL;
+ LWPOLY* poly = NULL;
+
+ assert(perimeter != NULL);
+
+ *perimeter = NULL;
+
+ /* empty raster, no perimeter */
+ if (rt_raster_is_empty(raster))
+ return ES_NONE;
+
+ /* raster metadata */
+ srid = rt_raster_get_srid(raster);
+ rt_raster_get_geotransform_matrix(raster, gt);
+ numband = rt_raster_get_num_bands(raster);
+
+ RASTER_DEBUGF(3, "rt_raster_get_perimeter: raster is %dx%d", raster->width, raster->height);
+
+ /* nband < 0 means all bands */
+ if (nband >= 0) {
+ if (nband >= numband) {
+ rterror("rt_raster_get_boundary: Band %d not found for raster", nband);
+ return ES_ERROR;
+ }
+
+ numband = 1;
+ }
+ else
+ nband = -1;
+
+ RASTER_DEBUGF(3, "rt_raster_get_perimeter: nband, numband = %d, %d", nband, numband);
+
+ _nband = rtalloc(sizeof(uint16_t) * numband);
+ if (_nband == NULL) {
+ rterror("rt_raster_get_boundary: Could not allocate memory for band indices");
+ return ES_ERROR;
+ }
+
+ if (nband < 0) {
+ for (i = 0; i < numband; i++)
+ _nband[i] = i;
+ }
+ else
+ _nband[0] = nband;
+
+ for (i = 0; i < numband; i++) {
+ band = rt_raster_get_band(raster, _nband[i]);
+ if (band == NULL) {
+ rterror("rt_raster_get_boundary: Could not get band at index %d", _nband[i]);
+ rtdealloc(_nband);
+ return ES_ERROR;
+ }
+
+ /* band is nodata */
+ if (rt_band_get_isnodata_flag(band) != 0)
+ continue;
+
+ if (_rti_raster_get_band_perimeter(band, trim) != ES_NONE) {
+ rterror("rt_raster_get_boundary: Could not get band perimeter");
+ rtdealloc(_nband);
+ return ES_ERROR;
+ }
+
+ for (j = 0; j < 4; j++) {
+ if (!isset[j] || trim[j] < _trim[j]) {
+ _trim[j] = trim[j];
+ isset[j] = 1;
+ }
+ }
+ }
+
+ /* no longer needed */
+ rtdealloc(_nband);
+
+ /* check isset, just need to check one element */
+ if (!isset[0]) {
+ /* return NULL as bands are empty */
+ return ES_NONE;
+ }
+
+ RASTER_DEBUGF(4, "trim = (%d, %d, %d, %d)",
+ trim[0], trim[1], trim[2], trim[3]);
+
+ /* only one ring */
+ rings = (POINTARRAY **) rtalloc(sizeof (POINTARRAY*));
+ if (!rings) {
+ rterror("rt_raster_get_perimeter: Could not allocate memory for polygon ring");
+ return ES_ERROR;
+ }
+ rings[0] = ptarray_construct(0, 0, 5);
+ if (!rings[0]) {
+ rterror("rt_raster_get_perimeter: Could not construct point array");
+ return ES_ERROR;
+ }
+ pts = rings[0];
+
+ /* Upper-left corner (first and last points) */
+ rt_raster_cell_to_geopoint(
+ raster,
+ _trim[3], _trim[0],
+ &p4d.x, &p4d.y,
+ gt
+ );
+ ptarray_set_point4d(pts, 0, &p4d);
+ ptarray_set_point4d(pts, 4, &p4d);
+
+ /* Upper-right corner (we go clockwise) */
+ rt_raster_cell_to_geopoint(
+ raster,
+ raster->width - _trim[1], _trim[0],
+ &p4d.x, &p4d.y,
+ gt
+ );
+ ptarray_set_point4d(pts, 1, &p4d);
+
+ /* Lower-right corner */
+ rt_raster_cell_to_geopoint(
+ raster,
+ raster->width - _trim[1], raster->height - _trim[2],
+ &p4d.x, &p4d.y,
+ gt
+ );
+ ptarray_set_point4d(pts, 2, &p4d);
+
+ /* Lower-left corner */
+ rt_raster_cell_to_geopoint(
+ raster,
+ _trim[3], raster->height - _trim[2],
+ &p4d.x, &p4d.y,
+ gt
+ );
+ ptarray_set_point4d(pts, 3, &p4d);
+
+ poly = lwpoly_construct(srid, 0, 1, rings);
+ *perimeter = lwpoly_as_lwgeom(poly);
+
+ return ES_NONE;
+}
+
+/******************************************************************************
+* rt_raster_surface()
+******************************************************************************/
+
+/**
+ * Get a raster as a surface (multipolygon). If a band is specified,
+ * those pixels with value (not NODATA) contribute to the area
+ * of the output multipolygon.
+ *
+ * @param raster : the raster to convert to a multipolygon
+ * @param nband : the 0-based band of raster rast to use
+ * if value is less than zero, bands are ignored.
+ * @param *surface : raster as a surface (multipolygon).
+ * if all pixels are NODATA, NULL is set
+ *
+ * @return ES_NONE on success, ES_ERROR on error
+ */
+rt_errorstate rt_raster_surface(rt_raster raster, int nband, LWMPOLY **surface) {
+ rt_band band = NULL;
+ LWGEOM *mpoly = NULL;
+ LWGEOM *tmp = NULL;
+ LWGEOM *clone = NULL;
+ rt_geomval gv = NULL;
+ int gvcount = 0;
+ GEOSGeometry *gc = NULL;
+ GEOSGeometry *gunion = NULL;
+ GEOSGeometry **geoms = NULL;
+ int geomscount = 0;
+ int i = 0;
+
+ assert(surface != NULL);
+
+ /* init *surface to NULL */
+ *surface = NULL;
+
+ /* raster is empty, surface = NULL */
+ if (rt_raster_is_empty(raster))
+ return ES_NONE;
+
+ /* if nband < 0, return the convex hull as a multipolygon */
+ if (nband < 0) {
+ /*
+ lwgeom_as_multi() only does a shallow clone internally
+ so input and output geometries may share memory
+ hence the deep clone of the output geometry for returning
+ is the only way to guarentee the memory isn't shared
+ */
+ if (rt_raster_get_convex_hull(raster, &tmp) != ES_NONE) {
+ rterror("rt_raster_surface: Could not get convex hull of raster");
+ return ES_ERROR;
+ }
+ mpoly = lwgeom_as_multi(tmp);
+ clone = lwgeom_clone_deep(mpoly);
+ lwgeom_free(tmp);
+ lwgeom_free(mpoly);
+
+ *surface = lwgeom_as_lwmpoly(clone);
+ return ES_NONE;
+ }
+ /* check that nband is valid */
+ else if (nband >= rt_raster_get_num_bands(raster)) {
+ rterror("rt_raster_surface: The band index %d is invalid", nband);
+ return ES_ERROR;
+ }
+
+ /* get band */
+ band = rt_raster_get_band(raster, nband);
+ if (band == NULL) {
+ rterror("rt_raster_surface: Error getting band %d from raster", nband);
+ return ES_ERROR;
+ }
+
+ /* band does not have a NODATA flag, return convex hull */
+ if (!rt_band_get_hasnodata_flag(band)) {
+ /*
+ lwgeom_as_multi() only does a shallow clone internally
+ so input and output geometries may share memory
+ hence the deep clone of the output geometry for returning
+ is the only way to guarentee the memory isn't shared
+ */
+ if (rt_raster_get_convex_hull(raster, &tmp) != ES_NONE) {
+ rterror("rt_raster_surface: Could not get convex hull of raster");
+ return ES_ERROR;
+ }
+ mpoly = lwgeom_as_multi(tmp);
+ clone = lwgeom_clone_deep(mpoly);
+ lwgeom_free(tmp);
+ lwgeom_free(mpoly);
+
+ *surface = lwgeom_as_lwmpoly(clone);
+ return ES_NONE;
+ }
+ /* band is NODATA, return NULL */
+ else if (rt_band_get_isnodata_flag(band)) {
+ RASTER_DEBUG(3, "Band is NODATA. Returning NULL");
+ return ES_NONE;
+ }
+
+ /* initialize GEOS */
+ initGEOS(lwnotice, lwgeom_geos_error);
+
+ /* use gdal polygonize */
+ gv = rt_raster_gdal_polygonize(raster, nband, 1, &gvcount);
+ /* no polygons returned */
+ if (gvcount < 1) {
+ RASTER_DEBUG(3, "All pixels of band are NODATA. Returning NULL");
+ if (gv != NULL) rtdealloc(gv);
+ return ES_NONE;
+ }
+ /* more than 1 polygon */
+ else if (gvcount > 1) {
+ /* convert LWPOLY to GEOSGeometry */
+ geomscount = gvcount;
+ geoms = rtalloc(sizeof(GEOSGeometry *) * geomscount);
+ if (geoms == NULL) {
+ rterror("rt_raster_surface: Could not allocate memory for pixel polygons as GEOSGeometry");
+ for (i = 0; i < gvcount; i++) lwpoly_free(gv[i].geom);
+ rtdealloc(gv);
+ return ES_ERROR;
+ }
+ for (i = 0; i < gvcount; i++) {
+#if POSTGIS_DEBUG_LEVEL > 3
+ {
+ char *wkt = lwgeom_to_wkt(lwpoly_as_lwgeom(gv[i].geom), WKT_ISO, DBL_DIG, NULL);
+ RASTER_DEBUGF(4, "geom %d = %s", i, wkt);
+ rtdealloc(wkt);
+ }
+#endif
+
+ geoms[i] = LWGEOM2GEOS(lwpoly_as_lwgeom(gv[i].geom));
+ lwpoly_free(gv[i].geom);
+ }
+ rtdealloc(gv);
+
+ /* create geometry collection */
+#if POSTGIS_GEOS_VERSION >= 33
+ gc = GEOSGeom_createCollection(GEOS_GEOMETRYCOLLECTION, geoms, geomscount);
+#else
+ gc = GEOSGeom_createCollection(GEOS_MULTIPOLYGON, geoms, geomscount);
+#endif
+
+ if (gc == NULL) {
+#if POSTGIS_GEOS_VERSION >= 33
+ rterror("rt_raster_surface: Could not create GEOS GEOMETRYCOLLECTION from set of pixel polygons");
+#else
+ rterror("rt_raster_surface: Could not create GEOS MULTIPOLYGON from set of pixel polygons");
+#endif
+
+ for (i = 0; i < geomscount; i++)
+ GEOSGeom_destroy(geoms[i]);
+ rtdealloc(geoms);
+ return ES_ERROR;
+ }
+
+ /* run the union */
+#if POSTGIS_GEOS_VERSION >= 33
+ gunion = GEOSUnaryUnion(gc);
+#else
+ gunion = GEOSUnionCascaded(gc);
+#endif
+ GEOSGeom_destroy(gc);
+ rtdealloc(geoms);
+
+ if (gunion == NULL) {
+#if POSTGIS_GEOS_VERSION >= 33
+ rterror("rt_raster_surface: Could not union the pixel polygons using GEOSUnaryUnion()");
+#else
+ rterror("rt_raster_surface: Could not union the pixel polygons using GEOSUnionCascaded()");
+#endif
+ return ES_ERROR;
+ }
+
+ /* convert union result from GEOSGeometry to LWGEOM */
+ mpoly = GEOS2LWGEOM(gunion, 0);
+
+ /*
+ is geometry valid?
+ if not, try to make valid
+ */
+ do {
+ LWGEOM *mpolyValid = NULL;
+
+#if POSTGIS_GEOS_VERSION < 33
+ break;
+#endif
+
+ if (GEOSisValid(gunion))
+ break;
+
+ /* make geometry valid */
+ mpolyValid = lwgeom_make_valid(mpoly);
+ if (mpolyValid == NULL) {
+ rtwarn("Cannot fix invalid geometry");
+ break;
+ }
+
+ lwgeom_free(mpoly);
+ mpoly = mpolyValid;
+ }
+ while (0);
+
+ GEOSGeom_destroy(gunion);
+ }
+ else {
+ mpoly = lwpoly_as_lwgeom(gv[0].geom);
+ rtdealloc(gv);
+
+#if POSTGIS_DEBUG_LEVEL > 3
+ {
+ char *wkt = lwgeom_to_wkt(mpoly, WKT_ISO, DBL_DIG, NULL);
+ RASTER_DEBUGF(4, "geom 0 = %s", wkt);
+ rtdealloc(wkt);
+ }
+#endif
+ }
+
+ /* specify SRID */
+ lwgeom_set_srid(mpoly, rt_raster_get_srid(raster));
+
+ if (mpoly != NULL) {
+ /* convert to multi */
+ if (!lwgeom_is_collection(mpoly)) {
+ tmp = mpoly;
+
+#if POSTGIS_DEBUG_LEVEL > 3
+ {
+ char *wkt = lwgeom_to_wkt(mpoly, WKT_ISO, DBL_DIG, NULL);
+ RASTER_DEBUGF(4, "before multi = %s", wkt);
+ rtdealloc(wkt);
+ }
+#endif
+
+ RASTER_DEBUGF(4, "mpoly @ %p", mpoly);
+
+ /*
+ lwgeom_as_multi() only does a shallow clone internally
+ so input and output geometries may share memory
+ hence the deep clone of the output geometry for returning
+ is the only way to guarentee the memory isn't shared
+ */
+ mpoly = lwgeom_as_multi(tmp);
+ clone = lwgeom_clone_deep(mpoly);
+ lwgeom_free(tmp);
+ lwgeom_free(mpoly);
+ mpoly = clone;
+
+ RASTER_DEBUGF(4, "mpoly @ %p", mpoly);
+
+#if POSTGIS_DEBUG_LEVEL > 3
+ {
+ char *wkt = lwgeom_to_wkt(mpoly, WKT_ISO, DBL_DIG, NULL);
+ RASTER_DEBUGF(4, "after multi = %s", wkt);
+ rtdealloc(wkt);
+ }
+#endif
+ }
+
+#if POSTGIS_DEBUG_LEVEL > 3
+ {
+ char *wkt = lwgeom_to_wkt(mpoly, WKT_ISO, DBL_DIG, NULL);
+ RASTER_DEBUGF(4, "returning geometry = %s", wkt);
+ rtdealloc(wkt);
+ }
+#endif
+
+ *surface = lwgeom_as_lwmpoly(mpoly);
+ return ES_NONE;
+ }
+
+ return ES_NONE;
+}
+
+/******************************************************************************
+* rt_raster_pixel_as_polygon()
+******************************************************************************/
+
+/**
+ * Get a raster pixel as a polygon.
+ *
+ * The pixel shape is a 4 vertices (5 to be closed) single
+ * ring polygon bearing the raster's rotation
+ * and using projection coordinates
+ *
+ * @param raster : the raster to get pixel from
+ * @param x : the column number
+ * @param y : the row number
+ *
+ * @return the pixel polygon, or NULL on error.
+ *
+ */
+LWPOLY*
+rt_raster_pixel_as_polygon(rt_raster rast, int x, int y)
+{
+ double scale_x, scale_y;
+ double skew_x, skew_y;
+ double ul_x, ul_y;
+ int srid;
+ POINTARRAY **points;
+ POINT4D p, p0;
+ LWPOLY *poly;
+
+ assert(rast != NULL);
+
+ scale_x = rt_raster_get_x_scale(rast);
+ scale_y = rt_raster_get_y_scale(rast);
+ skew_x = rt_raster_get_x_skew(rast);
+ skew_y = rt_raster_get_y_skew(rast);
+ ul_x = rt_raster_get_x_offset(rast);
+ ul_y = rt_raster_get_y_offset(rast);
+ srid = rt_raster_get_srid(rast);
+
+ points = rtalloc(sizeof(POINTARRAY *)*1);
+ points[0] = ptarray_construct(0, 0, 5);
+
+ p0.x = scale_x * x + skew_x * y + ul_x;
+ p0.y = scale_y * y + skew_y * x + ul_y;
+ ptarray_set_point4d(points[0], 0, &p0);
+
+ p.x = p0.x + scale_x;
+ p.y = p0.y + skew_y;
+ ptarray_set_point4d(points[0], 1, &p);
+
+ p.x = p0.x + scale_x + skew_x;
+ p.y = p0.y + scale_y + skew_y;
+ ptarray_set_point4d(points[0], 2, &p);
+
+ p.x = p0.x + skew_x;
+ p.y = p0.y + scale_y;
+ ptarray_set_point4d(points[0], 3, &p);
+
+ /* close it */
+ ptarray_set_point4d(points[0], 4, &p0);
+
+ poly = lwpoly_construct(srid, NULL, 1, points);
+
+ return poly;
+}
+
+/******************************************************************************
+* rt_raster_get_convex_hull()
+******************************************************************************/
+
+/**
+ * Get raster's convex hull.
+ *
+ * The convex hull is typically a 4 vertices (5 to be closed)
+ * single ring polygon bearing the raster's rotation and using
+ * projection coordinates.
+ *
+ * @param raster : the raster to get info from
+ * @param **hull : pointer to convex hull
+ *
+ * @return ES_NONE if success, ES_ERROR if error
+ */
+rt_errorstate
+rt_raster_get_convex_hull(rt_raster raster, LWGEOM **hull) {
+ double gt[6] = {0.0};
+ int srid = SRID_UNKNOWN;
+
+ POINTARRAY *pts = NULL;
+ POINT4D p4d;
+
+ assert(hull != NULL);
+ *hull = NULL;
+
+ /* raster is NULL, convex hull is NULL */
+ if (raster == NULL)
+ return ES_NONE;
+
+ /* raster metadata */
+ srid = rt_raster_get_srid(raster);
+ rt_raster_get_geotransform_matrix(raster, gt);
+
+ RASTER_DEBUGF(3, "rt_raster_get_convex_hull: raster is %dx%d", raster->width, raster->height);
+
+ /* return point or line since at least one of the two dimensions is 0 */
+ if ((!raster->width) || (!raster->height)) {
+ p4d.x = gt[0];
+ p4d.y = gt[3];
+
+ /* return point */
+ if (!raster->width && !raster->height) {
+ LWPOINT *point = lwpoint_make2d(srid, p4d.x, p4d.y);
+ *hull = lwpoint_as_lwgeom(point);
+ }
+ /* return linestring */
+ else {
+ LWLINE *line = NULL;
+ pts = ptarray_construct_empty(0, 0, 2);
+
+ /* first point of line */
+ ptarray_append_point(pts, &p4d, LW_TRUE);
+
+ /* second point of line */
+ if (rt_raster_cell_to_geopoint(
+ raster,
+ rt_raster_get_width(raster), rt_raster_get_height(raster),
+ &p4d.x, &p4d.y,
+ gt
+ ) != ES_NONE) {
+ rterror("rt_raster_get_convex_hull: Could not get second point for linestring");
+ return ES_ERROR;
+ }
+ ptarray_append_point(pts, &p4d, LW_TRUE);
+ line = lwline_construct(srid, NULL, pts);
+
+ *hull = lwline_as_lwgeom(line);
+ }
+
+ return ES_NONE;
+ }
+ else {
+ POINTARRAY **rings = NULL;
+ LWPOLY* poly = NULL;
+
+ /* only one ring */
+ rings = (POINTARRAY **) rtalloc(sizeof (POINTARRAY*));
+ if (!rings) {
+ rterror("rt_raster_get_convex_hull: Could not allocate memory for polygon ring");
+ return ES_ERROR;
+ }
+ rings[0] = ptarray_construct(0, 0, 5);
+ /* TODO: handle error on ptarray construction */
+ /* XXX jorgearevalo: the error conditions aren't managed in ptarray_construct */
+ if (!rings[0]) {
+ rterror("rt_raster_get_convex_hull: Could not construct point array");
+ return ES_ERROR;
+ }
+ pts = rings[0];
+
+ /* Upper-left corner (first and last points) */
+ p4d.x = gt[0];
+ p4d.y = gt[3];
+ ptarray_set_point4d(pts, 0, &p4d);
+ ptarray_set_point4d(pts, 4, &p4d);
+
+ /* Upper-right corner (we go clockwise) */
+ rt_raster_cell_to_geopoint(
+ raster,
+ raster->width, 0,
+ &p4d.x, &p4d.y,
+ gt
+ );
+ ptarray_set_point4d(pts, 1, &p4d);
+
+ /* Lower-right corner */
+ rt_raster_cell_to_geopoint(
+ raster,
+ raster->width, raster->height,
+ &p4d.x, &p4d.y,
+ gt
+ );
+ ptarray_set_point4d(pts, 2, &p4d);
+
+ /* Lower-left corner */
+ rt_raster_cell_to_geopoint(
+ raster,
+ 0, raster->height,
+ &p4d.x, &p4d.y,
+ gt
+ );
+ ptarray_set_point4d(pts, 3, &p4d);
+
+ poly = lwpoly_construct(srid, 0, 1, rings);
+ *hull = lwpoly_as_lwgeom(poly);
+ }
+
+ return ES_NONE;
+}
+
+/******************************************************************************
+* rt_raster_gdal_polygonize()
+******************************************************************************/
+
+/**
+ * Returns a set of "geomval" value, one for each group of pixel
+ * sharing the same value for the provided band.
+ *
+ * A "geomval" value is a complex type composed of a geometry
+ * in LWPOLY representation (one for each group of pixel sharing
+ * the same value) and the value associated with this geometry.
+ *
+ * @param raster : the raster to get info from.
+ * @param nband : the band to polygonize. 0-based
+ * @param exclude_nodata_value : if non-zero, ignore nodata values
+ * to check for pixels with value
+ *
+ * @return A set of "geomval" values, one for each group of pixels
+ * sharing the same value for the provided band. The returned values are
+ * LWPOLY geometries.
+ */
+rt_geomval
+rt_raster_gdal_polygonize(
+ rt_raster raster, int nband,
+ int exclude_nodata_value,
+ int *pnElements
+) {
+ CPLErr cplerr = CE_None;
+ char *pszQuery;
+ long j;
+ OGRSFDriverH ogr_drv = NULL;
+ GDALDriverH gdal_drv = NULL;
+ GDALDatasetH memdataset = NULL;
+ GDALRasterBandH gdal_band = NULL;
+ OGRDataSourceH memdatasource = NULL;
+ rt_geomval pols = NULL;
+ OGRLayerH hLayer = NULL;
+ OGRFeatureH hFeature = NULL;
+ OGRGeometryH hGeom = NULL;
+ OGRFieldDefnH hFldDfn = NULL;
+ unsigned char *wkb = NULL;
+ int wkbsize = 0;
+ LWGEOM *lwgeom = NULL;
+ int nFeatureCount = 0;
+ rt_band band = NULL;
+ int iPixVal = -1;
+ double dValue = 0.0;
+ int iBandHasNodataValue = FALSE;
+ double dBandNoData = 0.0;
+
+ /* for checking that a geometry is valid */
+ GEOSGeometry *ggeom = NULL;
+ int isValid;
+ LWGEOM *lwgeomValid = NULL;
+
+ uint32_t bandNums[1] = {nband};
+ int excludeNodataValues[1] = {exclude_nodata_value};
+
+ /* checks */
+ assert(NULL != raster);
+ assert(NULL != pnElements);
+
+ RASTER_DEBUG(2, "In rt_raster_gdal_polygonize");
+
+ *pnElements = 0;
+
+ /*******************************
+ * Get band
+ *******************************/
+ band = rt_raster_get_band(raster, nband);
+ if (NULL == band) {
+ rterror("rt_raster_gdal_polygonize: Error getting band %d from raster", nband);
+ return NULL;
+ }
+
+ if (exclude_nodata_value) {
+
+ /* band is NODATA */
+ if (rt_band_get_isnodata_flag(band)) {
+ RASTER_DEBUG(3, "Band is NODATA. Returning null");
+ *pnElements = 0;
+ return NULL;
+ }
+
+ iBandHasNodataValue = rt_band_get_hasnodata_flag(band);
+ if (iBandHasNodataValue)
+ rt_band_get_nodata(band, &dBandNoData);
+ else
+ exclude_nodata_value = FALSE;
+ }
+
+ /*****************************************************
+ * Convert raster to GDAL MEM dataset
+ *****************************************************/
+ memdataset = rt_raster_to_gdal_mem(raster, NULL, bandNums, excludeNodataValues, 1, &gdal_drv);
+ if (NULL == memdataset) {
+ rterror("rt_raster_gdal_polygonize: Couldn't convert raster to GDAL MEM dataset");
+ return NULL;
+ }
+
+ /*****************************
+ * Register ogr mem driver
+ *****************************/
+ OGRRegisterAll();
+
+ RASTER_DEBUG(3, "creating OGR MEM vector");
+
+ /*****************************************************
+ * Create an OGR in-memory vector for layers
+ *****************************************************/
+ ogr_drv = OGRGetDriverByName("Memory");
+ memdatasource = OGR_Dr_CreateDataSource(ogr_drv, "", NULL);
+ if (NULL == memdatasource) {
+ rterror("rt_raster_gdal_polygonize: Couldn't create a OGR Datasource to store pols");
+ GDALClose(memdataset);
+ return NULL;
+ }
+
+ /* Can MEM driver create new layers? */
+ if (!OGR_DS_TestCapability(memdatasource, ODsCCreateLayer)) {
+ rterror("rt_raster_gdal_polygonize: MEM driver can't create new layers, aborting");
+
+ /* xxx jorgearevalo: what should we do now? */
+ GDALClose(memdataset);
+ OGRReleaseDataSource(memdatasource);
+
+ return NULL;
+ }
+
+ RASTER_DEBUG(3, "polygonizying GDAL MEM raster band");
+
+ /*****************************
+ * Polygonize the raster band
+ *****************************/
+
+ /**
+ * From GDALPolygonize function header: "Polygon features will be
+ * created on the output layer, with polygon geometries representing
+ * the polygons". So,the WKB geometry type should be "wkbPolygon"
+ **/
+ hLayer = OGR_DS_CreateLayer(memdatasource, "PolygonizedLayer", NULL, wkbPolygon, NULL);
+
+ if (NULL == hLayer) {
+ rterror("rt_raster_gdal_polygonize: Couldn't create layer to store polygons");
+
+ GDALClose(memdataset);
+ OGRReleaseDataSource(memdatasource);
+
+ return NULL;
+ }
+
+ /**
+ * Create a new field in the layer, to store the px value
+ */
+
+ /* First, create a field definition to create the field */
+ hFldDfn = OGR_Fld_Create("PixelValue", OFTReal);
+
+ /* Second, create the field */
+ if (OGR_L_CreateField(hLayer, hFldDfn, TRUE) != OGRERR_NONE) {
+ rtwarn("Couldn't create a field in OGR Layer. The polygons generated won't be able to store the pixel value");
+ iPixVal = -1;
+ }
+ else {
+ /* Index to the new field created in the layer */
+ iPixVal = 0;
+ }
+
+ /* Get GDAL raster band */
+ gdal_band = GDALGetRasterBand(memdataset, 1);
+ if (NULL == gdal_band) {
+ rterror("rt_raster_gdal_polygonize: Couldn't get GDAL band to polygonize");
+
+ GDALClose(memdataset);
+ OGR_Fld_Destroy(hFldDfn);
+ OGR_DS_DeleteLayer(memdatasource, 0);
+ OGRReleaseDataSource(memdatasource);
+
+ return NULL;
+ }
+
+ /**
+ * We don't need a raster mask band. Each band has a nodata value.
+ **/
+#ifdef GDALFPOLYGONIZE
+ cplerr = GDALFPolygonize(gdal_band, NULL, hLayer, iPixVal, NULL, NULL, NULL);
+#else
+ cplerr = GDALPolygonize(gdal_band, NULL, hLayer, iPixVal, NULL, NULL, NULL);
+#endif
+
+ if (cplerr != CE_None) {
+ rterror("rt_raster_gdal_polygonize: Could not polygonize GDAL band");
+
+ GDALClose(memdataset);
+ OGR_Fld_Destroy(hFldDfn);
+ OGR_DS_DeleteLayer(memdatasource, 0);
+ OGRReleaseDataSource(memdatasource);
+
+ return NULL;
+ }
+
+ /**
+ * Optimization: Apply a OGR SQL filter to the layer to select the
+ * features different from NODATA value.
+ *
+ * Thanks to David Zwarg.
+ **/
+ if (iBandHasNodataValue) {
+ pszQuery = (char *) rtalloc(50 * sizeof (char));
+ sprintf(pszQuery, "PixelValue != %f", dBandNoData );
+ OGRErr e = OGR_L_SetAttributeFilter(hLayer, pszQuery);
+ if (e != OGRERR_NONE) {
+ rtwarn("Error filtering NODATA values for band. All values will be treated as data values");
+ }
+ }
+ else {
+ pszQuery = NULL;
+ }
+
+ /*********************************************************************
+ * Transform OGR layers to WKB polygons
+ * XXX jorgearevalo: GDALPolygonize does not set the coordinate system
+ * on the output layer. Application code should do this when the layer
+ * is created, presumably matching the raster coordinate system.
+ *********************************************************************/
+ nFeatureCount = OGR_L_GetFeatureCount(hLayer, TRUE);
+
+ /* Allocate memory for pols */
+ pols = (rt_geomval) rtalloc(nFeatureCount * sizeof(struct rt_geomval_t));
+
+ if (NULL == pols) {
+ rterror("rt_raster_gdal_polygonize: Could not allocate memory for geomval set");
+
+ GDALClose(memdataset);
+ OGR_Fld_Destroy(hFldDfn);
+ OGR_DS_DeleteLayer(memdatasource, 0);
+ if (NULL != pszQuery)
+ rtdealloc(pszQuery);
+ OGRReleaseDataSource(memdatasource);
+
+ return NULL;
+ }
+
+ /* initialize GEOS */
+ initGEOS(lwnotice, lwgeom_geos_error);
+
+ RASTER_DEBUGF(3, "storing polygons (%d)", nFeatureCount);
+
+ /* Reset feature reading to start in the first feature */
+ OGR_L_ResetReading(hLayer);
+
+ for (j = 0; j < nFeatureCount; j++) {
+ hFeature = OGR_L_GetNextFeature(hLayer);
+ dValue = OGR_F_GetFieldAsDouble(hFeature, iPixVal);
+
+ hGeom = OGR_F_GetGeometryRef(hFeature);
+ wkbsize = OGR_G_WkbSize(hGeom);
+
+ /* allocate wkb buffer */
+ wkb = rtalloc(sizeof(unsigned char) * wkbsize);
+ if (wkb == NULL) {
+ rterror("rt_raster_gdal_polygonize: Could not allocate memory for WKB buffer");
+
+ OGR_F_Destroy(hFeature);
+ GDALClose(memdataset);
+ OGR_Fld_Destroy(hFldDfn);
+ OGR_DS_DeleteLayer(memdatasource, 0);
+ if (NULL != pszQuery)
+ rtdealloc(pszQuery);
+ OGRReleaseDataSource(memdatasource);
+
+ return NULL;
+ }
+
+ /* export WKB with LSB byte order */
+ OGR_G_ExportToWkb(hGeom, wkbNDR, wkb);
+
+ /* convert WKB to LWGEOM */
+ lwgeom = lwgeom_from_wkb(wkb, wkbsize, LW_PARSER_CHECK_NONE);
+
+#if POSTGIS_DEBUG_LEVEL > 3
+ {
+ char *wkt = NULL;
+ OGR_G_ExportToWkt(hGeom, &wkt);
+ RASTER_DEBUGF(4, "GDAL wkt = %s", wkt);
+ CPLFree(wkt);
+
+ d_print_binary_hex("GDAL wkb", wkb, wkbsize);
+ }
+#endif
+
+ /* cleanup unnecessary stuff */
+ rtdealloc(wkb);
+ wkb = NULL;
+ wkbsize = 0;
+
+ OGR_F_Destroy(hFeature);
+
+ /* specify SRID */
+ lwgeom_set_srid(lwgeom, rt_raster_get_srid(raster));
+
+ /*
+ is geometry valid?
+ if not, try to make valid
+ */
+ do {
+#if POSTGIS_GEOS_VERSION < 33
+ /* nothing can be done if the geometry was invalid if GEOS < 3.3 */
+ break;
+#endif
+
+ ggeom = (GEOSGeometry *) LWGEOM2GEOS(lwgeom);
+ if (ggeom == NULL) {
+ rtwarn("Cannot test geometry for validity");
+ break;
+ }
+
+ isValid = GEOSisValid(ggeom);
+
+ GEOSGeom_destroy(ggeom);
+ ggeom = NULL;
+
+ /* geometry is valid */
+ if (isValid)
+ break;
+
+ RASTER_DEBUG(3, "fixing invalid geometry");
+
+ /* make geometry valid */
+ lwgeomValid = lwgeom_make_valid(lwgeom);
+ if (lwgeomValid == NULL) {
+ rtwarn("Cannot fix invalid geometry");
+ break;
+ }
+
+ lwgeom_free(lwgeom);
+ lwgeom = lwgeomValid;
+ }
+ while (0);
+
+ /* save lwgeom */
+ pols[j].geom = lwgeom_as_lwpoly(lwgeom);
+
+#if POSTGIS_DEBUG_LEVEL > 3
+ {
+ char *wkt = lwgeom_to_wkt(lwgeom, WKT_ISO, DBL_DIG, NULL);
+ RASTER_DEBUGF(4, "LWGEOM wkt = %s", wkt);
+ rtdealloc(wkt);
+
+ size_t lwwkbsize = 0;
+ uint8_t *lwwkb = lwgeom_to_wkb(lwgeom, WKB_ISO | WKB_NDR, &lwwkbsize);
+ if (lwwkbsize) {
+ d_print_binary_hex("LWGEOM wkb", lwwkb, lwwkbsize);
+ rtdealloc(lwwkb);
+ }
+ }
+#endif
+
+ /* set pixel value */
+ pols[j].val = dValue;
+ }
+
+ *pnElements = nFeatureCount;
+
+ RASTER_DEBUG(3, "destroying GDAL MEM raster");
+ GDALClose(memdataset);
+
+ RASTER_DEBUG(3, "destroying OGR MEM vector");
+ OGR_Fld_Destroy(hFldDfn);
+ OGR_DS_DeleteLayer(memdatasource, 0);
+ if (NULL != pszQuery) rtdealloc(pszQuery);
+ OGRReleaseDataSource(memdatasource);
+
+ return pols;
+}
+
--- /dev/null
+/*
+ * $Id$
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "librtcore.h"
+#include "librtcore_internal.h"
+
+/******************************************************************************
+* rt_band_reclass()
+******************************************************************************/
+
+/**
+ * Returns new band with values reclassified
+ *
+ * @param srcband : the band who's values will be reclassified
+ * @param pixtype : pixel type of the new band
+ * @param hasnodata : indicates if the band has a nodata value
+ * @param nodataval : nodata value for the new band
+ * @param exprset : array of rt_reclassexpr structs
+ * @param exprcount : number of elements in expr
+ *
+ * @return a new rt_band or NULL on error
+ */
+rt_band
+rt_band_reclass(
+ rt_band srcband, rt_pixtype pixtype,
+ uint32_t hasnodata, double nodataval,
+ rt_reclassexpr *exprset, int exprcount
+) {
+ rt_band band = NULL;
+ uint32_t width = 0;
+ uint32_t height = 0;
+ int numval = 0;
+ int memsize = 0;
+ void *mem = NULL;
+ uint32_t src_hasnodata = 0;
+ double src_nodataval = 0.0;
+ int isnodata = 0;
+
+ int rtn;
+ uint32_t x;
+ uint32_t y;
+ int i;
+ double or = 0;
+ double ov = 0;
+ double nr = 0;
+ double nv = 0;
+ int do_nv = 0;
+ rt_reclassexpr expr = NULL;
+
+ assert(NULL != srcband);
+ assert(NULL != exprset && exprcount > 0);
+ RASTER_DEBUGF(4, "exprcount = %d", exprcount);
+ RASTER_DEBUGF(4, "exprset @ %p", exprset);
+
+ /* source nodata */
+ src_hasnodata = rt_band_get_hasnodata_flag(srcband);
+ if (src_hasnodata)
+ rt_band_get_nodata(srcband, &src_nodataval);
+
+ /* size of memory block to allocate */
+ width = rt_band_get_width(srcband);
+ height = rt_band_get_height(srcband);
+ numval = width * height;
+ memsize = rt_pixtype_size(pixtype) * numval;
+ mem = (int *) rtalloc(memsize);
+ if (!mem) {
+ rterror("rt_band_reclass: Could not allocate memory for band");
+ return 0;
+ }
+
+ /* initialize to zero */
+ if (!hasnodata) {
+ memset(mem, 0, memsize);
+ }
+ /* initialize to nodataval */
+ else {
+ int32_t checkvalint = 0;
+ uint32_t checkvaluint = 0;
+ double checkvaldouble = 0;
+ float checkvalfloat = 0;
+
+ switch (pixtype) {
+ case PT_1BB:
+ {
+ uint8_t *ptr = mem;
+ uint8_t clamped_initval = rt_util_clamp_to_1BB(nodataval);
+ for (i = 0; i < numval; i++)
+ ptr[i] = clamped_initval;
+ checkvalint = ptr[0];
+ break;
+ }
+ case PT_2BUI:
+ {
+ uint8_t *ptr = mem;
+ uint8_t clamped_initval = rt_util_clamp_to_2BUI(nodataval);
+ for (i = 0; i < numval; i++)
+ ptr[i] = clamped_initval;
+ checkvalint = ptr[0];
+ break;
+ }
+ case PT_4BUI:
+ {
+ uint8_t *ptr = mem;
+ uint8_t clamped_initval = rt_util_clamp_to_4BUI(nodataval);
+ for (i = 0; i < numval; i++)
+ ptr[i] = clamped_initval;
+ checkvalint = ptr[0];
+ break;
+ }
+ case PT_8BSI:
+ {
+ int8_t *ptr = mem;
+ int8_t clamped_initval = rt_util_clamp_to_8BSI(nodataval);
+ for (i = 0; i < numval; i++)
+ ptr[i] = clamped_initval;
+ checkvalint = ptr[0];
+ break;
+ }
+ case PT_8BUI:
+ {
+ uint8_t *ptr = mem;
+ uint8_t clamped_initval = rt_util_clamp_to_8BUI(nodataval);
+ for (i = 0; i < numval; i++)
+ ptr[i] = clamped_initval;
+ checkvalint = ptr[0];
+ break;
+ }
+ case PT_16BSI:
+ {
+ int16_t *ptr = mem;
+ int16_t clamped_initval = rt_util_clamp_to_16BSI(nodataval);
+ for (i = 0; i < numval; i++)
+ ptr[i] = clamped_initval;
+ checkvalint = ptr[0];
+ break;
+ }
+ case PT_16BUI:
+ {
+ uint16_t *ptr = mem;
+ uint16_t clamped_initval = rt_util_clamp_to_16BUI(nodataval);
+ for (i = 0; i < numval; i++)
+ ptr[i] = clamped_initval;
+ checkvalint = ptr[0];
+ break;
+ }
+ case PT_32BSI:
+ {
+ int32_t *ptr = mem;
+ int32_t clamped_initval = rt_util_clamp_to_32BSI(nodataval);
+ for (i = 0; i < numval; i++)
+ ptr[i] = clamped_initval;
+ checkvalint = ptr[0];
+ break;
+ }
+ case PT_32BUI:
+ {
+ uint32_t *ptr = mem;
+ uint32_t clamped_initval = rt_util_clamp_to_32BUI(nodataval);
+ for (i = 0; i < numval; i++)
+ ptr[i] = clamped_initval;
+ checkvaluint = ptr[0];
+ break;
+ }
+ case PT_32BF:
+ {
+ float *ptr = mem;
+ float clamped_initval = rt_util_clamp_to_32F(nodataval);
+ for (i = 0; i < numval; i++)
+ ptr[i] = clamped_initval;
+ checkvalfloat = ptr[0];
+ break;
+ }
+ case PT_64BF:
+ {
+ double *ptr = mem;
+ for (i = 0; i < numval; i++)
+ ptr[i] = nodataval;
+ checkvaldouble = ptr[0];
+ break;
+ }
+ default:
+ {
+ rterror("rt_band_reclass: Unknown pixeltype %d", pixtype);
+ rtdealloc(mem);
+ return 0;
+ }
+ }
+
+ /* Overflow checking */
+ rt_util_dbl_trunc_warning(
+ nodataval,
+ checkvalint, checkvaluint,
+ checkvalfloat, checkvaldouble,
+ pixtype
+ );
+ }
+ RASTER_DEBUGF(3, "rt_band_reclass: width = %d height = %d", width, height);
+
+ band = rt_band_new_inline(width, height, pixtype, hasnodata, nodataval, mem);
+ if (!band) {
+ rterror("rt_band_reclass: Could not create new band");
+ rtdealloc(mem);
+ return 0;
+ }
+ rt_band_set_ownsdata_flag(band, 1); /* we DO own this data!!! */
+ RASTER_DEBUGF(3, "rt_band_reclass: new band @ %p", band);
+
+ for (x = 0; x < width; x++) {
+ for (y = 0; y < height; y++) {
+ rtn = rt_band_get_pixel(srcband, x, y, &ov, &isnodata);
+
+ /* error getting value, skip */
+ if (rtn != ES_NONE) {
+ RASTER_DEBUGF(3, "Cannot get value at %d, %d", x, y);
+ continue;
+ }
+
+ do {
+ do_nv = 0;
+
+ /* no data*/
+ if (hasnodata && isnodata) {
+ do_nv = 1;
+ break;
+ }
+
+ for (i = 0; i < exprcount; i++) {
+ expr = exprset[i];
+
+ /* ov matches min and max*/
+ if (
+ FLT_EQ(expr->src.min, ov) &&
+ FLT_EQ(expr->src.max, ov)
+ ) {
+ do_nv = 1;
+ break;
+ }
+
+ /* process min */
+ if ((
+ expr->src.exc_min && (
+ expr->src.min > ov ||
+ FLT_EQ(expr->src.min, ov)
+ )) || (
+ expr->src.inc_min && (
+ expr->src.min < ov ||
+ FLT_EQ(expr->src.min, ov)
+ )) || (
+ expr->src.min < ov
+ )) {
+ /* process max */
+ if ((
+ expr->src.exc_max && (
+ ov > expr->src.max ||
+ FLT_EQ(expr->src.max, ov)
+ )) || (
+ expr->src.inc_max && (
+ ov < expr->src.max ||
+ FLT_EQ(expr->src.max, ov)
+ )) || (
+ ov < expr->src.max
+ )) {
+ do_nv = 1;
+ break;
+ }
+ }
+ }
+ }
+ while (0);
+
+ /* no expression matched, do not continue */
+ if (!do_nv) continue;
+ RASTER_DEBUGF(3, "Using exprset[%d] unless NODATA", i);
+
+ /* converting a value from one range to another range
+ OldRange = (OldMax - OldMin)
+ NewRange = (NewMax - NewMin)
+ NewValue = (((OldValue - OldMin) * NewRange) / OldRange) + NewMin
+ */
+
+ /* NODATA */
+ if (hasnodata && isnodata) {
+ nv = nodataval;
+ }
+ /*
+ "src" min and max is the same, prevent division by zero
+ set nv to "dst" min, which should be the same as "dst" max
+ */
+ else if (FLT_EQ(expr->src.max, expr->src.min)) {
+ nv = expr->dst.min;
+ }
+ else {
+ or = expr->src.max - expr->src.min;
+ nr = expr->dst.max - expr->dst.min;
+ nv = (((ov - expr->src.min) * nr) / or) + expr->dst.min;
+
+ /* if dst range is from high to low */
+ if (expr->dst.min > expr->dst.max) {
+ if (nv > expr->dst.min)
+ nv = expr->dst.min;
+ else if (nv < expr->dst.max)
+ nv = expr->dst.max;
+ }
+ /* if dst range is from low to high */
+ else {
+ if (nv < expr->dst.min)
+ nv = expr->dst.min;
+ else if (nv > expr->dst.max)
+ nv = expr->dst.max;
+ }
+ }
+
+ /* round the value for integers */
+ switch (pixtype) {
+ case PT_1BB:
+ case PT_2BUI:
+ case PT_4BUI:
+ case PT_8BSI:
+ case PT_8BUI:
+ case PT_16BSI:
+ case PT_16BUI:
+ case PT_32BSI:
+ case PT_32BUI:
+ nv = round(nv);
+ break;
+ default:
+ break;
+ }
+
+ RASTER_DEBUGF(4, "(%d, %d) ov: %f or: %f - %f nr: %f - %f nv: %f"
+ , x
+ , y
+ , ov
+ , (NULL != expr) ? expr->src.min : 0
+ , (NULL != expr) ? expr->src.max : 0
+ , (NULL != expr) ? expr->dst.min : 0
+ , (NULL != expr) ? expr->dst.max : 0
+ , nv
+ );
+ if (rt_band_set_pixel(band, x, y, nv, NULL) != ES_NONE) {
+ rterror("rt_band_reclass: Could not assign value to new band");
+ rt_band_destroy(band);
+ rtdealloc(mem);
+ return 0;
+ }
+
+ expr = NULL;
+ }
+ }
+
+ return band;
+}
+
+/******************************************************************************
+* rt_raster_iterator()
+******************************************************************************/
+
+typedef struct _rti_iterator_arg_t* _rti_iterator_arg;
+struct _rti_iterator_arg_t {
+ int count;
+
+ rt_raster *raster;
+ int *isempty;
+ double **offset;
+ int *width;
+ int *height;
+
+ struct {
+ rt_band *rtband;
+ int *hasnodata;
+ int *isnodata;
+ double *nodataval;
+ double *minval;
+ } band;
+
+ struct {
+ uint16_t x;
+ uint16_t y;
+ } distance;
+
+ struct {
+ uint32_t rows;
+ uint32_t columns;
+ } dimension;
+
+ struct {
+ double **values;
+ int **nodata;
+ } empty;
+
+ rt_iterator_arg arg;
+};
+
+static _rti_iterator_arg
+_rti_iterator_arg_init() {
+ _rti_iterator_arg _param;
+
+ _param = rtalloc(sizeof(struct _rti_iterator_arg_t));
+ if (_param == NULL) {
+ rterror("_rti_iterator_arg_init: Could not allocate memory for _rti_iterator_arg");
+ return NULL;
+ }
+
+ _param->count = 0;
+
+ _param->raster = NULL;
+ _param->isempty = NULL;
+ _param->offset = NULL;
+ _param->width = NULL;
+ _param->height = NULL;
+
+ _param->band.rtband = NULL;
+ _param->band.hasnodata = NULL;
+ _param->band.isnodata = NULL;
+ _param->band.nodataval = NULL;
+ _param->band.minval = NULL;
+
+ _param->distance.x = 0;
+ _param->distance.y = 0;
+
+ _param->dimension.rows = 0;
+ _param->dimension.columns = 0;
+
+ _param->empty.values = NULL;
+ _param->empty.nodata = NULL;
+
+ _param->arg = NULL;
+
+ return _param;
+}
+
+static void
+_rti_iterator_arg_destroy(_rti_iterator_arg _param) {
+ int i = 0;
+
+ if (_param->raster != NULL)
+ rtdealloc(_param->raster);
+ if (_param->isempty != NULL)
+ rtdealloc(_param->isempty);
+ if (_param->width != NULL)
+ rtdealloc(_param->width);
+ if (_param->height != NULL)
+ rtdealloc(_param->height);
+
+ if (_param->band.rtband != NULL)
+ rtdealloc(_param->band.rtband);
+ if (_param->band.hasnodata != NULL)
+ rtdealloc(_param->band.hasnodata);
+ if (_param->band.isnodata != NULL)
+ rtdealloc(_param->band.isnodata);
+ if (_param->band.nodataval != NULL)
+ rtdealloc(_param->band.nodataval);
+ if (_param->band.minval != NULL)
+ rtdealloc(_param->band.minval);
+
+ if (_param->offset != NULL) {
+ for (i = 0; i < _param->count; i++) {
+ if (_param->offset[i] == NULL)
+ continue;
+ rtdealloc(_param->offset[i]);
+ }
+ rtdealloc(_param->offset);
+ }
+
+ if (_param->empty.values != NULL) {
+ for (i = 0; i < _param->dimension.rows; i++) {
+ if (_param->empty.values[i] == NULL)
+ continue;
+ rtdealloc(_param->empty.values[i]);
+ }
+ rtdealloc(_param->empty.values);
+ }
+ if (_param->empty.nodata != NULL) {
+ for (i = 0; i < _param->dimension.rows; i++) {
+ if (_param->empty.nodata[i] == NULL)
+ continue;
+ rtdealloc(_param->empty.nodata[i]);
+ }
+ rtdealloc(_param->empty.nodata);
+ }
+
+ if (_param->arg != NULL) {
+ if (_param->arg->values != NULL)
+ rtdealloc(_param->arg->values);
+ if (_param->arg->nodata != NULL)
+ rtdealloc(_param->arg->nodata);
+ if (_param->arg->src_pixel != NULL) {
+ for (i = 0; i < _param->count; i++) {
+ if (_param->arg->src_pixel[i] == NULL)
+ continue;
+ rtdealloc(_param->arg->src_pixel[i]);
+ }
+
+ rtdealloc(_param->arg->src_pixel);
+ }
+
+ rtdealloc(_param->arg);
+ }
+
+ rtdealloc(_param);
+}
+
+static int
+_rti_iterator_arg_populate(
+ _rti_iterator_arg _param,
+ rt_iterator itrset, uint16_t itrcount,
+ uint16_t distancex, uint16_t distancey,
+ int *allnull, int *allempty
+) {
+ int i = 0;
+ int hasband = 0;
+
+ _param->count = itrcount;
+ _param->distance.x = distancex;
+ _param->distance.y = distancey;
+ _param->dimension.columns = distancex * 2 + 1;
+ _param->dimension.rows = distancey * 2 + 1;
+
+ /* allocate memory for children */
+ _param->raster = rtalloc(sizeof(rt_raster) * itrcount);
+ _param->isempty = rtalloc(sizeof(int) * itrcount);
+ _param->width = rtalloc(sizeof(int) * itrcount);
+ _param->height = rtalloc(sizeof(int) * itrcount);
+
+ _param->offset = rtalloc(sizeof(double *) * itrcount);
+
+ _param->band.rtband = rtalloc(sizeof(rt_band) * itrcount);
+ _param->band.hasnodata = rtalloc(sizeof(int) * itrcount);
+ _param->band.isnodata = rtalloc(sizeof(int) * itrcount);
+ _param->band.nodataval = rtalloc(sizeof(double) * itrcount);
+ _param->band.minval = rtalloc(sizeof(double) * itrcount);
+
+ if (
+ _param->raster == NULL ||
+ _param->isempty == NULL ||
+ _param->width == NULL ||
+ _param->height == NULL ||
+ _param->offset == NULL ||
+ _param->band.rtband == NULL ||
+ _param->band.hasnodata == NULL ||
+ _param->band.isnodata == NULL ||
+ _param->band.nodataval == NULL ||
+ _param->band.minval == NULL
+ ) {
+ rterror("_rti_iterator_arg_populate: Could not allocate memory for children of _rti_iterator_arg");
+ return 0;
+ }
+
+ *allnull = 0;
+ *allempty = 0;
+
+ /*
+ check input rasters
+ not empty, band # is valid
+ copy raster pointers and set flags
+ */
+ for (i = 0; i < itrcount; i++) {
+ /* initialize elements */
+ _param->raster[i] = NULL;
+ _param->isempty[i] = 0;
+ _param->width[i] = 0;
+ _param->height[i] = 0;
+
+ _param->offset[i] = NULL;
+
+ _param->band.rtband[i] = NULL;
+ _param->band.hasnodata[i] = 0;
+ _param->band.isnodata[i] = 0;
+ _param->band.nodataval[i] = 0;
+ _param->band.minval[i] = 0;
+
+ /* set isempty */
+ if (itrset[i].raster == NULL) {
+ _param->isempty[i] = 1;
+
+ (*allnull)++;
+ (*allempty)++;
+
+ continue;
+ }
+ else if (rt_raster_is_empty(itrset[i].raster)) {
+ _param->isempty[i] = 1;
+
+ (*allempty)++;
+
+ continue;
+ }
+
+ /* check band number */
+ hasband = rt_raster_has_band(itrset[i].raster, itrset[i].nband);
+ if (!hasband) {
+ if (!itrset[i].nbnodata) {
+ rterror("_rti_iterator_arg_populate: Band %d not found for raster %d", itrset[i].nband, i);
+ return 0;
+ }
+ else {
+ RASTER_DEBUGF(4, "Band %d not found for raster %d. Using NODATA", itrset[i].nband, i);
+ }
+ }
+
+ _param->raster[i] = itrset[i].raster;
+ if (hasband) {
+ _param->band.rtband[i] = rt_raster_get_band(itrset[i].raster, itrset[i].nband);
+ if (_param->band.rtband[i] == NULL) {
+ rterror("_rti_iterator_arg_populate: Could not get band %d for raster %d", itrset[i].nband, i);
+ return 0;
+ }
+
+ /* hasnodata */
+ _param->band.hasnodata[i] = rt_band_get_hasnodata_flag(_param->band.rtband[i]);
+
+ /* hasnodata = TRUE */
+ if (_param->band.hasnodata[i]) {
+ /* nodataval */
+ rt_band_get_nodata(_param->band.rtband[i], &(_param->band.nodataval[i]));
+
+ /* isnodata */
+ _param->band.isnodata[i] = rt_band_get_isnodata_flag(_param->band.rtband[i]);
+ }
+ /* hasnodata = FALSE */
+ else {
+ /* minval */
+ _param->band.minval[i] = rt_band_get_min_value(_param->band.rtband[i]);
+ }
+ }
+
+ /* width, height */
+ _param->width[i] = rt_raster_get_width(_param->raster[i]);
+ _param->height[i] = rt_raster_get_height(_param->raster[i]);
+
+ /* init offset */
+ _param->offset[i] = rtalloc(sizeof(double) * 2);
+ if (_param->offset[i] == NULL) {
+ rterror("_rti_iterator_arg_populate: Could not allocate memory for offsets");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int
+_rti_iterator_arg_empty_init(_rti_iterator_arg _param) {
+ int x = 0;
+ int y = 0;
+
+ _param->empty.values = rtalloc(sizeof(double *) * _param->dimension.rows);
+ _param->empty.nodata = rtalloc(sizeof(int *) * _param->dimension.rows);
+ if (_param->empty.values == NULL || _param->empty.nodata == NULL) {
+ rterror("_rti_iterator_arg_empty_init: Could not allocate memory for empty values and NODATA");
+ return 0;
+ }
+
+ for (y = 0; y < _param->dimension.rows; y++) {
+ _param->empty.values[y] = rtalloc(sizeof(double) * _param->dimension.columns);
+ _param->empty.nodata[y] = rtalloc(sizeof(int) * _param->dimension.columns);
+
+ if (_param->empty.values[y] == NULL || _param->empty.nodata[y] == NULL) {
+ rterror("_rti_iterator_arg_empty_init: Could not allocate memory for elements of empty values and NODATA");
+ return 0;
+ }
+
+ for (x = 0; x < _param->dimension.columns; x++) {
+ _param->empty.values[y][x] = 0;
+ _param->empty.nodata[y][x] = 1;
+ }
+ }
+
+ return 1;
+}
+
+static int
+_rti_iterator_arg_callback_init(_rti_iterator_arg _param) {
+ int i = 0;
+
+ _param->arg = rtalloc(sizeof(struct rt_iterator_arg_t));
+ if (_param->arg == NULL) {
+ rterror("_rti_iterator_arg_callback_init: Could not allocate memory for rt_iterator_arg");
+ return 0;
+ }
+
+ _param->arg->values = NULL;
+ _param->arg->nodata = NULL;
+ _param->arg->src_pixel = NULL;
+
+ /* initialize argument components */
+ _param->arg->values = rtalloc(sizeof(double **) * _param->count);
+ _param->arg->nodata = rtalloc(sizeof(int **) * _param->count);
+ _param->arg->src_pixel = rtalloc(sizeof(int *) * _param->count);
+ if (_param->arg->values == NULL || _param->arg->nodata == NULL || _param->arg->src_pixel == NULL) {
+ rterror("_rti_iterator_arg_callback_init: Could not allocate memory for element of rt_iterator_arg");
+ return 0;
+ }
+ memset(_param->arg->values, 0, sizeof(double **) * _param->count);
+ memset(_param->arg->nodata, 0, sizeof(int **) * _param->count);
+
+ /* initialize pos */
+ for (i = 0; i < _param->count; i++) {
+
+ _param->arg->src_pixel[i] = rtalloc(sizeof(int) * 2);
+ if (_param->arg->src_pixel[i] == NULL) {
+ rterror("_rti_iterator_arg_callback_init: Could not allocate memory for position elements of rt_iterator_arg");
+ return 0;
+ }
+ memset(_param->arg->src_pixel[i], 0, sizeof(int) * 2);
+ }
+
+ _param->arg->rasters = _param->count;
+ _param->arg->rows = _param->dimension.rows;
+ _param->arg->columns = _param->dimension.columns;
+
+ _param->arg->dst_pixel[0] = 0;
+ _param->arg->dst_pixel[1] = 0;
+
+ return 1;
+}
+
+static void
+_rti_iterator_arg_callback_clean(_rti_iterator_arg _param) {
+ int i = 0;
+ int y = 0;
+
+ for (i = 0; i < _param->count; i++) {
+ RASTER_DEBUGF(5, "empty at @ %p", _param->empty.values);
+ RASTER_DEBUGF(5, "values at @ %p", _param->arg->values[i]);
+
+ if (_param->arg->values[i] == _param->empty.values) {
+ _param->arg->values[i] = NULL;
+ _param->arg->nodata[i] = NULL;
+
+ continue;
+ }
+
+ for (y = 0; y < _param->dimension.rows; y++) {
+ rtdealloc(_param->arg->values[i][y]);
+ rtdealloc(_param->arg->nodata[i][y]);
+ }
+
+ rtdealloc(_param->arg->values[i]);
+ rtdealloc(_param->arg->nodata[i]);
+
+ _param->arg->values[i] = NULL;
+ _param->arg->nodata[i] = NULL;
+ }
+}
+
+/**
+ * n-raster iterator.
+ * The raster returned should be freed by the caller
+ *
+ * @param itrset : set of rt_iterator objects.
+ * @param itrcount : number of objects in itrset.
+ * @param extenttype : type of extent for the output raster.
+ * @param customextent : raster specifying custom extent.
+ * is only used if extenttype is ET_CUSTOM.
+ * @param pixtype : the desired pixel type of the output raster's band.
+ * @param hasnodata : indicates if the band has nodata value
+ * @param nodataval : the nodata value, will be appropriately
+ * truncated to fit the pixtype size.
+ * @param distancex : the number of pixels around the specified pixel
+ * along the X axis
+ * @param distancey : the number of pixels around the specified pixel
+ * along the Y axis
+ * @param userarg : pointer to any argument that is passed as-is to callback.
+ * @param callback : callback function for actual processing of pixel values.
+ * @param *rtnraster : return one band raster from iterator process
+ *
+ * The callback function _must_ have the following signature.
+ *
+ * int FNAME(rt_iterator_arg arg, void *userarg, double *value, int *nodata)
+ *
+ * The callback function _must_ return zero (error) or non-zero (success)
+ * indicating whether the function ran successfully.
+ * The parameters passed to the callback function are as follows.
+ *
+ * - rt_iterator_arg arg: struct containing pixel values, NODATA flags and metadata
+ * - void *userarg: NULL or calling function provides to rt_raster_iterator() for use by callback function
+ * - double *value: value of pixel to be burned by rt_raster_iterator()
+ * - int *nodata: flag (0 or 1) indicating that pixel to be burned is NODATA
+ *
+ * @return ES_NONE on success, ES_ERROR on error
+ */
+rt_errorstate
+rt_raster_iterator(
+ rt_iterator itrset, uint16_t itrcount,
+ rt_extenttype extenttype, rt_raster customextent,
+ rt_pixtype pixtype,
+ uint8_t hasnodata, double nodataval,
+ uint16_t distancex, uint16_t distancey,
+ void *userarg,
+ int (*callback)(
+ rt_iterator_arg arg,
+ void *userarg,
+ double *value,
+ int *nodata
+ ),
+ rt_raster *rtnraster
+) {
+ /* output raster */
+ rt_raster rtnrast = NULL;
+ /* output raster's band */
+ rt_band rtnband = NULL;
+
+ /* working raster */
+ rt_raster rast = NULL;
+
+ _rti_iterator_arg _param = NULL;
+ int allnull = 0;
+ int allempty = 0;
+ int aligned = 0;
+ double offset[4] = {0.};
+ rt_pixel npixels;
+
+ int i = 0;
+ int status = 0;
+ int inextent = 0;
+ int x = 0;
+ int y = 0;
+ int _x = 0;
+ int _y = 0;
+
+ int _width = 0;
+ int _height = 0;
+
+ double minval;
+ double value;
+ int isnodata;
+ int nodata;
+
+ RASTER_DEBUG(3, "Starting...");
+
+ assert(itrset != NULL && itrcount > 0);
+ assert(rtnraster != NULL);
+
+ /* init rtnraster to NULL */
+ *rtnraster = NULL;
+
+ /* check that callback function is not NULL */
+ if (callback == NULL) {
+ rterror("rt_raster_iterator: Callback function not provided");
+ return ES_ERROR;
+ }
+
+ /* check that custom extent is provided if extenttype = ET_CUSTOM */
+ if (extenttype == ET_CUSTOM && rt_raster_is_empty(customextent)) {
+ rterror("rt_raster_iterator: Custom extent cannot be empty if extent type is ET_CUSTOM");
+ return ES_ERROR;
+ }
+
+ /* check that pixtype != PT_END */
+ if (pixtype == PT_END) {
+ rterror("rt_raster_iterator: Pixel type cannot be PT_END");
+ return ES_ERROR;
+ }
+
+ /* initialize _param */
+ if ((_param = _rti_iterator_arg_init()) == NULL) {
+ rterror("rt_raster_iterator: Could not initialize internal variables");
+ return ES_ERROR;
+ }
+
+ /* fill _param */
+ if (!_rti_iterator_arg_populate(_param, itrset, itrcount, distancex, distancey, &allnull, &allempty)) {
+ rterror("rt_raster_iterator: Could not populate for internal variables");
+ _rti_iterator_arg_destroy(_param);
+ return ES_ERROR;
+ }
+
+ /* shortcut if all null, return NULL */
+ if (allnull == itrcount) {
+ RASTER_DEBUG(3, "all rasters are NULL, returning NULL");
+
+ _rti_iterator_arg_destroy(_param);
+
+ return ES_NONE;
+ }
+ /* shortcut if all empty, return empty raster */
+ else if (allempty == itrcount) {
+ RASTER_DEBUG(3, "all rasters are empty, returning empty raster");
+
+ _rti_iterator_arg_destroy(_param);
+
+ rtnrast = rt_raster_new(0, 0);
+ if (rtnrast == NULL) {
+ rterror("rt_raster_iterator: Could not create empty raster");
+ return ES_ERROR;
+ }
+ rt_raster_set_scale(rtnrast, 0, 0);
+
+ *rtnraster = rtnrast;
+ return ES_NONE;
+ }
+
+ /* check that all rasters are aligned */
+ RASTER_DEBUG(3, "checking alignment of all rasters");
+ rast = NULL;
+
+ /* find raster to use as reference */
+ /* use custom if provided */
+ if (extenttype == ET_CUSTOM) {
+ RASTER_DEBUG(4, "using custom extent as reference raster");
+ rast = customextent;
+ }
+ /* use first valid one in _param->raster */
+ else {
+ for (i = 0; i < itrcount; i++) {
+ if (!_param->isempty[i]) {
+ RASTER_DEBUGF(4, "using raster at index %d as reference raster", i);
+ rast = _param->raster[i];
+ break;
+ }
+ }
+ }
+
+ /* no rasters found, SHOULD NEVER BE HERE! */
+ if (rast == NULL) {
+ rterror("rt_raster_iterator: Could not find reference raster to use for alignment tests");
+
+ _rti_iterator_arg_destroy(_param);
+
+ return ES_ERROR;
+ }
+
+ do {
+ aligned = 1;
+
+ /* check custom first if set. also skip if rasters are the same */
+ if (extenttype == ET_CUSTOM && rast != customextent) {
+ if (rt_raster_same_alignment(rast, customextent, &aligned, NULL) != ES_NONE) {
+ rterror("rt_raster_iterator: Could not test for alignment between reference raster and custom extent");
+
+ _rti_iterator_arg_destroy(_param);
+
+ return ES_ERROR;
+ }
+
+ RASTER_DEBUGF(5, "custom extent alignment: %d", aligned);
+ if (!aligned)
+ break;
+ }
+
+ for (i = 0; i < itrcount; i++) {
+ /* skip NULL rasters and if rasters are the same */
+ if (_param->isempty[i] || rast == _param->raster[i])
+ continue;
+
+ if (rt_raster_same_alignment(rast, _param->raster[i], &aligned, NULL) != ES_NONE) {
+ rterror("rt_raster_iterator: Could not test for alignment between reference raster and raster %d", i);
+
+ _rti_iterator_arg_destroy(_param);
+
+ return ES_ERROR;
+ }
+ RASTER_DEBUGF(5, "raster at index %d alignment: %d", i, aligned);
+
+ /* abort checking since a raster isn't aligned */
+ if (!aligned)
+ break;
+ }
+ }
+ while (0);
+
+ /* not aligned, error */
+ if (!aligned) {
+ rterror("rt_raster_iterator: The set of rasters provided (custom extent included, if appropriate) do not have the same alignment");
+
+ _rti_iterator_arg_destroy(_param);
+
+ return ES_ERROR;
+ }
+
+ /* use extenttype to build output raster (no bands though) */
+ i = -1;
+ switch (extenttype) {
+ case ET_INTERSECTION:
+ case ET_UNION:
+ /* make copy of first "real" raster */
+ rtnrast = rtalloc(sizeof(struct rt_raster_t));
+ if (rtnrast == NULL) {
+ rterror("rt_raster_iterator: Could not allocate memory for output raster");
+
+ _rti_iterator_arg_destroy(_param);
+
+ return ES_ERROR;
+ }
+
+ for (i = 0; i < itrcount; i++) {
+ if (!_param->isempty[i]) {
+ memcpy(rtnrast, _param->raster[i], sizeof(struct rt_raster_serialized_t));
+ break;
+ }
+ }
+ rtnrast->numBands = 0;
+ rtnrast->bands = NULL;
+
+ /* get extent of output raster */
+ rast = NULL;
+ for (i = i + 1; i < itrcount; i++) {
+ if (_param->isempty[i])
+ continue;
+
+ status = rt_raster_from_two_rasters(rtnrast, _param->raster[i], extenttype, &rast, NULL);
+ rtdealloc(rtnrast);
+
+ if (rast == NULL || status != ES_NONE) {
+ rterror("rt_raster_iterator: Could not compute %s extent of rasters",
+ extenttype == ET_UNION ? "union" : "intersection"
+ );
+
+ _rti_iterator_arg_destroy(_param);
+
+ return ES_ERROR;
+ }
+ else if (rt_raster_is_empty(rast)) {
+ rtinfo("rt_raster_iterator: Computed raster for %s extent is empty",
+ extenttype == ET_UNION ? "union" : "intersection"
+ );
+
+ _rti_iterator_arg_destroy(_param);
+
+ *rtnraster = rast;
+ return ES_NONE;
+ }
+
+ rtnrast = rast;
+ rast = NULL;
+ }
+
+ break;
+ /*
+ first, second and last have similar checks
+ and continue into custom
+ */
+ case ET_FIRST:
+ i = 0;
+ case ET_SECOND:
+ if (i < 0) {
+ if (itrcount < 2)
+ i = 0;
+ else
+ i = 1;
+ }
+ case ET_LAST:
+ if (i < 0) i = itrcount - 1;
+
+ /* input raster is null, return NULL */
+ if (_param->raster[i] == NULL) {
+ RASTER_DEBUGF(3, "returning NULL as %s raster is NULL and extent type is ET_%s",
+ (i == 0 ? "first" : (i == 1 ? "second" : "last")),
+ (i == 0 ? "FIRST" : (i == 1 ? "SECOND" : "LAST"))
+ );
+
+ _rti_iterator_arg_destroy(_param);
+
+ return ES_NONE;
+ }
+ /* input raster is empty, return empty raster */
+ else if (_param->isempty[i]) {
+ RASTER_DEBUGF(3, "returning empty raster as %s raster is empty and extent type is ET_%s",
+ (i == 0 ? "first" : (i == 1 ? "second" : "last")),
+ (i == 0 ? "FIRST" : (i == 1 ? "SECOND" : "LAST"))
+ );
+
+ _rti_iterator_arg_destroy(_param);
+
+ rtnrast = rt_raster_new(0, 0);
+ if (rtnrast == NULL) {
+ rterror("rt_raster_iterator: Could not create empty raster");
+ return ES_ERROR;
+ }
+ rt_raster_set_scale(rtnrast, 0, 0);
+
+ *rtnraster = rtnrast;
+ return ES_NONE;
+ }
+ /* copy the custom extent raster */
+ case ET_CUSTOM:
+ rtnrast = rtalloc(sizeof(struct rt_raster_t));
+ if (rtnrast == NULL) {
+ rterror("rt_raster_iterator: Could not allocate memory for output raster");
+
+ _rti_iterator_arg_destroy(_param);
+
+ return ES_ERROR;
+ }
+
+ switch (extenttype) {
+ case ET_CUSTOM:
+ memcpy(rtnrast, customextent, sizeof(struct rt_raster_serialized_t));
+ break;
+ /* first, second, last */
+ default:
+ memcpy(rtnrast, _param->raster[i], sizeof(struct rt_raster_serialized_t));
+ break;
+ }
+ rtnrast->numBands = 0;
+ rtnrast->bands = NULL;
+ break;
+ }
+
+ _width = rt_raster_get_width(rtnrast);
+ _height = rt_raster_get_height(rtnrast);
+
+ RASTER_DEBUGF(4, "rtnrast (width, height, ulx, uly, scalex, scaley, skewx, skewy, srid) = (%d, %d, %f, %f, %f, %f, %f, %f, %d)",
+ _width,
+ _height,
+ rt_raster_get_x_offset(rtnrast),
+ rt_raster_get_y_offset(rtnrast),
+ rt_raster_get_x_scale(rtnrast),
+ rt_raster_get_y_scale(rtnrast),
+ rt_raster_get_x_skew(rtnrast),
+ rt_raster_get_y_skew(rtnrast),
+ rt_raster_get_srid(rtnrast)
+ );
+
+ /* init values and NODATA for use with empty rasters */
+ if (!_rti_iterator_arg_empty_init(_param)) {
+ rterror("rt_raster_iterator: Could not initialize empty values and NODATA");
+
+ _rti_iterator_arg_destroy(_param);
+ rt_raster_destroy(rtnrast);
+
+ return ES_ERROR;
+ }
+
+ /* create output band */
+ if (rt_raster_generate_new_band(
+ rtnrast,
+ pixtype,
+ nodataval,
+ hasnodata, nodataval,
+ 0
+ ) < 0) {
+ rterror("rt_raster_iterator: Could not add new band to output raster");
+
+ _rti_iterator_arg_destroy(_param);
+ rt_raster_destroy(rtnrast);
+
+ return ES_ERROR;
+ }
+
+ /* get output band */
+ rtnband = rt_raster_get_band(rtnrast, 0);
+ if (rtnband == NULL) {
+ rterror("rt_raster_iterator: Could not get new band from output raster");
+
+ _rti_iterator_arg_destroy(_param);
+ rt_raster_destroy(rtnrast);
+
+ return ES_ERROR;
+ }
+
+ /* output band's minimum value */
+ minval = rt_band_get_min_value(rtnband);
+
+ /* initialize argument for callback function */
+ if (!_rti_iterator_arg_callback_init(_param)) {
+ rterror("rt_raster_iterator: Could not initialize callback function argument");
+
+ _rti_iterator_arg_destroy(_param);
+ rt_band_destroy(rtnband);
+ rt_raster_destroy(rtnrast);
+
+ return ES_ERROR;
+ }
+
+ /* fill _param->offset */
+ for (i = 0; i < itrcount; i++) {
+ if (_param->isempty[i])
+ continue;
+
+ status = rt_raster_from_two_rasters(rtnrast, _param->raster[i], ET_FIRST, &rast, offset);
+ rtdealloc(rast);
+ if (status != ES_NONE) {
+ rterror("rt_raster_iterator: Could not compute raster offsets");
+
+ _rti_iterator_arg_destroy(_param);
+ rt_band_destroy(rtnband);
+ rt_raster_destroy(rtnrast);
+
+ return ES_ERROR;
+ }
+
+ _param->offset[i][0] = offset[2];
+ _param->offset[i][1] = offset[3];
+ RASTER_DEBUGF(4, "rast %d offset: %f %f", i, offset[2], offset[3]);
+ }
+
+ /* loop over each pixel (POI) of output raster */
+ /* _x,_y are for output raster */
+ /* x,y are for input raster */
+ for (_y = 0; _y < _height; _y++) {
+ for (_x = 0; _x < _width; _x++) {
+ RASTER_DEBUGF(4, "iterating output pixel (x, y) = (%d, %d)", _x, _y);
+ _param->arg->dst_pixel[0] = _x;
+ _param->arg->dst_pixel[1] = _y;
+
+ /* loop through each input raster */
+ for (i = 0; i < itrcount; i++) {
+ RASTER_DEBUGF(4, "raster %d", i);
+
+ /*
+ empty raster
+ OR band does not exist and flag set to use NODATA
+ OR band is NODATA
+ */
+ if (
+ _param->isempty[i] ||
+ (_param->band.rtband[i] == NULL && itrset[i].nbnodata) ||
+ _param->band.isnodata[i]
+ ) {
+ RASTER_DEBUG(4, "empty raster, band does not exist or band is NODATA. using empty values and NODATA");
+
+ x = _x;
+ y = _y;
+
+ _param->arg->values[i] = _param->empty.values;
+ _param->arg->nodata[i] = _param->empty.nodata;
+
+ continue;
+ }
+
+ /* input raster's X,Y */
+ x = _x - (int) _param->offset[i][0];
+ y = _y - (int) _param->offset[i][1];
+ RASTER_DEBUGF(4, "source pixel (x, y) = (%d, %d)", x, y);
+
+ _param->arg->src_pixel[i][0] = x;
+ _param->arg->src_pixel[i][1] = y;
+
+ /* neighborhood */
+ npixels = NULL;
+ status = 0;
+ if (distancex > 0 && distancey > 0) {
+ RASTER_DEBUG(4, "getting neighborhood");
+
+ status = rt_band_get_nearest_pixel(
+ _param->band.rtband[i],
+ x, y,
+ distancex, distancey,
+ 1,
+ &npixels
+ );
+ if (status < 0) {
+ rterror("rt_raster_iterator: Could not get pixel neighborhood");
+
+ _rti_iterator_arg_destroy(_param);
+ rt_band_destroy(rtnband);
+ rt_raster_destroy(rtnrast);
+
+ return ES_ERROR;
+ }
+ }
+
+ /* get value of POI */
+ /* get pixel's value */
+ if (
+ (x >= 0 && x < _param->width[i]) &&
+ (y >= 0 && y < _param->height[i])
+ ) {
+ RASTER_DEBUG(4, "getting value of POI");
+ if (rt_band_get_pixel(
+ _param->band.rtband[i],
+ x, y,
+ &value,
+ &isnodata
+ ) != ES_NONE) {
+ rterror("rt_raster_iterator: Could not get the pixel value of band");
+
+ _rti_iterator_arg_destroy(_param);
+ rt_band_destroy(rtnband);
+ rt_raster_destroy(rtnrast);
+
+ return ES_ERROR;
+ }
+ inextent = 1;
+ }
+ /* outside band extent, set to NODATA */
+ else {
+ RASTER_DEBUG(4, "Outside band extent, setting value to NODATA");
+ /* has NODATA, use NODATA */
+ if (_param->band.hasnodata[i])
+ value = _param->band.nodataval[i];
+ /* no NODATA, use min possible value */
+ else
+ value = _param->band.minval[i];
+
+ inextent = 0;
+ isnodata = 1;
+ }
+
+ /* add pixel to neighborhood */
+ status++;
+ if (status > 1)
+ npixels = (rt_pixel) rtrealloc(npixels, sizeof(struct rt_pixel_t) * status);
+ else
+ npixels = (rt_pixel) rtalloc(sizeof(struct rt_pixel_t));
+
+ if (npixels == NULL) {
+ rterror("rt_raster_iterator: Could not reallocate memory for neighborhood");
+
+ _rti_iterator_arg_destroy(_param);
+ rt_band_destroy(rtnband);
+ rt_raster_destroy(rtnrast);
+
+ return ES_ERROR;
+ }
+
+ npixels[status - 1].x = x;
+ npixels[status - 1].y = y;
+ npixels[status - 1].nodata = 1;
+ npixels[status - 1].value = value;
+
+ /* set nodata flag */
+ if ((!_param->band.hasnodata[i] && inextent) || !isnodata) {
+ npixels[status - 1].nodata = 0;
+ }
+ RASTER_DEBUGF(4, "value, nodata: %f, %d", value, npixels[status - 1].nodata);
+
+ /* convert set of rt_pixel to 2D array */
+ status = rt_pixel_set_to_array(
+ npixels, status,
+ x, y,
+ distancex, distancey,
+ &(_param->arg->values[i]),
+ &(_param->arg->nodata[i]),
+ NULL, NULL
+ );
+ rtdealloc(npixels);
+ if (status != ES_NONE) {
+ rterror("rt_raster_iterator: Could not create 2D array of neighborhood");
+
+ _rti_iterator_arg_destroy(_param);
+ rt_band_destroy(rtnband);
+ rt_raster_destroy(rtnrast);
+
+ return ES_ERROR;
+ }
+ }
+
+ /* callback */
+ RASTER_DEBUG(4, "calling callback function");
+ value = 0;
+ nodata = 0;
+ status = callback(_param->arg, userarg, &value, &nodata);
+
+ /* free memory from callback */
+ _rti_iterator_arg_callback_clean(_param);
+
+ /* handle callback status */
+ if (status == 0) {
+ rterror("rt_raster_iterator: Callback function returned an error");
+
+ _rti_iterator_arg_destroy(_param);
+ rt_band_destroy(rtnband);
+ rt_raster_destroy(rtnrast);
+
+ return ES_ERROR;
+ }
+
+ /* burn value to pixel */
+ status = 0;
+ if (!nodata) {
+ status = rt_band_set_pixel(rtnband, _x, _y, value, NULL);
+ RASTER_DEBUGF(4, "burning pixel (%d, %d) with value: %f", _x, _y, value);
+ }
+ else if (!hasnodata) {
+ status = rt_band_set_pixel(rtnband, _x, _y, minval, NULL);
+ RASTER_DEBUGF(4, "burning pixel (%d, %d) with minval: %f", _x, _y, minval);
+ }
+ else {
+ RASTER_DEBUGF(4, "NOT burning pixel (%d, %d)", _x, _y);
+ }
+ if (status != ES_NONE) {
+ rterror("rt_raster_iterator: Could not set pixel value");
+
+ _rti_iterator_arg_destroy(_param);
+ rt_band_destroy(rtnband);
+ rt_raster_destroy(rtnrast);
+
+ return ES_ERROR;
+ }
+ }
+ }
+
+ /* lots of cleanup */
+ _rti_iterator_arg_destroy(_param);
+
+ *rtnraster = rtnrast;
+ return ES_NONE;
+}
+
+/******************************************************************************
+* rt_raster_colormap()
+******************************************************************************/
+
+typedef struct _rti_colormap_arg_t* _rti_colormap_arg;
+struct _rti_colormap_arg_t {
+ rt_raster raster;
+ rt_band band;
+
+ rt_colormap_entry nodataentry;
+ int hasnodata;
+ double nodataval;
+
+ int nexpr;
+ rt_reclassexpr *expr;
+
+ int npos;
+ uint16_t *pos;
+
+};
+
+static _rti_colormap_arg
+_rti_colormap_arg_init(rt_raster raster) {
+ _rti_colormap_arg arg = NULL;
+
+ arg = rtalloc(sizeof(struct _rti_colormap_arg_t));
+ if (arg == NULL) {
+ rterror("_rti_colormap_arg_init: Could not allocate memory for _rti_color_arg");
+ return NULL;
+ }
+
+ arg->band = NULL;
+ arg->nodataentry = NULL;
+ arg->hasnodata = 0;
+ arg->nodataval = 0;
+
+ if (raster == NULL)
+ arg->raster = NULL;
+ /* raster provided */
+ else {
+ arg->raster = rt_raster_clone(raster, 0);
+ if (arg->raster == NULL) {
+ rterror("_rti_colormap_arg_init: Could not create output raster");
+ return NULL;
+ }
+ }
+
+ arg->nexpr = 0;
+ arg->expr = NULL;
+
+ arg->npos = 0;
+ arg->pos = NULL;
+
+ return arg;
+}
+
+static void
+_rti_colormap_arg_destroy(_rti_colormap_arg arg) {
+ int i = 0;
+
+ if (arg->raster != NULL) {
+ rt_band band = NULL;
+
+ for (i = rt_raster_get_num_bands(arg->raster) - 1; i >= 0; i--) {
+ band = rt_raster_get_band(arg->raster, i);
+ if (band != NULL)
+ rt_band_destroy(band);
+ }
+
+ rt_raster_destroy(arg->raster);
+ }
+
+ if (arg->nexpr) {
+ for (i = 0; i < arg->nexpr; i++) {
+ if (arg->expr[i] != NULL)
+ rtdealloc(arg->expr[i]);
+ }
+ rtdealloc(arg->expr);
+ }
+
+ if (arg->npos)
+ rtdealloc(arg->pos);
+
+ rtdealloc(arg);
+ arg = NULL;
+}
+
+/**
+ * Returns a new raster with up to four 8BUI bands (RGBA) from
+ * applying a colormap to the user-specified band of the
+ * input raster.
+ *
+ * @param raster: input raster
+ * @param nband: 0-based index of the band to process with colormap
+ * @param colormap: rt_colormap object of colormap to apply to band
+ *
+ * @return new raster or NULL on error
+ */
+rt_raster rt_raster_colormap(
+ rt_raster raster, int nband,
+ rt_colormap colormap
+) {
+ _rti_colormap_arg arg = NULL;
+ rt_raster rtnraster = NULL;
+ rt_band band = NULL;
+ int i = 0;
+ int j = 0;
+ int k = 0;
+
+ assert(colormap != NULL);
+
+ /* empty raster */
+ if (rt_raster_is_empty(raster))
+ return NULL;
+
+ /* no colormap entries */
+ if (colormap->nentry < 1) {
+ rterror("rt_raster_colormap: colormap must have at least one entry");
+ return NULL;
+ }
+
+ /* nband is valid */
+ if (!rt_raster_has_band(raster, nband)) {
+ rterror("rt_raster_colormap: raster has no band at index %d", nband);
+ return NULL;
+ }
+
+ band = rt_raster_get_band(raster, nband);
+ if (band == NULL) {
+ rterror("rt_raster_colormap: Could not get band at index %d", nband);
+ return NULL;
+ }
+
+ /* init internal variables */
+ arg = _rti_colormap_arg_init(raster);
+ if (arg == NULL) {
+ rterror("rt_raster_colormap: Could not initialize internal variables");
+ return NULL;
+ }
+
+ /* handle NODATA */
+ if (rt_band_get_hasnodata_flag(band)) {
+ arg->hasnodata = 1;
+ rt_band_get_nodata(band, &(arg->nodataval));
+ }
+
+ /* # of colors */
+ if (colormap->ncolor < 1) {
+ rterror("rt_raster_colormap: At least one color must be provided");
+ _rti_colormap_arg_destroy(arg);
+ return NULL;
+ }
+ else if (colormap->ncolor > 4) {
+ rtinfo("More than four colors indicated. Using only the first four colors");
+ colormap->ncolor = 4;
+ }
+
+ /* find non-NODATA entries */
+ arg->npos = 0;
+ arg->pos = rtalloc(sizeof(uint16_t) * colormap->nentry);
+ if (arg->pos == NULL) {
+ rterror("rt_raster_colormap: Could not allocate memory for valid entries");
+ _rti_colormap_arg_destroy(arg);
+ return NULL;
+ }
+ for (i = 0, j = 0; i < colormap->nentry; i++) {
+ /* special handling for NODATA entries */
+ if (colormap->entry[i].isnodata) {
+ /* first NODATA entry found, use it */
+ if (arg->nodataentry == NULL)
+ arg->nodataentry = &(colormap->entry[i]);
+ else
+ rtwarn("More than one colormap entry found for NODATA value. Only using first NOTDATA entry");
+
+ continue;
+ }
+
+ (arg->npos)++;
+ arg->pos[j++] = i;
+ }
+
+ /* INTERPOLATE and only one non-NODATA entry */
+ if (colormap->method == CM_INTERPOLATE && arg->npos < 2) {
+ rtinfo("Method INTERPOLATE requires at least two non-NODATA colormap entries. Using NEAREST instead");
+ colormap->method = CM_NEAREST;
+ }
+
+ /* NODATA entry but band has no NODATA value */
+ if (!arg->hasnodata && arg->nodataentry != NULL) {
+ rtinfo("Band at index %d has no NODATA value. Ignoring NODATA entry", nband);
+ arg->nodataentry = NULL;
+ }
+
+ /* allocate expr */
+ arg->nexpr = arg->npos;
+
+ /* INTERPOLATE needs one less than the number of entries */
+ if (colormap->method == CM_INTERPOLATE)
+ arg->nexpr -= 1;
+ /* EXACT requires a no matching expression */
+ else if (colormap->method == CM_EXACT)
+ arg->nexpr += 1;
+
+ /* NODATA entry exists, add expression */
+ if (arg->nodataentry != NULL)
+ arg->nexpr += 1;
+ arg->expr = rtalloc(sizeof(rt_reclassexpr) * arg->nexpr);
+ if (arg->expr == NULL) {
+ rterror("rt_raster_colormap: Could not allocate memory for reclass expressions");
+ _rti_colormap_arg_destroy(arg);
+ return NULL;
+ }
+ RASTER_DEBUGF(4, "nexpr = %d", arg->nexpr);
+ RASTER_DEBUGF(4, "expr @ %p", arg->expr);
+
+ for (i = 0; i < arg->nexpr; i++) {
+ arg->expr[i] = rtalloc(sizeof(struct rt_reclassexpr_t));
+ if (arg->expr[i] == NULL) {
+ rterror("rt_raster_colormap: Could not allocate memory for reclass expression");
+ _rti_colormap_arg_destroy(arg);
+ return NULL;
+ }
+ }
+
+ /* reclassify bands */
+ /* by # of colors */
+ for (i = 0; i < colormap->ncolor; i++) {
+ k = 0;
+
+ /* handle NODATA entry first */
+ if (arg->nodataentry != NULL) {
+ arg->expr[k]->src.min = arg->nodataentry->value;
+ arg->expr[k]->src.max = arg->nodataentry->value;
+ arg->expr[k]->src.inc_min = 1;
+ arg->expr[k]->src.inc_max = 1;
+ arg->expr[k]->src.exc_min = 0;
+ arg->expr[k]->src.exc_max = 0;
+
+ arg->expr[k]->dst.min = arg->nodataentry->color[i];
+ arg->expr[k]->dst.max = arg->nodataentry->color[i];
+
+ arg->expr[k]->dst.inc_min = 1;
+ arg->expr[k]->dst.inc_max = 1;
+ arg->expr[k]->dst.exc_min = 0;
+ arg->expr[k]->dst.exc_max = 0;
+
+ RASTER_DEBUGF(4, "NODATA expr[%d]->src (min, max, in, ix, en, ex) = (%f, %f, %d, %d, %d, %d)",
+ k,
+ arg->expr[k]->src.min,
+ arg->expr[k]->src.max,
+ arg->expr[k]->src.inc_min,
+ arg->expr[k]->src.inc_max,
+ arg->expr[k]->src.exc_min,
+ arg->expr[k]->src.exc_max
+ );
+ RASTER_DEBUGF(4, "NODATA expr[%d]->dst (min, max, in, ix, en, ex) = (%f, %f, %d, %d, %d, %d)",
+ k,
+ arg->expr[k]->dst.min,
+ arg->expr[k]->dst.max,
+ arg->expr[k]->dst.inc_min,
+ arg->expr[k]->dst.inc_max,
+ arg->expr[k]->dst.exc_min,
+ arg->expr[k]->dst.exc_max
+ );
+
+ k++;
+ }
+
+ /* by non-NODATA entry */
+ for (j = 0; j < arg->npos; j++) {
+ if (colormap->method == CM_INTERPOLATE) {
+ if (j == arg->npos - 1)
+ continue;
+
+ arg->expr[k]->src.min = colormap->entry[arg->pos[j + 1]].value;
+ arg->expr[k]->src.inc_min = 1;
+ arg->expr[k]->src.exc_min = 0;
+
+ arg->expr[k]->src.max = colormap->entry[arg->pos[j]].value;
+ arg->expr[k]->src.inc_max = 1;
+ arg->expr[k]->src.exc_max = 0;
+
+ arg->expr[k]->dst.min = colormap->entry[arg->pos[j + 1]].color[i];
+ arg->expr[k]->dst.max = colormap->entry[arg->pos[j]].color[i];
+
+ arg->expr[k]->dst.inc_min = 1;
+ arg->expr[k]->dst.exc_min = 0;
+
+ arg->expr[k]->dst.inc_max = 1;
+ arg->expr[k]->dst.exc_max = 0;
+ }
+ else if (colormap->method == CM_NEAREST) {
+
+ /* NOT last entry */
+ if (j != arg->npos - 1) {
+ arg->expr[k]->src.min = ((colormap->entry[arg->pos[j]].value - colormap->entry[arg->pos[j + 1]].value) / 2.) + colormap->entry[arg->pos[j + 1]].value;
+ arg->expr[k]->src.inc_min = 0;
+ arg->expr[k]->src.exc_min = 0;
+ }
+ /* last entry */
+ else {
+ arg->expr[k]->src.min = colormap->entry[arg->pos[j]].value;
+ arg->expr[k]->src.inc_min = 1;
+ arg->expr[k]->src.exc_min = 1;
+ }
+
+ /* NOT first entry */
+ if (j > 0) {
+ arg->expr[k]->src.max = arg->expr[k - 1]->src.min;
+ arg->expr[k]->src.inc_max = 1;
+ arg->expr[k]->src.exc_max = 0;
+ }
+ /* first entry */
+ else {
+ arg->expr[k]->src.max = colormap->entry[arg->pos[j]].value;
+ arg->expr[k]->src.inc_max = 1;
+ arg->expr[k]->src.exc_max = 1;
+ }
+
+ arg->expr[k]->dst.min = colormap->entry[arg->pos[j]].color[i];
+ arg->expr[k]->dst.inc_min = 1;
+ arg->expr[k]->dst.exc_min = 0;
+
+ arg->expr[k]->dst.max = colormap->entry[arg->pos[j]].color[i];
+ arg->expr[k]->dst.inc_max = 1;
+ arg->expr[k]->dst.exc_max = 0;
+ }
+ else if (colormap->method == CM_EXACT) {
+ arg->expr[k]->src.min = colormap->entry[arg->pos[j]].value;
+ arg->expr[k]->src.inc_min = 1;
+ arg->expr[k]->src.exc_min = 0;
+
+ arg->expr[k]->src.max = colormap->entry[arg->pos[j]].value;
+ arg->expr[k]->src.inc_max = 1;
+ arg->expr[k]->src.exc_max = 0;
+
+ arg->expr[k]->dst.min = colormap->entry[arg->pos[j]].color[i];
+ arg->expr[k]->dst.inc_min = 1;
+ arg->expr[k]->dst.exc_min = 0;
+
+ arg->expr[k]->dst.max = colormap->entry[arg->pos[j]].color[i];
+ arg->expr[k]->dst.inc_max = 1;
+ arg->expr[k]->dst.exc_max = 0;
+ }
+
+ RASTER_DEBUGF(4, "expr[%d]->src (min, max, in, ix, en, ex) = (%f, %f, %d, %d, %d, %d)",
+ k,
+ arg->expr[k]->src.min,
+ arg->expr[k]->src.max,
+ arg->expr[k]->src.inc_min,
+ arg->expr[k]->src.inc_max,
+ arg->expr[k]->src.exc_min,
+ arg->expr[k]->src.exc_max
+ );
+
+ RASTER_DEBUGF(4, "expr[%d]->dst (min, max, in, ix, en, ex) = (%f, %f, %d, %d, %d, %d)",
+ k,
+ arg->expr[k]->dst.min,
+ arg->expr[k]->dst.max,
+ arg->expr[k]->dst.inc_min,
+ arg->expr[k]->dst.inc_max,
+ arg->expr[k]->dst.exc_min,
+ arg->expr[k]->dst.exc_max
+ );
+
+ k++;
+ }
+
+ /* EXACT has one last expression for catching all uncaught values */
+ if (colormap->method == CM_EXACT) {
+ arg->expr[k]->src.min = 0;
+ arg->expr[k]->src.inc_min = 1;
+ arg->expr[k]->src.exc_min = 1;
+
+ arg->expr[k]->src.max = 0;
+ arg->expr[k]->src.inc_max = 1;
+ arg->expr[k]->src.exc_max = 1;
+
+ arg->expr[k]->dst.min = 0;
+ arg->expr[k]->dst.inc_min = 1;
+ arg->expr[k]->dst.exc_min = 0;
+
+ arg->expr[k]->dst.max = 0;
+ arg->expr[k]->dst.inc_max = 1;
+ arg->expr[k]->dst.exc_max = 0;
+
+ RASTER_DEBUGF(4, "expr[%d]->src (min, max, in, ix, en, ex) = (%f, %f, %d, %d, %d, %d)",
+ k,
+ arg->expr[k]->src.min,
+ arg->expr[k]->src.max,
+ arg->expr[k]->src.inc_min,
+ arg->expr[k]->src.inc_max,
+ arg->expr[k]->src.exc_min,
+ arg->expr[k]->src.exc_max
+ );
+
+ RASTER_DEBUGF(4, "expr[%d]->dst (min, max, in, ix, en, ex) = (%f, %f, %d, %d, %d, %d)",
+ k,
+ arg->expr[k]->dst.min,
+ arg->expr[k]->dst.max,
+ arg->expr[k]->dst.inc_min,
+ arg->expr[k]->dst.inc_max,
+ arg->expr[k]->dst.exc_min,
+ arg->expr[k]->dst.exc_max
+ );
+
+ k++;
+ }
+
+ /* call rt_band_reclass */
+ arg->band = rt_band_reclass(band, PT_8BUI, 0, 0, arg->expr, arg->nexpr);
+ if (arg->band == NULL) {
+ rterror("rt_raster_colormap: Could not reclassify band");
+ _rti_colormap_arg_destroy(arg);
+ return NULL;
+ }
+
+ /* add reclassified band to raster */
+ if (rt_raster_add_band(arg->raster, arg->band, rt_raster_get_num_bands(arg->raster)) < 0) {
+ rterror("rt_raster_colormap: Could not add reclassified band to output raster");
+ _rti_colormap_arg_destroy(arg);
+ return NULL;
+ }
+ }
+
+ rtnraster = arg->raster;
+ arg->raster = NULL;
+ _rti_colormap_arg_destroy(arg);
+
+ return rtnraster;
+}
--- /dev/null
+/*
+ * $Id$
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "librtcore.h"
+#include "librtcore_internal.h"
+
+/******************************************************************************
+* rt_pixeltype
+******************************************************************************/
+
+int
+rt_pixtype_size(rt_pixtype pixtype) {
+ int pixbytes = -1;
+
+ switch (pixtype) {
+ case PT_1BB:
+ case PT_2BUI:
+ case PT_4BUI:
+ case PT_8BSI:
+ case PT_8BUI:
+ pixbytes = 1;
+ break;
+ case PT_16BSI:
+ case PT_16BUI:
+ pixbytes = 2;
+ break;
+ case PT_32BSI:
+ case PT_32BUI:
+ case PT_32BF:
+ pixbytes = 4;
+ break;
+ case PT_64BF:
+ pixbytes = 8;
+ break;
+ default:
+ rterror("rt_pixtype_size: Unknown pixeltype %d", pixtype);
+ pixbytes = -1;
+ break;
+ }
+
+ RASTER_DEBUGF(3, "Pixel type = %s and size = %d bytes",
+ rt_pixtype_name(pixtype), pixbytes);
+
+ return pixbytes;
+}
+
+int
+rt_pixtype_alignment(rt_pixtype pixtype) {
+ return rt_pixtype_size(pixtype);
+}
+
+rt_pixtype
+rt_pixtype_index_from_name(const char* pixname) {
+ assert(pixname && strlen(pixname) > 0);
+
+ if (strcmp(pixname, "1BB") == 0)
+ return PT_1BB;
+ else if (strcmp(pixname, "2BUI") == 0)
+ return PT_2BUI;
+ else if (strcmp(pixname, "4BUI") == 0)
+ return PT_4BUI;
+ else if (strcmp(pixname, "8BSI") == 0)
+ return PT_8BSI;
+ else if (strcmp(pixname, "8BUI") == 0)
+ return PT_8BUI;
+ else if (strcmp(pixname, "16BSI") == 0)
+ return PT_16BSI;
+ else if (strcmp(pixname, "16BUI") == 0)
+ return PT_16BUI;
+ else if (strcmp(pixname, "32BSI") == 0)
+ return PT_32BSI;
+ else if (strcmp(pixname, "32BUI") == 0)
+ return PT_32BUI;
+ else if (strcmp(pixname, "32BF") == 0)
+ return PT_32BF;
+ else if (strcmp(pixname, "64BF") == 0)
+ return PT_64BF;
+
+ return PT_END;
+}
+
+const char*
+rt_pixtype_name(rt_pixtype pixtype) {
+ switch (pixtype) {
+ case PT_1BB:
+ return "1BB";
+ case PT_2BUI:
+ return "2BUI";
+ case PT_4BUI:
+ return "4BUI";
+ case PT_8BSI:
+ return "8BSI";
+ case PT_8BUI:
+ return "8BUI";
+ case PT_16BSI:
+ return "16BSI";
+ case PT_16BUI:
+ return "16BUI";
+ case PT_32BSI:
+ return "32BSI";
+ case PT_32BUI:
+ return "32BUI";
+ case PT_32BF:
+ return "32BF";
+ case PT_64BF:
+ return "64BF";
+ default:
+ rterror("rt_pixtype_name: Unknown pixeltype %d", pixtype);
+ return "Unknown";
+ }
+}
+
+/**
+ * Return minimum value possible for pixel type
+ *
+ * @param pixtype : the pixel type to get minimum possible value for
+ *
+ * @return the minimum possible value for the pixel type.
+ */
+double
+rt_pixtype_get_min_value(rt_pixtype pixtype) {
+ switch (pixtype) {
+ case PT_1BB: {
+ return (double) rt_util_clamp_to_1BB((double) CHAR_MIN);
+ }
+ case PT_2BUI: {
+ return (double) rt_util_clamp_to_2BUI((double) CHAR_MIN);
+ }
+ case PT_4BUI: {
+ return (double) rt_util_clamp_to_4BUI((double) CHAR_MIN);
+ }
+ case PT_8BUI: {
+ return (double) rt_util_clamp_to_8BUI((double) CHAR_MIN);
+ }
+ case PT_8BSI: {
+ return (double) rt_util_clamp_to_8BSI((double) SCHAR_MIN);
+ }
+ case PT_16BSI: {
+ return (double) rt_util_clamp_to_16BSI((double) SHRT_MIN);
+ }
+ case PT_16BUI: {
+ return (double) rt_util_clamp_to_16BUI((double) SHRT_MIN);
+ }
+ case PT_32BSI: {
+ return (double) rt_util_clamp_to_32BSI((double) INT_MIN);
+ }
+ case PT_32BUI: {
+ return (double) rt_util_clamp_to_32BUI((double) INT_MIN);
+ }
+ case PT_32BF: {
+ return (double) -FLT_MAX;
+ }
+ case PT_64BF: {
+ return (double) -DBL_MAX;
+ }
+ default: {
+ rterror("rt_pixtype_get_min_value: Unknown pixeltype %d", pixtype);
+ return (double) rt_util_clamp_to_8BUI((double) CHAR_MIN);
+ }
+ }
+}
+
+/**
+ * Returns 1 if clamped values are equal, 0 if not equal, -1 if error
+ *
+ * @param pixtype : the pixel type to clamp the provided values
+ * @param val : value to compare to reference value
+ * @param refval : reference value to be compared with
+ * @param isequal : non-zero if clamped values are equal, 0 otherwise
+ *
+ * @return ES_NONE on success, ES_ERROR on error
+ */
+rt_errorstate rt_pixtype_compare_clamped_values(
+ rt_pixtype pixtype,
+ double val, double refval,
+ int *isequal
+) {
+ assert(isequal != NULL);
+ *isequal = 0;
+
+ switch (pixtype) {
+ case PT_1BB:
+ if (rt_util_clamp_to_1BB(val) == rt_util_clamp_to_1BB(refval))
+ *isequal = 1;
+ break;
+ case PT_2BUI:
+ if (rt_util_clamp_to_2BUI(val) == rt_util_clamp_to_2BUI(refval))
+ *isequal = 1;
+ break;
+ case PT_4BUI:
+ if (rt_util_clamp_to_4BUI(val) == rt_util_clamp_to_4BUI(refval))
+ *isequal = 1;
+ break;
+ case PT_8BSI:
+ if (rt_util_clamp_to_8BSI(val) == rt_util_clamp_to_8BSI(refval))
+ *isequal = 1;
+ break;
+ case PT_8BUI:
+ if (rt_util_clamp_to_8BUI(val) == rt_util_clamp_to_8BUI(refval))
+ *isequal = 1;
+ break;
+ case PT_16BSI:
+ if (rt_util_clamp_to_16BSI(val) == rt_util_clamp_to_16BSI(refval))
+ *isequal = 1;
+ break;
+ case PT_16BUI:
+ if (rt_util_clamp_to_16BUI(val) == rt_util_clamp_to_16BUI(refval))
+ *isequal = 1;
+ break;
+ case PT_32BSI:
+ if (rt_util_clamp_to_32BSI(val) == rt_util_clamp_to_32BSI(refval))
+ *isequal = 1;
+ break;
+ case PT_32BUI:
+ if (rt_util_clamp_to_32BUI(val) == rt_util_clamp_to_32BUI(refval))
+ *isequal = 1;
+ break;
+ case PT_32BF:
+ if (FLT_EQ(rt_util_clamp_to_32F(val), rt_util_clamp_to_32F(refval)))
+ *isequal = 1;
+ break;
+ case PT_64BF:
+ if (FLT_EQ(val, refval))
+ *isequal = 1;
+ break;
+ default:
+ rterror("rt_pixtype_compare_clamped_values: Unknown pixeltype %d", pixtype);
+ return ES_ERROR;
+ }
+
+ return ES_NONE;
+}
+
+/******************************************************************************
+* rt_pixel
+******************************************************************************/
+
+/*
+ * Convert an array of rt_pixel objects to two 2D arrays of value and NODATA.
+ * The dimensions of the returned 2D array are [Y][X], going by row Y and
+ * then column X.
+ *
+ * @param npixel : array of rt_pixel objects
+ * @param count : number of elements in npixel
+ * @param x : the column of the center pixel (0-based)
+ * @param y : the line of the center pixel (0-based)
+ * @param distancex : the number of pixels around the specified pixel
+ * along the X axis
+ * @param distancey : the number of pixels around the specified pixel
+ * along the Y axis
+ * @param value : pointer to pointer for 2D value array
+ * @param nodata : pointer to pointer for 2D NODATA array
+ * @param dimx : size of value and nodata along the X axis
+ * @param dimy : size of value and nodata along the Y axis
+ *
+ * @return ES_NONE on success, ES_ERROR on error
+ */
+rt_errorstate rt_pixel_set_to_array(
+ rt_pixel npixel, int count,
+ int x, int y,
+ uint16_t distancex, uint16_t distancey,
+ double ***value,
+ int ***nodata,
+ int *dimx, int *dimy
+) {
+ uint32_t i;
+ uint32_t j;
+ uint32_t dim[2] = {0};
+ double **values = NULL;
+ int **nodatas = NULL;
+ int zero[2] = {0};
+ int _x;
+ int _y;
+
+ assert(npixel != NULL && count > 0);
+ assert(value != NULL);
+ assert(nodata != NULL);
+
+ /* dimensions */
+ dim[0] = distancex * 2 + 1;
+ dim[1] = distancey * 2 + 1;
+ RASTER_DEBUGF(4, "dimensions = %d x %d", dim[0], dim[1]);
+
+ /* establish 2D arrays (Y axis) */
+ values = rtalloc(sizeof(double *) * dim[1]);
+ nodatas = rtalloc(sizeof(int *) * dim[1]);
+
+ if (values == NULL || nodatas == NULL) {
+ rterror("rt_pixel_set_to_array: Could not allocate memory for 2D array");
+ return ES_ERROR;
+ }
+
+ /* initialize X axis */
+ for (i = 0; i < dim[1]; i++) {
+ values[i] = rtalloc(sizeof(double) * dim[0]);
+ nodatas[i] = rtalloc(sizeof(int) * dim[0]);
+
+ if (values[i] == NULL || nodatas[i] == NULL) {
+ rterror("rt_pixel_set_to_array: Could not allocate memory for dimension of 2D array");
+
+ if (values[i] == NULL) {
+ for (j = 0; j < i; j++) {
+ rtdealloc(values[j]);
+ rtdealloc(nodatas[j]);
+ }
+ }
+ else {
+ for (j = 0; j <= i; j++) {
+ rtdealloc(values[j]);
+ if (j < i)
+ rtdealloc(nodatas[j]);
+ }
+ }
+
+ rtdealloc(values);
+ rtdealloc(nodatas);
+
+ return ES_ERROR;
+ }
+
+ /* set values to 0 */
+ memset(values[i], 0, sizeof(double) * dim[0]);
+
+ /* set nodatas to 1 */
+ for (j = 0; j < dim[0]; j++)
+ nodatas[i][j] = 1;
+ }
+
+ /* get 0,0 of grid */
+ zero[0] = x - distancex;
+ zero[1] = y - distancey;
+
+ /* populate 2D arrays */
+ for (i = 0; i < count; i++) {
+ if (npixel[i].nodata)
+ continue;
+
+ _x = npixel[i].x - zero[0];
+ _y = npixel[i].y - zero[1];
+
+ RASTER_DEBUGF(4, "absolute x,y: %d x %d", npixel[i].x, npixel[i].y);
+ RASTER_DEBUGF(4, "relative x,y: %d x %d", _x, _y);
+
+ values[_y][_x] = npixel[i].value;
+ nodatas[_y][_x] = 0;
+
+ RASTER_DEBUGF(4, "(x, y, nodata, value) = (%d, %d, %d, %f)", _x, _y, nodatas[_y][_x], values[_y][_x]);
+ }
+
+ *value = &(*values);
+ *nodata = &(*nodatas);
+ if (dimx != NULL)
+ *dimx = dim[0];
+ if (dimy != NULL)
+ *dimy = dim[1];
+
+ return ES_NONE;
+}
--- /dev/null
+/*
+ * $Id$
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "librtcore.h"
+#include "librtcore_internal.h"
+
+#include <math.h>
+
+/**
+ * Construct a raster with given dimensions.
+ *
+ * Transform will be set to identity.
+ * Will contain no bands.
+ *
+ * @param width : number of pixel columns
+ * @param height : number of pixel rows
+ *
+ * @return an rt_raster or NULL if out of memory
+ */
+rt_raster
+rt_raster_new(uint32_t width, uint32_t height) {
+ rt_raster ret = NULL;
+
+ ret = (rt_raster) rtalloc(sizeof (struct rt_raster_t));
+ if (!ret) {
+ rterror("rt_raster_new: Out of virtual memory creating an rt_raster");
+ return NULL;
+ }
+
+ RASTER_DEBUGF(3, "Created rt_raster @ %p", ret);
+
+ if (width > 65535 || height > 65535) {
+ rterror("rt_raster_new: Dimensions requested exceed the maximum (65535 x 65535) permitted for a raster");
+ rt_raster_destroy(ret);
+ return NULL;
+ }
+
+ ret->width = width;
+ ret->height = height;
+ ret->scaleX = 1;
+ ret->scaleY = -1;
+ ret->ipX = 0.0;
+ ret->ipY = 0.0;
+ ret->skewX = 0.0;
+ ret->skewY = 0.0;
+ ret->srid = SRID_UNKNOWN;
+
+ ret->numBands = 0;
+ ret->bands = NULL;
+
+ return ret;
+}
+
+void
+rt_raster_destroy(rt_raster raster) {
+ if (raster == NULL)
+ return;
+
+ RASTER_DEBUGF(3, "Destroying rt_raster @ %p", raster);
+
+ if (raster->bands)
+ rtdealloc(raster->bands);
+
+ rtdealloc(raster);
+}
+
+static void
+_rt_raster_geotransform_warn_offline_band(rt_raster raster) {
+ int numband = 0;
+ int i = 0;
+ rt_band band = NULL;
+
+ if (raster == NULL)
+ return;
+
+ numband = rt_raster_get_num_bands(raster);
+ if (numband < 1)
+ return;
+
+ for (i = 0; i < numband; i++) {
+ band = rt_raster_get_band(raster, i);
+ if (NULL == band)
+ continue;
+
+ if (!rt_band_is_offline(band))
+ continue;
+
+ rtwarn("Changes made to raster geotransform matrix may affect out-db band data. Returned band data may be incorrect");
+ break;
+ }
+}
+
+uint16_t
+rt_raster_get_width(rt_raster raster) {
+
+ assert(NULL != raster);
+
+ return raster->width;
+}
+
+uint16_t
+rt_raster_get_height(rt_raster raster) {
+
+ assert(NULL != raster);
+
+ return raster->height;
+}
+
+void
+rt_raster_set_scale(
+ rt_raster raster,
+ double scaleX, double scaleY
+) {
+ assert(NULL != raster);
+
+ raster->scaleX = scaleX;
+ raster->scaleY = scaleY;
+
+ _rt_raster_geotransform_warn_offline_band(raster);
+}
+
+double
+rt_raster_get_x_scale(rt_raster raster) {
+
+
+ assert(NULL != raster);
+
+ return raster->scaleX;
+}
+
+double
+rt_raster_get_y_scale(rt_raster raster) {
+
+
+ assert(NULL != raster);
+
+ return raster->scaleY;
+}
+
+void
+rt_raster_set_skews(
+ rt_raster raster,
+ double skewX, double skewY
+) {
+ assert(NULL != raster);
+
+ raster->skewX = skewX;
+ raster->skewY = skewY;
+
+ _rt_raster_geotransform_warn_offline_band(raster);
+}
+
+double
+rt_raster_get_x_skew(rt_raster raster) {
+
+
+ assert(NULL != raster);
+
+ return raster->skewX;
+}
+
+double
+rt_raster_get_y_skew(rt_raster raster) {
+
+
+ assert(NULL != raster);
+
+ return raster->skewY;
+}
+
+void
+rt_raster_set_offsets(
+ rt_raster raster,
+ double x, double y
+) {
+
+ assert(NULL != raster);
+
+ raster->ipX = x;
+ raster->ipY = y;
+
+ _rt_raster_geotransform_warn_offline_band(raster);
+}
+
+double
+rt_raster_get_x_offset(rt_raster raster) {
+
+
+ assert(NULL != raster);
+
+ return raster->ipX;
+}
+
+double
+rt_raster_get_y_offset(rt_raster raster) {
+
+
+ assert(NULL != raster);
+
+ return raster->ipY;
+}
+
+void
+rt_raster_get_phys_params(rt_raster rast,
+ double *i_mag, double *j_mag, double *theta_i, double *theta_ij)
+{
+ double o11, o12, o21, o22 ; /* geotransform coefficients */
+
+ if (rast == NULL) return ;
+ if ( (i_mag==NULL) || (j_mag==NULL) || (theta_i==NULL) || (theta_ij==NULL))
+ return ;
+
+ /* retrieve coefficients from raster */
+ o11 = rt_raster_get_x_scale(rast) ;
+ o12 = rt_raster_get_x_skew(rast) ;
+ o21 = rt_raster_get_y_skew(rast) ;
+ o22 = rt_raster_get_y_scale(rast) ;
+
+ rt_raster_calc_phys_params(o11, o12, o21, o22, i_mag, j_mag, theta_i, theta_ij);
+}
+
+void
+rt_raster_calc_phys_params(double xscale, double xskew, double yskew, double yscale,
+ double *i_mag, double *j_mag, double *theta_i, double *theta_ij)
+
+{
+ double theta_test ;
+
+ if ( (i_mag==NULL) || (j_mag==NULL) || (theta_i==NULL) || (theta_ij==NULL))
+ return ;
+
+ /* pixel size in the i direction */
+ *i_mag = sqrt(xscale*xscale + yskew*yskew) ;
+
+ /* pixel size in the j direction */
+ *j_mag = sqrt(xskew*xskew + yscale*yscale) ;
+
+ /* Rotation
+ * ========
+ * Two steps:
+ * 1] calculate the magnitude of the angle between the x axis and
+ * the i basis vector.
+ * 2] Calculate the sign of theta_i based on the angle between the y axis
+ * and the i basis vector.
+ */
+ *theta_i = acos(xscale/(*i_mag)) ; /* magnitude */
+ theta_test = acos(yskew/(*i_mag)) ; /* sign */
+ if (theta_test < M_PI_2){
+ *theta_i = -(*theta_i) ;
+ }
+
+
+ /* Angular separation of basis vectors
+ * ===================================
+ * Two steps:
+ * 1] calculate the magnitude of the angle between the j basis vector and
+ * the i basis vector.
+ * 2] Calculate the sign of theta_ij based on the angle between the
+ * perpendicular of the i basis vector and the j basis vector.
+ */
+ *theta_ij = acos(((xscale*xskew) + (yskew*yscale))/((*i_mag)*(*j_mag))) ;
+ theta_test = acos( ((-yskew*xskew)+(xscale*yscale)) /
+ ((*i_mag)*(*j_mag)));
+ if (theta_test > M_PI_2) {
+ *theta_ij = -(*theta_ij) ;
+ }
+}
+
+void
+rt_raster_set_phys_params(rt_raster rast,double i_mag, double j_mag, double theta_i, double theta_ij)
+{
+ double o11, o12, o21, o22 ; /* calculated geotransform coefficients */
+ int success ;
+
+ if (rast == NULL) return ;
+
+ success = rt_raster_calc_gt_coeff(i_mag, j_mag, theta_i, theta_ij,
+ &o11, &o12, &o21, &o22) ;
+
+ if (success) {
+ rt_raster_set_scale(rast, o11, o22) ;
+ rt_raster_set_skews(rast, o12, o21) ;
+ }
+}
+
+int
+rt_raster_calc_gt_coeff(double i_mag, double j_mag, double theta_i, double theta_ij,
+ double *xscale, double *xskew, double *yskew, double *yscale)
+{
+ double f ; /* reflection flag 1.0 or -1.0 */
+ double k_i ; /* shearing coefficient */
+ double s_i, s_j ; /* scaling coefficients */
+ double cos_theta_i, sin_theta_i ;
+
+ if ( (xscale==NULL) || (xskew==NULL) || (yskew==NULL) || (yscale==NULL)) {
+ return 0;
+ }
+
+ if ( (theta_ij == 0.0) || (theta_ij == M_PI)) {
+ return 0;
+ }
+
+ /* Reflection across the i axis */
+ f=1.0 ;
+ if (theta_ij < 0) {
+ f = -1.0;
+ }
+
+ /* scaling along i axis */
+ s_i = i_mag ;
+
+ /* shearing parallel to i axis */
+ k_i = tan(f*M_PI_2 - theta_ij) ;
+
+ /* scaling along j axis */
+ s_j = j_mag / (sqrt(k_i*k_i + 1)) ;
+
+ /* putting it altogether */
+ cos_theta_i = cos(theta_i) ;
+ sin_theta_i = sin(theta_i) ;
+ *xscale = s_i * cos_theta_i ;
+ *xskew = k_i * s_j * f * cos_theta_i + s_j * f * sin_theta_i ;
+ *yskew = -s_i * sin_theta_i ;
+ *yscale = -k_i * s_j * f * sin_theta_i + s_j * f * cos_theta_i ;
+ return 1;
+}
+
+int32_t
+rt_raster_get_srid(rt_raster raster) {
+ assert(NULL != raster);
+
+ return clamp_srid(raster->srid);
+}
+
+void
+rt_raster_set_srid(rt_raster raster, int32_t srid) {
+ assert(NULL != raster);
+
+ raster->srid = clamp_srid(srid);
+
+ _rt_raster_geotransform_warn_offline_band(raster);
+}
+
+int
+rt_raster_get_num_bands(rt_raster raster) {
+
+
+ assert(NULL != raster);
+
+ return raster->numBands;
+}
+
+rt_band
+rt_raster_get_band(rt_raster raster, int n) {
+ assert(NULL != raster);
+
+ if (n >= raster->numBands || n < 0)
+ return NULL;
+
+ return raster->bands[n];
+}
+
+/******************************************************************************
+* rt_raster_add_band()
+******************************************************************************/
+
+/**
+ * Add band data to a raster.
+ *
+ * @param raster : the raster to add a band to
+ * @param band : the band to add, ownership left to caller.
+ * Band dimensions are required to match with raster ones.
+ * @param index : the position where to insert the new band (0 based)
+ *
+ * @return identifier (position) for the just-added raster, or -1 on error
+ */
+int
+rt_raster_add_band(rt_raster raster, rt_band band, int index) {
+ rt_band *oldbands = NULL;
+ rt_band oldband = NULL;
+ rt_band tmpband = NULL;
+ uint16_t i = 0;
+
+ assert(NULL != raster);
+ assert(NULL != band);
+
+ RASTER_DEBUGF(3, "Adding band %p to raster %p", band, raster);
+
+ if (band->width != raster->width || band->height != raster->height) {
+ rterror("rt_raster_add_band: Can't add a %dx%d band to a %dx%d raster",
+ band->width, band->height, raster->width, raster->height);
+ return -1;
+ }
+
+ if (index > raster->numBands)
+ index = raster->numBands;
+
+ if (index < 0)
+ index = 0;
+
+ oldbands = raster->bands;
+
+ RASTER_DEBUGF(3, "Oldbands at %p", oldbands);
+
+ raster->bands = (rt_band*) rtrealloc(raster->bands,
+ sizeof (rt_band)*(raster->numBands + 1)
+ );
+
+ RASTER_DEBUG(3, "Checking bands");
+
+ if (NULL == raster->bands) {
+ rterror("rt_raster_add_band: Out of virtual memory "
+ "reallocating band pointers");
+ raster->bands = oldbands;
+ return -1;
+ }
+
+ RASTER_DEBUGF(4, "realloc returned %p", raster->bands);
+
+ for (i = 0; i <= raster->numBands; ++i) {
+ if (i == index) {
+ oldband = raster->bands[i];
+ raster->bands[i] = band;
+ } else if (i > index) {
+ tmpband = raster->bands[i];
+ raster->bands[i] = oldband;
+ oldband = tmpband;
+ }
+ }
+
+ band->raster = raster;
+
+ raster->numBands++;
+
+ RASTER_DEBUGF(4, "Raster now has %d bands", raster->numBands);
+
+ return index;
+}
+
+/******************************************************************************
+* rt_raster_generate_new_band()
+******************************************************************************/
+
+/**
+ * Generate a new inline band and add it to a raster.
+ * Memory is allocated in this function for band data.
+ *
+ * @param raster : the raster to add a band to
+ * @param pixtype : the pixel type for the new band
+ * @param initialvalue : initial value for pixels
+ * @param hasnodata : indicates if the band has a nodata value
+ * @param nodatavalue : nodata value for the new band
+ * @param index : position to add the new band in the raster
+ *
+ * @return identifier (position) for the just-added raster, or -1 on error
+ */
+int
+rt_raster_generate_new_band(
+ rt_raster raster, rt_pixtype pixtype,
+ double initialvalue, uint32_t hasnodata, double nodatavalue,
+ int index
+) {
+ rt_band band = NULL;
+ int width = 0;
+ int height = 0;
+ int numval = 0;
+ int datasize = 0;
+ int oldnumbands = 0;
+ int numbands = 0;
+ void * mem = NULL;
+ int32_t checkvalint = 0;
+ uint32_t checkvaluint = 0;
+ double checkvaldouble = 0;
+ float checkvalfloat = 0;
+ int i;
+
+
+ assert(NULL != raster);
+
+ /* Make sure index is in a valid range */
+ oldnumbands = rt_raster_get_num_bands(raster);
+ if (index < 0)
+ index = 0;
+ else if (index > oldnumbands + 1)
+ index = oldnumbands + 1;
+
+ /* Determine size of memory block to allocate and allocate it */
+ width = rt_raster_get_width(raster);
+ height = rt_raster_get_height(raster);
+ numval = width * height;
+ datasize = rt_pixtype_size(pixtype) * numval;
+
+ mem = (int *)rtalloc(datasize);
+ if (!mem) {
+ rterror("rt_raster_generate_new_band: Could not allocate memory for band");
+ return -1;
+ }
+
+ if (FLT_EQ(initialvalue, 0.0))
+ memset(mem, 0, datasize);
+ else {
+ switch (pixtype)
+ {
+ case PT_1BB:
+ {
+ uint8_t *ptr = mem;
+ uint8_t clamped_initval = rt_util_clamp_to_1BB(initialvalue);
+ for (i = 0; i < numval; i++)
+ ptr[i] = clamped_initval;
+ checkvalint = ptr[0];
+ break;
+ }
+ case PT_2BUI:
+ {
+ uint8_t *ptr = mem;
+ uint8_t clamped_initval = rt_util_clamp_to_2BUI(initialvalue);
+ for (i = 0; i < numval; i++)
+ ptr[i] = clamped_initval;
+ checkvalint = ptr[0];
+ break;
+ }
+ case PT_4BUI:
+ {
+ uint8_t *ptr = mem;
+ uint8_t clamped_initval = rt_util_clamp_to_4BUI(initialvalue);
+ for (i = 0; i < numval; i++)
+ ptr[i] = clamped_initval;
+ checkvalint = ptr[0];
+ break;
+ }
+ case PT_8BSI:
+ {
+ int8_t *ptr = mem;
+ int8_t clamped_initval = rt_util_clamp_to_8BSI(initialvalue);
+ for (i = 0; i < numval; i++)
+ ptr[i] = clamped_initval;
+ checkvalint = ptr[0];
+ break;
+ }
+ case PT_8BUI:
+ {
+ uint8_t *ptr = mem;
+ uint8_t clamped_initval = rt_util_clamp_to_8BUI(initialvalue);
+ for (i = 0; i < numval; i++)
+ ptr[i] = clamped_initval;
+ checkvalint = ptr[0];
+ break;
+ }
+ case PT_16BSI:
+ {
+ int16_t *ptr = mem;
+ int16_t clamped_initval = rt_util_clamp_to_16BSI(initialvalue);
+ for (i = 0; i < numval; i++)
+ ptr[i] = clamped_initval;
+ checkvalint = ptr[0];
+ break;
+ }
+ case PT_16BUI:
+ {
+ uint16_t *ptr = mem;
+ uint16_t clamped_initval = rt_util_clamp_to_16BUI(initialvalue);
+ for (i = 0; i < numval; i++)
+ ptr[i] = clamped_initval;
+ checkvalint = ptr[0];
+ break;
+ }
+ case PT_32BSI:
+ {
+ int32_t *ptr = mem;
+ int32_t clamped_initval = rt_util_clamp_to_32BSI(initialvalue);
+ for (i = 0; i < numval; i++)
+ ptr[i] = clamped_initval;
+ checkvalint = ptr[0];
+ break;
+ }
+ case PT_32BUI:
+ {
+ uint32_t *ptr = mem;
+ uint32_t clamped_initval = rt_util_clamp_to_32BUI(initialvalue);
+ for (i = 0; i < numval; i++)
+ ptr[i] = clamped_initval;
+ checkvaluint = ptr[0];
+ break;
+ }
+ case PT_32BF:
+ {
+ float *ptr = mem;
+ float clamped_initval = rt_util_clamp_to_32F(initialvalue);
+ for (i = 0; i < numval; i++)
+ ptr[i] = clamped_initval;
+ checkvalfloat = ptr[0];
+ break;
+ }
+ case PT_64BF:
+ {
+ double *ptr = mem;
+ for (i = 0; i < numval; i++)
+ ptr[i] = initialvalue;
+ checkvaldouble = ptr[0];
+ break;
+ }
+ default:
+ {
+ rterror("rt_raster_generate_new_band: Unknown pixeltype %d", pixtype);
+ rtdealloc(mem);
+ return -1;
+ }
+ }
+ }
+
+ /* Overflow checking */
+ rt_util_dbl_trunc_warning(
+ initialvalue,
+ checkvalint, checkvaluint,
+ checkvalfloat, checkvaldouble,
+ pixtype
+ );
+
+ band = rt_band_new_inline(width, height, pixtype, hasnodata, nodatavalue, mem);
+ if (! band) {
+ rterror("rt_raster_generate_new_band: Could not add band to raster. Aborting");
+ rtdealloc(mem);
+ return -1;
+ }
+ rt_band_set_ownsdata_flag(band, 1); /* we DO own this data!!! */
+ index = rt_raster_add_band(raster, band, index);
+ numbands = rt_raster_get_num_bands(raster);
+ if (numbands == oldnumbands || index == -1) {
+ rterror("rt_raster_generate_new_band: Could not add band to raster. Aborting");
+ rt_band_destroy(band);
+ }
+
+ /* set isnodata if hasnodata = TRUE and initial value = nodatavalue */
+ if (hasnodata && FLT_EQ(initialvalue, nodatavalue))
+ rt_band_set_isnodata_flag(band, 1);
+
+ return index;
+}
+
+/**
+ * Get 6-element array of raster inverse geotransform matrix
+ *
+ * @param raster : the raster to get matrix of
+ * @param gt : optional input parameter, 6-element geotransform matrix
+ * @param igt : output parameter, 6-element inverse geotransform matrix
+ *
+ * @return ES_NONE if success, ES_ERROR if error
+ */
+rt_errorstate rt_raster_get_inverse_geotransform_matrix(
+ rt_raster raster,
+ double *gt, double *igt
+) {
+ double _gt[6] = {0};
+
+ assert((raster != NULL || gt != NULL));
+ assert(igt != NULL);
+
+ if (gt == NULL)
+ rt_raster_get_geotransform_matrix(raster, _gt);
+ else
+ memcpy(_gt, gt, sizeof(double) * 6);
+
+ if (!GDALInvGeoTransform(_gt, igt)) {
+ rterror("rt_raster_get_inverse_geotransform_matrix: Could not compute inverse geotransform matrix");
+ return ES_ERROR;
+ }
+
+ return ES_NONE;
+}
+
+/**
+ * Get 6-element array of raster geotransform matrix
+ *
+ * @param raster : the raster to get matrix of
+ * @param gt : output parameter, 6-element geotransform matrix
+ *
+ */
+void
+rt_raster_get_geotransform_matrix(rt_raster raster,
+ double *gt) {
+ assert(NULL != raster);
+ assert(NULL != gt);
+
+ gt[0] = raster->ipX;
+ gt[1] = raster->scaleX;
+ gt[2] = raster->skewX;
+ gt[3] = raster->ipY;
+ gt[4] = raster->skewY;
+ gt[5] = raster->scaleY;
+}
+
+/**
+ * Set raster's geotransform using 6-element array
+ *
+ * @param raster : the raster to set matrix of
+ * @param gt : intput parameter, 6-element geotransform matrix
+ *
+ */
+void
+rt_raster_set_geotransform_matrix(rt_raster raster,
+ double *gt) {
+ assert(NULL != raster);
+ assert(NULL != gt);
+
+ raster->ipX = gt[0];
+ raster->scaleX = gt[1];
+ raster->skewX = gt[2];
+ raster->ipY = gt[3];
+ raster->skewY = gt[4];
+ raster->scaleY = gt[5];
+
+ _rt_raster_geotransform_warn_offline_band(raster);
+}
+
+/**
+ * Convert an xr, yr raster point to an xw, yw point on map
+ *
+ * @param raster : the raster to get info from
+ * @param xr : the pixel's column
+ * @param yr : the pixel's row
+ * @param xw : output parameter, X ordinate of the geographical point
+ * @param yw : output parameter, Y ordinate of the geographical point
+ * @param gt : input/output parameter, 3x2 geotransform matrix
+ *
+ * @return ES_NONE if success, ES_ERROR if error
+ */
+rt_errorstate
+rt_raster_cell_to_geopoint(
+ rt_raster raster,
+ double xr, double yr,
+ double *xw, double *yw,
+ double *gt
+) {
+ double _gt[6] = {0};
+
+ assert(NULL != raster);
+ assert(NULL != xw && NULL != yw);
+
+ if (NULL != gt)
+ memcpy(_gt, gt, sizeof(double) * 6);
+
+ /* scale of matrix is not set */
+ if (
+ FLT_EQ(_gt[1], 0) ||
+ FLT_EQ(_gt[5], 0)
+ ) {
+ rt_raster_get_geotransform_matrix(raster, _gt);
+ }
+
+ RASTER_DEBUGF(4, "gt = (%f, %f, %f, %f, %f, %f)",
+ _gt[0],
+ _gt[1],
+ _gt[2],
+ _gt[3],
+ _gt[4],
+ _gt[5]
+ );
+
+ GDALApplyGeoTransform(_gt, xr, yr, xw, yw);
+ RASTER_DEBUGF(4, "GDALApplyGeoTransform (c -> g) for (%f, %f) = (%f, %f)",
+ xr, yr, *xw, *yw);
+
+ return ES_NONE;
+}
+
+/**
+ * Convert an xw,yw map point to a xr,yr raster point
+ *
+ * @param raster : the raster to get info from
+ * @param xw : X ordinate of the geographical point
+ * @param yw : Y ordinate of the geographical point
+ * @param xr : output parameter, the pixel's column
+ * @param yr : output parameter, the pixel's row
+ * @param igt : input/output parameter, inverse geotransform matrix
+ *
+ * @return ES_NONE if success, ES_ERROR if error
+ */
+rt_errorstate
+rt_raster_geopoint_to_cell(
+ rt_raster raster,
+ double xw, double yw,
+ double *xr, double *yr,
+ double *igt
+) {
+ double _igt[6] = {0};
+ double rnd = 0;
+
+ assert(NULL != raster);
+ assert(NULL != xr && NULL != yr);
+
+ if (igt != NULL)
+ memcpy(_igt, igt, sizeof(double) * 6);
+
+ /* matrix is not set */
+ if (
+ FLT_EQ(_igt[0], 0.) &&
+ FLT_EQ(_igt[1], 0.) &&
+ FLT_EQ(_igt[2], 0.) &&
+ FLT_EQ(_igt[3], 0.) &&
+ FLT_EQ(_igt[4], 0.) &&
+ FLT_EQ(_igt[5], 0.)
+ ) {
+ if (rt_raster_get_inverse_geotransform_matrix(raster, NULL, _igt) != ES_NONE) {
+ rterror("rt_raster_geopoint_to_cell: Could not get inverse geotransform matrix");
+ return ES_ERROR;
+ }
+ }
+
+ GDALApplyGeoTransform(_igt, xw, yw, xr, yr);
+ RASTER_DEBUGF(4, "GDALApplyGeoTransform (g -> c) for (%f, %f) = (%f, %f)",
+ xw, yw, *xr, *yr);
+
+ rnd = ROUND(*xr, 0);
+ if (FLT_EQ(rnd, *xr))
+ *xr = rnd;
+ else
+ *xr = floor(*xr);
+
+ rnd = ROUND(*yr, 0);
+ if (FLT_EQ(rnd, *yr))
+ *yr = rnd;
+ else
+ *yr = floor(*yr);
+
+ RASTER_DEBUGF(4, "Corrected GDALApplyGeoTransform (g -> c) for (%f, %f) = (%f, %f)",
+ xw, yw, *xr, *yr);
+
+ return ES_NONE;
+}
+
+/******************************************************************************
+* rt_raster_get_envelope()
+******************************************************************************/
+
+/**
+ * Get raster's envelope.
+ *
+ * The envelope is the minimum bounding rectangle of the raster
+ *
+ * @param raster : the raster to get envelope of
+ * @param env : pointer to rt_envelope
+ *
+ * @return ES_NONE if success, ES_ERROR if error
+ */
+rt_errorstate
+rt_raster_get_envelope(
+ rt_raster raster,
+ rt_envelope *env
+) {
+ int i;
+ int rtn;
+ int set = 0;
+ double _r[2] = {0.};
+ double _w[2] = {0.};
+ double _gt[6] = {0.};
+
+ assert(raster != NULL);
+ assert(env != NULL);
+
+ rt_raster_get_geotransform_matrix(raster, _gt);
+
+ for (i = 0; i < 4; i++) {
+ switch (i) {
+ case 0:
+ _r[0] = 0;
+ _r[1] = 0;
+ break;
+ case 1:
+ _r[0] = 0;
+ _r[1] = raster->height;
+ break;
+ case 2:
+ _r[0] = raster->width;
+ _r[1] = raster->height;
+ break;
+ case 3:
+ _r[0] = raster->width;
+ _r[1] = 0;
+ break;
+ }
+
+ rtn = rt_raster_cell_to_geopoint(
+ raster,
+ _r[0], _r[1],
+ &(_w[0]), &(_w[1]),
+ _gt
+ );
+ if (rtn != ES_NONE) {
+ rterror("rt_raster_get_envelope: Could not compute spatial coordinates for raster pixel");
+ return ES_ERROR;
+ }
+
+ if (!set) {
+ set = 1;
+ env->MinX = _w[0];
+ env->MaxX = _w[0];
+ env->MinY = _w[1];
+ env->MaxY = _w[1];
+ }
+ else {
+ if (_w[0] < env->MinX)
+ env->MinX = _w[0];
+ else if (_w[0] > env->MaxX)
+ env->MaxX = _w[0];
+
+ if (_w[1] < env->MinY)
+ env->MinY = _w[1];
+ else if (_w[1] > env->MaxY)
+ env->MaxY = _w[1];
+ }
+ }
+
+ return ES_NONE;
+}
+
+/******************************************************************************
+* rt_raster_compute_skewed_raster()
+******************************************************************************/
+
+/*
+ * Compute skewed extent that covers unskewed extent.
+ *
+ * @param envelope : unskewed extent of type rt_envelope
+ * @param skew : pointer to 2-element array (x, y) of skew
+ * @param scale : pointer to 2-element array (x, y) of scale
+ * @param tolerance : value between 0 and 1 where the smaller the tolerance
+ * results in an extent approaching the "minimum" skewed extent.
+ * If value <= 0, tolerance = 0.1. If value > 1, tolerance = 1.
+ *
+ * @return skewed raster who's extent covers unskewed extent, NULL on error
+ */
+rt_raster
+rt_raster_compute_skewed_raster(
+ rt_envelope extent,
+ double *skew,
+ double *scale,
+ double tolerance
+) {
+ uint32_t run = 0;
+ uint32_t max_run = 1;
+ double dbl_run = 0;
+
+ int rtn;
+ int covers = 0;
+ rt_raster raster;
+ double _gt[6] = {0};
+ double _igt[6] = {0};
+ int _d[2] = {1, -1};
+ int _dlast = 0;
+ int _dlastpos = 0;
+ double _w[2] = {0};
+ double _r[2] = {0};
+ double _xy[2] = {0};
+ int i;
+ int j;
+ int x;
+ int y;
+
+ LWGEOM *geom = NULL;
+ GEOSGeometry *sgeom = NULL;
+ GEOSGeometry *ngeom = NULL;
+
+ if (
+ (tolerance < 0.) ||
+ FLT_EQ(tolerance, 0.)
+ ) {
+ tolerance = 0.1;
+ }
+ else if (tolerance > 1.)
+ tolerance = 1;
+
+ dbl_run = tolerance;
+ while (dbl_run < 10) {
+ dbl_run *= 10.;
+ max_run *= 10;
+ }
+
+ /* scale must be provided */
+ if (scale == NULL)
+ return NULL;
+ for (i = 0; i < 2; i++) {
+ if (FLT_EQ(scale[i], 0)) {
+ rterror("rt_raster_compute_skewed_raster: Scale cannot be zero");
+ return 0;
+ }
+
+ if (i < 1)
+ _gt[1] = fabs(scale[i] * tolerance);
+ else
+ _gt[5] = fabs(scale[i] * tolerance);
+ }
+ /* conform scale-y to be negative */
+ _gt[5] *= -1;
+
+ /* skew not provided or skew is zero, return raster of correct dim and spatial attributes */
+ if (
+ (skew == NULL) || (
+ FLT_EQ(skew[0], 0) &&
+ FLT_EQ(skew[1], 0)
+ )
+ ) {
+ int _dim[2] = {
+ (int) fmax((fabs(extent.MaxX - extent.MinX) + (fabs(scale[0]) / 2.)) / fabs(scale[0]), 1),
+ (int) fmax((fabs(extent.MaxY - extent.MinY) + (fabs(scale[1]) / 2.)) / fabs(scale[1]), 1)
+ };
+
+ raster = rt_raster_new(_dim[0], _dim[1]);
+ if (raster == NULL) {
+ rterror("rt_raster_compute_skewed_raster: Could not create output raster");
+ return NULL;
+ }
+
+ rt_raster_set_offsets(raster, extent.MinX, extent.MaxY);
+ rt_raster_set_scale(raster, fabs(scale[0]), -1 * fabs(scale[1]));
+ rt_raster_set_skews(raster, skew[0], skew[1]);
+
+ return raster;
+ }
+
+ /* direction to shift upper-left corner */
+ if (skew[0] > 0.)
+ _d[0] = -1;
+ if (skew[1] < 0.)
+ _d[1] = 1;
+
+ /* geotransform */
+ _gt[0] = extent.UpperLeftX;
+ _gt[2] = skew[0] * tolerance;
+ _gt[3] = extent.UpperLeftY;
+ _gt[4] = skew[1] * tolerance;
+
+ RASTER_DEBUGF(4, "Initial geotransform: %f, %f, %f, %f, %f, %f",
+ _gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]
+ );
+ RASTER_DEBUGF(4, "Delta: %d, %d", _d[0], _d[1]);
+
+ /* simple raster */
+ if ((raster = rt_raster_new(1, 1)) == NULL) {
+ rterror("rt_raster_compute_skewed_raster: Out of memory allocating extent raster");
+ return NULL;
+ }
+ rt_raster_set_geotransform_matrix(raster, _gt);
+
+ /* get inverse geotransform matrix */
+ if (!GDALInvGeoTransform(_gt, _igt)) {
+ rterror("rt_raster_compute_skewed_raster: Could not compute inverse geotransform matrix");
+ rt_raster_destroy(raster);
+ return NULL;
+ }
+ RASTER_DEBUGF(4, "Inverse geotransform: %f, %f, %f, %f, %f, %f",
+ _igt[0], _igt[1], _igt[2], _igt[3], _igt[4], _igt[5]
+ );
+
+ /* shift along axis */
+ for (i = 0; i < 2; i++) {
+ covers = 0;
+ run = 0;
+
+ RASTER_DEBUGF(3, "Shifting along %s axis", i < 1 ? "X" : "Y");
+
+ do {
+
+ /* prevent possible infinite loop */
+ if (run > max_run) {
+ rterror("rt_raster_compute_skewed_raster: Could not compute skewed extent due to check preventing infinite loop");
+ rt_raster_destroy(raster);
+ return NULL;
+ }
+
+ /*
+ check the four corners that they are covered along the specific axis
+ pixel column should be >= 0
+ */
+ for (j = 0; j < 4; j++) {
+ switch (j) {
+ /* upper-left */
+ case 0:
+ _xy[0] = extent.MinX;
+ _xy[1] = extent.MaxY;
+ break;
+ /* lower-left */
+ case 1:
+ _xy[0] = extent.MinX;
+ _xy[1] = extent.MinY;
+ break;
+ /* lower-right */
+ case 2:
+ _xy[0] = extent.MaxX;
+ _xy[1] = extent.MinY;
+ break;
+ /* upper-right */
+ case 3:
+ _xy[0] = extent.MaxX;
+ _xy[1] = extent.MaxY;
+ break;
+ }
+
+ rtn = rt_raster_geopoint_to_cell(
+ raster,
+ _xy[0], _xy[1],
+ &(_r[0]), &(_r[1]),
+ _igt
+ );
+ if (rtn != ES_NONE) {
+ rterror("rt_raster_compute_skewed_raster: Could not compute raster pixel for spatial coordinates");
+ rt_raster_destroy(raster);
+ return NULL;
+ }
+
+ RASTER_DEBUGF(4, "Point %d at cell %d x %d", j, (int) _r[0], (int) _r[1]);
+
+ /* raster doesn't cover point */
+ if ((int) _r[i] < 0) {
+ RASTER_DEBUGF(4, "Point outside of skewed extent: %d", j);
+ covers = 0;
+
+ if (_dlastpos != j) {
+ _dlast = (int) _r[i];
+ _dlastpos = j;
+ }
+ else if ((int) _r[i] < _dlast) {
+ RASTER_DEBUG(4, "Point going in wrong direction. Reversing direction");
+ _d[i] *= -1;
+ _dlastpos = -1;
+ run = 0;
+ }
+
+ break;
+ }
+
+ covers++;
+ }
+
+ if (!covers) {
+ x = 0;
+ y = 0;
+ if (i < 1)
+ x = _d[i] * fabs(_r[i]);
+ else
+ y = _d[i] * fabs(_r[i]);
+
+ rtn = rt_raster_cell_to_geopoint(
+ raster,
+ x, y,
+ &(_w[0]), &(_w[1]),
+ _gt
+ );
+ if (rtn != ES_NONE) {
+ rterror("rt_raster_compute_skewed_raster: Could not compute spatial coordinates for raster pixel");
+ rt_raster_destroy(raster);
+ return NULL;
+ }
+
+ /* adjust ul */
+ if (i < 1)
+ _gt[0] = _w[i];
+ else
+ _gt[3] = _w[i];
+ rt_raster_set_geotransform_matrix(raster, _gt);
+ RASTER_DEBUGF(4, "Shifted geotransform: %f, %f, %f, %f, %f, %f",
+ _gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]
+ );
+
+ /* get inverse geotransform matrix */
+ if (!GDALInvGeoTransform(_gt, _igt)) {
+ rterror("rt_raster_compute_skewed_raster: Could not compute inverse geotransform matrix");
+ rt_raster_destroy(raster);
+ return NULL;
+ }
+ RASTER_DEBUGF(4, "Inverse geotransform: %f, %f, %f, %f, %f, %f",
+ _igt[0], _igt[1], _igt[2], _igt[3], _igt[4], _igt[5]
+ );
+ }
+
+ run++;
+ }
+ while (!covers);
+ }
+
+ /* covers test */
+ rtn = rt_raster_geopoint_to_cell(
+ raster,
+ extent.MaxX, extent.MinY,
+ &(_r[0]), &(_r[1]),
+ _igt
+ );
+ if (rtn != ES_NONE) {
+ rterror("rt_raster_compute_skewed_raster: Could not compute raster pixel for spatial coordinates");
+ rt_raster_destroy(raster);
+ return NULL;
+ }
+
+ RASTER_DEBUGF(4, "geopoint %f x %f at cell %d x %d", extent.MaxX, extent.MinY, (int) _r[0], (int) _r[1]);
+
+ raster->width = _r[0];
+ raster->height = _r[1];
+
+ /* initialize GEOS */
+ initGEOS(lwnotice, lwgeom_geos_error);
+
+ /* create reference LWPOLY */
+ {
+ LWPOLY *npoly = rt_util_envelope_to_lwpoly(extent);
+ if (npoly == NULL) {
+ rterror("rt_raster_compute_skewed_raster: Could not build extent's geometry for covers test");
+ rt_raster_destroy(raster);
+ return NULL;
+ }
+
+ ngeom = (GEOSGeometry *) LWGEOM2GEOS(lwpoly_as_lwgeom(npoly));
+ lwpoly_free(npoly);
+ }
+
+ do {
+ covers = 0;
+
+ /* construct sgeom from raster */
+ if ((rt_raster_get_convex_hull(raster, &geom) != ES_NONE) || geom == NULL) {
+ rterror("rt_raster_compute_skewed_raster: Could not build skewed extent's geometry for covers test");
+ GEOSGeom_destroy(ngeom);
+ rt_raster_destroy(raster);
+ return NULL;
+ }
+
+ sgeom = (GEOSGeometry *) LWGEOM2GEOS(geom);
+ lwgeom_free(geom);
+
+ covers = GEOSRelatePattern(sgeom, ngeom, "******FF*");
+ GEOSGeom_destroy(sgeom);
+
+ if (covers == 2) {
+ rterror("rt_raster_compute_skewed_raster: Could not run covers test");
+ GEOSGeom_destroy(ngeom);
+ rt_raster_destroy(raster);
+ return NULL;
+ }
+
+ if (covers)
+ break;
+
+ raster->width++;
+ raster->height++;
+ }
+ while (!covers);
+
+ RASTER_DEBUGF(4, "Skewed extent does cover normal extent with dimensions %d x %d", raster->width, raster->height);
+
+ raster->width = (int) ((((double) raster->width) * fabs(_gt[1]) + fabs(scale[0] / 2.)) / fabs(scale[0]));
+ raster->height = (int) ((((double) raster->height) * fabs(_gt[5]) + fabs(scale[1] / 2.)) / fabs(scale[1]));
+ _gt[1] = fabs(scale[0]);
+ _gt[5] = -1 * fabs(scale[1]);
+ _gt[2] = skew[0];
+ _gt[4] = skew[1];
+ rt_raster_set_geotransform_matrix(raster, _gt);
+
+ /* minimize width/height */
+ for (i = 0; i < 2; i++) {
+ covers = 1;
+ do {
+ if (i < 1)
+ raster->width--;
+ else
+ raster->height--;
+
+ /* construct sgeom from raster */
+ if ((rt_raster_get_convex_hull(raster, &geom) != ES_NONE) || geom == NULL) {
+ rterror("rt_raster_compute_skewed_raster: Could not build skewed extent's geometry for minimizing dimensions");
+ GEOSGeom_destroy(ngeom);
+ rt_raster_destroy(raster);
+ return NULL;
+ }
+
+ sgeom = (GEOSGeometry *) LWGEOM2GEOS(geom);
+ lwgeom_free(geom);
+
+ covers = GEOSRelatePattern(sgeom, ngeom, "******FF*");
+ GEOSGeom_destroy(sgeom);
+
+ if (covers == 2) {
+ rterror("rt_raster_compute_skewed_raster: Could not run covers test for minimizing dimensions");
+ GEOSGeom_destroy(ngeom);
+ rt_raster_destroy(raster);
+ return NULL;
+ }
+
+ if (!covers) {
+ if (i < 1)
+ raster->width++;
+ else
+ raster->height++;
+
+ break;
+ }
+ }
+ while (covers);
+ }
+
+ GEOSGeom_destroy(ngeom);
+
+ return raster;
+}
+
+/**
+ * Return TRUE if the raster is empty. i.e. is NULL, width = 0 or height = 0
+ *
+ * @param raster : the raster to get info from
+ *
+ * @return TRUE if the raster is empty, FALSE otherwise
+ */
+int
+rt_raster_is_empty(rt_raster raster) {
+ return (NULL == raster || raster->height <= 0 || raster->width <= 0);
+}
+
+/**
+ * Return TRUE if the raster has a band of this number.
+ *
+ * @param raster : the raster to get info from
+ * @param nband : the band number. 0-based
+ *
+ * @return TRUE if the raster has a band of this number, FALSE otherwise
+ */
+int
+rt_raster_has_band(rt_raster raster, int nband) {
+ return !(NULL == raster || nband >= raster->numBands || nband < 0);
+}
+
+/******************************************************************************
+* rt_raster_copy_band()
+******************************************************************************/
+
+/**
+ * Copy one band from one raster to another. Bands are duplicated from
+ * fromrast to torast using rt_band_duplicate. The caller will need
+ * to ensure that the copied band's data or path remains allocated
+ * for the lifetime of the copied bands.
+ *
+ * @param torast : raster to copy band to
+ * @param fromrast : raster to copy band from
+ * @param fromindex : index of band in source raster, 0-based
+ * @param toindex : index of new band in destination raster, 0-based
+ *
+ * @return The band index of the second raster where the new band is copied.
+ * -1 if error
+ */
+int
+rt_raster_copy_band(
+ rt_raster torast, rt_raster fromrast,
+ int fromindex, int toindex
+) {
+ rt_band srcband = NULL;
+ rt_band dstband = NULL;
+
+ assert(NULL != torast);
+ assert(NULL != fromrast);
+
+ /* Check raster dimensions */
+ if (torast->height != fromrast->height || torast->width != fromrast->width) {
+ rtwarn("rt_raster_copy_band: Attempting to add a band with different width or height");
+ return -1;
+ }
+
+ /* Check bands limits */
+ if (fromrast->numBands < 1) {
+ rtwarn("rt_raster_copy_band: Second raster has no band");
+ return -1;
+ }
+ else if (fromindex < 0) {
+ rtwarn("rt_raster_copy_band: Band index for second raster < 0. Defaulted to 0");
+ fromindex = 0;
+ }
+ else if (fromindex >= fromrast->numBands) {
+ rtwarn("rt_raster_copy_band: Band index for second raster > number of bands, truncated from %u to %u", fromindex, fromrast->numBands - 1);
+ fromindex = fromrast->numBands - 1;
+ }
+
+ if (toindex < 0) {
+ rtwarn("rt_raster_copy_band: Band index for first raster < 0. Defaulted to 0");
+ toindex = 0;
+ }
+ else if (toindex > torast->numBands) {
+ rtwarn("rt_raster_copy_band: Band index for first raster > number of bands, truncated from %u to %u", toindex, torast->numBands);
+ toindex = torast->numBands;
+ }
+
+ /* Get band from source raster */
+ srcband = rt_raster_get_band(fromrast, fromindex);
+
+ /* duplicate band */
+ dstband = rt_band_duplicate(srcband);
+
+ /* Add band to the second raster */
+ return rt_raster_add_band(torast, dstband, toindex);
+}
+
+/******************************************************************************
+* rt_raster_from_band()
+******************************************************************************/
+
+/**
+ * Construct a new rt_raster from an existing rt_raster and an array
+ * of band numbers
+ *
+ * @param raster : the source raster
+ * @param bandNums : array of band numbers to extract from source raster
+ * and add to the new raster (0 based)
+ * @param count : number of elements in bandNums
+ *
+ * @return a new rt_raster or NULL on error
+ */
+rt_raster
+rt_raster_from_band(rt_raster raster, uint32_t *bandNums, int count) {
+ rt_raster rast = NULL;
+ int i = 0;
+ int j = 0;
+ int idx;
+ int32_t flag;
+ double gt[6] = {0.};
+
+ assert(NULL != raster);
+ assert(NULL != bandNums);
+
+ RASTER_DEBUGF(3, "rt_raster_from_band: source raster has %d bands",
+ rt_raster_get_num_bands(raster));
+
+ /* create new raster */
+ rast = rt_raster_new(raster->width, raster->height);
+ if (NULL == rast) {
+ rterror("rt_raster_from_band: Out of memory allocating new raster");
+ return NULL;
+ }
+
+ /* copy raster attributes */
+ rt_raster_get_geotransform_matrix(raster, gt);
+ rt_raster_set_geotransform_matrix(rast, gt);
+
+ /* srid */
+ rt_raster_set_srid(rast, raster->srid);
+
+ /* copy bands */
+ for (i = 0; i < count; i++) {
+ idx = bandNums[i];
+ flag = rt_raster_copy_band(rast, raster, idx, i);
+
+ if (flag < 0) {
+ rterror("rt_raster_from_band: Could not copy band");
+ for (j = 0; j < i; j++) rt_band_destroy(rast->bands[j]);
+ rt_raster_destroy(rast);
+ return NULL;
+ }
+
+ RASTER_DEBUGF(3, "rt_raster_from_band: band created at index %d",
+ flag);
+ }
+
+ RASTER_DEBUGF(3, "rt_raster_from_band: new raster has %d bands",
+ rt_raster_get_num_bands(rast));
+ return rast;
+}
+
+/******************************************************************************
+* rt_raster_replace_band()
+******************************************************************************/
+
+/**
+ * Replace band at provided index with new band
+ *
+ * @param raster: raster of band to be replaced
+ * @param band : new band to add to raster
+ * @param index : index of band to replace (0-based)
+ *
+ * @return NULL on error or replaced band
+ */
+rt_band
+rt_raster_replace_band(rt_raster raster, rt_band band, int index) {
+ rt_band oldband = NULL;
+ assert(NULL != raster);
+ assert(NULL != band);
+
+ if (band->width != raster->width || band->height != raster->height) {
+ rterror("rt_raster_replace_band: Band does not match raster's dimensions: %dx%d band to %dx%d raster",
+ band->width, band->height, raster->width, raster->height);
+ return 0;
+ }
+
+ if (index >= raster->numBands || index < 0) {
+ rterror("rt_raster_replace_band: Band index is not valid");
+ return 0;
+ }
+
+ oldband = rt_raster_get_band(raster, index);
+ RASTER_DEBUGF(3, "rt_raster_replace_band: old band at %p", oldband);
+ RASTER_DEBUGF(3, "rt_raster_replace_band: new band at %p", band);
+
+ raster->bands[index] = band;
+ RASTER_DEBUGF(3, "rt_raster_replace_band: new band at %p", raster->bands[index]);
+
+ band->raster = raster;
+ oldband->raster = NULL;
+
+ return oldband;
+}
+
+/******************************************************************************
+* rt_raster_clone()
+******************************************************************************/
+
+/**
+ * Clone an existing raster
+ *
+ * @param raster : raster to clone
+ * @param deep : flag indicating if bands should be cloned
+ *
+ * @return a new rt_raster or NULL on error
+ */
+rt_raster
+rt_raster_clone(rt_raster raster, uint8_t deep) {
+ rt_raster rtn = NULL;
+ double gt[6] = {0};
+
+ assert(NULL != raster);
+
+ if (deep) {
+ int numband = rt_raster_get_num_bands(raster);
+ uint32_t *nband = NULL;
+ int i = 0;
+
+ nband = rtalloc(sizeof(uint32_t) * numband);
+ if (nband == NULL) {
+ rterror("rt_raster_clone: Could not allocate memory for deep clone");
+ return NULL;
+ }
+ for (i = 0; i < numband; i++)
+ nband[i] = i;
+
+ rtn = rt_raster_from_band(raster, nband, numband);
+ rtdealloc(nband);
+
+ return rtn;
+ }
+
+ rtn = rt_raster_new(
+ rt_raster_get_width(raster),
+ rt_raster_get_height(raster)
+ );
+ if (rtn == NULL) {
+ rterror("rt_raster_clone: Could not create cloned raster");
+ return NULL;
+ }
+
+ rt_raster_get_geotransform_matrix(raster, gt);
+ rt_raster_set_geotransform_matrix(rtn, gt);
+ rt_raster_set_srid(rtn, rt_raster_get_srid(raster));
+
+ return rtn;
+}
+
+/******************************************************************************
+* rt_raster_to_gdal()
+******************************************************************************/
+
+/**
+ * Return formatted GDAL raster from raster
+ *
+ * @param raster : the raster to convert
+ * @param srs : the raster's coordinate system in OGC WKT
+ * @param format : format to convert to. GDAL driver short name
+ * @param options : list of format creation options. array of strings
+ * @param gdalsize : will be set to the size of returned bytea
+ *
+ * @return formatted GDAL raster. the calling function is responsible
+ * for freeing the returned data using CPLFree()
+ */
+uint8_t*
+rt_raster_to_gdal(rt_raster raster, const char *srs,
+ char *format, char **options, uint64_t *gdalsize) {
+ GDALDriverH src_drv = NULL;
+ GDALDatasetH src_ds = NULL;
+
+ vsi_l_offset rtn_lenvsi;
+ uint64_t rtn_len = 0;
+
+ GDALDriverH rtn_drv = NULL;
+ GDALDatasetH rtn_ds = NULL;
+ uint8_t *rtn = NULL;
+
+ assert(NULL != raster);
+ assert(NULL != gdalsize);
+
+ /* any supported format is possible */
+ rt_util_gdal_register_all();
+ RASTER_DEBUG(3, "loaded all supported GDAL formats");
+
+ /* output format not specified */
+ if (format == NULL || !strlen(format))
+ format = "GTiff";
+ RASTER_DEBUGF(3, "output format is %s", format);
+
+ /* load raster into a GDAL MEM raster */
+ src_ds = rt_raster_to_gdal_mem(raster, srs, NULL, NULL, 0, &src_drv);
+ if (NULL == src_ds) {
+ rterror("rt_raster_to_gdal: Could not convert raster to GDAL MEM format");
+ return 0;
+ }
+
+ /* load driver */
+ rtn_drv = GDALGetDriverByName(format);
+ if (NULL == rtn_drv) {
+ rterror("rt_raster_to_gdal: Could not load the output GDAL driver");
+ GDALClose(src_ds);
+ return 0;
+ }
+ RASTER_DEBUG(3, "Output driver loaded");
+
+ /* convert GDAL MEM raster to output format */
+ RASTER_DEBUG(3, "Copying GDAL MEM raster to memory file in output format");
+ rtn_ds = GDALCreateCopy(
+ rtn_drv,
+ "/vsimem/out.dat", /* should be fine assuming this is in a process */
+ src_ds,
+ FALSE, /* should copy be strictly equivelent? */
+ options, /* format options */
+ NULL, /* progress function */
+ NULL /* progress data */
+ );
+ if (NULL == rtn_ds) {
+ rterror("rt_raster_to_gdal: Could not create the output GDAL dataset");
+ GDALClose(src_ds);
+ return 0;
+ }
+
+ /* close source dataset */
+ GDALClose(src_ds);
+ RASTER_DEBUG(3, "Closed GDAL MEM raster");
+
+ RASTER_DEBUGF(4, "dataset SRS: %s", GDALGetProjectionRef(rtn_ds));
+
+ /* close dataset, this also flushes any pending writes */
+ GDALClose(rtn_ds);
+ RASTER_DEBUG(3, "Closed GDAL output raster");
+
+ RASTER_DEBUG(3, "Done copying GDAL MEM raster to memory file in output format");
+
+ /* from memory file to buffer */
+ RASTER_DEBUG(3, "Copying GDAL memory file to buffer");
+ rtn = VSIGetMemFileBuffer("/vsimem/out.dat", &rtn_lenvsi, TRUE);
+ RASTER_DEBUG(3, "Done copying GDAL memory file to buffer");
+ if (NULL == rtn) {
+ rterror("rt_raster_to_gdal: Could not create the output GDAL raster");
+ return 0;
+ }
+
+ rtn_len = (uint64_t) rtn_lenvsi;
+ *gdalsize = rtn_len;
+
+ return rtn;
+}
+
+/******************************************************************************
+* rt_raster_gdal_drivers()
+******************************************************************************/
+
+/**
+ * Returns a set of available GDAL drivers
+ *
+ * @param drv_count : number of GDAL drivers available
+ * @param cancc : if non-zero, filter drivers to only those
+ * with support for CreateCopy and VirtualIO
+ *
+ * @return set of "gdaldriver" values of available GDAL drivers
+ */
+rt_gdaldriver
+rt_raster_gdal_drivers(uint32_t *drv_count, uint8_t cancc) {
+ const char *state;
+ const char *txt;
+ int txt_len;
+ GDALDriverH *drv = NULL;
+ rt_gdaldriver rtn = NULL;
+ int count;
+ int i;
+ uint32_t j;
+
+ assert(drv_count != NULL);
+
+ rt_util_gdal_register_all();
+ count = GDALGetDriverCount();
+ rtn = (rt_gdaldriver) rtalloc(count * sizeof(struct rt_gdaldriver_t));
+ if (NULL == rtn) {
+ rterror("rt_raster_gdal_drivers: Could not allocate memory for gdaldriver structure");
+ return 0;
+ }
+
+ for (i = 0, j = 0; i < count; i++) {
+ drv = GDALGetDriver(i);
+
+ if (cancc) {
+ /* CreateCopy support */
+ state = GDALGetMetadataItem(drv, GDAL_DCAP_CREATECOPY, NULL);
+ if (state == NULL) continue;
+
+ /* VirtualIO support */
+ state = GDALGetMetadataItem(drv, GDAL_DCAP_VIRTUALIO, NULL);
+ if (state == NULL) continue;
+ }
+
+ /* index of driver */
+ rtn[j].idx = i;
+
+ /* short name */
+ txt = GDALGetDriverShortName(drv);
+ txt_len = strlen(txt);
+
+ if (cancc) {
+ RASTER_DEBUGF(3, "rt_raster_gdal_driver: driver %s (%d) supports CreateCopy() and VirtualIO()", txt, i);
+ }
+
+ txt_len = (txt_len + 1) * sizeof(char);
+ rtn[j].short_name = (char *) rtalloc(txt_len);
+ memcpy(rtn[j].short_name, txt, txt_len);
+
+ /* long name */
+ txt = GDALGetDriverLongName(drv);
+ txt_len = strlen(txt);
+
+ txt_len = (txt_len + 1) * sizeof(char);
+ rtn[j].long_name = (char *) rtalloc(txt_len);
+ memcpy(rtn[j].long_name, txt, txt_len);
+
+ /* creation options */
+ txt = GDALGetDriverCreationOptionList(drv);
+ txt_len = strlen(txt);
+
+ txt_len = (txt_len + 1) * sizeof(char);
+ rtn[j].create_options = (char *) rtalloc(txt_len);
+ memcpy(rtn[j].create_options, txt, txt_len);
+
+ j++;
+ }
+
+ /* free unused memory */
+ rtn = rtrealloc(rtn, j * sizeof(struct rt_gdaldriver_t));
+ *drv_count = j;
+
+ return rtn;
+}
+
+/******************************************************************************
+* rt_raster_to_gdal_mem()
+******************************************************************************/
+
+/**
+ * Return GDAL dataset using GDAL MEM driver from raster
+ *
+ * @param raster : raster to convert to GDAL MEM
+ * @param srs : the raster's coordinate system in OGC WKT
+ * @param bandNums : array of band numbers to extract from raster
+ * and include in the GDAL dataset (0 based)
+ * @param excludeNodataValues : array of zero, nonzero where if non-zero,
+ * ignore nodata values for the band
+ * @param count : number of elements in bandNums
+ * @param rtn_drv : is set to the GDAL driver object
+ *
+ * @return GDAL dataset using GDAL MEM driver
+ */
+GDALDatasetH
+rt_raster_to_gdal_mem(
+ rt_raster raster,
+ const char *srs,
+ uint32_t *bandNums,
+ int *excludeNodataValues,
+ int count,
+ GDALDriverH *rtn_drv
+) {
+ GDALDriverH drv = NULL;
+ GDALDatasetH ds = NULL;
+ double gt[6] = {0.0};
+ CPLErr cplerr;
+ GDALDataType gdal_pt = GDT_Unknown;
+ GDALRasterBandH band;
+ void *pVoid;
+ char *pszDataPointer;
+ char szGDALOption[50];
+ char *apszOptions[4];
+ double nodata = 0.0;
+ int allocBandNums = 0;
+ int allocNodataValues = 0;
+
+ int i;
+ int numBands;
+ uint32_t width = 0;
+ uint32_t height = 0;
+ rt_band rtband = NULL;
+ rt_pixtype pt = PT_END;
+
+ assert(NULL != raster);
+ assert(NULL != rtn_drv);
+
+ /* store raster in GDAL MEM raster */
+ if (!rt_util_gdal_driver_registered("MEM"))
+ GDALRegister_MEM();
+ drv = GDALGetDriverByName("MEM");
+ if (NULL == drv) {
+ rterror("rt_raster_to_gdal_mem: Could not load the MEM GDAL driver");
+ return 0;
+ }
+ *rtn_drv = drv;
+
+ width = rt_raster_get_width(raster);
+ height = rt_raster_get_height(raster);
+ ds = GDALCreate(
+ drv, "",
+ width, height,
+ 0, GDT_Byte, NULL
+ );
+ if (NULL == ds) {
+ rterror("rt_raster_to_gdal_mem: Could not create a GDALDataset to convert into");
+ return 0;
+ }
+
+ /* add geotransform */
+ rt_raster_get_geotransform_matrix(raster, gt);
+ cplerr = GDALSetGeoTransform(ds, gt);
+ if (cplerr != CE_None) {
+ rterror("rt_raster_to_gdal_mem: Could not set geotransformation");
+ GDALClose(ds);
+ return 0;
+ }
+
+ /* set spatial reference */
+ if (NULL != srs && strlen(srs)) {
+ char *_srs = rt_util_gdal_convert_sr(srs, 0);
+ if (_srs == NULL) {
+ rterror("rt_raster_to_gdal_mem: Could not convert srs to GDAL accepted format");
+ GDALClose(ds);
+ return 0;
+ }
+
+ cplerr = GDALSetProjection(ds, _srs);
+ CPLFree(_srs);
+ if (cplerr != CE_None) {
+ rterror("rt_raster_to_gdal_mem: Could not set projection");
+ GDALClose(ds);
+ return 0;
+ }
+ RASTER_DEBUGF(3, "Projection set to: %s", GDALGetProjectionRef(ds));
+ }
+
+ /* process bandNums */
+ numBands = rt_raster_get_num_bands(raster);
+ if (NULL != bandNums && count > 0) {
+ for (i = 0; i < count; i++) {
+ if (bandNums[i] >= numBands) {
+ rterror("rt_raster_to_gdal_mem: The band index %d is invalid", bandNums[i]);
+ GDALClose(ds);
+ return 0;
+ }
+ }
+ }
+ else {
+ count = numBands;
+ bandNums = (uint32_t *) rtalloc(sizeof(uint32_t) * count);
+ if (NULL == bandNums) {
+ rterror("rt_raster_to_gdal_mem: Could not allocate memory for band indices");
+ GDALClose(ds);
+ return 0;
+ }
+ allocBandNums = 1;
+ for (i = 0; i < count; i++) bandNums[i] = i;
+ }
+
+ /* process exclude_nodata_values */
+ if (NULL == excludeNodataValues) {
+ excludeNodataValues = (int *) rtalloc(sizeof(int) * count);
+ if (NULL == excludeNodataValues) {
+ rterror("rt_raster_to_gdal_mem: Could not allocate memory for NODATA flags");
+ GDALClose(ds);
+ return 0;
+ }
+ allocNodataValues = 1;
+ for (i = 0; i < count; i++) excludeNodataValues[i] = 1;
+ }
+
+ /* add band(s) */
+ for (i = 0; i < count; i++) {
+ rtband = rt_raster_get_band(raster, bandNums[i]);
+ if (NULL == rtband) {
+ rterror("rt_raster_to_gdal_mem: Could not get requested band index %d", bandNums[i]);
+ if (allocBandNums) rtdealloc(bandNums);
+ if (allocNodataValues) rtdealloc(excludeNodataValues);
+ GDALClose(ds);
+ return 0;
+ }
+
+ pt = rt_band_get_pixtype(rtband);
+ gdal_pt = rt_util_pixtype_to_gdal_datatype(pt);
+ if (gdal_pt == GDT_Unknown)
+ rtwarn("rt_raster_to_gdal_mem: Unknown pixel type for band");
+
+ /*
+ For all pixel types other than PT_8BSI, set pointer to start of data
+ */
+ if (pt != PT_8BSI) {
+ pVoid = rt_band_get_data(rtband);
+ RASTER_DEBUGF(4, "Band data is at pos %p", pVoid);
+
+ pszDataPointer = (char *) rtalloc(20 * sizeof (char));
+ sprintf(pszDataPointer, "%p", pVoid);
+ RASTER_DEBUGF(4, "rt_raster_to_gdal_mem: szDatapointer is %p",
+ pszDataPointer);
+
+ if (strnicmp(pszDataPointer, "0x", 2) == 0)
+ sprintf(szGDALOption, "DATAPOINTER=%s", pszDataPointer);
+ else
+ sprintf(szGDALOption, "DATAPOINTER=0x%s", pszDataPointer);
+
+ RASTER_DEBUG(3, "Storing info for GDAL MEM raster band");
+
+ apszOptions[0] = szGDALOption;
+ apszOptions[1] = NULL; /* pixel offset, not needed */
+ apszOptions[2] = NULL; /* line offset, not needed */
+ apszOptions[3] = NULL;
+
+ /* free */
+ rtdealloc(pszDataPointer);
+
+ /* add band */
+ if (GDALAddBand(ds, gdal_pt, apszOptions) == CE_Failure) {
+ rterror("rt_raster_to_gdal_mem: Could not add GDAL raster band");
+ if (allocBandNums) rtdealloc(bandNums);
+ GDALClose(ds);
+ return 0;
+ }
+ }
+ /*
+ PT_8BSI is special as GDAL has no equivalent pixel type.
+ Must convert 8BSI to 16BSI so create basic band
+ */
+ else {
+ /* add band */
+ if (GDALAddBand(ds, gdal_pt, NULL) == CE_Failure) {
+ rterror("rt_raster_to_gdal_mem: Could not add GDAL raster band");
+ if (allocBandNums) rtdealloc(bandNums);
+ if (allocNodataValues) rtdealloc(excludeNodataValues);
+ GDALClose(ds);
+ return 0;
+ }
+ }
+
+ /* check band count */
+ if (GDALGetRasterCount(ds) != i + 1) {
+ rterror("rt_raster_to_gdal_mem: Error creating GDAL MEM raster band");
+ if (allocBandNums) rtdealloc(bandNums);
+ if (allocNodataValues) rtdealloc(excludeNodataValues);
+ GDALClose(ds);
+ return 0;
+ }
+
+ /* get new band */
+ band = NULL;
+ band = GDALGetRasterBand(ds, i + 1);
+ if (NULL == band) {
+ rterror("rt_raster_to_gdal_mem: Could not get GDAL band for additional processing");
+ if (allocBandNums) rtdealloc(bandNums);
+ if (allocNodataValues) rtdealloc(excludeNodataValues);
+ GDALClose(ds);
+ return 0;
+ }
+
+ /* PT_8BSI requires manual setting of pixels */
+ if (pt == PT_8BSI) {
+ int nXBlocks, nYBlocks;
+ int nXBlockSize, nYBlockSize;
+ int iXBlock, iYBlock;
+ int nXValid, nYValid;
+ int iX, iY;
+ int iXMax, iYMax;
+
+ int x, y, z;
+ uint32_t valueslen = 0;
+ int16_t *values = NULL;
+ double value = 0.;
+
+ /* this makes use of GDAL's "natural" blocks */
+ GDALGetBlockSize(band, &nXBlockSize, &nYBlockSize);
+ nXBlocks = (width + nXBlockSize - 1) / nXBlockSize;
+ nYBlocks = (height + nYBlockSize - 1) / nYBlockSize;
+ RASTER_DEBUGF(4, "(nXBlockSize, nYBlockSize) = (%d, %d)", nXBlockSize, nYBlockSize);
+ RASTER_DEBUGF(4, "(nXBlocks, nYBlocks) = (%d, %d)", nXBlocks, nYBlocks);
+
+ /* length is for the desired pixel type */
+ valueslen = rt_pixtype_size(PT_16BSI) * nXBlockSize * nYBlockSize;
+ values = rtalloc(valueslen);
+ if (NULL == values) {
+ rterror("rt_raster_to_gdal_mem: Could not allocate memory for GDAL band pixel values");
+ if (allocBandNums) rtdealloc(bandNums);
+ if (allocNodataValues) rtdealloc(excludeNodataValues);
+ GDALClose(ds);
+ return 0;
+ }
+
+ for (iYBlock = 0; iYBlock < nYBlocks; iYBlock++) {
+ for (iXBlock = 0; iXBlock < nXBlocks; iXBlock++) {
+ memset(values, 0, valueslen);
+
+ x = iXBlock * nXBlockSize;
+ y = iYBlock * nYBlockSize;
+ RASTER_DEBUGF(4, "(iXBlock, iYBlock) = (%d, %d)", iXBlock, iYBlock);
+ RASTER_DEBUGF(4, "(x, y) = (%d, %d)", x, y);
+
+ /* valid block width */
+ if ((iXBlock + 1) * nXBlockSize > width)
+ nXValid = width - (iXBlock * nXBlockSize);
+ else
+ nXValid = nXBlockSize;
+
+ /* valid block height */
+ if ((iYBlock + 1) * nYBlockSize > height)
+ nYValid = height - (iYBlock * nYBlockSize);
+ else
+ nYValid = nYBlockSize;
+
+ RASTER_DEBUGF(4, "(nXValid, nYValid) = (%d, %d)", nXValid, nYValid);
+
+ /* convert 8BSI values to 16BSI */
+ z = 0;
+ iYMax = y + nYValid;
+ iXMax = x + nXValid;
+ for (iY = y; iY < iYMax; iY++) {
+ for (iX = x; iX < iXMax; iX++) {
+ if (rt_band_get_pixel(rtband, iX, iY, &value, NULL) != ES_NONE) {
+ rterror("rt_raster_to_gdal_mem: Could not get pixel value to convert from 8BSI to 16BSI");
+ rtdealloc(values);
+ if (allocBandNums) rtdealloc(bandNums);
+ if (allocNodataValues) rtdealloc(excludeNodataValues);
+ GDALClose(ds);
+ return 0;
+ }
+
+ values[z++] = rt_util_clamp_to_16BSI(value);
+ }
+ }
+
+ /* burn values */
+ if (GDALRasterIO(
+ band, GF_Write,
+ x, y,
+ nXValid, nYValid,
+ values, nXValid, nYValid,
+ gdal_pt,
+ 0, 0
+ ) != CE_None) {
+ rterror("rt_raster_to_gdal_mem: Could not write converted 8BSI to 16BSI values to GDAL band");
+ rtdealloc(values);
+ if (allocBandNums) rtdealloc(bandNums);
+ if (allocNodataValues) rtdealloc(excludeNodataValues);
+ GDALClose(ds);
+ return 0;
+ }
+ }
+ }
+
+ rtdealloc(values);
+ }
+
+ /* Add nodata value for band */
+ if (rt_band_get_hasnodata_flag(rtband) != FALSE && excludeNodataValues[i]) {
+ rt_band_get_nodata(rtband, &nodata);
+ if (GDALSetRasterNoDataValue(band, nodata) != CE_None)
+ rtwarn("rt_raster_to_gdal_mem: Could not set nodata value for band");
+ RASTER_DEBUGF(3, "nodata value set to %f", GDALGetRasterNoDataValue(band, NULL));
+ }
+
+#if POSTGIS_DEBUG_LEVEL > 3
+ {
+ GDALRasterBandH _grb = NULL;
+ double _min;
+ double _max;
+ double _mean;
+ double _stddev;
+
+ _grb = GDALGetRasterBand(ds, i + 1);
+ GDALComputeRasterStatistics(_grb, FALSE, &_min, &_max, &_mean, &_stddev, NULL, NULL);
+ RASTER_DEBUGF(4, "GDAL Band %d stats: %f, %f, %f, %f", i + 1, _min, _max, _mean, _stddev);
+ }
+#endif
+
+ }
+
+ /* necessary??? */
+ GDALFlushCache(ds);
+
+ if (allocBandNums) rtdealloc(bandNums);
+ if (allocNodataValues) rtdealloc(excludeNodataValues);
+
+ return ds;
+}
+
+/******************************************************************************
+* rt_raster_from_gdal_dataset()
+******************************************************************************/
+
+/**
+ * Return a raster from a GDAL dataset
+ *
+ * @param ds : the GDAL dataset to convert to a raster
+ *
+ * @return raster or NULL
+ */
+rt_raster
+rt_raster_from_gdal_dataset(GDALDatasetH ds) {
+ rt_raster rast = NULL;
+ double gt[6] = {0};
+ CPLErr cplerr;
+ uint32_t width = 0;
+ uint32_t height = 0;
+ uint32_t numBands = 0;
+ int i = 0;
+ char *authname = NULL;
+ char *authcode = NULL;
+
+ GDALRasterBandH gdband = NULL;
+ GDALDataType gdpixtype = GDT_Unknown;
+ rt_band band;
+ int32_t idx;
+ rt_pixtype pt = PT_END;
+ uint32_t ptlen = 0;
+ int hasnodata = 0;
+ double nodataval;
+
+ int x;
+ int y;
+
+ int nXBlocks, nYBlocks;
+ int nXBlockSize, nYBlockSize;
+ int iXBlock, iYBlock;
+ int nXValid, nYValid;
+ int iY;
+
+ void *values = NULL;
+ uint32_t valueslen = 0;
+ void *ptr = NULL;
+
+ assert(NULL != ds);
+
+ /* raster size */
+ width = GDALGetRasterXSize(ds);
+ height = GDALGetRasterYSize(ds);
+ RASTER_DEBUGF(3, "Raster dimensions (width x height): %d x %d", width, height);
+
+ /* create new raster */
+ RASTER_DEBUG(3, "Creating new raster");
+ rast = rt_raster_new(width, height);
+ if (NULL == rast) {
+ rterror("rt_raster_from_gdal_dataset: Out of memory allocating new raster");
+ return NULL;
+ }
+ RASTER_DEBUGF(3, "Created raster dimensions (width x height): %d x %d", rast->width, rast->height);
+
+ /* get raster attributes */
+ cplerr = GDALGetGeoTransform(ds, gt);
+ if (GDALGetGeoTransform(ds, gt) != CE_None) {
+ RASTER_DEBUG(4, "Using default geotransform matrix (0, 1, 0, 0, 0, -1)");
+ gt[0] = 0;
+ gt[1] = 1;
+ gt[2] = 0;
+ gt[3] = 0;
+ gt[4] = 0;
+ gt[5] = -1;
+ }
+
+ /* apply raster attributes */
+ rt_raster_set_geotransform_matrix(rast, gt);
+
+ RASTER_DEBUGF(3, "Raster geotransform (%f, %f, %f, %f, %f, %f)",
+ gt[0], gt[1], gt[2], gt[3], gt[4], gt[5]);
+
+ /* srid */
+ if (rt_util_gdal_sr_auth_info(ds, &authname, &authcode) == ES_NONE) {
+ if (
+ authname != NULL &&
+ strcmp(authname, "EPSG") == 0 &&
+ authcode != NULL
+ ) {
+ rt_raster_set_srid(rast, atoi(authcode));
+ RASTER_DEBUGF(3, "New raster's SRID = %d", rast->srid);
+ }
+
+ if (authname != NULL)
+ rtdealloc(authname);
+ if (authcode != NULL)
+ rtdealloc(authcode);
+ }
+
+ numBands = GDALGetRasterCount(ds);
+
+#if POSTGIS_DEBUG_LEVEL > 3
+ for (i = 1; i <= numBands; i++) {
+ GDALRasterBandH _grb = NULL;
+ double _min;
+ double _max;
+ double _mean;
+ double _stddev;
+
+ _grb = GDALGetRasterBand(ds, i);
+ GDALComputeRasterStatistics(_grb, FALSE, &_min, &_max, &_mean, &_stddev, NULL, NULL);
+ RASTER_DEBUGF(4, "GDAL Band %d stats: %f, %f, %f, %f", i, _min, _max, _mean, _stddev);
+ }
+#endif
+
+ /* copy bands */
+ for (i = 1; i <= numBands; i++) {
+ RASTER_DEBUGF(3, "Processing band %d of %d", i, numBands);
+ gdband = NULL;
+ gdband = GDALGetRasterBand(ds, i);
+ if (NULL == gdband) {
+ rterror("rt_raster_from_gdal_dataset: Could not get GDAL band");
+ rt_raster_destroy(rast);
+ return NULL;
+ }
+ RASTER_DEBUGF(4, "gdband @ %p", gdband);
+
+ /* pixtype */
+ gdpixtype = GDALGetRasterDataType(gdband);
+ RASTER_DEBUGF(4, "gdpixtype, size = %s, %d", GDALGetDataTypeName(gdpixtype), GDALGetDataTypeSize(gdpixtype) / 8);
+ pt = rt_util_gdal_datatype_to_pixtype(gdpixtype);
+ if (pt == PT_END) {
+ rterror("rt_raster_from_gdal_dataset: Unknown pixel type for GDAL band");
+ rt_raster_destroy(rast);
+ return NULL;
+ }
+ ptlen = rt_pixtype_size(pt);
+
+ /* size: width and height */
+ width = GDALGetRasterBandXSize(gdband);
+ height = GDALGetRasterBandYSize(gdband);
+ RASTER_DEBUGF(3, "GDAL band dimensions (width x height): %d x %d", width, height);
+
+ /* nodata */
+ nodataval = GDALGetRasterNoDataValue(gdband, &hasnodata);
+ RASTER_DEBUGF(3, "(hasnodata, nodataval) = (%d, %f)", hasnodata, nodataval);
+
+ /* create band object */
+ idx = rt_raster_generate_new_band(
+ rast, pt,
+ (hasnodata ? nodataval : 0),
+ hasnodata, nodataval, rt_raster_get_num_bands(rast)
+ );
+ if (idx < 0) {
+ rterror("rt_raster_from_gdal_dataset: Could not allocate memory for raster band");
+ rt_raster_destroy(rast);
+ return NULL;
+ }
+ band = rt_raster_get_band(rast, idx);
+ RASTER_DEBUGF(3, "Created band of dimension (width x height): %d x %d", band->width, band->height);
+
+ /* this makes use of GDAL's "natural" blocks */
+ GDALGetBlockSize(gdband, &nXBlockSize, &nYBlockSize);
+ nXBlocks = (width + nXBlockSize - 1) / nXBlockSize;
+ nYBlocks = (height + nYBlockSize - 1) / nYBlockSize;
+ RASTER_DEBUGF(4, "(nXBlockSize, nYBlockSize) = (%d, %d)", nXBlockSize, nYBlockSize);
+ RASTER_DEBUGF(4, "(nXBlocks, nYBlocks) = (%d, %d)", nXBlocks, nYBlocks);
+
+ /* allocate memory for values */
+ valueslen = ptlen * nXBlockSize * nYBlockSize;
+ switch (gdpixtype) {
+ case GDT_Byte:
+ values = (uint8_t *) rtalloc(valueslen);
+ break;
+ case GDT_UInt16:
+ values = (uint16_t *) rtalloc(valueslen);
+ break;
+ case GDT_Int16:
+ values = (int16_t *) rtalloc(valueslen);
+ break;
+ case GDT_UInt32:
+ values = (uint32_t *) rtalloc(valueslen);
+ break;
+ case GDT_Int32:
+ values = (int32_t *) rtalloc(valueslen);
+ break;
+ case GDT_Float32:
+ values = (float *) rtalloc(valueslen);
+ break;
+ case GDT_Float64:
+ values = (double *) rtalloc(valueslen);
+ break;
+ default:
+ /* should NEVER get here */
+ rterror("rt_raster_from_gdal_dataset: Could not allocate memory for unknown pixel type");
+ rt_raster_destroy(rast);
+ return NULL;
+ }
+ if (values == NULL) {
+ rterror("rt_raster_from_gdal_dataset: Could not allocate memory for GDAL band pixel values");
+ rt_raster_destroy(rast);
+ return NULL;
+ }
+ RASTER_DEBUGF(3, "values @ %p of length = %d", values, valueslen);
+
+ for (iYBlock = 0; iYBlock < nYBlocks; iYBlock++) {
+ for (iXBlock = 0; iXBlock < nXBlocks; iXBlock++) {
+ x = iXBlock * nXBlockSize;
+ y = iYBlock * nYBlockSize;
+ RASTER_DEBUGF(4, "(iXBlock, iYBlock) = (%d, %d)", iXBlock, iYBlock);
+ RASTER_DEBUGF(4, "(x, y) = (%d, %d)", x, y);
+
+ memset(values, 0, valueslen);
+
+ /* valid block width */
+ if ((iXBlock + 1) * nXBlockSize > width)
+ nXValid = width - (iXBlock * nXBlockSize);
+ else
+ nXValid = nXBlockSize;
+
+ /* valid block height */
+ if ((iYBlock + 1) * nYBlockSize > height)
+ nYValid = height - (iYBlock * nYBlockSize);
+ else
+ nYValid = nYBlockSize;
+
+ RASTER_DEBUGF(4, "(nXValid, nYValid) = (%d, %d)", nXValid, nYValid);
+
+ cplerr = GDALRasterIO(
+ gdband, GF_Read,
+ x, y,
+ nXValid, nYValid,
+ values, nXValid, nYValid,
+ gdpixtype,
+ 0, 0
+ );
+ if (cplerr != CE_None) {
+ rterror("rt_raster_from_gdal_dataset: Could not get data from GDAL raster");
+ rtdealloc(values);
+ rt_raster_destroy(rast);
+ return NULL;
+ }
+
+ /* if block width is same as raster width, shortcut */
+ if (nXBlocks == 1 && nYBlockSize > 1 && nXValid == width) {
+ x = 0;
+ y = nYBlockSize * iYBlock;
+
+ RASTER_DEBUGF(4, "Setting set of pixel lines at (%d, %d) for %d pixels", x, y, nXValid * nYValid);
+ rt_band_set_pixel_line(band, x, y, values, nXValid * nYValid);
+ }
+ else {
+ ptr = values;
+ x = nXBlockSize * iXBlock;
+ for (iY = 0; iY < nYValid; iY++) {
+ y = iY + (nYBlockSize * iYBlock);
+
+ RASTER_DEBUGF(4, "Setting pixel line at (%d, %d) for %d pixels", x, y, nXValid);
+ rt_band_set_pixel_line(band, x, y, ptr, nXValid);
+ ptr += (nXValid * ptlen);
+ }
+ }
+ }
+ }
+
+ /* free memory */
+ rtdealloc(values);
+ }
+
+ return rast;
+}
+
+/******************************************************************************
+* rt_raster_gdal_rasterize()
+******************************************************************************/
+
+typedef struct _rti_rasterize_arg_t* _rti_rasterize_arg;
+struct _rti_rasterize_arg_t {
+ uint8_t noband;
+
+ uint32_t numbands;
+
+ rt_pixtype *pixtype;
+ double *init;
+ double *nodata;
+ uint8_t *hasnodata;
+ double *value;
+ int *bandlist;
+};
+
+static _rti_rasterize_arg
+_rti_rasterize_arg_init() {
+ _rti_rasterize_arg arg = NULL;
+
+ arg = rtalloc(sizeof(struct _rti_rasterize_arg_t));
+ if (arg == NULL) {
+ rterror("_rti_rasterize_arg_init: Could not allocate memory for _rti_rasterize_arg");
+ return NULL;
+ }
+
+ arg->noband = 0;
+
+ arg->numbands = 0;
+ arg->pixtype = NULL;
+ arg->init = NULL;
+ arg->nodata = NULL;
+ arg->hasnodata = NULL;
+ arg->value = NULL;
+ arg->bandlist = NULL;
+
+ return arg;
+}
+
+static void
+_rti_rasterize_arg_destroy(_rti_rasterize_arg arg) {
+ if (arg->noband) {
+ if (arg->pixtype != NULL)
+ rtdealloc(arg->pixtype);
+ if (arg->init != NULL)
+ rtdealloc(arg->init);
+ if (arg->nodata != NULL)
+ rtdealloc(arg->nodata);
+ if (arg->hasnodata != NULL)
+ rtdealloc(arg->hasnodata);
+ if (arg->value != NULL)
+ rtdealloc(arg->value);
+ }
+
+ if (arg->bandlist != NULL)
+ rtdealloc(arg->bandlist);
+
+ rtdealloc(arg);
+}
+
+/**
+ * Return a raster of the provided geometry
+ *
+ * @param wkb : WKB representation of the geometry to convert
+ * @param wkb_len : length of the WKB representation of the geometry
+ * @param srs : the geometry's coordinate system in OGC WKT
+ * @param num_bands : number of bands in the output raster
+ * @param pixtype : data type of each band
+ * @param init : array of values to initialize each band with
+ * @param value : array of values for pixels of geometry
+ * @param nodata : array of nodata values for each band
+ * @param hasnodata : array flagging the presence of nodata for each band
+ * @param width : the number of columns of the raster
+ * @param height : the number of rows of the raster
+ * @param scale_x : the pixel width of the raster
+ * @param scale_y : the pixel height of the raster
+ * @param ul_xw : the X value of upper-left corner of the raster
+ * @param ul_yw : the Y value of upper-left corner of the raster
+ * @param grid_xw : the X value of point on grid to align raster to
+ * @param grid_yw : the Y value of point on grid to align raster to
+ * @param skew_x : the X skew of the raster
+ * @param skew_y : the Y skew of the raster
+ * @param options : array of options. only option is "ALL_TOUCHED"
+ *
+ * @return the raster of the provided geometry or NULL
+ */
+rt_raster
+rt_raster_gdal_rasterize(
+ const unsigned char *wkb, uint32_t wkb_len,
+ const char *srs,
+ uint32_t num_bands, rt_pixtype *pixtype,
+ double *init, double *value,
+ double *nodata, uint8_t *hasnodata,
+ int *width, int *height,
+ double *scale_x, double *scale_y,
+ double *ul_xw, double *ul_yw,
+ double *grid_xw, double *grid_yw,
+ double *skew_x, double *skew_y,
+ char **options
+) {
+ rt_raster rast = NULL;
+ int i = 0;
+ int err = 0;
+
+ _rti_rasterize_arg arg = NULL;
+
+ int _dim[2] = {0};
+ double _scale[2] = {0};
+ double _skew[2] = {0};
+
+ OGRErr ogrerr;
+ OGRSpatialReferenceH src_sr = NULL;
+ OGRGeometryH src_geom;
+ OGREnvelope src_env;
+ rt_envelope extent;
+ OGRwkbGeometryType wkbtype = wkbUnknown;
+
+ int ul_user = 0;
+
+ CPLErr cplerr;
+ double _gt[6] = {0};
+ GDALDriverH _drv = NULL;
+ GDALDatasetH _ds = NULL;
+ GDALRasterBandH _band = NULL;
+
+ uint16_t _width = 0;
+ uint16_t _height = 0;
+
+ RASTER_DEBUG(3, "starting");
+
+ assert(NULL != wkb);
+ assert(0 != wkb_len);
+
+ /* internal variables */
+ arg = _rti_rasterize_arg_init();
+ if (arg == NULL) {
+ rterror("rt_raster_gdal_rasterize: Could not initialize internal variables");
+ return NULL;
+ }
+
+ /* no bands, raster is a mask */
+ if (num_bands < 1) {
+ arg->noband = 1;
+ arg->numbands = 1;
+
+ arg->pixtype = (rt_pixtype *) rtalloc(sizeof(rt_pixtype));
+ arg->pixtype[0] = PT_8BUI;
+
+ arg->init = (double *) rtalloc(sizeof(double));
+ arg->init[0] = 0;
+
+ arg->nodata = (double *) rtalloc(sizeof(double));
+ arg->nodata[0] = 0;
+
+ arg->hasnodata = (uint8_t *) rtalloc(sizeof(uint8_t));
+ arg->hasnodata[0] = 1;
+
+ arg->value = (double *) rtalloc(sizeof(double));
+ arg->value[0] = 1;
+ }
+ else {
+ arg->noband = 0;
+ arg->numbands = num_bands;
+
+ arg->pixtype = pixtype;
+ arg->init = init;
+ arg->nodata = nodata;
+ arg->hasnodata = hasnodata;
+ arg->value = value;
+ }
+
+ /* OGR spatial reference */
+ if (NULL != srs && strlen(srs)) {
+ src_sr = OSRNewSpatialReference(NULL);
+ if (OSRSetFromUserInput(src_sr, srs) != OGRERR_NONE) {
+ rterror("rt_raster_gdal_rasterize: Could not create OSR spatial reference using the provided srs: %s", srs);
+ _rti_rasterize_arg_destroy(arg);
+ return NULL;
+ }
+ }
+
+ /* convert WKB to OGR Geometry */
+ ogrerr = OGR_G_CreateFromWkb((unsigned char *) wkb, src_sr, &src_geom, wkb_len);
+ if (ogrerr != OGRERR_NONE) {
+ rterror("rt_raster_gdal_rasterize: Could not create OGR Geometry from WKB");
+
+ _rti_rasterize_arg_destroy(arg);
+
+ if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
+ /* OGRCleanupAll(); */
+
+ return NULL;
+ }
+
+ /* OGR Geometry is empty */
+ if (OGR_G_IsEmpty(src_geom)) {
+ rtinfo("Geometry provided is empty. Returning empty raster");
+
+ _rti_rasterize_arg_destroy(arg);
+
+ OGR_G_DestroyGeometry(src_geom);
+ if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
+ /* OGRCleanupAll(); */
+
+ return rt_raster_new(0, 0);
+ }
+
+ /* get envelope */
+ OGR_G_GetEnvelope(src_geom, &src_env);
+ rt_util_from_ogr_envelope(src_env, &extent);
+
+ RASTER_DEBUGF(3, "Suggested raster envelope: %f, %f, %f, %f",
+ extent.MinX, extent.MinY, extent.MaxX, extent.MaxY);
+
+ /* user-defined scale */
+ if (
+ (NULL != scale_x) &&
+ (NULL != scale_y) &&
+ (FLT_NEQ(*scale_x, 0.0)) &&
+ (FLT_NEQ(*scale_y, 0.0))
+ ) {
+ /* for now, force scale to be in left-right, top-down orientation */
+ _scale[0] = fabs(*scale_x);
+ _scale[1] = fabs(*scale_y);
+ }
+ /* user-defined width/height */
+ else if (
+ (NULL != width) &&
+ (NULL != height) &&
+ (FLT_NEQ(*width, 0.0)) &&
+ (FLT_NEQ(*height, 0.0))
+ ) {
+ _dim[0] = fabs(*width);
+ _dim[1] = fabs(*height);
+
+ if (FLT_NEQ(extent.MaxX, extent.MinX))
+ _scale[0] = fabs((extent.MaxX - extent.MinX) / _dim[0]);
+ else
+ _scale[0] = 1.;
+
+ if (FLT_NEQ(extent.MaxY, extent.MinY))
+ _scale[1] = fabs((extent.MaxY - extent.MinY) / _dim[1]);
+ else
+ _scale[1] = 1.;
+ }
+ else {
+ rterror("rt_raster_gdal_rasterize: Values must be provided for width and height or X and Y of scale");
+
+ _rti_rasterize_arg_destroy(arg);
+
+ OGR_G_DestroyGeometry(src_geom);
+ if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
+ /* OGRCleanupAll(); */
+
+ return NULL;
+ }
+ RASTER_DEBUGF(3, "scale (x, y) = %f, %f", _scale[0], -1 * _scale[1]);
+ RASTER_DEBUGF(3, "dim (x, y) = %d, %d", _dim[0], _dim[1]);
+
+ /* user-defined skew */
+ if (NULL != skew_x) {
+ _skew[0] = *skew_x;
+
+ /*
+ negative scale-x affects skew
+ for now, force skew to be in left-right, top-down orientation
+ */
+ if (
+ NULL != scale_x &&
+ *scale_x < 0.
+ ) {
+ _skew[0] *= -1;
+ }
+ }
+ if (NULL != skew_y) {
+ _skew[1] = *skew_y;
+
+ /*
+ positive scale-y affects skew
+ for now, force skew to be in left-right, top-down orientation
+ */
+ if (
+ NULL != scale_y &&
+ *scale_y > 0.
+ ) {
+ _skew[1] *= -1;
+ }
+ }
+
+ /*
+ if geometry is a point, a linestring or set of either and bounds not set,
+ increase extent by a pixel to avoid missing points on border
+
+ a whole pixel is used instead of half-pixel due to backward
+ compatibility with GDAL 1.6, 1.7 and 1.8. 1.9+ works fine with half-pixel.
+ */
+ wkbtype = wkbFlatten(OGR_G_GetGeometryType(src_geom));
+ if ((
+ (wkbtype == wkbPoint) ||
+ (wkbtype == wkbMultiPoint) ||
+ (wkbtype == wkbLineString) ||
+ (wkbtype == wkbMultiLineString)
+ ) &&
+ FLT_EQ(_dim[0], 0) &&
+ FLT_EQ(_dim[1], 0)
+ ) {
+ int result;
+ LWPOLY *epoly = NULL;
+ LWGEOM *lwgeom = NULL;
+ GEOSGeometry *egeom = NULL;
+ GEOSGeometry *geom = NULL;
+
+ RASTER_DEBUG(3, "Testing geometry is properly contained by extent");
+
+ /*
+ see if geometry is properly contained by extent
+ all parts of geometry lies within extent
+ */
+
+ /* initialize GEOS */
+ initGEOS(lwnotice, lwgeom_geos_error);
+
+ /* convert envelope to geometry */
+ RASTER_DEBUG(4, "Converting envelope to geometry");
+ epoly = rt_util_envelope_to_lwpoly(extent);
+ if (epoly == NULL) {
+ rterror("rt_raster_gdal_rasterize: Could not create envelope's geometry to test if geometry is properly contained by extent");
+
+ _rti_rasterize_arg_destroy(arg);
+
+ OGR_G_DestroyGeometry(src_geom);
+ if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
+ /* OGRCleanupAll(); */
+
+ return NULL;
+ }
+
+ egeom = (GEOSGeometry *) LWGEOM2GEOS(lwpoly_as_lwgeom(epoly));
+ lwpoly_free(epoly);
+
+ /* convert WKB to geometry */
+ RASTER_DEBUG(4, "Converting WKB to geometry");
+ lwgeom = lwgeom_from_wkb(wkb, wkb_len, LW_PARSER_CHECK_NONE);
+ geom = (GEOSGeometry *) LWGEOM2GEOS(lwgeom);
+ lwgeom_free(lwgeom);
+
+ result = GEOSRelatePattern(egeom, geom, "T**FF*FF*");
+ GEOSGeom_destroy(geom);
+ GEOSGeom_destroy(egeom);
+
+ if (result == 2) {
+ rterror("rt_raster_gdal_rasterize: Could not test if geometry is properly contained by extent for geometry within extent");
+
+ _rti_rasterize_arg_destroy(arg);
+
+ OGR_G_DestroyGeometry(src_geom);
+ if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
+ /* OGRCleanupAll(); */
+
+ return NULL;
+ }
+
+ /* geometry NOT properly contained by extent */
+ if (!result) {
+
+#if POSTGIS_GDAL_VERSION > 18
+
+ /* check alignment flag: grid_xw */
+ if (
+ (NULL == ul_xw && NULL == ul_yw) &&
+ (NULL != grid_xw && NULL != grid_xw) &&
+ FLT_NEQ(*grid_xw, extent.MinX)
+ ) {
+ // do nothing
+ RASTER_DEBUG(3, "Skipping extent adjustment on X-axis due to upcoming alignment");
+ }
+ else {
+ RASTER_DEBUG(3, "Adjusting extent for GDAL > 1.8 by half the scale on X-axis");
+ extent.MinX -= (_scale[0] / 2.);
+ extent.MaxX += (_scale[0] / 2.);
+ }
+
+ /* check alignment flag: grid_yw */
+ if (
+ (NULL == ul_xw && NULL == ul_yw) &&
+ (NULL != grid_xw && NULL != grid_xw) &&
+ FLT_NEQ(*grid_yw, extent.MaxY)
+ ) {
+ // do nothing
+ RASTER_DEBUG(3, "Skipping extent adjustment on Y-axis due to upcoming alignment");
+ }
+ else {
+ RASTER_DEBUG(3, "Adjusting extent for GDAL > 1.8 by half the scale on Y-axis");
+ extent.MinY -= (_scale[1] / 2.);
+ extent.MaxY += (_scale[1] / 2.);
+ }
+
+#else
+
+ /* check alignment flag: grid_xw */
+ if (
+ (NULL == ul_xw && NULL == ul_yw) &&
+ (NULL != grid_xw && NULL != grid_xw) &&
+ FLT_NEQ(*grid_xw, extent.MinX)
+ ) {
+ // do nothing
+ RASTER_DEBUG(3, "Skipping extent adjustment on X-axis due to upcoming alignment");
+ }
+ else {
+ RASTER_DEBUG(3, "Adjusting extent for GDAL <= 1.8 by the scale on X-axis");
+ extent.MinX -= _scale[0];
+ extent.MaxX += _scale[0];
+ }
+
+
+ /* check alignment flag: grid_yw */
+ if (
+ (NULL == ul_xw && NULL == ul_yw) &&
+ (NULL != grid_xw && NULL != grid_xw) &&
+ FLT_NEQ(*grid_yw, extent.MaxY)
+ ) {
+ // do nothing
+ RASTER_DEBUG(3, "Skipping extent adjustment on Y-axis due to upcoming alignment");
+ }
+ else {
+ RASTER_DEBUG(3, "Adjusting extent for GDAL <= 1.8 by the scale on Y-axis");
+ extent.MinY -= _scale[1];
+ extent.MaxY += _scale[1];
+ }
+
+#endif
+
+ }
+
+ RASTER_DEBUGF(3, "Adjusted extent: %f, %f, %f, %f",
+ extent.MinX, extent.MinY, extent.MaxX, extent.MaxY);
+
+ extent.UpperLeftX = extent.MinX;
+ extent.UpperLeftY = extent.MaxY;
+ }
+
+ /* reprocess extent if skewed */
+ if (
+ FLT_NEQ(_skew[0], 0) ||
+ FLT_NEQ(_skew[1], 0)
+ ) {
+ rt_raster skewedrast;
+
+ RASTER_DEBUG(3, "Computing skewed extent's envelope");
+
+ skewedrast = rt_raster_compute_skewed_raster(
+ extent,
+ _skew,
+ _scale,
+ 0.01
+ );
+ if (skewedrast == NULL) {
+ rterror("rt_raster_gdal_rasterize: Could not compute skewed raster");
+
+ _rti_rasterize_arg_destroy(arg);
+
+ OGR_G_DestroyGeometry(src_geom);
+ if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
+ /* OGRCleanupAll(); */
+
+ return NULL;
+ }
+
+ _dim[0] = skewedrast->width;
+ _dim[1] = skewedrast->height;
+
+ extent.UpperLeftX = skewedrast->ipX;
+ extent.UpperLeftY = skewedrast->ipY;
+
+ rt_raster_destroy(skewedrast);
+ }
+
+ /* raster dimensions */
+ if (!_dim[0])
+ _dim[0] = (int) fmax((fabs(extent.MaxX - extent.MinX) + (_scale[0] / 2.)) / _scale[0], 1);
+ if (!_dim[1])
+ _dim[1] = (int) fmax((fabs(extent.MaxY - extent.MinY) + (_scale[1] / 2.)) / _scale[1], 1);
+
+ /* temporary raster */
+ rast = rt_raster_new(_dim[0], _dim[1]);
+ if (rast == NULL) {
+ rterror("rt_raster_gdal_rasterize: Out of memory allocating temporary raster");
+
+ _rti_rasterize_arg_destroy(arg);
+
+ OGR_G_DestroyGeometry(src_geom);
+ if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
+ /* OGRCleanupAll(); */
+
+ return NULL;
+ }
+
+ /* set raster's spatial attributes */
+ rt_raster_set_offsets(rast, extent.UpperLeftX, extent.UpperLeftY);
+ rt_raster_set_scale(rast, _scale[0], -1 * _scale[1]);
+ rt_raster_set_skews(rast, _skew[0], _skew[1]);
+
+ rt_raster_get_geotransform_matrix(rast, _gt);
+ RASTER_DEBUGF(3, "Temp raster's geotransform: %f, %f, %f, %f, %f, %f",
+ _gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]);
+ RASTER_DEBUGF(3, "Temp raster's dimensions (width x height): %d x %d",
+ _dim[0], _dim[1]);
+
+ /* user-specified upper-left corner */
+ if (
+ NULL != ul_xw &&
+ NULL != ul_yw
+ ) {
+ ul_user = 1;
+
+ RASTER_DEBUGF(4, "Using user-specified upper-left corner: %f, %f", *ul_xw, *ul_yw);
+
+ /* set upper-left corner */
+ rt_raster_set_offsets(rast, *ul_xw, *ul_yw);
+ extent.UpperLeftX = *ul_xw;
+ extent.UpperLeftY = *ul_yw;
+ }
+ else if (
+ ((NULL != ul_xw) && (NULL == ul_yw)) ||
+ ((NULL == ul_xw) && (NULL != ul_yw))
+ ) {
+ rterror("rt_raster_gdal_rasterize: Both X and Y upper-left corner values must be provided");
+
+ rt_raster_destroy(rast);
+ _rti_rasterize_arg_destroy(arg);
+
+ OGR_G_DestroyGeometry(src_geom);
+ if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
+ /* OGRCleanupAll(); */
+
+ return NULL;
+ }
+
+ /* alignment only considered if upper-left corner not provided */
+ if (
+ !ul_user && (
+ (NULL != grid_xw) || (NULL != grid_yw)
+ )
+ ) {
+
+ if (
+ ((NULL != grid_xw) && (NULL == grid_yw)) ||
+ ((NULL == grid_xw) && (NULL != grid_yw))
+ ) {
+ rterror("rt_raster_gdal_rasterize: Both X and Y alignment values must be provided");
+
+ rt_raster_destroy(rast);
+ _rti_rasterize_arg_destroy(arg);
+
+ OGR_G_DestroyGeometry(src_geom);
+ if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
+ /* OGRCleanupAll(); */
+
+ return NULL;
+ }
+
+ RASTER_DEBUGF(4, "Aligning extent to user-specified grid: %f, %f", *grid_xw, *grid_yw);
+
+ do {
+ double _r[2] = {0};
+ double _w[2] = {0};
+
+ /* raster is already aligned */
+ if (FLT_EQ(*grid_xw, extent.UpperLeftX) && FLT_EQ(*grid_yw, extent.UpperLeftY)) {
+ RASTER_DEBUG(3, "Skipping raster alignment as it is already aligned to grid");
+ break;
+ }
+
+ extent.UpperLeftX = rast->ipX;
+ extent.UpperLeftY = rast->ipY;
+ rt_raster_set_offsets(rast, *grid_xw, *grid_yw);
+
+ /* process upper-left corner */
+ if (rt_raster_geopoint_to_cell(
+ rast,
+ extent.UpperLeftX, extent.UpperLeftY,
+ &(_r[0]), &(_r[1]),
+ NULL
+ ) != ES_NONE) {
+ rterror("rt_raster_gdal_rasterize: Could not compute raster pixel for spatial coordinates");
+
+ rt_raster_destroy(rast);
+ _rti_rasterize_arg_destroy(arg);
+
+ OGR_G_DestroyGeometry(src_geom);
+ if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
+ /* OGRCleanupAll(); */
+
+ return NULL;
+ }
+
+ if (rt_raster_cell_to_geopoint(
+ rast,
+ _r[0], _r[1],
+ &(_w[0]), &(_w[1]),
+ NULL
+ ) != ES_NONE) {
+ rterror("rt_raster_gdal_rasterize: Could not compute spatial coordinates for raster pixel");
+
+ rt_raster_destroy(rast);
+ _rti_rasterize_arg_destroy(arg);
+
+ OGR_G_DestroyGeometry(src_geom);
+ if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
+ /* OGRCleanupAll(); */
+
+ return NULL;
+ }
+
+ /* shift occurred */
+ if (FLT_NEQ(_w[0], extent.UpperLeftX)) {
+ if (NULL == width)
+ rast->width++;
+ else if (NULL == scale_x) {
+ double _c[2] = {0};
+
+ rt_raster_set_offsets(rast, extent.UpperLeftX, extent.UpperLeftY);
+
+ /* get upper-right corner */
+ if (rt_raster_cell_to_geopoint(
+ rast,
+ rast->width, 0,
+ &(_c[0]), &(_c[1]),
+ NULL
+ ) != ES_NONE) {
+ rterror("rt_raster_gdal_rasterize: Could not compute spatial coordinates for raster pixel");
+
+ rt_raster_destroy(rast);
+ _rti_rasterize_arg_destroy(arg);
+
+ OGR_G_DestroyGeometry(src_geom);
+ if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
+ /* OGRCleanupAll(); */
+
+ return NULL;
+ }
+
+ rast->scaleX = fabs((_c[0] - _w[0]) / ((double) rast->width));
+ }
+ }
+ if (FLT_NEQ(_w[1], extent.UpperLeftY)) {
+ if (NULL == height)
+ rast->height++;
+ else if (NULL == scale_y) {
+ double _c[2] = {0};
+
+ rt_raster_set_offsets(rast, extent.UpperLeftX, extent.UpperLeftY);
+
+ /* get upper-right corner */
+ if (rt_raster_cell_to_geopoint(
+ rast,
+ 0, rast->height,
+ &(_c[0]), &(_c[1]),
+ NULL
+ ) != ES_NONE) {
+ rterror("rt_raster_gdal_rasterize: Could not compute spatial coordinates for raster pixel");
+
+ rt_raster_destroy(rast);
+ _rti_rasterize_arg_destroy(arg);
+
+ OGR_G_DestroyGeometry(src_geom);
+ if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
+ /* OGRCleanupAll(); */
+
+ return NULL;
+ }
+
+ rast->scaleY = -1 * fabs((_c[1] - _w[1]) / ((double) rast->height));
+ }
+ }
+
+ rt_raster_set_offsets(rast, _w[0], _w[1]);
+ }
+ while (0);
+ }
+
+ /*
+ after this point, rt_envelope extent is no longer used
+ */
+
+ /* get key attributes from rast */
+ _dim[0] = rast->width;
+ _dim[1] = rast->height;
+ rt_raster_get_geotransform_matrix(rast, _gt);
+
+ /* scale-x is negative or scale-y is positive */
+ if ((
+ (NULL != scale_x) && (*scale_x < 0.)
+ ) || (
+ (NULL != scale_y) && (*scale_y > 0)
+ )) {
+ double _w[2] = {0};
+
+ /* negative scale-x */
+ if (
+ (NULL != scale_x) &&
+ (*scale_x < 0.)
+ ) {
+ RASTER_DEBUG(3, "Processing negative scale-x");
+
+ if (rt_raster_cell_to_geopoint(
+ rast,
+ _dim[0], 0,
+ &(_w[0]), &(_w[1]),
+ NULL
+ ) != ES_NONE) {
+ rterror("rt_raster_gdal_rasterize: Could not compute spatial coordinates for raster pixel");
+
+ rt_raster_destroy(rast);
+ _rti_rasterize_arg_destroy(arg);
+
+ OGR_G_DestroyGeometry(src_geom);
+ if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
+ /* OGRCleanupAll(); */
+
+ return NULL;
+ }
+
+ _gt[0] = _w[0];
+ _gt[1] = *scale_x;
+
+ /* check for skew */
+ if (NULL != skew_x && FLT_NEQ(*skew_x, 0))
+ _gt[2] = *skew_x;
+ }
+ /* positive scale-y */
+ if (
+ (NULL != scale_y) &&
+ (*scale_y > 0)
+ ) {
+ RASTER_DEBUG(3, "Processing positive scale-y");
+
+ if (rt_raster_cell_to_geopoint(
+ rast,
+ 0, _dim[1],
+ &(_w[0]), &(_w[1]),
+ NULL
+ ) != ES_NONE) {
+ rterror("rt_raster_gdal_rasterize: Could not compute spatial coordinates for raster pixel");
+
+ rt_raster_destroy(rast);
+ _rti_rasterize_arg_destroy(arg);
+
+ OGR_G_DestroyGeometry(src_geom);
+ if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
+ /* OGRCleanupAll(); */
+
+ return NULL;
+ }
+
+ _gt[3] = _w[1];
+ _gt[5] = *scale_y;
+
+ /* check for skew */
+ if (NULL != skew_y && FLT_NEQ(*skew_y, 0))
+ _gt[4] = *skew_y;
+ }
+ }
+
+ rt_raster_destroy(rast);
+ rast = NULL;
+
+ RASTER_DEBUGF(3, "Applied geotransform: %f, %f, %f, %f, %f, %f",
+ _gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]);
+ RASTER_DEBUGF(3, "Raster dimensions (width x height): %d x %d",
+ _dim[0], _dim[1]);
+
+ /* load GDAL mem */
+ if (!rt_util_gdal_driver_registered("MEM"))
+ GDALRegister_MEM();
+ _drv = GDALGetDriverByName("MEM");
+ if (NULL == _drv) {
+ rterror("rt_raster_gdal_rasterize: Could not load the MEM GDAL driver");
+
+ _rti_rasterize_arg_destroy(arg);
+
+ OGR_G_DestroyGeometry(src_geom);
+ if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
+ /* OGRCleanupAll(); */
+
+ return NULL;
+ }
+
+ _ds = GDALCreate(_drv, "", _dim[0], _dim[1], 0, GDT_Byte, NULL);
+ if (NULL == _ds) {
+ rterror("rt_raster_gdal_rasterize: Could not create a GDALDataset to rasterize the geometry into");
+
+ _rti_rasterize_arg_destroy(arg);
+
+ OGR_G_DestroyGeometry(src_geom);
+ if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
+ /* OGRCleanupAll(); */
+
+ return NULL;
+ }
+
+ /* set geotransform */
+ cplerr = GDALSetGeoTransform(_ds, _gt);
+ if (cplerr != CE_None) {
+ rterror("rt_raster_gdal_rasterize: Could not set geotransform on GDALDataset");
+
+ _rti_rasterize_arg_destroy(arg);
+
+ OGR_G_DestroyGeometry(src_geom);
+ if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
+ /* OGRCleanupAll(); */
+
+ GDALClose(_ds);
+
+ return NULL;
+ }
+
+ /* set SRS */
+ if (NULL != src_sr) {
+ char *_srs = NULL;
+ OSRExportToWkt(src_sr, &_srs);
+
+ cplerr = GDALSetProjection(_ds, _srs);
+ CPLFree(_srs);
+ if (cplerr != CE_None) {
+ rterror("rt_raster_gdal_rasterize: Could not set projection on GDALDataset");
+
+ _rti_rasterize_arg_destroy(arg);
+
+ OGR_G_DestroyGeometry(src_geom);
+ OSRDestroySpatialReference(src_sr);
+ /* OGRCleanupAll(); */
+
+ GDALClose(_ds);
+
+ return NULL;
+ }
+ }
+
+ /* set bands */
+ for (i = 0; i < arg->numbands; i++) {
+ err = 0;
+
+ do {
+ /* add band */
+ cplerr = GDALAddBand(_ds, rt_util_pixtype_to_gdal_datatype(arg->pixtype[i]), NULL);
+ if (cplerr != CE_None) {
+ rterror("rt_raster_gdal_rasterize: Could not add band to GDALDataset");
+ err = 1;
+ break;
+ }
+
+ _band = GDALGetRasterBand(_ds, i + 1);
+ if (NULL == _band) {
+ rterror("rt_raster_gdal_rasterize: Could not get band %d from GDALDataset", i + 1);
+ err = 1;
+ break;
+ }
+
+ /* nodata value */
+ if (arg->hasnodata[i]) {
+ RASTER_DEBUGF(4, "Setting NODATA value of band %d to %f", i, arg->nodata[i]);
+ cplerr = GDALSetRasterNoDataValue(_band, arg->nodata[i]);
+ if (cplerr != CE_None) {
+ rterror("rt_raster_gdal_rasterize: Could not set nodata value");
+ err = 1;
+ break;
+ }
+ RASTER_DEBUGF(4, "NODATA value set to %f", GDALGetRasterNoDataValue(_band, NULL));
+ }
+
+ /* initial value */
+ cplerr = GDALFillRaster(_band, arg->init[i], 0);
+ if (cplerr != CE_None) {
+ rterror("rt_raster_gdal_rasterize: Could not set initial value");
+ err = 1;
+ break;
+ }
+ }
+ while (0);
+
+ if (err) {
+ _rti_rasterize_arg_destroy(arg);
+
+ OGR_G_DestroyGeometry(src_geom);
+ if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
+ /* OGRCleanupAll(); */
+
+ GDALClose(_ds);
+
+ return NULL;
+ }
+ }
+
+ arg->bandlist = (int *) rtalloc(sizeof(int) * arg->numbands);
+ for (i = 0; i < arg->numbands; i++) arg->bandlist[i] = i + 1;
+
+
+ /* burn geometry */
+ cplerr = GDALRasterizeGeometries(
+ _ds,
+ arg->numbands, arg->bandlist,
+ 1, &src_geom,
+ NULL, NULL,
+ arg->value,
+ options,
+ NULL, NULL
+ );
+ if (cplerr != CE_None) {
+ rterror("rt_raster_gdal_rasterize: Could not rasterize geometry");
+
+ _rti_rasterize_arg_destroy(arg);
+
+ OGR_G_DestroyGeometry(src_geom);
+ if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
+ /* OGRCleanupAll(); */
+
+ GDALClose(_ds);
+
+ return NULL;
+ }
+
+ /* convert gdal dataset to raster */
+ GDALFlushCache(_ds);
+ RASTER_DEBUG(3, "Converting GDAL dataset to raster");
+ rast = rt_raster_from_gdal_dataset(_ds);
+
+ OGR_G_DestroyGeometry(src_geom);
+ if (src_sr != NULL) OSRDestroySpatialReference(src_sr);
+ /* OGRCleanupAll(); */
+
+ GDALClose(_ds);
+
+ if (NULL == rast) {
+ rterror("rt_raster_gdal_rasterize: Could not rasterize geometry");
+ return NULL;
+ }
+
+ /* width, height */
+ _width = rt_raster_get_width(rast);
+ _height = rt_raster_get_height(rast);
+
+ /* check each band for pixtype */
+ for (i = 0; i < arg->numbands; i++) {
+ uint8_t *data = NULL;
+ rt_band band = NULL;
+ rt_band oldband = NULL;
+
+ double val = 0;
+ int nodata = 0;
+ int hasnodata = 0;
+ double nodataval = 0;
+ int x = 0;
+ int y = 0;
+
+ oldband = rt_raster_get_band(rast, i);
+ if (oldband == NULL) {
+ rterror("rt_raster_gdal_rasterize: Could not get band %d of output raster", i);
+ _rti_rasterize_arg_destroy(arg);
+ rt_raster_destroy(rast);
+ return NULL;
+ }
+
+ /* band is of user-specified type */
+ if (rt_band_get_pixtype(oldband) == arg->pixtype[i])
+ continue;
+
+ /* hasnodata, nodataval */
+ hasnodata = rt_band_get_hasnodata_flag(oldband);
+ if (hasnodata)
+ rt_band_get_nodata(oldband, &nodataval);
+
+ /* allocate data */
+ data = rtalloc(rt_pixtype_size(arg->pixtype[i]) * _width * _height);
+ if (data == NULL) {
+ rterror("rt_raster_gdal_rasterize: Could not allocate memory for band data");
+ _rti_rasterize_arg_destroy(arg);
+ rt_raster_destroy(rast);
+ return NULL;
+ }
+ memset(data, 0, rt_pixtype_size(arg->pixtype[i]) * _width * _height);
+
+ /* create new band of correct type */
+ band = rt_band_new_inline(
+ _width, _height,
+ arg->pixtype[i],
+ hasnodata, nodataval,
+ data
+ );
+ if (band == NULL) {
+ rterror("rt_raster_gdal_rasterize: Could not create band");
+ rtdealloc(data);
+ _rti_rasterize_arg_destroy(arg);
+ rt_raster_destroy(rast);
+ return NULL;
+ }
+
+ /* give ownership of data to band */
+ rt_band_set_ownsdata_flag(band, 1);
+
+ /* copy pixel by pixel */
+ for (x = 0; x < _width; x++) {
+ for (y = 0; y < _height; y++) {
+ err = rt_band_get_pixel(oldband, x, y, &val, &nodata);
+ if (err != ES_NONE) {
+ rterror("rt_raster_gdal_rasterize: Could not get pixel value");
+ _rti_rasterize_arg_destroy(arg);
+ rt_raster_destroy(rast);
+ rt_band_destroy(band);
+ return NULL;
+ }
+
+ if (nodata)
+ val = nodataval;
+
+ err = rt_band_set_pixel(band, x, y, val, NULL);
+ if (err != ES_NONE) {
+ rterror("rt_raster_gdal_rasterize: Could not set pixel value");
+ _rti_rasterize_arg_destroy(arg);
+ rt_raster_destroy(rast);
+ rt_band_destroy(band);
+ return NULL;
+ }
+ }
+ }
+
+ /* replace band */
+ oldband = rt_raster_replace_band(rast, band, i);
+ if (oldband == NULL) {
+ rterror("rt_raster_gdal_rasterize: Could not replace band %d of output raster", i);
+ _rti_rasterize_arg_destroy(arg);
+ rt_raster_destroy(rast);
+ rt_band_destroy(band);
+ return NULL;
+ }
+
+ /* free oldband */
+ rt_band_destroy(oldband);
+ }
+
+ _rti_rasterize_arg_destroy(arg);
+
+ RASTER_DEBUG(3, "done");
+
+ return rast;
+}
+
+/******************************************************************************
+* rt_raster_from_two_rasters()
+******************************************************************************/
+
+/*
+ * Return raster of computed extent specified extenttype applied
+ * on two input rasters. The raster returned should be freed by
+ * the caller
+ *
+ * @param rast1 : the first raster
+ * @param rast2 : the second raster
+ * @param extenttype : type of extent for the output raster
+ * @param *rtnraster : raster of computed extent
+ * @param *offset : 4-element array indicating the X,Y offsets
+ * for each raster. 0,1 for rast1 X,Y. 2,3 for rast2 X,Y.
+ *
+ * @return ES_NONE if success, ES_ERROR if error
+ */
+rt_errorstate
+rt_raster_from_two_rasters(
+ rt_raster rast1, rt_raster rast2,
+ rt_extenttype extenttype,
+ rt_raster *rtnraster, double *offset
+) {
+ int i;
+
+ rt_raster _rast[2] = {rast1, rast2};
+ double _offset[2][4] = {{0.}};
+ uint16_t _dim[2][2] = {{0}};
+
+ rt_raster raster = NULL;
+ int aligned = 0;
+ int dim[2] = {0};
+ double gt[6] = {0.};
+
+ assert(NULL != rast1);
+ assert(NULL != rast2);
+ assert(NULL != rtnraster);
+
+ /* set rtnraster to NULL */
+ *rtnraster = NULL;
+
+ /* rasters must be aligned */
+ if (rt_raster_same_alignment(rast1, rast2, &aligned, NULL) != ES_NONE) {
+ rterror("rt_raster_from_two_rasters: Could not test for alignment on the two rasters");
+ return ES_ERROR;
+ }
+ if (!aligned) {
+ rterror("rt_raster_from_two_rasters: The two rasters provided do not have the same alignment");
+ return ES_ERROR;
+ }
+
+ /* dimensions */
+ _dim[0][0] = rast1->width;
+ _dim[0][1] = rast1->height;
+ _dim[1][0] = rast2->width;
+ _dim[1][1] = rast2->height;
+
+ /* get raster offsets */
+ if (rt_raster_geopoint_to_cell(
+ _rast[1],
+ _rast[0]->ipX, _rast[0]->ipY,
+ &(_offset[1][0]), &(_offset[1][1]),
+ NULL
+ ) != ES_NONE) {
+ rterror("rt_raster_from_two_rasters: Could not compute offsets of the second raster relative to the first raster");
+ return ES_ERROR;
+ }
+ _offset[1][0] = -1 * _offset[1][0];
+ _offset[1][1] = -1 * _offset[1][1];
+ _offset[1][2] = _offset[1][0] + _dim[1][0] - 1;
+ _offset[1][3] = _offset[1][1] + _dim[1][1] - 1;
+
+ i = -1;
+ switch (extenttype) {
+ case ET_FIRST:
+ i = 0;
+ _offset[0][0] = 0.;
+ _offset[0][1] = 0.;
+ case ET_LAST:
+ case ET_SECOND:
+ if (i < 0) {
+ i = 1;
+ _offset[0][0] = -1 * _offset[1][0];
+ _offset[0][1] = -1 * _offset[1][1];
+ _offset[1][0] = 0.;
+ _offset[1][1] = 0.;
+ }
+
+ dim[0] = _dim[i][0];
+ dim[1] = _dim[i][1];
+ raster = rt_raster_new(
+ dim[0],
+ dim[1]
+ );
+ if (raster == NULL) {
+ rterror("rt_raster_from_two_rasters: Could not create output raster");
+ return ES_ERROR;
+ }
+ rt_raster_set_srid(raster, _rast[i]->srid);
+ rt_raster_get_geotransform_matrix(_rast[i], gt);
+ rt_raster_set_geotransform_matrix(raster, gt);
+ break;
+ case ET_UNION: {
+ double off[4] = {0};
+
+ rt_raster_get_geotransform_matrix(_rast[0], gt);
+ RASTER_DEBUGF(4, "gt = (%f, %f, %f, %f, %f, %f)",
+ gt[0],
+ gt[1],
+ gt[2],
+ gt[3],
+ gt[4],
+ gt[5]
+ );
+
+ /* new raster upper left offset */
+ off[0] = 0;
+ if (_offset[1][0] < 0)
+ off[0] = _offset[1][0];
+ off[1] = 0;
+ if (_offset[1][1] < 0)
+ off[1] = _offset[1][1];
+
+ /* new raster lower right offset */
+ off[2] = _dim[0][0] - 1;
+ if ((int) _offset[1][2] >= _dim[0][0])
+ off[2] = _offset[1][2];
+ off[3] = _dim[0][1] - 1;
+ if ((int) _offset[1][3] >= _dim[0][1])
+ off[3] = _offset[1][3];
+
+ /* upper left corner */
+ if (rt_raster_cell_to_geopoint(
+ _rast[0],
+ off[0], off[1],
+ &(gt[0]), &(gt[3]),
+ NULL
+ ) != ES_NONE) {
+ rterror("rt_raster_from_two_rasters: Could not get spatial coordinates of upper-left pixel of output raster");
+ return ES_ERROR;
+ }
+
+ dim[0] = off[2] - off[0] + 1;
+ dim[1] = off[3] - off[1] + 1;
+ RASTER_DEBUGF(4, "off = (%f, %f, %f, %f)",
+ off[0],
+ off[1],
+ off[2],
+ off[3]
+ );
+ RASTER_DEBUGF(4, "dim = (%d, %d)", dim[0], dim[1]);
+
+ raster = rt_raster_new(
+ dim[0],
+ dim[1]
+ );
+ if (raster == NULL) {
+ rterror("rt_raster_from_two_rasters: Could not create output raster");
+ return ES_ERROR;
+ }
+ rt_raster_set_srid(raster, _rast[0]->srid);
+ rt_raster_set_geotransform_matrix(raster, gt);
+ RASTER_DEBUGF(4, "gt = (%f, %f, %f, %f, %f, %f)",
+ gt[0],
+ gt[1],
+ gt[2],
+ gt[3],
+ gt[4],
+ gt[5]
+ );
+
+ /* get offsets */
+ if (rt_raster_geopoint_to_cell(
+ _rast[0],
+ gt[0], gt[3],
+ &(_offset[0][0]), &(_offset[0][1]),
+ NULL
+ ) != ES_NONE) {
+ rterror("rt_raster_from_two_rasters: Could not get offsets of the FIRST raster relative to the output raster");
+ rt_raster_destroy(raster);
+ return ES_ERROR;
+ }
+ _offset[0][0] *= -1;
+ _offset[0][1] *= -1;
+
+ if (rt_raster_geopoint_to_cell(
+ _rast[1],
+ gt[0], gt[3],
+ &(_offset[1][0]), &(_offset[1][1]),
+ NULL
+ ) != ES_NONE) {
+ rterror("rt_raster_from_two_rasters: Could not get offsets of the SECOND raster relative to the output raster");
+ rt_raster_destroy(raster);
+ return ES_ERROR;
+ }
+ _offset[1][0] *= -1;
+ _offset[1][1] *= -1;
+ break;
+ }
+ case ET_INTERSECTION: {
+ double off[4] = {0};
+
+ /* no intersection */
+ if (
+ (_offset[1][2] < 0 || _offset[1][0] > (_dim[0][0] - 1)) ||
+ (_offset[1][3] < 0 || _offset[1][1] > (_dim[0][1] - 1))
+ ) {
+ RASTER_DEBUG(3, "The two rasters provided have no intersection. Returning no band raster");
+
+ raster = rt_raster_new(0, 0);
+ if (raster == NULL) {
+ rterror("rt_raster_from_two_rasters: Could not create output raster");
+ return ES_ERROR;
+ }
+ rt_raster_set_srid(raster, _rast[0]->srid);
+ rt_raster_set_scale(raster, 0, 0);
+
+ /* set offsets if provided */
+ if (NULL != offset) {
+ for (i = 0; i < 4; i++)
+ offset[i] = _offset[i / 2][i % 2];
+ }
+
+ *rtnraster = raster;
+ return ES_NONE;
+ }
+
+ if (_offset[1][0] > 0)
+ off[0] = _offset[1][0];
+ if (_offset[1][1] > 0)
+ off[1] = _offset[1][1];
+
+ off[2] = _dim[0][0] - 1;
+ if (_offset[1][2] < _dim[0][0])
+ off[2] = _offset[1][2];
+ off[3] = _dim[0][1] - 1;
+ if (_offset[1][3] < _dim[0][1])
+ off[3] = _offset[1][3];
+
+ dim[0] = off[2] - off[0] + 1;
+ dim[1] = off[3] - off[1] + 1;
+ raster = rt_raster_new(
+ dim[0],
+ dim[1]
+ );
+ if (raster == NULL) {
+ rterror("rt_raster_from_two_rasters: Could not create output raster");
+ return ES_ERROR;
+ }
+ rt_raster_set_srid(raster, _rast[0]->srid);
+
+ /* get upper-left corner */
+ rt_raster_get_geotransform_matrix(_rast[0], gt);
+ if (rt_raster_cell_to_geopoint(
+ _rast[0],
+ off[0], off[1],
+ &(gt[0]), &(gt[3]),
+ gt
+ ) != ES_NONE) {
+ rterror("rt_raster_from_two_rasters: Could not get spatial coordinates of upper-left pixel of output raster");
+ rt_raster_destroy(raster);
+ return ES_ERROR;
+ }
+
+ rt_raster_set_geotransform_matrix(raster, gt);
+
+ /* get offsets */
+ if (rt_raster_geopoint_to_cell(
+ _rast[0],
+ gt[0], gt[3],
+ &(_offset[0][0]), &(_offset[0][1]),
+ NULL
+ ) != ES_NONE) {
+ rterror("rt_raster_from_two_rasters: Could not get pixel coordinates to compute the offsets of the FIRST raster relative to the output raster");
+ rt_raster_destroy(raster);
+ return ES_ERROR;
+ }
+ _offset[0][0] *= -1;
+ _offset[0][1] *= -1;
+
+ if (rt_raster_geopoint_to_cell(
+ _rast[1],
+ gt[0], gt[3],
+ &(_offset[1][0]), &(_offset[1][1]),
+ NULL
+ ) != ES_NONE) {
+ rterror("rt_raster_from_two_rasters: Could not get pixel coordinates to compute the offsets of the SECOND raster relative to the output raster");
+ rt_raster_destroy(raster);
+ return ES_ERROR;
+ }
+ _offset[1][0] *= -1;
+ _offset[1][1] *= -1;
+ break;
+ }
+ case ET_CUSTOM:
+ rterror("rt_raster_from_two_rasters: Extent type ET_CUSTOM is not supported by this function");
+ break;
+ }
+
+ /* set offsets if provided */
+ if (NULL != offset) {
+ for (i = 0; i < 4; i++)
+ offset[i] = _offset[i / 2][i % 2];
+ }
+
+ *rtnraster = raster;
+ return ES_NONE;
+}
--- /dev/null
+/*
+ * $Id$
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "librtcore.h"
+#include "librtcore_internal.h"
+#include "rt_serialize.h"
+
+/******************************************************************************
+* Debug and Testing Utilities
+******************************************************************************/
+
+#if POSTGIS_DEBUG_LEVEL > 2
+
+char*
+d_binary_to_hex(const uint8_t * const raw, uint32_t size, uint32_t *hexsize) {
+ char* hex = NULL;
+ uint32_t i = 0;
+
+
+ assert(NULL != raw);
+ assert(NULL != hexsize);
+
+
+ *hexsize = size * 2; /* hex is 2 times bytes */
+ hex = (char*) rtalloc((*hexsize) + 1);
+ if (!hex) {
+ rterror("d_binary_to_hex: Out of memory hexifying raw binary");
+ return NULL;
+ }
+ hex[*hexsize] = '\0'; /* Null-terminate */
+
+ for (i = 0; i < size; ++i) {
+ deparse_hex(raw[i], &(hex[2 * i]));
+ }
+
+ assert(NULL != hex);
+ assert(0 == strlen(hex) % 2);
+ return hex;
+}
+
+void
+d_print_binary_hex(const char* msg, const uint8_t * const raw, uint32_t size) {
+ char* hex = NULL;
+ uint32_t hexsize = 0;
+
+
+ assert(NULL != msg);
+ assert(NULL != raw);
+
+
+ hex = d_binary_to_hex(raw, size, &hexsize);
+ if (NULL != hex) {
+ rtinfo("%s\t%s", msg, hex);
+ rtdealloc(hex);
+ }
+}
+
+size_t
+d_binptr_to_pos(const uint8_t * const ptr, const uint8_t * const end, size_t size) {
+ assert(NULL != ptr && NULL != end);
+
+ return (size - (end - ptr));
+}
+
+#endif /* if POSTGIS_DEBUG_LEVEL > 2 */
+
+
+#ifdef OPTIMIZE_SPACE
+
+/*
+ * Set given number of bits of the given byte,
+ * starting from given bitOffset (from the first)
+ * to the given value.
+ *
+ * Examples:
+ * char ch;
+ * ch=0; setBits(&ch, 1, 1, 0) -> ch==8
+ * ch=0; setBits(&ch, 3, 2, 1) -> ch==96 (0x60)
+ *
+ * Note that number of bits set must be <= 8-bitOffset
+ *
+ */
+void
+setBits(char* ch, double val, int bits, int bitOffset) {
+ char mask = 0xFF >> (8 - bits);
+ char ival = val;
+
+
+ assert(ch != NULL);
+ assert(8 - bitOffset >= bits);
+
+ RASTER_DEBUGF(4, "ival:%d bits:%d mask:%hhx bitoffset:%d\n",
+ ival, bits, mask, bitOffset);
+
+ /* clear all but significant bits from ival */
+ ival &= mask;
+#if POSTGIS_RASTER_WARN_ON_TRUNCATION > 0
+ if (ival != val) {
+ rtwarn("Pixel value for %d-bits band got truncated"
+ " from %g to %hhu", bits, val, ival);
+ }
+#endif /* POSTGIS_RASTER_WARN_ON_TRUNCATION */
+
+ RASTER_DEBUGF(4, " cleared ival:%hhx\n", ival);
+
+
+ /* Shift ival so the significant bits start at
+ * the first bit */
+ ival <<= (8 - bitOffset - bits);
+
+ RASTER_DEBUGF(4, " ival shifted:%hhx\n", ival);
+ RASTER_DEBUGF(4, " ch:%hhx\n", *ch);
+
+ /* clear first bits of target */
+ *ch &= ~(mask << (8 - bits - bitOffset));
+
+ RASTER_DEBUGF(4, " ch cleared:%hhx\n", *ch);
+
+ /* Set the first bit of target */
+ *ch |= ival;
+
+ RASTER_DEBUGF(4, " ch ored:%hhx\n", *ch);
+
+}
+#endif /* OPTIMIZE_SPACE */
+
+void
+swap_char(uint8_t *a, uint8_t *b) {
+ uint8_t c = 0;
+
+ assert(NULL != a && NULL != b);
+
+ c = *a;
+ *a = *b;
+ *b = c;
+}
+
+void
+flip_endian_16(uint8_t *d) {
+ assert(NULL != d);
+
+ swap_char(d, d + 1);
+}
+
+void
+flip_endian_32(uint8_t *d) {
+ assert(NULL != d);
+
+ swap_char(d, d + 3);
+ swap_char(d + 1, d + 2);
+}
+
+void
+flip_endian_64(uint8_t *d) {
+ assert(NULL != d);
+
+ swap_char(d + 7, d);
+ swap_char(d + 6, d + 1);
+ swap_char(d + 5, d + 2);
+ swap_char(d + 4, d + 3);
+}
+
+uint8_t
+isMachineLittleEndian(void) {
+ static int endian_check_int = 1; /* dont modify this!!! */
+ /* 0=big endian|xdr -- 1=little endian|ndr */
+ return *((uint8_t *) & endian_check_int);
+}
+
+uint8_t
+read_uint8(const uint8_t** from) {
+ assert(NULL != from);
+
+ return *(*from)++;
+}
+
+/* unused up to now
+void
+write_uint8(uint8_t** from, uint8_t v) {
+ assert(NULL != from);
+
+ *(*from)++ = v;
+}
+*/
+
+int8_t
+read_int8(const uint8_t** from) {
+ assert(NULL != from);
+
+ return (int8_t) read_uint8(from);
+}
+
+/* unused up to now
+void
+write_int8(uint8_t** from, int8_t v) {
+ assert(NULL != from);
+
+ *(*from)++ = v;
+}
+*/
+
+uint16_t
+read_uint16(const uint8_t** from, uint8_t littleEndian) {
+ uint16_t ret = 0;
+
+ assert(NULL != from);
+
+ if (littleEndian) {
+ ret = (*from)[0] |
+ (*from)[1] << 8;
+ } else {
+ /* big endian */
+ ret = (*from)[0] << 8 |
+ (*from)[1];
+ }
+ *from += 2;
+ return ret;
+}
+
+void
+write_uint16(uint8_t** to, uint8_t littleEndian, uint16_t v) {
+ assert(NULL != to);
+
+ if (littleEndian) {
+ (*to)[0] = v & 0x00FF;
+ (*to)[1] = v >> 8;
+ } else {
+ (*to)[1] = v & 0x00FF;
+ (*to)[0] = v >> 8;
+ }
+ *to += 2;
+}
+
+int16_t
+read_int16(const uint8_t** from, uint8_t littleEndian) {
+ assert(NULL != from);
+
+ return read_uint16(from, littleEndian);
+}
+
+/* unused up to now
+void
+write_int16(uint8_t** to, uint8_t littleEndian, int16_t v) {
+ assert(NULL != to);
+
+ if ( littleEndian )
+ {
+ (*to)[0] = v & 0x00FF;
+ (*to)[1] = v >> 8;
+ }
+ else
+ {
+ (*to)[1] = v & 0x00FF;
+ (*to)[0] = v >> 8;
+ }
+ *to += 2;
+}
+*/
+
+uint32_t
+read_uint32(const uint8_t** from, uint8_t littleEndian) {
+ uint32_t ret = 0;
+
+ assert(NULL != from);
+
+ if (littleEndian) {
+ ret = (uint32_t) ((*from)[0] & 0xff) |
+ (uint32_t) ((*from)[1] & 0xff) << 8 |
+ (uint32_t) ((*from)[2] & 0xff) << 16 |
+ (uint32_t) ((*from)[3] & 0xff) << 24;
+ } else {
+ /* big endian */
+ ret = (uint32_t) ((*from)[3] & 0xff) |
+ (uint32_t) ((*from)[2] & 0xff) << 8 |
+ (uint32_t) ((*from)[1] & 0xff) << 16 |
+ (uint32_t) ((*from)[0] & 0xff) << 24;
+ }
+
+ *from += 4;
+ return ret;
+}
+
+/* unused up to now
+void
+write_uint32(uint8_t** to, uint8_t littleEndian, uint32_t v) {
+ assert(NULL != to);
+
+ if ( littleEndian )
+ {
+ (*to)[0] = v & 0x000000FF;
+ (*to)[1] = ( v & 0x0000FF00 ) >> 8;
+ (*to)[2] = ( v & 0x00FF0000 ) >> 16;
+ (*to)[3] = ( v & 0xFF000000 ) >> 24;
+ }
+ else
+ {
+ (*to)[3] = v & 0x000000FF;
+ (*to)[2] = ( v & 0x0000FF00 ) >> 8;
+ (*to)[1] = ( v & 0x00FF0000 ) >> 16;
+ (*to)[0] = ( v & 0xFF000000 ) >> 24;
+ }
+ *to += 4;
+}
+*/
+
+int32_t
+read_int32(const uint8_t** from, uint8_t littleEndian) {
+ assert(NULL != from);
+
+ return read_uint32(from, littleEndian);
+}
+
+/* unused up to now
+void
+write_int32(uint8_t** to, uint8_t littleEndian, int32_t v) {
+ assert(NULL != to);
+
+ if ( littleEndian )
+ {
+ (*to)[0] = v & 0x000000FF;
+ (*to)[1] = ( v & 0x0000FF00 ) >> 8;
+ (*to)[2] = ( v & 0x00FF0000 ) >> 16;
+ (*to)[3] = ( v & 0xFF000000 ) >> 24;
+ }
+ else
+ {
+ (*to)[3] = v & 0x000000FF;
+ (*to)[2] = ( v & 0x0000FF00 ) >> 8;
+ (*to)[1] = ( v & 0x00FF0000 ) >> 16;
+ (*to)[0] = ( v & 0xFF000000 ) >> 24;
+ }
+ *to += 4;
+}
+*/
+
+float
+read_float32(const uint8_t** from, uint8_t littleEndian) {
+
+ union {
+ float f;
+ uint32_t i;
+ } ret;
+
+ ret.i = read_uint32(from, littleEndian);
+
+ return ret.f;
+}
+
+/* unused up to now
+void
+write_float32(uint8_t** from, uint8_t littleEndian, float f) {
+ union {
+ float f;
+ uint32_t i;
+ } u;
+
+ u.f = f;
+ write_uint32(from, littleEndian, u.i);
+}
+*/
+
+double
+read_float64(const uint8_t** from, uint8_t littleEndian) {
+
+ union {
+ double d;
+ uint64_t i;
+ } ret;
+
+ assert(NULL != from);
+
+ if (littleEndian) {
+ ret.i = (uint64_t) ((*from)[0] & 0xff) |
+ (uint64_t) ((*from)[1] & 0xff) << 8 |
+ (uint64_t) ((*from)[2] & 0xff) << 16 |
+ (uint64_t) ((*from)[3] & 0xff) << 24 |
+ (uint64_t) ((*from)[4] & 0xff) << 32 |
+ (uint64_t) ((*from)[5] & 0xff) << 40 |
+ (uint64_t) ((*from)[6] & 0xff) << 48 |
+ (uint64_t) ((*from)[7] & 0xff) << 56;
+ } else {
+ /* big endian */
+ ret.i = (uint64_t) ((*from)[7] & 0xff) |
+ (uint64_t) ((*from)[6] & 0xff) << 8 |
+ (uint64_t) ((*from)[5] & 0xff) << 16 |
+ (uint64_t) ((*from)[4] & 0xff) << 24 |
+ (uint64_t) ((*from)[3] & 0xff) << 32 |
+ (uint64_t) ((*from)[2] & 0xff) << 40 |
+ (uint64_t) ((*from)[1] & 0xff) << 48 |
+ (uint64_t) ((*from)[0] & 0xff) << 56;
+ }
+
+ *from += 8;
+ return ret.d;
+}
+
+/* unused up to now
+void
+write_float64(uint8_t** to, uint8_t littleEndian, double v) {
+ union {
+ double d;
+ uint64_t i;
+ } u;
+
+ assert(NULL != to);
+
+ u.d = v;
+
+ if ( littleEndian )
+ {
+ (*to)[0] = u.i & 0x00000000000000FFULL;
+ (*to)[1] = ( u.i & 0x000000000000FF00ULL ) >> 8;
+ (*to)[2] = ( u.i & 0x0000000000FF0000ULL ) >> 16;
+ (*to)[3] = ( u.i & 0x00000000FF000000ULL ) >> 24;
+ (*to)[4] = ( u.i & 0x000000FF00000000ULL ) >> 32;
+ (*to)[5] = ( u.i & 0x0000FF0000000000ULL ) >> 40;
+ (*to)[6] = ( u.i & 0x00FF000000000000ULL ) >> 48;
+ (*to)[7] = ( u.i & 0xFF00000000000000ULL ) >> 56;
+ }
+ else
+ {
+ (*to)[7] = u.i & 0x00000000000000FFULL;
+ (*to)[6] = ( u.i & 0x000000000000FF00ULL ) >> 8;
+ (*to)[5] = ( u.i & 0x0000000000FF0000ULL ) >> 16;
+ (*to)[4] = ( u.i & 0x00000000FF000000ULL ) >> 24;
+ (*to)[3] = ( u.i & 0x000000FF00000000ULL ) >> 32;
+ (*to)[2] = ( u.i & 0x0000FF0000000000ULL ) >> 40;
+ (*to)[1] = ( u.i & 0x00FF000000000000ULL ) >> 48;
+ (*to)[0] = ( u.i & 0xFF00000000000000ULL ) >> 56;
+ }
+ *to += 8;
+}
+*/
+
+static uint32_t
+rt_raster_serialized_size(rt_raster raster) {
+ uint32_t size = sizeof (struct rt_raster_serialized_t);
+ uint16_t i = 0;
+
+ assert(NULL != raster);
+
+ RASTER_DEBUGF(3, "Serialized size with just header:%d - now adding size of %d bands",
+ size, raster->numBands);
+
+ for (i = 0; i < raster->numBands; ++i) {
+ rt_band band = raster->bands[i];
+ rt_pixtype pixtype = band->pixtype;
+ int pixbytes = rt_pixtype_size(pixtype);
+
+ if (pixbytes < 1) {
+ rterror("rt_raster_serialized_size: Corrupted band: unknown pixtype");
+ return 0;
+ }
+
+ /* Add space for band type, hasnodata flag and data padding */
+ size += pixbytes;
+
+ /* Add space for nodata value */
+ size += pixbytes;
+
+ if (band->offline) {
+ /* Add space for band number */
+ size += 1;
+
+ /* Add space for null-terminated path */
+ size += strlen(band->data.offline.path) + 1;
+ }
+ else {
+ /* Add space for raster band data */
+ size += pixbytes * raster->width * raster->height;
+ }
+
+ RASTER_DEBUGF(3, "Size before alignment is %d", size);
+
+ /* Align size to 8-bytes boundary (trailing padding) */
+ /* XXX jorgearevalo: bug here. If the size is actually 8-bytes aligned,
+ this line will add 8 bytes trailing padding, and it's not necessary */
+ /*size += 8 - (size % 8);*/
+ if (size % 8)
+ size += 8 - (size % 8);
+
+ RASTER_DEBUGF(3, "Size after alignment is %d", size);
+ }
+
+ return size;
+}
+
+/**
+ * Return this raster in serialized form.
+ * Memory (band data included) is copied from rt_raster.
+ *
+ * Serialized form is documented in doc/RFC1-SerializedFormat.
+ */
+void*
+rt_raster_serialize(rt_raster raster) {
+ uint32_t size = 0;
+ uint8_t* ret = NULL;
+ uint8_t* ptr = NULL;
+ uint16_t i = 0;
+
+ assert(NULL != raster);
+
+ size = rt_raster_serialized_size(raster);
+ ret = (uint8_t*) rtalloc(size);
+ if (!ret) {
+ rterror("rt_raster_serialize: Out of memory allocating %d bytes for serializing a raster", size);
+ return NULL;
+ }
+ memset(ret, '-', size);
+ ptr = ret;
+
+ RASTER_DEBUGF(3, "sizeof(struct rt_raster_serialized_t):%u",
+ sizeof (struct rt_raster_serialized_t));
+ RASTER_DEBUGF(3, "sizeof(struct rt_raster_t):%u",
+ sizeof (struct rt_raster_t));
+ RASTER_DEBUGF(3, "serialized size:%lu", (long unsigned) size);
+
+ /* Set size */
+ /* NOTE: Value of rt_raster.size may be updated in
+ * returned object, for instance, by rt_pg layer to
+ * store value calculated by SET_VARSIZE.
+ */
+ raster->size = size;
+
+ /* Set version */
+ raster->version = 0;
+
+ /* Copy header */
+ memcpy(ptr, raster, sizeof (struct rt_raster_serialized_t));
+
+ RASTER_DEBUG(3, "Start hex dump of raster being serialized using 0x2D to mark non-written bytes");
+
+#if POSTGIS_DEBUG_LEVEL > 2
+ uint8_t* dbg_ptr = ptr;
+ d_print_binary_hex("HEADER", dbg_ptr, size);
+#endif
+
+ ptr += sizeof (struct rt_raster_serialized_t);
+
+ /* Serialize bands now */
+ for (i = 0; i < raster->numBands; ++i) {
+ rt_band band = raster->bands[i];
+ assert(NULL != band);
+
+ rt_pixtype pixtype = band->pixtype;
+ int pixbytes = rt_pixtype_size(pixtype);
+ if (pixbytes < 1) {
+ rterror("rt_raster_serialize: Corrupted band: unknown pixtype");
+ rtdealloc(ret);
+ return NULL;
+ }
+
+ /* Add band type */
+ *ptr = band->pixtype;
+ if (band->offline) {
+ *ptr |= BANDTYPE_FLAG_OFFDB;
+ }
+ if (band->hasnodata) {
+ *ptr |= BANDTYPE_FLAG_HASNODATA;
+ }
+
+ if (band->isnodata) {
+ *ptr |= BANDTYPE_FLAG_ISNODATA;
+ }
+
+#if POSTGIS_DEBUG_LEVEL > 2
+ d_print_binary_hex("PIXTYPE", dbg_ptr, size);
+#endif
+
+ ptr += 1;
+
+ /* Add padding (if needed) */
+ if (pixbytes > 1) {
+ memset(ptr, '\0', pixbytes - 1);
+ ptr += pixbytes - 1;
+ }
+
+#if POSTGIS_DEBUG_LEVEL > 2
+ d_print_binary_hex("PADDING", dbg_ptr, size);
+#endif
+
+ /* Consistency checking (ptr is pixbytes-aligned) */
+ assert(!((ptr - ret) % pixbytes));
+
+ /* Add nodata value */
+ switch (pixtype) {
+ case PT_1BB:
+ case PT_2BUI:
+ case PT_4BUI:
+ case PT_8BUI: {
+ uint8_t v = band->nodataval;
+ *ptr = v;
+ ptr += 1;
+ break;
+ }
+ case PT_8BSI: {
+ int8_t v = band->nodataval;
+ *ptr = v;
+ ptr += 1;
+ break;
+ }
+ case PT_16BSI:
+ case PT_16BUI: {
+ uint16_t v = band->nodataval;
+ memcpy(ptr, &v, 2);
+ ptr += 2;
+ break;
+ }
+ case PT_32BSI:
+ case PT_32BUI: {
+ uint32_t v = band->nodataval;
+ memcpy(ptr, &v, 4);
+ ptr += 4;
+ break;
+ }
+ case PT_32BF: {
+ float v = band->nodataval;
+ memcpy(ptr, &v, 4);
+ ptr += 4;
+ break;
+ }
+ case PT_64BF: {
+ memcpy(ptr, &band->nodataval, 8);
+ ptr += 8;
+ break;
+ }
+ default:
+ rterror("rt_raster_serialize: Fatal error caused by unknown pixel type. Aborting.");
+ rtdealloc(ret);
+ return NULL;
+ }
+
+ /* Consistency checking (ptr is pixbytes-aligned) */
+ assert(!((ptr - ret) % pixbytes));
+
+#if POSTGIS_DEBUG_LEVEL > 2
+ d_print_binary_hex("nodata", dbg_ptr, size);
+#endif
+
+ if (band->offline) {
+ /* Write band number */
+ *ptr = band->data.offline.bandNum;
+ ptr += 1;
+
+ /* Write path */
+ strcpy((char*) ptr, band->data.offline.path);
+ ptr += strlen(band->data.offline.path) + 1;
+ }
+ else {
+ /* Write data */
+ uint32_t datasize = raster->width * raster->height * pixbytes;
+ memcpy(ptr, band->data.mem, datasize);
+ ptr += datasize;
+ }
+
+#if POSTGIS_DEBUG_LEVEL > 2
+ d_print_binary_hex("BAND", dbg_ptr, size);
+#endif
+
+ /* Pad up to 8-bytes boundary */
+ while ((uintptr_t) ptr % 8) {
+ *ptr = 0;
+ ++ptr;
+
+ RASTER_DEBUGF(3, "PAD at %d", (uintptr_t) ptr % 8);
+ }
+
+ /* Consistency checking (ptr is pixbytes-aligned) */
+ assert(!((ptr - ret) % pixbytes));
+ } /* for-loop over bands */
+
+#if POSTGIS_DEBUG_LEVEL > 2
+ d_print_binary_hex("SERIALIZED RASTER", dbg_ptr, size);
+#endif
+ return ret;
+}
+
+/**
+ * Return a raster from a serialized form.
+ *
+ * Serialized form is documented in doc/RFC1-SerializedFormat.
+ *
+ * NOTE: the raster will contain pointer to the serialized
+ * form (including band data), which must be kept alive.
+ */
+rt_raster
+rt_raster_deserialize(void* serialized, int header_only) {
+ rt_raster rast = NULL;
+ const uint8_t *ptr = NULL;
+ const uint8_t *beg = NULL;
+ uint16_t i = 0;
+ uint16_t j = 0;
+ uint8_t littleEndian = isMachineLittleEndian();
+
+ assert(NULL != serialized);
+
+ RASTER_DEBUG(2, "rt_raster_deserialize: Entering...");
+
+ /* NOTE: Value of rt_raster.size may be different
+ * than actual size of raster data being read.
+ * See note on SET_VARSIZE in rt_raster_serialize function above.
+ */
+
+ /* Allocate memory for deserialized raster header */
+ RASTER_DEBUG(3, "rt_raster_deserialize: Allocating memory for deserialized raster header");
+ rast = (rt_raster) rtalloc(sizeof (struct rt_raster_t));
+ if (!rast) {
+ rterror("rt_raster_deserialize: Out of memory allocating raster for deserialization");
+ return NULL;
+ }
+
+ /* Deserialize raster header */
+ RASTER_DEBUG(3, "rt_raster_deserialize: Deserialize raster header");
+ memcpy(rast, serialized, sizeof (struct rt_raster_serialized_t));
+
+ if (0 == rast->numBands || header_only) {
+ rast->bands = 0;
+ return rast;
+ }
+
+ beg = (const uint8_t*) serialized;
+
+ /* Allocate registry of raster bands */
+ RASTER_DEBUG(3, "rt_raster_deserialize: Allocating memory for bands");
+ rast->bands = rtalloc(rast->numBands * sizeof (rt_band));
+ if (rast->bands == NULL) {
+ rterror("rt_raster_deserialize: Out of memory allocating bands");
+ rtdealloc(rast);
+ return NULL;
+ }
+
+ RASTER_DEBUGF(3, "rt_raster_deserialize: %d bands", rast->numBands);
+
+ /* Move to the beginning of first band */
+ ptr = beg;
+ ptr += sizeof (struct rt_raster_serialized_t);
+
+ /* Deserialize bands now */
+ for (i = 0; i < rast->numBands; ++i) {
+ rt_band band = NULL;
+ uint8_t type = 0;
+ int pixbytes = 0;
+
+ band = rtalloc(sizeof(struct rt_band_t));
+ if (!band) {
+ rterror("rt_raster_deserialize: Out of memory allocating rt_band during deserialization");
+ for (j = 0; j < i; j++) rt_band_destroy(rast->bands[j]);
+ rt_raster_destroy(rast);
+ return NULL;
+ }
+
+ rast->bands[i] = band;
+
+ type = *ptr;
+ ptr++;
+ band->pixtype = type & BANDTYPE_PIXTYPE_MASK;
+
+ RASTER_DEBUGF(3, "rt_raster_deserialize: band %d with pixel type %s", i, rt_pixtype_name(band->pixtype));
+
+ band->offline = BANDTYPE_IS_OFFDB(type) ? 1 : 0;
+ band->hasnodata = BANDTYPE_HAS_NODATA(type) ? 1 : 0;
+ band->isnodata = band->hasnodata ? (BANDTYPE_IS_NODATA(type) ? 1 : 0) : 0;
+ band->width = rast->width;
+ band->height = rast->height;
+ band->ownsdata = 0; /* we do NOT own this data!!! */
+ band->raster = rast;
+
+ /* Advance by data padding */
+ pixbytes = rt_pixtype_size(band->pixtype);
+ ptr += pixbytes - 1;
+
+ /* Read nodata value */
+ switch (band->pixtype) {
+ case PT_1BB: {
+ band->nodataval = ((int) read_uint8(&ptr)) & 0x01;
+ break;
+ }
+ case PT_2BUI: {
+ band->nodataval = ((int) read_uint8(&ptr)) & 0x03;
+ break;
+ }
+ case PT_4BUI: {
+ band->nodataval = ((int) read_uint8(&ptr)) & 0x0F;
+ break;
+ }
+ case PT_8BSI: {
+ band->nodataval = read_int8(&ptr);
+ break;
+ }
+ case PT_8BUI: {
+ band->nodataval = read_uint8(&ptr);
+ break;
+ }
+ case PT_16BSI: {
+ band->nodataval = read_int16(&ptr, littleEndian);
+ break;
+ }
+ case PT_16BUI: {
+ band->nodataval = read_uint16(&ptr, littleEndian);
+ break;
+ }
+ case PT_32BSI: {
+ band->nodataval = read_int32(&ptr, littleEndian);
+ break;
+ }
+ case PT_32BUI: {
+ band->nodataval = read_uint32(&ptr, littleEndian);
+ break;
+ }
+ case PT_32BF: {
+ band->nodataval = read_float32(&ptr, littleEndian);
+ break;
+ }
+ case PT_64BF: {
+ band->nodataval = read_float64(&ptr, littleEndian);
+ break;
+ }
+ default: {
+ rterror("rt_raster_deserialize: Unknown pixeltype %d", band->pixtype);
+ for (j = 0; j <= i; j++) rt_band_destroy(rast->bands[j]);
+ rt_raster_destroy(rast);
+ return NULL;
+ }
+ }
+
+ RASTER_DEBUGF(3, "rt_raster_deserialize: has nodata flag %d", band->hasnodata);
+ RASTER_DEBUGF(3, "rt_raster_deserialize: nodata value %g", band->nodataval);
+
+ /* Consistency checking (ptr is pixbytes-aligned) */
+ assert(!((ptr - beg) % pixbytes));
+
+ if (band->offline) {
+ int pathlen = 0;
+
+ /* Read band number */
+ band->data.offline.bandNum = *ptr;
+ ptr += 1;
+
+ /* Register path */
+ pathlen = strlen((char*) ptr);
+ band->data.offline.path = rtalloc(sizeof(char) * (pathlen + 1));
+ if (band->data.offline.path == NULL) {
+ rterror("rt_raster_deserialize: Could not allocate momory for offline band path");
+ for (j = 0; j <= i; j++) rt_band_destroy(rast->bands[j]);
+ rt_raster_destroy(rast);
+ return NULL;
+ }
+
+ memcpy(band->data.offline.path, ptr, pathlen);
+ band->data.offline.path[pathlen] = '\0';
+ ptr += pathlen + 1;
+
+ band->data.offline.mem = NULL;
+ }
+ else {
+ /* Register data */
+ const uint32_t datasize = rast->width * rast->height * pixbytes;
+ band->data.mem = (uint8_t*) ptr;
+ ptr += datasize;
+ }
+
+ /* Skip bytes of padding up to 8-bytes boundary */
+#if POSTGIS_DEBUG_LEVEL > 0
+ const uint8_t *padbeg = ptr;
+#endif
+ while (0 != ((ptr - beg) % 8)) {
+ ++ptr;
+ }
+
+ RASTER_DEBUGF(3, "rt_raster_deserialize: skip %d bytes of 8-bytes boundary padding", ptr - padbeg);
+
+ /* Consistency checking (ptr is pixbytes-aligned) */
+ assert(!((ptr - beg) % pixbytes));
+ }
+
+ return rast;
+}
--- /dev/null
+/*
+ * $Id$
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef RT_SERIALIZE_H_INCLUDED
+#define RT_SERIALIZE_H_INCLUDED
+
+#include "librtcore.h"
+
+#define BANDTYPE_FLAGS_MASK 0xF0
+#define BANDTYPE_PIXTYPE_MASK 0x0F
+#define BANDTYPE_FLAG_OFFDB (1<<7)
+#define BANDTYPE_FLAG_HASNODATA (1<<6)
+#define BANDTYPE_FLAG_ISNODATA (1<<5)
+#define BANDTYPE_FLAG_RESERVED3 (1<<4)
+
+#define BANDTYPE_PIXTYPE(x) ((x)&BANDTYPE_PIXTYPE_MASK)
+#define BANDTYPE_IS_OFFDB(x) ((x)&BANDTYPE_FLAG_OFFDB)
+#define BANDTYPE_HAS_NODATA(x) ((x)&BANDTYPE_FLAG_HASNODATA)
+#define BANDTYPE_IS_NODATA(x) ((x)&BANDTYPE_FLAG_ISNODATA)
+
+#if POSTGIS_DEBUG_LEVEL > 2
+char*
+d_binary_to_hex(const uint8_t * const raw, uint32_t size, uint32_t *hexsize);
+
+void
+d_print_binary_hex(const char* msg, const uint8_t * const raw, uint32_t size);
+
+size_t
+d_binptr_to_pos(const uint8_t * const ptr, const uint8_t * const end, size_t size);
+
+#define CHECK_BINPTR_POSITION(ptr, end, size, pos) \
+ { if (pos != d_binptr_to_pos(ptr, end, size)) { \
+ fprintf(stderr, "Check of binary stream pointer position failed on line %d\n", __LINE__); \
+ fprintf(stderr, "\tactual = %lu, expected = %lu\n", (long unsigned)d_binptr_to_pos(ptr, end, size), (long unsigned)pos); \
+ } }
+
+#else
+
+#define CHECK_BINPTR_POSITION(ptr, end, size, pos) ((void)0);
+
+#endif /* if POSTGIS_DEBUG_LEVEL > 2 */
+
+#ifdef OPTIMIZE_SPACE
+
+/*
+ * Set given number of bits of the given byte,
+ * starting from given bitOffset (from the first)
+ * to the given value.
+ *
+ * Examples:
+ * char ch;
+ * ch=0; setBits(&ch, 1, 1, 0) -> ch==8
+ * ch=0; setBits(&ch, 3, 2, 1) -> ch==96 (0x60)
+ *
+ * Note that number of bits set must be <= 8-bitOffset
+ *
+ */
+void
+setBits(char* ch, double val, int bits, int bitOffset);
+
+#endif /* ifdef OPTIMIZE_SPACE */
+
+void
+swap_char(uint8_t *a, uint8_t *b);
+
+void
+flip_endian_16(uint8_t *d);
+
+void
+flip_endian_32(uint8_t *d);
+
+void
+flip_endian_64(uint8_t *d);
+
+uint8_t
+isMachineLittleEndian(void);
+
+uint8_t
+read_uint8(const uint8_t** from);
+
+/* unused up to now
+void
+write_uint8(uint8_t** from, uint8_t v);
+*/
+
+int8_t
+read_int8(const uint8_t** from);
+
+/* unused up to now
+void
+write_int8(uint8_t** from, int8_t v);
+*/
+
+uint16_t
+read_uint16(const uint8_t** from, uint8_t littleEndian);
+
+void
+write_uint16(uint8_t** to, uint8_t littleEndian, uint16_t v);
+
+int16_t
+read_int16(const uint8_t** from, uint8_t littleEndian);
+
+/* unused up to now
+void
+write_int16(uint8_t** to, uint8_t littleEndian, int16_t v);
+*/
+
+uint32_t
+read_uint32(const uint8_t** from, uint8_t littleEndian);
+
+/* unused up to now
+void
+write_uint32(uint8_t** to, uint8_t littleEndian, uint32_t v);
+*/
+
+int32_t
+read_int32(const uint8_t** from, uint8_t littleEndian);
+
+/* unused up to now
+void
+write_int32(uint8_t** to, uint8_t littleEndian, int32_t v);
+*/
+
+float
+read_float32(const uint8_t** from, uint8_t littleEndian);
+
+/* unused up to now
+void
+write_float32(uint8_t** from, uint8_t littleEndian, float f);
+*/
+
+double
+read_float64(const uint8_t** from, uint8_t littleEndian);
+
+/* unused up to now
+void
+write_float64(uint8_t** to, uint8_t littleEndian, double v);
+*/
+
+#endif /* RT_SERIALIZE_H_INCLUDED */
--- /dev/null
+/*
+ * $Id$
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "librtcore.h"
+#include "librtcore_internal.h"
+
+/*
+ * Return ES_ERROR if error occurred in function.
+ * Paramter aligned returns non-zero if two rasters are aligned
+ *
+ * @param rast1 : the first raster for alignment test
+ * @param rast2 : the second raster for alignment test
+ * @param *aligned : non-zero value if the two rasters are aligned
+ * @param *reason : reason why rasters are not aligned
+ *
+ * @return ES_NONE if success, ES_ERROR if error
+ */
+rt_errorstate
+rt_raster_same_alignment(
+ rt_raster rast1,
+ rt_raster rast2,
+ int *aligned, char **reason
+) {
+ double xr;
+ double yr;
+ double xw;
+ double yw;
+ int err = 0;
+
+ assert(NULL != rast1);
+ assert(NULL != rast2);
+ assert(NULL != aligned);
+
+ err = 0;
+ /* same srid */
+ if (rt_raster_get_srid(rast1) != rt_raster_get_srid(rast2)) {
+ if (reason != NULL) *reason = "The rasters have different SRIDs";
+ err = 1;
+ }
+ /* scales must match */
+ else if (FLT_NEQ(fabs(rast1->scaleX), fabs(rast2->scaleX))) {
+ if (reason != NULL) *reason = "The rasters have different scales on the X axis";
+ err = 1;
+ }
+ else if (FLT_NEQ(fabs(rast1->scaleY), fabs(rast2->scaleY))) {
+ if (reason != NULL) *reason = "The rasters have different scales on the Y axis";
+ err = 1;
+ }
+ /* skews must match */
+ else if (FLT_NEQ(rast1->skewX, rast2->skewX)) {
+ if (reason != NULL) *reason = "The rasters have different skews on the X axis";
+ err = 1;
+ }
+ else if (FLT_NEQ(rast1->skewY, rast2->skewY)) {
+ if (reason != NULL) *reason = "The rasters have different skews on the Y axis";
+ err = 1;
+ }
+
+ if (err) {
+ *aligned = 0;
+ return ES_NONE;
+ }
+
+ /* raster coordinates in context of second raster of first raster's upper-left corner */
+ if (rt_raster_geopoint_to_cell(
+ rast2,
+ rast1->ipX, rast1->ipY,
+ &xr, &yr,
+ NULL
+ ) != ES_NONE) {
+ rterror("rt_raster_same_alignment: Could not get raster coordinates of second raster from first raster's spatial coordinates");
+ *aligned = 0;
+ return ES_ERROR;
+ }
+
+ /* spatial coordinates of raster coordinates from above */
+ if (rt_raster_cell_to_geopoint(
+ rast2,
+ xr, yr,
+ &xw, &yw,
+ NULL
+ ) != ES_NONE) {
+ rterror("rt_raster_same_alignment: Could not get spatial coordinates of second raster from raster coordinates");
+ *aligned = 0;
+ return ES_ERROR;
+ }
+
+ RASTER_DEBUGF(4, "rast1(ipX, ipxY) = (%f, %f)", rast1->ipX, rast1->ipY);
+ RASTER_DEBUGF(4, "rast2(xr, yr) = (%f, %f)", xr, yr);
+ RASTER_DEBUGF(4, "rast2(xw, yw) = (%f, %f)", xw, yw);
+
+ /* spatial coordinates are identical to that of first raster's upper-left corner */
+ if (FLT_EQ(xw, rast1->ipX) && FLT_EQ(yw, rast1->ipY)) {
+ if (reason != NULL) *reason = "The rasters are aligned";
+ *aligned = 1;
+ return ES_NONE;
+ }
+
+ /* no alignment */
+ if (reason != NULL) *reason = "The rasters (pixel corner coordinates) are not aligned";
+
+ *aligned = 0;
+ return ES_NONE;
+}
+
+/******************************************************************************
+* GEOS-based spatial relationship tests
+******************************************************************************/
+
+static
+rt_errorstate rt_raster_geos_spatial_relationship(
+ rt_raster rast1, int nband1,
+ rt_raster rast2, int nband2,
+ rt_geos_spatial_test testtype,
+ int *testresult
+) {
+ LWMPOLY *surface1 = NULL;
+ LWMPOLY *surface2 = NULL;
+ GEOSGeometry *geom1 = NULL;
+ GEOSGeometry *geom2 = NULL;
+ int rtn = 0;
+ int flag = 0;
+
+ RASTER_DEBUG(3, "Starting");
+
+ assert(NULL != rast1);
+ assert(NULL != rast2);
+ assert(NULL != testresult);
+
+ if (nband1 < 0 && nband2 < 0) {
+ nband1 = -1;
+ nband2 = -1;
+ }
+ else {
+ assert(nband1 >= 0 && nband1 < rt_raster_get_num_bands(rast1));
+ assert(nband2 >= 0 && nband2 < rt_raster_get_num_bands(rast2));
+ }
+
+ /* initialize to zero, false result of spatial relationship test */
+ *testresult = 0;
+
+ /* same srid */
+ if (rt_raster_get_srid(rast1) != rt_raster_get_srid(rast2)) {
+ rterror("rt_raster_geos_spatial_relationship: The two rasters provided have different SRIDs");
+ return ES_ERROR;
+ }
+
+ initGEOS(lwnotice, lwgeom_geos_error);
+
+ /* get LWMPOLY of each band */
+ if (rt_raster_surface(rast1, nband1, &surface1) != ES_NONE) {
+ rterror("rt_raster_geos_spatial_relationship: Could not get surface of the specified band from the first raster");
+ return ES_ERROR;
+ }
+ if (rt_raster_surface(rast2, nband2, &surface2) != ES_NONE) {
+ rterror("rt_raster_geos_spatial_relationship: Could not get surface of the specified band from the second raster");
+ lwmpoly_free(surface1);
+ return ES_ERROR;
+ }
+
+ /* either surface is NULL, spatial relationship test is false */
+ if (surface1 == NULL || surface2 == NULL) {
+ if (surface1 != NULL) lwmpoly_free(surface1);
+ if (surface2 != NULL) lwmpoly_free(surface2);
+ return ES_NONE;
+ }
+
+ /* convert LWMPOLY to GEOSGeometry */
+ geom1 = LWGEOM2GEOS(lwmpoly_as_lwgeom(surface1));
+ lwmpoly_free(surface1);
+ if (geom1 == NULL) {
+ rterror("rt_raster_geos_spatial_relationship: Could not convert surface of the specified band from the first raster to a GEOSGeometry");
+ lwmpoly_free(surface2);
+ return ES_ERROR;
+ }
+
+ geom2 = LWGEOM2GEOS(lwmpoly_as_lwgeom(surface2));
+ lwmpoly_free(surface2);
+ if (geom2 == NULL) {
+ rterror("rt_raster_geos_spatial_relationship: Could not convert surface of the specified band from the second raster to a GEOSGeometry");
+ return ES_ERROR;
+ }
+
+ flag = 0;
+ switch (testtype) {
+ case GSR_OVERLAPS:
+ rtn = GEOSOverlaps(geom1, geom2);
+ break;
+ case GSR_TOUCHES:
+ rtn = GEOSTouches(geom1, geom2);
+ break;
+ case GSR_CONTAINS:
+ rtn = GEOSContains(geom1, geom2);
+ break;
+ case GSR_CONTAINSPROPERLY:
+ rtn = GEOSRelatePattern(geom1, geom2, "T**FF*FF*");
+ break;
+ case GSR_COVERS:
+ rtn = GEOSRelatePattern(geom1, geom2, "******FF*");
+ break;
+ case GSR_COVEREDBY:
+ rtn = GEOSRelatePattern(geom1, geom2, "**F**F***");
+ break;
+ default:
+ rterror("rt_raster_geos_spatial_relationship: Unknown or unsupported GEOS spatial relationship test");
+ flag = -1;
+ break;
+ }
+ GEOSGeom_destroy(geom1);
+ GEOSGeom_destroy(geom2);
+
+ /* something happened in the spatial relationship test */
+ if (rtn == 2) {
+ rterror("rt_raster_geos_spatial_relationship: Could not run the appropriate GEOS spatial relationship test");
+ flag = ES_ERROR;
+ }
+ /* spatial relationship test ran fine */
+ else if (flag >= 0) {
+ if (rtn != 0)
+ *testresult = 1;
+ flag = ES_NONE;
+ }
+ /* flag < 0 for when testtype is unknown */
+ else
+ flag = ES_ERROR;
+
+ return flag;
+}
+
+/**
+ * Return ES_ERROR if error occurred in function.
+ * Parameter overlaps returns non-zero if two rasters overlap
+ *
+ * @param rast1 : the first raster whose band will be tested
+ * @param nband1 : the 0-based band of raster rast1 to use
+ * if value is less than zero, bands are ignored.
+ * if nband1 gte zero, nband2 must be gte zero
+ * @param rast2 : the second raster whose band will be tested
+ * @param nband2 : the 0-based band of raster rast2 to use
+ * if value is less than zero, bands are ignored
+ * if nband2 gte zero, nband1 must be gte zero
+ * @param overlaps : non-zero value if the two rasters' bands overlaps
+ *
+ * @return ES_NONE if success, ES_ERROR if error
+ */
+rt_errorstate rt_raster_overlaps(
+ rt_raster rast1, int nband1,
+ rt_raster rast2, int nband2,
+ int *overlaps
+) {
+ RASTER_DEBUG(3, "Starting");
+
+ return rt_raster_geos_spatial_relationship(
+ rast1, nband1,
+ rast2, nband2,
+ GSR_OVERLAPS,
+ overlaps
+ );
+}
+
+/**
+ * Return ES_ERROR if error occurred in function.
+ * Parameter touches returns non-zero if two rasters touch
+ *
+ * @param rast1 : the first raster whose band will be tested
+ * @param nband1 : the 0-based band of raster rast1 to use
+ * if value is less than zero, bands are ignored.
+ * if nband1 gte zero, nband2 must be gte zero
+ * @param rast2 : the second raster whose band will be tested
+ * @param nband2 : the 0-based band of raster rast2 to use
+ * if value is less than zero, bands are ignored
+ * if nband2 gte zero, nband1 must be gte zero
+ * @param touches : non-zero value if the two rasters' bands touch
+ *
+ * @return ES_NONE if success, ES_ERROR if error
+ */
+rt_errorstate rt_raster_touches(
+ rt_raster rast1, int nband1,
+ rt_raster rast2, int nband2,
+ int *touches
+) {
+ RASTER_DEBUG(3, "Starting");
+
+ return rt_raster_geos_spatial_relationship(
+ rast1, nband1,
+ rast2, nband2,
+ GSR_TOUCHES,
+ touches
+ );
+}
+
+/**
+ * Return ES_ERROR if error occurred in function.
+ * Parameter contains returns non-zero if rast1 contains rast2
+ *
+ * @param rast1 : the first raster whose band will be tested
+ * @param nband1 : the 0-based band of raster rast1 to use
+ * if value is less than zero, bands are ignored.
+ * if nband1 gte zero, nband2 must be gte zero
+ * @param rast2 : the second raster whose band will be tested
+ * @param nband2 : the 0-based band of raster rast2 to use
+ * if value is less than zero, bands are ignored
+ * if nband2 gte zero, nband1 must be gte zero
+ * @param contains : non-zero value if rast1 contains rast2
+ *
+ * @return ES_NONE if success, ES_ERROR if error
+ */
+rt_errorstate rt_raster_contains(
+ rt_raster rast1, int nband1,
+ rt_raster rast2, int nband2,
+ int *contains
+) {
+ RASTER_DEBUG(3, "Starting");
+
+ return rt_raster_geos_spatial_relationship(
+ rast1, nband1,
+ rast2, nband2,
+ GSR_CONTAINS,
+ contains
+ );
+}
+
+/**
+ * Return ES_ERROR if error occurred in function.
+ * Parameter contains returns non-zero if rast1 contains properly rast2
+ *
+ * @param rast1 : the first raster whose band will be tested
+ * @param nband1 : the 0-based band of raster rast1 to use
+ * if value is less than zero, bands are ignored.
+ * if nband1 gte zero, nband2 must be gte zero
+ * @param rast2 : the second raster whose band will be tested
+ * @param nband2 : the 0-based band of raster rast2 to use
+ * if value is less than zero, bands are ignored
+ * if nband2 gte zero, nband1 must be gte zero
+ * @param contains : non-zero value if rast1 contains properly rast2
+ *
+ * @return ES_NONE if success, ES_ERROR if error
+ */
+rt_errorstate rt_raster_contains_properly(
+ rt_raster rast1, int nband1,
+ rt_raster rast2, int nband2,
+ int *contains
+) {
+ RASTER_DEBUG(3, "Starting");
+
+ return rt_raster_geos_spatial_relationship(
+ rast1, nband1,
+ rast2, nband2,
+ GSR_CONTAINSPROPERLY,
+ contains
+ );
+}
+
+/**
+ * Return ES_ERROR if error occurred in function.
+ * Parameter covers returns non-zero if rast1 covers rast2
+ *
+ * @param rast1 : the first raster whose band will be tested
+ * @param nband1 : the 0-based band of raster rast1 to use
+ * if value is less than zero, bands are ignored.
+ * if nband1 gte zero, nband2 must be gte zero
+ * @param rast2 : the second raster whose band will be tested
+ * @param nband2 : the 0-based band of raster rast2 to use
+ * if value is less than zero, bands are ignored
+ * if nband2 gte zero, nband1 must be gte zero
+ * @param covers : non-zero value if rast1 covers rast2
+ *
+ * @return ES_NONE if success, ES_ERROR if error
+ */
+rt_errorstate rt_raster_covers(
+ rt_raster rast1, int nband1,
+ rt_raster rast2, int nband2,
+ int *covers
+) {
+ RASTER_DEBUG(3, "Starting");
+
+ return rt_raster_geos_spatial_relationship(
+ rast1, nband1,
+ rast2, nband2,
+ GSR_COVERS,
+ covers
+ );
+}
+
+/**
+ * Return ES_ERROR if error occurred in function.
+ * Parameter coveredby returns non-zero if rast1 is covered by rast2
+ *
+ * @param rast1 : the first raster whose band will be tested
+ * @param nband1 : the 0-based band of raster rast1 to use
+ * if value is less than zero, bands are ignored.
+ * if nband1 gte zero, nband2 must be gte zero
+ * @param rast2 : the second raster whose band will be tested
+ * @param nband2 : the 0-based band of raster rast2 to use
+ * if value is less than zero, bands are ignored
+ * if nband2 gte zero, nband1 must be gte zero
+ * @param coveredby : non-zero value if rast1 is covered by rast2
+ *
+ * @return ES_NONE if success, ES_ERROR if error
+ */
+rt_errorstate rt_raster_coveredby(
+ rt_raster rast1, int nband1,
+ rt_raster rast2, int nband2,
+ int *coveredby
+) {
+ RASTER_DEBUG(3, "Starting");
+
+ return rt_raster_geos_spatial_relationship(
+ rast1, nband1,
+ rast2, nband2,
+ GSR_COVEREDBY,
+ coveredby
+ );
+}
+
+/**
+ * Return ES_ERROR if error occurred in function.
+ * Parameter dwithin returns non-zero if rast1 is within the specified
+ * distance of rast2
+ *
+ * @param rast1 : the first raster whose band will be tested
+ * @param nband1 : the 0-based band of raster rast1 to use
+ * if value is less than zero, bands are ignored.
+ * if nband1 gte zero, nband2 must be gte zero
+ * @param rast2 : the second raster whose band will be tested
+ * @param nband2 : the 0-based band of raster rast2 to use
+ * if value is less than zero, bands are ignored
+ * if nband2 gte zero, nband1 must be gte zero
+ * @param dwithin : non-zero value if rast1 is within the specified distance
+ * of rast2
+ *
+ * @return ES_NONE if success, ES_ERROR if error
+ */
+rt_errorstate rt_raster_within_distance(
+ rt_raster rast1, int nband1,
+ rt_raster rast2, int nband2,
+ double distance,
+ int *dwithin
+) {
+ LWMPOLY *surface = NULL;
+ LWGEOM *surface1 = NULL;
+ LWGEOM *surface2 = NULL;
+ double mindist = 0;
+
+ RASTER_DEBUG(3, "Starting");
+
+ assert(NULL != rast1);
+ assert(NULL != rast2);
+ assert(NULL != dwithin);
+
+ if (nband1 < 0 && nband2 < 0) {
+ nband1 = -1;
+ nband2 = -1;
+ }
+ else {
+ assert(nband1 >= 0 && nband1 < rt_raster_get_num_bands(rast1));
+ assert(nband2 >= 0 && nband2 < rt_raster_get_num_bands(rast2));
+ }
+
+ /* initialize to zero, false result */
+ *dwithin = 0;
+
+ /* same srid */
+ if (rt_raster_get_srid(rast1) != rt_raster_get_srid(rast2)) {
+ rterror("rt_raster_distance_within: The two rasters provided have different SRIDs");
+ return ES_ERROR;
+ }
+
+ /* distance cannot be less than zero */
+ if (distance < 0) {
+ rterror("rt_raster_distance_within: Distance cannot be less than zero");
+ return ES_ERROR;
+ }
+
+ /* get LWMPOLY of each band */
+ if (rt_raster_surface(rast1, nband1, &surface) != ES_NONE) {
+ rterror("rt_raster_distance_within: Could not get surface of the specified band from the first raster");
+ return ES_ERROR;
+ }
+ surface1 = lwmpoly_as_lwgeom(surface);
+
+ if (rt_raster_surface(rast2, nband2, &surface) != ES_NONE) {
+ rterror("rt_raster_distance_within: Could not get surface of the specified band from the second raster");
+ lwgeom_free(surface1);
+ return ES_ERROR;
+ }
+ surface2 = lwmpoly_as_lwgeom(surface);
+
+ /* either surface is NULL, test is false */
+ if (surface1 == NULL || surface2 == NULL) {
+ if (surface1 != NULL) lwgeom_free(surface1);
+ if (surface2 != NULL) lwgeom_free(surface2);
+ return ES_NONE;
+ }
+
+ /* get the min distance between the two surfaces */
+ mindist = lwgeom_mindistance2d_tolerance(surface1, surface2, distance);
+
+ lwgeom_free(surface1);
+ lwgeom_free(surface2);
+
+ /* if distance >= mindist, true */
+ if (FLT_EQ(mindist, distance) || distance > mindist)
+ *dwithin = 1;
+
+ RASTER_DEBUGF(3, "(mindist, distance) = (%f, %f, %d)", mindist, distance, *dwithin);
+
+ return ES_NONE;
+}
+
+/**
+ * Return ES_ERROR if error occurred in function.
+ * Parameter dfwithin returns non-zero if rast1 is fully within the specified
+ * distance of rast2
+ *
+ * @param rast1 : the first raster whose band will be tested
+ * @param nband1 : the 0-based band of raster rast1 to use
+ * if value is less than zero, bands are ignored.
+ * if nband1 gte zero, nband2 must be gte zero
+ * @param rast2 : the second raster whose band will be tested
+ * @param nband2 : the 0-based band of raster rast2 to use
+ * if value is less than zero, bands are ignored
+ * if nband2 gte zero, nband1 must be gte zero
+ * @param dfwithin : non-zero value if rast1 is fully within the specified
+ * distance of rast2
+ *
+ * @return ES_NONE if success, ES_ERROR if error
+ */
+rt_errorstate rt_raster_fully_within_distance(
+ rt_raster rast1, int nband1,
+ rt_raster rast2, int nband2,
+ double distance,
+ int *dfwithin
+) {
+ LWMPOLY *surface = NULL;
+ LWGEOM *surface1 = NULL;
+ LWGEOM *surface2 = NULL;
+ double maxdist = 0;
+
+ RASTER_DEBUG(3, "Starting");
+
+ assert(NULL != rast1);
+ assert(NULL != rast2);
+ assert(NULL != dfwithin);
+
+ if (nband1 < 0 && nband2 < 0) {
+ nband1 = -1;
+ nband2 = -1;
+ }
+ else {
+ assert(nband1 >= 0 && nband1 < rt_raster_get_num_bands(rast1));
+ assert(nband2 >= 0 && nband2 < rt_raster_get_num_bands(rast2));
+ }
+
+ /* initialize to zero, false result */
+ *dfwithin = 0;
+
+ /* same srid */
+ if (rt_raster_get_srid(rast1) != rt_raster_get_srid(rast2)) {
+ rterror("rt_raster_fully_within_distance: The two rasters provided have different SRIDs");
+ return ES_ERROR;
+ }
+
+ /* distance cannot be less than zero */
+ if (distance < 0) {
+ rterror("rt_raster_fully_within_distance: Distance cannot be less than zero");
+ return ES_ERROR;
+ }
+
+ /* get LWMPOLY of each band */
+ if (rt_raster_surface(rast1, nband1, &surface) != ES_NONE) {
+ rterror("rt_raster_fully_within_distance: Could not get surface of the specified band from the first raster");
+ return ES_ERROR;
+ }
+ surface1 = lwmpoly_as_lwgeom(surface);
+
+ if (rt_raster_surface(rast2, nband2, &surface) != ES_NONE) {
+ rterror("rt_raster_fully_within_distance: Could not get surface of the specified band from the second raster");
+ lwgeom_free(surface1);
+ return ES_ERROR;
+ }
+ surface2 = lwmpoly_as_lwgeom(surface);
+
+ /* either surface is NULL, test is false */
+ if (surface1 == NULL || surface2 == NULL) {
+ if (surface1 != NULL) lwgeom_free(surface1);
+ if (surface2 != NULL) lwgeom_free(surface2);
+ return ES_NONE;
+ }
+
+ /* get the maximum distance between the two surfaces */
+ maxdist = lwgeom_maxdistance2d_tolerance(surface1, surface2, distance);
+
+ lwgeom_free(surface1);
+ lwgeom_free(surface2);
+
+ /* if distance >= maxdist, true */
+ if (FLT_EQ(maxdist, distance) || distance > maxdist)
+ *dfwithin = 1;
+
+ RASTER_DEBUGF(3, "(maxdist, distance, dfwithin) = (%f, %f, %d)", maxdist, distance, *dfwithin);
+
+ return ES_NONE;
+}
+
+/******************************************************************************
+* rt_raster_intersects()
+******************************************************************************/
+
+static
+int rt_raster_intersects_algorithm(
+ rt_raster rast1, rt_raster rast2,
+ rt_band band1, rt_band band2,
+ int hasnodata1, int hasnodata2,
+ double nodata1, double nodata2
+) {
+ int i;
+ int byHeight = 1;
+ uint32_t dimValue;
+
+ uint32_t row;
+ uint32_t rowoffset;
+ uint32_t col;
+ uint32_t coloffset;
+
+ enum line_points {X1, Y1, X2, Y2};
+ enum point {pX, pY};
+ double line1[4] = {0.};
+ double line2[4] = {0.};
+ double P[2] = {0.};
+ double Qw[2] = {0.};
+ double Qr[2] = {0.};
+ double gt1[6] = {0.};
+ double gt2[6] = {0.};
+ double igt1[6] = {0};
+ double igt2[6] = {0};
+ double d;
+ double val1;
+ int noval1;
+ int isnodata1;
+ double val2;
+ int noval2;
+ int isnodata2;
+ uint32_t adjacent[8] = {0};
+
+ double xscale;
+ double yscale;
+
+ uint16_t width1;
+ uint16_t height1;
+ uint16_t width2;
+ uint16_t height2;
+
+ width1 = rt_raster_get_width(rast1);
+ height1 = rt_raster_get_height(rast1);
+ width2 = rt_raster_get_width(rast2);
+ height2 = rt_raster_get_height(rast2);
+
+ /* sampling scale */
+ xscale = fmin(rt_raster_get_x_scale(rast1), rt_raster_get_x_scale(rast2)) / 10.;
+ yscale = fmin(rt_raster_get_y_scale(rast1), rt_raster_get_y_scale(rast2)) / 10.;
+
+ /* see if skew made rast2's rows are parallel to rast1's cols */
+ rt_raster_cell_to_geopoint(
+ rast1,
+ 0, 0,
+ &(line1[X1]), &(line1[Y1]),
+ gt1
+ );
+
+ rt_raster_cell_to_geopoint(
+ rast1,
+ 0, height1,
+ &(line1[X2]), &(line1[Y2]),
+ gt1
+ );
+
+ rt_raster_cell_to_geopoint(
+ rast2,
+ 0, 0,
+ &(line2[X1]), &(line2[Y1]),
+ gt2
+ );
+
+ rt_raster_cell_to_geopoint(
+ rast2,
+ width2, 0,
+ &(line2[X2]), &(line2[Y2]),
+ gt2
+ );
+
+ /* parallel vertically */
+ if (FLT_EQ(line1[X2] - line1[X1], 0.) && FLT_EQ(line2[X2] - line2[X1], 0.))
+ byHeight = 0;
+ /* parallel */
+ else if (FLT_EQ(((line1[Y2] - line1[Y1]) / (line1[X2] - line1[X1])), ((line2[Y2] - line2[Y1]) / (line2[X2] - line2[X1]))))
+ byHeight = 0;
+
+ if (byHeight)
+ dimValue = height2;
+ else
+ dimValue = width2;
+ RASTER_DEBUGF(4, "byHeight: %d, dimValue: %d", byHeight, dimValue);
+
+ /* 3 x 3 search */
+ for (coloffset = 0; coloffset < 3; coloffset++) {
+ for (rowoffset = 0; rowoffset < 3; rowoffset++) {
+ /* smaller raster */
+ for (col = coloffset; col <= width1; col += 3) {
+
+ rt_raster_cell_to_geopoint(
+ rast1,
+ col, 0,
+ &(line1[X1]), &(line1[Y1]),
+ gt1
+ );
+
+ rt_raster_cell_to_geopoint(
+ rast1,
+ col, height1,
+ &(line1[X2]), &(line1[Y2]),
+ gt1
+ );
+
+ /* larger raster */
+ for (row = rowoffset; row <= dimValue; row += 3) {
+
+ if (byHeight) {
+ rt_raster_cell_to_geopoint(
+ rast2,
+ 0, row,
+ &(line2[X1]), &(line2[Y1]),
+ gt2
+ );
+
+ rt_raster_cell_to_geopoint(
+ rast2,
+ width2, row,
+ &(line2[X2]), &(line2[Y2]),
+ gt2
+ );
+ }
+ else {
+ rt_raster_cell_to_geopoint(
+ rast2,
+ row, 0,
+ &(line2[X1]), &(line2[Y1]),
+ gt2
+ );
+
+ rt_raster_cell_to_geopoint(
+ rast2,
+ row, height2,
+ &(line2[X2]), &(line2[Y2]),
+ gt2
+ );
+ }
+
+ RASTER_DEBUGF(4, "(col, row) = (%d, %d)", col, row);
+ RASTER_DEBUGF(4, "line1(x1, y1, x2, y2) = (%f, %f, %f, %f)",
+ line1[X1], line1[Y1], line1[X2], line1[Y2]);
+ RASTER_DEBUGF(4, "line2(x1, y1, x2, y2) = (%f, %f, %f, %f)",
+ line2[X1], line2[Y1], line2[X2], line2[Y2]);
+
+ /* intersection */
+ /* http://en.wikipedia.org/wiki/Line-line_intersection */
+ d = ((line1[X1] - line1[X2]) * (line2[Y1] - line2[Y2])) - ((line1[Y1] - line1[Y2]) * (line2[X1] - line2[X2]));
+ /* no intersection */
+ if (FLT_EQ(d, 0.)) {
+ continue;
+ }
+
+ P[pX] = (((line1[X1] * line1[Y2]) - (line1[Y1] * line1[X2])) * (line2[X1] - line2[X2])) -
+ ((line1[X1] - line1[X2]) * ((line2[X1] * line2[Y2]) - (line2[Y1] * line2[X2])));
+ P[pX] = P[pX] / d;
+
+ P[pY] = (((line1[X1] * line1[Y2]) - (line1[Y1] * line1[X2])) * (line2[Y1] - line2[Y2])) -
+ ((line1[Y1] - line1[Y2]) * ((line2[X1] * line2[Y2]) - (line2[Y1] * line2[X2])));
+ P[pY] = P[pY] / d;
+
+ RASTER_DEBUGF(4, "P(x, y) = (%f, %f)", P[pX], P[pY]);
+
+ /* intersection within bounds */
+ if ((
+ (FLT_EQ(P[pX], line1[X1]) || FLT_EQ(P[pX], line1[X2])) ||
+ (P[pX] > fmin(line1[X1], line1[X2]) && (P[pX] < fmax(line1[X1], line1[X2])))
+ ) && (
+ (FLT_EQ(P[pY], line1[Y1]) || FLT_EQ(P[pY], line1[Y2])) ||
+ (P[pY] > fmin(line1[Y1], line1[Y2]) && (P[pY] < fmax(line1[Y1], line1[Y2])))
+ ) && (
+ (FLT_EQ(P[pX], line2[X1]) || FLT_EQ(P[pX], line2[X2])) ||
+ (P[pX] > fmin(line2[X1], line2[X2]) && (P[pX] < fmax(line2[X1], line2[X2])))
+ ) && (
+ (FLT_EQ(P[pY], line2[Y1]) || FLT_EQ(P[pY], line2[Y2])) ||
+ (P[pY] > fmin(line2[Y1], line2[Y2]) && (P[pY] < fmax(line2[Y1], line2[Y2])))
+ )) {
+ RASTER_DEBUG(4, "within bounds");
+
+ for (i = 0; i < 8; i++) adjacent[i] = 0;
+
+ /* test points around intersection */
+ for (i = 0; i < 8; i++) {
+ switch (i) {
+ case 7:
+ Qw[pX] = P[pX] - xscale;
+ Qw[pY] = P[pY] + yscale;
+ break;
+ /* 270 degrees = 09:00 */
+ case 6:
+ Qw[pX] = P[pX] - xscale;
+ Qw[pY] = P[pY];
+ break;
+ case 5:
+ Qw[pX] = P[pX] - xscale;
+ Qw[pY] = P[pY] - yscale;
+ break;
+ /* 180 degrees = 06:00 */
+ case 4:
+ Qw[pX] = P[pX];
+ Qw[pY] = P[pY] - yscale;
+ break;
+ case 3:
+ Qw[pX] = P[pX] + xscale;
+ Qw[pY] = P[pY] - yscale;
+ break;
+ /* 90 degrees = 03:00 */
+ case 2:
+ Qw[pX] = P[pX] + xscale;
+ Qw[pY] = P[pY];
+ break;
+ /* 45 degrees */
+ case 1:
+ Qw[pX] = P[pX] + xscale;
+ Qw[pY] = P[pY] + yscale;
+ break;
+ /* 0 degrees = 00:00 */
+ case 0:
+ Qw[pX] = P[pX];
+ Qw[pY] = P[pY] + yscale;
+ break;
+ }
+
+ /* unable to convert point to cell */
+ noval1 = 0;
+ if (rt_raster_geopoint_to_cell(
+ rast1,
+ Qw[pX], Qw[pY],
+ &(Qr[pX]), &(Qr[pY]),
+ igt1
+ ) != ES_NONE) {
+ noval1 = 1;
+ }
+ /* cell is outside bounds of grid */
+ else if (
+ (Qr[pX] < 0 || Qr[pX] > width1 || FLT_EQ(Qr[pX], width1)) ||
+ (Qr[pY] < 0 || Qr[pY] > height1 || FLT_EQ(Qr[pY], height1))
+ ) {
+ noval1 = 1;
+ }
+ else if (hasnodata1 == FALSE)
+ val1 = 1;
+ /* unable to get value at cell */
+ else if (rt_band_get_pixel(band1, Qr[pX], Qr[pY], &val1, &isnodata1) != ES_NONE)
+ noval1 = 1;
+
+ /* unable to convert point to cell */
+ noval2 = 0;
+ if (rt_raster_geopoint_to_cell(
+ rast2,
+ Qw[pX], Qw[pY],
+ &(Qr[pX]), &(Qr[pY]),
+ igt2
+ ) != ES_NONE) {
+ noval2 = 1;
+ }
+ /* cell is outside bounds of grid */
+ else if (
+ (Qr[pX] < 0 || Qr[pX] > width2 || FLT_EQ(Qr[pX], width2)) ||
+ (Qr[pY] < 0 || Qr[pY] > height2 || FLT_EQ(Qr[pY], height2))
+ ) {
+ noval2 = 1;
+ }
+ else if (hasnodata2 == FALSE)
+ val2 = 1;
+ /* unable to get value at cell */
+ else if (rt_band_get_pixel(band2, Qr[pX], Qr[pY], &val2, &isnodata2) != ES_NONE)
+ noval2 = 1;
+
+ if (!noval1) {
+ RASTER_DEBUGF(4, "val1 = %f", val1);
+ }
+ if (!noval2) {
+ RASTER_DEBUGF(4, "val2 = %f", val2);
+ }
+
+ /* pixels touch */
+ if (!noval1 && (
+ (hasnodata1 == FALSE) || !isnodata1
+ )) {
+ adjacent[i]++;
+ }
+ if (!noval2 && (
+ (hasnodata2 == FALSE) || !isnodata2
+ )) {
+ adjacent[i] += 3;
+ }
+
+ /* two pixel values not present */
+ if (noval1 || noval2) {
+ RASTER_DEBUGF(4, "noval1 = %d, noval2 = %d", noval1, noval2);
+ continue;
+ }
+
+ /* pixels valid, so intersect */
+ if (
+ ((hasnodata1 == FALSE) || !isnodata1) &&
+ ((hasnodata2 == FALSE) || !isnodata2)
+ ) {
+ RASTER_DEBUG(3, "The two rasters do intersect");
+
+ return 1;
+ }
+ }
+
+ /* pixels touch */
+ for (i = 0; i < 4; i++) {
+ RASTER_DEBUGF(4, "adjacent[%d] = %d, adjacent[%d] = %d"
+ , i, adjacent[i], i + 4, adjacent[i + 4]);
+ if (adjacent[i] == 0) continue;
+
+ if (adjacent[i] + adjacent[i + 4] == 4) {
+ RASTER_DEBUG(3, "The two rasters touch");
+
+ return 1;
+ }
+ }
+ }
+ else {
+ RASTER_DEBUG(4, "outside of bounds");
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Return zero if error occurred in function.
+ * Parameter intersects returns non-zero if two rasters intersect
+ *
+ * @param rast1 : the first raster whose band will be tested
+ * @param nband1 : the 0-based band of raster rast1 to use
+ * if value is less than zero, bands are ignored.
+ * if nband1 gte zero, nband2 must be gte zero
+ * @param rast2 : the second raster whose band will be tested
+ * @param nband2 : the 0-based band of raster rast2 to use
+ * if value is less than zero, bands are ignored
+ * if nband2 gte zero, nband1 must be gte zero
+ * @param intersects : non-zero value if the two rasters' bands intersects
+ *
+ * @return ES_NONE if success, ES_ERROR if error
+ */
+rt_errorstate
+rt_raster_intersects(
+ rt_raster rast1, int nband1,
+ rt_raster rast2, int nband2,
+ int *intersects
+) {
+ int i;
+ int j;
+ int within = 0;
+
+ LWGEOM *hull[2] = {NULL};
+ GEOSGeometry *ghull[2] = {NULL};
+
+ uint16_t width1;
+ uint16_t height1;
+ uint16_t width2;
+ uint16_t height2;
+ double area1;
+ double area2;
+ double pixarea1;
+ double pixarea2;
+ rt_raster rastS = NULL;
+ rt_raster rastL = NULL;
+ uint16_t *widthS = NULL;
+ uint16_t *heightS = NULL;
+ uint16_t *widthL = NULL;
+ uint16_t *heightL = NULL;
+ int nbandS;
+ int nbandL;
+ rt_band bandS = NULL;
+ rt_band bandL = NULL;
+ int hasnodataS = FALSE;
+ int hasnodataL = FALSE;
+ double nodataS = 0;
+ double nodataL = 0;
+ int isnodataS = 0;
+ int isnodataL = 0;
+ double gtS[6] = {0};
+ double igtL[6] = {0};
+
+ uint32_t row;
+ uint32_t rowoffset;
+ uint32_t col;
+ uint32_t coloffset;
+
+ enum line_points {X1, Y1, X2, Y2};
+ enum point {pX, pY};
+ double lineS[4];
+ double Qr[2];
+ double valS;
+ double valL;
+
+ RASTER_DEBUG(3, "Starting");
+
+ assert(NULL != rast1);
+ assert(NULL != rast2);
+ assert(NULL != intersects);
+
+ if (nband1 < 0 && nband2 < 0) {
+ nband1 = -1;
+ nband2 = -1;
+ }
+ else {
+ assert(nband1 >= 0 && nband1 < rt_raster_get_num_bands(rast1));
+ assert(nband2 >= 0 && nband2 < rt_raster_get_num_bands(rast2));
+ }
+
+ /* same srid */
+ if (rt_raster_get_srid(rast1) != rt_raster_get_srid(rast2)) {
+ rterror("rt_raster_intersects: The two rasters provided have different SRIDs");
+ *intersects = 0;
+ return ES_ERROR;
+ }
+
+ /* raster extents need to intersect */
+ do {
+ int rtn;
+
+ initGEOS(lwnotice, lwgeom_geos_error);
+
+ rtn = 1;
+ for (i = 0; i < 2; i++) {
+ if ((rt_raster_get_convex_hull(i < 1 ? rast1 : rast2, &(hull[i])) != ES_NONE) || NULL == hull[i]) {
+ for (j = 0; j < i; j++) {
+ GEOSGeom_destroy(ghull[j]);
+ lwgeom_free(hull[j]);
+ }
+ rtn = 0;
+ break;
+ }
+ ghull[i] = (GEOSGeometry *) LWGEOM2GEOS(hull[i]);
+ if (NULL == ghull[i]) {
+ for (j = 0; j < i; j++) {
+ GEOSGeom_destroy(ghull[j]);
+ lwgeom_free(hull[j]);
+ }
+ lwgeom_free(hull[i]);
+ rtn = 0;
+ break;
+ }
+ }
+ if (!rtn) break;
+
+ /* test to see if raster within the other */
+ within = 0;
+ if (GEOSWithin(ghull[0], ghull[1]) == 1)
+ within = -1;
+ else if (GEOSWithin(ghull[1], ghull[0]) == 1)
+ within = 1;
+
+ if (within != 0)
+ rtn = 1;
+ else
+ rtn = GEOSIntersects(ghull[0], ghull[1]);
+
+ for (i = 0; i < 2; i++) {
+ GEOSGeom_destroy(ghull[i]);
+ lwgeom_free(hull[i]);
+ }
+
+ if (rtn != 2) {
+ RASTER_DEBUGF(4, "convex hulls of rasters do %sintersect", rtn != 1 ? "NOT " : "");
+ if (rtn != 1) {
+ *intersects = 0;
+ return ES_NONE;
+ }
+ /* band isn't specified */
+ else if (nband1 < 0) {
+ *intersects = 1;
+ return ES_NONE;
+ }
+ }
+ else {
+ RASTER_DEBUG(4, "GEOSIntersects() returned a 2!!!!");
+ }
+ }
+ while (0);
+
+ /* smaller raster by area or width */
+ width1 = rt_raster_get_width(rast1);
+ height1 = rt_raster_get_height(rast1);
+ width2 = rt_raster_get_width(rast2);
+ height2 = rt_raster_get_height(rast2);
+ pixarea1 = fabs(rt_raster_get_x_scale(rast1) * rt_raster_get_y_scale(rast1));
+ pixarea2 = fabs(rt_raster_get_x_scale(rast2) * rt_raster_get_y_scale(rast2));
+ area1 = fabs(width1 * height1 * pixarea1);
+ area2 = fabs(width2 * height2 * pixarea2);
+ RASTER_DEBUGF(4, "pixarea1, pixarea2, area1, area2 = %f, %f, %f, %f",
+ pixarea1, pixarea2, area1, area2);
+ if (
+ (within <= 0) ||
+ (area1 < area2) ||
+ FLT_EQ(area1, area2) ||
+ (area1 < pixarea2) || /* area of rast1 smaller than pixel area of rast2 */
+ FLT_EQ(area1, pixarea2)
+ ) {
+ rastS = rast1;
+ nbandS = nband1;
+ widthS = &width1;
+ heightS = &height1;
+
+ rastL = rast2;
+ nbandL = nband2;
+ widthL = &width2;
+ heightL = &height2;
+ }
+ else {
+ rastS = rast2;
+ nbandS = nband2;
+ widthS = &width2;
+ heightS = &height2;
+
+ rastL = rast1;
+ nbandL = nband1;
+ widthL = &width1;
+ heightL = &height1;
+ }
+
+ /* no band to use, set band to zero */
+ if (nband1 < 0) {
+ nbandS = 0;
+ nbandL = 0;
+ }
+
+ RASTER_DEBUGF(4, "rast1 @ %p", rast1);
+ RASTER_DEBUGF(4, "rast2 @ %p", rast2);
+ RASTER_DEBUGF(4, "rastS @ %p", rastS);
+ RASTER_DEBUGF(4, "rastL @ %p", rastL);
+
+ /* load band of smaller raster */
+ bandS = rt_raster_get_band(rastS, nbandS);
+ if (NULL == bandS) {
+ rterror("rt_raster_intersects: Could not get band %d of the first raster", nbandS);
+ *intersects = 0;
+ return ES_ERROR;
+ }
+
+ hasnodataS = rt_band_get_hasnodata_flag(bandS);
+ if (hasnodataS != FALSE)
+ rt_band_get_nodata(bandS, &nodataS);
+
+ /* load band of larger raster */
+ bandL = rt_raster_get_band(rastL, nbandL);
+ if (NULL == bandL) {
+ rterror("rt_raster_intersects: Could not get band %d of the first raster", nbandL);
+ *intersects = 0;
+ return ES_ERROR;
+ }
+
+ hasnodataL = rt_band_get_hasnodata_flag(bandL);
+ if (hasnodataL != FALSE)
+ rt_band_get_nodata(bandL, &nodataL);
+
+ /* no band to use, ignore nodata */
+ if (nband1 < 0) {
+ hasnodataS = FALSE;
+ hasnodataL = FALSE;
+ }
+
+ /* hasnodata(S|L) = TRUE and one of the two bands is isnodata */
+ if (
+ (hasnodataS && rt_band_get_isnodata_flag(bandS)) ||
+ (hasnodataL && rt_band_get_isnodata_flag(bandL))
+ ) {
+ RASTER_DEBUG(3, "One of the two raster bands is NODATA. The two rasters do not intersect");
+ *intersects = 0;
+ return ES_NONE;
+ }
+
+ /* special case where a raster can fit inside another raster's pixel */
+ if (within != 0 && ((pixarea1 > area2) || (pixarea2 > area1))) {
+ RASTER_DEBUG(4, "Using special case of raster fitting into another raster's pixel");
+ /* 3 x 3 search */
+ for (coloffset = 0; coloffset < 3; coloffset++) {
+ for (rowoffset = 0; rowoffset < 3; rowoffset++) {
+ for (col = coloffset; col < *widthS; col += 3) {
+ for (row = rowoffset; row < *heightS; row += 3) {
+ if (hasnodataS == FALSE)
+ valS = 1;
+ else if (rt_band_get_pixel(bandS, col, row, &valS, &isnodataS) != ES_NONE)
+ continue;
+
+ if ((hasnodataS == FALSE) || !isnodataS) {
+ rt_raster_cell_to_geopoint(
+ rastS,
+ col, row,
+ &(lineS[X1]), &(lineS[Y1]),
+ gtS
+ );
+
+ if (rt_raster_geopoint_to_cell(
+ rastL,
+ lineS[X1], lineS[Y1],
+ &(Qr[pX]), &(Qr[pY]),
+ igtL
+ ) != ES_NONE) {
+ continue;
+ }
+
+ if (
+ (Qr[pX] < 0 || Qr[pX] > *widthL || FLT_EQ(Qr[pX], *widthL)) ||
+ (Qr[pY] < 0 || Qr[pY] > *heightL || FLT_EQ(Qr[pY], *heightL))
+ ) {
+ continue;
+ }
+
+ if (hasnodataS == FALSE)
+ valL = 1;
+ else if (rt_band_get_pixel(bandL, Qr[pX], Qr[pY], &valL, &isnodataL) != ES_NONE)
+ continue;
+
+ if ((hasnodataL == FALSE) || !isnodataL) {
+ RASTER_DEBUG(3, "The two rasters do intersect");
+ *intersects = 1;
+ return ES_NONE;
+ }
+ }
+ }
+ }
+ }
+ }
+ RASTER_DEBUG(4, "Smaller raster not in the other raster's pixel. Continuing");
+ }
+
+ RASTER_DEBUG(4, "Testing smaller raster vs larger raster");
+ *intersects = rt_raster_intersects_algorithm(
+ rastS, rastL,
+ bandS, bandL,
+ hasnodataS, hasnodataL,
+ nodataS, nodataL
+ );
+
+ if (*intersects) return ES_NONE;
+
+ RASTER_DEBUG(4, "Testing larger raster vs smaller raster");
+ *intersects = rt_raster_intersects_algorithm(
+ rastL, rastS,
+ bandL, bandS,
+ hasnodataL, hasnodataS,
+ nodataL, nodataS
+ );
+
+ if (*intersects) return ES_NONE;
+
+ RASTER_DEBUG(3, "The two rasters do not intersect");
+
+ *intersects = 0;
+ return ES_NONE;
+}
+
--- /dev/null
+/*
+ * $Id$
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "librtcore.h"
+#include "librtcore_internal.h"
+
+/******************************************************************************
+* quicksort
+******************************************************************************/
+
+#define SWAP(x, y) { double t; t = x; x = y; y = t; }
+#define ORDER(x, y) if (x > y) SWAP(x, y)
+
+static double pivot(double *left, double *right) {
+ double l, m, r, *p;
+
+ l = *left;
+ m = *(left + (right - left) / 2);
+ r = *right;
+
+ /* order */
+ ORDER(l, m);
+ ORDER(l, r);
+ ORDER(m, r);
+
+ /* pivot is higher of two values */
+ if (l < m) return m;
+ if (m < r) return r;
+
+ /* find pivot that isn't left */
+ for (p = left + 1; p <= right; ++p) {
+ if (*p != *left)
+ return (*p < *left) ? *left : *p;
+ }
+
+ /* all values are same */
+ return -1;
+}
+
+static double *partition(double *left, double *right, double pivot) {
+ while (left <= right) {
+ while (*left < pivot) ++left;
+ while (*right >= pivot) --right;
+
+ if (left < right) {
+ SWAP(*left, *right);
+ ++left;
+ --right;
+ }
+ }
+
+ return left;
+}
+
+static void quicksort(double *left, double *right) {
+ double p = pivot(left, right);
+ double *pos;
+
+ if (p != -1) {
+ pos = partition(left, right, p);
+ quicksort(left, pos - 1);
+ quicksort(pos, right);
+ }
+}
+
+/******************************************************************************
+* rt_band_get_summary_stats()
+******************************************************************************/
+
+/**
+ * Compute summary statistics for a band
+ *
+ * @param band : the band to query for summary stats
+ * @param exclude_nodata_value : if non-zero, ignore nodata values
+ * @param sample : percentage of pixels to sample
+ * @param inc_vals : flag to include values in return struct
+ * @param cK : number of pixels counted thus far in coverage
+ * @param cM : M component of 1-pass stddev for coverage
+ * @param cQ : Q component of 1-pass stddev for coverage
+ *
+ * @return the summary statistics for a band or NULL
+ */
+rt_bandstats
+rt_band_get_summary_stats(
+ rt_band band,
+ int exclude_nodata_value, double sample, int inc_vals,
+ uint64_t *cK, double *cM, double *cQ
+) {
+ uint32_t x = 0;
+ uint32_t y = 0;
+ uint32_t z = 0;
+ uint32_t offset = 0;
+ uint32_t diff = 0;
+ int rtn;
+ int hasnodata = FALSE;
+ double nodata = 0;
+ double *values = NULL;
+ double value;
+ int isnodata = 0;
+ rt_bandstats stats = NULL;
+
+ uint32_t do_sample = 0;
+ uint32_t sample_size = 0;
+ uint32_t sample_per = 0;
+ uint32_t sample_int = 0;
+ uint32_t i = 0;
+ uint32_t j = 0;
+ double sum = 0;
+ uint32_t k = 0;
+ double M = 0;
+ double Q = 0;
+
+#if POSTGIS_DEBUG_LEVEL > 0
+ clock_t start, stop;
+ double elapsed = 0;
+#endif
+
+ RASTER_DEBUG(3, "starting");
+#if POSTGIS_DEBUG_LEVEL > 0
+ start = clock();
+#endif
+
+ assert(NULL != band);
+
+ /* band is empty (width < 1 || height < 1) */
+ if (band->width < 1 || band->height < 1) {
+ stats = (rt_bandstats) rtalloc(sizeof(struct rt_bandstats_t));
+ if (NULL == stats) {
+ rterror("rt_band_get_summary_stats: Could not allocate memory for stats");
+ return NULL;
+ }
+
+ rtwarn("Band is empty as width and/or height is 0");
+
+ stats->sample = 1;
+ stats->sorted = 0;
+ stats->values = NULL;
+ stats->count = 0;
+ stats->min = stats->max = 0;
+ stats->sum = 0;
+ stats->mean = 0;
+ stats->stddev = -1;
+
+ return stats;
+ }
+
+ hasnodata = rt_band_get_hasnodata_flag(band);
+ if (hasnodata != FALSE)
+ rt_band_get_nodata(band, &nodata);
+ else
+ exclude_nodata_value = 0;
+
+ RASTER_DEBUGF(3, "nodata = %f", nodata);
+ RASTER_DEBUGF(3, "hasnodata = %d", hasnodata);
+ RASTER_DEBUGF(3, "exclude_nodata_value = %d", exclude_nodata_value);
+
+ /* entire band is nodata */
+ if (rt_band_get_isnodata_flag(band) != FALSE) {
+ stats = (rt_bandstats) rtalloc(sizeof(struct rt_bandstats_t));
+ if (NULL == stats) {
+ rterror("rt_band_get_summary_stats: Could not allocate memory for stats");
+ return NULL;
+ }
+
+ stats->sample = 1;
+ stats->sorted = 0;
+ stats->values = NULL;
+
+ if (exclude_nodata_value) {
+ rtwarn("All pixels of band have the NODATA value");
+
+ stats->count = 0;
+ stats->min = stats->max = 0;
+ stats->sum = 0;
+ stats->mean = 0;
+ stats->stddev = -1;
+ }
+ else {
+ stats->count = band->width * band->height;
+ stats->min = stats->max = nodata;
+ stats->sum = stats->count * nodata;
+ stats->mean = nodata;
+ stats->stddev = 0;
+ }
+
+ return stats;
+ }
+
+ /* clamp percentage */
+ if (
+ (sample < 0 || FLT_EQ(sample, 0.0)) ||
+ (sample > 1 || FLT_EQ(sample, 1.0))
+ ) {
+ do_sample = 0;
+ sample = 1;
+ }
+ else
+ do_sample = 1;
+ RASTER_DEBUGF(3, "do_sample = %d", do_sample);
+
+ /* sample all pixels */
+ if (!do_sample) {
+ sample_size = band->width * band->height;
+ sample_per = band->height;
+ }
+ /*
+ randomly sample a percentage of available pixels
+ sampling method is known as
+ "systematic random sample without replacement"
+ */
+ else {
+ sample_size = round((band->width * band->height) * sample);
+ sample_per = round(sample_size / band->width);
+ if (sample_per < 1)
+ sample_per = 1;
+ sample_int = round(band->height / sample_per);
+ srand(time(NULL));
+ }
+
+ RASTER_DEBUGF(3, "sampling %d of %d available pixels w/ %d per set"
+ , sample_size, (band->width * band->height), sample_per);
+
+ if (inc_vals) {
+ values = rtalloc(sizeof(double) * sample_size);
+ if (NULL == values) {
+ rtwarn("Could not allocate memory for values");
+ inc_vals = 0;
+ }
+ }
+
+ /* initialize stats */
+ stats = (rt_bandstats) rtalloc(sizeof(struct rt_bandstats_t));
+ if (NULL == stats) {
+ rterror("rt_band_get_summary_stats: Could not allocate memory for stats");
+ return NULL;
+ }
+ stats->sample = sample;
+ stats->count = 0;
+ stats->sum = 0;
+ stats->mean = 0;
+ stats->stddev = -1;
+ stats->min = stats->max = 0;
+ stats->values = NULL;
+ stats->sorted = 0;
+
+ for (x = 0, j = 0, k = 0; x < band->width; x++) {
+ y = -1;
+ diff = 0;
+
+ for (i = 0, z = 0; i < sample_per; i++) {
+ if (!do_sample)
+ y = i;
+ else {
+ offset = (rand() % sample_int) + 1;
+ y += diff + offset;
+ diff = sample_int - offset;
+ }
+ RASTER_DEBUGF(5, "(x, y, z) = (%d, %d, %d)", x, y, z);
+ if (y >= band->height || z > sample_per) break;
+
+ rtn = rt_band_get_pixel(band, x, y, &value, &isnodata);
+
+ j++;
+ if (rtn == ES_NONE && (!exclude_nodata_value || (exclude_nodata_value && !isnodata))) {
+
+ /* inc_vals set, collect pixel values */
+ if (inc_vals) values[k] = value;
+
+ /* average */
+ k++;
+ sum += value;
+
+ /*
+ one-pass standard deviation
+ http://www.eecs.berkeley.edu/~mhoemmen/cs194/Tutorials/variance.pdf
+ */
+ if (k == 1) {
+ Q = 0;
+ M = value;
+ }
+ else {
+ Q += (((k - 1) * pow(value - M, 2)) / k);
+ M += ((value - M ) / k);
+ }
+
+ /* coverage one-pass standard deviation */
+ if (NULL != cK) {
+ (*cK)++;
+ if (*cK == 1) {
+ *cQ = 0;
+ *cM = value;
+ }
+ else {
+ *cQ += (((*cK - 1) * pow(value - *cM, 2)) / *cK);
+ *cM += ((value - *cM ) / *cK);
+ }
+ }
+
+ /* min/max */
+ if (stats->count < 1) {
+ stats->count = 1;
+ stats->min = stats->max = value;
+ }
+ else {
+ if (value < stats->min)
+ stats->min = value;
+ if (value > stats->max)
+ stats->max = value;
+ }
+
+ }
+
+ z++;
+ }
+ }
+
+ RASTER_DEBUG(3, "sampling complete");
+
+ stats->count = k;
+ if (k > 0) {
+ if (inc_vals) {
+ /* free unused memory */
+ if (sample_size != k) {
+ values = rtrealloc(values, k * sizeof(double));
+ }
+
+ stats->values = values;
+ }
+
+ stats->sum = sum;
+ stats->mean = sum / k;
+
+ /* standard deviation */
+ if (!do_sample)
+ stats->stddev = sqrt(Q / k);
+ /* sample deviation */
+ else {
+ if (k < 2)
+ stats->stddev = -1;
+ else
+ stats->stddev = sqrt(Q / (k - 1));
+ }
+ }
+ /* inc_vals thus values allocated but not used */
+ else if (inc_vals)
+ rtdealloc(values);
+
+ /* if do_sample is one */
+ if (do_sample && k < 1)
+ rtwarn("All sampled pixels of band have the NODATA value");
+
+#if POSTGIS_DEBUG_LEVEL > 0
+ stop = clock();
+ elapsed = ((double) (stop - start)) / CLOCKS_PER_SEC;
+ RASTER_DEBUGF(3, "(time, count, mean, stddev, min, max) = (%0.4f, %d, %f, %f, %f, %f)",
+ elapsed, stats->count, stats->mean, stats->stddev, stats->min, stats->max);
+#endif
+
+ RASTER_DEBUG(3, "done");
+ return stats;
+}
+
+/******************************************************************************
+* rt_band_get_histogram()
+******************************************************************************/
+
+/**
+ * Count the distribution of data
+ *
+ * @param stats : a populated stats struct for processing
+ * @param bin_count : the number of bins to group the data by
+ * @param bin_width : the width of each bin as an array
+ * @param bin_width_count : number of values in bin_width
+ * @param right : evaluate bins by (a,b] rather than default [a,b)
+ * @param min : user-defined minimum value of the histogram
+ * a value less than the minimum value is not counted in any bins
+ * if min = max, min and max are not used
+ * @param max : user-defined maximum value of the histogram
+ * a value greater than the max value is not counted in any bins
+ * if min = max, min and max are not used
+ * @param rtn_count : set to the number of bins being returned
+ *
+ * @return the histogram of the data or NULL
+ */
+rt_histogram
+rt_band_get_histogram(
+ rt_bandstats stats,
+ int bin_count, double *bin_width, int bin_width_count,
+ int right, double min, double max,
+ uint32_t *rtn_count
+) {
+ rt_histogram bins = NULL;
+ int init_width = 0;
+ int i;
+ int j;
+ double tmp;
+ double value;
+ int sum = 0;
+ double qmin;
+ double qmax;
+
+#if POSTGIS_DEBUG_LEVEL > 0
+ clock_t start, stop;
+ double elapsed = 0;
+#endif
+
+ RASTER_DEBUG(3, "starting");
+#if POSTGIS_DEBUG_LEVEL > 0
+ start = clock();
+#endif
+
+ assert(NULL != stats);
+ assert(NULL != rtn_count);
+
+ if (stats->count < 1 || NULL == stats->values) {
+ rterror("rt_util_get_histogram: rt_bandstats object has no value");
+ return NULL;
+ }
+
+ /* bin width must be positive numbers and not zero */
+ if (NULL != bin_width && bin_width_count > 0) {
+ for (i = 0; i < bin_width_count; i++) {
+ if (bin_width[i] < 0 || FLT_EQ(bin_width[i], 0.0)) {
+ rterror("rt_util_get_histogram: bin_width element is less than or equal to zero");
+ return NULL;
+ }
+ }
+ }
+
+ /* ignore min and max parameters */
+ if (FLT_EQ(max, min)) {
+ qmin = stats->min;
+ qmax = stats->max;
+ }
+ else {
+ qmin = min;
+ qmax = max;
+ if (qmin > qmax) {
+ qmin = max;
+ qmax = min;
+ }
+ }
+
+ /* # of bins not provided */
+ if (bin_count <= 0) {
+ /*
+ determine # of bins
+ http://en.wikipedia.org/wiki/Histogram
+
+ all computed bins are assumed to have equal width
+ */
+ /* Square-root choice for stats->count < 30 */
+ if (stats->count < 30)
+ bin_count = ceil(sqrt(stats->count));
+ /* Sturges' formula for stats->count >= 30 */
+ else
+ bin_count = ceil(log2((double) stats->count) + 1.);
+
+ /* bin_width_count provided and bin_width has value */
+ if (bin_width_count > 0 && NULL != bin_width) {
+ /* user has defined something specific */
+ if (bin_width_count > bin_count)
+ bin_count = bin_width_count;
+ else if (bin_width_count > 1) {
+ tmp = 0;
+ for (i = 0; i < bin_width_count; i++) tmp += bin_width[i];
+ bin_count = ceil((qmax - qmin) / tmp) * bin_width_count;
+ }
+ else
+ bin_count = ceil((qmax - qmin) / bin_width[0]);
+ }
+ /* set bin width count to zero so that one can be calculated */
+ else {
+ bin_width_count = 0;
+ }
+ }
+
+ /* min and max the same */
+ if (FLT_EQ(qmax, qmin)) bin_count = 1;
+
+ RASTER_DEBUGF(3, "bin_count = %d", bin_count);
+
+ /* bin count = 1, all values are in one bin */
+ if (bin_count < 2) {
+ bins = rtalloc(sizeof(struct rt_histogram_t));
+ if (NULL == bins) {
+ rterror("rt_util_get_histogram: Could not allocate memory for histogram");
+ return NULL;
+ }
+
+ bins->count = stats->count;
+ bins->percent = -1;
+ bins->min = qmin;
+ bins->max = qmax;
+ bins->inc_min = bins->inc_max = 1;
+
+ *rtn_count = bin_count;
+ return bins;
+ }
+
+ /* establish bin width */
+ if (bin_width_count == 0) {
+ bin_width_count = 1;
+
+ /* bin_width unallocated */
+ if (NULL == bin_width) {
+ bin_width = rtalloc(sizeof(double));
+ if (NULL == bin_width) {
+ rterror("rt_util_get_histogram: Could not allocate memory for bin widths");
+ return NULL;
+ }
+ init_width = 1;
+ }
+
+ bin_width[0] = (qmax - qmin) / bin_count;
+ }
+
+ /* initialize bins */
+ bins = rtalloc(bin_count * sizeof(struct rt_histogram_t));
+ if (NULL == bins) {
+ rterror("rt_util_get_histogram: Could not allocate memory for histogram");
+ if (init_width) rtdealloc(bin_width);
+ return NULL;
+ }
+ if (!right)
+ tmp = qmin;
+ else
+ tmp = qmax;
+ for (i = 0; i < bin_count;) {
+ for (j = 0; j < bin_width_count; j++) {
+ bins[i].count = 0;
+ bins->percent = -1;
+
+ if (!right) {
+ bins[i].min = tmp;
+ tmp += bin_width[j];
+ bins[i].max = tmp;
+
+ bins[i].inc_min = 1;
+ bins[i].inc_max = 0;
+ }
+ else {
+ bins[i].max = tmp;
+ tmp -= bin_width[j];
+ bins[i].min = tmp;
+
+ bins[i].inc_min = 0;
+ bins[i].inc_max = 1;
+ }
+
+ i++;
+ }
+ }
+ if (!right) {
+ bins[bin_count - 1].inc_max = 1;
+
+ /* align last bin to the max value */
+ if (bins[bin_count - 1].max < qmax)
+ bins[bin_count - 1].max = qmax;
+ }
+ else {
+ bins[bin_count - 1].inc_min = 1;
+
+ /* align first bin to the min value */
+ if (bins[bin_count - 1].min > qmin)
+ bins[bin_count - 1].min = qmin;
+ }
+
+ /* process the values */
+ for (i = 0; i < stats->count; i++) {
+ value = stats->values[i];
+
+ /* default, [a, b) */
+ if (!right) {
+ for (j = 0; j < bin_count; j++) {
+ if (
+ (!bins[j].inc_max && value < bins[j].max) || (
+ bins[j].inc_max && (
+ (value < bins[j].max) ||
+ FLT_EQ(value, bins[j].max)
+ )
+ )
+ ) {
+ bins[j].count++;
+ sum++;
+ break;
+ }
+ }
+ }
+ /* (a, b] */
+ else {
+ for (j = 0; j < bin_count; j++) {
+ if (
+ (!bins[j].inc_min && value > bins[j].min) || (
+ bins[j].inc_min && (
+ (value > bins[j].min) ||
+ FLT_EQ(value, bins[j].min)
+ )
+ )
+ ) {
+ bins[j].count++;
+ sum++;
+ break;
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < bin_count; i++) {
+ bins[i].percent = ((double) bins[i].count) / sum;
+ }
+
+#if POSTGIS_DEBUG_LEVEL > 0
+ stop = clock();
+ elapsed = ((double) (stop - start)) / CLOCKS_PER_SEC;
+ RASTER_DEBUGF(3, "elapsed time = %0.4f", elapsed);
+
+ for (j = 0; j < bin_count; j++) {
+ RASTER_DEBUGF(5, "(min, max, inc_min, inc_max, count, sum, percent) = (%f, %f, %d, %d, %d, %d, %f)",
+ bins[j].min, bins[j].max, bins[j].inc_min, bins[j].inc_max, bins[j].count, sum, bins[j].percent);
+ }
+#endif
+
+ if (init_width) rtdealloc(bin_width);
+ *rtn_count = bin_count;
+ RASTER_DEBUG(3, "done");
+ return bins;
+}
+
+/******************************************************************************
+* rt_band_get_quantiles()
+******************************************************************************/
+
+/**
+ * Compute the default set of or requested quantiles for a set of data
+ * the quantile formula used is same as Excel and R default method
+ *
+ * @param stats : a populated stats struct for processing
+ * @param quantiles : the quantiles to be computed
+ * @param quantiles_count : the number of quantiles to be computed
+ * @param rtn_count : set to the number of quantiles being returned
+ *
+ * @return the default set of or requested quantiles for a band or NULL
+ */
+rt_quantile
+rt_band_get_quantiles(
+ rt_bandstats stats,
+ double *quantiles, int quantiles_count,
+ uint32_t *rtn_count
+) {
+ rt_quantile rtn;
+ int init_quantiles = 0;
+ int i = 0;
+ double h;
+ int hl;
+
+#if POSTGIS_DEBUG_LEVEL > 0
+ clock_t start, stop;
+ double elapsed = 0;
+#endif
+
+ RASTER_DEBUG(3, "starting");
+#if POSTGIS_DEBUG_LEVEL > 0
+ start = clock();
+#endif
+
+ assert(NULL != stats);
+ assert(NULL != rtn_count);
+
+ if (stats->count < 1 || NULL == stats->values) {
+ rterror("rt_band_get_quantiles: rt_bandstats object has no value");
+ return NULL;
+ }
+
+ /* quantiles not provided */
+ if (NULL == quantiles) {
+ /* quantile count not specified, default to quartiles */
+ if (quantiles_count < 2)
+ quantiles_count = 5;
+
+ quantiles = rtalloc(sizeof(double) * quantiles_count);
+ init_quantiles = 1;
+ if (NULL == quantiles) {
+ rterror("rt_band_get_quantiles: Could not allocate memory for quantile input");
+ return NULL;
+ }
+
+ quantiles_count--;
+ for (i = 0; i <= quantiles_count; i++)
+ quantiles[i] = ((double) i) / quantiles_count;
+ quantiles_count++;
+ }
+
+ /* check quantiles */
+ for (i = 0; i < quantiles_count; i++) {
+ if (quantiles[i] < 0. || quantiles[i] > 1.) {
+ rterror("rt_band_get_quantiles: Quantile value not between 0 and 1");
+ if (init_quantiles) rtdealloc(quantiles);
+ return NULL;
+ }
+ }
+ quicksort(quantiles, quantiles + quantiles_count - 1);
+
+ /* initialize rt_quantile */
+ rtn = rtalloc(sizeof(struct rt_quantile_t) * quantiles_count);
+ if (NULL == rtn) {
+ rterror("rt_band_get_quantiles: Could not allocate memory for quantile output");
+ if (init_quantiles) rtdealloc(quantiles);
+ return NULL;
+ }
+
+ /* sort values */
+ if (!stats->sorted) {
+ quicksort(stats->values, stats->values + stats->count - 1);
+ stats->sorted = 1;
+ }
+
+ /*
+ make quantiles
+
+ formula is that used in R (method 7) and Excel from
+ http://en.wikipedia.org/wiki/Quantile
+ */
+ for (i = 0; i < quantiles_count; i++) {
+ rtn[i].quantile = quantiles[i];
+
+ h = ((stats->count - 1.) * quantiles[i]) + 1.;
+ hl = floor(h);
+
+ /* h greater than hl, do full equation */
+ if (h > hl)
+ rtn[i].value = stats->values[hl - 1] + ((h - hl) * (stats->values[hl] - stats->values[hl - 1]));
+ /* shortcut as second part of equation is zero */
+ else
+ rtn[i].value = stats->values[hl - 1];
+ }
+
+#if POSTGIS_DEBUG_LEVEL > 0
+ stop = clock();
+ elapsed = ((double) (stop - start)) / CLOCKS_PER_SEC;
+ RASTER_DEBUGF(3, "elapsed time = %0.4f", elapsed);
+#endif
+
+ *rtn_count = quantiles_count;
+ if (init_quantiles) rtdealloc(quantiles);
+ RASTER_DEBUG(3, "done");
+ return rtn;
+}
+
+/******************************************************************************
+* rt_band_get_quantiles_stream()
+******************************************************************************/
+
+static struct quantile_llist_element *quantile_llist_search(
+ struct quantile_llist_element *element,
+ double needle
+) {
+ if (NULL == element)
+ return NULL;
+ else if (FLT_NEQ(needle, element->value)) {
+ if (NULL != element->next)
+ return quantile_llist_search(element->next, needle);
+ else
+ return NULL;
+ }
+ else
+ return element;
+}
+
+static struct quantile_llist_element *quantile_llist_insert(
+ struct quantile_llist_element *element,
+ double value,
+ uint32_t *idx
+) {
+ struct quantile_llist_element *qle = NULL;
+
+ if (NULL == element) {
+ qle = rtalloc(sizeof(struct quantile_llist_element));
+ RASTER_DEBUGF(4, "qle @ %p is only element in list", qle);
+ if (NULL == qle) return NULL;
+
+ qle->value = value;
+ qle->count = 1;
+
+ qle->prev = NULL;
+ qle->next = NULL;
+
+ if (NULL != idx) *idx = 0;
+ return qle;
+ }
+ else if (value > element->value) {
+ if (NULL != idx) *idx += 1;
+ if (NULL != element->next)
+ return quantile_llist_insert(element->next, value, idx);
+ /* insert as last element in list */
+ else {
+ qle = rtalloc(sizeof(struct quantile_llist_element));
+ RASTER_DEBUGF(4, "insert qle @ %p as last element", qle);
+ if (NULL == qle) return NULL;
+
+ qle->value = value;
+ qle->count = 1;
+
+ qle->prev = element;
+ qle->next = NULL;
+ element->next = qle;
+
+ return qle;
+ }
+ }
+ /* insert before current element */
+ else {
+ qle = rtalloc(sizeof(struct quantile_llist_element));
+ RASTER_DEBUGF(4, "insert qle @ %p before current element", qle);
+ if (NULL == qle) return NULL;
+
+ qle->value = value;
+ qle->count = 1;
+
+ if (NULL != element->prev) element->prev->next = qle;
+ qle->next = element;
+ qle->prev = element->prev;
+ element->prev = qle;
+
+ return qle;
+ }
+}
+
+static int quantile_llist_delete(struct quantile_llist_element *element) {
+ if (NULL == element) return 0;
+
+ /* beginning of list */
+ if (NULL == element->prev && NULL != element->next) {
+ element->next->prev = NULL;
+ }
+ /* end of list */
+ else if (NULL != element->prev && NULL == element->next) {
+ element->prev->next = NULL;
+ }
+ /* within list */
+ else if (NULL != element->prev && NULL != element->next) {
+ element->prev->next = element->next;
+ element->next->prev = element->prev;
+ }
+
+ RASTER_DEBUGF(4, "qle @ %p destroyed", element);
+ rtdealloc(element);
+
+ return 1;
+}
+
+int quantile_llist_destroy(struct quantile_llist **list, uint32_t list_count) {
+ struct quantile_llist_element *element = NULL;
+ uint32_t i;
+
+ if (NULL == *list) return 0;
+
+ for (i = 0; i < list_count; i++) {
+ element = (*list)[i].head;
+ while (NULL != element->next) {
+ quantile_llist_delete(element->next);
+ }
+ quantile_llist_delete(element);
+
+ rtdealloc((*list)[i].index);
+ }
+
+ rtdealloc(*list);
+ return 1;
+}
+
+static void quantile_llist_index_update(struct quantile_llist *qll, struct quantile_llist_element *qle, uint32_t idx) {
+ uint32_t anchor = (uint32_t) floor(idx / 100);
+
+ if (qll->tail == qle) return;
+
+ if (
+ (anchor != 0) && (
+ NULL == qll->index[anchor].element ||
+ idx <= qll->index[anchor].index
+ )
+ ) {
+ qll->index[anchor].index = idx;
+ qll->index[anchor].element = qle;
+ }
+
+ if (anchor != 0 && NULL == qll->index[0].element) {
+ qll->index[0].index = 0;
+ qll->index[0].element = qll->head;
+ }
+}
+
+static void quantile_llist_index_delete(struct quantile_llist *qll, struct quantile_llist_element *qle) {
+ uint32_t i = 0;
+
+ for (i = 0; i < qll->index_max; i++) {
+ if (
+ NULL == qll->index[i].element ||
+ (qll->index[i].element) != qle
+ ) {
+ continue;
+ }
+
+ RASTER_DEBUGF(5, "deleting index: %d => %f", i, qle->value);
+ qll->index[i].index = -1;
+ qll->index[i].element = NULL;
+ }
+}
+
+static struct quantile_llist_element *quantile_llist_index_search(
+ struct quantile_llist *qll,
+ double value,
+ uint32_t *index
+) {
+ uint32_t i = 0, j = 0;
+ RASTER_DEBUGF(5, "searching index for value %f", value);
+
+ for (i = 0; i < qll->index_max; i++) {
+ if (NULL == qll->index[i].element) {
+ if (i < 1) break;
+ continue;
+ }
+ if (value > (qll->index[i]).element->value) continue;
+
+ if (FLT_EQ(value, qll->index[i].element->value)) {
+ RASTER_DEBUGF(5, "using index value at %d = %f", i, qll->index[i].element->value);
+ *index = i * 100;
+ return qll->index[i].element;
+ }
+ else if (i > 0) {
+ for (j = 1; j < i; j++) {
+ if (NULL != qll->index[i - j].element) {
+ RASTER_DEBUGF(5, "using index value at %d = %f", i - j, qll->index[i - j].element->value);
+ *index = (i - j) * 100;
+ return qll->index[i - j].element;
+ }
+ }
+ }
+ }
+
+ *index = 0;
+ return qll->head;
+}
+
+static void quantile_llist_index_reset(struct quantile_llist *qll) {
+ uint32_t i = 0;
+
+ RASTER_DEBUG(5, "resetting index");
+ for (i = 0; i < qll->index_max; i++) {
+ qll->index[i].index = -1;
+ qll->index[i].element = NULL;
+ }
+}
+
+
+/**
+ * Compute the default set of or requested quantiles for a coverage
+ *
+ * This function is based upon the algorithm described in:
+ *
+ * A One-Pass Space-Efficient Algorithm for Finding Quantiles (1995)
+ * by Rakesh Agrawal, Arun Swami
+ * in Proc. 7th Intl. Conf. Management of Data (COMAD-95)
+ *
+ * http://www.almaden.ibm.com/cs/projects/iis/hdb/Publications/papers/comad95.pdf
+ *
+ * In the future, it may be worth exploring algorithms that don't
+ * require the size of the coverage
+ *
+ * @param band : the band to include in the quantile search
+ * @param exclude_nodata_value : if non-zero, ignore nodata values
+ * @param sample : percentage of pixels to sample
+ * @param cov_count : number of values in coverage
+ * @param qlls : set of quantile_llist structures
+ * @param qlls_count : the number of quantile_llist structures
+ * @param quantiles : the quantiles to be computed
+ * if bot qlls and quantiles provided, qlls is used
+ * @param quantiles_count : the number of quantiles to be computed
+ * @param rtn_count : the number of quantiles being returned
+ *
+ * @return the default set of or requested quantiles for a band or NULL
+ */
+rt_quantile
+rt_band_get_quantiles_stream(
+ rt_band band,
+ int exclude_nodata_value, double sample,
+ uint64_t cov_count,
+ struct quantile_llist **qlls, uint32_t *qlls_count,
+ double *quantiles, int quantiles_count,
+ uint32_t *rtn_count
+) {
+ rt_quantile rtn = NULL;
+ int init_quantiles = 0;
+
+ struct quantile_llist *qll = NULL;
+ struct quantile_llist_element *qle = NULL;
+ struct quantile_llist_element *qls = NULL;
+ const uint32_t MAX_VALUES = 750;
+
+ uint8_t *data = NULL;
+ double value;
+ int isnodata = 0;
+
+ uint32_t a = 0;
+ uint32_t i = 0;
+ uint32_t j = 0;
+ uint32_t k = 0;
+ uint32_t x = 0;
+ uint32_t y = 0;
+ uint32_t z = 0;
+ uint32_t idx = 0;
+ uint32_t offset = 0;
+ uint32_t diff = 0;
+ uint8_t exists = 0;
+
+ uint32_t do_sample = 0;
+ uint32_t sample_size = 0;
+ uint32_t sample_per = 0;
+ uint32_t sample_int = 0;
+ int status;
+
+ RASTER_DEBUG(3, "starting");
+
+ assert(NULL != band);
+ assert(cov_count > 1);
+ assert(NULL != rtn_count);
+ RASTER_DEBUGF(3, "cov_count = %d", cov_count);
+
+ data = rt_band_get_data(band);
+ if (data == NULL) {
+ rterror("rt_band_get_summary_stats: Cannot get band data");
+ return NULL;
+ }
+
+ if (!rt_band_get_hasnodata_flag(band))
+ exclude_nodata_value = 0;
+ RASTER_DEBUGF(3, "exclude_nodata_value = %d", exclude_nodata_value);
+
+ /* quantile_llist not provided */
+ if (NULL == *qlls) {
+ /* quantiles not provided */
+ if (NULL == quantiles) {
+ /* quantile count not specified, default to quartiles */
+ if (quantiles_count < 2)
+ quantiles_count = 5;
+
+ quantiles = rtalloc(sizeof(double) * quantiles_count);
+ init_quantiles = 1;
+ if (NULL == quantiles) {
+ rterror("rt_band_get_quantiles_stream: Could not allocate memory for quantile input");
+ return NULL;
+ }
+
+ quantiles_count--;
+ for (i = 0; i <= quantiles_count; i++)
+ quantiles[i] = ((double) i) / quantiles_count;
+ quantiles_count++;
+ }
+
+ /* check quantiles */
+ for (i = 0; i < quantiles_count; i++) {
+ if (quantiles[i] < 0. || quantiles[i] > 1.) {
+ rterror("rt_band_get_quantiles_stream: Quantile value not between 0 and 1");
+ if (init_quantiles) rtdealloc(quantiles);
+ return NULL;
+ }
+ }
+ quicksort(quantiles, quantiles + quantiles_count - 1);
+
+ /* initialize linked-list set */
+ *qlls_count = quantiles_count * 2;
+ RASTER_DEBUGF(4, "qlls_count = %d", *qlls_count);
+ *qlls = rtalloc(sizeof(struct quantile_llist) * *qlls_count);
+ if (NULL == *qlls) {
+ rterror("rt_band_get_quantiles_stream: Could not allocate memory for quantile output");
+ if (init_quantiles) rtdealloc(quantiles);
+ return NULL;
+ }
+
+ j = (uint32_t) floor(MAX_VALUES / 100.) + 1;
+ for (i = 0; i < *qlls_count; i++) {
+ qll = &((*qlls)[i]);
+ qll->quantile = quantiles[(i * quantiles_count) / *qlls_count];
+ qll->count = 0;
+ qll->sum1 = 0;
+ qll->sum2 = 0;
+ qll->head = NULL;
+ qll->tail = NULL;
+
+ /* initialize index */
+ qll->index = rtalloc(sizeof(struct quantile_llist_index) * j);
+ if (NULL == qll->index) {
+ rterror("rt_band_get_quantiles_stream: Could not allocate memory for quantile output");
+ if (init_quantiles) rtdealloc(quantiles);
+ return NULL;
+ }
+ qll->index_max = j;
+ quantile_llist_index_reset(qll);
+
+ /* AL-GEQ */
+ if (!(i % 2)) {
+ qll->algeq = 1;
+ qll->tau = (uint64_t) ROUND(cov_count - (cov_count * qll->quantile), 0);
+ if (qll->tau < 1) qll->tau = 1;
+ }
+ /* AL-GT */
+ else {
+ qll->algeq = 0;
+ qll->tau = cov_count - (*qlls)[i - 1].tau + 1;
+ }
+
+ RASTER_DEBUGF(4, "qll init: (algeq, quantile, count, tau, sum1, sum2) = (%d, %f, %d, %d, %d, %d)",
+ qll->algeq, qll->quantile, qll->count, qll->tau, qll->sum1, qll->sum2);
+ RASTER_DEBUGF(4, "qll init: (head, tail) = (%p, %p)", qll->head, qll->tail);
+ }
+
+ if (init_quantiles) rtdealloc(quantiles);
+ }
+
+ /* clamp percentage */
+ if (
+ (sample < 0 || FLT_EQ(sample, 0.0)) ||
+ (sample > 1 || FLT_EQ(sample, 1.0))
+ ) {
+ do_sample = 0;
+ sample = 1;
+ }
+ else
+ do_sample = 1;
+ RASTER_DEBUGF(3, "do_sample = %d", do_sample);
+
+ /* sample all pixels */
+ if (!do_sample) {
+ sample_size = band->width * band->height;
+ sample_per = band->height;
+ }
+ /*
+ randomly sample a percentage of available pixels
+ sampling method is known as
+ "systematic random sample without replacement"
+ */
+ else {
+ sample_size = round((band->width * band->height) * sample);
+ sample_per = round(sample_size / band->width);
+ sample_int = round(band->height / sample_per);
+ srand(time(NULL));
+ }
+ RASTER_DEBUGF(3, "sampling %d of %d available pixels w/ %d per set"
+ , sample_size, (band->width * band->height), sample_per);
+
+ for (x = 0, j = 0, k = 0; x < band->width; x++) {
+ y = -1;
+ diff = 0;
+
+ /* exclude_nodata_value = TRUE and band is NODATA */
+ if (exclude_nodata_value && rt_band_get_isnodata_flag(band)) {
+ RASTER_DEBUG(3, "Skipping quantile calcuation as band is NODATA");
+ break;
+ }
+
+ for (i = 0, z = 0; i < sample_per; i++) {
+ if (do_sample != 1)
+ y = i;
+ else {
+ offset = (rand() % sample_int) + 1;
+ y += diff + offset;
+ diff = sample_int - offset;
+ }
+ RASTER_DEBUGF(5, "(x, y, z) = (%d, %d, %d)", x, y, z);
+ if (y >= band->height || z > sample_per) break;
+
+ status = rt_band_get_pixel(band, x, y, &value, &isnodata);
+
+ j++;
+ if (status == ES_NONE && (!exclude_nodata_value || (exclude_nodata_value && !isnodata))) {
+
+ /* process each quantile */
+ for (a = 0; a < *qlls_count; a++) {
+ qll = &((*qlls)[a]);
+ qls = NULL;
+ RASTER_DEBUGF(4, "%d of %d (%f)", a + 1, *qlls_count, qll->quantile);
+ RASTER_DEBUGF(5, "qll before: (algeq, quantile, count, tau, sum1, sum2) = (%d, %f, %d, %d, %d, %d)",
+ qll->algeq, qll->quantile, qll->count, qll->tau, qll->sum1, qll->sum2);
+ RASTER_DEBUGF(5, "qll before: (head, tail) = (%p, %p)", qll->head, qll->tail);
+
+ /* OPTIMIZATION: shortcuts for quantiles of zero or one */
+ if (FLT_EQ(qll->quantile, 0.)) {
+ if (NULL != qll->head) {
+ if (value < qll->head->value)
+ qll->head->value = value;
+ }
+ else {
+ qle = quantile_llist_insert(qll->head, value, NULL);
+ qll->head = qle;
+ qll->tail = qle;
+ qll->count = 1;
+ }
+
+ RASTER_DEBUGF(4, "quantile shortcut for %f\n\n", qll->quantile);
+ continue;
+ }
+ else if (FLT_EQ(qll->quantile, 1.)) {
+ if (NULL != qll->head) {
+ if (value > qll->head->value)
+ qll->head->value = value;
+ }
+ else {
+ qle = quantile_llist_insert(qll->head, value, NULL);
+ qll->head = qle;
+ qll->tail = qle;
+ qll->count = 1;
+ }
+
+ RASTER_DEBUGF(4, "quantile shortcut for %f\n\n", qll->quantile);
+ continue;
+ }
+
+ /* value exists in list */
+ /* OPTIMIZATION: check to see if value exceeds last value */
+ if (NULL != qll->tail && value > qll->tail->value)
+ qle = NULL;
+ /* OPTIMIZATION: check to see if value equals last value */
+ else if (NULL != qll->tail && FLT_EQ(value, qll->tail->value))
+ qle = qll->tail;
+ /* OPTIMIZATION: use index if possible */
+ else {
+ qls = quantile_llist_index_search(qll, value, &idx);
+ qle = quantile_llist_search(qls, value);
+ }
+
+ /* value found */
+ if (NULL != qle) {
+ RASTER_DEBUGF(4, "%f found in list", value);
+ RASTER_DEBUGF(5, "qle before: (value, count) = (%f, %d)", qle->value, qle->count);
+
+ qle->count++;
+ qll->sum1++;
+
+ if (qll->algeq)
+ qll->sum2 = qll->sum1 - qll->head->count;
+ else
+ qll->sum2 = qll->sum1 - qll->tail->count;
+
+ RASTER_DEBUGF(4, "qle after: (value, count) = (%f, %d)", qle->value, qle->count);
+ }
+ /* can still add element */
+ else if (qll->count < MAX_VALUES) {
+ RASTER_DEBUGF(4, "Adding %f to list", value);
+
+ /* insert */
+ /* OPTIMIZATION: check to see if value exceeds last value */
+ if (NULL != qll->tail && (value > qll->tail->value || FLT_EQ(value, qll->tail->value))) {
+ idx = qll->count;
+ qle = quantile_llist_insert(qll->tail, value, &idx);
+ }
+ /* OPTIMIZATION: use index if possible */
+ else
+ qle = quantile_llist_insert(qls, value, &idx);
+ if (NULL == qle) return NULL;
+ RASTER_DEBUGF(5, "value added at index: %d => %f", idx, value);
+ qll->count++;
+ qll->sum1++;
+
+ /* first element */
+ if (NULL == qle->prev)
+ qll->head = qle;
+ /* last element */
+ if (NULL == qle->next)
+ qll->tail = qle;
+
+ if (qll->algeq)
+ qll->sum2 = qll->sum1 - qll->head->count;
+ else
+ qll->sum2 = qll->sum1 - qll->tail->count;
+
+ /* index is only needed if there are at least 100 values */
+ quantile_llist_index_update(qll, qle, idx);
+
+ RASTER_DEBUGF(5, "qle, prev, next, head, tail = %p, %p, %p, %p, %p", qle, qle->prev, qle->next, qll->head, qll->tail);
+ }
+ /* AL-GEQ */
+ else if (qll->algeq) {
+ RASTER_DEBUGF(4, "value, head->value = %f, %f", value, qll->head->value);
+
+ if (value < qll->head->value) {
+ /* ignore value if test isn't true */
+ if (qll->sum1 >= qll->tau) {
+ RASTER_DEBUGF(4, "skipping %f", value);
+ }
+ else {
+
+ /* delete last element */
+ RASTER_DEBUGF(4, "deleting %f from list", qll->tail->value);
+ qle = qll->tail->prev;
+ RASTER_DEBUGF(5, "to-be tail is %f with count %d", qle->value, qle->count);
+ qle->count += qll->tail->count;
+ quantile_llist_index_delete(qll, qll->tail);
+ quantile_llist_delete(qll->tail);
+ qll->tail = qle;
+ qll->count--;
+ RASTER_DEBUGF(5, "tail is %f with count %d", qll->tail->value, qll->tail->count);
+
+ /* insert value */
+ RASTER_DEBUGF(4, "adding %f to list", value);
+ /* OPTIMIZATION: check to see if value exceeds last value */
+ if (NULL != qll->tail && (value > qll->tail->value || FLT_EQ(value, qll->tail->value))) {
+ idx = qll->count;
+ qle = quantile_llist_insert(qll->tail, value, &idx);
+ }
+ /* OPTIMIZATION: use index if possible */
+ else {
+ qls = quantile_llist_index_search(qll, value, &idx);
+ qle = quantile_llist_insert(qls, value, &idx);
+ }
+ if (NULL == qle) return NULL;
+ RASTER_DEBUGF(5, "value added at index: %d => %f", idx, value);
+ qll->count++;
+ qll->sum1++;
+
+ /* first element */
+ if (NULL == qle->prev)
+ qll->head = qle;
+ /* last element */
+ if (NULL == qle->next)
+ qll->tail = qle;
+
+ qll->sum2 = qll->sum1 - qll->head->count;
+
+ quantile_llist_index_update(qll, qle, idx);
+
+ RASTER_DEBUGF(5, "qle, head, tail = %p, %p, %p", qle, qll->head, qll->tail);
+
+ }
+ }
+ else {
+ qle = qll->tail;
+ while (NULL != qle) {
+ if (qle->value < value) {
+ qle->count++;
+ qll->sum1++;
+ qll->sum2 = qll->sum1 - qll->head->count;
+ RASTER_DEBUGF(4, "incremented count of %f by 1 to %d", qle->value, qle->count);
+ break;
+ }
+
+ qle = qle->prev;
+ }
+ }
+ }
+ /* AL-GT */
+ else {
+ RASTER_DEBUGF(4, "value, tail->value = %f, %f", value, qll->tail->value);
+
+ if (value > qll->tail->value) {
+ /* ignore value if test isn't true */
+ if (qll->sum1 >= qll->tau) {
+ RASTER_DEBUGF(4, "skipping %f", value);
+ }
+ else {
+
+ /* delete last element */
+ RASTER_DEBUGF(4, "deleting %f from list", qll->head->value);
+ qle = qll->head->next;
+ RASTER_DEBUGF(5, "to-be tail is %f with count %d", qle->value, qle->count);
+ qle->count += qll->head->count;
+ quantile_llist_index_delete(qll, qll->head);
+ quantile_llist_delete(qll->head);
+ qll->head = qle;
+ qll->count--;
+ quantile_llist_index_update(qll, NULL, 0);
+ RASTER_DEBUGF(5, "tail is %f with count %d", qll->head->value, qll->head->count);
+
+ /* insert value */
+ RASTER_DEBUGF(4, "adding %f to list", value);
+ /* OPTIMIZATION: check to see if value exceeds last value */
+ if (NULL != qll->tail && (value > qll->tail->value || FLT_EQ(value, qll->tail->value))) {
+ idx = qll->count;
+ qle = quantile_llist_insert(qll->tail, value, &idx);
+ }
+ /* OPTIMIZATION: use index if possible */
+ else {
+ qls = quantile_llist_index_search(qll, value, &idx);
+ qle = quantile_llist_insert(qls, value, &idx);
+ }
+ if (NULL == qle) return NULL;
+ RASTER_DEBUGF(5, "value added at index: %d => %f", idx, value);
+ qll->count++;
+ qll->sum1++;
+
+ /* first element */
+ if (NULL == qle->prev)
+ qll->head = qle;
+ /* last element */
+ if (NULL == qle->next)
+ qll->tail = qle;
+
+ qll->sum2 = qll->sum1 - qll->tail->count;
+
+ quantile_llist_index_update(qll, qle, idx);
+
+ RASTER_DEBUGF(5, "qle, head, tail = %p, %p, %p", qle, qll->head, qll->tail);
+
+ }
+ }
+ else {
+ qle = qll->head;
+ while (NULL != qle) {
+ if (qle->value > value) {
+ qle->count++;
+ qll->sum1++;
+ qll->sum2 = qll->sum1 - qll->tail->count;
+ RASTER_DEBUGF(4, "incremented count of %f by 1 to %d", qle->value, qle->count);
+ break;
+ }
+
+ qle = qle->next;
+ }
+ }
+ }
+
+ RASTER_DEBUGF(5, "sum2, tau = %d, %d", qll->sum2, qll->tau);
+ if (qll->sum2 >= qll->tau) {
+ /* AL-GEQ */
+ if (qll->algeq) {
+ RASTER_DEBUGF(4, "deleting first element %f from list", qll->head->value);
+
+ if (NULL != qll->head->next) {
+ qle = qll->head->next;
+ qll->sum1 -= qll->head->count;
+ qll->sum2 = qll->sum1 - qle->count;
+ quantile_llist_index_delete(qll, qll->head);
+ quantile_llist_delete(qll->head);
+ qll->head = qle;
+ qll->count--;
+
+ quantile_llist_index_update(qll, NULL, 0);
+ }
+ else {
+ quantile_llist_delete(qll->head);
+ qll->head = NULL;
+ qll->tail = NULL;
+ qll->sum1 = 0;
+ qll->sum2 = 0;
+ qll->count = 0;
+
+ quantile_llist_index_reset(qll);
+ }
+ }
+ /* AL-GT */
+ else {
+ RASTER_DEBUGF(4, "deleting first element %f from list", qll->tail->value);
+
+ if (NULL != qll->tail->prev) {
+ qle = qll->tail->prev;
+ qll->sum1 -= qll->tail->count;
+ qll->sum2 = qll->sum1 - qle->count;
+ quantile_llist_index_delete(qll, qll->tail);
+ quantile_llist_delete(qll->tail);
+ qll->tail = qle;
+ qll->count--;
+ }
+ else {
+ quantile_llist_delete(qll->tail);
+ qll->head = NULL;
+ qll->tail = NULL;
+ qll->sum1 = 0;
+ qll->sum2 = 0;
+ qll->count = 0;
+
+ quantile_llist_index_reset(qll);
+ }
+ }
+ }
+
+ RASTER_DEBUGF(5, "qll after: (algeq, quantile, count, tau, sum1, sum2) = (%d, %f, %d, %d, %d, %d)",
+ qll->algeq, qll->quantile, qll->count, qll->tau, qll->sum1, qll->sum2);
+ RASTER_DEBUGF(5, "qll after: (head, tail) = (%p, %p)\n\n", qll->head, qll->tail);
+ }
+
+ }
+ else {
+ RASTER_DEBUGF(5, "skipping value at (x, y) = (%d, %d)", x, y);
+ }
+
+ z++;
+ }
+ }
+
+ /* process quantiles */
+ *rtn_count = *qlls_count / 2;
+ rtn = rtalloc(sizeof(struct rt_quantile_t) * *rtn_count);
+ if (NULL == rtn) return NULL;
+
+ RASTER_DEBUGF(3, "returning %d quantiles", *rtn_count);
+ for (i = 0, k = 0; i < *qlls_count; i++) {
+ qll = &((*qlls)[i]);
+
+ exists = 0;
+ for (x = 0; x < k; x++) {
+ if (FLT_EQ(qll->quantile, rtn[x].quantile)) {
+ exists = 1;
+ break;
+ }
+ }
+ if (exists) continue;
+
+ RASTER_DEBUGF(5, "qll: (algeq, quantile, count, tau, sum1, sum2) = (%d, %f, %d, %d, %d, %d)",
+ qll->algeq, qll->quantile, qll->count, qll->tau, qll->sum1, qll->sum2);
+ RASTER_DEBUGF(5, "qll: (head, tail) = (%p, %p)", qll->head, qll->tail);
+
+ rtn[k].quantile = qll->quantile;
+ rtn[k].has_value = 0;
+
+ /* check that qll->head and qll->tail have value */
+ if (qll->head == NULL || qll->tail == NULL)
+ continue;
+
+ /* AL-GEQ */
+ if (qll->algeq)
+ qle = qll->head;
+ /* AM-GT */
+ else
+ qle = qll->tail;
+
+ exists = 0;
+ for (j = i + 1; j < *qlls_count; j++) {
+ if (FLT_EQ((*qlls)[j].quantile, qll->quantile)) {
+
+ RASTER_DEBUGF(5, "qlls[%d]: (algeq, quantile, count, tau, sum1, sum2) = (%d, %f, %d, %d, %d, %d)",
+ j, (*qlls)[j].algeq, (*qlls)[j].quantile, (*qlls)[j].count, (*qlls)[j].tau, (*qlls)[j].sum1, (*qlls)[j].sum2);
+ RASTER_DEBUGF(5, "qlls[%d]: (head, tail) = (%p, %p)", j, (*qlls)[j].head, (*qlls)[j].tail);
+
+ exists = 1;
+ break;
+ }
+ }
+
+ /* weighted average for quantile */
+ if (exists) {
+ if ((*qlls)[j].algeq) {
+ rtn[k].value = ((qle->value * qle->count) + ((*qlls)[j].head->value * (*qlls)[j].head->count)) / (qle->count + (*qlls)[j].head->count);
+ RASTER_DEBUGF(5, "qlls[%d].head: (value, count) = (%f, %d)", j, (*qlls)[j].head->value, (*qlls)[j].head->count);
+ }
+ else {
+ rtn[k].value = ((qle->value * qle->count) + ((*qlls)[j].tail->value * (*qlls)[j].tail->count)) / (qle->count + (*qlls)[j].tail->count);
+ RASTER_DEBUGF(5, "qlls[%d].tail: (value, count) = (%f, %d)", j, (*qlls)[j].tail->value, (*qlls)[j].tail->count);
+ }
+ }
+ /* straight value for quantile */
+ else {
+ rtn[k].value = qle->value;
+ }
+ rtn[k].has_value = 1;
+ RASTER_DEBUGF(3, "(quantile, value) = (%f, %f)\n\n", rtn[k].quantile, rtn[k].value);
+
+ k++;
+ }
+
+ RASTER_DEBUG(3, "done");
+ return rtn;
+}
+
+/******************************************************************************
+* rt_band_get_value_count()
+******************************************************************************/
+
+/**
+ * Count the number of times provided value(s) occur in
+ * the band
+ *
+ * @param band : the band to query for minimum and maximum pixel values
+ * @param exclude_nodata_value : if non-zero, ignore nodata values
+ * @param search_values : array of values to count
+ * @param search_values_count : the number of search values
+ * @param roundto : the decimal place to round the values to
+ * @param rtn_total : the number of pixels examined in the band
+ * @param rtn_count : the number of value counts being returned
+ *
+ * @return the number of times the provide value(s) occur or NULL
+ */
+rt_valuecount
+rt_band_get_value_count(
+ rt_band band, int exclude_nodata_value,
+ double *search_values, uint32_t search_values_count, double roundto,
+ uint32_t *rtn_total, uint32_t *rtn_count
+) {
+ rt_valuecount vcnts = NULL;
+ rt_pixtype pixtype = PT_END;
+ uint8_t *data = NULL;
+ double nodata = 0;
+
+ int scale = 0;
+ int doround = 0;
+ double tmpd = 0;
+ int i = 0;
+
+ uint32_t x = 0;
+ uint32_t y = 0;
+ int rtn;
+ double pxlval;
+ int isnodata = 0;
+ double rpxlval;
+ uint32_t total = 0;
+ int vcnts_count = 0;
+ int new_valuecount = 0;
+
+#if POSTGIS_DEBUG_LEVEL > 0
+ clock_t start, stop;
+ double elapsed = 0;
+#endif
+
+ RASTER_DEBUG(3, "starting");
+#if POSTGIS_DEBUG_LEVEL > 0
+ start = clock();
+#endif
+
+ assert(NULL != band);
+ assert(NULL != rtn_count);
+
+ data = rt_band_get_data(band);
+ if (data == NULL) {
+ rterror("rt_band_get_summary_stats: Cannot get band data");
+ return NULL;
+ }
+
+ pixtype = band->pixtype;
+
+ if (rt_band_get_hasnodata_flag(band)) {
+ rt_band_get_nodata(band, &nodata);
+ RASTER_DEBUGF(3, "hasnodata, nodataval = 1, %f", nodata);
+ }
+ else {
+ exclude_nodata_value = 0;
+ RASTER_DEBUG(3, "hasnodata, nodataval = 0, 0");
+ }
+
+ RASTER_DEBUGF(3, "exclude_nodata_value = %d", exclude_nodata_value);
+
+ /* process roundto */
+ if (roundto < 0 || FLT_EQ(roundto, 0.0)) {
+ roundto = 0;
+ scale = 0;
+ }
+ /* tenths, hundredths, thousandths, etc */
+ else if (roundto < 1) {
+ switch (pixtype) {
+ /* integer band types don't have digits after the decimal place */
+ case PT_1BB:
+ case PT_2BUI:
+ case PT_4BUI:
+ case PT_8BSI:
+ case PT_8BUI:
+ case PT_16BSI:
+ case PT_16BUI:
+ case PT_32BSI:
+ case PT_32BUI:
+ roundto = 0;
+ break;
+ /* floating points, check the rounding */
+ case PT_32BF:
+ case PT_64BF:
+ for (scale = 0; scale <= 20; scale++) {
+ tmpd = roundto * pow(10, scale);
+ if (FLT_EQ((tmpd - ((int) tmpd)), 0.0)) break;
+ }
+ break;
+ case PT_END:
+ break;
+ }
+ }
+ /* ones, tens, hundreds, etc */
+ else {
+ for (scale = 0; scale >= -20; scale--) {
+ tmpd = roundto * pow(10, scale);
+ if (tmpd < 1 || FLT_EQ(tmpd, 1.0)) {
+ if (scale == 0) doround = 1;
+ break;
+ }
+ }
+ }
+
+ if (scale != 0 || doround)
+ doround = 1;
+ else
+ doround = 0;
+ RASTER_DEBUGF(3, "scale = %d", scale);
+ RASTER_DEBUGF(3, "doround = %d", doround);
+
+ /* process search_values */
+ if (search_values_count > 0 && NULL != search_values) {
+ vcnts = (rt_valuecount) rtalloc(sizeof(struct rt_valuecount_t) * search_values_count);
+ if (NULL == vcnts) {
+ rterror("rt_band_get_count_of_values: Could not allocate memory for value counts");
+ *rtn_count = 0;
+ return NULL;
+ }
+
+ for (i = 0; i < search_values_count; i++) {
+ vcnts[i].count = 0;
+ vcnts[i].percent = 0;
+ if (!doround)
+ vcnts[i].value = search_values[i];
+ else
+ vcnts[i].value = ROUND(search_values[i], scale);
+ }
+ vcnts_count = i;
+ }
+ else
+ search_values_count = 0;
+ RASTER_DEBUGF(3, "search_values_count = %d", search_values_count);
+
+ /* entire band is nodata */
+ if (rt_band_get_isnodata_flag(band) != FALSE) {
+ if (exclude_nodata_value) {
+ rtwarn("All pixels of band have the NODATA value");
+ return NULL;
+ }
+ else {
+ if (search_values_count > 0) {
+ /* check for nodata match */
+ for (i = 0; i < search_values_count; i++) {
+ if (!doround)
+ tmpd = nodata;
+ else
+ tmpd = ROUND(nodata, scale);
+
+ if (FLT_NEQ(tmpd, vcnts[i].value))
+ continue;
+
+ vcnts[i].count = band->width * band->height;
+ if (NULL != rtn_total) *rtn_total = vcnts[i].count;
+ vcnts->percent = 1.0;
+ }
+
+ *rtn_count = vcnts_count;
+ }
+ /* no defined search values */
+ else {
+ vcnts = (rt_valuecount) rtalloc(sizeof(struct rt_valuecount_t));
+ if (NULL == vcnts) {
+ rterror("rt_band_get_count_of_values: Could not allocate memory for value counts");
+ *rtn_count = 0;
+ return NULL;
+ }
+
+ vcnts->value = nodata;
+ vcnts->count = band->width * band->height;
+ if (NULL != rtn_total) *rtn_total = vcnts[i].count;
+ vcnts->percent = 1.0;
+
+ *rtn_count = 1;
+ }
+
+ return vcnts;
+ }
+ }
+
+ for (x = 0; x < band->width; x++) {
+ for (y = 0; y < band->height; y++) {
+ rtn = rt_band_get_pixel(band, x, y, &pxlval, &isnodata);
+
+ /* error getting value, continue */
+ if (rtn != ES_NONE)
+ continue;
+
+ if (!exclude_nodata_value || (exclude_nodata_value && !isnodata)) {
+ total++;
+ if (doround) {
+ rpxlval = ROUND(pxlval, scale);
+ }
+ else
+ rpxlval = pxlval;
+ RASTER_DEBUGF(5, "(pxlval, rpxlval) => (%0.6f, %0.6f)", pxlval, rpxlval);
+
+ new_valuecount = 1;
+ /* search for match in existing valuecounts */
+ for (i = 0; i < vcnts_count; i++) {
+ /* match found */
+ if (FLT_EQ(vcnts[i].value, rpxlval)) {
+ vcnts[i].count++;
+ new_valuecount = 0;
+ RASTER_DEBUGF(5, "(value, count) => (%0.6f, %d)", vcnts[i].value, vcnts[i].count);
+ break;
+ }
+ }
+
+ /*
+ don't add new valuecount either because
+ - no need for new one
+ - user-defined search values
+ */
+ if (!new_valuecount || search_values_count > 0) continue;
+
+ /* add new valuecount */
+ vcnts = rtrealloc(vcnts, sizeof(struct rt_valuecount_t) * (vcnts_count + 1));
+ if (NULL == vcnts) {
+ rterror("rt_band_get_count_of_values: Could not allocate memory for value counts");
+ *rtn_count = 0;
+ return NULL;
+ }
+
+ vcnts[vcnts_count].value = rpxlval;
+ vcnts[vcnts_count].count = 1;
+ vcnts[vcnts_count].percent = 0;
+ RASTER_DEBUGF(5, "(value, count) => (%0.6f, %d)", vcnts[vcnts_count].value, vcnts[vcnts_count].count);
+ vcnts_count++;
+ }
+ }
+ }
+
+#if POSTGIS_DEBUG_LEVEL > 0
+ stop = clock();
+ elapsed = ((double) (stop - start)) / CLOCKS_PER_SEC;
+ RASTER_DEBUGF(3, "elapsed time = %0.4f", elapsed);
+#endif
+
+ for (i = 0; i < vcnts_count; i++) {
+ vcnts[i].percent = (double) vcnts[i].count / total;
+ RASTER_DEBUGF(5, "(value, count) => (%0.6f, %d)", vcnts[i].value, vcnts[i].count);
+ }
+
+ RASTER_DEBUG(3, "done");
+ if (NULL != rtn_total) *rtn_total = total;
+ *rtn_count = vcnts_count;
+ return vcnts;
+}
--- /dev/null
+/*
+ * $Id$
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "librtcore.h"
+#include "librtcore_internal.h"
+
+uint8_t
+rt_util_clamp_to_1BB(double value) {
+ return (uint8_t)fmin(fmax((value), 0), POSTGIS_RT_1BBMAX);
+}
+
+uint8_t
+rt_util_clamp_to_2BUI(double value) {
+ return (uint8_t)fmin(fmax((value), 0), POSTGIS_RT_2BUIMAX);
+}
+
+uint8_t
+rt_util_clamp_to_4BUI(double value) {
+ return (uint8_t)fmin(fmax((value), 0), POSTGIS_RT_4BUIMAX);
+}
+
+int8_t
+rt_util_clamp_to_8BSI(double value) {
+ return (int8_t)fmin(fmax((value), SCHAR_MIN), SCHAR_MAX);
+}
+
+uint8_t
+rt_util_clamp_to_8BUI(double value) {
+ return (uint8_t)fmin(fmax((value), 0), UCHAR_MAX);
+}
+
+int16_t
+rt_util_clamp_to_16BSI(double value) {
+ return (int16_t)fmin(fmax((value), SHRT_MIN), SHRT_MAX);
+}
+
+uint16_t
+rt_util_clamp_to_16BUI(double value) {
+ return (uint16_t)fmin(fmax((value), 0), USHRT_MAX);
+}
+
+int32_t
+rt_util_clamp_to_32BSI(double value) {
+ return (int32_t)fmin(fmax((value), INT_MIN), INT_MAX);
+}
+
+uint32_t
+rt_util_clamp_to_32BUI(double value) {
+ return (uint32_t)fmin(fmax((value), 0), UINT_MAX);
+}
+
+float
+rt_util_clamp_to_32F(double value) {
+ return (float)fmin(fmax((value), -FLT_MAX), FLT_MAX);
+}
+
+/**
+ * Convert cstring name to GDAL Resample Algorithm
+ *
+ * @param algname : cstring name to convert
+ *
+ * @return valid GDAL resampling algorithm
+ */
+GDALResampleAlg
+rt_util_gdal_resample_alg(const char *algname) {
+ assert(algname != NULL && strlen(algname) > 0);
+
+ if (strcmp(algname, "NEARESTNEIGHBOUR") == 0)
+ return GRA_NearestNeighbour;
+ else if (strcmp(algname, "NEARESTNEIGHBOR") == 0)
+ return GRA_NearestNeighbour;
+ else if (strcmp(algname, "BILINEAR") == 0)
+ return GRA_Bilinear;
+ else if (strcmp(algname, "CUBICSPLINE") == 0)
+ return GRA_CubicSpline;
+ else if (strcmp(algname, "CUBIC") == 0)
+ return GRA_Cubic;
+ else if (strcmp(algname, "LANCZOS") == 0)
+ return GRA_Lanczos;
+
+ return GRA_NearestNeighbour;
+}
+
+/**
+ * Convert rt_pixtype to GDALDataType
+ *
+ * @param pt : pixeltype to convert
+ *
+ * @return valid GDALDataType
+ */
+GDALDataType
+rt_util_pixtype_to_gdal_datatype(rt_pixtype pt) {
+ switch (pt) {
+ case PT_1BB:
+ case PT_2BUI:
+ case PT_4BUI:
+ case PT_8BUI:
+ return GDT_Byte;
+ case PT_8BSI:
+ case PT_16BSI:
+ return GDT_Int16;
+ case PT_16BUI:
+ return GDT_UInt16;
+ case PT_32BSI:
+ return GDT_Int32;
+ case PT_32BUI:
+ return GDT_UInt32;
+ case PT_32BF:
+ return GDT_Float32;
+ case PT_64BF:
+ return GDT_Float64;
+ default:
+ return GDT_Unknown;
+ }
+
+ return GDT_Unknown;
+}
+
+/**
+ * Convert GDALDataType to rt_pixtype
+ *
+ * @param gdt : GDAL datatype to convert
+ *
+ * @return valid rt_pixtype
+ */
+rt_pixtype
+rt_util_gdal_datatype_to_pixtype(GDALDataType gdt) {
+ switch (gdt) {
+ case GDT_Byte:
+ return PT_8BUI;
+ case GDT_UInt16:
+ return PT_16BUI;
+ case GDT_Int16:
+ return PT_16BSI;
+ case GDT_UInt32:
+ return PT_32BUI;
+ case GDT_Int32:
+ return PT_32BSI;
+ case GDT_Float32:
+ return PT_32BF;
+ case GDT_Float64:
+ return PT_64BF;
+ default:
+ return PT_END;
+ }
+
+ return PT_END;
+}
+
+/*
+ get GDAL runtime version information
+*/
+const char*
+rt_util_gdal_version(const char *request) {
+ if (NULL == request || !strlen(request))
+ return GDALVersionInfo("RELEASE_NAME");
+ else
+ return GDALVersionInfo(request);
+}
+
+/*
+ computed extent type
+*/
+rt_extenttype
+rt_util_extent_type(const char *name) {
+ assert(name != NULL && strlen(name) > 0);
+
+ if (strcmp(name, "UNION") == 0)
+ return ET_UNION;
+ else if (strcmp(name, "FIRST") == 0)
+ return ET_FIRST;
+ else if (strcmp(name, "SECOND") == 0)
+ return ET_SECOND;
+ else if (strcmp(name, "LAST") == 0)
+ return ET_LAST;
+ else if (strcmp(name, "CUSTOM") == 0)
+ return ET_CUSTOM;
+ else
+ return ET_INTERSECTION;
+}
+
+/*
+ convert the spatial reference string from a GDAL recognized format to either WKT or Proj4
+*/
+char*
+rt_util_gdal_convert_sr(const char *srs, int proj4) {
+ OGRSpatialReferenceH hsrs;
+ char *rtn = NULL;
+
+ assert(srs != NULL);
+
+ hsrs = OSRNewSpatialReference(NULL);
+ if (OSRSetFromUserInput(hsrs, srs) == OGRERR_NONE) {
+ if (proj4)
+ OSRExportToProj4(hsrs, &rtn);
+ else
+ OSRExportToWkt(hsrs, &rtn);
+ }
+ else {
+ rterror("rt_util_gdal_convert_sr: Could not process the provided srs: %s", srs);
+ return NULL;
+ }
+
+ OSRDestroySpatialReference(hsrs);
+ if (rtn == NULL) {
+ rterror("rt_util_gdal_convert_sr: Could not process the provided srs: %s", srs);
+ return NULL;
+ }
+
+ return rtn;
+}
+
+/*
+ is the spatial reference string supported by GDAL
+*/
+int
+rt_util_gdal_supported_sr(const char *srs) {
+ OGRSpatialReferenceH hsrs;
+ OGRErr rtn = OGRERR_NONE;
+
+ assert(srs != NULL);
+
+ hsrs = OSRNewSpatialReference(NULL);
+ rtn = OSRSetFromUserInput(hsrs, srs);
+ OSRDestroySpatialReference(hsrs);
+
+ if (rtn == OGRERR_NONE)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * Get auth name and code
+ *
+ * @param authname: authority organization of code. calling function
+ * is expected to free the memory allocated for value
+ * @param authcode: code assigned by authority organization. calling function
+ * is expected to free the memory allocated for value
+ *
+ * @return ES_NONE on success, ES_ERROR on error
+ */
+rt_errorstate
+rt_util_gdal_sr_auth_info(GDALDatasetH hds, char **authname, char **authcode) {
+ const char *srs = NULL;
+
+ assert(authname != NULL);
+ assert(authcode != NULL);
+
+ *authname = NULL;
+ *authcode = NULL;
+
+ srs = GDALGetProjectionRef(hds);
+ if (srs != NULL && srs[0] != '\0') {
+ OGRSpatialReferenceH hSRS = OSRNewSpatialReference(NULL);
+
+ if (OSRSetFromUserInput(hSRS, srs) == OGRERR_NONE) {
+ const char* pszAuthorityName = OSRGetAuthorityName(hSRS, NULL);
+ const char* pszAuthorityCode = OSRGetAuthorityCode(hSRS, NULL);
+
+ if (pszAuthorityName != NULL && pszAuthorityCode != NULL) {
+ *authname = rtalloc(sizeof(char) * (strlen(pszAuthorityName) + 1));
+ *authcode = rtalloc(sizeof(char) * (strlen(pszAuthorityCode) + 1));
+
+ if (*authname == NULL || *authcode == NULL) {
+ rterror("rt_util_gdal_sr_auth_info: Could not allocate memory for auth name and code");
+ if (*authname != NULL) rtdealloc(*authname);
+ if (*authcode != NULL) rtdealloc(*authcode);
+ OSRDestroySpatialReference(hSRS);
+ return ES_ERROR;
+ }
+
+ strncpy(*authname, pszAuthorityName, strlen(pszAuthorityName) + 1);
+ strncpy(*authcode, pszAuthorityCode, strlen(pszAuthorityCode) + 1);
+ }
+ }
+
+ OSRDestroySpatialReference(hSRS);
+ }
+
+ return ES_NONE;
+}
+
+/*
+ is GDAL configured correctly?
+*/
+int rt_util_gdal_configured(void) {
+
+ /* set of EPSG codes */
+ if (!rt_util_gdal_supported_sr("EPSG:4326"))
+ return 0;
+ if (!rt_util_gdal_supported_sr("EPSG:4269"))
+ return 0;
+ if (!rt_util_gdal_supported_sr("EPSG:4267"))
+ return 0;
+ if (!rt_util_gdal_supported_sr("EPSG:3310"))
+ return 0;
+ if (!rt_util_gdal_supported_sr("EPSG:2163"))
+ return 0;
+
+ return 1;
+}
+
+/*
+ register all GDAL drivers
+*/
+void
+rt_util_gdal_register_all(void) {
+ static int registered = 0;
+
+ if (registered)
+ return;
+
+ GDALAllRegister();
+ registered = 1;
+}
+
+/*
+ is the driver registered?
+*/
+int
+rt_util_gdal_driver_registered(const char *drv) {
+ int count = GDALGetDriverCount();
+ int i = 0;
+ GDALDriverH hdrv = NULL;
+
+ if (drv == NULL || !strlen(drv) || count < 1)
+ return 0;
+
+ for (i = 0; i < count; i++) {
+ hdrv = GDALGetDriver(i);
+ if (hdrv == NULL) continue;
+
+ if (strcmp(drv, GDALGetDriverShortName(hdrv)) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+rt_util_from_ogr_envelope(
+ OGREnvelope env,
+ rt_envelope *ext
+) {
+ assert(ext != NULL);
+
+ ext->MinX = env.MinX;
+ ext->MaxX = env.MaxX;
+ ext->MinY = env.MinY;
+ ext->MaxY = env.MaxY;
+
+ ext->UpperLeftX = env.MinX;
+ ext->UpperLeftY = env.MaxY;
+}
+
+void
+rt_util_to_ogr_envelope(
+ rt_envelope ext,
+ OGREnvelope *env
+) {
+ assert(env != NULL);
+
+ env->MinX = ext.MinX;
+ env->MaxX = ext.MaxX;
+ env->MinY = ext.MinY;
+ env->MaxY = ext.MaxY;
+}
+
+LWPOLY *
+rt_util_envelope_to_lwpoly(
+ rt_envelope env
+) {
+ LWPOLY *npoly = NULL;
+ POINTARRAY **rings = NULL;
+ POINTARRAY *pts = NULL;
+ POINT4D p4d;
+
+ rings = (POINTARRAY **) rtalloc(sizeof (POINTARRAY*));
+ if (!rings) {
+ rterror("rt_util_envelope_to_lwpoly: Out of memory building envelope's geometry");
+ return NULL;
+ }
+ rings[0] = ptarray_construct(0, 0, 5);
+ if (!rings[0]) {
+ rterror("rt_util_envelope_to_lwpoly: Out of memory building envelope's geometry ring");
+ return NULL;
+ }
+
+ pts = rings[0];
+
+ /* Upper-left corner (first and last points) */
+ p4d.x = env.MinX;
+ p4d.y = env.MaxY;
+ ptarray_set_point4d(pts, 0, &p4d);
+ ptarray_set_point4d(pts, 4, &p4d);
+
+ /* Upper-right corner (we go clockwise) */
+ p4d.x = env.MaxX;
+ p4d.y = env.MaxY;
+ ptarray_set_point4d(pts, 1, &p4d);
+
+ /* Lower-right corner */
+ p4d.x = env.MaxX;
+ p4d.y = env.MinY;
+ ptarray_set_point4d(pts, 2, &p4d);
+
+ /* Lower-left corner */
+ p4d.x = env.MinX;
+ p4d.y = env.MinY;
+ ptarray_set_point4d(pts, 3, &p4d);
+
+ npoly = lwpoly_construct(SRID_UNKNOWN, 0, 1, rings);
+ if (npoly == NULL) {
+ rterror("rt_util_envelope_to_lwpoly: Could not build envelope's geometry");
+ return NULL;
+ }
+
+ return npoly;
+}
+
+int
+rt_util_same_geotransform_matrix(double *gt1, double *gt2) {
+ int k = 0;
+
+ if (gt1 == NULL || gt2 == NULL)
+ return FALSE;
+
+ for (k = 0; k < 6; k++) {
+ if (FLT_NEQ(gt1[k], gt2[k]))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* coordinates in RGB and HSV are floating point values between 0 and 1 */
+rt_errorstate
+rt_util_rgb_to_hsv(double rgb[3], double hsv[3]) {
+ int i;
+
+ double minc;
+ double maxc;
+
+ double h = 0.;
+ double s = 0.;
+ double v = 0.;
+
+ minc = rgb[0];
+ maxc = rgb[0];
+
+ /* get min and max values from RGB */
+ for (i = 1; i < 3; i++) {
+ if (rgb[i] > maxc)
+ maxc = rgb[i];
+ if (rgb[i] < minc)
+ minc = rgb[i];
+ }
+ v = maxc;
+
+ if (maxc != minc) {
+ double diff = 0.;
+ double rc = 0.;
+ double gc = 0.;
+ double bc = 0.;
+ double junk = 0.;
+
+ diff = maxc - minc;
+ s = diff / maxc;
+ rc = (maxc - rgb[0]) / diff;
+ gc = (maxc - rgb[1]) / diff;
+ bc = (maxc - rgb[2]) / diff;
+
+ if (DBL_EQ(rgb[0], maxc))
+ h = bc - gc;
+ else if (DBL_EQ(rgb[1], maxc))
+ h = 2.0 + rc - bc;
+ else
+ h = 4.0 + gc - rc;
+
+ h = modf((h / 6.0), &junk);
+ }
+
+ hsv[0] = h;
+ hsv[1] = s;
+ hsv[2] = v;
+
+ return ES_NONE;
+}
+
+/* coordinates in RGB and HSV are floating point values between 0 and 1 */
+rt_errorstate
+rt_util_hsv_to_rgb(double hsv[3], double rgb[3]) {
+ double r = 0;
+ double g = 0;
+ double b = 0;
+ double v = hsv[2];
+
+ if (DBL_EQ(hsv[1], 0.))
+ r = g = b = v;
+ else {
+ double i;
+ double f;
+ double p;
+ double q;
+ double t;
+
+ int a;
+
+ i = floor(hsv[0] * 6.);
+ f = (hsv[0] * 6.0) - i;
+ p = v * (1. - hsv[1]);
+ q = v * (1. - hsv[1] * f);
+ t = v * (1. - hsv[1] * (1. - f));
+
+ a = (int) i;
+ switch (a) {
+ case 1:
+ r = q;
+ g = v;
+ b = p;
+ break;
+ case 2:
+ r = p;
+ g = v;
+ b = t;
+ break;
+ case 3:
+ r = p;
+ g = q;
+ b = v;
+ break;
+ case 4:
+ r = t;
+ g = p;
+ b = v;
+ break;
+ case 5:
+ r = v;
+ g = p;
+ b = q;
+ break;
+ case 0:
+ case 6:
+ default:
+ r = v;
+ g = t;
+ b = p;
+ break;
+ }
+ }
+
+ rgb[0] = r;
+ rgb[1] = g;
+ rgb[2] = b;
+
+ return ES_NONE;
+}
+
+int
+rt_util_dbl_trunc_warning(
+ double initialvalue,
+ int32_t checkvalint, uint32_t checkvaluint,
+ float checkvalfloat, double checkvaldouble,
+ rt_pixtype pixtype
+) {
+ int result = 0;
+
+ switch (pixtype) {
+ case PT_1BB:
+ case PT_2BUI:
+ case PT_4BUI:
+ case PT_8BSI:
+ case PT_8BUI:
+ case PT_16BSI:
+ case PT_16BUI:
+ case PT_32BSI: {
+ if (fabs(checkvalint - initialvalue) >= 1) {
+#if POSTGIS_RASTER_WARN_ON_TRUNCATION > 0
+ rtwarn("Value set for %s band got clamped from %f to %d",
+ rt_pixtype_name(pixtype),
+ initialvalue, checkvalint
+ );
+#endif
+ result = 1;
+ }
+ else if (FLT_NEQ(checkvalint, initialvalue)) {
+#if POSTGIS_RASTER_WARN_ON_TRUNCATION > 0
+ rtwarn("Value set for %s band got truncated from %f to %d",
+ rt_pixtype_name(pixtype),
+ initialvalue, checkvalint
+ );
+#endif
+ result = 1;
+ }
+ break;
+ }
+ case PT_32BUI: {
+ if (fabs(checkvaluint - initialvalue) >= 1) {
+#if POSTGIS_RASTER_WARN_ON_TRUNCATION > 0
+ rtwarn("Value set for %s band got clamped from %f to %u",
+ rt_pixtype_name(pixtype),
+ initialvalue, checkvaluint
+ );
+#endif
+ result = 1;
+ }
+ else if (FLT_NEQ(checkvaluint, initialvalue)) {
+#if POSTGIS_RASTER_WARN_ON_TRUNCATION > 0
+ rtwarn("Value set for %s band got truncated from %f to %u",
+ rt_pixtype_name(pixtype),
+ initialvalue, checkvaluint
+ );
+#endif
+ result = 1;
+ }
+ break;
+ }
+ case PT_32BF: {
+ /*
+ For float, because the initial value is a double,
+ there is very often a difference between the desired value and the obtained one
+ */
+ if (FLT_NEQ(checkvalfloat, initialvalue)) {
+#if POSTGIS_RASTER_WARN_ON_TRUNCATION > 0
+ rtwarn("Value set for %s band got converted from %f to %f",
+ rt_pixtype_name(pixtype),
+ initialvalue, checkvalfloat
+ );
+#endif
+ result = 1;
+ }
+ break;
+ }
+ case PT_64BF: {
+ if (FLT_NEQ(checkvaldouble, initialvalue)) {
+#if POSTGIS_RASTER_WARN_ON_TRUNCATION > 0
+ rtwarn("Value set for %s band got converted from %f to %f",
+ rt_pixtype_name(pixtype),
+ initialvalue, checkvaldouble
+ );
+#endif
+ result = 1;
+ }
+ break;
+ }
+ case PT_END:
+ break;
+ }
+
+ return result;
+}
+
--- /dev/null
+/*
+ * $Id$
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "librtcore.h"
+#include "librtcore_internal.h"
+
+/******************************************************************************
+* rt_raster_gdal_warp()
+******************************************************************************/
+
+typedef struct _rti_warp_arg_t* _rti_warp_arg;
+struct _rti_warp_arg_t {
+
+ struct {
+ GDALDriverH drv;
+ GDALDatasetH ds;
+ char *srs;
+ } src, dst;
+
+ GDALWarpOptions *wopts;
+
+ struct {
+ struct {
+ char **item;
+ int len;
+ } option;
+
+ struct {
+ void *transform;
+ void *imgproj;
+ void *approx;
+ } arg;
+
+ GDALTransformerFunc func;
+ } transform;
+
+};
+
+static _rti_warp_arg
+_rti_warp_arg_init() {
+ _rti_warp_arg arg = NULL;
+
+ arg = rtalloc(sizeof(struct _rti_warp_arg_t));
+ if (arg == NULL) {
+ rterror("_rti_warp_arg_init: Could not allocate memory for _rti_warp_arg");
+ return NULL;
+ }
+
+ arg->src.drv = NULL;
+ arg->src.ds = NULL;
+ arg->src.srs = NULL;
+
+ arg->dst.drv = NULL;
+ arg->dst.ds = NULL;
+ arg->dst.srs = NULL;
+
+ arg->wopts = NULL;
+
+ arg->transform.option.item = NULL;
+ arg->transform.option.len = 0;
+
+ arg->transform.arg.transform = NULL;
+ arg->transform.arg.imgproj = NULL;
+ arg->transform.arg.approx = NULL;
+
+ arg->transform.func = NULL;
+
+ return arg;
+}
+
+static void
+_rti_warp_arg_destroy(_rti_warp_arg arg) {
+ int i = 0;
+
+ if (arg->dst.ds != NULL)
+ GDALClose(arg->dst.ds);
+ if (arg->dst.srs != NULL)
+ CPLFree(arg->dst.srs);
+
+ if (arg->src.ds != NULL)
+ GDALClose(arg->src.ds);
+ if (arg->src.srs != NULL)
+ CPLFree(arg->src.srs);
+
+ if (arg->transform.func == GDALApproxTransform) {
+ if (arg->transform.arg.imgproj != NULL)
+ GDALDestroyGenImgProjTransformer(arg->transform.arg.imgproj);
+ }
+
+ if (arg->wopts != NULL)
+ GDALDestroyWarpOptions(arg->wopts);
+
+ if (arg->transform.option.len > 0 && arg->transform.option.item != NULL) {
+ for (i = 0; i < arg->transform.option.len; i++) {
+ if (arg->transform.option.item[i] != NULL)
+ rtdealloc(arg->transform.option.item[i]);
+ }
+ rtdealloc(arg->transform.option.item);
+ }
+
+ rtdealloc(arg);
+ arg = NULL;
+}
+
+/**
+ * Return a warped raster using GDAL Warp API
+ *
+ * @param raster : raster to transform
+ * @param src_srs : the raster's coordinate system in OGC WKT
+ * @param dst_srs : the warped raster's coordinate system in OGC WKT
+ * @param scale_x : the x size of pixels of the warped raster's pixels in
+ * units of dst_srs
+ * @param scale_y : the y size of pixels of the warped raster's pixels in
+ * units of dst_srs
+ * @param width : the number of columns of the warped raster. note that
+ * width/height CANNOT be used with scale_x/scale_y
+ * @param height : the number of rows of the warped raster. note that
+ * width/height CANNOT be used with scale_x/scale_y
+ * @param ul_xw : the X value of upper-left corner of the warped raster in
+ * units of dst_srs
+ * @param ul_yw : the Y value of upper-left corner of the warped raster in
+ * units of dst_srs
+ * @param grid_xw : the X value of point on a grid to align warped raster
+ * to in units of dst_srs
+ * @param grid_yw : the Y value of point on a grid to align warped raster
+ * to in units of dst_srs
+ * @param skew_x : the X skew of the warped raster in units of dst_srs
+ * @param skew_y : the Y skew of the warped raster in units of dst_srs
+ * @param resample_alg : the resampling algorithm
+ * @param max_err : maximum error measured in input pixels permitted
+ * (0.0 for exact calculations)
+ *
+ * @return the warped raster or NULL
+ */
+rt_raster rt_raster_gdal_warp(
+ rt_raster raster,
+ const char *src_srs, const char *dst_srs,
+ double *scale_x, double *scale_y,
+ int *width, int *height,
+ double *ul_xw, double *ul_yw,
+ double *grid_xw, double *grid_yw,
+ double *skew_x, double *skew_y,
+ GDALResampleAlg resample_alg, double max_err
+) {
+ CPLErr cplerr;
+ char *dst_options[] = {"SUBCLASS=VRTWarpedDataset", NULL};
+ _rti_warp_arg arg = NULL;
+
+ int hasnodata = 0;
+
+ GDALRasterBandH band;
+ rt_band rtband = NULL;
+ rt_pixtype pt = PT_END;
+ GDALDataType gdal_pt = GDT_Unknown;
+ double nodata = 0.0;
+
+ double _gt[6] = {0};
+ double dst_extent[4];
+ rt_envelope extent;
+
+ int _dim[2] = {0};
+ double _skew[2] = {0};
+ double _scale[2] = {0};
+ int ul_user = 0;
+
+ rt_raster rast = NULL;
+ int i = 0;
+ int numBands = 0;
+
+ int subgt = 0;
+
+ RASTER_DEBUG(3, "starting");
+
+ assert(NULL != raster);
+
+ /* internal variables */
+ arg = _rti_warp_arg_init();
+ if (arg == NULL) {
+ rterror("rt_raster_gdal_warp: Could not initialize internal variables");
+ return NULL;
+ }
+
+ /*
+ max_err must be gte zero
+
+ the value 0.125 is the default used in gdalwarp.cpp on line 283
+ */
+ if (max_err < 0.) max_err = 0.125;
+ RASTER_DEBUGF(4, "max_err = %f", max_err);
+
+ /* handle srs */
+ if (src_srs != NULL) {
+ /* reprojection taking place */
+ if (dst_srs != NULL && strcmp(src_srs, dst_srs) != 0) {
+ RASTER_DEBUG(4, "Warp operation does include a reprojection");
+ arg->src.srs = rt_util_gdal_convert_sr(src_srs, 0);
+ arg->dst.srs = rt_util_gdal_convert_sr(dst_srs, 0);
+
+ if (arg->src.srs == NULL || arg->dst.srs == NULL) {
+ rterror("rt_raster_gdal_warp: Could not convert srs values to GDAL accepted format");
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+ }
+ /* no reprojection, a stub just for clarity */
+ else {
+ RASTER_DEBUG(4, "Warp operation does NOT include reprojection");
+ }
+ }
+ else if (dst_srs != NULL) {
+ /* dst_srs provided but not src_srs */
+ rterror("rt_raster_gdal_warp: SRS required for input raster if SRS provided for warped raster");
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ /* load raster into a GDAL MEM dataset */
+ arg->src.ds = rt_raster_to_gdal_mem(raster, arg->src.srs, NULL, NULL, 0, &(arg->src.drv));
+ if (NULL == arg->src.ds) {
+ rterror("rt_raster_gdal_warp: Could not convert raster to GDAL MEM format");
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+ RASTER_DEBUG(3, "raster loaded into GDAL MEM dataset");
+
+ /* special case when src_srs and dst_srs is NULL and raster's geotransform matrix is default */
+ if (src_srs == NULL && dst_srs == NULL && rt_raster_get_srid(raster) == SRID_UNKNOWN) {
+ double gt[6];
+
+#if POSTGIS_DEBUG_LEVEL > 3
+ GDALGetGeoTransform(arg->src.ds, gt);
+ RASTER_DEBUGF(3, "GDAL MEM geotransform: %f, %f, %f, %f, %f, %f",
+ gt[0], gt[1], gt[2], gt[3], gt[4], gt[5]);
+#endif
+
+ /* default geotransform */
+ rt_raster_get_geotransform_matrix(raster, gt);
+ RASTER_DEBUGF(3, "raster geotransform: %f, %f, %f, %f, %f, %f",
+ gt[0], gt[1], gt[2], gt[3], gt[4], gt[5]);
+
+ if (
+ FLT_EQ(gt[0], 0) &&
+ FLT_EQ(gt[1], 1) &&
+ FLT_EQ(gt[2], 0) &&
+ FLT_EQ(gt[3], 0) &&
+ FLT_EQ(gt[4], 0) &&
+ FLT_EQ(gt[5], -1)
+ ) {
+ double ngt[6] = {0, 10, 0, 0, 0, -10};
+
+ rtinfo("Raster has default geotransform. Adjusting metadata for use of GDAL Warp API");
+
+ GDALSetGeoTransform(arg->src.ds, ngt);
+ GDALFlushCache(arg->src.ds);
+
+ subgt = 1;
+
+#if POSTGIS_DEBUG_LEVEL > 3
+ GDALGetGeoTransform(arg->src.ds, gt);
+ RASTER_DEBUGF(3, "GDAL MEM geotransform: %f, %f, %f, %f, %f, %f",
+ gt[0], gt[1], gt[2], gt[3], gt[4], gt[5]);
+#endif
+ }
+ }
+
+ /* set transform options */
+ if (arg->src.srs != NULL || arg->dst.srs != NULL) {
+ arg->transform.option.len = 2;
+ arg->transform.option.item = rtalloc(sizeof(char *) * (arg->transform.option.len + 1));
+ if (NULL == arg->transform.option.item) {
+ rterror("rt_raster_gdal_warp: Could not allocation memory for transform options");
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+ memset(arg->transform.option.item, 0, sizeof(char *) * (arg->transform.option.len + 1));
+
+ for (i = 0; i < arg->transform.option.len; i++) {
+ switch (i) {
+ case 1:
+ if (arg->dst.srs != NULL)
+ arg->transform.option.item[i] = (char *) rtalloc(sizeof(char) * (strlen("DST_SRS=") + strlen(arg->dst.srs) + 1));
+ else
+ arg->transform.option.item[i] = (char *) rtalloc(sizeof(char) * (strlen("DST_SRS=") + 1));
+ break;
+ case 0:
+ if (arg->src.srs != NULL)
+ arg->transform.option.item[i] = (char *) rtalloc(sizeof(char) * (strlen("SRC_SRS=") + strlen(arg->src.srs) + 1));
+ else
+ arg->transform.option.item[i] = (char *) rtalloc(sizeof(char) * (strlen("SRC_SRS=") + 1));
+ break;
+ }
+ if (NULL == arg->transform.option.item[i]) {
+ rterror("rt_raster_gdal_warp: Could not allocation memory for transform options");
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ switch (i) {
+ case 1:
+ if (arg->dst.srs != NULL) {
+ snprintf(
+ arg->transform.option.item[i],
+ sizeof(char) * (strlen("DST_SRS=") + strlen(arg->dst.srs) + 1),
+ "DST_SRS=%s",
+ arg->dst.srs
+ );
+ }
+ else
+ sprintf(arg->transform.option.item[i], "%s", "DST_SRS=");
+ break;
+ case 0:
+ if (arg->src.srs != NULL) {
+ snprintf(
+ arg->transform.option.item[i],
+ sizeof(char) * (strlen("SRC_SRS=") + strlen(arg->src.srs) + 1),
+ "SRC_SRS=%s",
+ arg->src.srs
+ );
+ }
+ else
+ sprintf(arg->transform.option.item[i], "%s", "SRC_SRS=");
+ break;
+ }
+ RASTER_DEBUGF(4, "arg->transform.option.item[%d] = %s", i, arg->transform.option.item[i]);
+ }
+ }
+ else
+ arg->transform.option.len = 0;
+
+ /* transformation object for building dst dataset */
+ arg->transform.arg.transform = GDALCreateGenImgProjTransformer2(arg->src.ds, NULL, arg->transform.option.item);
+ if (NULL == arg->transform.arg.transform) {
+ rterror("rt_raster_gdal_warp: Could not create GDAL transformation object for output dataset creation");
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ /* get approximate output georeferenced bounds and resolution */
+ cplerr = GDALSuggestedWarpOutput2(
+ arg->src.ds, GDALGenImgProjTransform,
+ arg->transform.arg.transform, _gt, &(_dim[0]), &(_dim[1]), dst_extent, 0);
+ if (cplerr != CE_None) {
+ rterror("rt_raster_gdal_warp: Could not get GDAL suggested warp output for output dataset creation");
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+ GDALDestroyGenImgProjTransformer(arg->transform.arg.transform);
+ arg->transform.arg.transform = NULL;
+
+ /*
+ don't use suggested dimensions as use of suggested scales
+ on suggested extent will result in suggested dimensions
+ */
+ _dim[0] = 0;
+ _dim[1] = 0;
+
+ RASTER_DEBUGF(3, "Suggested geotransform: %f, %f, %f, %f, %f, %f",
+ _gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]);
+
+ /* store extent in easier-to-use object */
+ extent.MinX = dst_extent[0];
+ extent.MinY = dst_extent[1];
+ extent.MaxX = dst_extent[2];
+ extent.MaxY = dst_extent[3];
+
+ extent.UpperLeftX = dst_extent[0];
+ extent.UpperLeftY = dst_extent[3];
+
+ RASTER_DEBUGF(3, "Suggested extent: %f, %f, %f, %f",
+ extent.MinX, extent.MinY, extent.MaxX, extent.MaxY);
+
+ /* scale and width/height are mutually exclusive */
+ if (
+ ((NULL != scale_x) || (NULL != scale_y)) &&
+ ((NULL != width) || (NULL != height))
+ ) {
+ rterror("rt_raster_gdal_warp: Scale X/Y and width/height are mutually exclusive. Only provide one");
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ /* user-defined width */
+ if (NULL != width) {
+ _dim[0] = abs(*width);
+ _scale[0] = fabs((extent.MaxX - extent.MinX) / ((double) _dim[0]));
+ }
+ /* user-defined height */
+ if (NULL != height) {
+ _dim[1] = abs(*height);
+ _scale[1] = fabs((extent.MaxY - extent.MinY) / ((double) _dim[1]));
+ }
+
+ /* user-defined scale */
+ if (
+ ((NULL != scale_x) && (FLT_NEQ(*scale_x, 0.0))) &&
+ ((NULL != scale_y) && (FLT_NEQ(*scale_y, 0.0)))
+ ) {
+ _scale[0] = fabs(*scale_x);
+ _scale[1] = fabs(*scale_y);
+
+ /* special override */
+ if (subgt) {
+ _scale[0] *= 10;
+ _scale[1] *= 10;
+ }
+ }
+ else if (
+ ((NULL != scale_x) && (NULL == scale_y)) ||
+ ((NULL == scale_x) && (NULL != scale_y))
+ ) {
+ rterror("rt_raster_gdal_warp: Both X and Y scale values must be provided for scale");
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ /* scale not defined, use suggested */
+ if (FLT_EQ(_scale[0], 0) && FLT_EQ(_scale[1], 0)) {
+ _scale[0] = fabs(_gt[1]);
+ _scale[1] = fabs(_gt[5]);
+ }
+
+ RASTER_DEBUGF(4, "Using scale: %f x %f", _scale[0], -1 * _scale[1]);
+
+ /* user-defined skew */
+ if (NULL != skew_x) {
+ _skew[0] = *skew_x;
+
+ /*
+ negative scale-x affects skew
+ for now, force skew to be in left-right, top-down orientation
+ */
+ if (
+ NULL != scale_x &&
+ *scale_x < 0.
+ ) {
+ _skew[0] *= -1;
+ }
+ }
+ if (NULL != skew_y) {
+ _skew[1] = *skew_y;
+
+ /*
+ positive scale-y affects skew
+ for now, force skew to be in left-right, top-down orientation
+ */
+ if (
+ NULL != scale_y &&
+ *scale_y > 0.
+ ) {
+ _skew[1] *= -1;
+ }
+ }
+
+ RASTER_DEBUGF(4, "Using skew: %f x %f", _skew[0], _skew[1]);
+
+ /* reprocess extent if skewed */
+ if (
+ FLT_NEQ(_skew[0], 0) ||
+ FLT_NEQ(_skew[1], 0)
+ ) {
+ rt_raster skewedrast;
+
+ RASTER_DEBUG(3, "Computing skewed extent's envelope");
+
+ skewedrast = rt_raster_compute_skewed_raster(
+ extent,
+ _skew,
+ _scale,
+ 0.01
+ );
+ if (skewedrast == NULL) {
+ rterror("rt_raster_gdal_warp: Could not compute skewed raster");
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ if (_dim[0] == 0)
+ _dim[0] = skewedrast->width;
+ if (_dim[1] == 0)
+ _dim[1] = skewedrast->height;
+
+ extent.UpperLeftX = skewedrast->ipX;
+ extent.UpperLeftY = skewedrast->ipY;
+
+ rt_raster_destroy(skewedrast);
+ }
+
+ /* dimensions not defined, compute */
+ if (!_dim[0])
+ _dim[0] = (int) fmax((fabs(extent.MaxX - extent.MinX) + (_scale[0] / 2.)) / _scale[0], 1);
+ if (!_dim[1])
+ _dim[1] = (int) fmax((fabs(extent.MaxY - extent.MinY) + (_scale[1] / 2.)) / _scale[1], 1);
+
+ /* temporary raster */
+ rast = rt_raster_new(_dim[0], _dim[1]);
+ if (rast == NULL) {
+ rterror("rt_raster_gdal_warp: Out of memory allocating temporary raster");
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ /* set raster's spatial attributes */
+ rt_raster_set_offsets(rast, extent.UpperLeftX, extent.UpperLeftY);
+ rt_raster_set_scale(rast, _scale[0], -1 * _scale[1]);
+ rt_raster_set_skews(rast, _skew[0], _skew[1]);
+
+ rt_raster_get_geotransform_matrix(rast, _gt);
+ RASTER_DEBUGF(3, "Temp raster's geotransform: %f, %f, %f, %f, %f, %f",
+ _gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]);
+ RASTER_DEBUGF(3, "Temp raster's dimensions (width x height): %d x %d",
+ _dim[0], _dim[1]);
+
+ /* user-defined upper-left corner */
+ if (
+ NULL != ul_xw &&
+ NULL != ul_yw
+ ) {
+ ul_user = 1;
+
+ RASTER_DEBUGF(4, "Using user-specified upper-left corner: %f, %f", *ul_xw, *ul_yw);
+
+ /* set upper-left corner */
+ rt_raster_set_offsets(rast, *ul_xw, *ul_yw);
+ extent.UpperLeftX = *ul_xw;
+ extent.UpperLeftY = *ul_yw;
+ }
+ else if (
+ ((NULL != ul_xw) && (NULL == ul_yw)) ||
+ ((NULL == ul_xw) && (NULL != ul_yw))
+ ) {
+ rterror("rt_raster_gdal_warp: Both X and Y upper-left corner values must be provided");
+ rt_raster_destroy(rast);
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ /* alignment only considered if upper-left corner not provided */
+ if (
+ !ul_user && (
+ (NULL != grid_xw) || (NULL != grid_yw)
+ )
+ ) {
+
+ if (
+ ((NULL != grid_xw) && (NULL == grid_yw)) ||
+ ((NULL == grid_xw) && (NULL != grid_yw))
+ ) {
+ rterror("rt_raster_gdal_warp: Both X and Y alignment values must be provided");
+ rt_raster_destroy(rast);
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ RASTER_DEBUGF(4, "Aligning extent to user-specified grid: %f, %f", *grid_xw, *grid_yw);
+
+ do {
+ double _r[2] = {0};
+ double _w[2] = {0};
+
+ /* raster is already aligned */
+ if (FLT_EQ(*grid_xw, extent.UpperLeftX) && FLT_EQ(*grid_yw, extent.UpperLeftY)) {
+ RASTER_DEBUG(3, "Skipping raster alignment as it is already aligned to grid");
+ break;
+ }
+
+ extent.UpperLeftX = rast->ipX;
+ extent.UpperLeftY = rast->ipY;
+ rt_raster_set_offsets(rast, *grid_xw, *grid_yw);
+
+ /* process upper-left corner */
+ if (rt_raster_geopoint_to_cell(
+ rast,
+ extent.UpperLeftX, extent.UpperLeftY,
+ &(_r[0]), &(_r[1]),
+ NULL
+ ) != ES_NONE) {
+ rterror("rt_raster_gdal_warp: Could not compute raster pixel for spatial coordinates");
+ rt_raster_destroy(rast);
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ if (rt_raster_cell_to_geopoint(
+ rast,
+ _r[0], _r[1],
+ &(_w[0]), &(_w[1]),
+ NULL
+ ) != ES_NONE) {
+ rterror("rt_raster_gdal_warp: Could not compute spatial coordinates for raster pixel");
+
+ rt_raster_destroy(rast);
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ /* shift occurred */
+ if (FLT_NEQ(_w[0], extent.UpperLeftX)) {
+ if (NULL == width)
+ rast->width++;
+ else if (NULL == scale_x) {
+ double _c[2] = {0};
+
+ rt_raster_set_offsets(rast, extent.UpperLeftX, extent.UpperLeftY);
+
+ /* get upper-right corner */
+ if (rt_raster_cell_to_geopoint(
+ rast,
+ rast->width, 0,
+ &(_c[0]), &(_c[1]),
+ NULL
+ ) != ES_NONE) {
+ rterror("rt_raster_gdal_warp: Could not compute spatial coordinates for raster pixel");
+ rt_raster_destroy(rast);
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ rast->scaleX = fabs((_c[0] - _w[0]) / ((double) rast->width));
+ }
+ }
+ if (FLT_NEQ(_w[1], extent.UpperLeftY)) {
+ if (NULL == height)
+ rast->height++;
+ else if (NULL == scale_y) {
+ double _c[2] = {0};
+
+ rt_raster_set_offsets(rast, extent.UpperLeftX, extent.UpperLeftY);
+
+ /* get upper-right corner */
+ if (rt_raster_cell_to_geopoint(
+ rast,
+ 0, rast->height,
+ &(_c[0]), &(_c[1]),
+ NULL
+ ) != ES_NONE) {
+ rterror("rt_raster_gdal_warp: Could not compute spatial coordinates for raster pixel");
+
+ rt_raster_destroy(rast);
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ rast->scaleY = -1 * fabs((_c[1] - _w[1]) / ((double) rast->height));
+ }
+ }
+
+ rt_raster_set_offsets(rast, _w[0], _w[1]);
+ RASTER_DEBUGF(4, "aligned offsets: %f x %f", _w[0], _w[1]);
+ }
+ while (0);
+ }
+
+ /*
+ after this point, rt_envelope extent is no longer used
+ */
+
+ /* get key attributes from rast */
+ _dim[0] = rast->width;
+ _dim[1] = rast->height;
+ rt_raster_get_geotransform_matrix(rast, _gt);
+
+ /* scale-x is negative or scale-y is positive */
+ if ((
+ (NULL != scale_x) && (*scale_x < 0.)
+ ) || (
+ (NULL != scale_y) && (*scale_y > 0)
+ )) {
+ double _w[2] = {0};
+
+ /* negative scale-x */
+ if (
+ (NULL != scale_x) &&
+ (*scale_x < 0.)
+ ) {
+ if (rt_raster_cell_to_geopoint(
+ rast,
+ rast->width, 0,
+ &(_w[0]), &(_w[1]),
+ NULL
+ ) != ES_NONE) {
+ rterror("rt_raster_gdal_warp: Could not compute spatial coordinates for raster pixel");
+ rt_raster_destroy(rast);
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ _gt[0] = _w[0];
+ _gt[1] = *scale_x;
+
+ /* check for skew */
+ if (NULL != skew_x && FLT_NEQ(*skew_x, 0))
+ _gt[2] = *skew_x;
+ }
+ /* positive scale-y */
+ if (
+ (NULL != scale_y) &&
+ (*scale_y > 0)
+ ) {
+ if (rt_raster_cell_to_geopoint(
+ rast,
+ 0, rast->height,
+ &(_w[0]), &(_w[1]),
+ NULL
+ ) != ES_NONE) {
+ rterror("rt_raster_gdal_warp: Could not compute spatial coordinates for raster pixel");
+ rt_raster_destroy(rast);
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ _gt[3] = _w[1];
+ _gt[5] = *scale_y;
+
+ /* check for skew */
+ if (NULL != skew_y && FLT_NEQ(*skew_y, 0))
+ _gt[4] = *skew_y;
+ }
+ }
+
+ rt_raster_destroy(rast);
+ rast = NULL;
+
+ RASTER_DEBUGF(3, "Applied geotransform: %f, %f, %f, %f, %f, %f",
+ _gt[0], _gt[1], _gt[2], _gt[3], _gt[4], _gt[5]);
+ RASTER_DEBUGF(3, "Raster dimensions (width x height): %d x %d",
+ _dim[0], _dim[1]);
+
+ if (FLT_EQ(_dim[0], 0) || FLT_EQ(_dim[1], 0)) {
+ rterror("rt_raster_gdal_warp: The width (%f) or height (%f) of the warped raster is zero", _dim[0], _dim[1]);
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ /* load VRT driver */
+ if (!rt_util_gdal_driver_registered("VRT"))
+ GDALRegister_VRT();
+ arg->dst.drv = GDALGetDriverByName("VRT");
+ if (NULL == arg->dst.drv) {
+ rterror("rt_raster_gdal_warp: Could not load the output GDAL VRT driver");
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ /* create dst dataset */
+ arg->dst.ds = GDALCreate(arg->dst.drv, "", _dim[0], _dim[1], 0, GDT_Byte, dst_options);
+ if (NULL == arg->dst.ds) {
+ rterror("rt_raster_gdal_warp: Could not create GDAL VRT dataset");
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ /* set dst srs */
+ if (arg->dst.srs != NULL) {
+ cplerr = GDALSetProjection(arg->dst.ds, arg->dst.srs);
+ if (cplerr != CE_None) {
+ rterror("rt_raster_gdal_warp: Could not set projection");
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+ RASTER_DEBUGF(3, "Applied SRS: %s", GDALGetProjectionRef(arg->dst.ds));
+ }
+
+ /* set dst geotransform */
+ cplerr = GDALSetGeoTransform(arg->dst.ds, _gt);
+ if (cplerr != CE_None) {
+ rterror("rt_raster_gdal_warp: Could not set geotransform");
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ /* add bands to dst dataset */
+ numBands = rt_raster_get_num_bands(raster);
+ for (i = 0; i < numBands; i++) {
+ rtband = rt_raster_get_band(raster, i);
+ if (NULL == rtband) {
+ rterror("rt_raster_gdal_warp: Could not get band %d for adding to VRT dataset", i);
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ pt = rt_band_get_pixtype(rtband);
+ gdal_pt = rt_util_pixtype_to_gdal_datatype(pt);
+ if (gdal_pt == GDT_Unknown)
+ rtwarn("rt_raster_gdal_warp: Unknown pixel type for band %d", i);
+
+ cplerr = GDALAddBand(arg->dst.ds, gdal_pt, NULL);
+ if (cplerr != CE_None) {
+ rterror("rt_raster_gdal_warp: Could not add band to VRT dataset");
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ /* get band to write data to */
+ band = NULL;
+ band = GDALGetRasterBand(arg->dst.ds, i + 1);
+ if (NULL == band) {
+ rterror("rt_raster_gdal_warp: Could not get GDAL band for additional processing");
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ /* set nodata */
+ if (rt_band_get_hasnodata_flag(rtband) != FALSE) {
+ hasnodata = 1;
+ rt_band_get_nodata(rtband, &nodata);
+ if (GDALSetRasterNoDataValue(band, nodata) != CE_None)
+ rtwarn("rt_raster_gdal_warp: Could not set nodata value for band %d", i);
+ RASTER_DEBUGF(3, "nodata value set to %f", GDALGetRasterNoDataValue(band, NULL));
+ }
+ }
+
+ /* create transformation object */
+ arg->transform.arg.transform = arg->transform.arg.imgproj = GDALCreateGenImgProjTransformer2(
+ arg->src.ds, arg->dst.ds,
+ arg->transform.option.item
+ );
+ if (NULL == arg->transform.arg.transform) {
+ rterror("rt_raster_gdal_warp: Could not create GDAL transformation object");
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+ arg->transform.func = GDALGenImgProjTransform;
+
+ /* use approximate transformation object */
+ if (max_err > 0.0) {
+ arg->transform.arg.transform = arg->transform.arg.approx = GDALCreateApproxTransformer(
+ GDALGenImgProjTransform,
+ arg->transform.arg.imgproj, max_err
+ );
+ if (NULL == arg->transform.arg.transform) {
+ rterror("rt_raster_gdal_warp: Could not create GDAL approximate transformation object");
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ arg->transform.func = GDALApproxTransform;
+ }
+
+ /* warp options */
+ arg->wopts = GDALCreateWarpOptions();
+ if (NULL == arg->wopts) {
+ rterror("rt_raster_gdal_warp: Could not create GDAL warp options object");
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ /* set options */
+ arg->wopts->eResampleAlg = resample_alg;
+ arg->wopts->hSrcDS = arg->src.ds;
+ arg->wopts->hDstDS = arg->dst.ds;
+ arg->wopts->pfnTransformer = arg->transform.func;
+ arg->wopts->pTransformerArg = arg->transform.arg.transform;
+ arg->wopts->papszWarpOptions = (char **) CPLMalloc(sizeof(char *) * 2);
+ arg->wopts->papszWarpOptions[0] = (char *) CPLMalloc(sizeof(char) * (strlen("INIT_DEST=NO_DATA") + 1));
+ strcpy(arg->wopts->papszWarpOptions[0], "INIT_DEST=NO_DATA");
+ arg->wopts->papszWarpOptions[1] = NULL;
+
+ /* set band mapping */
+ arg->wopts->nBandCount = numBands;
+ arg->wopts->panSrcBands = (int *) CPLMalloc(sizeof(int) * arg->wopts->nBandCount);
+ arg->wopts->panDstBands = (int *) CPLMalloc(sizeof(int) * arg->wopts->nBandCount);
+ for (i = 0; i < arg->wopts->nBandCount; i++)
+ arg->wopts->panDstBands[i] = arg->wopts->panSrcBands[i] = i + 1;
+
+ /* set nodata mapping */
+ if (hasnodata) {
+ RASTER_DEBUG(3, "Setting nodata mapping");
+ arg->wopts->padfSrcNoDataReal = (double *) CPLMalloc(numBands * sizeof(double));
+ arg->wopts->padfDstNoDataReal = (double *) CPLMalloc(numBands * sizeof(double));
+ arg->wopts->padfSrcNoDataImag = (double *) CPLMalloc(numBands * sizeof(double));
+ arg->wopts->padfDstNoDataImag = (double *) CPLMalloc(numBands * sizeof(double));
+ if (
+ NULL == arg->wopts->padfSrcNoDataReal ||
+ NULL == arg->wopts->padfDstNoDataReal ||
+ NULL == arg->wopts->padfSrcNoDataImag ||
+ NULL == arg->wopts->padfDstNoDataImag
+ ) {
+ rterror("rt_raster_gdal_warp: Out of memory allocating nodata mapping");
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+ for (i = 0; i < numBands; i++) {
+ band = rt_raster_get_band(raster, i);
+ if (!band) {
+ rterror("rt_raster_gdal_warp: Could not process bands for nodata values");
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+
+ if (!rt_band_get_hasnodata_flag(band)) {
+ /*
+ based on line 1004 of gdalwarp.cpp
+ the problem is that there is a chance that this number is a legitimate value
+ */
+ arg->wopts->padfSrcNoDataReal[i] = -123456.789;
+ }
+ else {
+ rt_band_get_nodata(band, &(arg->wopts->padfSrcNoDataReal[i]));
+ }
+
+ arg->wopts->padfDstNoDataReal[i] = arg->wopts->padfSrcNoDataReal[i];
+ arg->wopts->padfDstNoDataImag[i] = arg->wopts->padfSrcNoDataImag[i] = 0.0;
+ RASTER_DEBUGF(4, "Mapped nodata value for band %d: %f (%f) => %f (%f)",
+ i,
+ arg->wopts->padfSrcNoDataReal[i], arg->wopts->padfSrcNoDataImag[i],
+ arg->wopts->padfDstNoDataReal[i], arg->wopts->padfDstNoDataImag[i]
+ );
+ }
+ }
+
+ /* warp raster */
+ RASTER_DEBUG(3, "Warping raster");
+ cplerr = GDALInitializeWarpedVRT(arg->dst.ds, arg->wopts);
+ if (cplerr != CE_None) {
+ rterror("rt_raster_gdal_warp: Could not warp raster");
+ _rti_warp_arg_destroy(arg);
+ return NULL;
+ }
+ /*
+ GDALSetDescription(arg->dst.ds, "/tmp/warped.vrt");
+ */
+ GDALFlushCache(arg->dst.ds);
+ RASTER_DEBUG(3, "Raster warped");
+
+ /* convert gdal dataset to raster */
+ RASTER_DEBUG(3, "Converting GDAL dataset to raster");
+ rast = rt_raster_from_gdal_dataset(arg->dst.ds);
+
+ _rti_warp_arg_destroy(arg);
+
+ if (NULL == rast) {
+ rterror("rt_raster_gdal_warp: Could not warp raster");
+ return NULL;
+ }
+
+ /* substitute geotransform matrix, reset back to default */
+ if (subgt) {
+ double gt[6] = {0, 1, 0, 0, 0, -1};
+
+ rt_raster_set_geotransform_matrix(rast, gt);
+ }
+
+ RASTER_DEBUG(3, "done");
+
+ return rast;
+}
+
--- /dev/null
+/*
+ * $Id$
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "librtcore.h"
+#include "librtcore_internal.h"
+#include "rt_serialize.h"
+
+/* Read band from WKB as at start of band */
+static rt_band
+rt_band_from_wkb(
+ uint16_t width, uint16_t height,
+ const uint8_t** ptr, const uint8_t* end,
+ uint8_t littleEndian
+) {
+ rt_band band = NULL;
+ int pixbytes = 0;
+ uint8_t type = 0;
+ unsigned long sz = 0;
+ uint32_t v = 0;
+
+ assert(NULL != ptr);
+ assert(NULL != end);
+
+ band = rtalloc(sizeof (struct rt_band_t));
+ if (!band) {
+ rterror("rt_band_from_wkb: Out of memory allocating rt_band during WKB parsing");
+ return NULL;
+ }
+ band->ownsdata = 0; /* assume we don't own data */
+
+ if (end - *ptr < 1) {
+ rterror("rt_band_from_wkb: Premature end of WKB on band reading (%s:%d)",
+ __FILE__, __LINE__);
+ rt_band_destroy(band);
+ return NULL;
+ }
+ type = read_uint8(ptr);
+
+ if ((type & BANDTYPE_PIXTYPE_MASK) >= PT_END) {
+ rterror("rt_band_from_wkb: Invalid pixtype %d", type & BANDTYPE_PIXTYPE_MASK);
+ rt_band_destroy(band);
+ return NULL;
+ }
+
+ band->pixtype = type & BANDTYPE_PIXTYPE_MASK;
+ band->offline = BANDTYPE_IS_OFFDB(type) ? 1 : 0;
+ band->hasnodata = BANDTYPE_HAS_NODATA(type) ? 1 : 0;
+ band->isnodata = band->hasnodata ? (BANDTYPE_IS_NODATA(type) ? 1 : 0) : 0;
+ band->width = width;
+ band->height = height;
+
+ RASTER_DEBUGF(3, " Band pixtype:%s, offline:%d, hasnodata:%d",
+ rt_pixtype_name(band->pixtype),
+ band->offline,
+ band->hasnodata
+ );
+
+ /* Check there's enough bytes to read nodata value */
+ pixbytes = rt_pixtype_size(band->pixtype);
+ if (((*ptr) + pixbytes) >= end) {
+ rterror("rt_band_from_wkb: Premature end of WKB on band novalue reading");
+ rt_band_destroy(band);
+ return NULL;
+ }
+
+ /* Read nodata value */
+ switch (band->pixtype) {
+ case PT_1BB: {
+ band->nodataval = ((int) read_uint8(ptr)) & 0x01;
+ break;
+ }
+ case PT_2BUI: {
+ band->nodataval = ((int) read_uint8(ptr)) & 0x03;
+ break;
+ }
+ case PT_4BUI: {
+ band->nodataval = ((int) read_uint8(ptr)) & 0x0F;
+ break;
+ }
+ case PT_8BSI: {
+ band->nodataval = read_int8(ptr);
+ break;
+ }
+ case PT_8BUI: {
+ band->nodataval = read_uint8(ptr);
+ break;
+ }
+ case PT_16BSI: {
+ band->nodataval = read_int16(ptr, littleEndian);
+ break;
+ }
+ case PT_16BUI: {
+ band->nodataval = read_uint16(ptr, littleEndian);
+ break;
+ }
+ case PT_32BSI: {
+ band->nodataval = read_int32(ptr, littleEndian);
+ break;
+ }
+ case PT_32BUI: {
+ band->nodataval = read_uint32(ptr, littleEndian);
+ break;
+ }
+ case PT_32BF: {
+ band->nodataval = read_float32(ptr, littleEndian);
+ break;
+ }
+ case PT_64BF: {
+ band->nodataval = read_float64(ptr, littleEndian);
+ break;
+ }
+ default: {
+ rterror("rt_band_from_wkb: Unknown pixeltype %d", band->pixtype);
+ rt_band_destroy(band);
+ return NULL;
+ }
+ }
+
+ RASTER_DEBUGF(3, " Nodata value: %g, pixbytes: %d, ptr @ %p, end @ %p",
+ band->nodataval, pixbytes, *ptr, end);
+
+ if (band->offline) {
+ if (((*ptr) + 1) >= end) {
+ rterror("rt_band_from_wkb: Premature end of WKB on offline "
+ "band data bandNum reading (%s:%d)",
+ __FILE__, __LINE__
+ );
+ rt_band_destroy(band);
+ return NULL;
+ }
+
+ band->data.offline.bandNum = read_int8(ptr);
+ band->data.offline.mem = NULL;
+
+ {
+ /* check we have a NULL-termination */
+ sz = 0;
+ while ((*ptr)[sz] && &((*ptr)[sz]) < end) ++sz;
+ if (&((*ptr)[sz]) >= end) {
+ rterror("rt_band_from_wkb: Premature end of WKB on band offline path reading");
+ rt_band_destroy(band);
+ return NULL;
+ }
+
+ /* we never own offline band data */
+ band->ownsdata = 0;
+
+ band->data.offline.path = rtalloc(sz + 1);
+ if (band->data.offline.path == NULL) {
+ rterror("rt_band_from_wkb: Out of memory allocating for offline path of band");
+ rt_band_destroy(band);
+ return NULL;
+ }
+
+ memcpy(band->data.offline.path, *ptr, sz);
+ band->data.offline.path[sz] = '\0';
+
+ RASTER_DEBUGF(3, "OFFDB band path is %s (size is %d)",
+ band->data.offline.path, sz);
+
+ *ptr += sz + 1;
+
+ /* TODO: How could we know if the offline band is a nodata band? */
+ /* trust in the force */
+ /*band->isnodata = FALSE;*/
+ }
+
+ return band;
+ }
+
+ /* This is an on-disk band */
+ sz = width * height * pixbytes;
+ if (((*ptr) + sz) > end) {
+ rterror("rt_band_from_wkb: Premature end of WKB on band data reading (%s:%d)",
+ __FILE__, __LINE__);
+ rt_band_destroy(band);
+ return NULL;
+ }
+
+ band->data.mem = rtalloc(sz);
+ if (!band->data.mem) {
+ rterror("rt_band_from_wkb: Out of memory during band creation in WKB parser");
+ rt_band_destroy(band);
+ return NULL;
+ }
+
+ band->ownsdata = 1; /* we DO own this data!!! */
+ memcpy(band->data.mem, *ptr, sz);
+ *ptr += sz;
+
+ /* Should now flip values if > 8bit and
+ * littleEndian != isMachineLittleEndian */
+ if (pixbytes > 1) {
+ if (isMachineLittleEndian() != littleEndian) {
+ void (*flipper)(uint8_t*) = 0;
+ uint8_t *flipme = NULL;
+
+ if (pixbytes == 2)
+ flipper = flip_endian_16;
+ else if (pixbytes == 4)
+ flipper = flip_endian_32;
+ else if (pixbytes == 8)
+ flipper = flip_endian_64;
+ else {
+ rterror("rt_band_from_wkb: Unexpected pix bytes %d", pixbytes);
+ rt_band_destroy(band);
+ return NULL;
+ }
+
+ flipme = band->data.mem;
+ sz = width * height;
+ for (v = 0; v < sz; ++v) {
+ flipper(flipme);
+ flipme += pixbytes;
+ }
+ }
+ }
+ /* And should check for invalid values for < 8bit types */
+ else if (
+ band->pixtype == PT_1BB ||
+ band->pixtype == PT_2BUI ||
+ band->pixtype == PT_4BUI
+ ) {
+ uint8_t maxVal = band->pixtype == PT_1BB ? 1 : (band->pixtype == PT_2BUI ? 3 : 15);
+ uint8_t val;
+
+ sz = width*height;
+ for (v = 0; v < sz; ++v) {
+ val = ((uint8_t*) band->data.mem)[v];
+ if (val > maxVal) {
+ rterror("rt_band_from_wkb: Invalid value %d for pixel of type %s",
+ val, rt_pixtype_name(band->pixtype));
+ rt_band_destroy(band);
+ return NULL;
+ }
+ }
+ }
+
+ /* And we should check if the band is a nodata band */
+ /* TODO: No!! This is too slow */
+ /*rt_band_check_is_nodata(band);*/
+
+ return band;
+}
+
+/* -4 for size, +1 for endian */
+#define RT_WKB_HDR_SZ (sizeof(struct rt_raster_serialized_t)-4+1)
+
+rt_raster
+rt_raster_from_wkb(const uint8_t* wkb, uint32_t wkbsize) {
+ const uint8_t *ptr = wkb;
+ const uint8_t *wkbend = NULL;
+ rt_raster rast = NULL;
+ uint8_t endian = 0;
+ uint16_t version = 0;
+ uint16_t i = 0;
+ uint16_t j = 0;
+
+ assert(NULL != ptr);
+
+ /* Check that wkbsize is >= sizeof(rt_raster_serialized) */
+ if (wkbsize < RT_WKB_HDR_SZ) {
+ rterror("rt_raster_from_wkb: wkb size (%d) < min size (%d)",
+ wkbsize, RT_WKB_HDR_SZ);
+ return NULL;
+ }
+ wkbend = wkb + wkbsize;
+
+ RASTER_DEBUGF(3, "Parsing header from wkb position %d (expected 0)",
+ d_binptr_to_pos(ptr, wkbend, wkbsize));
+
+ CHECK_BINPTR_POSITION(ptr, wkbend, wkbsize, 0);
+
+ /* Read endianness */
+ endian = *ptr;
+ ptr += 1;
+
+ /* Read version of protocol */
+ version = read_uint16(&ptr, endian);
+ if (version != 0) {
+ rterror("rt_raster_from_wkb: WKB version %d unsupported", version);
+ return NULL;
+ }
+
+ /* Read other components of raster header */
+ rast = (rt_raster) rtalloc(sizeof (struct rt_raster_t));
+ if (!rast) {
+ rterror("rt_raster_from_wkb: Out of memory allocating raster for wkb input");
+ return NULL;
+ }
+
+ rast->numBands = read_uint16(&ptr, endian);
+ rast->scaleX = read_float64(&ptr, endian);
+ rast->scaleY = read_float64(&ptr, endian);
+ rast->ipX = read_float64(&ptr, endian);
+ rast->ipY = read_float64(&ptr, endian);
+ rast->skewX = read_float64(&ptr, endian);
+ rast->skewY = read_float64(&ptr, endian);
+ rast->srid = clamp_srid(read_int32(&ptr, endian));
+ rast->width = read_uint16(&ptr, endian);
+ rast->height = read_uint16(&ptr, endian);
+
+ /* Consistency checking, should have been checked before */
+ assert(ptr <= wkbend);
+
+ RASTER_DEBUGF(3, "rt_raster_from_wkb: Raster numBands: %d",
+ rast->numBands);
+ RASTER_DEBUGF(3, "rt_raster_from_wkb: Raster scale: %gx%g",
+ rast->scaleX, rast->scaleY);
+ RASTER_DEBUGF(3, "rt_raster_from_wkb: Raster ip: %gx%g",
+ rast->ipX, rast->ipY);
+ RASTER_DEBUGF(3, "rt_raster_from_wkb: Raster skew: %gx%g",
+ rast->skewX, rast->skewY);
+ RASTER_DEBUGF(3, "rt_raster_from_wkb: Raster srid: %d",
+ rast->srid);
+ RASTER_DEBUGF(3, "rt_raster_from_wkb: Raster dims: %dx%d",
+ rast->width, rast->height);
+ RASTER_DEBUGF(3, "Parsing raster header finished at wkb position %d (expected 61)",
+ d_binptr_to_pos(ptr, wkbend, wkbsize));
+
+ CHECK_BINPTR_POSITION(ptr, wkbend, wkbsize, 61);
+
+ /* Read all bands of raster */
+ if (!rast->numBands) {
+ /* Here ptr should have been left to right after last used byte */
+ if (ptr < wkbend) {
+ rtwarn("%d bytes of WKB remained unparsed", wkbend - ptr);
+ }
+ else if (ptr > wkbend) {
+ /* Easier to get a segfault before I guess */
+ rtwarn("We parsed %d bytes more then available!", ptr - wkbend);
+ }
+
+ rast->bands = NULL;
+ return rast;
+ }
+
+ /* Now read the bands */
+ rast->bands = (rt_band*) rtalloc(sizeof(rt_band) * rast->numBands);
+ if (!rast->bands) {
+ rterror("rt_raster_from_wkb: Out of memory allocating bands for WKB raster decoding");
+ rt_raster_destroy(rast);
+ return NULL;
+ }
+
+ /* ptr should now point to start of first band */
+ /* we should have checked this before */
+ assert(ptr <= wkbend);
+
+ for (i = 0; i < rast->numBands; ++i) {
+ RASTER_DEBUGF(3, "Parsing band %d from wkb position %d", i,
+ d_binptr_to_pos(ptr, wkbend, wkbsize));
+
+ rt_band band = rt_band_from_wkb(rast->width, rast->height,
+ &ptr, wkbend, endian);
+ if (!band) {
+ rterror("rt_raster_from_wkb: Error reading WKB form of band %d", i);
+ for (j = 0; j < i; j++) rt_band_destroy(rast->bands[j]);
+ rt_raster_destroy(rast);
+ return NULL;
+ }
+
+ band->raster = rast;
+ rast->bands[i] = band;
+ }
+
+ /* Here ptr should have been left to right after last used byte */
+ if (ptr < wkbend) {
+ rtwarn("%d bytes of WKB remained unparsed", wkbend - ptr);
+ }
+ else if (ptr > wkbend) {
+ /* Easier to get a segfault before I guess */
+ rtwarn("We parsed %d bytes more then available!", ptr - wkbend);
+ }
+
+ return rast;
+}
+
+rt_raster
+rt_raster_from_hexwkb(const char* hexwkb, uint32_t hexwkbsize) {
+ rt_raster ret = NULL;
+ uint8_t* wkb = NULL;
+ uint32_t wkbsize = 0;
+ uint32_t i = 0;
+
+ assert(NULL != hexwkb);
+
+ RASTER_DEBUGF(3, "input wkb: %s", hexwkb);
+ RASTER_DEBUGF(3, "input wkbsize: %d", hexwkbsize);
+
+ if (hexwkbsize % 2) {
+ rterror("rt_raster_from_hexwkb: Raster HEXWKB input must have an even number of characters");
+ return NULL;
+ }
+ wkbsize = hexwkbsize / 2;
+
+ wkb = rtalloc(wkbsize);
+ if (!wkb) {
+ rterror("rt_raster_from_hexwkb: Out of memory allocating memory for decoding HEXWKB");
+ return NULL;
+ }
+
+ /* parse full hex */
+ for (i = 0; i < wkbsize; ++i) {
+ wkb[i] = parse_hex((char*) & (hexwkb[i * 2]));
+ }
+
+ ret = rt_raster_from_wkb(wkb, wkbsize);
+ rtdealloc(wkb); /* as long as rt_raster_from_wkb copies memory */
+
+ return ret;
+}
+
+static uint32_t
+rt_raster_wkb_size(rt_raster raster, int outasin) {
+ uint32_t size = RT_WKB_HDR_SZ;
+ uint16_t i = 0;
+
+ assert(NULL != raster);
+
+ RASTER_DEBUGF(3, "rt_raster_wkb_size: computing size for %d bands",
+ raster->numBands);
+
+ for (i = 0; i < raster->numBands; ++i) {
+ rt_band band = raster->bands[i];
+ rt_pixtype pixtype = band->pixtype;
+ int pixbytes = rt_pixtype_size(pixtype);
+
+ RASTER_DEBUGF(3, "rt_raster_wkb_size: adding size of band %d", i);
+
+ if (pixbytes < 1) {
+ rterror("rt_raster_wkb_size: Corrupted band: unknown pixtype");
+ return 0;
+ }
+
+ /* Add space for band type */
+ size += 1;
+
+ /* Add space for nodata value */
+ size += pixbytes;
+
+ if (!outasin && band->offline) {
+ /* Add space for band number */
+ size += 1;
+
+ /* Add space for null-terminated path */
+ size += strlen(band->data.offline.path) + 1;
+ }
+ else {
+ /* Add space for actual data */
+ size += pixbytes * raster->width * raster->height;
+ }
+ }
+
+ return size;
+}
+
+/**
+ * Return this raster in WKB form
+ *
+ * @param raster : the raster
+ * @param outasin : if TRUE, out-db bands are treated as in-db
+ * @param wkbsize : will be set to the size of returned wkb form
+ *
+ * @return WKB of raster or NULL on error
+ */
+uint8_t *
+rt_raster_to_wkb(rt_raster raster, int outasin, uint32_t *wkbsize) {
+
+#if POSTGIS_DEBUG_LEVEL > 0
+ const uint8_t *wkbend = NULL;
+#endif
+
+ uint8_t *wkb = NULL;
+ uint8_t *ptr = NULL;
+ uint16_t i = 0;
+ uint8_t littleEndian = isMachineLittleEndian();
+
+ assert(NULL != raster);
+ assert(NULL != wkbsize);
+
+ RASTER_DEBUG(2, "rt_raster_to_wkb: about to call rt_raster_wkb_size");
+
+ *wkbsize = rt_raster_wkb_size(raster, outasin);
+ RASTER_DEBUGF(3, "rt_raster_to_wkb: found size: %d", *wkbsize);
+
+ wkb = (uint8_t*) rtalloc(*wkbsize);
+ if (!wkb) {
+ rterror("rt_raster_to_wkb: Out of memory allocating WKB for raster");
+ return NULL;
+ }
+
+ ptr = wkb;
+
+#if POSTGIS_DEBUG_LEVEL > 2
+ wkbend = ptr + (*wkbsize);
+#endif
+ RASTER_DEBUGF(3, "Writing raster header to wkb on position %d (expected 0)",
+ d_binptr_to_pos(ptr, wkbend, *wkbsize));
+
+ /* Write endianness */
+ *ptr = littleEndian;
+ ptr += 1;
+
+ /* Write version(size - (end - ptr)) */
+ write_uint16(&ptr, littleEndian, 0);
+
+ /* Copy header (from numBands up) */
+ memcpy(ptr, &(raster->numBands), sizeof (struct rt_raster_serialized_t) - 6);
+ ptr += sizeof (struct rt_raster_serialized_t) - 6;
+
+ RASTER_DEBUGF(3, "Writing bands header to wkb position %d (expected 61)",
+ d_binptr_to_pos(ptr, wkbend, *wkbsize));
+
+ /* Serialize bands now */
+ for (i = 0; i < raster->numBands; ++i) {
+ rt_band band = raster->bands[i];
+ rt_pixtype pixtype = band->pixtype;
+ int pixbytes = rt_pixtype_size(pixtype);
+
+ RASTER_DEBUGF(3, "Writing WKB for band %d", i);
+ RASTER_DEBUGF(3, "Writing band pixel type to wkb position %d",
+ d_binptr_to_pos(ptr, wkbend, *wkbsize));
+
+ if (pixbytes < 1) {
+ rterror("rt_raster_to_wkb: Corrupted band: unknown pixtype");
+ rtdealloc(wkb);
+ return NULL;
+ }
+
+ /* Add band type */
+ *ptr = band->pixtype;
+ if (!outasin && band->offline) *ptr |= BANDTYPE_FLAG_OFFDB;
+ if (band->hasnodata) *ptr |= BANDTYPE_FLAG_HASNODATA;
+ if (band->isnodata) *ptr |= BANDTYPE_FLAG_ISNODATA;
+ ptr += 1;
+
+#if 0
+ /* no padding required for WKB */
+ /* Add padding (if needed) */
+ if (pixbytes > 1) {
+ memset(ptr, '\0', pixbytes - 1);
+ ptr += pixbytes - 1;
+ }
+ /* Consistency checking (ptr is pixbytes-aligned) */
+ assert(!(((uint64_t) ptr) % pixbytes));
+#endif
+
+ RASTER_DEBUGF(3, "Writing band nodata to wkb position %d",
+ d_binptr_to_pos(ptr, wkbend, *wkbsize));
+
+ /* Add nodata value */
+ switch (pixtype) {
+ case PT_1BB:
+ case PT_2BUI:
+ case PT_4BUI:
+ case PT_8BUI: {
+ uint8_t v = band->nodataval;
+ *ptr = v;
+ ptr += 1;
+ break;
+ }
+ case PT_8BSI: {
+ int8_t v = band->nodataval;
+ *ptr = v;
+ ptr += 1;
+ break;
+ }
+ case PT_16BSI:
+ case PT_16BUI: {
+ uint16_t v = band->nodataval;
+ memcpy(ptr, &v, 2);
+ ptr += 2;
+ break;
+ }
+ case PT_32BSI:
+ case PT_32BUI: {
+ uint32_t v = band->nodataval;
+ memcpy(ptr, &v, 4);
+ ptr += 4;
+ break;
+ }
+ case PT_32BF: {
+ float v = band->nodataval;
+ memcpy(ptr, &v, 4);
+ ptr += 4;
+ break;
+ }
+ case PT_64BF: {
+ memcpy(ptr, &band->nodataval, 8);
+ ptr += 8;
+ break;
+ }
+ default:
+ rterror("rt_raster_to_wkb: Fatal error caused by unknown pixel type. Aborting.");
+ rtdealloc(wkb);
+ abort(); /* shoudn't happen */
+ return 0;
+ }
+
+#if 0
+ /* no padding for WKB */
+ /* Consistency checking (ptr is pixbytes-aligned) */
+ assert(!((uint64_t) ptr % pixbytes));
+#endif
+
+ if (!outasin && band->offline) {
+ /* Write band number */
+ *ptr = band->data.offline.bandNum;
+ ptr += 1;
+
+ /* Write path */
+ strcpy((char*) ptr, band->data.offline.path);
+ ptr += strlen(band->data.offline.path) + 1;
+ }
+ else {
+ /* Write data */
+ uint32_t datasize = raster->width * raster->height * pixbytes;
+ RASTER_DEBUGF(4, "rt_raster_to_wkb: Copying %d bytes", datasize);
+
+ memcpy(ptr, rt_band_get_data(band), datasize);
+
+ ptr += datasize;
+ }
+
+#if 0
+ /* no padding for WKB */
+ /* Pad up to 8-bytes boundary */
+ while ((uint64_t) ptr % 8) {
+ *ptr = 0;
+ ++ptr;
+ }
+
+ /* Consistency checking (ptr is pixbytes-aligned) */
+ assert(!((uint64_t) ptr % pixbytes));
+#endif
+ }
+
+ return wkb;
+}
+
+char *
+rt_raster_to_hexwkb(rt_raster raster, int outasin, uint32_t *hexwkbsize) {
+ uint8_t *wkb = NULL;
+ char* hexwkb = NULL;
+ uint32_t i = 0;
+ uint32_t wkbsize = 0;
+
+ assert(NULL != raster);
+ assert(NULL != hexwkbsize);
+
+ RASTER_DEBUG(2, "rt_raster_to_hexwkb: calling rt_raster_to_wkb");
+
+ wkb = rt_raster_to_wkb(raster, outasin, &wkbsize);
+
+ RASTER_DEBUG(3, "rt_raster_to_hexwkb: rt_raster_to_wkb returned");
+
+ *hexwkbsize = wkbsize * 2; /* hex is 2 times bytes */
+ hexwkb = (char*) rtalloc((*hexwkbsize) + 1);
+ if (!hexwkb) {
+ rterror("rt_raster_to_hexwkb: Out of memory hexifying raster WKB");
+ rtdealloc(wkb);
+ return NULL;
+ }
+ hexwkb[*hexwkbsize] = '\0'; /* Null-terminate */
+
+ for (i = 0; i < wkbsize; ++i) {
+ deparse_hex(wkb[i], &(hexwkb[2 * i]));
+ }
+
+ rtdealloc(wkb); /* we don't need this anymore */
+
+ RASTER_DEBUGF(3, "rt_raster_to_hexwkb: output wkb: %s", hexwkb);
+ return hexwkb;
+}
SQL_OBJS=rtpostgis.sql rtpostgis_drop.sql rtpostgis_upgrade_cleanup.sql rtpostgis_legacy.sql
# Objects to build using PGXS
-OBJS=rt_pg.o
+OBJS = \
+ rtpostgis.o \
+ rtpg_internal.o \
+ rtpg_spatial_relationship.o \
+ rtpg_mapalgebra.o \
+ rtpg_utility.o \
+ rtpg_inout.o \
+ rtpg_geometry.o \
+ rtpg_raster_properties.o \
+ rtpg_band_properties.o \
+ rtpg_pixel.o \
+ rtpg_create.o \
+ rtpg_gdal.o \
+ rtpg_statistics.o
# Libraries to link into the module (proj, geos)
#
+++ /dev/null
-/*
- * $Id$
- *
- * WKTRaster - Raster Types for PostGIS
- * http://www.postgis.org/support/wiki/index.php?WKTRasterHomePage
- *
- * 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@keybit.net>
- *
- * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#include <math.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h> /* for strtod in RASTER_reclass */
-#include <errno.h>
-#include <assert.h>
-#include <ctype.h> /* for isspace */
-
-#include <postgres.h> /* for palloc */
-#include <access/gist.h>
-#include <access/itup.h>
-#include <fmgr.h>
-#include <utils/elog.h>
-#include <utils/builtins.h>
-#include <executor/spi.h>
-#include <executor/executor.h> /* for GetAttributeByName in RASTER_reclass */
-#include <funcapi.h>
-
-#include "../../postgis_config.h"
-
-#include "utils/guc.h"
-#include "lwgeom_pg.h"
-#include "rt_pg.h"
-#include "pgsql_compat.h"
-
-#include "utils/lsyscache.h" /* for get_typlenbyvalalign */
-#include "utils/array.h" /* for ArrayType */
-#include "catalog/pg_type.h" /* for INT2OID, INT4OID, FLOAT4OID, FLOAT8OID and TEXTOID */
-
-#if POSTGIS_PGSQL_VERSION > 92
-#include "access/htup_details.h"
-#endif
-
-/* maximum char length required to hold any double or long long value */
-#define MAX_DBL_CHARLEN (3 + DBL_MANT_DIG - DBL_MIN_EXP)
-#define MAX_INT_CHARLEN 32
-
-/*
- * This is required for builds against pgsql
- */
-PG_MODULE_MAGIC;
-
-/* Module load callback */
-void _PG_init(void);
-
-/***************************************************************
- * Internal functions must be prefixed with rtpg_. This is
- * keeping inline with the use of pgis_ for ./postgis C utility
- * functions.
- ****************************************************************/
-/* Internal funcs */
-static char *rtpg_strreplace(
- const char *str,
- const char *oldstr, const char *newstr,
- int *count
-);
-static char *rtpg_strtoupper(char *str);
-static char *rtpg_chartrim(const char* input, char *remove);
-static char **rtpg_strsplit(const char *str, const char *delimiter, int *n);
-static char *rtpg_removespaces(char *str);
-static char *rtpg_trim(const char* input);
-static char *rtpg_getSR(int srid);
-
-static char *gdaldatapath;
-static void rtpg_assignHookGDALDataPath(const char *newpath, void *extra);
-
-/***************************************************************
- * Some rules for returning NOTICE or ERROR...
- *
- * Send an ERROR like:
- *
- * elog(ERROR, "RASTER_out: Could not deserialize raster");
- *
- * only when:
- *
- * -something wrong happen with memory,
- * -a function got an invalid argument ('3BUI' as pixel type) so that no row can
- * be processed
- *
- * *** IMPORTANT: elog(ERROR, ...) does NOT return to calling function ***
- *
- * Send a NOTICE like:
- *
- * elog(NOTICE, "Invalid band index (must use 1-based). Returning NULL");
- *
- * when arguments (e.g. x, y, band) are NULL or out of range so that some or
- * most rows can be processed anyway
- *
- * in this case,
- * for SET functions or function normally returning a modified raster, return
- * the original raster
- * for GET functions, return NULL
- * try to deduce a valid parameter value if it makes sence (e.g. out of range
- * index for addBand)
- *
- * Do not put the name of the faulty function for NOTICEs, only with ERRORs.
- *
- ****************************************************************/
-
-/******************************************************************************
- * Some notes on memory management...
- *
- * Every time a SQL function is called, PostgreSQL creates a new memory context.
- * So, all the memory allocated with palloc/repalloc in that context is
- * automatically free'd at the end of the function. If you want some data to
- * live between function calls, you have 2 options:
- *
- * - Use fcinfo->flinfo->fn_mcxt contex to store the data (by pointing the
- * data you want to keep with fcinfo->flinfo->fn_extra)
- * - Use SRF funcapi, and storing the data at multi_call_memory_ctx (by pointing
- * the data you want to keep with funcctx->user_fctx. funcctx is created by
- * funcctx = SPI_FIRSTCALL_INIT()). Recommended way in functions returning rows,
- * like RASTER_dumpAsPolygons (see section 34.9.9 at
- * http://www.postgresql.org/docs/8.4/static/xfunc-c.html).
- *
- * But raster code follows the same philosophy than the rest of PostGIS: keep
- * memory as clean as possible. So, we free all allocated memory.
- *
- * TODO: In case of functions returning NULL, we should free the memory too.
- *****************************************************************************/
-
-/******************************************************************************
- * Notes for use of PG_DETOAST_DATUM(), PG_DETOAST_DATUM_SLICE()
- * and PG_DETOAST_DATUM_COPY()
- *
- * When ONLY getting raster (not band) metadata, use PG_DETOAST_DATUM_SLICE()
- * as it is generally quicker to get only the chunk of memory that contains
- * the raster metadata.
- *
- * Example: PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0,
- * sizeof(struct rt_raster_serialized_t))
- *
- * When ONLY setting raster or band(s) metadata OR reading band data, use
- * PG_DETOAST_DATUM() as rt_raster_deserialize() allocates local memory
- * for the raster and band(s) metadata.
- *
- * Example: PG_DETOAST_DATUM(PG_GETARG_DATUM(0))
- *
- * When SETTING band pixel values, use PG_DETOAST_DATUM_COPY(). This is
- * because band data (not metadata) is just a pointer to the correct
- * memory location in the detoasted datum. What is returned from
- * PG_DETOAST_DATUM() may or may not be a copy of the input datum.
- *
- * From the comments in postgresql/src/include/fmgr.h...
- *
- * pg_detoast_datum() gives you either the input datum (if not toasted)
- * or a detoasted copy allocated with palloc().
- *
- * From the mouth of Tom Lane...
- * http://archives.postgresql.org/pgsql-hackers/2002-01/msg01289.php
- *
- * PG_DETOAST_DATUM_COPY guarantees to give you a copy, even if the
- * original wasn't toasted. This allows you to scribble on the input,
- * in case that happens to be a useful way of forming your result.
- * Without a forced copy, a routine for a pass-by-ref datatype must
- * NEVER, EVER scribble on its input ... because very possibly it'd
- * be scribbling on a valid tuple in a disk buffer, or a valid entry
- * in the syscache.
- *
- * The key detail above is that the raster datatype is a varlena, a
- * passed by reference datatype.
- *
- * Example: PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0))
- *
- * If in doubt, use PG_DETOAST_DATUM_COPY() as that guarantees that the input
- * datum is copied for use.
- *****************************************************************************/
-
-/* Prototypes */
-
-/* Utility functions */
-Datum RASTER_lib_version(PG_FUNCTION_ARGS);
-Datum RASTER_lib_build_date(PG_FUNCTION_ARGS);
-Datum RASTER_gdal_version(PG_FUNCTION_ARGS);
-Datum RASTER_minPossibleValue(PG_FUNCTION_ARGS);
-
-/* Input/output and format conversions */
-Datum RASTER_in(PG_FUNCTION_ARGS);
-Datum RASTER_out(PG_FUNCTION_ARGS);
-
-Datum RASTER_to_bytea(PG_FUNCTION_ARGS);
-Datum RASTER_to_binary(PG_FUNCTION_ARGS);
-
-/* Raster as geometry operations */
-Datum RASTER_convex_hull(PG_FUNCTION_ARGS);
-Datum RASTER_dumpAsPolygons(PG_FUNCTION_ARGS);
-
-/* Get all the properties of a raster */
-Datum RASTER_getSRID(PG_FUNCTION_ARGS);
-Datum RASTER_getWidth(PG_FUNCTION_ARGS);
-Datum RASTER_getHeight(PG_FUNCTION_ARGS);
-Datum RASTER_getNumBands(PG_FUNCTION_ARGS);
-Datum RASTER_getXScale(PG_FUNCTION_ARGS);
-Datum RASTER_getYScale(PG_FUNCTION_ARGS);
-Datum RASTER_getXSkew(PG_FUNCTION_ARGS);
-Datum RASTER_getYSkew(PG_FUNCTION_ARGS);
-Datum RASTER_getXUpperLeft(PG_FUNCTION_ARGS);
-Datum RASTER_getYUpperLeft(PG_FUNCTION_ARGS);
-Datum RASTER_getPixelWidth(PG_FUNCTION_ARGS);
-Datum RASTER_getPixelHeight(PG_FUNCTION_ARGS);
-Datum RASTER_getGeotransform(PG_FUNCTION_ARGS);
-
-/* Set all the properties of a raster */
-Datum RASTER_setSRID(PG_FUNCTION_ARGS);
-Datum RASTER_setScale(PG_FUNCTION_ARGS);
-Datum RASTER_setScaleXY(PG_FUNCTION_ARGS);
-Datum RASTER_setSkew(PG_FUNCTION_ARGS);
-Datum RASTER_setSkewXY(PG_FUNCTION_ARGS);
-Datum RASTER_setUpperLeftXY(PG_FUNCTION_ARGS);
-Datum RASTER_setRotation(PG_FUNCTION_ARGS);
-Datum RASTER_setGeotransform(PG_FUNCTION_ARGS);
-
-/* Get all the properties of a raster band */
-Datum RASTER_getBandPixelType(PG_FUNCTION_ARGS);
-Datum RASTER_getBandPixelTypeName(PG_FUNCTION_ARGS);
-Datum RASTER_getBandNoDataValue(PG_FUNCTION_ARGS);
-Datum RASTER_getBandPath(PG_FUNCTION_ARGS);
-Datum RASTER_bandIsNoData(PG_FUNCTION_ARGS);
-Datum RASTER_isEmpty(PG_FUNCTION_ARGS);
-Datum RASTER_hasNoBand(PG_FUNCTION_ARGS);
-
-/* Set all the properties of a raster band */
-Datum RASTER_setBandIsNoData(PG_FUNCTION_ARGS);
-Datum RASTER_setBandNoDataValue(PG_FUNCTION_ARGS);
-
-/* Get pixel value */
-Datum RASTER_getPixelValue(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);
-
-/* Get pixel geographical shape */
-Datum RASTER_getPixelPolygons(PG_FUNCTION_ARGS);
-
-/* Get raster band's polygon */
-Datum RASTER_getPolygon(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);
-
-/* Raster and band creation */
-Datum RASTER_makeEmpty(PG_FUNCTION_ARGS);
-Datum RASTER_addBand(PG_FUNCTION_ARGS);
-Datum RASTER_copyBand(PG_FUNCTION_ARGS);
-Datum RASTER_addBandRasterArray(PG_FUNCTION_ARGS);
-Datum RASTER_addBandOutDB(PG_FUNCTION_ARGS);
-Datum RASTER_tile(PG_FUNCTION_ARGS);
-
-/* create new raster from existing raster's bands */
-Datum RASTER_band(PG_FUNCTION_ARGS);
-
-/* Get summary stats */
-Datum RASTER_summaryStats(PG_FUNCTION_ARGS);
-Datum RASTER_summaryStatsCoverage(PG_FUNCTION_ARGS);
-
-/* get histogram */
-Datum RASTER_histogram(PG_FUNCTION_ARGS);
-Datum RASTER_histogramCoverage(PG_FUNCTION_ARGS);
-
-/* get quantiles */
-Datum RASTER_quantile(PG_FUNCTION_ARGS);
-Datum RASTER_quantileCoverage(PG_FUNCTION_ARGS);
-
-/* get counts of values */
-Datum RASTER_valueCount(PG_FUNCTION_ARGS);
-Datum RASTER_valueCountCoverage(PG_FUNCTION_ARGS);
-
-/* reclassify specified bands of a raster */
-Datum RASTER_reclass(PG_FUNCTION_ARGS);
-
-/* apply colormap to specified band of a raster */
-Datum RASTER_colorMap(PG_FUNCTION_ARGS);
-
-/* convert GDAL raster to raster */
-Datum RASTER_fromGDALRaster(PG_FUNCTION_ARGS);
-
-/* convert raster to GDAL raster */
-Datum RASTER_asGDALRaster(PG_FUNCTION_ARGS);
-Datum RASTER_getGDALDrivers(PG_FUNCTION_ARGS);
-
-/* rasterize a geometry */
-Datum RASTER_asRaster(PG_FUNCTION_ARGS);
-
-/* warp a raster using GDAL Warp API */
-Datum RASTER_GDALWarp(PG_FUNCTION_ARGS);
-
-/* get raster's meta data */
-Datum RASTER_metadata(PG_FUNCTION_ARGS);
-
-/* get raster band's meta data */
-Datum RASTER_bandmetadata(PG_FUNCTION_ARGS);
-
-/* convert pixel/line to spatial coordinates */
-Datum RASTER_rasterToWorldCoord(PG_FUNCTION_ARGS);
-
-/* convert spatial coordinates to pixel/line*/
-Datum RASTER_worldToRasterCoord(PG_FUNCTION_ARGS);
-
-/* determine if two rasters intersect */
-Datum RASTER_intersects(PG_FUNCTION_ARGS);
-
-/* determine if two rasters overlap */
-Datum RASTER_overlaps(PG_FUNCTION_ARGS);
-
-/* determine if two rasters touch */
-Datum RASTER_touches(PG_FUNCTION_ARGS);
-
-/* determine if the first raster contains the second raster */
-Datum RASTER_contains(PG_FUNCTION_ARGS);
-
-/* determine if the first raster contains properly the second raster */
-Datum RASTER_containsProperly(PG_FUNCTION_ARGS);
-
-/* determine if the first raster covers the second raster */
-Datum RASTER_covers(PG_FUNCTION_ARGS);
-
-/* determine if the first raster is covered by the second raster */
-Datum RASTER_coveredby(PG_FUNCTION_ARGS);
-
-/* determine if the two rasters are within the specified distance of each other */
-Datum RASTER_dwithin(PG_FUNCTION_ARGS);
-
-/* determine if the two rasters are fully within the specified distance of each other */
-Datum RASTER_dfullywithin(PG_FUNCTION_ARGS);
-
-/* determine if two rasters are aligned */
-Datum RASTER_sameAlignment(PG_FUNCTION_ARGS);
-Datum RASTER_notSameAlignmentReason(PG_FUNCTION_ARGS);
-
-/* one-raster MapAlgebra */
-Datum RASTER_mapAlgebraExpr(PG_FUNCTION_ARGS);
-Datum RASTER_mapAlgebraFct(PG_FUNCTION_ARGS);
-
-/* one-raster neighborhood MapAlgebra */
-Datum RASTER_mapAlgebraFctNgb(PG_FUNCTION_ARGS);
-
-/* two-raster MapAlgebra */
-Datum RASTER_mapAlgebra2(PG_FUNCTION_ARGS);
-
-/* n-raster MapAlgebra */
-Datum RASTER_nMapAlgebra(PG_FUNCTION_ARGS);
-Datum RASTER_nMapAlgebraExpr(PG_FUNCTION_ARGS);
-
-/* raster union aggregate */
-Datum RASTER_union_transfn(PG_FUNCTION_ARGS);
-Datum RASTER_union_finalfn(PG_FUNCTION_ARGS);
-
-/* raster clip */
-Datum RASTER_clip(PG_FUNCTION_ARGS);
-
-/* Module load callback */
-void
-_PG_init(void) {
- /* Install liblwgeom handlers */
- pg_install_lwgeom_handlers();
-
- /* TODO: Install raster callbacks (see rt_init_allocators)??? */
-
- /* Define custom GUC variables. */
- DefineCustomStringVariable(
- "postgis.gdal.datapath", /* name */
- "Path to GDAL data files.", /* short_desc */
- "Physical path to directory containing GDAL data files (sets the GDAL_DATA config option).", /* long_desc */
- &gdaldatapath, /* valueAddr */
- NULL, /* bootValue */
- PGC_SUSET, /* GucContext context */
- 0, /* int flags */
-#if POSTGIS_PGSQL_VERSION >= 91
- NULL, /* GucStringCheckHook check_hook */
-#endif
- rtpg_assignHookGDALDataPath, /* GucStringAssignHook assign_hook */
- NULL /* GucShowHook show_hook */
- );
-}
-
-/* string replacement function taken from
- * http://ubuntuforums.org/showthread.php?s=aa6f015109fd7e4c7e30d2fd8b717497&t=141670&page=3
- */
-/* ---------------------------------------------------------------------------
- Name : replace - Search & replace a substring by another one.
- Creation : Thierry Husson, Sept 2010
- Parameters :
- str : Big string where we search
- oldstr : Substring we are looking for
- newstr : Substring we want to replace with
- count : Optional pointer to int (input / output value). NULL to ignore.
- Input: Maximum replacements to be done. NULL or < 1 to do all.
- Output: Number of replacements done or -1 if not enough memory.
- Returns : Pointer to the new string or NULL if error.
- Notes :
- - Case sensitive - Otherwise, replace functions "strstr" by "strcasestr"
- - Always allocates memory for the result.
---------------------------------------------------------------------------- */
-static char*
-rtpg_strreplace(
- const char *str,
- const char *oldstr, const char *newstr,
- int *count
-) {
- const char *tmp = str;
- char *result;
- int found = 0;
- int length, reslen;
- int oldlen = strlen(oldstr);
- int newlen = strlen(newstr);
- int limit = (count != NULL && *count > 0) ? *count : -1;
-
- tmp = str;
- while ((tmp = strstr(tmp, oldstr)) != NULL && found != limit)
- found++, tmp += oldlen;
-
- length = strlen(str) + found * (newlen - oldlen);
- if ((result = (char *) palloc(length + 1)) == NULL) {
- fprintf(stderr, "Not enough memory\n");
- found = -1;
- }
- else {
- tmp = str;
- limit = found; /* Countdown */
- reslen = 0; /* length of current result */
-
- /* Replace each old string found with new string */
- while ((limit-- > 0) && (tmp = strstr(tmp, oldstr)) != NULL) {
- length = (tmp - str); /* Number of chars to keep intouched */
- strncpy(result + reslen, str, length); /* Original part keeped */
- strcpy(result + (reslen += length), newstr); /* Insert new string */
-
- reslen += newlen;
- tmp += oldlen;
- str = tmp;
- }
- strcpy(result + reslen, str); /* Copies last part and ending null char */
- }
-
- if (count != NULL) *count = found;
- return result;
-}
-
-static char *
-rtpg_strtoupper(char * str) {
- int j;
-
- for (j = strlen(str) - 1; j >= 0; j--)
- str[j] = toupper(str[j]);
-
- return str;
-}
-
-static char*
-rtpg_chartrim(const char *input, char *remove) {
- char *rtn = NULL;
- char *ptr = NULL;
- uint32_t offset = 0;
-
- if (!input)
- return NULL;
- else if (!*input)
- return (char *) input;
-
- /* trim left */
- while (strchr(remove, *input) != NULL)
- input++;
-
- /* trim right */
- ptr = ((char *) input) + strlen(input);
- while (strchr(remove, *--ptr) != NULL)
- offset++;
-
- rtn = palloc(sizeof(char) * (strlen(input) - offset + 1));
- if (rtn == NULL) {
- fprintf(stderr, "Not enough memory\n");
- return NULL;
- }
- strncpy(rtn, input, strlen(input) - offset);
- rtn[strlen(input) - offset] = '\0';
-
- return rtn;
-}
-
-/* split a string based on a delimiter */
-static char**
-rtpg_strsplit(const char *str, const char *delimiter, int *n) {
- char *tmp = NULL;
- char **rtn = NULL;
- char *token = NULL;
-
- *n = 0;
- if (!str)
- return NULL;
-
- /* copy str to tmp as strtok will mangle the string */
- tmp = palloc(sizeof(char) * (strlen(str) + 1));
- if (NULL == tmp) {
- fprintf(stderr, "Not enough memory\n");
- return NULL;
- }
- strcpy(tmp, str);
-
- if (!strlen(tmp) || !delimiter || !strlen(delimiter)) {
- *n = 1;
- rtn = (char **) palloc(*n * sizeof(char *));
- if (NULL == rtn) {
- fprintf(stderr, "Not enough memory\n");
- return NULL;
- }
- rtn[0] = (char *) palloc(sizeof(char) * (strlen(tmp) + 1));
- if (NULL == rtn[0]) {
- fprintf(stderr, "Not enough memory\n");
- return NULL;
- }
- strcpy(rtn[0], tmp);
- pfree(tmp);
- return rtn;
- }
-
- token = strtok(tmp, delimiter);
- while (token != NULL) {
- if (*n < 1) {
- rtn = (char **) palloc(sizeof(char *));
- }
- else {
- rtn = (char **) repalloc(rtn, (*n + 1) * sizeof(char *));
- }
- if (NULL == rtn) {
- fprintf(stderr, "Not enough memory\n");
- return NULL;
- }
-
- rtn[*n] = NULL;
- rtn[*n] = (char *) palloc(sizeof(char) * (strlen(token) + 1));
- if (NULL == rtn[*n]) {
- fprintf(stderr, "Not enough memory\n");
- return NULL;
- }
-
- strcpy(rtn[*n], token);
- *n = *n + 1;
-
- token = strtok(NULL, delimiter);
- }
-
- pfree(tmp);
- return rtn;
-}
-
-static char *
-rtpg_removespaces(char *str) {
- char *rtn;
- char *tmp;
-
- rtn = rtpg_strreplace(str, " ", "", NULL);
-
- tmp = rtpg_strreplace(rtn, "\n", "", NULL);
- pfree(rtn);
- rtn = rtpg_strreplace(tmp, "\t", "", NULL);
- pfree(tmp);
- tmp = rtpg_strreplace(rtn, "\f", "", NULL);
- pfree(rtn);
- rtn = rtpg_strreplace(tmp, "\r", "", NULL);
- pfree(tmp);
-
- return rtn;
-}
-
-static char*
-rtpg_trim(const char *input) {
- char *rtn;
- char *ptr;
- uint32_t offset = 0;
- int inputlen = 0;
-
- if (!input)
- return NULL;
- else if (!*input)
- return (char *) input;
-
- /* trim left */
- while (isspace(*input) && *input != '\0')
- input++;
-
- /* trim right */
- inputlen = strlen(input);
- if (inputlen) {
- ptr = ((char *) input) + inputlen;
- while (isspace(*--ptr))
- offset++;
- }
-
- rtn = palloc(sizeof(char) * (inputlen - offset + 1));
- if (rtn == NULL) {
- fprintf(stderr, "Not enough memory\n");
- return NULL;
- }
- strncpy(rtn, input, inputlen - offset);
- rtn[inputlen - offset] = '\0';
-
- return rtn;
-}
-
-static char*
-rtpg_getSR(int srid) {
- int i = 0;
- int len = 0;
- char *sql = NULL;
- int spi_result;
- TupleDesc tupdesc;
- SPITupleTable *tuptable = NULL;
- HeapTuple tuple;
- char *tmp = NULL;
- char *srs = NULL;
-
-/*
-SELECT
- CASE
- WHEN (upper(auth_name) = 'EPSG' OR upper(auth_name) = 'EPSGA') AND length(COALESCE(auth_srid::text, '')) > 0
- THEN upper(auth_name) || ':' || auth_srid
- WHEN length(COALESCE(auth_name, '') || COALESCE(auth_srid::text, '')) > 0
- THEN COALESCE(auth_name, '') || COALESCE(auth_srid::text, '')
- ELSE ''
- END,
- proj4text,
- srtext
-FROM spatial_ref_sys
-WHERE srid = X
-LIMIT 1
-*/
-
- len = sizeof(char) * (strlen("SELECT CASE WHEN (upper(auth_name) = 'EPSG' OR upper(auth_name) = 'EPSGA') AND length(COALESCE(auth_srid::text, '')) > 0 THEN upper(auth_name) || ':' || auth_srid WHEN length(COALESCE(auth_name, '') || COALESCE(auth_srid::text, '')) > 0 THEN COALESCE(auth_name, '') || COALESCE(auth_srid::text, '') ELSE '' END, proj4text, srtext FROM spatial_ref_sys WHERE srid = LIMIT 1") + MAX_INT_CHARLEN + 1);
- sql = (char *) palloc(len);
- if (NULL == sql) {
- elog(ERROR, "rtpg_getSR: Could not allocate memory for sql\n");
- return NULL;
- }
-
- spi_result = SPI_connect();
- if (spi_result != SPI_OK_CONNECT) {
- pfree(sql);
- elog(ERROR, "rtpg_getSR: Could not connect to database using SPI\n");
- return NULL;
- }
-
- /* execute query */
- snprintf(sql, len, "SELECT CASE WHEN (upper(auth_name) = 'EPSG' OR upper(auth_name) = 'EPSGA') AND length(COALESCE(auth_srid::text, '')) > 0 THEN upper(auth_name) || ':' || auth_srid WHEN length(COALESCE(auth_name, '') || COALESCE(auth_srid::text, '')) > 0 THEN COALESCE(auth_name, '') || COALESCE(auth_srid::text, '') ELSE '' END, proj4text, srtext FROM spatial_ref_sys WHERE srid = %d LIMIT 1", srid);
- POSTGIS_RT_DEBUGF(4, "SRS query: %s", sql);
- spi_result = SPI_execute(sql, TRUE, 0);
- SPI_pfree(sql);
- if (spi_result != SPI_OK_SELECT || SPI_tuptable == NULL || SPI_processed != 1) {
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_finish();
- elog(ERROR, "rtpg_getSR: Cannot find SRID (%d) in spatial_ref_sys", srid);
- return NULL;
- }
-
- tupdesc = SPI_tuptable->tupdesc;
- tuptable = SPI_tuptable;
- tuple = tuptable->vals[0];
-
- /* which column to use? */
- for (i = 1; i < 4; i++) {
- tmp = SPI_getvalue(tuple, tupdesc, i);
-
- /* value AND GDAL supports this SR */
- if (
- SPI_result != SPI_ERROR_NOATTRIBUTE &&
- SPI_result != SPI_ERROR_NOOUTFUNC &&
- tmp != NULL &&
- strlen(tmp) &&
- rt_util_gdal_supported_sr(tmp)
- ) {
- POSTGIS_RT_DEBUGF(4, "Value for column %d is %s", i, tmp);
-
- len = strlen(tmp) + 1;
- srs = SPI_palloc(sizeof(char) * len);
- if (NULL == srs) {
- pfree(tmp);
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_finish();
- elog(ERROR, "rtpg_getSR: Could not allocate memory for spatial reference text\n");
- return NULL;
- }
- strncpy(srs, tmp, len);
- pfree(tmp);
-
- break;
- }
-
- if (tmp != NULL)
- pfree(tmp);
- continue;
- }
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_finish();
-
- /* unable to get SR info */
- if (srs == NULL) {
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_finish();
- elog(ERROR, "rtpg_getSR: Could not find a viable spatial reference for SRID (%d)", srid);
- return NULL;
- }
-
- return srs;
-}
-
-static void
-rtpg_assignHookGDALDataPath(const char *newpath, void *extra) {
- POSTGIS_RT_DEBUGF(4, "newpath = %s", newpath);
-
- /* clear finder cache */
- CPLFinderClean();
-
- /* clear cached OSR */
- OSRCleanup();
-
- /* set GDAL_DATA */
- CPLSetConfigOption("GDAL_DATA", newpath);
- POSTGIS_RT_DEBUGF(4, "GDAL_DATA = %s", CPLGetConfigOption("GDAL_DATA", NULL));
-}
-
-PG_FUNCTION_INFO_V1(RASTER_lib_version);
-Datum RASTER_lib_version(PG_FUNCTION_ARGS)
-{
- char ver[64];
- text *result;
-
- snprintf(ver, 64, "%s r%d", POSTGIS_LIB_VERSION, POSTGIS_SVN_REVISION);
- ver[63] = '\0';
-
- result = cstring2text(ver);
- PG_RETURN_TEXT_P(result);
-}
-
-PG_FUNCTION_INFO_V1(RASTER_lib_build_date);
-Datum RASTER_lib_build_date(PG_FUNCTION_ARGS)
-{
- char *ver = POSTGIS_BUILD_DATE;
- text *result;
- result = palloc(VARHDRSZ + strlen(ver));
- SET_VARSIZE(result, VARHDRSZ + strlen(ver));
- memcpy(VARDATA(result), ver, strlen(ver));
- PG_RETURN_POINTER(result);
-}
-
-PG_FUNCTION_INFO_V1(RASTER_gdal_version);
-Datum RASTER_gdal_version(PG_FUNCTION_ARGS)
-{
- const char *ver = rt_util_gdal_version("--version");
- text *result;
-
- /* add indicator if GDAL isn't configured right */
- if (!rt_util_gdal_configured()) {
- char *rtn = NULL;
- rtn = palloc(strlen(ver) + strlen(" GDAL_DATA not found") + 1);
- if (!rtn)
- result = cstring2text(ver);
- else {
- sprintf(rtn, "%s GDAL_DATA not found", ver);
- result = cstring2text(rtn);
- pfree(rtn);
- }
- }
- else
- result = cstring2text(ver);
-
- PG_RETURN_POINTER(result);
-}
-
-PG_FUNCTION_INFO_V1(RASTER_minPossibleValue);
-Datum RASTER_minPossibleValue(PG_FUNCTION_ARGS)
-{
- text *pixeltypetext = NULL;
- char *pixeltypechar = NULL;
- rt_pixtype pixtype = PT_END;
- double pixsize = 0;
-
- if (PG_ARGISNULL(0))
- PG_RETURN_NULL();
-
- pixeltypetext = PG_GETARG_TEXT_P(0);
- pixeltypechar = text_to_cstring(pixeltypetext);
-
- pixtype = rt_pixtype_index_from_name(pixeltypechar);
- if (pixtype == PT_END) {
- elog(ERROR, "RASTER_minPossibleValue: Invalid pixel type: %s", pixeltypechar);
- PG_RETURN_NULL();
- }
-
- pixsize = rt_pixtype_get_min_value(pixtype);
-
- /*
- correct pixsize of unsigned pixel types
- example: for PT_8BUI, the value is CHAR_MIN but if char is signed,
- the value returned is -127 instead of 0.
- */
- switch (pixtype) {
- case PT_1BB:
- case PT_2BUI:
- case PT_4BUI:
- case PT_8BUI:
- case PT_16BUI:
- case PT_32BUI:
- pixsize = 0;
- break;
- default:
- break;
- }
-
- PG_RETURN_FLOAT8(pixsize);
-}
-
-/**
- * Input is a string with hex chars in it.
- * Convert to binary and put in the result
- */
-PG_FUNCTION_INFO_V1(RASTER_in);
-Datum RASTER_in(PG_FUNCTION_ARGS)
-{
- rt_raster raster;
- char *hexwkb = PG_GETARG_CSTRING(0);
- void *result = NULL;
-
- POSTGIS_RT_DEBUG(3, "Starting");
-
- raster = rt_raster_from_hexwkb(hexwkb, strlen(hexwkb));
- if (raster == NULL)
- PG_RETURN_NULL();
-
- result = rt_raster_serialize(raster);
- rt_raster_destroy(raster);
- if (result == NULL)
- PG_RETURN_NULL();
-
- SET_VARSIZE(result, ((rt_pgraster*)result)->size);
- PG_RETURN_POINTER(result);
-}
-
-/**
- * Given a RASTER structure, convert it to Hex and put it in a string
- */
-PG_FUNCTION_INFO_V1(RASTER_out);
-Datum RASTER_out(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- uint32_t hexwkbsize = 0;
- char *hexwkb = NULL;
-
- POSTGIS_RT_DEBUG(3, "Starting");
-
- 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_out: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- hexwkb = rt_raster_to_hexwkb(raster, FALSE, &hexwkbsize);
- if (!hexwkb) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_out: Could not HEX-WKBize raster");
- PG_RETURN_NULL();
- }
-
- /* Free the raster objects used */
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- PG_RETURN_CSTRING(hexwkb);
-}
-
-/**
- * Return bytea object with raster in Well-Known-Binary form.
- */
-PG_FUNCTION_INFO_V1(RASTER_to_bytea);
-Datum RASTER_to_bytea(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- uint8_t *wkb = NULL;
- uint32_t wkb_size = 0;
- bytea *result = NULL;
- int result_size = 0;
-
- if (PG_ARGISNULL(0)) PG_RETURN_NULL();
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
-
- /* Get raster object */
- raster = rt_raster_deserialize(pgraster, FALSE);
- if (!raster) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_to_bytea: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- /* Parse raster to wkb object */
- wkb = rt_raster_to_wkb(raster, FALSE, &wkb_size);
- if (!wkb) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_to_bytea: Could not allocate and generate WKB data");
- PG_RETURN_NULL();
- }
-
- /* Create varlena object */
- result_size = wkb_size + VARHDRSZ;
- result = (bytea *)palloc(result_size);
- SET_VARSIZE(result, result_size);
- memcpy(VARDATA(result), wkb, VARSIZE(result) - VARHDRSZ);
-
- /* Free raster objects used */
- rt_raster_destroy(raster);
- pfree(wkb);
- PG_FREE_IF_COPY(pgraster, 0);
-
- PG_RETURN_POINTER(result);
-}
-
-/**
- * Return bytea object with raster in Well-Known-Binary form requested using ST_AsBinary function.
- */
-PG_FUNCTION_INFO_V1(RASTER_to_binary);
-Datum RASTER_to_binary(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- uint8_t *wkb = NULL;
- uint32_t wkb_size = 0;
- char *result = NULL;
- int result_size = 0;
- int outasin = FALSE;
-
- if (PG_ARGISNULL(0)) PG_RETURN_NULL();
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
-
- /* Get raster object */
- raster = rt_raster_deserialize(pgraster, FALSE);
- if (!raster) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_to_binary: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- if (!PG_ARGISNULL(1))
- outasin = PG_GETARG_BOOL(1);
-
- /* Parse raster to wkb object */
- wkb = rt_raster_to_wkb(raster, outasin, &wkb_size);
- if (!wkb) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_to_binary: Could not allocate and generate WKB data");
- PG_RETURN_NULL();
- }
-
- /* Create varlena object */
- result_size = wkb_size + VARHDRSZ;
- result = (char *)palloc(result_size);
- SET_VARSIZE(result, result_size);
- memcpy(VARDATA(result), wkb, VARSIZE(result) - VARHDRSZ);
-
- /* Free raster objects used */
- rt_raster_destroy(raster);
- pfree(wkb);
- PG_FREE_IF_COPY(pgraster, 0);
-
- PG_RETURN_POINTER(result);
-}
-
-/**
- * Return the convex hull of this raster
- */
-PG_FUNCTION_INFO_V1(RASTER_convex_hull);
-Datum RASTER_convex_hull(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster;
- rt_raster raster;
- LWGEOM *geom = NULL;
- GSERIALIZED* gser = NULL;
- size_t gser_size;
- int err = ES_NONE;
-
- bool minhull = FALSE;
-
- if (PG_ARGISNULL(0))
- PG_RETURN_NULL();
-
- /* # of args */
- if (PG_NARGS() > 1)
- minhull = TRUE;
-
- if (!minhull) {
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
- raster = rt_raster_deserialize(pgraster, TRUE);
- }
- else {
- 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_convex_hull: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- if (!minhull)
- err = rt_raster_get_convex_hull(raster, &geom);
- else {
- int nband = -1;
-
- /* get arg 1 */
- if (!PG_ARGISNULL(1)) {
- nband = PG_GETARG_INT32(1);
- if (!rt_raster_has_band(raster, nband - 1)) {
- elog(NOTICE, "Invalid band index (must use 1-based). Returning NULL");
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- PG_RETURN_NULL();
- }
- nband = nband - 1;
- }
-
- err = rt_raster_get_perimeter(raster, nband, &geom);
- }
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- if (err != ES_NONE) {
- elog(ERROR, "RASTER_convex_hull: Could not get raster's convex hull");
- PG_RETURN_NULL();
- }
- else if (geom == NULL) {
- elog(NOTICE, "Raster's convex hull is NULL");
- PG_RETURN_NULL();
- }
-
- gser = gserialized_from_lwgeom(geom, 0, &gser_size);
- lwgeom_free(geom);
-
- SET_VARSIZE(gser, gser_size);
- PG_RETURN_POINTER(gser);
-}
-
-PG_FUNCTION_INFO_V1(RASTER_dumpAsPolygons);
-Datum RASTER_dumpAsPolygons(PG_FUNCTION_ARGS) {
- FuncCallContext *funcctx;
- TupleDesc tupdesc;
- rt_geomval geomval;
- rt_geomval geomval2;
- int call_cntr;
- int max_calls;
-
- /* stuff done only on the first call of the function */
- if (SRF_IS_FIRSTCALL()) {
- MemoryContext oldcontext;
- int numbands;
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- int nband;
- bool exclude_nodata_value = TRUE;
- int nElements;
-
- POSTGIS_RT_DEBUG(2, "RASTER_dumpAsPolygons first call");
-
- /* 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);
- }
-
- if (!PG_ARGISNULL(1))
- nband = PG_GETARG_UINT32(1);
- else
- nband = 1; /* By default, first band */
-
- POSTGIS_RT_DEBUGF(3, "band %d", nband);
- numbands = rt_raster_get_num_bands(raster);
-
- if (nband < 1 || nband > numbands) {
- elog(NOTICE, "Invalid band index (must use 1-based). Returning NULL");
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- if (!PG_ARGISNULL(2))
- exclude_nodata_value = PG_GETARG_BOOL(2);
-
- /* see if band is NODATA */
- if (rt_band_get_isnodata_flag(rt_raster_get_band(raster, nband - 1))) {
- POSTGIS_RT_DEBUGF(3, "Band at index %d is NODATA. Returning NULL", nband);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- /* Polygonize raster */
-
- /**
- * Dump raster
- */
- geomval = rt_raster_gdal_polygonize(raster, nband - 1, exclude_nodata_value, &nElements);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- if (NULL == geomval) {
- ereport(ERROR, (
- errcode(ERRCODE_NO_DATA_FOUND),
- errmsg("Could not polygonize raster")
- ));
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- POSTGIS_RT_DEBUGF(3, "raster dump, %d elements returned", nElements);
-
- /* Store needed information */
- funcctx->user_fctx = geomval;
-
- /* total number of tuples to be returned */
- funcctx->max_calls = nElements;
-
- /* 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;
- geomval2 = funcctx->user_fctx;
-
- /* do when there is more left to send */
- if (call_cntr < max_calls) {
- int values_length = 2;
- Datum values[values_length];
- bool nulls[values_length];
- HeapTuple tuple;
- Datum result;
-
- GSERIALIZED *gser = NULL;
- size_t gser_size = 0;
-
- POSTGIS_RT_DEBUGF(3, "call number %d", call_cntr);
-
- memset(nulls, FALSE, sizeof(bool) * values_length);
-
- /* convert LWGEOM to GSERIALIZED */
- gser = gserialized_from_lwgeom(lwpoly_as_lwgeom(geomval2[call_cntr].geom), 0, &gser_size);
- lwgeom_free(lwpoly_as_lwgeom(geomval2[call_cntr].geom));
-
- values[0] = PointerGetDatum(gser);
- values[1] = Float8GetDatum(geomval2[call_cntr].val);
-
- /* build a tuple */
- tuple = heap_form_tuple(tupdesc, values, nulls);
-
- /* make the tuple into a datum */
- result = HeapTupleGetDatum(tuple);
-
- SRF_RETURN_NEXT(funcctx, result);
- }
- /* do when there is no more left */
- else {
- pfree(geomval2);
- SRF_RETURN_DONE(funcctx);
- }
-}
-
-/**
- * Make a new raster with no bands
- */
-PG_FUNCTION_INFO_V1(RASTER_makeEmpty);
-Datum RASTER_makeEmpty(PG_FUNCTION_ARGS)
-{
- uint16 width = 0, height = 0;
- double ipx = 0, ipy = 0, scalex = 0, scaley = 0, skewx = 0, skewy = 0;
- int32_t srid = SRID_UNKNOWN;
- rt_pgraster *pgraster = NULL;
- rt_raster raster;
-
- if (PG_NARGS() < 9) {
- elog(ERROR, "RASTER_makeEmpty: ST_MakeEmptyRaster requires 9 args");
- PG_RETURN_NULL();
- }
-
- if (!PG_ARGISNULL(0))
- width = PG_GETARG_UINT16(0);
-
- if (!PG_ARGISNULL(1))
- height = PG_GETARG_UINT16(1);
-
- if (!PG_ARGISNULL(2))
- ipx = PG_GETARG_FLOAT8(2);
-
- if (!PG_ARGISNULL(3))
- ipy = PG_GETARG_FLOAT8(3);
-
- if (!PG_ARGISNULL(4))
- scalex = PG_GETARG_FLOAT8(4);
-
- if (!PG_ARGISNULL(5))
- scaley = PG_GETARG_FLOAT8(5);
-
- if (!PG_ARGISNULL(6))
- skewx = PG_GETARG_FLOAT8(6);
-
- if (!PG_ARGISNULL(7))
- skewy = PG_GETARG_FLOAT8(7);
-
- if (!PG_ARGISNULL(8))
- srid = PG_GETARG_INT32(8);
-
- POSTGIS_RT_DEBUGF(4, "%dx%d, ip:%g,%g, scale:%g,%g, skew:%g,%g srid:%d",
- width, height, ipx, ipy, scalex, scaley,
- skewx, skewy, srid);
-
- raster = rt_raster_new(width, height);
- if (raster == NULL)
- PG_RETURN_NULL(); /* error was supposedly printed already */
-
- rt_raster_set_scale(raster, scalex, scaley);
- rt_raster_set_offsets(raster, ipx, ipy);
- rt_raster_set_skews(raster, skewx, skewy);
- rt_raster_set_srid(raster, srid);
-
- pgraster = rt_raster_serialize(raster);
- rt_raster_destroy(raster);
- if (!pgraster)
- PG_RETURN_NULL();
-
- SET_VARSIZE(pgraster, pgraster->size);
- PG_RETURN_POINTER(pgraster);
-}
-
-/**
- * Return the SRID associated with the raster.
- */
-PG_FUNCTION_INFO_V1(RASTER_getSRID);
-Datum RASTER_getSRID(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster;
- rt_raster raster;
- int32_t srid;
-
- if (PG_ARGISNULL(0)) PG_RETURN_NULL();
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
-
- raster = rt_raster_deserialize(pgraster, TRUE);
- if ( ! raster ) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_getSRID: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- srid = rt_raster_get_srid(raster);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- PG_RETURN_INT32(srid);
-}
-
-/**
- * Set the SRID associated with the raster.
- */
-PG_FUNCTION_INFO_V1(RASTER_setSRID);
-Datum RASTER_setSRID(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_pgraster *pgrtn = NULL;
- rt_raster raster;
- int32_t newSRID = PG_GETARG_INT32(1);
-
- 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_setSRID: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- rt_raster_set_srid(raster, newSRID);
-
- 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);
-}
-
-/**
- * Return the width of the raster.
- */
-PG_FUNCTION_INFO_V1(RASTER_getWidth);
-Datum RASTER_getWidth(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster;
- rt_raster raster;
- uint16_t width;
-
- if (PG_ARGISNULL(0)) PG_RETURN_NULL();
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
-
- raster = rt_raster_deserialize(pgraster, TRUE);
- if ( ! raster ) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_getWidth: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- width = rt_raster_get_width(raster);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- PG_RETURN_INT32(width);
-}
-
-/**
- * Return the height of the raster.
- */
-PG_FUNCTION_INFO_V1(RASTER_getHeight);
-Datum RASTER_getHeight(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster;
- rt_raster raster;
- uint16_t height;
-
- if (PG_ARGISNULL(0)) PG_RETURN_NULL();
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
-
- raster = rt_raster_deserialize(pgraster, TRUE);
- if ( ! raster ) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_getHeight: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- height = rt_raster_get_height(raster);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- PG_RETURN_INT32(height);
-}
-
-/**
- * Return the number of bands included in the raster.
- */
-PG_FUNCTION_INFO_V1(RASTER_getNumBands);
-Datum RASTER_getNumBands(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster;
- rt_raster raster;
- int32_t num_bands;
-
- if (PG_ARGISNULL(0)) PG_RETURN_NULL();
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
-
- raster = rt_raster_deserialize(pgraster, TRUE);
- if ( ! raster ) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_getNumBands: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- num_bands = rt_raster_get_num_bands(raster);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- PG_RETURN_INT32(num_bands);
-}
-
-/**
- * Return X scale from georeference of the raster.
- */
-PG_FUNCTION_INFO_V1(RASTER_getXScale);
-Datum RASTER_getXScale(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster;
- rt_raster raster;
- double xsize;
-
- if (PG_ARGISNULL(0)) PG_RETURN_NULL();
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
-
- raster = rt_raster_deserialize(pgraster, TRUE);
- if ( ! raster ) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_getXScale: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- xsize = rt_raster_get_x_scale(raster);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- PG_RETURN_FLOAT8(xsize);
-}
-
-/**
- * Return Y scale from georeference of the raster.
- */
-PG_FUNCTION_INFO_V1(RASTER_getYScale);
-Datum RASTER_getYScale(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster;
- rt_raster raster;
- double ysize;
-
- if (PG_ARGISNULL(0)) PG_RETURN_NULL();
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
-
- raster = rt_raster_deserialize(pgraster, TRUE);
- if ( ! raster ) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_getYScale: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- ysize = rt_raster_get_y_scale(raster);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- PG_RETURN_FLOAT8(ysize);
-}
-
-/**
- * Set the scale of the raster.
- */
-PG_FUNCTION_INFO_V1(RASTER_setScale);
-Datum RASTER_setScale(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_pgraster *pgrtn = NULL;
- rt_raster raster;
- double size = PG_GETARG_FLOAT8(1);
-
- 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_setScale: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- rt_raster_set_scale(raster, size, size);
-
- 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 the pixel size of the raster.
- */
-PG_FUNCTION_INFO_V1(RASTER_setScaleXY);
-Datum RASTER_setScaleXY(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_pgraster *pgrtn = NULL;
- rt_raster raster;
- double xscale = PG_GETARG_FLOAT8(1);
- double yscale = PG_GETARG_FLOAT8(2);
-
- 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_setScaleXY: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- rt_raster_set_scale(raster, xscale, yscale);
- 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);
-}
-
-/**
- * Return value of the raster skew about the X axis.
- */
-PG_FUNCTION_INFO_V1(RASTER_getXSkew);
-Datum RASTER_getXSkew(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster;
- rt_raster raster;
- double xskew;
-
- if (PG_ARGISNULL(0)) PG_RETURN_NULL();
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
-
- raster = rt_raster_deserialize(pgraster, TRUE);
- if ( ! raster ) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_getXSkew: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- xskew = rt_raster_get_x_skew(raster);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- PG_RETURN_FLOAT8(xskew);
-}
-
-/**
- * Return value of the raster skew about the Y axis.
- */
-PG_FUNCTION_INFO_V1(RASTER_getYSkew);
-Datum RASTER_getYSkew(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster;
- rt_raster raster;
- double yskew;
-
- if (PG_ARGISNULL(0)) PG_RETURN_NULL();
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
-
- raster = rt_raster_deserialize(pgraster, TRUE);
- if ( ! raster ) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_getYSkew: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- yskew = rt_raster_get_y_skew(raster);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- PG_RETURN_FLOAT8(yskew);
-}
-
-/**
- * Set the skew of the raster.
- */
-PG_FUNCTION_INFO_V1(RASTER_setSkew);
-Datum RASTER_setSkew(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_pgraster *pgrtn = NULL;
- rt_raster raster;
- double skew = PG_GETARG_FLOAT8(1);
-
- 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_setSkew: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- rt_raster_set_skews(raster, skew, skew);
-
- 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 the skew of the raster.
- */
-PG_FUNCTION_INFO_V1(RASTER_setSkewXY);
-Datum RASTER_setSkewXY(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_pgraster *pgrtn = NULL;
- rt_raster raster;
- double xskew = PG_GETARG_FLOAT8(1);
- double yskew = PG_GETARG_FLOAT8(2);
-
- 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_setSkewXY: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- rt_raster_set_skews(raster, xskew, yskew);
-
- 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);
-}
-
-/**
- * Return value of the raster offset in the X dimension.
- */
-PG_FUNCTION_INFO_V1(RASTER_getXUpperLeft);
-Datum RASTER_getXUpperLeft(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster;
- rt_raster raster;
- double xul;
-
- if (PG_ARGISNULL(0)) PG_RETURN_NULL();
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
-
- raster = rt_raster_deserialize(pgraster, TRUE);
- if ( ! raster ) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_getXUpperLeft: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- xul = rt_raster_get_x_offset(raster);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- PG_RETURN_FLOAT8(xul);
-}
-
-/**
- * Return value of the raster offset in the Y dimension.
- */
-PG_FUNCTION_INFO_V1(RASTER_getYUpperLeft);
-Datum RASTER_getYUpperLeft(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster;
- rt_raster raster;
- double yul;
-
- if (PG_ARGISNULL(0)) PG_RETURN_NULL();
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
-
- raster = rt_raster_deserialize(pgraster, TRUE);
- if ( ! raster ) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_getYUpperLeft: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- yul = rt_raster_get_y_offset(raster);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- PG_RETURN_FLOAT8(yul);
-}
-
-/**
- * Set the raster offset in the X and Y dimension.
- */
-PG_FUNCTION_INFO_V1(RASTER_setUpperLeftXY);
-Datum RASTER_setUpperLeftXY(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_pgraster *pgrtn = NULL;
- rt_raster raster;
- double xoffset = PG_GETARG_FLOAT8(1);
- double yoffset = PG_GETARG_FLOAT8(2);
-
- 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_setUpperLeftXY: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- rt_raster_set_offsets(raster, xoffset, yoffset);
-
- 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);
-}
-
-/**
- * Return the pixel width of the raster. The pixel width is
- * a read-only, dynamically computed value derived from the
- * X Scale and the Y Skew.
- *
- * Pixel Width = sqrt( X Scale * X Scale + Y Skew * Y Skew )
- */
-PG_FUNCTION_INFO_V1(RASTER_getPixelWidth);
-Datum RASTER_getPixelWidth(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster;
- rt_raster raster;
- double xscale;
- double yskew;
- double pwidth;
-
- if (PG_ARGISNULL(0)) PG_RETURN_NULL();
- pgraster = (rt_pgraster *)PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
-
- raster = rt_raster_deserialize(pgraster, TRUE);
- if (!raster) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_getPixelWidth: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- xscale = rt_raster_get_x_scale(raster);
- yskew = rt_raster_get_y_skew(raster);
- pwidth = sqrt(xscale*xscale + yskew*yskew);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- PG_RETURN_FLOAT8(pwidth);
-}
-
-/**
- * Return the pixel height of the raster. The pixel height is
- * a read-only, dynamically computed value derived from the
- * Y Scale and the X Skew.
- *
- * Pixel Height = sqrt( Y Scale * Y Scale + X Skew * X Skew )
- */
-PG_FUNCTION_INFO_V1(RASTER_getPixelHeight);
-Datum RASTER_getPixelHeight(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster;
- rt_raster raster;
- double yscale;
- double xskew;
- double pheight;
-
- if (PG_ARGISNULL(0)) PG_RETURN_NULL();
- pgraster = (rt_pgraster *)PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
-
- raster = rt_raster_deserialize(pgraster, TRUE);
- if (!raster) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_getPixelHeight: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- yscale = rt_raster_get_y_scale(raster);
- xskew = rt_raster_get_x_skew(raster);
- pheight = sqrt(yscale*yscale + xskew*xskew);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- PG_RETURN_FLOAT8(pheight);
-}
-
-/**
- * Set the geotransform of the supplied raster. Returns the raster.
- */
-PG_FUNCTION_INFO_V1(RASTER_setGeotransform);
-Datum RASTER_setGeotransform(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_pgraster *pgrtn = NULL;
- rt_raster raster;
- float8 imag, jmag, theta_i, theta_ij, xoffset, yoffset;
-
- if (
- PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2) ||
- PG_ARGISNULL(3) || PG_ARGISNULL(4) ||
- PG_ARGISNULL(5) || PG_ARGISNULL(6)
- ) {
- PG_RETURN_NULL();
- }
-
- /* get the inputs */
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
- imag = PG_GETARG_FLOAT8(1);
- jmag = PG_GETARG_FLOAT8(2);
- theta_i = PG_GETARG_FLOAT8(3);
- theta_ij = PG_GETARG_FLOAT8(4);
- xoffset = PG_GETARG_FLOAT8(5);
- yoffset = PG_GETARG_FLOAT8(6);
-
- raster = rt_raster_deserialize(pgraster, TRUE);
- if (!raster) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_setGeotransform: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- /* store the new geotransform */
- rt_raster_set_phys_params(raster, imag,jmag,theta_i,theta_ij);
- rt_raster_set_offsets(raster, xoffset, yoffset);
-
- /* prep the return value */
- 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);
-}
-
-/**
- * Calculates the physically relevant parameters of the supplied raster's
- * geotransform. Returns them as a set.
- */
-PG_FUNCTION_INFO_V1(RASTER_getGeotransform);
-Datum RASTER_getGeotransform(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
-
- double imag;
- double jmag;
- double theta_i;
- double theta_ij;
- /*
- double xoffset;
- double yoffset;
- */
-
- TupleDesc result_tuple; /* for returning a composite */
- int values_length = 6;
- Datum values[values_length];
- bool nulls[values_length];
- HeapTuple heap_tuple ; /* instance of the tuple to return */
- Datum result;
-
- POSTGIS_RT_DEBUG(3, "RASTER_getGeotransform: Starting");
-
- /* get argument */
- if (PG_ARGISNULL(0))
- PG_RETURN_NULL();
- pgraster = (rt_pgraster *)PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
-
- /* raster */
- raster = rt_raster_deserialize(pgraster, TRUE);
- if (!raster) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_getGeotransform: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- /* do the calculation */
- rt_raster_calc_phys_params(
- rt_raster_get_x_scale(raster),
- rt_raster_get_x_skew(raster),
- rt_raster_get_y_skew(raster),
- rt_raster_get_y_scale(raster),
- &imag, &jmag, &theta_i, &theta_ij) ;
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- /* setup the return value infrastructure */
- if (get_call_result_type(fcinfo, NULL, &result_tuple) != TYPEFUNC_COMPOSITE) {
- ereport(ERROR, (
- errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("RASTER_getGeotransform(): function returning record called in context that cannot accept type record"
- )
- ));
- PG_RETURN_NULL();
- }
-
- BlessTupleDesc(result_tuple);
-
- /* get argument */
- /* prep the composite return value */
- /* construct datum array */
- values[0] = Float8GetDatum(imag);
- values[1] = Float8GetDatum(jmag);
- values[2] = Float8GetDatum(theta_i);
- values[3] = Float8GetDatum(theta_ij);
- values[4] = Float8GetDatum(rt_raster_get_x_offset(raster));
- values[5] = Float8GetDatum(rt_raster_get_y_offset(raster));
-
- memset(nulls, FALSE, sizeof(bool) * values_length);
-
- /* stick em on the heap */
- heap_tuple = heap_form_tuple(result_tuple, values, nulls);
-
- /* make the tuple into a datum */
- result = HeapTupleGetDatum(heap_tuple);
-
- PG_RETURN_DATUM(result);
-}
-
-
-/**
- * Set the rotation of the raster. This method will change the X Scale,
- * Y Scale, X Skew and Y Skew properties all at once to keep the rotations
- * about the X and Y axis uniform.
- *
- * This method will set the rotation about the X axis and Y axis based on
- * the pixel size. This pixel size may not be uniform if rasters have different
- * skew values (the raster cells are diamond-shaped). If a raster has different
- * skew values has a rotation set upon it, this method will remove the
- * diamond distortions of the cells, as each axis will have the same rotation.
- */
-PG_FUNCTION_INFO_V1(RASTER_setRotation);
-Datum RASTER_setRotation(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_pgraster *pgrtn = NULL;
- rt_raster raster;
- double rotation = PG_GETARG_FLOAT8(1);
- double imag, jmag, theta_i, theta_ij;
-
- 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_setRotation: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- /* preserve all defining characteristics of the grid except for rotation */
- rt_raster_get_phys_params(raster, &imag, &jmag, &theta_i, &theta_ij);
- rt_raster_set_phys_params(raster, imag, jmag, rotation, theta_ij);
-
- 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);
-}
-
-/**
- * Return pixel type of the specified band of raster.
- * Band index is 1-based.
- */
-PG_FUNCTION_INFO_V1(RASTER_getBandPixelType);
-Datum RASTER_getBandPixelType(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- rt_band band = NULL;
- rt_pixtype pixtype;
- int32_t bandindex;
-
- /* Deserialize raster */
- if (PG_ARGISNULL(0)) PG_RETURN_NULL();
- pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(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_FREE_IF_COPY(pgraster, 0);
- PG_RETURN_NULL();
- }
-
- raster = rt_raster_deserialize(pgraster, FALSE);
- if ( ! raster ) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_getBandPixelType: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- /* Fetch requested band and its pixel type */
- band = rt_raster_get_band(raster, bandindex - 1);
- if ( ! band ) {
- elog(NOTICE, "Could not find raster band of index %d when getting pixel type. Returning NULL", bandindex);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- PG_RETURN_NULL();
- }
-
- pixtype = rt_band_get_pixtype(band);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- PG_RETURN_INT32(pixtype);
-}
-
-/**
- * Return name of pixel type of the specified band of raster.
- * Band index is 1-based.
- * NOTE: This is unofficial utility not included in the spec.
- */
-PG_FUNCTION_INFO_V1(RASTER_getBandPixelTypeName);
-Datum RASTER_getBandPixelTypeName(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- rt_band band = NULL;
- rt_pixtype pixtype;
- int32_t bandindex;
- const size_t name_size = 8; /* size of type name */
- size_t size = 0;
- char *ptr = NULL;
- text *result = NULL;
-
- /* Deserialize raster */
- if (PG_ARGISNULL(0)) PG_RETURN_NULL();
- pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(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_FREE_IF_COPY(pgraster, 0);
- PG_RETURN_NULL();
- }
-
- raster = rt_raster_deserialize(pgraster, FALSE);
- if ( ! raster ) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_getBandPixelTypeName: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- /* Fetch requested band and its pixel type */
- band = rt_raster_get_band(raster, bandindex - 1);
- if ( ! band ) {
- elog(NOTICE, "Could not find raster band of index %d when getting pixel type name. Returning NULL", bandindex);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- PG_RETURN_NULL();
- }
-
- pixtype = rt_band_get_pixtype(band);
-
- result = palloc(VARHDRSZ + name_size);
- /* We don't need to check for NULL pointer, because if out of memory, palloc
- * exit via elog(ERROR). It never returns NULL.
- */
-
- memset(VARDATA(result), 0, name_size);
- ptr = (char *)result + VARHDRSZ;
- strcpy(ptr, rt_pixtype_name(pixtype));
-
- size = VARHDRSZ + strlen(ptr);
- SET_VARSIZE(result, size);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- PG_RETURN_TEXT_P(result);
-}
-
-/**
- * Return nodata value of the specified band of raster.
- * The value is always returned as FLOAT32 even if the pixel type is INTEGER.
- */
-PG_FUNCTION_INFO_V1(RASTER_getBandNoDataValue);
-Datum RASTER_getBandNoDataValue(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- rt_band band = NULL;
- int32_t bandindex;
- double nodata;
-
- /* Deserialize raster */
- if (PG_ARGISNULL(0)) PG_RETURN_NULL();
- pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(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_FREE_IF_COPY(pgraster, 0);
- PG_RETURN_NULL();
- }
-
- raster = rt_raster_deserialize(pgraster, FALSE);
- if ( ! raster ) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_getBandNoDataValue: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- /* Fetch requested band and its nodata value */
- band = rt_raster_get_band(raster, bandindex - 1);
- if ( ! band ) {
- elog(NOTICE, "Could not find raster band of index %d when getting band nodata value. Returning NULL", bandindex);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- PG_RETURN_NULL();
- }
-
- if ( ! rt_band_get_hasnodata_flag(band) ) {
- /* Raster does not have a nodata value set so we return NULL */
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- PG_RETURN_NULL();
- }
-
- rt_band_get_nodata(band, &nodata);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- PG_RETURN_FLOAT8(nodata);
-}
-
-
-/**
- * Set the nodata value of the specified band of raster.
- */
-PG_FUNCTION_INFO_V1(RASTER_setBandNoDataValue);
-Datum RASTER_setBandNoDataValue(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_pgraster *pgrtn = NULL;
- rt_raster raster = NULL;
- rt_band band = NULL;
- double nodata;
- int32_t bandindex;
- bool forcechecking = FALSE;
- bool skipset = FALSE;
-
- /* Deserialize raster */
- if (PG_ARGISNULL(0))
- PG_RETURN_NULL();
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
-
- /* Check index is not NULL or smaller than 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). Nodata value not set. Returning original raster");
- skipset = TRUE;
- }
-
- raster = rt_raster_deserialize(pgraster, FALSE);
- if (!raster) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_setBandNoDataValue: 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. Nodata value not set. Returning original raster", bandindex);
- }
- else {
- if (!PG_ARGISNULL(3))
- forcechecking = PG_GETARG_BOOL(3);
-
- if (PG_ARGISNULL(2)) {
- /* Set the hasnodata flag to FALSE */
- rt_band_set_hasnodata_flag(band, FALSE);
- POSTGIS_RT_DEBUGF(3, "Raster band %d does not have a nodata value", bandindex);
- }
- else {
- /* Get the nodata value */
- nodata = PG_GETARG_FLOAT8(2);
-
- /* Set the band's nodata value */
- rt_band_set_nodata(band, nodata, NULL);
-
- /* Recheck all pixels if requested */
- if (forcechecking)
- rt_band_check_is_nodata(band);
- }
- }
- }
-
- 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);
-}
-
-PG_FUNCTION_INFO_V1(RASTER_setBandIsNoData);
-Datum RASTER_setBandIsNoData(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_pgraster *pgrtn = NULL;
- rt_raster raster = NULL;
- rt_band band = NULL;
- int32_t bandindex;
-
- 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_setBandIsNoData: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- /* Check index is not NULL or smaller than 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). Isnodata flag not set. Returning original raster");
- else {
- /* Fetch requested band */
- band = rt_raster_get_band(raster, bandindex - 1);
-
- if (!band)
- elog(NOTICE, "Could not find raster band of index %d. Isnodata flag not set. Returning original raster", bandindex);
- else {
- if (!rt_band_get_hasnodata_flag(band)) {
- elog(NOTICE, "Band of index %d has no NODATA so cannot be NODATA. Returning original raster", bandindex);
- }
- /* Set the band's nodata value */
- else {
- rt_band_set_isnodata_flag(band, 1);
- }
- }
- }
-
- /* Serialize raster again */
- 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);
-}
-
-PG_FUNCTION_INFO_V1(RASTER_bandIsNoData);
-Datum RASTER_bandIsNoData(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- rt_band band = NULL;
- int32_t bandindex;
- bool forcechecking = FALSE;
- bool bandisnodata = FALSE;
-
- /* 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();
- }
-
- /* 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_bandIsNoData: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- /* Fetch requested band and its nodata value */
- band = rt_raster_get_band(raster, bandindex - 1);
- if ( ! band ) {
- elog(NOTICE, "Could not find raster band of index %d when determining if band is nodata. Returning NULL", bandindex);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- PG_RETURN_NULL();
- }
-
- forcechecking = PG_GETARG_BOOL(2);
-
- bandisnodata = (forcechecking) ?
- rt_band_check_is_nodata(band) : rt_band_get_isnodata_flag(band);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- PG_RETURN_BOOL(bandisnodata);
-}
-
-/**
- * Return the path of the raster for out-db raster.
- */
-PG_FUNCTION_INFO_V1(RASTER_getBandPath);
-Datum RASTER_getBandPath(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- rt_band band = NULL;
- int32_t bandindex;
- const char *bandpath;
- text *result;
-
- /* 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();
- }
-
- /* 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_getBandPath: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- /* Fetch requested band and its nodata value */
- band = rt_raster_get_band(raster, bandindex - 1);
- if ( ! band ) {
- elog(NOTICE, "Could not find raster band of index %d when getting band path. Returning NULL", bandindex);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- PG_RETURN_NULL();
- }
-
- bandpath = rt_band_get_ext_path(band);
- rt_band_destroy(band);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- if ( ! bandpath )
- {
- PG_RETURN_NULL();
- }
-
- result = (text *) palloc(VARHDRSZ + strlen(bandpath) + 1);
-
- SET_VARSIZE(result, VARHDRSZ + strlen(bandpath) + 1);
-
- strcpy((char *) VARDATA(result), bandpath);
-
- PG_RETURN_TEXT_P(result);
-}
-
-
-/**
- * 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);
-}
-
-/* ---------------------------------------------------------------- */
-/* 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) {
- if (arg->nbands != NULL)
- pfree(arg->nbands);
-
- 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]);
- }
-
- if (arg->values != NULL)
- pfree(arg->values);
- if (arg->nodata != NULL)
- pfree(arg->nodata);
- }
-
- pfree(arg);
-}
-
-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);
- }
- }
-
- }
- 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++) {
- 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) {
- int values_length = 2;
- Datum values[values_length];
- bool nulls[values_length];
- HeapTuple tuple;
- Datum result;
- ArrayType *mdValues = NULL;
- 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);
-
- /* assemble 3-dimension array of values */
- mdValues = construct_md_array(
- arg2->values[call_cntr], arg2->nodata[call_cntr],
- 2, 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;
- int 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;
- unsigned char *wkb = NULL;
- size_t wkb_len;
-
- int i = 0;
- int 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(arg->gv[arg->ngv].geom, WKB_SFSQL, &wkb_len);
-
- /* rasterize geometry */
- arg->gv[arg->ngv].mask = rt_raster_gdal_rasterize(
- wkb, wkb_len,
- 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,
- 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);
-}
-
-/**
- * Return the geographical shape of all pixels
- */
-PG_FUNCTION_INFO_V1(RASTER_getPixelPolygons);
-Datum RASTER_getPixelPolygons(PG_FUNCTION_ARGS)
-{
- FuncCallContext *funcctx;
- TupleDesc tupdesc;
- rt_pixel pix = NULL;
- rt_pixel pix2;
- int call_cntr;
- int max_calls;
- int i = 0;
-
- /* stuff done only on the first call of the function */
- if (SRF_IS_FIRSTCALL()) {
- MemoryContext oldcontext;
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- rt_band band = NULL;
- int nband = 1;
- int numbands;
- bool noband = FALSE;
- bool exclude_nodata_value = TRUE;
- bool nocolumnx = FALSE;
- bool norowy = FALSE;
- int x = 0;
- int y = 0;
- int bounds[4] = {0};
- int pixcount = 0;
- int isnodata = 0;
-
- LWPOLY *poly;
-
- /* create a function context for cross-call persistence */
- funcctx = SRF_FIRSTCALL_INIT();
-
- /* switch to memory context appropriate for multiple function calls */
- oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
-
- if (PG_ARGISNULL(0)) {
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
-
- /* band */
- if (PG_ARGISNULL(1))
- noband = TRUE;
- else {
- nband = PG_GETARG_INT32(1);
- noband = FALSE;
- }
-
- /* column */
- if (PG_ARGISNULL(2))
- nocolumnx = TRUE;
- else {
- bounds[0] = PG_GETARG_INT32(2);
- bounds[1] = bounds[0];
- }
-
- /* row */
- if (PG_ARGISNULL(3))
- norowy = TRUE;
- else {
- bounds[2] = PG_GETARG_INT32(3);
- bounds[3] = bounds[2];
- }
-
- /* exclude NODATA */
- if (!PG_ARGISNULL(4))
- exclude_nodata_value = PG_GETARG_BOOL(4);
-
- raster = rt_raster_deserialize(pgraster, FALSE);
- if (!raster) {
- PG_FREE_IF_COPY(pgraster, 0);
- ereport(ERROR, (
- errcode(ERRCODE_OUT_OF_MEMORY),
- errmsg("Could not deserialize raster")
- ));
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- /* raster empty, return NULL */
- if (rt_raster_is_empty(raster)) {
- elog(NOTICE, "Raster is empty. Returning NULL");
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- /* band specified, load band and info */
- if (!noband) {
- do {
- numbands = rt_raster_get_num_bands(raster);
- POSTGIS_RT_DEBUGF(3, "band %d", nband);
- POSTGIS_RT_DEBUGF(3, "# of bands %d", numbands);
-
- if (nband < 1 || nband > numbands) {
- elog(NOTICE, "Invalid band index (must use 1-based). Returning pixel values will be NULL");
- noband = TRUE;
- break;
- }
-
- band = rt_raster_get_band(raster, nband - 1);
- if (!band) {
- elog(NOTICE, "Could not find band at index %d. Returning pixel values will be NULL", nband);
- noband = TRUE;
- break;
- }
-
- if (!rt_band_get_hasnodata_flag(band))
- exclude_nodata_value = FALSE;
- }
- while (0);
- }
-
- /* set bounds if columnx, rowy not set */
- if (nocolumnx) {
- bounds[0] = 1;
- bounds[1] = rt_raster_get_width(raster);
- }
- if (norowy) {
- bounds[2] = 1;
- bounds[3] = rt_raster_get_height(raster);
- }
- POSTGIS_RT_DEBUGF(3, "bounds (min x, max x, min y, max y) = (%d, %d, %d, %d)",
- bounds[0], bounds[1], bounds[2], bounds[3]);
-
- /* rowy */
- pixcount = 0;
- for (y = bounds[2]; y <= bounds[3]; y++) {
- /* columnx */
- for (x = bounds[0]; x <= bounds[1]; x++) {
- /* geometry */
- poly = rt_raster_pixel_as_polygon(raster, x - 1, y - 1);
- if (!poly) {
- for (i = 0; i < pixcount; i++)
- lwgeom_free(pix[i].geom);
- if (pixcount) pfree(pix);
-
- if (!noband) rt_band_destroy(band);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_getPixelPolygons: Could not get pixel polygon");
- SRF_RETURN_DONE(funcctx);
- }
-
- if (!pixcount)
- pix = palloc(sizeof(struct rt_pixel_t) * (pixcount + 1));
- else
- pix = repalloc(pix, sizeof(struct rt_pixel_t) * (pixcount + 1));
- if (pix == NULL) {
-
- lwpoly_free(poly);
- if (!noband) rt_band_destroy(band);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_getPixelPolygons: Could not allocate memory for storing pixel polygons");
- SRF_RETURN_DONE(funcctx);
- }
- pix[pixcount].geom = (LWGEOM *) poly;
- /*
- POSTGIS_RT_DEBUGF(4, "poly @ %p", poly);
- POSTGIS_RT_DEBUGF(4, "geom @ %p", pix[pixcount].geom);
- */
-
- /* x, y */
- pix[pixcount].x = x;
- pix[pixcount].y = y;
-
- /* value, NODATA flag */
- if (!noband) {
- if (rt_band_get_pixel(band, x - 1, y - 1, &(pix[pixcount].value), &isnodata) != ES_NONE) {
-
- for (i = 0; i < pixcount; i++)
- lwgeom_free(pix[i].geom);
- if (pixcount) pfree(pix);
-
- if (!noband) rt_band_destroy(band);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_getPixelPolygons: Could not get pixel value");
- SRF_RETURN_DONE(funcctx);
- }
-
- if (!exclude_nodata_value || !isnodata) {
- pix[pixcount].nodata = 0;
- }
- else {
- pix[pixcount].nodata = 1;
- }
- }
- else {
- pix[pixcount].nodata = 1;
- }
-
- pixcount++;
- }
- }
-
- if (!noband) rt_band_destroy(band);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- /* Store needed information */
- funcctx->user_fctx = pix;
-
- /* total number of tuples to be returned */
- funcctx->max_calls = pixcount;
-
- /* Build a tuple descriptor for our result type */
- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) {
- ereport(ERROR, (
- errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("function returning record called in context that cannot accept type record")
- ));
- }
-
- BlessTupleDesc(tupdesc);
- funcctx->tuple_desc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
- }
-
- /* stuff done on every call of the function */
- funcctx = SRF_PERCALL_SETUP();
-
- call_cntr = funcctx->call_cntr;
- max_calls = funcctx->max_calls;
- tupdesc = funcctx->tuple_desc;
- pix2 = funcctx->user_fctx;
-
- /* do when there is more left to send */
- if (call_cntr < max_calls) {
- int values_length = 4;
- Datum values[values_length];
- bool nulls[values_length];
- HeapTuple tuple;
- Datum result;
-
- GSERIALIZED *gser = NULL;
- size_t gser_size = 0;
-
- POSTGIS_RT_DEBUGF(3, "call number %d", call_cntr);
-
- memset(nulls, FALSE, sizeof(bool) * values_length);
-
- /* convert LWGEOM to GSERIALIZED */
- gser = gserialized_from_lwgeom(pix2[call_cntr].geom, 0, &gser_size);
- lwgeom_free(pix2[call_cntr].geom);
-
- values[0] = PointerGetDatum(gser);
- if (pix2[call_cntr].nodata)
- nulls[1] = TRUE;
- else
- values[1] = Float8GetDatum(pix2[call_cntr].value);
- values[2] = Int32GetDatum(pix2[call_cntr].x);
- values[3] = Int32GetDatum(pix2[call_cntr].y);
-
- /* build a tuple */
- tuple = heap_form_tuple(tupdesc, values, nulls);
-
- /* make the tuple into a datum */
- result = HeapTupleGetDatum(tuple);
-
- SRF_RETURN_NEXT(funcctx, result);
- }
- /* do when there is no more left */
- else {
- pfree(pix2);
- SRF_RETURN_DONE(funcctx);
- }
-}
-
-/**
- * Get raster band's polygon
- */
-PG_FUNCTION_INFO_V1(RASTER_getPolygon);
-Datum RASTER_getPolygon(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- int num_bands = 0;
- int nband = 1;
- int err;
- LWMPOLY *surface = NULL;
- GSERIALIZED *rtn = NULL;
-
- /* 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_getPolygon: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- /* 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);
- PG_RETURN_NULL();
- }
-
- /* 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);
- PG_RETURN_NULL();
- }
-
- /* get band surface */
- err = rt_raster_surface(raster, nband - 1, &surface);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- if (err != ES_NONE) {
- elog(ERROR, "RASTER_getPolygon: Could not get raster band's surface");
- PG_RETURN_NULL();
- }
- else if (surface == NULL) {
- elog(NOTICE, "Raster is empty or all pixels of band are NODATA. Returning NULL");
- PG_RETURN_NULL();
- }
-
- rtn = geometry_serialize(lwmpoly_as_lwgeom(surface));
- lwmpoly_free(surface);
-
- PG_RETURN_POINTER(rtn);
-}
-
-/**
- * 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) {
- int values_length = 3;
- 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 = (GSERIALIZED *) PG_DETOAST_DATUM(PG_GETARG_DATUM(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,
- _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);
-}
-
-/**
- * Add band(s) to the given raster at the given position(s).
- */
-PG_FUNCTION_INFO_V1(RASTER_addBand);
-Datum RASTER_addBand(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_pgraster *pgrtn = NULL;
- rt_raster raster = NULL;
- int bandindex = 0;
- int maxbandindex = 0;
- int numbands = 0;
- int lastnumbands = 0;
-
- text *text_pixtype = NULL;
- char *char_pixtype = NULL;
-
- struct addbandarg {
- int index;
- bool append;
- rt_pixtype pixtype;
- double initialvalue;
- bool hasnodata;
- double nodatavalue;
- };
- struct addbandarg *arg = NULL;
-
- ArrayType *array;
- Oid etype;
- Datum *e;
- bool *nulls;
- int16 typlen;
- bool typbyval;
- char typalign;
- int n = 0;
-
- HeapTupleHeader tup;
- bool isnull;
- Datum tupv;
-
- int i = 0;
-
- /* pgraster is null, return null */
- if (PG_ARGISNULL(0))
- PG_RETURN_NULL();
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
-
- /* raster */
- raster = rt_raster_deserialize(pgraster, FALSE);
- if (!raster) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_addBand: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- /* process set of addbandarg */
- POSTGIS_RT_DEBUG(3, "Processing Arg 1 (addbandargset)");
- array = PG_GETARG_ARRAYTYPE_P(1);
- etype = ARR_ELEMTYPE(array);
- get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
-
- deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
- &nulls, &n);
-
- if (!n) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_addBand: Invalid argument for addbandargset");
- PG_RETURN_NULL();
- }
-
- /* allocate addbandarg */
- arg = (struct addbandarg *) palloc(sizeof(struct addbandarg) * n);
- if (arg == NULL) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_addBand: Could not allocate memory for addbandarg");
- PG_RETURN_NULL();
- }
-
- /*
- process each element of addbandargset
- each element is the index of where to add the new band,
- new band's pixeltype, the new band's initial value and
- the new band's NODATA value if NOT NULL
- */
- for (i = 0; i < n; i++) {
- if (nulls[i]) continue;
-
- POSTGIS_RT_DEBUGF(4, "Processing addbandarg at index %d", i);
-
- /* each element is a tuple */
- tup = (HeapTupleHeader) DatumGetPointer(e[i]);
- if (NULL == tup) {
- pfree(arg);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_addBand: Invalid argument for addbandargset");
- PG_RETURN_NULL();
- }
-
- /* new band index, 1-based */
- arg[i].index = 0;
- arg[i].append = TRUE;
- tupv = GetAttributeByName(tup, "index", &isnull);
- if (!isnull) {
- arg[i].index = DatumGetInt32(tupv);
- arg[i].append = FALSE;
- }
-
- /* for now, only check that band index is 1-based */
- if (!arg[i].append && arg[i].index < 1) {
- pfree(arg);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_addBand: Invalid argument for addbandargset. Invalid band index (must be 1-based) for addbandarg of index %d", i);
- PG_RETURN_NULL();
- }
-
- /* new band pixeltype */
- arg[i].pixtype = PT_END;
- tupv = GetAttributeByName(tup, "pixeltype", &isnull);
- if (isnull) {
- pfree(arg);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_addBand: Invalid argument for addbandargset. Pixel type cannot be NULL for addbandarg of index %d", i);
- PG_RETURN_NULL();
- }
- text_pixtype = (text *) DatumGetPointer(tupv);
- if (text_pixtype == NULL) {
- pfree(arg);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_addBand: Invalid argument for addbandargset. Pixel type cannot be NULL for addbandarg of index %d", i);
- PG_RETURN_NULL();
- }
- char_pixtype = text_to_cstring(text_pixtype);
-
- arg[i].pixtype = rt_pixtype_index_from_name(char_pixtype);
- pfree(char_pixtype);
- if (arg[i].pixtype == PT_END) {
- pfree(arg);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_addBand: Invalid argument for addbandargset. Invalid pixel type for addbandarg of index %d", i);
- PG_RETURN_NULL();
- }
-
- /* new band initialvalue */
- arg[i].initialvalue = 0;
- tupv = GetAttributeByName(tup, "initialvalue", &isnull);
- if (!isnull)
- arg[i].initialvalue = DatumGetFloat8(tupv);
-
- /* new band NODATA value */
- arg[i].hasnodata = FALSE;
- arg[i].nodatavalue = 0;
- tupv = GetAttributeByName(tup, "nodataval", &isnull);
- if (!isnull) {
- arg[i].hasnodata = TRUE;
- arg[i].nodatavalue = DatumGetFloat8(tupv);
- }
- }
-
- /* add new bands to raster */
- lastnumbands = rt_raster_get_num_bands(raster);
- for (i = 0; i < n; i++) {
- if (nulls[i]) continue;
-
- POSTGIS_RT_DEBUGF(3, "%d bands in old raster", lastnumbands);
- maxbandindex = lastnumbands + 1;
-
- /* check that new band's index doesn't exceed maxbandindex */
- if (!arg[i].append) {
- if (arg[i].index > maxbandindex) {
- elog(NOTICE, "Band index for addbandarg of index %d exceeds possible value. Adding band at index %d", i, maxbandindex);
- arg[i].index = maxbandindex;
- }
- }
- /* append, so use maxbandindex */
- else
- arg[i].index = maxbandindex;
-
- POSTGIS_RT_DEBUGF(4, "new band (index, pixtype, initialvalue, hasnodata, nodatavalue) = (%d, %s, %f, %s, %f)",
- arg[i].index,
- rt_pixtype_name(arg[i].pixtype),
- arg[i].initialvalue,
- arg[i].hasnodata ? "TRUE" : "FALSE",
- arg[i].nodatavalue
- );
-
- bandindex = rt_raster_generate_new_band(
- raster,
- arg[i].pixtype, arg[i].initialvalue,
- arg[i].hasnodata, arg[i].nodatavalue,
- arg[i].index - 1
- );
-
- numbands = rt_raster_get_num_bands(raster);
- if (numbands == lastnumbands || bandindex == -1) {
- pfree(arg);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_addBand: Could not add band defined by addbandarg of index %d to raster", i);
- PG_RETURN_NULL();
- }
-
- lastnumbands = numbands;
- POSTGIS_RT_DEBUGF(3, "%d bands in new raster", lastnumbands);
- }
-
- pfree(arg);
-
- 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);
-}
-
-/**
- * Add bands from array of rasters to a destination raster
- */
-PG_FUNCTION_INFO_V1(RASTER_addBandRasterArray);
-Datum RASTER_addBandRasterArray(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_pgraster *pgsrc = NULL;
- rt_pgraster *pgrtn = NULL;
-
- rt_raster raster = NULL;
- rt_raster src = NULL;
-
- int srcnband = 1;
- bool appendband = FALSE;
- int dstnband = 1;
- int srcnumbands = 0;
- int dstnumbands = 0;
-
- ArrayType *array;
- Oid etype;
- Datum *e;
- bool *nulls;
- int16 typlen;
- bool typbyval;
- char typalign;
- int n = 0;
-
- int rtn = 0;
- int i = 0;
-
- /* destination raster */
- if (!PG_ARGISNULL(0)) {
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
-
- /* raster */
- raster = rt_raster_deserialize(pgraster, FALSE);
- if (!raster) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_addBandRasterArray: Could not deserialize destination raster");
- PG_RETURN_NULL();
- }
-
- POSTGIS_RT_DEBUG(4, "destination raster isn't NULL");
- }
-
- /* source rasters' band index, 1-based */
- if (!PG_ARGISNULL(2))
- srcnband = PG_GETARG_INT32(2);
- if (srcnband < 1) {
- elog(NOTICE, "Invalid band index for source rasters (must be 1-based). Returning original raster");
- if (raster != NULL) {
- rt_raster_destroy(raster);
- PG_RETURN_POINTER(pgraster);
- }
- else
- PG_RETURN_NULL();
- }
- POSTGIS_RT_DEBUGF(4, "srcnband = %d", srcnband);
-
- /* destination raster's band index, 1-based */
- if (!PG_ARGISNULL(3)) {
- dstnband = PG_GETARG_INT32(3);
- appendband = FALSE;
-
- if (dstnband < 1) {
- elog(NOTICE, "Invalid band index for destination raster (must be 1-based). Returning original raster");
- if (raster != NULL) {
- rt_raster_destroy(raster);
- PG_RETURN_POINTER(pgraster);
- }
- else
- PG_RETURN_NULL();
- }
- }
- else
- appendband = TRUE;
-
- /* additional processing of dstnband */
- if (raster != NULL) {
- dstnumbands = rt_raster_get_num_bands(raster);
-
- if (dstnumbands < 1) {
- appendband = TRUE;
- dstnband = 1;
- }
- else if (appendband)
- dstnband = dstnumbands + 1;
- else if (dstnband > dstnumbands) {
- elog(NOTICE, "Band index provided for destination raster is greater than the number of bands in the raster. Bands will be appended");
- appendband = TRUE;
- dstnband = dstnumbands + 1;
- }
- }
- POSTGIS_RT_DEBUGF(4, "appendband = %d", appendband);
- POSTGIS_RT_DEBUGF(4, "dstnband = %d", dstnband);
-
- /* process set of source rasters */
- POSTGIS_RT_DEBUG(3, "Processing array of source rasters");
- array = PG_GETARG_ARRAYTYPE_P(1);
- etype = ARR_ELEMTYPE(array);
- get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
-
- deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
- &nulls, &n);
-
- /* decrement srcnband and dstnband by 1, now 0-based */
- srcnband--;
- dstnband--;
- POSTGIS_RT_DEBUGF(4, "0-based nband (src, dst) = (%d, %d)", srcnband, dstnband);
-
- /* time to copy bands */
- for (i = 0; i < n; i++) {
- if (nulls[i]) continue;
- src = NULL;
-
- pgsrc = (rt_pgraster *) PG_DETOAST_DATUM(e[i]);
- src = rt_raster_deserialize(pgsrc, FALSE);
- if (src == NULL) {
- pfree(nulls);
- pfree(e);
- if (raster != NULL)
- rt_raster_destroy(raster);
- if (pgraster != NULL)
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_addBandRasterArray: Could not deserialize source raster at index %d", i + 1);
- PG_RETURN_NULL();
- }
-
- srcnumbands = rt_raster_get_num_bands(src);
- POSTGIS_RT_DEBUGF(4, "source raster %d has %d bands", i + 1, srcnumbands);
-
- /* band index isn't valid */
- if (srcnband > srcnumbands - 1) {
- elog(NOTICE, "Invalid band index for source raster at index %d. Returning original raster", i + 1);
- pfree(nulls);
- pfree(e);
- rt_raster_destroy(src);
- if (raster != NULL) {
- rt_raster_destroy(raster);
- PG_RETURN_POINTER(pgraster);
- }
- else
- PG_RETURN_NULL();
- }
-
- /* destination raster is empty, new raster */
- if (raster == NULL) {
- uint32_t srcnbands[1] = {srcnband};
-
- POSTGIS_RT_DEBUG(4, "empty destination raster, using rt_raster_from_band");
-
- raster = rt_raster_from_band(src, srcnbands, 1);
- rt_raster_destroy(src);
- if (raster == NULL) {
- pfree(nulls);
- pfree(e);
- if (pgraster != NULL)
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_addBandRasterArray: Could not create raster from source raster at index %d", i + 1);
- PG_RETURN_NULL();
- }
- }
- /* copy band */
- else {
- rtn = rt_raster_copy_band(
- raster, src,
- srcnband, dstnband
- );
- rt_raster_destroy(src);
-
- if (rtn == -1 || rt_raster_get_num_bands(raster) == dstnumbands) {
- elog(NOTICE, "Could not add band from source raster at index %d to destination raster. Returning original raster", i + 1);
- rt_raster_destroy(raster);
- pfree(nulls);
- pfree(e);
- if (pgraster != NULL)
- PG_RETURN_POINTER(pgraster);
- else
- PG_RETURN_NULL();
- }
- }
-
- dstnband++;
- dstnumbands++;
- }
-
- if (raster != NULL) {
- pgrtn = rt_raster_serialize(raster);
- rt_raster_destroy(raster);
- if (pgraster != NULL)
- PG_FREE_IF_COPY(pgraster, 0);
- if (!pgrtn)
- PG_RETURN_NULL();
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
- PG_RETURN_NULL();
-}
-
-/**
- * Add out-db band to the given raster at the given position
- */
-PG_FUNCTION_INFO_V1(RASTER_addBandOutDB);
-Datum RASTER_addBandOutDB(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_pgraster *pgrtn = NULL;
-
- rt_raster raster = NULL;
- rt_band band = NULL;
- int numbands = 0;
- int dstnband = 1; /* 1-based */
- int appendband = FALSE;
- char *outdbfile = NULL;
- int *srcnband = NULL; /* 1-based */
- int numsrcnband = 0;
- int allbands = FALSE;
- int hasnodata = FALSE;
- double nodataval = 0.;
- uint16_t width = 0;
- uint16_t height = 0;
- char *authname = NULL;
- char *authcode = NULL;
-
- int i = 0;
- int j = 0;
-
- GDALDatasetH hdsOut;
- GDALRasterBandH hbandOut;
- GDALDataType gdpixtype;
-
- rt_pixtype pt = PT_END;
- double gt[6] = {0.};
- double ogt[6] = {0.};
- rt_raster _rast = NULL;
- int aligned = 0;
- int err = 0;
-
- /* destination raster */
- if (!PG_ARGISNULL(0)) {
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
-
- /* raster */
- raster = rt_raster_deserialize(pgraster, FALSE);
- if (!raster) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_addBandOutDB: Could not deserialize destination raster");
- PG_RETURN_NULL();
- }
-
- POSTGIS_RT_DEBUG(4, "destination raster isn't NULL");
- }
-
- /* destination band index (1) */
- if (!PG_ARGISNULL(1))
- dstnband = PG_GETARG_INT32(1);
- else
- appendband = TRUE;
-
- /* outdb file (2) */
- if (PG_ARGISNULL(2)) {
- elog(NOTICE, "Out-db raster file not provided. Returning original raster");
- if (pgraster != NULL) {
- rt_raster_destroy(raster);
- PG_RETURN_POINTER(pgraster);
- }
- else
- PG_RETURN_NULL();
- }
- else {
- outdbfile = text_to_cstring(PG_GETARG_TEXT_P(2));
- if (!strlen(outdbfile)) {
- elog(NOTICE, "Out-db raster file not provided. Returning original raster");
- if (pgraster != NULL) {
- rt_raster_destroy(raster);
- PG_RETURN_POINTER(pgraster);
- }
- else
- PG_RETURN_NULL();
- }
- }
-
- /* outdb band index (3) */
- if (!PG_ARGISNULL(3)) {
- ArrayType *array;
- Oid etype;
- Datum *e;
- bool *nulls;
-
- int16 typlen;
- bool typbyval;
- char typalign;
-
- allbands = FALSE;
-
- array = PG_GETARG_ARRAYTYPE_P(3);
- etype = ARR_ELEMTYPE(array);
- get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
-
- switch (etype) {
- case INT2OID:
- case INT4OID:
- break;
- default:
- if (pgraster != NULL) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- }
- elog(ERROR, "RASTER_addBandOutDB: Invalid data type for band indexes");
- PG_RETURN_NULL();
- break;
- }
-
- deconstruct_array(array, etype, typlen, typbyval, typalign, &e, &nulls, &numsrcnband);
-
- srcnband = palloc(sizeof(int) * numsrcnband);
- if (srcnband == NULL) {
- if (pgraster != NULL) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- }
- elog(ERROR, "RASTER_addBandOutDB: Could not allocate memory for band indexes");
- PG_RETURN_NULL();
- }
-
- for (i = 0, j = 0; i < numsrcnband; i++) {
- if (nulls[i]) continue;
-
- switch (etype) {
- case INT2OID:
- srcnband[j] = DatumGetInt16(e[i]);
- break;
- case INT4OID:
- srcnband[j] = DatumGetInt32(e[i]);
- break;
- }
- j++;
- }
-
- if (j < numsrcnband) {
- srcnband = repalloc(srcnband, sizeof(int) * j);
- if (srcnband == NULL) {
- if (pgraster != NULL) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- }
- elog(ERROR, "RASTER_addBandOutDB: Could not reallocate memory for band indexes");
- PG_RETURN_NULL();
- }
-
- numsrcnband = j;
- }
- }
- else
- allbands = TRUE;
-
- /* nodataval (4) */
- if (!PG_ARGISNULL(4)) {
- hasnodata = TRUE;
- nodataval = PG_GETARG_FLOAT8(4);
- }
- else
- hasnodata = FALSE;
-
- /* validate input */
-
- /* make sure dstnband is valid */
- if (raster != NULL) {
- numbands = rt_raster_get_num_bands(raster);
- if (!appendband) {
- if (dstnband < 1) {
- elog(NOTICE, "Invalid band index %d for adding bands. Using band index 1", dstnband);
- dstnband = 1;
- }
- else if (dstnband > numbands) {
- elog(NOTICE, "Invalid band index %d for adding bands. Using band index %d", dstnband, numbands);
- dstnband = numbands + 1;
- }
- }
- else
- dstnband = numbands + 1;
- }
-
- /* open outdb raster file */
- rt_util_gdal_register_all();
- hdsOut = GDALOpenShared(outdbfile, GA_ReadOnly);
- if (hdsOut == NULL) {
- if (pgraster != NULL) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- }
- elog(ERROR, "RASTER_addBandOutDB: Could not open out-db file with GDAL");
- PG_RETURN_NULL();
- }
-
- /* get offline raster's geotransform */
- if (GDALGetGeoTransform(hdsOut, ogt) != CE_None) {
- ogt[0] = 0;
- ogt[1] = 1;
- ogt[2] = 0;
- ogt[3] = 0;
- ogt[4] = 0;
- ogt[5] = -1;
- }
-
- /* raster doesn't exist, create it now */
- if (raster == NULL) {
- raster = rt_raster_new(GDALGetRasterXSize(hdsOut), GDALGetRasterYSize(hdsOut));
- if (rt_raster_is_empty(raster)) {
- elog(ERROR, "RASTER_addBandOutDB: Could not create new raster");
- PG_RETURN_NULL();
- }
- rt_raster_set_geotransform_matrix(raster, ogt);
- rt_raster_get_geotransform_matrix(raster, gt);
-
- if (rt_util_gdal_sr_auth_info(hdsOut, &authname, &authcode) == ES_NONE) {
- if (
- authname != NULL &&
- strcmp(authname, "EPSG") == 0 &&
- authcode != NULL
- ) {
- rt_raster_set_srid(raster, atoi(authcode));
- }
- else
- elog(INFO, "Unknown SRS auth name and code from out-db file. Defaulting SRID of new raster to %d", SRID_UNKNOWN);
- }
- else
- elog(INFO, "Could not get SRS auth name and code from out-db file. Defaulting SRID of new raster to %d", SRID_UNKNOWN);
- }
-
- /* some raster info */
- width = rt_raster_get_width(raster);
- height = rt_raster_get_width(raster);
-
- /* are rasters aligned? */
- _rast = rt_raster_new(1, 1);
- rt_raster_set_geotransform_matrix(_rast, ogt);
- rt_raster_set_srid(_rast, rt_raster_get_srid(raster));
- err = rt_raster_same_alignment(raster, _rast, &aligned, NULL);
- rt_raster_destroy(_rast);
-
- if (err != ES_NONE) {
- GDALClose(hdsOut);
- if (raster != NULL)
- rt_raster_destroy(raster);
- if (pgraster != NULL)
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_addBandOutDB: Could not test alignment of out-db file");
- return ES_ERROR;
- }
- else if (!aligned)
- elog(WARNING, "The in-db representation of the out-db raster is not aligned. Band data may be incorrect");
-
- numbands = GDALGetRasterCount(hdsOut);
-
- /* build up srcnband */
- if (allbands) {
- numsrcnband = numbands;
- srcnband = palloc(sizeof(int) * numsrcnband);
- if (srcnband == NULL) {
- GDALClose(hdsOut);
- if (raster != NULL)
- rt_raster_destroy(raster);
- if (pgraster != NULL)
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_addBandOutDB: Could not allocate memory for band indexes");
- PG_RETURN_NULL();
- }
-
- for (i = 0, j = 1; i < numsrcnband; i++, j++)
- srcnband[i] = j;
- }
-
- /* check band properties and add band */
- for (i = 0, j = dstnband - 1; i < numsrcnband; i++, j++) {
- /* valid index? */
- if (srcnband[i] < 1 || srcnband[i] > numbands) {
- elog(NOTICE, "Out-db file does not have a band at index %d. Returning original raster", srcnband[i]);
- GDALClose(hdsOut);
- if (raster != NULL)
- rt_raster_destroy(raster);
- if (pgraster != NULL)
- PG_RETURN_POINTER(pgraster);
- else
- PG_RETURN_NULL();
- }
-
- /* get outdb band */
- hbandOut = NULL;
- hbandOut = GDALGetRasterBand(hdsOut, srcnband[i]);
- if (NULL == hbandOut) {
- GDALClose(hdsOut);
- if (raster != NULL)
- rt_raster_destroy(raster);
- if (pgraster != NULL)
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_addBandOutDB: Could not get band %d from GDAL dataset", srcnband[i]);
- PG_RETURN_NULL();
- }
-
- /* supported pixel type */
- gdpixtype = GDALGetRasterDataType(hbandOut);
- pt = rt_util_gdal_datatype_to_pixtype(gdpixtype);
- if (pt == PT_END) {
- elog(NOTICE, "Pixel type %s of band %d from GDAL dataset is not supported. Returning original raster", GDALGetDataTypeName(gdpixtype), srcnband[i]);
- GDALClose(hdsOut);
- if (raster != NULL)
- rt_raster_destroy(raster);
- if (pgraster != NULL)
- PG_RETURN_POINTER(pgraster);
- else
- PG_RETURN_NULL();
- }
-
- /* use out-db band's nodata value if nodataval not already set */
- if (!hasnodata)
- nodataval = GDALGetRasterNoDataValue(hbandOut, &hasnodata);
-
- /* add band */
- band = rt_band_new_offline(
- width, height,
- pt,
- hasnodata, nodataval,
- srcnband[i] - 1, outdbfile
- );
- if (band == NULL) {
- GDALClose(hdsOut);
- if (raster != NULL)
- rt_raster_destroy(raster);
- if (pgraster != NULL)
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_addBandOutDB: Could not create new out-db band");
- PG_RETURN_NULL();
- }
-
- if (rt_raster_add_band(raster, band, j) < 0) {
- GDALClose(hdsOut);
- if (raster != NULL)
- rt_raster_destroy(raster);
- if (pgraster != NULL)
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_addBandOutDB: Could not add new out-db band to raster");
- PG_RETURN_NULL();
- }
- }
-
- pgrtn = rt_raster_serialize(raster);
- rt_raster_destroy(raster);
- if (pgraster != NULL)
- PG_FREE_IF_COPY(pgraster, 0);
- if (!pgrtn)
- PG_RETURN_NULL();
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
-}
-
-/**
- * Break up a raster into smaller tiles. SRF function
- */
-PG_FUNCTION_INFO_V1(RASTER_tile);
-Datum RASTER_tile(PG_FUNCTION_ARGS)
-{
- FuncCallContext *funcctx;
- int call_cntr;
- int max_calls;
- int i = 0;
- int j = 0;
-
- struct tile_arg_t {
-
- struct {
- rt_raster raster;
- double gt[6];
- int srid;
- int width;
- int height;
- } raster;
-
- struct {
- int width;
- int height;
-
- int nx;
- int ny;
- } tile;
-
- int numbands;
- int *nbands;
-
- struct {
- int pad;
- double hasnodata;
- double nodataval;
- } pad;
- };
- struct tile_arg_t *arg1 = NULL;
- struct tile_arg_t *arg2 = NULL;
-
- if (SRF_IS_FIRSTCALL()) {
- MemoryContext oldcontext;
- rt_pgraster *pgraster = NULL;
- int numbands;
-
- ArrayType *array;
- Oid etype;
- Datum *e;
- bool *nulls;
-
- int16 typlen;
- bool typbyval;
- char typalign;
-
- POSTGIS_RT_DEBUG(2, "RASTER_tile: 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);
- }
-
- /* allocate arg1 */
- arg1 = palloc(sizeof(struct tile_arg_t));
- if (arg1 == NULL) {
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_tile: Could not allocate memory for arguments");
- SRF_RETURN_DONE(funcctx);
- }
-
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0));
- arg1->raster.raster = rt_raster_deserialize(pgraster, FALSE);
- if (!arg1->raster.raster) {
- ereport(ERROR, (
- errcode(ERRCODE_OUT_OF_MEMORY),
- errmsg("Could not deserialize raster")
- ));
- pfree(arg1);
- PG_FREE_IF_COPY(pgraster, 0);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- /* raster has bands */
- numbands = rt_raster_get_num_bands(arg1->raster.raster);
- /*
- if (!numbands) {
- elog(NOTICE, "Raster provided has no bands");
- rt_raster_destroy(arg1->raster.raster);
- pfree(arg1);
- PG_FREE_IF_COPY(pgraster, 0);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- */
-
- /* width (1) */
- if (PG_ARGISNULL(1)) {
- elog(NOTICE, "Width cannot be NULL. Returning NULL");
- rt_raster_destroy(arg1->raster.raster);
- pfree(arg1);
- PG_FREE_IF_COPY(pgraster, 0);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- arg1->tile.width = PG_GETARG_INT32(1);
- if (arg1->tile.width < 1) {
- elog(NOTICE, "Width must be greater than zero. Returning NULL");
- rt_raster_destroy(arg1->raster.raster);
- pfree(arg1);
- PG_FREE_IF_COPY(pgraster, 0);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- /* height (2) */
- if (PG_ARGISNULL(2)) {
- elog(NOTICE, "Height cannot be NULL. Returning NULL");
- rt_raster_destroy(arg1->raster.raster);
- pfree(arg1);
- PG_FREE_IF_COPY(pgraster, 0);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- arg1->tile.height = PG_GETARG_INT32(2);
- if (arg1->tile.height < 1) {
- elog(NOTICE, "Height must be greater than zero. Returning NULL");
- rt_raster_destroy(arg1->raster.raster);
- pfree(arg1);
- PG_FREE_IF_COPY(pgraster, 0);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- /* nband, array (3) */
- if (numbands && !PG_ARGISNULL(3)) {
- array = PG_GETARG_ARRAYTYPE_P(3);
- etype = ARR_ELEMTYPE(array);
- get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
-
- switch (etype) {
- case INT2OID:
- case INT4OID:
- break;
- default:
- rt_raster_destroy(arg1->raster.raster);
- pfree(arg1);
- PG_FREE_IF_COPY(pgraster, 0);
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_tile: 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) {
- rt_raster_destroy(arg1->raster.raster);
- pfree(arg1);
- PG_FREE_IF_COPY(pgraster, 0);
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_tile: 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) {
- rt_raster_destroy(arg1->raster.raster);
- pfree(arg1);
- PG_FREE_IF_COPY(pgraster, 0);
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_tile: 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(arg1->raster.raster, arg1->nbands[i])) {
- elog(NOTICE, "Band at index %d not found in raster", arg1->nbands[i] + 1);
- rt_raster_destroy(arg1->raster.raster);
- pfree(arg1->nbands);
- pfree(arg1);
- PG_FREE_IF_COPY(pgraster, 0);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- }
- }
- else {
- arg1->numbands = numbands;
-
- if (numbands) {
- arg1->nbands = palloc(sizeof(int) * arg1->numbands);
-
- if (arg1->nbands == NULL) {
- rt_raster_destroy(arg1->raster.raster);
- pfree(arg1);
- PG_FREE_IF_COPY(pgraster, 0);
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_dumpValues: Could not allocate memory for pixel values");
- 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);
- }
- }
- }
-
- /* pad (4) and padnodata (5) */
- if (!PG_ARGISNULL(4)) {
- arg1->pad.pad = PG_GETARG_BOOL(4) ? 1 : 0;
-
- if (arg1->pad.pad && !PG_ARGISNULL(5)) {
- arg1->pad.hasnodata = 1;
- arg1->pad.nodataval = PG_GETARG_FLOAT8(5);
- }
- else {
- arg1->pad.hasnodata = 0;
- arg1->pad.nodataval = 0;
- }
- }
- else {
- arg1->pad.pad = 0;
- arg1->pad.hasnodata = 0;
- arg1->pad.nodataval = 0;
- }
-
- /* store some additional metadata */
- arg1->raster.srid = rt_raster_get_srid(arg1->raster.raster);
- arg1->raster.width = rt_raster_get_width(arg1->raster.raster);
- arg1->raster.height = rt_raster_get_height(arg1->raster.raster);
- rt_raster_get_geotransform_matrix(arg1->raster.raster, arg1->raster.gt);
-
- /* determine maximum number of tiles from raster */
- arg1->tile.nx = ceil(arg1->raster.width / (double) arg1->tile.width);
- arg1->tile.ny = ceil(arg1->raster.height / (double) arg1->tile.height);
- POSTGIS_RT_DEBUGF(4, "# of tiles (x, y) = (%d, %d)", arg1->tile.nx, arg1->tile.ny);
-
- /* Store needed information */
- funcctx->user_fctx = arg1;
-
- /* total number of tuples to be returned */
- funcctx->max_calls = (arg1->tile.nx * arg1->tile.ny);
-
- MemoryContextSwitchTo(oldcontext);
- }
-
- /* stuff done on every call of the function */
- funcctx = SRF_PERCALL_SETUP();
-
- call_cntr = funcctx->call_cntr;
- max_calls = funcctx->max_calls;
- arg2 = funcctx->user_fctx;
-
- /* do when there is more left to send */
- if (call_cntr < max_calls) {
- rt_pgraster *pgtile = NULL;
- rt_raster tile = NULL;
- rt_band _band = NULL;
- rt_band band = NULL;
- rt_pixtype pixtype = PT_END;
- int hasnodata = 0;
- double nodataval = 0;
- int width = 0;
- int height = 0;
-
- int k = 0;
- int tx = 0;
- int ty = 0;
- int rx = 0;
- int ry = 0;
- int ex = 0; /* edge tile on right */
- int ey = 0; /* edge tile on bottom */
- double ulx = 0;
- double uly = 0;
- uint16_t len = 0;
- void *vals = NULL;
- uint16_t nvals;
-
- POSTGIS_RT_DEBUGF(3, "call number %d", call_cntr);
-
- /*
- find offset based upon tile #
-
- 0 1 2
- 3 4 5
- 6 7 8
- */
- ty = call_cntr / arg2->tile.nx;
- tx = call_cntr % arg2->tile.nx;
- POSTGIS_RT_DEBUGF(4, "tile (x, y) = (%d, %d)", tx, ty);
-
- /* edge tile? only important if padding is false */
- if (!arg2->pad.pad) {
- if (ty + 1 == arg2->tile.ny)
- ey = 1;
- if (tx + 1 == arg2->tile.nx)
- ex = 1;
- }
-
- /* upper-left of tile in raster coordinates */
- rx = tx * arg2->tile.width;
- ry = ty * arg2->tile.height;
- POSTGIS_RT_DEBUGF(4, "raster coordinates = %d, %d", rx, ry);
-
- /* determine tile width and height */
- /* default to user-defined */
- width = arg2->tile.width;
- height = arg2->tile.height;
-
- /* override user-defined if edge tile (only possible if padding is false */
- if (ex || ey) {
- /* right edge */
- if (ex)
- width = arg2->raster.width - rx;
- /* bottom edge */
- if (ey)
- height = arg2->raster.height - ry;
- }
-
- /* create empty raster */
- tile = rt_raster_new(width, height);
- rt_raster_set_geotransform_matrix(tile, arg2->raster.gt);
- rt_raster_set_srid(tile, arg2->raster.srid);
-
- /* upper-left of tile in spatial coordinates */
- if (rt_raster_cell_to_geopoint(arg2->raster.raster, rx, ry, &ulx, &uly, arg2->raster.gt) != ES_NONE) {
- rt_raster_destroy(tile);
- rt_raster_destroy(arg2->raster.raster);
- if (arg2->numbands) pfree(arg2->nbands);
- pfree(arg2);
- elog(ERROR, "RASTER_tile: Could not compute the coordinates of the upper-left corner of the output tile");
- SRF_RETURN_DONE(funcctx);
- }
- rt_raster_set_offsets(tile, ulx, uly);
- POSTGIS_RT_DEBUGF(4, "spatial coordinates = %f, %f", ulx, uly);
-
- /* compute length of pixel line to read */
- len = arg2->tile.width;
- if (rx + arg2->tile.width >= arg2->raster.width)
- len = arg2->raster.width - rx;
- POSTGIS_RT_DEBUGF(3, "read line len = %d", len);
-
- /* copy bands to tile */
- for (i = 0; i < arg2->numbands; i++) {
- POSTGIS_RT_DEBUGF(4, "copying band %d to tile %d", arg2->nbands[i], call_cntr);
-
- _band = rt_raster_get_band(arg2->raster.raster, arg2->nbands[i]);
- if (_band == NULL) {
- int nband = arg2->nbands[i] + 1;
- rt_raster_destroy(tile);
- rt_raster_destroy(arg2->raster.raster);
- if (arg2->numbands) pfree(arg2->nbands);
- pfree(arg2);
- elog(ERROR, "RASTER_tile: Could not get band %d from source raster", nband);
- SRF_RETURN_DONE(funcctx);
- }
-
- pixtype = rt_band_get_pixtype(_band);
- hasnodata = rt_band_get_hasnodata_flag(_band);
- if (hasnodata)
- rt_band_get_nodata(_band, &nodataval);
- else if (arg2->pad.pad && arg2->pad.hasnodata) {
- hasnodata = 1;
- nodataval = arg2->pad.nodataval;
- }
- else
- nodataval = rt_band_get_min_value(_band);
-
- /* inline band */
- if (!rt_band_is_offline(_band)) {
- if (rt_raster_generate_new_band(tile, pixtype, nodataval, hasnodata, nodataval, i) < 0) {
- rt_raster_destroy(tile);
- rt_raster_destroy(arg2->raster.raster);
- pfree(arg2->nbands);
- pfree(arg2);
- elog(ERROR, "RASTER_tile: Could not add new band to output tile");
- SRF_RETURN_DONE(funcctx);
- }
- band = rt_raster_get_band(tile, i);
- if (band == NULL) {
- rt_raster_destroy(tile);
- rt_raster_destroy(arg2->raster.raster);
- if (arg2->numbands) pfree(arg2->nbands);
- pfree(arg2);
- elog(ERROR, "RASTER_tile: Could not get newly added band from output tile");
- SRF_RETURN_DONE(funcctx);
- }
-
- /* if isnodata, set flag and continue */
- if (rt_band_get_isnodata_flag(_band)) {
- rt_band_set_isnodata_flag(band, 1);
- continue;
- }
-
- /* copy data */
- for (j = 0; j < arg2->tile.height; j++) {
- k = ry + j;
-
- if (k >= arg2->raster.height) {
- POSTGIS_RT_DEBUGF(4, "row %d is beyond extent of source raster. skipping", k);
- continue;
- }
-
- POSTGIS_RT_DEBUGF(4, "getting pixel line %d, %d for %d pixels", rx, k, len);
- if (rt_band_get_pixel_line(_band, rx, k, len, &vals, &nvals) != ES_NONE) {
- rt_raster_destroy(tile);
- rt_raster_destroy(arg2->raster.raster);
- if (arg2->numbands) pfree(arg2->nbands);
- pfree(arg2);
- elog(ERROR, "RASTER_tile: Could not get pixel line from source raster");
- SRF_RETURN_DONE(funcctx);
- }
-
- if (nvals && rt_band_set_pixel_line(band, 0, j, vals, nvals) != ES_NONE) {
- rt_raster_destroy(tile);
- rt_raster_destroy(arg2->raster.raster);
- if (arg2->numbands) pfree(arg2->nbands);
- pfree(arg2);
- elog(ERROR, "RASTER_tile: Could not set pixel line of output tile");
- SRF_RETURN_DONE(funcctx);
- }
- }
- }
- /* offline */
- else {
- uint8_t bandnum = 0;
- rt_band_get_ext_band_num(_band, &bandnum);
-
- band = rt_band_new_offline(
- width, height,
- pixtype,
- hasnodata, nodataval,
- bandnum, rt_band_get_ext_path(_band)
- );
-
- if (band == NULL) {
- rt_raster_destroy(tile);
- rt_raster_destroy(arg2->raster.raster);
- if (arg2->numbands) pfree(arg2->nbands);
- pfree(arg2);
- elog(ERROR, "RASTER_tile: Could not create new offline band for output tile");
- SRF_RETURN_DONE(funcctx);
- }
-
- if (rt_raster_add_band(tile, band, i) < 0) {
- rt_band_destroy(band);
- rt_raster_destroy(tile);
- rt_raster_destroy(arg2->raster.raster);
- if (arg2->numbands) pfree(arg2->nbands);
- pfree(arg2);
- elog(ERROR, "RASTER_tile: Could not add new offline band to output tile");
- SRF_RETURN_DONE(funcctx);
- }
- }
- }
-
- pgtile = rt_raster_serialize(tile);
- rt_raster_destroy(tile);
- if (!pgtile) {
- rt_raster_destroy(arg2->raster.raster);
- if (arg2->numbands) pfree(arg2->nbands);
- pfree(arg2);
- SRF_RETURN_DONE(funcctx);
- }
-
- SET_VARSIZE(pgtile, pgtile->size);
- SRF_RETURN_NEXT(funcctx, PointerGetDatum(pgtile));
- }
- /* do when there is no more left */
- else {
- rt_raster_destroy(arg2->raster.raster);
- if (arg2->numbands) pfree(arg2->nbands);
- pfree(arg2);
- SRF_RETURN_DONE(funcctx);
- }
-}
-
-/**
- * Copy a band from one raster to another one at the given position.
- */
-PG_FUNCTION_INFO_V1(RASTER_copyBand);
-Datum RASTER_copyBand(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgto = NULL;
- rt_pgraster *pgfrom = NULL;
- rt_pgraster *pgrtn = NULL;
- rt_raster torast = NULL;
- rt_raster fromrast = NULL;
- int toindex = 0;
- int fromband = 0;
- int oldtorastnumbands = 0;
- int newtorastnumbands = 0;
- int newbandindex = 0;
-
- /* Deserialize torast */
- if (PG_ARGISNULL(0)) PG_RETURN_NULL();
- pgto = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
-
- torast = rt_raster_deserialize(pgto, FALSE);
- if (!torast) {
- PG_FREE_IF_COPY(pgto, 0);
- elog(ERROR, "RASTER_copyBand: Could not deserialize first raster");
- PG_RETURN_NULL();
- }
-
- /* Deserialize fromrast */
- if (!PG_ARGISNULL(1)) {
- pgfrom = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
-
- fromrast = rt_raster_deserialize(pgfrom, FALSE);
- if (!fromrast) {
- rt_raster_destroy(torast);
- PG_FREE_IF_COPY(pgfrom, 1);
- PG_FREE_IF_COPY(pgto, 0);
- elog(ERROR, "RASTER_copyBand: Could not deserialize second raster");
- PG_RETURN_NULL();
- }
-
- oldtorastnumbands = rt_raster_get_num_bands(torast);
-
- if (PG_ARGISNULL(2))
- fromband = 1;
- else
- fromband = PG_GETARG_INT32(2);
-
- if (PG_ARGISNULL(3))
- toindex = oldtorastnumbands + 1;
- else
- toindex = PG_GETARG_INT32(3);
-
- /* Copy band fromrast torast */
- newbandindex = rt_raster_copy_band(
- torast, fromrast,
- fromband - 1, toindex - 1
- );
-
- newtorastnumbands = rt_raster_get_num_bands(torast);
- if (newtorastnumbands == oldtorastnumbands || newbandindex == -1) {
- elog(NOTICE, "RASTER_copyBand: Could not add band to raster. "
- "Returning original raster."
- );
- }
-
- rt_raster_destroy(fromrast);
- PG_FREE_IF_COPY(pgfrom, 1);
- }
-
- /* Serialize and return torast */
- pgrtn = rt_raster_serialize(torast);
- rt_raster_destroy(torast);
- PG_FREE_IF_COPY(pgto, 0);
- if (!pgrtn) PG_RETURN_NULL();
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
-}
-
-/**
- * Check if raster is empty or not
- */
-PG_FUNCTION_INFO_V1(RASTER_isEmpty);
-Datum RASTER_isEmpty(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- bool isempty = FALSE;
-
- /* Deserialize raster */
- if (PG_ARGISNULL(0)) PG_RETURN_NULL();
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
-
- raster = rt_raster_deserialize(pgraster, TRUE);
- if ( ! raster )
- {
- ereport(ERROR,
- (errcode(ERRCODE_OUT_OF_MEMORY),
- errmsg("RASTER_isEmpty: Could not deserialize raster")));
- PG_FREE_IF_COPY(pgraster, 0);
- PG_RETURN_NULL();
- }
-
- isempty = rt_raster_is_empty(raster);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- PG_RETURN_BOOL(isempty);
-}
-
-/**
- * Check if the raster has a given band or not
- */
-PG_FUNCTION_INFO_V1(RASTER_hasNoBand);
-Datum RASTER_hasNoBand(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- int bandindex = 0;
- bool hasnoband = FALSE;
-
- /* Deserialize raster */
- if (PG_ARGISNULL(0)) PG_RETURN_NULL();
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
-
- raster = rt_raster_deserialize(pgraster, TRUE);
- if ( ! raster )
- {
- ereport(ERROR,
- (errcode(ERRCODE_OUT_OF_MEMORY),
- errmsg("RASTER_hasNoBand: Could not deserialize raster")));
- PG_FREE_IF_COPY(pgraster, 0);
- PG_RETURN_NULL();
- }
-
- /* Get band number */
- bandindex = PG_GETARG_INT32(1);
- hasnoband = !rt_raster_has_band(raster, bandindex - 1);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- PG_RETURN_BOOL(hasnoband);
-}
-
-PG_FUNCTION_INFO_V1(RASTER_mapAlgebraExpr);
-Datum RASTER_mapAlgebraExpr(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_pgraster *pgrtn = NULL;
- rt_raster raster = NULL;
- rt_raster newrast = NULL;
- rt_band band = NULL;
- rt_band newband = NULL;
- int x, y, nband, width, height;
- double r;
- double newnodatavalue = 0.0;
- double newinitialvalue = 0.0;
- double newval = 0.0;
- char *newexpr = NULL;
- char *initexpr = NULL;
- char *expression = NULL;
- int hasnodataval = 0;
- double nodataval = 0.;
- rt_pixtype newpixeltype;
- int skipcomputation = 0;
- int len = 0;
- const int argkwcount = 3;
- enum KEYWORDS { kVAL=0, kX=1, kY=2 };
- char *argkw[] = {"[rast]", "[rast.x]", "[rast.y]"};
- Oid argkwtypes[] = { FLOAT8OID, INT4OID, INT4OID };
- int argcount = 0;
- Oid argtype[] = { FLOAT8OID, INT4OID, INT4OID };
- uint8_t argpos[3] = {0};
- char place[5];
- int idx = 0;
- int ret = -1;
- TupleDesc tupdesc;
- SPIPlanPtr spi_plan = NULL;
- SPITupleTable * tuptable = NULL;
- HeapTuple tuple;
- char * strFromText = NULL;
- Datum *values = NULL;
- Datum datum = (Datum)NULL;
- char *nulls = NULL;
- bool isnull = FALSE;
- int i = 0;
- int j = 0;
-
- POSTGIS_RT_DEBUG(2, "RASTER_mapAlgebraExpr: Starting...");
-
- /* Check raster */
- if (PG_ARGISNULL(0)) {
- elog(NOTICE, "Raster is NULL. Returning NULL");
- PG_RETURN_NULL();
- }
-
-
- /* Deserialize raster */
- pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
- raster = rt_raster_deserialize(pgraster, FALSE);
- if (NULL == raster) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_mapAlgebraExpr: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraExpr: Getting arguments...");
-
- if (PG_ARGISNULL(1))
- nband = 1;
- else
- nband = PG_GETARG_INT32(1);
-
- if (nband < 1)
- nband = 1;
-
-
- POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraExpr: Creating new empty raster...");
-
- /**
- * Create a new empty raster with having the same georeference as the
- * provided raster
- **/
- width = rt_raster_get_width(raster);
- height = rt_raster_get_height(raster);
-
- newrast = rt_raster_new(width, height);
-
- if ( NULL == newrast ) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_mapAlgebraExpr: Could not create a new raster");
- PG_RETURN_NULL();
- }
-
- rt_raster_set_scale(newrast,
- rt_raster_get_x_scale(raster),
- rt_raster_get_y_scale(raster));
-
- rt_raster_set_offsets(newrast,
- rt_raster_get_x_offset(raster),
- rt_raster_get_y_offset(raster));
-
- rt_raster_set_skews(newrast,
- rt_raster_get_x_skew(raster),
- rt_raster_get_y_skew(raster));
-
- rt_raster_set_srid(newrast, rt_raster_get_srid(raster));
-
-
- /**
- * If this new raster is empty (width = 0 OR height = 0) then there is
- * nothing to compute and we return it right now
- **/
- if (rt_raster_is_empty(newrast))
- {
- elog(NOTICE, "Raster is empty. Returning an empty raster");
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- pgrtn = rt_raster_serialize(newrast);
- rt_raster_destroy(newrast);
- if (NULL == pgrtn) {
-
- elog(ERROR, "RASTER_mapAlgebraExpr: Could not serialize raster");
- PG_RETURN_NULL();
- }
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
-
- POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraExpr: Getting raster band %d...", nband);
-
- /**
- * Check if the raster has the required band. Otherwise, return a raster
- * without band
- **/
- if (!rt_raster_has_band(raster, nband - 1)) {
- elog(NOTICE, "Raster does not have the required band. Returning a raster "
- "without a band");
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- pgrtn = rt_raster_serialize(newrast);
- rt_raster_destroy(newrast);
- if (NULL == pgrtn) {
- elog(ERROR, "RASTER_mapAlgebraExpr: Could not serialize raster");
- PG_RETURN_NULL();
- }
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
- /* Get the raster band */
- band = rt_raster_get_band(raster, nband - 1);
- if ( NULL == band ) {
- elog(NOTICE, "Could not get the required band. Returning a raster "
- "without a band");
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- pgrtn = rt_raster_serialize(newrast);
- rt_raster_destroy(newrast);
- if (NULL == pgrtn) {
- elog(ERROR, "RASTER_mapAlgebraExpr: Could not serialize raster");
- PG_RETURN_NULL();
- }
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
- /*
- * Get NODATA value
- */
- POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraExpr: Getting NODATA value for band...");
-
- if (rt_band_get_hasnodata_flag(band)) {
- rt_band_get_nodata(band, &newnodatavalue);
- }
-
- else {
- newnodatavalue = rt_band_get_min_value(band);
- }
-
- POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraExpr: NODATA value for band: = %f",
- newnodatavalue);
-
- /**
- * We set the initial value of the future band to nodata value. If nodata
- * value is null, then the raster will be initialized to
- * rt_band_get_min_value but all the values should be recomputed anyway
- **/
- newinitialvalue = newnodatavalue;
-
- /**
- * Set the new pixeltype
- **/
- POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraExpr: Setting pixeltype...");
-
- if (PG_ARGISNULL(2)) {
- newpixeltype = rt_band_get_pixtype(band);
- }
-
- else {
- strFromText = text_to_cstring(PG_GETARG_TEXT_P(2));
- newpixeltype = rt_pixtype_index_from_name(strFromText);
- pfree(strFromText);
- if (newpixeltype == PT_END)
- newpixeltype = rt_band_get_pixtype(band);
- }
-
- if (newpixeltype == PT_END) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_mapAlgebraExpr: Invalid pixeltype");
- PG_RETURN_NULL();
- }
-
- POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraExpr: Pixeltype set to %s",
- rt_pixtype_name(newpixeltype));
-
-
- /* Construct expression for raster values */
- if (!PG_ARGISNULL(3)) {
- expression = text_to_cstring(PG_GETARG_TEXT_P(3));
- len = strlen("SELECT (") + strlen(expression) + strlen(")::double precision");
- initexpr = (char *)palloc(len + 1);
-
- strncpy(initexpr, "SELECT (", strlen("SELECT ("));
- strncpy(initexpr + strlen("SELECT ("), expression, strlen(expression));
- strncpy(initexpr + strlen("SELECT (") + strlen(expression), ")::double precision", strlen(")::double precision"));
- initexpr[len] = '\0';
-
- POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraExpr: Expression is %s", initexpr);
-
- /* We don't need this memory */
- /*
- pfree(expression);
- expression = NULL;
- */
- }
-
-
-
- /**
- * Optimization: If a nodataval is provided, use it for newinitialvalue.
- * Then, we can initialize the raster with this value and skip the
- * computation of nodata values one by one in the main computing loop
- **/
- if (!PG_ARGISNULL(4)) {
- hasnodataval = 1;
- nodataval = PG_GETARG_FLOAT8(4);
- newinitialvalue = nodataval;
-
- POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraExpr: new initial value = %f",
- newinitialvalue);
- }
- else
- hasnodataval = 0;
-
-
-
- /**
- * Optimization: If the raster is only filled with nodata values return
- * right now a raster filled with the newinitialvalue
- * TODO: Call rt_band_check_isnodata instead?
- **/
- if (rt_band_get_isnodata_flag(band)) {
-
- POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraExpr: Band is a nodata band, returning "
- "a raster filled with nodata");
-
- ret = rt_raster_generate_new_band(newrast, newpixeltype,
- newinitialvalue, TRUE, newnodatavalue, 0);
-
- /* Free memory */
- if (initexpr)
- pfree(initexpr);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- /* Serialize created raster */
- pgrtn = rt_raster_serialize(newrast);
- rt_raster_destroy(newrast);
- if (NULL == pgrtn) {
- elog(ERROR, "RASTER_mapAlgebraExpr: Could not serialize raster");
- PG_RETURN_NULL();
- }
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
-
- /**
- * Optimization: If expression resume to 'RAST' and hasnodataval is zero,
- * we can just return the band from the original raster
- **/
- if (initexpr != NULL && ( !strcmp(initexpr, "SELECT [rast]") || !strcmp(initexpr, "SELECT [rast.val]") ) && !hasnodataval) {
-
- POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraExpr: Expression resumes to RAST. "
- "Returning raster with band %d from original raster", nband);
-
- POSTGIS_RT_DEBUGF(4, "RASTER_mapAlgebraExpr: New raster has %d bands",
- rt_raster_get_num_bands(newrast));
-
- rt_raster_copy_band(newrast, raster, nband - 1, 0);
-
- POSTGIS_RT_DEBUGF(4, "RASTER_mapAlgebraExpr: New raster now has %d bands",
- rt_raster_get_num_bands(newrast));
-
- if (initexpr)
- pfree(initexpr);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- /* Serialize created raster */
- pgrtn = rt_raster_serialize(newrast);
- rt_raster_destroy(newrast);
- if (NULL == pgrtn) {
- elog(ERROR, "RASTER_mapAlgebraExpr: Could not serialize raster");
- PG_RETURN_NULL();
- }
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
- /**
- * Optimization: If expression resume to a constant (it does not contain
- * [rast)
- **/
- if (initexpr != NULL && strstr(initexpr, "[rast") == NULL) {
- ret = SPI_connect();
- if (ret != SPI_OK_CONNECT) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_mapAlgebraExpr: Could not connect to the SPI manager");
- PG_RETURN_NULL();
- };
-
- /* Execute the expresion into newval */
- ret = SPI_execute(initexpr, FALSE, 0);
-
- if (ret != SPI_OK_SELECT || SPI_tuptable == NULL || SPI_processed != 1) {
-
- /* Free memory allocated out of the current context */
- if (SPI_tuptable)
- SPI_freetuptable(tuptable);
- PG_FREE_IF_COPY(pgraster, 0);
-
- SPI_finish();
- elog(ERROR, "RASTER_mapAlgebraExpr: Invalid construction for expression");
- PG_RETURN_NULL();
- }
-
- tupdesc = SPI_tuptable->tupdesc;
- tuptable = SPI_tuptable;
-
- tuple = tuptable->vals[0];
- newexpr = SPI_getvalue(tuple, tupdesc, 1);
- if ( ! newexpr ) {
- POSTGIS_RT_DEBUG(3, "Constant expression evaluated to NULL, keeping initvalue");
- newval = newinitialvalue;
- } else {
- newval = atof(newexpr);
- }
-
- SPI_freetuptable(tuptable);
-
- POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraExpr: New raster value = %f",
- newval);
-
- SPI_finish();
-
- skipcomputation = 1;
-
- /**
- * Compute the new value, set it and we will return after creating the
- * new raster
- **/
- if (!hasnodataval) {
- newinitialvalue = newval;
- skipcomputation = 2;
- }
-
- /* Return the new raster as it will be before computing pixel by pixel */
- else if (FLT_NEQ(newval, newinitialvalue)) {
- skipcomputation = 2;
- }
- }
-
- /**
- * Create the raster receiving all the computed values. Initialize it to the
- * new initial value
- **/
- ret = rt_raster_generate_new_band(newrast, newpixeltype,
- newinitialvalue, TRUE, newnodatavalue, 0);
-
- /**
- * Optimization: If expression is NULL, or all the pixels could be set in
- * one step, return the initialized raster now
- **/
- /*if (initexpr == NULL || skipcomputation == 2) {*/
- if (expression == NULL || skipcomputation == 2) {
-
- /* Free memory */
- if (initexpr)
- pfree(initexpr);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- /* Serialize created raster */
- pgrtn = rt_raster_serialize(newrast);
- rt_raster_destroy(newrast);
- if (NULL == pgrtn) {
- elog(ERROR, "RASTER_mapAlgebraExpr: Could not serialize raster");
- PG_RETURN_NULL();
- }
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
- RASTER_DEBUG(3, "RASTER_mapAlgebraExpr: Creating new raster band...");
-
- /* Get the new raster band */
- newband = rt_raster_get_band(newrast, 0);
- if ( NULL == newband ) {
- elog(NOTICE, "Could not modify band for new raster. Returning new "
- "raster with the original band");
-
- if (initexpr)
- pfree(initexpr);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- /* Serialize created raster */
- pgrtn = rt_raster_serialize(newrast);
- rt_raster_destroy(newrast);
- if (NULL == pgrtn) {
- elog(ERROR, "RASTER_mapAlgebraExpr: Could not serialize raster");
- PG_RETURN_NULL();
- }
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
- POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraExpr: Main computing loop (%d x %d)",
- width, height);
-
- if (initexpr != NULL) {
- /* Convert [rast.val] to [rast] */
- newexpr = rtpg_strreplace(initexpr, "[rast.val]", "[rast]", NULL);
- pfree(initexpr); initexpr=newexpr;
-
- sprintf(place,"$1");
- for (i = 0, j = 1; i < argkwcount; i++) {
- len = 0;
- newexpr = rtpg_strreplace(initexpr, argkw[i], place, &len);
- pfree(initexpr); initexpr=newexpr;
- if (len > 0) {
- argtype[argcount] = argkwtypes[i];
- argcount++;
- argpos[i] = j++;
-
- sprintf(place, "$%d", j);
- }
- else {
- argpos[i] = 0;
- }
- }
-
- POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraExpr: initexpr = %s", initexpr);
-
- /* define values */
- values = (Datum *) palloc(sizeof(Datum) * argcount);
- if (values == NULL) {
-
- SPI_finish();
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- rt_raster_destroy(newrast);
-
- elog(ERROR, "RASTER_mapAlgebraExpr: Could not allocate memory for value parameters of prepared statement");
- PG_RETURN_NULL();
- }
-
- /* define nulls */
- nulls = (char *)palloc(argcount);
- if (nulls == NULL) {
-
- SPI_finish();
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- rt_raster_destroy(newrast);
-
- elog(ERROR, "RASTER_mapAlgebraExpr: Could not allocate memory for null parameters of prepared statement");
- PG_RETURN_NULL();
- }
-
- /* Connect to SPI and prepare the expression */
- ret = SPI_connect();
- if (ret != SPI_OK_CONNECT) {
-
- if (initexpr)
- pfree(initexpr);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- rt_raster_destroy(newrast);
-
- elog(ERROR, "RASTER_mapAlgebraExpr: Could not connect to the SPI manager");
- PG_RETURN_NULL();
- };
-
- /* Type of all arguments is FLOAT8OID */
- spi_plan = SPI_prepare(initexpr, argcount, argtype);
-
- if (spi_plan == NULL) {
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- rt_raster_destroy(newrast);
-
- SPI_finish();
-
- pfree(initexpr);
-
- elog(ERROR, "RASTER_mapAlgebraExpr: Could not prepare expression");
- PG_RETURN_NULL();
- }
- }
-
- for (x = 0; x < width; x++) {
- for(y = 0; y < height; y++) {
- ret = rt_band_get_pixel(band, x, y, &r, NULL);
-
- /**
- * We compute a value only for the withdata value pixel since the
- * nodata value has already been set by the first optimization
- **/
- if (ret == ES_NONE && FLT_NEQ(r, newnodatavalue)) {
- if (skipcomputation == 0) {
- if (initexpr != NULL) {
- /* Reset the null arg flags. */
- memset(nulls, 'n', argcount);
-
- for (i = 0; i < argkwcount; i++) {
- idx = argpos[i];
- if (idx < 1) continue;
- idx--;
-
- if (i == kX ) {
- /* x is 0 based index, but SQL expects 1 based index */
- values[idx] = Int32GetDatum(x+1);
- nulls[idx] = ' ';
- }
- else if (i == kY) {
- /* y is 0 based index, but SQL expects 1 based index */
- values[idx] = Int32GetDatum(y+1);
- nulls[idx] = ' ';
- }
- else if (i == kVAL ) {
- values[idx] = Float8GetDatum(r);
- nulls[idx] = ' ';
- }
-
- }
-
- ret = SPI_execute_plan(spi_plan, values, nulls, FALSE, 0);
- if (ret != SPI_OK_SELECT || SPI_tuptable == NULL ||
- SPI_processed != 1) {
- if (SPI_tuptable)
- SPI_freetuptable(tuptable);
-
- SPI_freeplan(spi_plan);
- SPI_finish();
-
- pfree(values);
- pfree(nulls);
- pfree(initexpr);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- rt_raster_destroy(newrast);
-
- elog(ERROR, "RASTER_mapAlgebraExpr: Error executing prepared plan");
-
- PG_RETURN_NULL();
- }
-
- tupdesc = SPI_tuptable->tupdesc;
- tuptable = SPI_tuptable;
-
- tuple = tuptable->vals[0];
- datum = SPI_getbinval(tuple, tupdesc, 1, &isnull);
- if ( SPI_result == SPI_ERROR_NOATTRIBUTE ) {
- POSTGIS_RT_DEBUGF(3, "Expression for pixel %d,%d (value %g) errored, skip setting", x+1,y+1,r);
- newval = newinitialvalue;
- }
- else if ( isnull ) {
- POSTGIS_RT_DEBUGF(3, "Expression for pixel %d,%d (value %g) evaluated to NULL, skip setting", x+1,y+1,r);
- newval = newinitialvalue;
- } else {
- newval = DatumGetFloat8(datum);
- }
-
- SPI_freetuptable(tuptable);
- }
-
- else
- newval = newinitialvalue;
-
- POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraExpr: new value = %f",
- newval);
- }
-
-
- rt_band_set_pixel(newband, x, y, newval, NULL);
- }
-
- }
- }
-
- if (initexpr != NULL) {
- SPI_freeplan(spi_plan);
- SPI_finish();
-
- pfree(values);
- pfree(nulls);
- pfree(initexpr);
- }
- else {
- POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraExpr: no SPI cleanup");
- }
-
-
- /* The newrast band has been modified */
-
- POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraExpr: raster modified, serializing it.");
- /* Serialize created raster */
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- pgrtn = rt_raster_serialize(newrast);
- rt_raster_destroy(newrast);
- if (NULL == pgrtn)
- PG_RETURN_NULL();
-
- SET_VARSIZE(pgrtn, pgrtn->size);
-
- POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraExpr: raster serialized");
-
-
- POSTGIS_RT_DEBUG(4, "RASTER_mapAlgebraExpr: returning raster");
-
-
- PG_RETURN_POINTER(pgrtn);
-}
-
-/*
- * One raster user function MapAlgebra.
- */
-PG_FUNCTION_INFO_V1(RASTER_mapAlgebraFct);
-Datum RASTER_mapAlgebraFct(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_pgraster *pgrtn = NULL;
- rt_raster raster = NULL;
- rt_raster newrast = NULL;
- rt_band band = NULL;
- rt_band newband = NULL;
- int x, y, nband, width, height;
- double r;
- double newnodatavalue = 0.0;
- double newinitialvalue = 0.0;
- double newval = 0.0;
- rt_pixtype newpixeltype;
- int ret = -1;
- Oid oid;
- FmgrInfo cbinfo;
- FunctionCallInfoData cbdata;
- Datum tmpnewval;
- char * strFromText = NULL;
- int k = 0;
-
- POSTGIS_RT_DEBUG(2, "RASTER_mapAlgebraFct: STARTING...");
-
- /* Check raster */
- if (PG_ARGISNULL(0)) {
- elog(WARNING, "Raster is NULL. Returning NULL");
- PG_RETURN_NULL();
- }
-
-
- /* Deserialize raster */
- pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
- raster = rt_raster_deserialize(pgraster, FALSE);
- if (NULL == raster) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_mapAlgebraFct: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFct: Getting arguments...");
-
- /* Get the rest of the arguments */
-
- if (PG_ARGISNULL(1))
- nband = 1;
- else
- nband = PG_GETARG_INT32(1);
-
- if (nband < 1)
- nband = 1;
-
- POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFct: Creating new empty raster...");
-
- /**
- * Create a new empty raster with having the same georeference as the
- * provided raster
- **/
- width = rt_raster_get_width(raster);
- height = rt_raster_get_height(raster);
-
- newrast = rt_raster_new(width, height);
-
- if ( NULL == newrast ) {
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- elog(ERROR, "RASTER_mapAlgebraFct: Could not create a new raster");
- PG_RETURN_NULL();
- }
-
- rt_raster_set_scale(newrast,
- rt_raster_get_x_scale(raster),
- rt_raster_get_y_scale(raster));
-
- rt_raster_set_offsets(newrast,
- rt_raster_get_x_offset(raster),
- rt_raster_get_y_offset(raster));
-
- rt_raster_set_skews(newrast,
- rt_raster_get_x_skew(raster),
- rt_raster_get_y_skew(raster));
-
- rt_raster_set_srid(newrast, rt_raster_get_srid(raster));
-
-
- /**
- * If this new raster is empty (width = 0 OR height = 0) then there is
- * nothing to compute and we return it right now
- **/
- if (rt_raster_is_empty(newrast))
- {
- elog(NOTICE, "Raster is empty. Returning an empty raster");
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- pgrtn = rt_raster_serialize(newrast);
- rt_raster_destroy(newrast);
- if (NULL == pgrtn) {
- elog(ERROR, "RASTER_mapAlgebraFct: Could not serialize raster");
- PG_RETURN_NULL();
- }
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
- POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFct: Getting raster band %d...", nband);
-
- /**
- * Check if the raster has the required band. Otherwise, return a raster
- * without band
- **/
- if (!rt_raster_has_band(raster, nband - 1)) {
- elog(NOTICE, "Raster does not have the required band. Returning a raster "
- "without a band");
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- pgrtn = rt_raster_serialize(newrast);
- rt_raster_destroy(newrast);
- if (NULL == pgrtn) {
- elog(ERROR, "RASTER_mapAlgebraFct: Could not serialize raster");
- PG_RETURN_NULL();
- }
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
- /* Get the raster band */
- band = rt_raster_get_band(raster, nband - 1);
- if ( NULL == band ) {
- elog(NOTICE, "Could not get the required band. Returning a raster "
- "without a band");
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- pgrtn = rt_raster_serialize(newrast);
- rt_raster_destroy(newrast);
- if (NULL == pgrtn) {
- elog(ERROR, "RASTER_mapAlgebraFct: Could not serialize raster");
- PG_RETURN_NULL();
- }
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
- /*
- * Get NODATA value
- */
- POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFct: Getting NODATA value for band...");
-
- if (rt_band_get_hasnodata_flag(band)) {
- rt_band_get_nodata(band, &newnodatavalue);
- }
-
- else {
- newnodatavalue = rt_band_get_min_value(band);
- }
-
- POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFct: NODATA value for band: %f",
- newnodatavalue);
- /**
- * We set the initial value of the future band to nodata value. If nodata
- * value is null, then the raster will be initialized to
- * rt_band_get_min_value but all the values should be recomputed anyway
- **/
- newinitialvalue = newnodatavalue;
-
- /**
- * Set the new pixeltype
- **/
- POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFct: Setting pixeltype...");
-
- if (PG_ARGISNULL(2)) {
- newpixeltype = rt_band_get_pixtype(band);
- }
-
- else {
- strFromText = text_to_cstring(PG_GETARG_TEXT_P(2));
- newpixeltype = rt_pixtype_index_from_name(strFromText);
- pfree(strFromText);
- if (newpixeltype == PT_END)
- newpixeltype = rt_band_get_pixtype(band);
- }
-
- if (newpixeltype == PT_END) {
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- rt_raster_destroy(newrast);
-
- elog(ERROR, "RASTER_mapAlgebraFct: Invalid pixeltype");
- PG_RETURN_NULL();
- }
-
- POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFct: Pixeltype set to %s",
- rt_pixtype_name(newpixeltype));
-
- /* Get the name of the callback user function for raster values */
- if (PG_ARGISNULL(3)) {
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- rt_raster_destroy(newrast);
-
- elog(ERROR, "RASTER_mapAlgebraFct: Required function is missing. Returning NULL");
- PG_RETURN_NULL();
- }
-
- oid = PG_GETARG_OID(3);
- if (oid == InvalidOid) {
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- rt_raster_destroy(newrast);
-
- elog(ERROR, "RASTER_mapAlgebraFct: Got invalid function object id. Returning NULL");
- PG_RETURN_NULL();
- }
-
- fmgr_info(oid, &cbinfo);
-
- /* function cannot return set */
- if (cbinfo.fn_retset) {
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- rt_raster_destroy(newrast);
-
- elog(ERROR, "RASTER_mapAlgebraFct: Function provided must return double precision not resultset");
- PG_RETURN_NULL();
- }
- /* function should have correct # of args */
- else if (cbinfo.fn_nargs < 2 || cbinfo.fn_nargs > 3) {
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- rt_raster_destroy(newrast);
-
- elog(ERROR, "RASTER_mapAlgebraFct: Function does not have two or three input parameters");
- PG_RETURN_NULL();
- }
-
- if (cbinfo.fn_nargs == 2)
- k = 1;
- else
- k = 2;
-
- if (func_volatile(oid) == 'v') {
- elog(NOTICE, "Function provided is VOLATILE. Unless required and for best performance, function should be IMMUTABLE or STABLE");
- }
-
- /* prep function call data */
-#if POSTGIS_PGSQL_VERSION <= 90
- InitFunctionCallInfoData(cbdata, &cbinfo, 2, InvalidOid, NULL);
-#else
- InitFunctionCallInfoData(cbdata, &cbinfo, 2, InvalidOid, NULL, NULL);
-#endif
- memset(cbdata.argnull, FALSE, sizeof(bool) * cbinfo.fn_nargs);
-
- /* check that the function isn't strict if the args are null. */
- if (PG_ARGISNULL(4)) {
- if (cbinfo.fn_strict) {
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- rt_raster_destroy(newrast);
-
- elog(ERROR, "RASTER_mapAlgebraFct: Strict callback functions cannot have null parameters");
- PG_RETURN_NULL();
- }
-
- cbdata.arg[k] = (Datum)NULL;
- cbdata.argnull[k] = TRUE;
- }
- else {
- cbdata.arg[k] = PG_GETARG_DATUM(4);
- }
-
- /**
- * Optimization: If the raster is only filled with nodata values return
- * right now a raster filled with the nodatavalueexpr
- * TODO: Call rt_band_check_isnodata instead?
- **/
- if (rt_band_get_isnodata_flag(band)) {
-
- POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFct: Band is a nodata band, returning "
- "a raster filled with nodata");
-
- ret = rt_raster_generate_new_band(newrast, newpixeltype,
- newinitialvalue, TRUE, newnodatavalue, 0);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- /* Serialize created raster */
- pgrtn = rt_raster_serialize(newrast);
- rt_raster_destroy(newrast);
- if (NULL == pgrtn) {
- elog(ERROR, "RASTER_mapAlgebraFct: Could not serialize raster");
- PG_RETURN_NULL();
- }
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
-
- /**
- * Create the raster receiving all the computed values. Initialize it to the
- * new initial value
- **/
- ret = rt_raster_generate_new_band(newrast, newpixeltype,
- newinitialvalue, TRUE, newnodatavalue, 0);
-
- /* Get the new raster band */
- newband = rt_raster_get_band(newrast, 0);
- if ( NULL == newband ) {
- elog(NOTICE, "Could not modify band for new raster. Returning new "
- "raster with the original band");
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- /* Serialize created raster */
- pgrtn = rt_raster_serialize(newrast);
- rt_raster_destroy(newrast);
- if (NULL == pgrtn) {
- elog(ERROR, "RASTER_mapAlgebraFct: Could not serialize raster");
- PG_RETURN_NULL();
- }
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
- POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFct: Main computing loop (%d x %d)",
- width, height);
-
- for (x = 0; x < width; x++) {
- for(y = 0; y < height; y++) {
- ret = rt_band_get_pixel(band, x, y, &r, NULL);
-
- /**
- * We compute a value only for the withdata value pixel since the
- * nodata value has already been set by the first optimization
- **/
- if (ret == ES_NONE) {
- if (FLT_EQ(r, newnodatavalue)) {
- if (cbinfo.fn_strict) {
- POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFct: Strict callbacks cannot accept NULL arguments, skipping NODATA cell.");
- continue;
- }
- cbdata.argnull[0] = TRUE;
- cbdata.arg[0] = (Datum)NULL;
- }
- else {
- cbdata.argnull[0] = FALSE;
- cbdata.arg[0] = Float8GetDatum(r);
- }
-
- /* Add pixel positions if callback has proper # of args */
- if (cbinfo.fn_nargs == 3) {
- Datum d[2];
- ArrayType *a;
-
- d[0] = Int32GetDatum(x+1);
- d[1] = Int32GetDatum(y+1);
-
- a = construct_array(d, 2, INT4OID, sizeof(int32), true, 'i');
-
- cbdata.argnull[1] = FALSE;
- cbdata.arg[1] = PointerGetDatum(a);
- }
-
- POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFct: (%dx%d), r = %f",
- x, y, r);
-
- tmpnewval = FunctionCallInvoke(&cbdata);
-
- if (cbdata.isnull) {
- newval = newnodatavalue;
- }
- else {
- newval = DatumGetFloat8(tmpnewval);
- }
-
- POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFct: new value = %f",
- newval);
-
- rt_band_set_pixel(newband, x, y, newval, NULL);
- }
-
- }
- }
-
- /* The newrast band has been modified */
-
- POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFct: raster modified, serializing it.");
- /* Serialize created raster */
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- pgrtn = rt_raster_serialize(newrast);
- rt_raster_destroy(newrast);
- if (NULL == pgrtn)
- PG_RETURN_NULL();
-
- POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFct: raster serialized");
-
- POSTGIS_RT_DEBUG(4, "RASTER_mapAlgebraFct: returning raster");
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
-}
-
-/**
- * Return new raster from selected bands of existing raster through ST_Band.
- * second argument is an array of band numbers (1 based)
- */
-PG_FUNCTION_INFO_V1(RASTER_band);
-Datum RASTER_band(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster;
- rt_pgraster *pgrast;
- rt_raster raster;
- rt_raster rast;
-
- bool skip = FALSE;
- ArrayType *array;
- Oid etype;
- Datum *e;
- bool *nulls;
- int16 typlen;
- bool typbyval;
- char typalign;
-
- uint32_t numBands;
- uint32_t *bandNums;
- uint32 idx = 0;
- int n;
- int i = 0;
- int j = 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_band: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- /* process bandNums */
- if (PG_ARGISNULL(1)) {
- elog(NOTICE, "Band number(s) not provided. Returning original raster");
- skip = TRUE;
- }
- do {
- if (skip) break;
-
- numBands = rt_raster_get_num_bands(raster);
-
- array = PG_GETARG_ARRAYTYPE_P(1);
- etype = ARR_ELEMTYPE(array);
- get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
-
- switch (etype) {
- case INT2OID:
- case INT4OID:
- break;
- default:
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_band: Invalid data type for band number(s)");
- PG_RETURN_NULL();
- break;
- }
-
- deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
- &nulls, &n);
-
- bandNums = palloc(sizeof(uint32_t) * n);
- for (i = 0, j = 0; i < n; i++) {
- if (nulls[i]) continue;
-
- switch (etype) {
- case INT2OID:
- idx = (uint32_t) DatumGetInt16(e[i]);
- break;
- case INT4OID:
- idx = (uint32_t) DatumGetInt32(e[i]);
- break;
- }
-
- POSTGIS_RT_DEBUGF(3, "band idx (before): %d", idx);
- if (idx > numBands || idx < 1) {
- elog(NOTICE, "Invalid band index (must use 1-based). Returning original raster");
- skip = TRUE;
- break;
- }
-
- bandNums[j] = idx - 1;
- POSTGIS_RT_DEBUGF(3, "bandNums[%d] = %d", j, bandNums[j]);
- j++;
- }
-
- if (skip || j < 1) {
- pfree(bandNums);
- skip = TRUE;
- }
- }
- while (0);
-
- if (!skip) {
- rast = rt_raster_from_band(raster, bandNums, j);
- pfree(bandNums);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- if (!rast) {
- elog(ERROR, "RASTER_band: Could not create new raster");
- PG_RETURN_NULL();
- }
-
- pgrast = rt_raster_serialize(rast);
- rt_raster_destroy(rast);
-
- if (!pgrast)
- PG_RETURN_NULL();
-
- SET_VARSIZE(pgrast, pgrast->size);
- PG_RETURN_POINTER(pgrast);
- }
-
- PG_RETURN_POINTER(pgraster);
-}
-
-/**
- * Get summary stats of a band
- */
-PG_FUNCTION_INFO_V1(RASTER_summaryStats);
-Datum RASTER_summaryStats(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- rt_band band = NULL;
- int32_t bandindex = 1;
- bool exclude_nodata_value = TRUE;
- int num_bands = 0;
- double sample = 0;
- rt_bandstats stats = NULL;
-
- TupleDesc tupdesc;
- int values_length = 6;
- Datum values[values_length];
- bool nulls[values_length];
- HeapTuple tuple;
- Datum result;
-
- /* pgraster is null, return null */
- 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_summaryStats: 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();
- }
-
- /* exclude_nodata_value flag */
- if (!PG_ARGISNULL(2))
- exclude_nodata_value = PG_GETARG_BOOL(2);
-
- /* sample % */
- if (!PG_ARGISNULL(3)) {
- sample = PG_GETARG_FLOAT8(3);
- if (sample < 0 || sample > 1) {
- elog(NOTICE, "Invalid sample percentage (must be between 0 and 1). Returning NULL");
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- PG_RETURN_NULL();
- }
- else if (FLT_EQ(sample, 0.0))
- sample = 1;
- }
- else
- sample = 1;
-
- /* 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();
- }
-
- /* we don't need the raw values, hence the zero parameter */
- stats = rt_band_get_summary_stats(band, (int) exclude_nodata_value, sample, 0, NULL, NULL, NULL);
- rt_band_destroy(band);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- if (NULL == stats) {
- elog(NOTICE, "Could not compute summary statistics for band at index %d. Returning NULL", bandindex);
- PG_RETURN_NULL();
- }
-
- /* 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);
-
- memset(nulls, FALSE, sizeof(bool) * values_length);
-
- values[0] = Int64GetDatum(stats->count);
- if (stats->count > 0) {
- values[1] = Float8GetDatum(stats->sum);
- values[2] = Float8GetDatum(stats->mean);
- values[3] = Float8GetDatum(stats->stddev);
- values[4] = Float8GetDatum(stats->min);
- values[5] = Float8GetDatum(stats->max);
- }
- else {
- nulls[1] = TRUE;
- nulls[2] = TRUE;
- nulls[3] = TRUE;
- nulls[4] = TRUE;
- nulls[5] = TRUE;
- }
-
- /* build a tuple */
- tuple = heap_form_tuple(tupdesc, values, nulls);
-
- /* make the tuple into a datum */
- result = HeapTupleGetDatum(tuple);
-
- /* clean up */
- pfree(stats);
-
- PG_RETURN_DATUM(result);
-}
-
-/**
- * Get summary stats of a coverage for a specific band
- */
-PG_FUNCTION_INFO_V1(RASTER_summaryStatsCoverage);
-Datum RASTER_summaryStatsCoverage(PG_FUNCTION_ARGS)
-{
- text *tablenametext = NULL;
- char *tablename = NULL;
- text *colnametext = NULL;
- char *colname = NULL;
- int32_t bandindex = 1;
- bool exclude_nodata_value = TRUE;
- double sample = 0;
-
- int len = 0;
- char *sql = NULL;
- int spi_result;
- Portal portal;
- TupleDesc tupdesc;
- SPITupleTable *tuptable = NULL;
- HeapTuple tuple;
- Datum datum;
- bool isNull = FALSE;
-
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- rt_band band = NULL;
- int num_bands = 0;
- uint64_t cK = 0;
- double cM = 0;
- double cQ = 0;
- rt_bandstats stats = NULL;
- rt_bandstats rtn = NULL;
-
- int values_length = 6;
- Datum values[values_length];
- bool nulls[values_length];
- Datum result;
-
- /* tablename is null, return null */
- if (PG_ARGISNULL(0)) {
- elog(NOTICE, "Table name must be provided");
- PG_RETURN_NULL();
- }
- tablenametext = PG_GETARG_TEXT_P(0);
- tablename = text_to_cstring(tablenametext);
- if (!strlen(tablename)) {
- elog(NOTICE, "Table name must be provided");
- PG_RETURN_NULL();
- }
-
- /* column name is null, return null */
- if (PG_ARGISNULL(1)) {
- elog(NOTICE, "Column name must be provided");
- PG_RETURN_NULL();
- }
- colnametext = PG_GETARG_TEXT_P(1);
- colname = text_to_cstring(colnametext);
- if (!strlen(colname)) {
- elog(NOTICE, "Column name must be provided");
- PG_RETURN_NULL();
- }
-
- /* band index is 1-based */
- if (!PG_ARGISNULL(2))
- bandindex = PG_GETARG_INT32(2);
-
- /* exclude_nodata_value flag */
- if (!PG_ARGISNULL(3))
- exclude_nodata_value = PG_GETARG_BOOL(3);
-
- /* sample % */
- if (!PG_ARGISNULL(4)) {
- sample = PG_GETARG_FLOAT8(4);
- if (sample < 0 || sample > 1) {
- elog(NOTICE, "Invalid sample percentage (must be between 0 and 1). Returning NULL");
- rt_raster_destroy(raster);
- PG_RETURN_NULL();
- }
- else if (FLT_EQ(sample, 0.0))
- sample = 1;
- }
- else
- sample = 1;
-
- /* iterate through rasters of coverage */
- /* connect to database */
- spi_result = SPI_connect();
- if (spi_result != SPI_OK_CONNECT) {
- pfree(sql);
- elog(ERROR, "RASTER_summaryStatsCoverage: Could not connect to database using SPI");
- PG_RETURN_NULL();
- }
-
- /* create sql */
- len = sizeof(char) * (strlen("SELECT \"\" FROM \"\" WHERE \"\" IS NOT NULL") + (strlen(colname) * 2) + strlen(tablename) + 1);
- sql = (char *) palloc(len);
- if (NULL == sql) {
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_finish();
- elog(ERROR, "RASTER_summaryStatsCoverage: Could not allocate memory for sql");
- PG_RETURN_NULL();
- }
-
- /* get cursor */
- snprintf(sql, len, "SELECT \"%s\" FROM \"%s\" WHERE \"%s\" IS NOT NULL", colname, tablename, colname);
- portal = SPI_cursor_open_with_args(
- "coverage",
- sql,
- 0, NULL,
- NULL, NULL,
- TRUE, 0
- );
- pfree(sql);
-
- /* process resultset */
- SPI_cursor_fetch(portal, TRUE, 1);
- while (SPI_processed == 1 && SPI_tuptable != NULL) {
- tupdesc = SPI_tuptable->tupdesc;
- tuptable = SPI_tuptable;
- tuple = tuptable->vals[0];
-
- datum = SPI_getbinval(tuple, tupdesc, 1, &isNull);
- if (SPI_result == SPI_ERROR_NOATTRIBUTE) {
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- if (NULL != rtn) pfree(rtn);
- elog(ERROR, "RASTER_summaryStatsCoverage: Could not get raster of coverage");
- PG_RETURN_NULL();
- }
- else if (isNull) {
- SPI_cursor_fetch(portal, TRUE, 1);
- continue;
- }
-
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM(datum);
-
- raster = rt_raster_deserialize(pgraster, FALSE);
- if (!raster) {
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- if (NULL != rtn) pfree(rtn);
- elog(ERROR, "RASTER_summaryStatsCoverage: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- /* inspect number of bands */
- 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);
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- if (NULL != rtn) pfree(rtn);
- 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);
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- if (NULL != rtn) pfree(rtn);
- PG_RETURN_NULL();
- }
-
- /* we don't need the raw values, hence the zero parameter */
- stats = rt_band_get_summary_stats(band, (int) exclude_nodata_value, sample, 0, &cK, &cM, &cQ);
-
- rt_band_destroy(band);
- rt_raster_destroy(raster);
-
- if (NULL == stats) {
- elog(NOTICE, "Could not compute summary statistics for band at index %d. Returning NULL", bandindex);
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- if (NULL != rtn) pfree(rtn);
- PG_RETURN_NULL();
- }
-
- /* initialize rtn */
- if (stats->count > 0) {
- if (NULL == rtn) {
- rtn = (rt_bandstats) SPI_palloc(sizeof(struct rt_bandstats_t));
- if (NULL == rtn) {
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- elog(ERROR, "RASTER_summaryStatsCoverage: Could not allocate memory for summary stats of coverage");
- PG_RETURN_NULL();
- }
-
- rtn->sample = stats->sample;
- rtn->count = stats->count;
- rtn->min = stats->min;
- rtn->max = stats->max;
- rtn->sum = stats->sum;
- rtn->mean = stats->mean;
- rtn->stddev = -1;
-
- rtn->values = NULL;
- rtn->sorted = 0;
- }
- else {
- rtn->count += stats->count;
- rtn->sum += stats->sum;
-
- if (stats->min < rtn->min)
- rtn->min = stats->min;
- if (stats->max > rtn->max)
- rtn->max = stats->max;
- }
- }
-
- pfree(stats);
-
- /* next record */
- SPI_cursor_fetch(portal, TRUE, 1);
- }
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- if (NULL == rtn) {
- elog(ERROR, "RASTER_summaryStatsCoverage: Could not compute coverage summary stats");
- PG_RETURN_NULL();
- }
-
- /* coverage mean and deviation */
- rtn->mean = rtn->sum / rtn->count;
- /* sample deviation */
- if (rtn->sample > 0 && rtn->sample < 1)
- rtn->stddev = sqrt(cQ / (rtn->count - 1));
- /* standard deviation */
- else
- rtn->stddev = sqrt(cQ / rtn->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);
-
- memset(nulls, FALSE, sizeof(bool) * values_length);
-
- values[0] = Int64GetDatum(rtn->count);
- if (rtn->count > 0) {
- values[1] = Float8GetDatum(rtn->sum);
- values[2] = Float8GetDatum(rtn->mean);
- values[3] = Float8GetDatum(rtn->stddev);
- values[4] = Float8GetDatum(rtn->min);
- values[5] = Float8GetDatum(rtn->max);
- }
- else {
- nulls[1] = TRUE;
- nulls[2] = TRUE;
- nulls[3] = TRUE;
- nulls[4] = TRUE;
- nulls[5] = TRUE;
- }
-
- /* build a tuple */
- tuple = heap_form_tuple(tupdesc, values, nulls);
-
- /* make the tuple into a datum */
- result = HeapTupleGetDatum(tuple);
-
- /* clean up */
- pfree(rtn);
-
- PG_RETURN_DATUM(result);
-}
-
-/**
- * Returns histogram for a band
- */
-PG_FUNCTION_INFO_V1(RASTER_histogram);
-Datum RASTER_histogram(PG_FUNCTION_ARGS)
-{
- FuncCallContext *funcctx;
- TupleDesc tupdesc;
-
- int i;
- rt_histogram hist;
- rt_histogram hist2;
- int call_cntr;
- int max_calls;
-
- /* first call of function */
- if (SRF_IS_FIRSTCALL()) {
- MemoryContext oldcontext;
-
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- rt_band band = NULL;
- int32_t bandindex = 1;
- int num_bands = 0;
- bool exclude_nodata_value = TRUE;
- double sample = 0;
- uint32_t bin_count = 0;
- double *bin_width = NULL;
- uint32_t bin_width_count = 0;
- double width = 0;
- bool right = FALSE;
- double min = 0;
- double max = 0;
- rt_bandstats stats = NULL;
- uint32_t count;
-
- int j;
- int n;
-
- ArrayType *array;
- Oid etype;
- Datum *e;
- bool *nulls;
- int16 typlen;
- bool typbyval;
- char typalign;
-
- POSTGIS_RT_DEBUG(3, "RASTER_histogram: Starting");
-
- /* 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);
-
- /* pgraster is null, return nothing */
- 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_histogram: Could not deserialize raster");
- SRF_RETURN_DONE(funcctx);
- }
-
- /* 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);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- /* exclude_nodata_value flag */
- if (!PG_ARGISNULL(2))
- exclude_nodata_value = PG_GETARG_BOOL(2);
-
- /* sample % */
- if (!PG_ARGISNULL(3)) {
- sample = PG_GETARG_FLOAT8(3);
- if (sample < 0 || sample > 1) {
- elog(NOTICE, "Invalid sample percentage (must be between 0 and 1). Returning NULL");
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- else if (FLT_EQ(sample, 0.0))
- sample = 1;
- }
- else
- sample = 1;
-
- /* bin_count */
- if (!PG_ARGISNULL(4)) {
- bin_count = PG_GETARG_INT32(4);
- if (bin_count < 1) bin_count = 0;
- }
-
- /* bin_width */
- if (!PG_ARGISNULL(5)) {
- array = PG_GETARG_ARRAYTYPE_P(5);
- 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_histogram: Invalid data type for width");
- SRF_RETURN_DONE(funcctx);
- break;
- }
-
- deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
- &nulls, &n);
-
- bin_width = palloc(sizeof(double) * n);
- for (i = 0, j = 0; i < n; i++) {
- if (nulls[i]) continue;
-
- switch (etype) {
- case FLOAT4OID:
- width = (double) DatumGetFloat4(e[i]);
- break;
- case FLOAT8OID:
- width = (double) DatumGetFloat8(e[i]);
- break;
- }
-
- if (width < 0 || FLT_EQ(width, 0.0)) {
- elog(NOTICE, "Invalid value for width (must be greater than 0). Returning NULL");
- pfree(bin_width);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- bin_width[j] = width;
- POSTGIS_RT_DEBUGF(5, "bin_width[%d] = %f", j, bin_width[j]);
- j++;
- }
- bin_width_count = j;
-
- if (j < 1) {
- pfree(bin_width);
- bin_width = NULL;
- }
- }
-
- /* right */
- if (!PG_ARGISNULL(6))
- right = PG_GETARG_BOOL(6);
-
- /* min */
- if (!PG_ARGISNULL(7)) min = PG_GETARG_FLOAT8(7);
-
- /* max */
- if (!PG_ARGISNULL(8)) max = PG_GETARG_FLOAT8(8);
-
- /* 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);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- /* get stats */
- stats = rt_band_get_summary_stats(band, (int) exclude_nodata_value, sample, 1, NULL, NULL, NULL);
- rt_band_destroy(band);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- if (NULL == stats || NULL == stats->values) {
- elog(NOTICE, "Could not compute summary statistics for band at index %d", bandindex);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- else if (stats->count < 1) {
- elog(NOTICE, "Could not compute histogram for band at index %d as the band has no values", bandindex);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- /* get histogram */
- hist = rt_band_get_histogram(stats, bin_count, bin_width, bin_width_count, right, min, max, &count);
- if (bin_width_count) pfree(bin_width);
- pfree(stats);
- if (NULL == hist || !count) {
- elog(NOTICE, "Could not compute histogram for band at index %d", bandindex);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- POSTGIS_RT_DEBUGF(3, "%d bins returned", count);
-
- /* Store needed information */
- funcctx->user_fctx = hist;
-
- /* 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;
- hist2 = funcctx->user_fctx;
-
- /* do when there is more left to send */
- if (call_cntr < max_calls) {
- int values_length = 4;
- Datum values[values_length];
- bool nulls[values_length];
- HeapTuple tuple;
- Datum result;
-
- POSTGIS_RT_DEBUGF(3, "Result %d", call_cntr);
-
- memset(nulls, FALSE, sizeof(bool) * values_length);
-
- values[0] = Float8GetDatum(hist2[call_cntr].min);
- values[1] = Float8GetDatum(hist2[call_cntr].max);
- values[2] = Int64GetDatum(hist2[call_cntr].count);
- values[3] = Float8GetDatum(hist2[call_cntr].percent);
-
- /* build a tuple */
- tuple = heap_form_tuple(tupdesc, values, nulls);
-
- /* make the tuple into a datum */
- result = HeapTupleGetDatum(tuple);
-
- SRF_RETURN_NEXT(funcctx, result);
- }
- /* do when there is no more left */
- else {
- pfree(hist2);
- SRF_RETURN_DONE(funcctx);
- }
-}
-
-/**
- * Returns histogram of a coverage for a specified band
- */
-PG_FUNCTION_INFO_V1(RASTER_histogramCoverage);
-Datum RASTER_histogramCoverage(PG_FUNCTION_ARGS)
-{
- FuncCallContext *funcctx;
- TupleDesc tupdesc;
-
- int i;
- rt_histogram covhist = NULL;
- rt_histogram covhist2;
- int call_cntr;
- int max_calls;
-
- POSTGIS_RT_DEBUG(3, "RASTER_histogramCoverage: Starting");
-
- /* first call of function */
- if (SRF_IS_FIRSTCALL()) {
- MemoryContext oldcontext;
-
- text *tablenametext = NULL;
- char *tablename = NULL;
- text *colnametext = NULL;
- char *colname = NULL;
- int32_t bandindex = 1;
- bool exclude_nodata_value = TRUE;
- double sample = 0;
- uint32_t bin_count = 0;
- double *bin_width = NULL;
- uint32_t bin_width_count = 0;
- double width = 0;
- bool right = FALSE;
- uint32_t count;
-
- int len = 0;
- char *sql = NULL;
- char *tmp = NULL;
- double min = 0;
- double max = 0;
- int spi_result;
- Portal portal;
- SPITupleTable *tuptable = NULL;
- HeapTuple tuple;
- Datum datum;
- bool isNull = FALSE;
-
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- rt_band band = NULL;
- int num_bands = 0;
- rt_bandstats stats = NULL;
- rt_histogram hist;
- uint64_t sum = 0;
-
- int j;
- int n;
-
- ArrayType *array;
- Oid etype;
- Datum *e;
- bool *nulls;
- int16 typlen;
- bool typbyval;
- char typalign;
-
- POSTGIS_RT_DEBUG(3, "RASTER_histogramCoverage: first call of function");
-
- /* 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);
-
- /* tablename is null, return null */
- if (PG_ARGISNULL(0)) {
- elog(NOTICE, "Table name must be provided");
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- tablenametext = PG_GETARG_TEXT_P(0);
- tablename = text_to_cstring(tablenametext);
- if (!strlen(tablename)) {
- elog(NOTICE, "Table name must be provided");
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- POSTGIS_RT_DEBUGF(3, "RASTER_histogramCoverage: tablename = %s", tablename);
-
- /* column name is null, return null */
- if (PG_ARGISNULL(1)) {
- elog(NOTICE, "Column name must be provided");
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- colnametext = PG_GETARG_TEXT_P(1);
- colname = text_to_cstring(colnametext);
- if (!strlen(colname)) {
- elog(NOTICE, "Column name must be provided");
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- POSTGIS_RT_DEBUGF(3, "RASTER_histogramCoverage: colname = %s", colname);
-
- /* band index is 1-based */
- if (!PG_ARGISNULL(2))
- bandindex = PG_GETARG_INT32(2);
-
- /* exclude_nodata_value flag */
- if (!PG_ARGISNULL(3))
- exclude_nodata_value = PG_GETARG_BOOL(3);
-
- /* sample % */
- if (!PG_ARGISNULL(4)) {
- sample = PG_GETARG_FLOAT8(4);
- if (sample < 0 || sample > 1) {
- elog(NOTICE, "Invalid sample percentage (must be between 0 and 1). Returning NULL");
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- else if (FLT_EQ(sample, 0.0))
- sample = 1;
- }
- else
- sample = 1;
-
- /* bin_count */
- if (!PG_ARGISNULL(5)) {
- bin_count = PG_GETARG_INT32(5);
- if (bin_count < 1) bin_count = 0;
- }
-
- /* bin_width */
- if (!PG_ARGISNULL(6)) {
- array = PG_GETARG_ARRAYTYPE_P(6);
- etype = ARR_ELEMTYPE(array);
- get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
-
- switch (etype) {
- case FLOAT4OID:
- case FLOAT8OID:
- break;
- default:
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_histogramCoverage: Invalid data type for width");
- SRF_RETURN_DONE(funcctx);
- break;
- }
-
- deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
- &nulls, &n);
-
- bin_width = palloc(sizeof(double) * n);
- for (i = 0, j = 0; i < n; i++) {
- if (nulls[i]) continue;
-
- switch (etype) {
- case FLOAT4OID:
- width = (double) DatumGetFloat4(e[i]);
- break;
- case FLOAT8OID:
- width = (double) DatumGetFloat8(e[i]);
- break;
- }
-
- if (width < 0 || FLT_EQ(width, 0.0)) {
- elog(NOTICE, "Invalid value for width (must be greater than 0). Returning NULL");
- pfree(bin_width);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- bin_width[j] = width;
- POSTGIS_RT_DEBUGF(5, "bin_width[%d] = %f", j, bin_width[j]);
- j++;
- }
- bin_width_count = j;
-
- if (j < 1) {
- pfree(bin_width);
- bin_width = NULL;
- }
- }
-
- /* right */
- if (!PG_ARGISNULL(7))
- right = PG_GETARG_BOOL(7);
-
- /* connect to database */
- spi_result = SPI_connect();
- if (spi_result != SPI_OK_CONNECT) {
-
- if (bin_width_count) pfree(bin_width);
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_histogramCoverage: Could not connect to database using SPI");
- SRF_RETURN_DONE(funcctx);
- }
-
- /* coverage stats */
- len = sizeof(char) * (strlen("SELECT min, max FROM _st_summarystats('','',,::boolean,)") + strlen(tablename) + strlen(colname) + (MAX_INT_CHARLEN * 2) + MAX_DBL_CHARLEN + 1);
- sql = (char *) palloc(len);
- if (NULL == sql) {
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_finish();
-
- if (bin_width_count) pfree(bin_width);
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_histogramCoverage: Could not allocate memory for sql");
- SRF_RETURN_DONE(funcctx);
- }
-
- /* get stats */
- snprintf(sql, len, "SELECT min, max FROM _st_summarystats('%s','%s',%d,%d::boolean,%f)", tablename, colname, bandindex, (exclude_nodata_value ? 1 : 0), sample);
- POSTGIS_RT_DEBUGF(3, "RASTER_histogramCoverage: %s", sql);
- spi_result = SPI_execute(sql, TRUE, 0);
- pfree(sql);
- if (spi_result != SPI_OK_SELECT || SPI_tuptable == NULL || SPI_processed != 1) {
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_finish();
-
- if (bin_width_count) pfree(bin_width);
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_histogramCoverage: Could not get summary stats of coverage");
- SRF_RETURN_DONE(funcctx);
- }
-
- tupdesc = SPI_tuptable->tupdesc;
- tuptable = SPI_tuptable;
- tuple = tuptable->vals[0];
-
- tmp = SPI_getvalue(tuple, tupdesc, 1);
- if (NULL == tmp || !strlen(tmp)) {
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_finish();
-
- if (bin_width_count) pfree(bin_width);
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_histogramCoverage: Could not get summary stats of coverage");
- SRF_RETURN_DONE(funcctx);
- }
- min = strtod(tmp, NULL);
- POSTGIS_RT_DEBUGF(3, "RASTER_histogramCoverage: min = %f", min);
- pfree(tmp);
-
- tmp = SPI_getvalue(tuple, tupdesc, 2);
- if (NULL == tmp || !strlen(tmp)) {
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_finish();
-
- if (bin_width_count) pfree(bin_width);
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_histogramCoverage: Could not get summary stats of coverage");
- SRF_RETURN_DONE(funcctx);
- }
- max = strtod(tmp, NULL);
- POSTGIS_RT_DEBUGF(3, "RASTER_histogramCoverage: max = %f", max);
- pfree(tmp);
-
- /* iterate through rasters of coverage */
- /* create sql */
- len = sizeof(char) * (strlen("SELECT \"\" FROM \"\" WHERE \"\" IS NOT NULL") + (strlen(colname) * 2) + strlen(tablename) + 1);
- sql = (char *) palloc(len);
- if (NULL == sql) {
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_finish();
-
- if (bin_width_count) pfree(bin_width);
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_histogramCoverage: Could not allocate memory for sql");
- SRF_RETURN_DONE(funcctx);
- }
-
- /* get cursor */
- snprintf(sql, len, "SELECT \"%s\" FROM \"%s\" WHERE \"%s\" IS NOT NULL", colname, tablename, colname);
- POSTGIS_RT_DEBUGF(3, "RASTER_histogramCoverage: %s", sql);
- portal = SPI_cursor_open_with_args(
- "coverage",
- sql,
- 0, NULL,
- NULL, NULL,
- TRUE, 0
- );
- pfree(sql);
-
- /* process resultset */
- SPI_cursor_fetch(portal, TRUE, 1);
- while (SPI_processed == 1 && SPI_tuptable != NULL) {
- tupdesc = SPI_tuptable->tupdesc;
- tuptable = SPI_tuptable;
- tuple = tuptable->vals[0];
-
- datum = SPI_getbinval(tuple, tupdesc, 1, &isNull);
- if (SPI_result == SPI_ERROR_NOATTRIBUTE) {
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- if (NULL != covhist) pfree(covhist);
- if (bin_width_count) pfree(bin_width);
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_histogramCoverage: Could not get raster of coverage");
- SRF_RETURN_DONE(funcctx);
- }
- else if (isNull) {
- SPI_cursor_fetch(portal, TRUE, 1);
- continue;
- }
-
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM(datum);
-
- raster = rt_raster_deserialize(pgraster, FALSE);
- if (!raster) {
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- if (NULL != covhist) pfree(covhist);
- if (bin_width_count) pfree(bin_width);
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_histogramCoverage: Could not deserialize raster");
- SRF_RETURN_DONE(funcctx);
- }
-
- /* inspect number of bands*/
- 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);
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- if (NULL != covhist) pfree(covhist);
- if (bin_width_count) pfree(bin_width);
-
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- /* 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);
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- if (NULL != covhist) pfree(covhist);
- if (bin_width_count) pfree(bin_width);
-
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- /* we need the raw values, hence the non-zero parameter */
- stats = rt_band_get_summary_stats(band, (int) exclude_nodata_value, sample, 1, NULL, NULL, NULL);
-
- rt_band_destroy(band);
- rt_raster_destroy(raster);
-
- if (NULL == stats) {
- elog(NOTICE, "Could not compute summary statistics for band at index %d. Returning NULL", bandindex);
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- if (NULL != covhist) pfree(covhist);
- if (bin_width_count) pfree(bin_width);
-
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- /* get histogram */
- if (stats->count > 0) {
- hist = rt_band_get_histogram(stats, bin_count, bin_width, bin_width_count, right, min, max, &count);
- pfree(stats);
- if (NULL == hist || !count) {
- elog(NOTICE, "Could not compute histogram for band at index %d", bandindex);
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- if (NULL != covhist) pfree(covhist);
- if (bin_width_count) pfree(bin_width);
-
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- POSTGIS_RT_DEBUGF(3, "%d bins returned", count);
-
- /* coverage histogram */
- if (NULL == covhist) {
- covhist = (rt_histogram) SPI_palloc(sizeof(struct rt_histogram_t) * count);
- if (NULL == covhist) {
-
- pfree(hist);
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- if (bin_width_count) pfree(bin_width);
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_histogramCoverage: Could not allocate memory for histogram of coverage");
- SRF_RETURN_DONE(funcctx);
- }
-
- for (i = 0; i < count; i++) {
- sum += hist[i].count;
- covhist[i].count = hist[i].count;
- covhist[i].percent = 0;
- covhist[i].min = hist[i].min;
- covhist[i].max = hist[i].max;
- }
- }
- else {
- for (i = 0; i < count; i++) {
- sum += hist[i].count;
- covhist[i].count += hist[i].count;
- }
- }
-
- pfree(hist);
-
- /* assuming bin_count wasn't set, force consistency */
- if (bin_count <= 0) bin_count = count;
- }
-
- /* next record */
- SPI_cursor_fetch(portal, TRUE, 1);
- }
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- if (bin_width_count) pfree(bin_width);
-
- /* finish percent of histogram */
- if (sum > 0) {
- for (i = 0; i < count; i++)
- covhist[i].percent = covhist[i].count / (double) sum;
- }
-
- /* Store needed information */
- funcctx->user_fctx = covhist;
-
- /* 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;
- covhist2 = funcctx->user_fctx;
-
- /* do when there is more left to send */
- if (call_cntr < max_calls) {
- int values_length = 4;
- Datum values[values_length];
- bool nulls[values_length];
- HeapTuple tuple;
- Datum result;
-
- POSTGIS_RT_DEBUGF(3, "Result %d", call_cntr);
-
- memset(nulls, FALSE, sizeof(bool) * values_length);
-
- values[0] = Float8GetDatum(covhist2[call_cntr].min);
- values[1] = Float8GetDatum(covhist2[call_cntr].max);
- values[2] = Int64GetDatum(covhist2[call_cntr].count);
- values[3] = Float8GetDatum(covhist2[call_cntr].percent);
-
- /* build a tuple */
- tuple = heap_form_tuple(tupdesc, values, nulls);
-
- /* make the tuple into a datum */
- result = HeapTupleGetDatum(tuple);
-
- SRF_RETURN_NEXT(funcctx, result);
- }
- /* do when there is no more left */
- else {
- pfree(covhist2);
- SRF_RETURN_DONE(funcctx);
- }
-}
-
-/**
- * Returns quantiles for a band
- */
-PG_FUNCTION_INFO_V1(RASTER_quantile);
-Datum RASTER_quantile(PG_FUNCTION_ARGS)
-{
- FuncCallContext *funcctx;
- TupleDesc tupdesc;
-
- int i;
- rt_quantile quant;
- rt_quantile quant2;
- int call_cntr;
- int max_calls;
-
- /* first call of function */
- if (SRF_IS_FIRSTCALL()) {
- MemoryContext oldcontext;
-
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- rt_band band = NULL;
- int32_t bandindex = 0;
- int num_bands = 0;
- bool exclude_nodata_value = TRUE;
- double sample = 0;
- double *quantiles = NULL;
- uint32_t quantiles_count = 0;
- double quantile = 0;
- rt_bandstats stats = NULL;
- uint32_t count;
-
- int j;
- int n;
-
- 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);
-
- /* pgraster is null, return nothing */
- 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_quantile: Could not deserialize raster");
- SRF_RETURN_DONE(funcctx);
- }
-
- /* band index is 1-based */
- 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);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- /* exclude_nodata_value flag */
- if (!PG_ARGISNULL(2))
- exclude_nodata_value = PG_GETARG_BOOL(2);
-
- /* sample % */
- if (!PG_ARGISNULL(3)) {
- sample = PG_GETARG_FLOAT8(3);
- if (sample < 0 || sample > 1) {
- elog(NOTICE, "Invalid sample percentage (must be between 0 and 1). Returning NULL");
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- else if (FLT_EQ(sample, 0.0))
- sample = 1;
- }
- else
- sample = 1;
-
- /* quantiles */
- if (!PG_ARGISNULL(4)) {
- 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);
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_quantile: Invalid data type for quantiles");
- SRF_RETURN_DONE(funcctx);
- break;
- }
-
- deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
- &nulls, &n);
-
- quantiles = palloc(sizeof(double) * n);
- for (i = 0, j = 0; i < n; i++) {
- if (nulls[i]) continue;
-
- switch (etype) {
- case FLOAT4OID:
- quantile = (double) DatumGetFloat4(e[i]);
- break;
- case FLOAT8OID:
- quantile = (double) DatumGetFloat8(e[i]);
- break;
- }
-
- if (quantile < 0 || quantile > 1) {
- elog(NOTICE, "Invalid value for quantile (must be between 0 and 1). Returning NULL");
- pfree(quantiles);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- quantiles[j] = quantile;
- POSTGIS_RT_DEBUGF(5, "quantiles[%d] = %f", j, quantiles[j]);
- j++;
- }
- quantiles_count = j;
-
- if (j < 1) {
- pfree(quantiles);
- quantiles = 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);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- /* get stats */
- stats = rt_band_get_summary_stats(band, (int) exclude_nodata_value, sample, 1, NULL, NULL, NULL);
- rt_band_destroy(band);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- if (NULL == stats || NULL == stats->values) {
- elog(NOTICE, "Could not retrieve summary statistics for band at index %d", bandindex);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- else if (stats->count < 1) {
- elog(NOTICE, "Could not compute quantiles for band at index %d as the band has no values", bandindex);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- /* get quantiles */
- quant = rt_band_get_quantiles(stats, quantiles, quantiles_count, &count);
- if (quantiles_count) pfree(quantiles);
- pfree(stats);
- if (NULL == quant || !count) {
- elog(NOTICE, "Could not compute quantiles for band at index %d", bandindex);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- POSTGIS_RT_DEBUGF(3, "%d quantiles returned", count);
-
- /* Store needed information */
- funcctx->user_fctx = quant;
-
- /* 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;
- quant2 = funcctx->user_fctx;
-
- /* do when there is more left to send */
- if (call_cntr < max_calls) {
- int values_length = 2;
- Datum values[values_length];
- bool nulls[values_length];
- HeapTuple tuple;
- Datum result;
-
- POSTGIS_RT_DEBUGF(3, "Result %d", call_cntr);
-
- memset(nulls, FALSE, sizeof(bool) * values_length);
-
- values[0] = Float8GetDatum(quant2[call_cntr].quantile);
- values[1] = Float8GetDatum(quant2[call_cntr].value);
-
- /* build a tuple */
- tuple = heap_form_tuple(tupdesc, values, nulls);
-
- /* make the tuple into a datum */
- result = HeapTupleGetDatum(tuple);
-
- SRF_RETURN_NEXT(funcctx, result);
- }
- /* do when there is no more left */
- else {
- pfree(quant2);
- SRF_RETURN_DONE(funcctx);
- }
-}
-
-/**
- * Returns selected quantiles of a coverage for a specified band
- */
-PG_FUNCTION_INFO_V1(RASTER_quantileCoverage);
-Datum RASTER_quantileCoverage(PG_FUNCTION_ARGS)
-{
- FuncCallContext *funcctx;
- TupleDesc tupdesc;
-
- int i;
- rt_quantile covquant = NULL;
- rt_quantile covquant2;
- int call_cntr;
- int max_calls;
-
- POSTGIS_RT_DEBUG(3, "RASTER_quantileCoverage: Starting");
-
- /* first call of function */
- if (SRF_IS_FIRSTCALL()) {
- MemoryContext oldcontext;
-
- text *tablenametext = NULL;
- char *tablename = NULL;
- text *colnametext = NULL;
- char *colname = NULL;
- int32_t bandindex = 1;
- bool exclude_nodata_value = TRUE;
- double sample = 0;
- double *quantiles = NULL;
- uint32_t quantiles_count = 0;
- double quantile = 0;
- uint32_t count;
-
- int len = 0;
- char *sql = NULL;
- char *tmp = NULL;
- uint64_t cov_count = 0;
- int spi_result;
- Portal portal;
- SPITupleTable *tuptable = NULL;
- HeapTuple tuple;
- Datum datum;
- bool isNull = FALSE;
-
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- rt_band band = NULL;
- int num_bands = 0;
- struct quantile_llist *qlls = NULL;
- uint32_t qlls_count;
-
- int j;
- int n;
-
- ArrayType *array;
- Oid etype;
- Datum *e;
- bool *nulls;
- int16 typlen;
- bool typbyval;
- char typalign;
-
- POSTGIS_RT_DEBUG(3, "RASTER_quantileCoverage: first call of function");
-
- /* 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);
-
- /* tablename is null, return null */
- if (PG_ARGISNULL(0)) {
- elog(NOTICE, "Table name must be provided");
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- tablenametext = PG_GETARG_TEXT_P(0);
- tablename = text_to_cstring(tablenametext);
- if (!strlen(tablename)) {
- elog(NOTICE, "Table name must be provided");
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- POSTGIS_RT_DEBUGF(3, "RASTER_quantileCoverage: tablename = %s", tablename);
-
- /* column name is null, return null */
- if (PG_ARGISNULL(1)) {
- elog(NOTICE, "Column name must be provided");
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- colnametext = PG_GETARG_TEXT_P(1);
- colname = text_to_cstring(colnametext);
- if (!strlen(colname)) {
- elog(NOTICE, "Column name must be provided");
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- POSTGIS_RT_DEBUGF(3, "RASTER_quantileCoverage: colname = %s", colname);
-
- /* band index is 1-based */
- if (!PG_ARGISNULL(2))
- bandindex = PG_GETARG_INT32(2);
-
- /* exclude_nodata_value flag */
- if (!PG_ARGISNULL(3))
- exclude_nodata_value = PG_GETARG_BOOL(3);
-
- /* sample % */
- if (!PG_ARGISNULL(4)) {
- sample = PG_GETARG_FLOAT8(4);
- if (sample < 0 || sample > 1) {
- elog(NOTICE, "Invalid sample percentage (must be between 0 and 1). Returning NULL");
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- else if (FLT_EQ(sample, 0.0))
- sample = 1;
- }
- else
- sample = 1;
-
- /* quantiles */
- if (!PG_ARGISNULL(5)) {
- array = PG_GETARG_ARRAYTYPE_P(5);
- etype = ARR_ELEMTYPE(array);
- get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
-
- switch (etype) {
- case FLOAT4OID:
- case FLOAT8OID:
- break;
- default:
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_quantileCoverage: Invalid data type for quantiles");
- SRF_RETURN_DONE(funcctx);
- break;
- }
-
- deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
- &nulls, &n);
-
- quantiles = palloc(sizeof(double) * n);
- for (i = 0, j = 0; i < n; i++) {
- if (nulls[i]) continue;
-
- switch (etype) {
- case FLOAT4OID:
- quantile = (double) DatumGetFloat4(e[i]);
- break;
- case FLOAT8OID:
- quantile = (double) DatumGetFloat8(e[i]);
- break;
- }
-
- if (quantile < 0 || quantile > 1) {
- elog(NOTICE, "Invalid value for quantile (must be between 0 and 1). Returning NULL");
- pfree(quantiles);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- quantiles[j] = quantile;
- POSTGIS_RT_DEBUGF(5, "quantiles[%d] = %f", j, quantiles[j]);
- j++;
- }
- quantiles_count = j;
-
- if (j < 1) {
- pfree(quantiles);
- quantiles = NULL;
- }
- }
-
- /* coverage stats */
- /* connect to database */
- spi_result = SPI_connect();
- if (spi_result != SPI_OK_CONNECT) {
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_quantileCoverage: Could not connect to database using SPI");
- SRF_RETURN_DONE(funcctx);
- }
-
- len = sizeof(char) * (strlen("SELECT count FROM _st_summarystats('','',,::boolean,)") + strlen(tablename) + strlen(colname) + (MAX_INT_CHARLEN * 2) + MAX_DBL_CHARLEN + 1);
- sql = (char *) palloc(len);
- if (NULL == sql) {
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_finish();
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_quantileCoverage: Could not allocate memory for sql");
- SRF_RETURN_DONE(funcctx);
- }
-
- /* get stats */
- snprintf(sql, len, "SELECT count FROM _st_summarystats('%s','%s',%d,%d::boolean,%f)", tablename, colname, bandindex, (exclude_nodata_value ? 1 : 0), sample);
- POSTGIS_RT_DEBUGF(3, "stats sql: %s", sql);
- spi_result = SPI_execute(sql, TRUE, 0);
- pfree(sql);
- if (spi_result != SPI_OK_SELECT || SPI_tuptable == NULL || SPI_processed != 1) {
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_finish();
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_quantileCoverage: Could not get summary stats of coverage");
- SRF_RETURN_DONE(funcctx);
- }
-
- tupdesc = SPI_tuptable->tupdesc;
- tuptable = SPI_tuptable;
- tuple = tuptable->vals[0];
-
- tmp = SPI_getvalue(tuple, tupdesc, 1);
- if (NULL == tmp || !strlen(tmp)) {
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_finish();
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_quantileCoverage: Could not get summary stats of coverage");
- SRF_RETURN_DONE(funcctx);
- }
- cov_count = strtol(tmp, NULL, 10);
- POSTGIS_RT_DEBUGF(3, "covcount = %d", (int) cov_count);
- pfree(tmp);
-
- /* iterate through rasters of coverage */
- /* create sql */
- len = sizeof(char) * (strlen("SELECT \"\" FROM \"\" WHERE \"\" IS NOT NULL") + (strlen(colname) * 2) + strlen(tablename) + 1);
- sql = (char *) palloc(len);
- if (NULL == sql) {
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_finish();
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_quantileCoverage: Could not allocate memory for sql");
- SRF_RETURN_DONE(funcctx);
- }
-
- /* get cursor */
- snprintf(sql, len, "SELECT \"%s\" FROM \"%s\" WHERE \"%s\" IS NOT NULL", colname, tablename, colname);
- POSTGIS_RT_DEBUGF(3, "coverage sql: %s", sql);
- portal = SPI_cursor_open_with_args(
- "coverage",
- sql,
- 0, NULL,
- NULL, NULL,
- TRUE, 0
- );
- pfree(sql);
-
- /* process resultset */
- SPI_cursor_fetch(portal, TRUE, 1);
- while (SPI_processed == 1 && SPI_tuptable != NULL) {
- if (NULL != covquant) pfree(covquant);
-
- tupdesc = SPI_tuptable->tupdesc;
- tuptable = SPI_tuptable;
- tuple = tuptable->vals[0];
-
- datum = SPI_getbinval(tuple, tupdesc, 1, &isNull);
- if (SPI_result == SPI_ERROR_NOATTRIBUTE) {
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_quantileCoverage: Could not get raster of coverage");
- SRF_RETURN_DONE(funcctx);
- }
- else if (isNull) {
- SPI_cursor_fetch(portal, TRUE, 1);
- continue;
- }
-
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM(datum);
-
- raster = rt_raster_deserialize(pgraster, FALSE);
- if (!raster) {
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_quantileCoverage: Could not deserialize raster");
- SRF_RETURN_DONE(funcctx);
- }
-
- /* inspect number of bands*/
- 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);
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- /* get band */
- band = rt_raster_get_band(raster, bandindex - 1);
- if (!band) {
- elog(NOTICE, "Could not find raster band of index %d. Returning NULL", bandindex);
-
- rt_raster_destroy(raster);
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- covquant = rt_band_get_quantiles_stream(
- band,
- exclude_nodata_value, sample, cov_count,
- &qlls, &qlls_count,
- quantiles, quantiles_count,
- &count
- );
-
- rt_band_destroy(band);
- rt_raster_destroy(raster);
-
- if (NULL == covquant || !count) {
- elog(NOTICE, "Could not compute quantiles for band at index %d", bandindex);
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- /* next record */
- SPI_cursor_fetch(portal, TRUE, 1);
- }
-
- covquant2 = SPI_palloc(sizeof(struct rt_quantile_t) * count);
- for (i = 0; i < count; i++) {
- covquant2[i].quantile = covquant[i].quantile;
- covquant2[i].has_value = covquant[i].has_value;
- if (covquant2[i].has_value)
- covquant2[i].value = covquant[i].value;
- }
-
- if (NULL != covquant) pfree(covquant);
- quantile_llist_destroy(&qlls, qlls_count);
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- if (quantiles_count) pfree(quantiles);
-
- POSTGIS_RT_DEBUGF(3, "%d quantiles returned", count);
-
- /* Store needed information */
- funcctx->user_fctx = covquant2;
-
- /* 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;
- covquant2 = funcctx->user_fctx;
-
- /* do when there is more left to send */
- if (call_cntr < max_calls) {
- int values_length = 2;
- Datum values[values_length];
- bool nulls[values_length];
- HeapTuple tuple;
- Datum result;
-
- POSTGIS_RT_DEBUGF(3, "Result %d", call_cntr);
-
- memset(nulls, FALSE, sizeof(bool) * values_length);
-
- values[0] = Float8GetDatum(covquant2[call_cntr].quantile);
- if (covquant2[call_cntr].has_value)
- values[1] = Float8GetDatum(covquant2[call_cntr].value);
- else
- nulls[1] = TRUE;
-
- /* build a tuple */
- tuple = heap_form_tuple(tupdesc, values, nulls);
-
- /* make the tuple into a datum */
- result = HeapTupleGetDatum(tuple);
-
- SRF_RETURN_NEXT(funcctx, result);
- }
- /* do when there is no more left */
- else {
- POSTGIS_RT_DEBUG(3, "done");
- pfree(covquant2);
- SRF_RETURN_DONE(funcctx);
- }
-}
-
-/* get counts of values */
-PG_FUNCTION_INFO_V1(RASTER_valueCount);
-Datum RASTER_valueCount(PG_FUNCTION_ARGS) {
- FuncCallContext *funcctx;
- TupleDesc tupdesc;
-
- int i;
- rt_valuecount vcnts;
- rt_valuecount vcnts2;
- int call_cntr;
- int max_calls;
-
- /* first call of function */
- if (SRF_IS_FIRSTCALL()) {
- MemoryContext oldcontext;
-
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- rt_band band = NULL;
- int32_t bandindex = 0;
- int num_bands = 0;
- bool exclude_nodata_value = TRUE;
- double *search_values = NULL;
- uint32_t search_values_count = 0;
- double roundto = 0;
- uint32_t count;
-
- int j;
- int n;
-
- 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);
-
- /* pgraster is null, return nothing */
- 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_valueCount: Could not deserialize raster");
- SRF_RETURN_DONE(funcctx);
- }
-
- /* band index is 1-based */
- 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);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- /* exclude_nodata_value flag */
- if (!PG_ARGISNULL(2))
- exclude_nodata_value = PG_GETARG_BOOL(2);
-
- /* search values */
- if (!PG_ARGISNULL(3)) {
- array = PG_GETARG_ARRAYTYPE_P(3);
- 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_valueCount: Invalid data type for values");
- SRF_RETURN_DONE(funcctx);
- break;
- }
-
- deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
- &nulls, &n);
-
- search_values = palloc(sizeof(double) * n);
- for (i = 0, j = 0; i < n; i++) {
- if (nulls[i]) continue;
-
- switch (etype) {
- case FLOAT4OID:
- search_values[j] = (double) DatumGetFloat4(e[i]);
- break;
- case FLOAT8OID:
- search_values[j] = (double) DatumGetFloat8(e[i]);
- break;
- }
-
- POSTGIS_RT_DEBUGF(5, "search_values[%d] = %f", j, search_values[j]);
- j++;
- }
- search_values_count = j;
-
- if (j < 1) {
- pfree(search_values);
- search_values = NULL;
- }
- }
-
- /* roundto */
- if (!PG_ARGISNULL(4)) {
- roundto = PG_GETARG_FLOAT8(4);
- if (roundto < 0.) roundto = 0;
- }
-
- /* 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);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- /* get counts of values */
- vcnts = rt_band_get_value_count(band, (int) exclude_nodata_value, search_values, search_values_count, roundto, NULL, &count);
- rt_band_destroy(band);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- if (NULL == vcnts || !count) {
- elog(NOTICE, "Could not count the values for band at index %d", bandindex);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- POSTGIS_RT_DEBUGF(3, "%d value counts returned", count);
-
- /* Store needed information */
- funcctx->user_fctx = vcnts;
-
- /* 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;
- vcnts2 = funcctx->user_fctx;
-
- /* do when there is more left to send */
- if (call_cntr < max_calls) {
- int values_length = 3;
- Datum values[values_length];
- bool nulls[values_length];
- HeapTuple tuple;
- Datum result;
-
- POSTGIS_RT_DEBUGF(3, "Result %d", call_cntr);
-
- memset(nulls, FALSE, sizeof(bool) * values_length);
-
- values[0] = Float8GetDatum(vcnts2[call_cntr].value);
- values[1] = UInt32GetDatum(vcnts2[call_cntr].count);
- values[2] = Float8GetDatum(vcnts2[call_cntr].percent);
-
- /* build a tuple */
- tuple = heap_form_tuple(tupdesc, values, nulls);
-
- /* make the tuple into a datum */
- result = HeapTupleGetDatum(tuple);
-
- SRF_RETURN_NEXT(funcctx, result);
- }
- /* do when there is no more left */
- else {
- pfree(vcnts2);
- SRF_RETURN_DONE(funcctx);
- }
-}
-
-/* get counts of values for a coverage */
-PG_FUNCTION_INFO_V1(RASTER_valueCountCoverage);
-Datum RASTER_valueCountCoverage(PG_FUNCTION_ARGS) {
- FuncCallContext *funcctx;
- TupleDesc tupdesc;
-
- int i;
- uint64_t covcount = 0;
- uint64_t covtotal = 0;
- rt_valuecount covvcnts = NULL;
- rt_valuecount covvcnts2;
- int call_cntr;
- int max_calls;
-
- POSTGIS_RT_DEBUG(3, "RASTER_valueCountCoverage: Starting");
-
- /* first call of function */
- if (SRF_IS_FIRSTCALL()) {
- MemoryContext oldcontext;
-
- text *tablenametext = NULL;
- char *tablename = NULL;
- text *colnametext = NULL;
- char *colname = NULL;
- int32_t bandindex = 1;
- bool exclude_nodata_value = TRUE;
- double *search_values = NULL;
- uint32_t search_values_count = 0;
- double roundto = 0;
-
- int len = 0;
- char *sql = NULL;
- int spi_result;
- Portal portal;
- SPITupleTable *tuptable = NULL;
- HeapTuple tuple;
- Datum datum;
- bool isNull = FALSE;
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- rt_band band = NULL;
- int num_bands = 0;
- uint32_t count;
- uint32_t total;
- rt_valuecount vcnts = NULL;
- int exists = 0;
-
- int j;
- int n;
-
- 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);
-
- /* tablename is null, return null */
- if (PG_ARGISNULL(0)) {
- elog(NOTICE, "Table name must be provided");
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- tablenametext = PG_GETARG_TEXT_P(0);
- tablename = text_to_cstring(tablenametext);
- if (!strlen(tablename)) {
- elog(NOTICE, "Table name must be provided");
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- POSTGIS_RT_DEBUGF(3, "tablename = %s", tablename);
-
- /* column name is null, return null */
- if (PG_ARGISNULL(1)) {
- elog(NOTICE, "Column name must be provided");
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- colnametext = PG_GETARG_TEXT_P(1);
- colname = text_to_cstring(colnametext);
- if (!strlen(colname)) {
- elog(NOTICE, "Column name must be provided");
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- POSTGIS_RT_DEBUGF(3, "colname = %s", colname);
-
- /* band index is 1-based */
- if (!PG_ARGISNULL(2))
- bandindex = PG_GETARG_INT32(2);
-
- /* exclude_nodata_value flag */
- if (!PG_ARGISNULL(3))
- exclude_nodata_value = PG_GETARG_BOOL(3);
-
- /* search values */
- if (!PG_ARGISNULL(4)) {
- array = PG_GETARG_ARRAYTYPE_P(4);
- etype = ARR_ELEMTYPE(array);
- get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
-
- switch (etype) {
- case FLOAT4OID:
- case FLOAT8OID:
- break;
- default:
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_valueCountCoverage: Invalid data type for values");
- SRF_RETURN_DONE(funcctx);
- break;
- }
-
- deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
- &nulls, &n);
-
- search_values = palloc(sizeof(double) * n);
- for (i = 0, j = 0; i < n; i++) {
- if (nulls[i]) continue;
-
- switch (etype) {
- case FLOAT4OID:
- search_values[j] = (double) DatumGetFloat4(e[i]);
- break;
- case FLOAT8OID:
- search_values[j] = (double) DatumGetFloat8(e[i]);
- break;
- }
-
- POSTGIS_RT_DEBUGF(5, "search_values[%d] = %f", j, search_values[j]);
- j++;
- }
- search_values_count = j;
-
- if (j < 1) {
- pfree(search_values);
- search_values = NULL;
- }
- }
-
- /* roundto */
- if (!PG_ARGISNULL(5)) {
- roundto = PG_GETARG_FLOAT8(5);
- if (roundto < 0.) roundto = 0;
- }
-
- /* iterate through rasters of coverage */
- /* connect to database */
- spi_result = SPI_connect();
- if (spi_result != SPI_OK_CONNECT) {
-
- if (search_values_count) pfree(search_values);
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_valueCountCoverage: Could not connect to database using SPI");
- SRF_RETURN_DONE(funcctx);
- }
-
- /* create sql */
- len = sizeof(char) * (strlen("SELECT \"\" FROM \"\" WHERE \"\" IS NOT NULL") + (strlen(colname) * 2) + strlen(tablename) + 1);
- sql = (char *) palloc(len);
- if (NULL == sql) {
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_finish();
-
- if (search_values_count) pfree(search_values);
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_valueCountCoverage: Could not allocate memory for sql");
- SRF_RETURN_DONE(funcctx);
- }
-
- /* get cursor */
- snprintf(sql, len, "SELECT \"%s\" FROM \"%s\" WHERE \"%s\" IS NOT NULL", colname, tablename, colname);
- POSTGIS_RT_DEBUGF(3, "RASTER_valueCountCoverage: %s", sql);
- portal = SPI_cursor_open_with_args(
- "coverage",
- sql,
- 0, NULL,
- NULL, NULL,
- TRUE, 0
- );
- pfree(sql);
-
- /* process resultset */
- SPI_cursor_fetch(portal, TRUE, 1);
- while (SPI_processed == 1 && SPI_tuptable != NULL) {
- tupdesc = SPI_tuptable->tupdesc;
- tuptable = SPI_tuptable;
- tuple = tuptable->vals[0];
-
- datum = SPI_getbinval(tuple, tupdesc, 1, &isNull);
- if (SPI_result == SPI_ERROR_NOATTRIBUTE) {
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- if (NULL != covvcnts) pfree(covvcnts);
- if (search_values_count) pfree(search_values);
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_valueCountCoverage: Could not get raster of coverage");
- SRF_RETURN_DONE(funcctx);
- }
- else if (isNull) {
- SPI_cursor_fetch(portal, TRUE, 1);
- continue;
- }
-
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM(datum);
-
- raster = rt_raster_deserialize(pgraster, FALSE);
- if (!raster) {
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- if (NULL != covvcnts) pfree(covvcnts);
- if (search_values_count) pfree(search_values);
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_valueCountCoverage: Could not deserialize raster");
- SRF_RETURN_DONE(funcctx);
- }
-
- /* inspect number of bands*/
- 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);
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- if (NULL != covvcnts) pfree(covvcnts);
- if (search_values_count) pfree(search_values);
-
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- /* 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);
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- if (NULL != covvcnts) pfree(covvcnts);
- if (search_values_count) pfree(search_values);
-
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- /* get counts of values */
- vcnts = rt_band_get_value_count(band, (int) exclude_nodata_value, search_values, search_values_count, roundto, &total, &count);
- rt_band_destroy(band);
- rt_raster_destroy(raster);
- if (NULL == vcnts || !count) {
- elog(NOTICE, "Could not count the values for band at index %d", bandindex);
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- if (NULL != covvcnts) free(covvcnts);
- if (search_values_count) pfree(search_values);
-
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- POSTGIS_RT_DEBUGF(3, "%d value counts returned", count);
-
- if (NULL == covvcnts) {
- covvcnts = (rt_valuecount) SPI_palloc(sizeof(struct rt_valuecount_t) * count);
- if (NULL == covvcnts) {
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- if (search_values_count) pfree(search_values);
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_valueCountCoverage: Could not allocate memory for value counts of coverage");
- SRF_RETURN_DONE(funcctx);
- }
-
- for (i = 0; i < count; i++) {
- covvcnts[i].value = vcnts[i].value;
- covvcnts[i].count = vcnts[i].count;
- covvcnts[i].percent = -1;
- }
-
- covcount = count;
- }
- else {
- for (i = 0; i < count; i++) {
- exists = 0;
-
- for (j = 0; j < covcount; j++) {
- if (FLT_EQ(vcnts[i].value, covvcnts[j].value)) {
- exists = 1;
- break;
- }
- }
-
- if (exists) {
- covvcnts[j].count += vcnts[i].count;
- }
- else {
- covcount++;
- covvcnts = SPI_repalloc(covvcnts, sizeof(struct rt_valuecount_t) * covcount);
- if (NULL == covvcnts) {
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- if (search_values_count) pfree(search_values);
- if (NULL != covvcnts) free(covvcnts);
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_valueCountCoverage: Could not change allocated memory for value counts of coverage");
- SRF_RETURN_DONE(funcctx);
- }
-
- covvcnts[covcount - 1].value = vcnts[i].value;
- covvcnts[covcount - 1].count = vcnts[i].count;
- covvcnts[covcount - 1].percent = -1;
- }
- }
- }
-
- covtotal += total;
-
- pfree(vcnts);
-
- /* next record */
- SPI_cursor_fetch(portal, TRUE, 1);
- }
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- SPI_cursor_close(portal);
- SPI_finish();
-
- if (search_values_count) pfree(search_values);
-
- /* compute percentages */
- for (i = 0; i < covcount; i++) {
- covvcnts[i].percent = (double) covvcnts[i].count / covtotal;
- }
-
- /* Store needed information */
- funcctx->user_fctx = covvcnts;
-
- /* total number of tuples to be returned */
- funcctx->max_calls = covcount;
-
- /* 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;
- covvcnts2 = funcctx->user_fctx;
-
- /* do when there is more left to send */
- if (call_cntr < max_calls) {
- int values_length = 3;
- Datum values[values_length];
- bool nulls[values_length];
- HeapTuple tuple;
- Datum result;
-
- POSTGIS_RT_DEBUGF(3, "Result %d", call_cntr);
-
- memset(nulls, FALSE, sizeof(bool) * values_length);
-
- values[0] = Float8GetDatum(covvcnts2[call_cntr].value);
- values[1] = UInt32GetDatum(covvcnts2[call_cntr].count);
- values[2] = Float8GetDatum(covvcnts2[call_cntr].percent);
-
- /* build a tuple */
- tuple = heap_form_tuple(tupdesc, values, nulls);
-
- /* make the tuple into a datum */
- result = HeapTupleGetDatum(tuple);
-
- SRF_RETURN_NEXT(funcctx, result);
- }
- /* do when there is no more left */
- else {
- pfree(covvcnts2);
- SRF_RETURN_DONE(funcctx);
- }
-}
-
-/**
- * Reclassify the specified bands of the raster
- */
-PG_FUNCTION_INFO_V1(RASTER_reclass);
-Datum RASTER_reclass(PG_FUNCTION_ARGS) {
- rt_pgraster *pgraster = NULL;
- rt_pgraster *pgrtn = NULL;
- rt_raster raster = NULL;
- rt_band band = NULL;
- rt_band newband = NULL;
- uint32_t numBands = 0;
-
- ArrayType *array;
- Oid etype;
- Datum *e;
- bool *nulls;
- int16 typlen;
- bool typbyval;
- char typalign;
- int n = 0;
-
- int i = 0;
- int j = 0;
- int k = 0;
-
- int a = 0;
- int b = 0;
- int c = 0;
-
- rt_reclassexpr *exprset = NULL;
- HeapTupleHeader tup;
- bool isnull;
- Datum tupv;
- uint32_t nband = 0;
- char *expr = NULL;
- text *exprtext = NULL;
- double val = 0;
- char *junk = NULL;
- int inc_val = 0;
- int exc_val = 0;
- char *pixeltype = NULL;
- text *pixeltypetext = NULL;
- rt_pixtype pixtype = PT_END;
- double nodataval = 0;
- bool hasnodata = FALSE;
-
- char **comma_set = NULL;
- int comma_n = 0;
- char **colon_set = NULL;
- int colon_n = 0;
- char **dash_set = NULL;
- int dash_n = 0;
-
- POSTGIS_RT_DEBUG(3, "RASTER_reclass: Starting");
-
- /* pgraster is null, return null */
- if (PG_ARGISNULL(0))
- PG_RETURN_NULL();
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
-
- /* raster */
- raster = rt_raster_deserialize(pgraster, FALSE);
- if (!raster) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_reclass: Could not deserialize raster");
- PG_RETURN_NULL();
- }
- numBands = rt_raster_get_num_bands(raster);
- POSTGIS_RT_DEBUGF(3, "RASTER_reclass: %d possible bands to be reclassified", n);
-
- /* process set of reclassarg */
- POSTGIS_RT_DEBUG(3, "RASTER_reclass: Processing Arg 1 (reclassargset)");
- array = PG_GETARG_ARRAYTYPE_P(1);
- etype = ARR_ELEMTYPE(array);
- get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
-
- deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
- &nulls, &n);
-
- if (!n) {
- elog(NOTICE, "Invalid argument for reclassargset. Returning original 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);
- }
-
- /*
- process each element of reclassarg
- each element is the index of the band to process, the set
- of reclass ranges and the output band's pixeltype
- */
- for (i = 0; i < n; i++) {
- if (nulls[i]) continue;
-
- /* each element is a tuple */
- tup = (HeapTupleHeader) DatumGetPointer(e[i]);
- if (NULL == tup) {
- elog(NOTICE, "Invalid argument for reclassargset. Returning original 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);
- }
-
- /* band index (1-based) */
- tupv = GetAttributeByName(tup, "nband", &isnull);
- if (isnull) {
- elog(NOTICE, "Invalid argument for reclassargset. Missing value of nband for reclassarg of index %d . Returning original raster", i);
-
- 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);
- }
- nband = DatumGetInt32(tupv);
- POSTGIS_RT_DEBUGF(3, "RASTER_reclass: expression for band %d", nband);
-
- /* valid band index? */
- if (nband < 1 || nband > numBands) {
- elog(NOTICE, "Invalid argument for reclassargset. Invalid band index (must use 1-based) for reclassarg of index %d . Returning original raster", i);
-
- 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);
- }
-
- /* reclass expr */
- tupv = GetAttributeByName(tup, "reclassexpr", &isnull);
- if (isnull) {
- elog(NOTICE, "Invalid argument for reclassargset. Missing value of reclassexpr for reclassarg of index %d . Returning original raster", i);
-
- 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);
- }
- exprtext = (text *) DatumGetPointer(tupv);
- if (NULL == exprtext) {
- elog(NOTICE, "Invalid argument for reclassargset. Missing value of reclassexpr for reclassarg of index %d . Returning original raster", i);
-
- 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);
- }
- expr = text_to_cstring(exprtext);
- POSTGIS_RT_DEBUGF(5, "RASTER_reclass: expr (raw) %s", expr);
- expr = rtpg_removespaces(expr);
- POSTGIS_RT_DEBUGF(5, "RASTER_reclass: expr (clean) %s", expr);
-
- /* split string to its components */
- /* comma (,) separating rangesets */
- comma_set = rtpg_strsplit(expr, ",", &comma_n);
- if (comma_n < 1) {
- elog(NOTICE, "Invalid argument for reclassargset. Invalid expression of reclassexpr for reclassarg of index %d . Returning original raster", i);
-
- 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 of reclass expressions */
- POSTGIS_RT_DEBUGF(5, "RASTER_reclass: %d possible expressions", comma_n);
- exprset = palloc(comma_n * sizeof(rt_reclassexpr));
-
- for (a = 0, j = 0; a < comma_n; a++) {
- POSTGIS_RT_DEBUGF(5, "RASTER_reclass: map %s", comma_set[a]);
-
- /* colon (:) separating range "src" and "dst" */
- colon_set = rtpg_strsplit(comma_set[a], ":", &colon_n);
- if (colon_n != 2) {
- elog(NOTICE, "Invalid argument for reclassargset. Invalid expression of reclassexpr for reclassarg of index %d . Returning original raster", i);
- for (k = 0; k < j; k++) pfree(exprset[k]);
- pfree(exprset);
-
- 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);
- }
-
- /* allocate mem for reclass expression */
- exprset[j] = palloc(sizeof(struct rt_reclassexpr_t));
-
- for (b = 0; b < colon_n; b++) {
- POSTGIS_RT_DEBUGF(5, "RASTER_reclass: range %s", colon_set[b]);
-
- /* dash (-) separating "min" and "max" */
- dash_set = rtpg_strsplit(colon_set[b], "-", &dash_n);
- if (dash_n < 1 || dash_n > 3) {
- elog(NOTICE, "Invalid argument for reclassargset. Invalid expression of reclassexpr for reclassarg of index %d . Returning original raster", i);
- for (k = 0; k < j; k++) pfree(exprset[k]);
- pfree(exprset);
-
- 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);
- }
-
- for (c = 0; c < dash_n; c++) {
- /* need to handle: (-9999-100 -> "(", "9999", "100" */
- if (
- c < 1 &&
- strlen(dash_set[c]) == 1 && (
- strchr(dash_set[c], '(') != NULL ||
- strchr(dash_set[c], '[') != NULL ||
- strchr(dash_set[c], ')') != NULL ||
- strchr(dash_set[c], ']') != NULL
- )
- ) {
- junk = palloc(sizeof(char) * (strlen(dash_set[c + 1]) + 2));
- if (NULL == junk) {
- for (k = 0; k <= j; k++) pfree(exprset[k]);
- pfree(exprset);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- elog(ERROR, "RASTER_reclass: Could not allocate memory");
- PG_RETURN_NULL();
- }
-
- sprintf(junk, "%s%s", dash_set[c], dash_set[c + 1]);
- c++;
- dash_set[c] = repalloc(dash_set[c], sizeof(char) * (strlen(junk) + 1));
- strcpy(dash_set[c], junk);
- pfree(junk);
-
- /* rebuild dash_set */
- for (k = 1; k < dash_n; k++) {
- dash_set[k - 1] = repalloc(dash_set[k - 1], (strlen(dash_set[k]) + 1) * sizeof(char));
- strcpy(dash_set[k - 1], dash_set[k]);
- }
- dash_n--;
- c--;
- pfree(dash_set[dash_n]);
- dash_set = repalloc(dash_set, sizeof(char *) * dash_n);
- }
-
- /* there shouldn't be more than two in dash_n */
- if (c < 1 && dash_n > 2) {
- elog(NOTICE, "Invalid argument for reclassargset. Invalid expression of reclassexpr for reclassarg of index %d . Returning original raster", i);
- for (k = 0; k < j; k++) pfree(exprset[k]);
- pfree(exprset);
-
- 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);
- }
-
- /* check interval flags */
- exc_val = 0;
- inc_val = 1;
- /* range */
- if (dash_n != 1) {
- /* min */
- if (c < 1) {
- if (
- strchr(dash_set[c], ')') != NULL ||
- strchr(dash_set[c], ']') != NULL
- ) {
- exc_val = 1;
- inc_val = 1;
- }
- else if (strchr(dash_set[c], '(') != NULL){
- inc_val = 0;
- }
- else {
- inc_val = 1;
- }
- }
- /* max */
- else {
- if (
- strrchr(dash_set[c], '(') != NULL ||
- strrchr(dash_set[c], '[') != NULL
- ) {
- exc_val = 1;
- inc_val = 0;
- }
- else if (strrchr(dash_set[c], ']') != NULL) {
- inc_val = 1;
- }
- else {
- inc_val = 0;
- }
- }
- }
- POSTGIS_RT_DEBUGF(5, "RASTER_reclass: exc_val %d inc_val %d", exc_val, inc_val);
-
- /* remove interval flags */
- dash_set[c] = rtpg_chartrim(dash_set[c], "()[]");
- POSTGIS_RT_DEBUGF(5, "RASTER_reclass: min/max (char) %s", dash_set[c]);
-
- /* value from string to double */
- errno = 0;
- val = strtod(dash_set[c], &junk);
- if (errno != 0 || dash_set[c] == junk) {
- elog(NOTICE, "Invalid argument for reclassargset. Invalid expression of reclassexpr for reclassarg of index %d . Returning original raster", i);
- for (k = 0; k < j; k++) pfree(exprset[k]);
- pfree(exprset);
-
- 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);
- }
- POSTGIS_RT_DEBUGF(5, "RASTER_reclass: min/max (double) %f", val);
-
- /* strsplit removes dash (a.k.a. negative sign), compare now to restore */
- junk = strstr(colon_set[b], dash_set[c]);
- /* not beginning of string */
- if (junk != colon_set[b]) {
- /* prior is a dash */
- if (*(junk - 1) == '-') {
- /* prior is beginning of string or prior - 1 char is dash, negative number */
- if (
- ((junk - 1) == colon_set[b]) ||
- (*(junk - 2) == '-') ||
- (*(junk - 2) == '[') ||
- (*(junk - 2) == '(')
- ) {
- val *= -1.;
- }
- }
- }
- POSTGIS_RT_DEBUGF(5, "RASTER_reclass: min/max (double) %f", val);
-
- /* src */
- if (b < 1) {
- /* singular value */
- if (dash_n == 1) {
- exprset[j]->src.exc_min = exprset[j]->src.exc_max = exc_val;
- exprset[j]->src.inc_min = exprset[j]->src.inc_max = inc_val;
- exprset[j]->src.min = exprset[j]->src.max = val;
- }
- /* min */
- else if (c < 1) {
- exprset[j]->src.exc_min = exc_val;
- exprset[j]->src.inc_min = inc_val;
- exprset[j]->src.min = val;
- }
- /* max */
- else {
- exprset[j]->src.exc_max = exc_val;
- exprset[j]->src.inc_max = inc_val;
- exprset[j]->src.max = val;
- }
- }
- /* dst */
- else {
- /* singular value */
- if (dash_n == 1)
- exprset[j]->dst.min = exprset[j]->dst.max = val;
- /* min */
- else if (c < 1)
- exprset[j]->dst.min = val;
- /* max */
- else
- exprset[j]->dst.max = val;
- }
- }
- pfree(dash_set);
- }
- pfree(colon_set);
-
- POSTGIS_RT_DEBUGF(3, "RASTER_reclass: or: %f - %f nr: %f - %f"
- , exprset[j]->src.min
- , exprset[j]->src.max
- , exprset[j]->dst.min
- , exprset[j]->dst.max
- );
- j++;
- }
- pfree(comma_set);
-
- /* pixel type */
- tupv = GetAttributeByName(tup, "pixeltype", &isnull);
- if (isnull) {
- elog(NOTICE, "Invalid argument for reclassargset. Missing value of pixeltype for reclassarg of index %d . Returning original raster", i);
-
- 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);
- }
- pixeltypetext = (text *) DatumGetPointer(tupv);
- if (NULL == pixeltypetext) {
- elog(NOTICE, "Invalid argument for reclassargset. Missing value of pixeltype for reclassarg of index %d . Returning original raster", i);
-
- 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);
- }
- pixeltype = text_to_cstring(pixeltypetext);
- POSTGIS_RT_DEBUGF(3, "RASTER_reclass: pixeltype %s", pixeltype);
- pixtype = rt_pixtype_index_from_name(pixeltype);
-
- /* nodata */
- tupv = GetAttributeByName(tup, "nodataval", &isnull);
- if (isnull) {
- nodataval = 0;
- hasnodata = FALSE;
- }
- else {
- nodataval = DatumGetFloat8(tupv);
- hasnodata = TRUE;
- }
- POSTGIS_RT_DEBUGF(3, "RASTER_reclass: nodataval %f", nodataval);
- POSTGIS_RT_DEBUGF(3, "RASTER_reclass: hasnodata %d", hasnodata);
-
- /* do reclass */
- band = rt_raster_get_band(raster, nband - 1);
- if (!band) {
- elog(NOTICE, "Could not find raster band of index %d. Returning original raster", nband);
- for (k = 0; k < j; k++) pfree(exprset[k]);
- pfree(exprset);
-
- 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);
- }
- newband = rt_band_reclass(band, pixtype, hasnodata, nodataval, exprset, j);
- if (!newband) {
- for (k = 0; k < j; k++) pfree(exprset[k]);
- pfree(exprset);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- elog(ERROR, "RASTER_reclass: Could not reclassify raster band of index %d", nband);
- PG_RETURN_NULL();
- }
-
- /* replace old band with new band */
- if (rt_raster_replace_band(raster, newband, nband - 1) == NULL) {
- for (k = 0; k < j; k++) pfree(exprset[k]);
- pfree(exprset);
-
- rt_band_destroy(newband);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- elog(ERROR, "RASTER_reclass: Could not replace raster band of index %d with reclassified band", nband);
- PG_RETURN_NULL();
- }
-
- /* old band is in the variable band */
- rt_band_destroy(band);
-
- /* free exprset */
- for (k = 0; k < j; k++) pfree(exprset[k]);
- pfree(exprset);
- }
-
- pgrtn = rt_raster_serialize(raster);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- if (!pgrtn)
- PG_RETURN_NULL();
-
- POSTGIS_RT_DEBUG(3, "RASTER_reclass: Finished");
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
-}
-
-/* ---------------------------------------------------------------- */
-/* apply colormap to specified band of a raster */
-/* ---------------------------------------------------------------- */
-
-typedef struct rtpg_colormap_arg_t *rtpg_colormap_arg;
-struct rtpg_colormap_arg_t {
- rt_raster raster;
- int nband; /* 1-based */
- rt_band band;
- rt_bandstats bandstats;
-
- rt_colormap colormap;
- int nodataentry;
-
- char **entry;
- int nentry;
- char **element;
- int nelement;
-};
-
-static rtpg_colormap_arg
-rtpg_colormap_arg_init() {
- rtpg_colormap_arg arg = NULL;
-
- arg = palloc(sizeof(struct rtpg_colormap_arg_t));
- if (arg == NULL) {
- elog(ERROR, "rtpg_colormap_arg: Could not allocate memory for function arguments");
- return NULL;
- }
-
- arg->raster = NULL;
- arg->nband = 1;
- arg->band = NULL;
- arg->bandstats = NULL;
-
- arg->colormap = palloc(sizeof(struct rt_colormap_t));
- if (arg->colormap == NULL) {
- elog(ERROR, "rtpg_colormap_arg: Could not allocate memory for function arguments");
- return NULL;
- }
- arg->colormap->nentry = 0;
- arg->colormap->entry = NULL;
- arg->colormap->ncolor = 4; /* assume RGBA */
- arg->colormap->method = CM_INTERPOLATE;
- arg->nodataentry = -1;
-
- arg->entry = NULL;
- arg->nentry = 0;
- arg->element = NULL;
- arg->nelement = 0;
-
- return arg;
-}
-
-static void
-rtpg_colormap_arg_destroy(rtpg_colormap_arg arg) {
- int i = 0;
- if (arg->raster != NULL)
- rt_raster_destroy(arg->raster);
-
- if (arg->bandstats != NULL)
- pfree(arg->bandstats);
-
- if (arg->colormap != NULL) {
- if (arg->colormap->entry != NULL)
- pfree(arg->colormap->entry);
- pfree(arg->colormap);
- }
-
- if (arg->nentry) {
- for (i = 0; i < arg->nentry; i++) {
- if (arg->entry[i] != NULL)
- pfree(arg->entry[i]);
- }
- pfree(arg->entry);
- }
-
- if (arg->nelement) {
- for (i = 0; i < arg->nelement; i++)
- pfree(arg->element[i]);
- pfree(arg->element);
- }
-
- pfree(arg);
- arg = NULL;
-}
-
-PG_FUNCTION_INFO_V1(RASTER_colorMap);
-Datum RASTER_colorMap(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rtpg_colormap_arg arg = NULL;
- char *junk = NULL;
- rt_raster raster = NULL;
-
- POSTGIS_RT_DEBUG(3, "RASTER_colorMap: Starting");
-
- /* pgraster is NULL, return NULL */
- if (PG_ARGISNULL(0))
- PG_RETURN_NULL();
-
- /* init arg */
- arg = rtpg_colormap_arg_init();
- if (arg == NULL) {
- elog(ERROR, "RASTER_colorMap: Could not initialize argument structure");
- PG_RETURN_NULL();
- }
-
- /* raster (0) */
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
-
- /* raster object */
- arg->raster = rt_raster_deserialize(pgraster, FALSE);
- if (!arg->raster) {
- rtpg_colormap_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_colorMap: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- /* nband (1) */
- if (!PG_ARGISNULL(1))
- arg->nband = PG_GETARG_INT32(1);
- POSTGIS_RT_DEBUGF(4, "nband = %d", arg->nband);
-
- /* check that band works */
- if (!rt_raster_has_band(arg->raster, arg->nband - 1)) {
- elog(NOTICE, "Raster does not have band at index %d. Returning empty raster", arg->nband);
-
- raster = rt_raster_clone(arg->raster, 0);
- if (raster == NULL) {
- rtpg_colormap_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_colorMap: Could not create empty raster");
- PG_RETURN_NULL();
- }
-
- rtpg_colormap_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
-
- pgraster = rt_raster_serialize(raster);
- rt_raster_destroy(raster);
- if (pgraster == NULL)
- PG_RETURN_NULL();
-
- SET_VARSIZE(pgraster, ((rt_pgraster*) pgraster)->size);
- PG_RETURN_POINTER(pgraster);
- }
-
- /* get band */
- arg->band = rt_raster_get_band(arg->raster, arg->nband - 1);
- if (arg->band == NULL) {
- int nband = arg->nband;
- rtpg_colormap_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_colorMap: Could not get band at index %d", nband);
- PG_RETURN_NULL();
- }
-
- /* method (3) */
- if (!PG_ARGISNULL(3)) {
- char *method = NULL;
- char *tmp = text_to_cstring(PG_GETARG_TEXT_P(3));
- POSTGIS_RT_DEBUGF(4, "raw method = %s", tmp);
-
- method = rtpg_trim(tmp);
- pfree(tmp);
- method = rtpg_strtoupper(method);
-
- if (strcmp(method, "INTERPOLATE") == 0)
- arg->colormap->method = CM_INTERPOLATE;
- else if (strcmp(method, "EXACT") == 0)
- arg->colormap->method = CM_EXACT;
- else if (strcmp(method, "NEAREST") == 0)
- arg->colormap->method = CM_NEAREST;
- else {
- elog(NOTICE, "Unknown value provided for method. Defaulting to INTERPOLATE");
- arg->colormap->method = CM_INTERPOLATE;
- }
- }
- /* default to INTERPOLATE */
- else
- arg->colormap->method = CM_INTERPOLATE;
- POSTGIS_RT_DEBUGF(4, "method = %d", arg->colormap->method);
-
- /* colormap (2) */
- if (PG_ARGISNULL(2)) {
- rtpg_colormap_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_colorMap: Value must be provided for colormap");
- PG_RETURN_NULL();
- }
- else {
- char *tmp = NULL;
- char *colormap = text_to_cstring(PG_GETARG_TEXT_P(2));
- char *_entry;
- char *_element;
- int i = 0;
- int j = 0;
-
- POSTGIS_RT_DEBUGF(4, "colormap = %s", colormap);
-
- /* empty string */
- if (!strlen(colormap)) {
- rtpg_colormap_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_colorMap: Value must be provided for colormap");
- PG_RETURN_NULL();
- }
-
- arg->entry = rtpg_strsplit(colormap, "\n", &(arg->nentry));
- pfree(colormap);
- if (arg->nentry < 1) {
- rtpg_colormap_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_colorMap: Could not process the value provided for colormap");
- PG_RETURN_NULL();
- }
-
- /* allocate the max # of colormap entries */
- arg->colormap->entry = palloc(sizeof(struct rt_colormap_entry_t) * arg->nentry);
- if (arg->colormap->entry == NULL) {
- rtpg_colormap_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_colorMap: Could not allocate memory for colormap entries");
- PG_RETURN_NULL();
- }
- memset(arg->colormap->entry, 0, sizeof(struct rt_colormap_entry_t) * arg->nentry);
-
- /* each entry */
- for (i = 0; i < arg->nentry; i++) {
- /* substitute space for other delimiters */
- tmp = rtpg_strreplace(arg->entry[i], ":", " ", NULL);
- _entry = rtpg_strreplace(tmp, ",", " ", NULL);
- pfree(tmp);
- tmp = rtpg_strreplace(_entry, "\t", " ", NULL);
- pfree(_entry);
- _entry = rtpg_trim(tmp);
- pfree(tmp);
-
- POSTGIS_RT_DEBUGF(4, "Processing entry[%d] = %s", i, arg->entry[i]);
- POSTGIS_RT_DEBUGF(4, "Cleaned entry[%d] = %s", i, _entry);
-
- /* empty entry, continue */
- if (!strlen(_entry)) {
- POSTGIS_RT_DEBUGF(3, "Skipping empty entry[%d]", i);
- pfree(_entry);
- continue;
- }
-
- arg->element = rtpg_strsplit(_entry, " ", &(arg->nelement));
- pfree(_entry);
- if (arg->nelement < 2) {
- rtpg_colormap_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_colorMap: Could not process colormap entry %d", i + 1);
- PG_RETURN_NULL();
- }
- else if (arg->nelement > 5) {
- elog(NOTICE, "More than five elements in colormap entry %d. Using at most five elements", i + 1);
- arg->nelement = 5;
- }
-
- /* smallest # of colors */
- if ((arg->nelement - 1) < arg->colormap->ncolor)
- arg->colormap->ncolor = arg->nelement - 1;
-
- /* each element of entry */
- for (j = 0; j < arg->nelement; j++) {
-
- _element = rtpg_trim(arg->element[j]);
- _element = rtpg_strtoupper(_element);
- POSTGIS_RT_DEBUGF(4, "Processing entry[%d][%d] = %s", i, j, arg->element[j]);
- POSTGIS_RT_DEBUGF(4, "Cleaned entry[%d][%d] = %s", i, j, _element);
-
- /* first element is ALWAYS a band value, percentage OR "nv" string */
- if (j == 0) {
- char *percent = NULL;
-
- /* NODATA */
- if (
- strcmp(_element, "NV") == 0 ||
- strcmp(_element, "NULL") == 0 ||
- strcmp(_element, "NODATA") == 0
- ) {
- POSTGIS_RT_DEBUG(4, "Processing NODATA string");
-
- if (arg->nodataentry > -1) {
- elog(NOTICE, "More than one NODATA entry found. Using only the first one");
- }
- else {
- arg->colormap->entry[arg->colormap->nentry].isnodata = 1;
- /* no need to set value as value comes from band's NODATA */
- arg->colormap->entry[arg->colormap->nentry].value = 0;
- }
- }
- /* percent value */
- else if ((percent = strchr(_element, '%')) != NULL) {
- double value;
- POSTGIS_RT_DEBUG(4, "Processing percent string");
-
- /* get the band stats */
- if (arg->bandstats == NULL) {
- POSTGIS_RT_DEBUG(4, "Getting band stats");
-
- arg->bandstats = rt_band_get_summary_stats(arg->band, 1, 1, 0, NULL, NULL, NULL);
- if (arg->bandstats == NULL) {
- pfree(_element);
- rtpg_colormap_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_colorMap: Could not get band's summary stats to process percentages");
- PG_RETURN_NULL();
- }
- }
-
- /* get the string before the percent char */
- tmp = palloc(sizeof(char) * (percent - _element + 1));
- if (tmp == NULL) {
- pfree(_element);
- rtpg_colormap_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_colorMap: Could not allocate memory for value of percentage");
- PG_RETURN_NULL();
- }
-
- memcpy(tmp, _element, percent - _element);
- tmp[percent - _element] = '\0';
- POSTGIS_RT_DEBUGF(4, "Percent value = %s", tmp);
-
- /* get percentage value */
- errno = 0;
- value = strtod(tmp, NULL);
- pfree(tmp);
- if (errno != 0 || _element == junk) {
- pfree(_element);
- rtpg_colormap_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_colorMap: Could not process percent string to value");
- PG_RETURN_NULL();
- }
-
- /* check percentage */
- if (value < 0.) {
- elog(NOTICE, "Percentage values cannot be less than zero. Defaulting to zero");
- value = 0.;
- }
- else if (value > 100.) {
- elog(NOTICE, "Percentage values cannot be greater than 100. Defaulting to 100");
- value = 100.;
- }
-
- /* get the true pixel value */
- /* TODO: should the percentage be quantile based? */
- arg->colormap->entry[arg->colormap->nentry].value = ((value / 100.) * (arg->bandstats->max - arg->bandstats->min)) + arg->bandstats->min;
- }
- /* straight value */
- else {
- errno = 0;
- arg->colormap->entry[arg->colormap->nentry].value = strtod(_element, &junk);
- if (errno != 0 || _element == junk) {
- pfree(_element);
- rtpg_colormap_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_colorMap: Could not process string to value");
- PG_RETURN_NULL();
- }
- }
-
- }
- /* RGB values (0 - 255) */
- else {
- int value = 0;
-
- errno = 0;
- value = (int) strtod(_element, &junk);
- if (errno != 0 || _element == junk) {
- pfree(_element);
- rtpg_colormap_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_colorMap: Could not process string to value");
- PG_RETURN_NULL();
- }
-
- if (value > 255) {
- elog(NOTICE, "RGBA value cannot be greater than 255. Defaulting to 255");
- value = 255;
- }
- else if (value < 0) {
- elog(NOTICE, "RGBA value cannot be less than zero. Defaulting to zero");
- value = 0;
- }
- arg->colormap->entry[arg->colormap->nentry].color[j - 1] = value;
- }
-
- pfree(_element);
- }
-
- POSTGIS_RT_DEBUGF(4, "colormap->entry[%d] (isnodata, value, R, G, B, A) = (%d, %f, %d, %d, %d, %d)",
- arg->colormap->nentry,
- arg->colormap->entry[arg->colormap->nentry].isnodata,
- arg->colormap->entry[arg->colormap->nentry].value,
- arg->colormap->entry[arg->colormap->nentry].color[0],
- arg->colormap->entry[arg->colormap->nentry].color[1],
- arg->colormap->entry[arg->colormap->nentry].color[2],
- arg->colormap->entry[arg->colormap->nentry].color[3]
- );
-
- arg->colormap->nentry++;
- }
-
- POSTGIS_RT_DEBUGF(4, "colormap->nentry = %d", arg->colormap->nentry);
- POSTGIS_RT_DEBUGF(4, "colormap->ncolor = %d", arg->colormap->ncolor);
- }
-
- /* call colormap */
- raster = rt_raster_colormap(arg->raster, arg->nband - 1, arg->colormap);
- if (raster == NULL) {
- rtpg_colormap_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_colorMap: Could not create new raster with applied colormap");
- PG_RETURN_NULL();
- }
-
- rtpg_colormap_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- pgraster = rt_raster_serialize(raster);
- rt_raster_destroy(raster);
-
- POSTGIS_RT_DEBUG(3, "RASTER_colorMap: Done");
-
- if (pgraster == NULL)
- PG_RETURN_NULL();
-
- SET_VARSIZE(pgraster, ((rt_pgraster*) pgraster)->size);
- PG_RETURN_POINTER(pgraster);
-}
-
-/* ---------------------------------------------------------------- */
-/* Returns raster from GDAL raster */
-/* ---------------------------------------------------------------- */
-PG_FUNCTION_INFO_V1(RASTER_fromGDALRaster);
-Datum RASTER_fromGDALRaster(PG_FUNCTION_ARGS)
-{
- bytea *bytea_data;
- uint8_t *data;
- int data_len = 0;
- VSILFILE *vsifp = NULL;
- GDALDatasetH hdsSrc;
- int srid = -1; /* -1 for NULL */
-
- rt_pgraster *pgraster = NULL;
- rt_raster raster;
-
- /* NULL if NULL */
- if (PG_ARGISNULL(0))
- PG_RETURN_NULL();
-
- /* get data */
- bytea_data = (bytea *) PG_GETARG_BYTEA_P(0);
- data = (uint8_t *) VARDATA(bytea_data);
- data_len = VARSIZE(bytea_data) - VARHDRSZ;
-
- /* process srid */
- /* NULL srid means try to determine SRID from bytea */
- if (!PG_ARGISNULL(1))
- srid = clamp_srid(PG_GETARG_INT32(1));
-
- /* create memory "file" */
- vsifp = VSIFileFromMemBuffer("/vsimem/in.dat", data, data_len, FALSE);
- if (vsifp == NULL) {
- PG_FREE_IF_COPY(bytea_data, 0);
- elog(ERROR, "RASTER_fromGDALRaster: Could not load bytea into memory file for use by GDAL");
- PG_RETURN_NULL();
- }
-
- /* register all GDAL drivers */
- rt_util_gdal_register_all();
-
- /* open GDAL raster */
- hdsSrc = GDALOpenShared("/vsimem/in.dat", GA_ReadOnly);
- if (hdsSrc == NULL) {
- VSIFCloseL(vsifp);
- PG_FREE_IF_COPY(bytea_data, 0);
- elog(ERROR, "RASTER_fromGDALRaster: Could not open bytea with GDAL. Check that the bytea is of a GDAL supported format");
- PG_RETURN_NULL();
- }
-
-#if POSTGIS_DEBUG_LEVEL > 3
- {
- GDALDriverH hdrv = GDALGetDatasetDriver(hdsSrc);
-
- POSTGIS_RT_DEBUGF(4, "Input GDAL Raster info: %s, (%d x %d)",
- GDALGetDriverShortName(hdrv),
- GDALGetRasterXSize(hdsSrc),
- GDALGetRasterYSize(hdsSrc)
- );
- }
-#endif
-
- /* convert GDAL raster to raster */
- raster = rt_raster_from_gdal_dataset(hdsSrc);
-
- GDALClose(hdsSrc);
- VSIFCloseL(vsifp);
- PG_FREE_IF_COPY(bytea_data, 0);
-
- if (raster == NULL) {
- elog(ERROR, "RASTER_fromGDALRaster: Could not convert GDAL raster to raster");
- PG_RETURN_NULL();
- }
-
- /* apply SRID if set */
- if (srid != -1)
- rt_raster_set_srid(raster, srid);
-
- pgraster = rt_raster_serialize(raster);
- rt_raster_destroy(raster);
- if (!pgraster)
- PG_RETURN_NULL();
-
- SET_VARSIZE(pgraster, pgraster->size);
- PG_RETURN_POINTER(pgraster);
-}
-
-/**
- * Returns formatted GDAL raster as bytea object of raster
- */
-PG_FUNCTION_INFO_V1(RASTER_asGDALRaster);
-Datum RASTER_asGDALRaster(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_raster raster;
-
- text *formattext = NULL;
- char *format = NULL;
- char **options = NULL;
- text *optiontext = NULL;
- char *option = NULL;
- int srid = SRID_UNKNOWN;
- char *srs = NULL;
-
- ArrayType *array;
- Oid etype;
- Datum *e;
- bool *nulls;
- int16 typlen;
- bool typbyval;
- char typalign;
- int n = 0;
- int i = 0;
- int j = 0;
-
- uint8_t *gdal = NULL;
- uint64_t gdal_size = 0;
- bytea *result = NULL;
- uint64_t result_size = 0;
-
- POSTGIS_RT_DEBUG(3, "RASTER_asGDALRaster: Starting");
-
- /* pgraster is null, return null */
- 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_asGDALRaster: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- /* format is required */
- if (PG_ARGISNULL(1)) {
- elog(NOTICE, "Format must be provided");
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- PG_RETURN_NULL();
- }
- else {
- formattext = PG_GETARG_TEXT_P(1);
- format = text_to_cstring(formattext);
- }
-
- POSTGIS_RT_DEBUGF(3, "RASTER_asGDALRaster: Arg 1 (format) is %s", format);
-
- /* process options */
- if (!PG_ARGISNULL(2)) {
- POSTGIS_RT_DEBUG(3, "RASTER_asGDALRaster: Processing Arg 2 (options)");
- array = PG_GETARG_ARRAYTYPE_P(2);
- etype = ARR_ELEMTYPE(array);
- get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
-
- switch (etype) {
- case TEXTOID:
- break;
- default:
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_asGDALRaster: Invalid data type for options");
- PG_RETURN_NULL();
- break;
- }
-
- deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
- &nulls, &n);
-
- if (n) {
- options = (char **) palloc(sizeof(char *) * (n + 1));
- if (options == NULL) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_asGDALRaster: Could not allocate memory for options");
- PG_RETURN_NULL();
- }
-
- /* clean each option */
- for (i = 0, j = 0; i < n; i++) {
- if (nulls[i]) continue;
-
- option = NULL;
- switch (etype) {
- case TEXTOID:
- optiontext = (text *) DatumGetPointer(e[i]);
- if (NULL == optiontext) break;
- option = text_to_cstring(optiontext);
-
- /* trim string */
- option = rtpg_trim(option);
- POSTGIS_RT_DEBUGF(3, "RASTER_asGDALRaster: option is '%s'", option);
- break;
- }
-
- if (strlen(option)) {
- options[j] = (char *) palloc(sizeof(char) * (strlen(option) + 1));
- options[j] = option;
- j++;
- }
- }
-
- if (j > 0) {
- /* trim allocation */
- options = repalloc(options, (j + 1) * sizeof(char *));
-
- /* add NULL to end */
- options[j] = NULL;
-
- }
- else {
- pfree(options);
- options = NULL;
- }
- }
- }
-
- /* process srid */
- /* NULL srid means use raster's srid */
- if (PG_ARGISNULL(3))
- srid = rt_raster_get_srid(raster);
- else
- srid = PG_GETARG_INT32(3);
-
- /* get srs from srid */
- if (clamp_srid(srid) != SRID_UNKNOWN) {
- srs = rtpg_getSR(srid);
- if (NULL == srs) {
- if (NULL != options) {
- for (i = j - 1; i >= 0; i--) pfree(options[i]);
- pfree(options);
- }
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_asGDALRaster: Could not find srtext for SRID (%d)", srid);
- PG_RETURN_NULL();
- }
- POSTGIS_RT_DEBUGF(3, "RASTER_asGDALRaster: Arg 3 (srs) is %s", srs);
- }
- else
- srs = NULL;
-
- POSTGIS_RT_DEBUG(3, "RASTER_asGDALRaster: Generating GDAL raster");
- gdal = rt_raster_to_gdal(raster, srs, format, options, &gdal_size);
-
- /* free memory */
- if (NULL != options) {
- for (i = j - 1; i >= 0; i--) pfree(options[i]);
- pfree(options);
- }
- if (NULL != srs) pfree(srs);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- if (!gdal) {
- elog(ERROR, "RASTER_asGDALRaster: Could not allocate and generate GDAL raster");
- PG_RETURN_NULL();
- }
- POSTGIS_RT_DEBUGF(3, "RASTER_asGDALRaster: GDAL raster generated with %d bytes", (int) gdal_size);
-
- /* result is a varlena */
- result_size = gdal_size + VARHDRSZ;
- result = (bytea *) palloc(result_size);
- if (NULL == result) {
- elog(ERROR, "RASTER_asGDALRaster: Insufficient virtual memory for GDAL raster");
- PG_RETURN_NULL();
- }
- SET_VARSIZE(result, result_size);
- memcpy(VARDATA(result), gdal, VARSIZE(result) - VARHDRSZ);
-
- /* for test output
- FILE *fh = NULL;
- fh = fopen("/tmp/out.dat", "w");
- fwrite(gdal, sizeof(uint8_t), gdal_size, fh);
- fclose(fh);
- */
-
- /* free gdal mem buffer */
- if (gdal) CPLFree(gdal);
-
- POSTGIS_RT_DEBUG(3, "RASTER_asGDALRaster: Returning pointer to GDAL raster");
- PG_RETURN_POINTER(result);
-}
-
-/**
- * Returns available GDAL drivers
- */
-PG_FUNCTION_INFO_V1(RASTER_getGDALDrivers);
-Datum RASTER_getGDALDrivers(PG_FUNCTION_ARGS)
-{
- FuncCallContext *funcctx;
- TupleDesc tupdesc;
-
- uint32_t drv_count;
- rt_gdaldriver drv_set;
- rt_gdaldriver drv_set2;
- int call_cntr;
- int max_calls;
-
- /* first call of function */
- if (SRF_IS_FIRSTCALL()) {
- MemoryContext oldcontext;
-
- /* 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);
-
- drv_set = rt_raster_gdal_drivers(&drv_count, 1);
- if (NULL == drv_set || !drv_count) {
- elog(NOTICE, "No GDAL drivers found");
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- POSTGIS_RT_DEBUGF(3, "%d drivers returned", (int) drv_count);
-
- /* Store needed information */
- funcctx->user_fctx = drv_set;
-
- /* total number of tuples to be returned */
- funcctx->max_calls = drv_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;
- drv_set2 = funcctx->user_fctx;
-
- /* do when there is more left to send */
- if (call_cntr < max_calls) {
- int values_length = 4;
- Datum values[values_length];
- bool nulls[values_length];
- HeapTuple tuple;
- Datum result;
-
- POSTGIS_RT_DEBUGF(3, "Result %d", call_cntr);
-
- memset(nulls, FALSE, sizeof(bool) * values_length);
-
- values[0] = Int32GetDatum(drv_set2[call_cntr].idx);
- values[1] = CStringGetTextDatum(drv_set2[call_cntr].short_name);
- values[2] = CStringGetTextDatum(drv_set2[call_cntr].long_name);
- values[3] = CStringGetTextDatum(drv_set2[call_cntr].create_options);
-
- POSTGIS_RT_DEBUGF(4, "Result %d, Index %d", call_cntr, drv_set2[call_cntr].idx);
- POSTGIS_RT_DEBUGF(4, "Result %d, Short Name %s", call_cntr, drv_set2[call_cntr].short_name);
- POSTGIS_RT_DEBUGF(4, "Result %d, Full Name %s", call_cntr, drv_set2[call_cntr].long_name);
- POSTGIS_RT_DEBUGF(5, "Result %d, Create Options %s", call_cntr, drv_set2[call_cntr].create_options);
-
- /* build a tuple */
- tuple = heap_form_tuple(tupdesc, values, nulls);
-
- /* make the tuple into a datum */
- result = HeapTupleGetDatum(tuple);
-
- /* clean up */
- pfree(drv_set2[call_cntr].short_name);
- pfree(drv_set2[call_cntr].long_name);
- pfree(drv_set2[call_cntr].create_options);
-
- SRF_RETURN_NEXT(funcctx, result);
- }
- /* do when there is no more left */
- else {
- pfree(drv_set2);
- SRF_RETURN_DONE(funcctx);
- }
-}
-
-/**
- * Rasterize a geometry
- */
-PG_FUNCTION_INFO_V1(RASTER_asRaster);
-Datum RASTER_asRaster(PG_FUNCTION_ARGS)
-{
- GSERIALIZED *gser = NULL;
-
- LWGEOM *geom = NULL;
- rt_raster rast = NULL;
- rt_pgraster *pgrast = NULL;
-
- unsigned char *wkb;
- size_t wkb_len = 0;
- unsigned char variant = WKB_SFSQL;
-
- double scale[2] = {0};
- double *scale_x = NULL;
- double *scale_y = NULL;
-
- int dim[2] = {0};
- int *dim_x = NULL;
- int *dim_y = NULL;
-
- ArrayType *array;
- Oid etype;
- Datum *e;
- bool *nulls;
- int16 typlen;
- bool typbyval;
- char typalign;
- int n = 0;
- int i = 0;
- int j = 0;
- int haserr = 0;
-
- text *pixeltypetext = NULL;
- char *pixeltype = NULL;
- rt_pixtype pixtype = PT_END;
- rt_pixtype *pixtypes = NULL;
- int pixtypes_len = 0;
-
- double *values = NULL;
- int values_len = 0;
-
- uint8_t *hasnodatas = NULL;
- double *nodatavals = NULL;
- int nodatavals_len = 0;
-
- double ulw[2] = {0};
- double *ul_xw = NULL;
- double *ul_yw = NULL;
-
- double gridw[2] = {0};
- double *grid_xw = NULL;
- double *grid_yw = NULL;
-
- double skew[2] = {0};
- double *skew_x = NULL;
- double *skew_y = NULL;
-
- char **options = NULL;
- int options_len = 0;
-
- uint32_t num_bands = 0;
-
- int srid = SRID_UNKNOWN;
- char *srs = NULL;
-
- POSTGIS_RT_DEBUG(3, "RASTER_asRaster: Starting");
-
- /* based upon LWGEOM_asBinary function in postgis/lwgeom_ogc.c */
-
- /* Get the geometry */
- if (PG_ARGISNULL(0))
- PG_RETURN_NULL();
-
- gser = (GSERIALIZED *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
- geom = lwgeom_from_gserialized(gser);
-
- /* Get a 2D version of the geometry if necessary */
- if (lwgeom_ndims(geom) > 2) {
- LWGEOM *geom2d = lwgeom_force_2d(geom);
- lwgeom_free(geom);
- geom = geom2d;
- }
-
- /* empty geometry, return empty raster */
- if (lwgeom_is_empty(geom)) {
- POSTGIS_RT_DEBUG(3, "Input geometry is empty. Returning empty raster");
- lwgeom_free(geom);
- PG_FREE_IF_COPY(gser, 0);
-
- rast = rt_raster_new(0, 0);
- if (rast == NULL)
- PG_RETURN_NULL();
-
- pgrast = rt_raster_serialize(rast);
- rt_raster_destroy(rast);
-
- if (NULL == pgrast)
- PG_RETURN_NULL();
-
- SET_VARSIZE(pgrast, pgrast->size);
- PG_RETURN_POINTER(pgrast);
- }
-
- /* scale x */
- if (!PG_ARGISNULL(1)) {
- scale[0] = PG_GETARG_FLOAT8(1);
- if (FLT_NEQ(scale[0], 0)) scale_x = &scale[0];
- }
-
- /* scale y */
- if (!PG_ARGISNULL(2)) {
- scale[1] = PG_GETARG_FLOAT8(2);
- if (FLT_NEQ(scale[1], 0)) scale_y = &scale[1];
- }
- POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: scale (x, y) = %f, %f", scale[0], scale[1]);
-
- /* width */
- if (!PG_ARGISNULL(3)) {
- dim[0] = PG_GETARG_INT32(3);
- if (dim[0] < 0) dim[0] = 0;
- if (dim[0] != 0) dim_x = &dim[0];
- }
-
- /* height */
- if (!PG_ARGISNULL(4)) {
- dim[1] = PG_GETARG_INT32(4);
- if (dim[1] < 0) dim[1] = 0;
- if (dim[1] != 0) dim_y = &dim[1];
- }
- POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: dim (x, y) = %d, %d", dim[0], dim[1]);
-
- /* pixeltype */
- if (!PG_ARGISNULL(5)) {
- array = PG_GETARG_ARRAYTYPE_P(5);
- etype = ARR_ELEMTYPE(array);
- get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
-
- switch (etype) {
- case TEXTOID:
- break;
- default:
-
- lwgeom_free(geom);
- PG_FREE_IF_COPY(gser, 0);
-
- elog(ERROR, "RASTER_asRaster: Invalid data type for pixeltype");
- PG_RETURN_NULL();
- break;
- }
-
- deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
- &nulls, &n);
-
- if (n) {
- pixtypes = (rt_pixtype *) palloc(sizeof(rt_pixtype) * n);
- /* clean each pixeltype */
- for (i = 0, j = 0; i < n; i++) {
- if (nulls[i]) {
- pixtypes[j++] = PT_64BF;
- continue;
- }
-
- pixeltype = NULL;
- switch (etype) {
- case TEXTOID:
- pixeltypetext = (text *) DatumGetPointer(e[i]);
- if (NULL == pixeltypetext) break;
- pixeltype = text_to_cstring(pixeltypetext);
-
- /* trim string */
- pixeltype = rtpg_trim(pixeltype);
- POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: pixeltype is '%s'", pixeltype);
- break;
- }
-
- if (strlen(pixeltype)) {
- pixtype = rt_pixtype_index_from_name(pixeltype);
- if (pixtype == PT_END) {
-
- pfree(pixtypes);
-
- lwgeom_free(geom);
- PG_FREE_IF_COPY(gser, 0);
-
- elog(ERROR, "RASTER_asRaster: Invalid pixel type provided: %s", pixeltype);
- PG_RETURN_NULL();
- }
-
- pixtypes[j] = pixtype;
- j++;
- }
- }
-
- if (j > 0) {
- /* trim allocation */
- pixtypes = repalloc(pixtypes, j * sizeof(rt_pixtype));
- pixtypes_len = j;
- }
- else {
- pfree(pixtypes);
- pixtypes = NULL;
- pixtypes_len = 0;
- }
- }
- }
-#if POSTGIS_DEBUG_LEVEL > 0
- for (i = 0; i < pixtypes_len; i++)
- POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: pixtypes[%d] = %d", i, (int) pixtypes[i]);
-#endif
-
- /* value */
- if (!PG_ARGISNULL(6)) {
- array = PG_GETARG_ARRAYTYPE_P(6);
- etype = ARR_ELEMTYPE(array);
- get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
-
- switch (etype) {
- case FLOAT4OID:
- case FLOAT8OID:
- break;
- default:
-
- if (pixtypes_len) pfree(pixtypes);
-
- lwgeom_free(geom);
- PG_FREE_IF_COPY(gser, 0);
-
- elog(ERROR, "RASTER_asRaster: Invalid data type for value");
- PG_RETURN_NULL();
- break;
- }
-
- deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
- &nulls, &n);
-
- if (n) {
- values = (double *) palloc(sizeof(double) * n);
- for (i = 0, j = 0; i < n; i++) {
- if (nulls[i]) {
- values[j++] = 1;
- continue;
- }
-
- switch (etype) {
- case FLOAT4OID:
- values[j] = (double) DatumGetFloat4(e[i]);
- break;
- case FLOAT8OID:
- values[j] = (double) DatumGetFloat8(e[i]);
- break;
- }
- POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: values[%d] = %f", j, values[j]);
-
- j++;
- }
-
- if (j > 0) {
- /* trim allocation */
- values = repalloc(values, j * sizeof(double));
- values_len = j;
- }
- else {
- pfree(values);
- values = NULL;
- values_len = 0;
- }
- }
- }
-#if POSTGIS_DEBUG_LEVEL > 0
- for (i = 0; i < values_len; i++)
- POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: values[%d] = %f", i, values[i]);
-#endif
-
- /* nodataval */
- if (!PG_ARGISNULL(7)) {
- array = PG_GETARG_ARRAYTYPE_P(7);
- etype = ARR_ELEMTYPE(array);
- get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
-
- switch (etype) {
- case FLOAT4OID:
- case FLOAT8OID:
- break;
- default:
-
- if (pixtypes_len) pfree(pixtypes);
- if (values_len) pfree(values);
-
- lwgeom_free(geom);
- PG_FREE_IF_COPY(gser, 0);
-
- elog(ERROR, "RASTER_asRaster: Invalid data type for nodataval");
- PG_RETURN_NULL();
- break;
- }
-
- deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
- &nulls, &n);
-
- if (n) {
- nodatavals = (double *) palloc(sizeof(double) * n);
- hasnodatas = (uint8_t *) palloc(sizeof(uint8_t) * n);
- for (i = 0, j = 0; i < n; i++) {
- if (nulls[i]) {
- hasnodatas[j] = 0;
- nodatavals[j] = 0;
- j++;
- continue;
- }
-
- hasnodatas[j] = 1;
- switch (etype) {
- case FLOAT4OID:
- nodatavals[j] = (double) DatumGetFloat4(e[i]);
- break;
- case FLOAT8OID:
- nodatavals[j] = (double) DatumGetFloat8(e[i]);
- break;
- }
- POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: hasnodatas[%d] = %d", j, hasnodatas[j]);
- POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: nodatavals[%d] = %f", j, nodatavals[j]);
-
- j++;
- }
-
- if (j > 0) {
- /* trim allocation */
- nodatavals = repalloc(nodatavals, j * sizeof(double));
- hasnodatas = repalloc(hasnodatas, j * sizeof(uint8_t));
- nodatavals_len = j;
- }
- else {
- pfree(nodatavals);
- pfree(hasnodatas);
- nodatavals = NULL;
- hasnodatas = NULL;
- nodatavals_len = 0;
- }
- }
- }
-#if POSTGIS_DEBUG_LEVEL > 0
- for (i = 0; i < nodatavals_len; i++) {
- POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: hasnodatas[%d] = %d", i, hasnodatas[i]);
- POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: nodatavals[%d] = %f", i, nodatavals[i]);
- }
-#endif
-
- /* upperleftx */
- if (!PG_ARGISNULL(8)) {
- ulw[0] = PG_GETARG_FLOAT8(8);
- ul_xw = &ulw[0];
- }
-
- /* upperlefty */
- if (!PG_ARGISNULL(9)) {
- ulw[1] = PG_GETARG_FLOAT8(9);
- ul_yw = &ulw[1];
- }
- POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: upperleft (x, y) = %f, %f", ulw[0], ulw[1]);
-
- /* gridx */
- if (!PG_ARGISNULL(10)) {
- gridw[0] = PG_GETARG_FLOAT8(10);
- grid_xw = &gridw[0];
- }
-
- /* gridy */
- if (!PG_ARGISNULL(11)) {
- gridw[1] = PG_GETARG_FLOAT8(11);
- grid_yw = &gridw[1];
- }
- POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: grid (x, y) = %f, %f", gridw[0], gridw[1]);
-
- /* check dependent variables */
- haserr = 0;
- do {
- /* only part of scale provided */
- if (
- (scale_x == NULL && scale_y != NULL) ||
- (scale_x != NULL && scale_y == NULL)
- ) {
- elog(NOTICE, "Values must be provided for both X and Y of scale if one is specified");
- haserr = 1;
- break;
- }
-
- /* only part of dimension provided */
- if (
- (dim_x == NULL && dim_y != NULL) ||
- (dim_x != NULL && dim_y == NULL)
- ) {
- elog(NOTICE, "Values must be provided for both width and height if one is specified");
- haserr = 1;
- break;
- }
-
- /* scale and dimension provided */
- if (
- (scale_x != NULL && scale_y != NULL) &&
- (dim_x != NULL && dim_y != NULL)
- ) {
- elog(NOTICE, "Values provided for X and Y of scale and width and height. Using the width and height");
- scale_x = NULL;
- scale_y = NULL;
- break;
- }
-
- /* neither scale or dimension provided */
- if (
- (scale_x == NULL && scale_y == NULL) &&
- (dim_x == NULL && dim_y == NULL)
- ) {
- elog(NOTICE, "Values must be provided for X and Y of scale or width and height");
- haserr = 1;
- break;
- }
-
- /* only part of upper-left provided */
- if (
- (ul_xw == NULL && ul_yw != NULL) ||
- (ul_xw != NULL && ul_yw == NULL)
- ) {
- elog(NOTICE, "Values must be provided for both X and Y when specifying the upper-left corner");
- haserr = 1;
- break;
- }
-
- /* only part of alignment provided */
- if (
- (grid_xw == NULL && grid_yw != NULL) ||
- (grid_xw != NULL && grid_yw == NULL)
- ) {
- elog(NOTICE, "Values must be provided for both X and Y when specifying the alignment");
- haserr = 1;
- break;
- }
-
- /* upper-left and alignment provided */
- if (
- (ul_xw != NULL && ul_yw != NULL) &&
- (grid_xw != NULL && grid_yw != NULL)
- ) {
- elog(NOTICE, "Values provided for both X and Y of upper-left corner and alignment. Using the values of upper-left corner");
- grid_xw = NULL;
- grid_yw = NULL;
- break;
- }
- }
- while (0);
-
- if (haserr) {
- if (pixtypes_len) pfree(pixtypes);
- if (values_len) pfree(values);
- if (nodatavals_len) {
- pfree(nodatavals);
- pfree(hasnodatas);
- }
-
- lwgeom_free(geom);
- PG_FREE_IF_COPY(gser, 0);
-
- PG_RETURN_NULL();
- }
-
- /* skewx */
- if (!PG_ARGISNULL(12)) {
- skew[0] = PG_GETARG_FLOAT8(12);
- if (FLT_NEQ(skew[0], 0)) skew_x = &skew[0];
- }
-
- /* skewy */
- if (!PG_ARGISNULL(13)) {
- skew[1] = PG_GETARG_FLOAT8(13);
- if (FLT_NEQ(skew[1], 0)) skew_y = &skew[1];
- }
- POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: skew (x, y) = %f, %f", skew[0], skew[1]);
-
- /* all touched */
- if (!PG_ARGISNULL(14) && PG_GETARG_BOOL(14) == TRUE) {
- if (options_len < 1) {
- options_len = 1;
- options = (char **) palloc(sizeof(char *) * options_len);
- }
- else {
- options_len++;
- options = (char **) repalloc(options, sizeof(char *) * options_len);
- }
-
- options[options_len - 1] = palloc(sizeof(char*) * (strlen("ALL_TOUCHED=TRUE") + 1));
- options[options_len - 1] = "ALL_TOUCHED=TRUE";
- }
-
- if (options_len) {
- options_len++;
- options = (char **) repalloc(options, sizeof(char *) * options_len);
- options[options_len - 1] = NULL;
- }
-
- /* get geometry's srid */
- srid = gserialized_get_srid(gser);
-
- POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: srid = %d", srid);
- if (clamp_srid(srid) != SRID_UNKNOWN) {
- srs = rtpg_getSR(srid);
- if (NULL == srs) {
-
- if (pixtypes_len) pfree(pixtypes);
- if (values_len) pfree(values);
- if (nodatavals_len) {
- pfree(hasnodatas);
- pfree(nodatavals);
- }
- if (options_len) pfree(options);
-
- lwgeom_free(geom);
- PG_FREE_IF_COPY(gser, 0);
-
- elog(ERROR, "RASTER_asRaster: Could not find srtext for SRID (%d)", srid);
- PG_RETURN_NULL();
- }
- POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: srs is %s", srs);
- }
- else
- srs = NULL;
-
- /* determine number of bands */
- /* MIN macro is from GDAL's cpl_port.h */
- num_bands = MIN(pixtypes_len, values_len);
- num_bands = MIN(num_bands, nodatavals_len);
- POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: pixtypes_len = %d", pixtypes_len);
- POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: values_len = %d", values_len);
- POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: nodatavals_len = %d", nodatavals_len);
- POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: num_bands = %d", num_bands);
-
- /* warn of imbalanced number of band elements */
- if (!(
- (pixtypes_len == values_len) &&
- (values_len == nodatavals_len)
- )) {
- elog(
- NOTICE,
- "Imbalanced number of values provided for pixeltype (%d), value (%d) and nodataval (%d). Using the first %d values of each parameter",
- pixtypes_len,
- values_len,
- nodatavals_len,
- num_bands
- );
- }
-
- /* get wkb of geometry */
- POSTGIS_RT_DEBUG(3, "RASTER_asRaster: getting wkb of geometry");
- wkb = lwgeom_to_wkb(geom, variant, &wkb_len);
- lwgeom_free(geom);
- PG_FREE_IF_COPY(gser, 0);
-
- /* rasterize geometry */
- POSTGIS_RT_DEBUG(3, "RASTER_asRaster: rasterizing geometry");
- /* use nodatavals for the init parameter */
- rast = rt_raster_gdal_rasterize(wkb,
- (uint32_t) wkb_len, srs,
- num_bands, pixtypes,
- nodatavals, values,
- nodatavals, hasnodatas,
- dim_x, dim_y,
- scale_x, scale_y,
- ul_xw, ul_yw,
- grid_xw, grid_yw,
- skew_x, skew_y,
- options
- );
-
- if (pixtypes_len) pfree(pixtypes);
- if (values_len) pfree(values);
- if (nodatavals_len) {
- pfree(hasnodatas);
- pfree(nodatavals);
- }
- if (options_len) pfree(options);
-
- if (!rast) {
- elog(ERROR, "RASTER_asRaster: Could not rasterize geometry");
- PG_RETURN_NULL();
- }
-
- /* add target srid */
- rt_raster_set_srid(rast, srid);
-
- pgrast = rt_raster_serialize(rast);
- rt_raster_destroy(rast);
-
- if (NULL == pgrast) PG_RETURN_NULL();
-
- POSTGIS_RT_DEBUG(3, "RASTER_asRaster: done");
-
- SET_VARSIZE(pgrast, pgrast->size);
- PG_RETURN_POINTER(pgrast);
-}
-
-/**
- * warp a raster using GDAL Warp API
- */
-PG_FUNCTION_INFO_V1(RASTER_GDALWarp);
-Datum RASTER_GDALWarp(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_pgraster *pgrast = NULL;
- rt_raster raster = NULL;
- rt_raster rast = NULL;
-
- text *algtext = NULL;
- char *algchar = NULL;
- GDALResampleAlg alg = GRA_NearestNeighbour;
- double max_err = 0.125;
-
- int src_srid = SRID_UNKNOWN;
- char *src_srs = NULL;
- int dst_srid = SRID_UNKNOWN;
- char *dst_srs = NULL;
- int no_srid = 0;
-
- double scale[2] = {0};
- double *scale_x = NULL;
- double *scale_y = NULL;
-
- double gridw[2] = {0};
- double *grid_xw = NULL;
- double *grid_yw = NULL;
-
- double skew[2] = {0};
- double *skew_x = NULL;
- double *skew_y = NULL;
-
- int dim[2] = {0};
- int *dim_x = NULL;
- int *dim_y = NULL;
-
- POSTGIS_RT_DEBUG(3, "RASTER_GDALWarp: Starting");
-
- /* pgraster is null, return null */
- if (PG_ARGISNULL(0))
- PG_RETURN_NULL();
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
-
- /* raster */
- raster = rt_raster_deserialize(pgraster, FALSE);
- if (!raster) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_GDALWarp: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- /* resampling algorithm */
- if (!PG_ARGISNULL(1)) {
- algtext = PG_GETARG_TEXT_P(1);
- algchar = rtpg_trim(rtpg_strtoupper(text_to_cstring(algtext)));
- alg = rt_util_gdal_resample_alg(algchar);
- }
- POSTGIS_RT_DEBUGF(4, "Resampling algorithm: %d", alg);
-
- /* max error */
- if (!PG_ARGISNULL(2)) {
- max_err = PG_GETARG_FLOAT8(2);
- if (max_err < 0.) max_err = 0.;
- }
- POSTGIS_RT_DEBUGF(4, "max_err: %f", max_err);
-
- /* source SRID */
- src_srid = clamp_srid(rt_raster_get_srid(raster));
- POSTGIS_RT_DEBUGF(4, "source SRID: %d", src_srid);
-
- /* target SRID */
- if (!PG_ARGISNULL(3)) {
- dst_srid = clamp_srid(PG_GETARG_INT32(3));
- if (dst_srid == SRID_UNKNOWN) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_GDALWarp: %d is an invalid target SRID", dst_srid);
- PG_RETURN_NULL();
- }
- }
- else
- dst_srid = src_srid;
- POSTGIS_RT_DEBUGF(4, "destination SRID: %d", dst_srid);
-
- /* target SRID != src SRID, error */
- if (src_srid == SRID_UNKNOWN && dst_srid != src_srid) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_GDALWarp: Input raster has unknown (%d) SRID", src_srid);
- PG_RETURN_NULL();
- }
- /* target SRID == src SRID, no reprojection */
- else if (dst_srid == src_srid) {
- no_srid = 1;
- }
-
- /* scale x */
- if (!PG_ARGISNULL(4)) {
- scale[0] = PG_GETARG_FLOAT8(4);
- if (FLT_NEQ(scale[0], 0)) scale_x = &scale[0];
- }
-
- /* scale y */
- if (!PG_ARGISNULL(5)) {
- scale[1] = PG_GETARG_FLOAT8(5);
- if (FLT_NEQ(scale[1], 0)) scale_y = &scale[1];
- }
-
- /* grid alignment x */
- if (!PG_ARGISNULL(6)) {
- gridw[0] = PG_GETARG_FLOAT8(6);
- grid_xw = &gridw[0];
- }
-
- /* grid alignment y */
- if (!PG_ARGISNULL(7)) {
- gridw[1] = PG_GETARG_FLOAT8(7);
- grid_yw = &gridw[1];
- }
-
- /* skew x */
- if (!PG_ARGISNULL(8)) {
- skew[0] = PG_GETARG_FLOAT8(8);
- if (FLT_NEQ(skew[0], 0)) skew_x = &skew[0];
- }
-
- /* skew y */
- if (!PG_ARGISNULL(9)) {
- skew[1] = PG_GETARG_FLOAT8(9);
- if (FLT_NEQ(skew[1], 0)) skew_y = &skew[1];
- }
-
- /* width */
- if (!PG_ARGISNULL(10)) {
- dim[0] = PG_GETARG_INT32(10);
- if (dim[0] < 0) dim[0] = 0;
- if (dim[0] > 0) dim_x = &dim[0];
- }
-
- /* height */
- if (!PG_ARGISNULL(11)) {
- dim[1] = PG_GETARG_INT32(11);
- if (dim[1] < 0) dim[1] = 0;
- if (dim[1] > 0) dim_y = &dim[1];
- }
-
- /* check that at least something is to be done */
- if (
- (dst_srid == SRID_UNKNOWN) &&
- (scale_x == NULL) &&
- (scale_y == NULL) &&
- (grid_xw == NULL) &&
- (grid_yw == NULL) &&
- (skew_x == NULL) &&
- (skew_y == NULL) &&
- (dim_x == NULL) &&
- (dim_y == NULL)
- ) {
- elog(NOTICE, "No resampling parameters provided. Returning original raster");
- rt_raster_destroy(raster);
- PG_RETURN_POINTER(pgraster);
- }
- /* both values of alignment must be provided if any one is provided */
- else if (
- (grid_xw != NULL && grid_yw == NULL) ||
- (grid_xw == NULL && grid_yw != NULL)
- ) {
- elog(NOTICE, "Values must be provided for both X and Y when specifying the alignment. Returning original raster");
- rt_raster_destroy(raster);
- PG_RETURN_POINTER(pgraster);
- }
- /* both values of scale must be provided if any one is provided */
- else if (
- (scale_x != NULL && scale_y == NULL) ||
- (scale_x == NULL && scale_y != NULL)
- ) {
- elog(NOTICE, "Values must be provided for both X and Y when specifying the scale. Returning original raster");
- rt_raster_destroy(raster);
- PG_RETURN_POINTER(pgraster);
- }
- /* scale and width/height provided */
- else if (
- (scale_x != NULL || scale_y != NULL) &&
- (dim_x != NULL || dim_y != NULL)
- ) {
- elog(NOTICE, "Scale X/Y and width/height are mutually exclusive. Only provide one. Returning original raster");
- rt_raster_destroy(raster);
- PG_RETURN_POINTER(pgraster);
- }
-
- /* get srses from srids */
- if (!no_srid) {
- /* source srs */
- src_srs = rtpg_getSR(src_srid);
- if (NULL == src_srs) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_GDALWarp: Input raster has unknown SRID (%d)", src_srid);
- PG_RETURN_NULL();
- }
- POSTGIS_RT_DEBUGF(4, "src srs: %s", src_srs);
-
- dst_srs = rtpg_getSR(dst_srid);
- if (NULL == dst_srs) {
- if (!no_srid) pfree(src_srs);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_GDALWarp: Target SRID (%d) is unknown", dst_srid);
- PG_RETURN_NULL();
- }
- POSTGIS_RT_DEBUGF(4, "dst srs: %s", dst_srs);
- }
-
- rast = rt_raster_gdal_warp(
- raster,
- src_srs, dst_srs,
- scale_x, scale_y,
- dim_x, dim_y,
- NULL, NULL,
- grid_xw, grid_yw,
- skew_x, skew_y,
- alg, max_err);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- if (!no_srid) {
- pfree(src_srs);
- pfree(dst_srs);
- }
- if (!rast) {
- elog(ERROR, "RASTER_band: Could not create transformed raster");
- PG_RETURN_NULL();
- }
-
- /* add target SRID */
- rt_raster_set_srid(rast, dst_srid);
-
- pgrast = rt_raster_serialize(rast);
- rt_raster_destroy(rast);
-
- if (NULL == pgrast) PG_RETURN_NULL();
-
- POSTGIS_RT_DEBUG(3, "RASTER_GDALWarp: done");
-
- SET_VARSIZE(pgrast, pgrast->size);
- PG_RETURN_POINTER(pgrast);
-}
-
-/**
- * Get raster's meta data
- */
-PG_FUNCTION_INFO_V1(RASTER_metadata);
-Datum RASTER_metadata(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
-
- uint32_t numBands;
- double scaleX;
- double scaleY;
- double ipX;
- double ipY;
- double skewX;
- double skewY;
- int32_t srid;
- uint32_t width;
- uint32_t height;
-
- TupleDesc tupdesc;
- int values_length = 10;
- Datum values[values_length];
- bool nulls[values_length];
- HeapTuple tuple;
- Datum result;
-
- POSTGIS_RT_DEBUG(3, "RASTER_metadata: Starting");
-
- /* pgraster is null, return null */
- if (PG_ARGISNULL(0))
- PG_RETURN_NULL();
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
-
- /* raster */
- raster = rt_raster_deserialize(pgraster, TRUE);
- if (!raster) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_metadata; Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- /* upper left x, y */
- ipX = rt_raster_get_x_offset(raster);
- ipY = rt_raster_get_y_offset(raster);
-
- /* width, height */
- width = rt_raster_get_width(raster);
- height = rt_raster_get_height(raster);
-
- /* scale x, y */
- scaleX = rt_raster_get_x_scale(raster);
- scaleY = rt_raster_get_y_scale(raster);
-
- /* skew x, y */
- skewX = rt_raster_get_x_skew(raster);
- skewY = rt_raster_get_y_skew(raster);
-
- /* srid */
- srid = rt_raster_get_srid(raster);
-
- /* numbands */
- numBands = rt_raster_get_num_bands(raster);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- /* 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);
-
- values[0] = Float8GetDatum(ipX);
- values[1] = Float8GetDatum(ipY);
- values[2] = UInt32GetDatum(width);
- values[3] = UInt32GetDatum(height);
- values[4] = Float8GetDatum(scaleX);
- values[5] = Float8GetDatum(scaleY);
- values[6] = Float8GetDatum(skewX);
- values[7] = Float8GetDatum(skewY);
- values[8] = Int32GetDatum(srid);
- values[9] = UInt32GetDatum(numBands);
-
- memset(nulls, FALSE, sizeof(bool) * values_length);
-
- /* build a tuple */
- tuple = heap_form_tuple(tupdesc, values, nulls);
-
- /* make the tuple into a datum */
- result = HeapTupleGetDatum(tuple);
-
- PG_RETURN_DATUM(result);
-}
-
-/**
- * Get raster bands' meta data
- */
-PG_FUNCTION_INFO_V1(RASTER_bandmetadata);
-Datum RASTER_bandmetadata(PG_FUNCTION_ARGS)
-{
- FuncCallContext *funcctx;
- TupleDesc tupdesc;
- int call_cntr;
- int max_calls;
-
- struct bandmetadata {
- uint32_t bandnum;
- char *pixeltype;
- bool hasnodata;
- double nodataval;
- bool isoutdb;
- char *bandpath;
- };
- struct bandmetadata *bmd = NULL;
- struct bandmetadata *bmd2 = NULL;
-
- HeapTuple tuple;
- Datum result;
-
- if (SRF_IS_FIRSTCALL()) {
- MemoryContext oldcontext;
-
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- rt_band band = NULL;
-
- ArrayType *array;
- Oid etype;
- Datum *e;
- bool *nulls;
- int16 typlen;
- bool typbyval;
- char typalign;
- int i = 0;
- int j = 0;
- int n = 0;
-
- uint32_t numBands;
- uint32_t idx = 1;
- uint32_t *bandNums = NULL;
- const char *tmp = NULL;
-
- POSTGIS_RT_DEBUG(3, "RASTER_bandmetadata: Starting");
-
- /* 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);
-
- /* pgraster is null, return null */
- if (PG_ARGISNULL(0)) {
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
-
- /* raster */
- raster = rt_raster_deserialize(pgraster, FALSE);
- if (!raster) {
- PG_FREE_IF_COPY(pgraster, 0);
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_bandmetadata: Could not deserialize raster");
- SRF_RETURN_DONE(funcctx);
- }
-
- /* numbands */
- numBands = rt_raster_get_num_bands(raster);
- if (numBands < 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 */
- array = PG_GETARG_ARRAYTYPE_P(1);
- etype = ARR_ELEMTYPE(array);
- get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
-
- switch (etype) {
- case INT2OID:
- case INT4OID:
- break;
- default:
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_bandmetadata: Invalid data type for band number(s)");
- SRF_RETURN_DONE(funcctx);
- break;
- }
-
- deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
- &nulls, &n);
-
- bandNums = palloc(sizeof(uint32_t) * n);
- for (i = 0, j = 0; i < n; i++) {
- if (nulls[i]) continue;
-
- switch (etype) {
- case INT2OID:
- idx = (uint32_t) DatumGetInt16(e[i]);
- break;
- case INT4OID:
- idx = (uint32_t) DatumGetInt32(e[i]);
- break;
- }
-
- POSTGIS_RT_DEBUGF(3, "band idx (before): %d", idx);
- if (idx > numBands || idx < 1) {
- elog(NOTICE, "Invalid band index: %d. Indices must be 1-based. Returning NULL", idx);
- pfree(bandNums);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- bandNums[j] = idx;
- POSTGIS_RT_DEBUGF(3, "bandNums[%d] = %d", j, bandNums[j]);
- j++;
- }
-
- if (j < 1) {
- j = numBands;
- bandNums = repalloc(bandNums, sizeof(uint32_t) * j);
- for (i = 0; i < j; i++)
- bandNums[i] = i + 1;
- }
- else if (j < n)
- bandNums = repalloc(bandNums, sizeof(uint32_t) * j);
-
- bmd = (struct bandmetadata *) palloc(sizeof(struct bandmetadata) * j);
-
- for (i = 0; i < j; i++) {
- band = rt_raster_get_band(raster, bandNums[i] - 1);
- if (NULL == band) {
- elog(NOTICE, "Could not get raster band at index %d", bandNums[i]);
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- MemoryContextSwitchTo(oldcontext);
- SRF_RETURN_DONE(funcctx);
- }
-
- /* bandnum */
- bmd[i].bandnum = bandNums[i];
-
- /* pixeltype */
- tmp = rt_pixtype_name(rt_band_get_pixtype(band));
- bmd[i].pixeltype = palloc(sizeof(char) * (strlen(tmp) + 1));
- strncpy(bmd[i].pixeltype, tmp, strlen(tmp) + 1);
-
- /* hasnodatavalue */
- if (rt_band_get_hasnodata_flag(band))
- bmd[i].hasnodata = TRUE;
- else
- bmd[i].hasnodata = FALSE;
-
- /* nodatavalue */
- if (bmd[i].hasnodata)
- rt_band_get_nodata(band, &(bmd[i].nodataval));
- else
- bmd[i].nodataval = 0;
-
- /* path */
- tmp = rt_band_get_ext_path(band);
- if (tmp) {
- bmd[i].bandpath = palloc(sizeof(char) * (strlen(tmp) + 1));
- strncpy(bmd[i].bandpath, tmp, strlen(tmp) + 1);
- }
- else
- bmd[i].bandpath = NULL;
-
- /* isoutdb */
- bmd[i].isoutdb = bmd[i].bandpath ? TRUE : FALSE;
-
- rt_band_destroy(band);
- }
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- /* Store needed information */
- funcctx->user_fctx = bmd;
-
- /* total number of tuples to be returned */
- funcctx->max_calls = j;
-
- /* 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;
- bmd2 = funcctx->user_fctx;
-
- /* do when there is more left to send */
- if (call_cntr < max_calls) {
- int values_length = 5;
- Datum values[values_length];
- bool nulls[values_length];
-
- memset(nulls, FALSE, sizeof(bool) * values_length);
-
- values[0] = UInt32GetDatum(bmd2[call_cntr].bandnum);
- values[1] = CStringGetTextDatum(bmd2[call_cntr].pixeltype);
-
- if (bmd2[call_cntr].hasnodata)
- values[2] = Float8GetDatum(bmd2[call_cntr].nodataval);
- else
- nulls[2] = TRUE;
-
- values[3] = BoolGetDatum(bmd2[call_cntr].isoutdb);
- if (bmd2[call_cntr].bandpath && strlen(bmd2[call_cntr].bandpath))
- values[4] = CStringGetTextDatum(bmd2[call_cntr].bandpath);
- else
- nulls[4] = TRUE;
-
- /* build a tuple */
- tuple = heap_form_tuple(tupdesc, values, nulls);
-
- /* make the tuple into a datum */
- result = HeapTupleGetDatum(tuple);
-
- /* clean up */
- pfree(bmd2[call_cntr].pixeltype);
- if (bmd2[call_cntr].bandpath) pfree(bmd2[call_cntr].bandpath);
-
- SRF_RETURN_NEXT(funcctx, result);
- }
- /* do when there is no more left */
- else {
- pfree(bmd2);
- SRF_RETURN_DONE(funcctx);
- }
-}
-
-PG_FUNCTION_INFO_V1(RASTER_rasterToWorldCoord);
-Datum RASTER_rasterToWorldCoord(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- int i = 0;
- int cr[2] = {0};
- bool skewed[2] = {false, false};
- double cw[2] = {0};
-
- TupleDesc tupdesc;
- int values_length = 2;
- Datum values[values_length];
- bool nulls[values_length];
- HeapTuple tuple;
- Datum result;
-
- POSTGIS_RT_DEBUG(3, "RASTER_rasterToWorldCoord: Starting");
-
- /* pgraster is null, return null */
- if (PG_ARGISNULL(0))
- PG_RETURN_NULL();
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
-
- /* raster */
- raster = rt_raster_deserialize(pgraster, TRUE);
- if (!raster) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_rasterToWorldCoord: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- /* raster skewed? */
- skewed[0] = FLT_NEQ(rt_raster_get_x_skew(raster), 0) ? true : false;
- skewed[1] = FLT_NEQ(rt_raster_get_y_skew(raster), 0) ? true : false;
-
- /* column and row */
- for (i = 1; i <= 2; i++) {
- if (PG_ARGISNULL(i)) {
- /* if skewed on same axis, parameter is required */
- if (skewed[i - 1]) {
- elog(NOTICE, "Pixel row and column required for computing longitude and latitude of a rotated raster");
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- PG_RETURN_NULL();
- }
-
- continue;
- }
-
- cr[i - 1] = PG_GETARG_INT32(i);
- }
-
- /* user-provided value is 1-based but needs to be 0-based */
- if (rt_raster_cell_to_geopoint(
- raster,
- (double) cr[0] - 1, (double) cr[1] - 1,
- &(cw[0]), &(cw[1]),
- NULL
- ) != ES_NONE) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_rasterToWorldCoord: Could not compute longitude and latitude from pixel row and column");
- PG_RETURN_NULL();
- }
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- /* 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);
-
- values[0] = Float8GetDatum(cw[0]);
- values[1] = Float8GetDatum(cw[1]);
-
- memset(nulls, FALSE, sizeof(bool) * values_length);
-
- /* build a tuple */
- tuple = heap_form_tuple(tupdesc, values, nulls);
-
- /* make the tuple into a datum */
- result = HeapTupleGetDatum(tuple);
-
- PG_RETURN_DATUM(result);
-}
-
-PG_FUNCTION_INFO_V1(RASTER_worldToRasterCoord);
-Datum RASTER_worldToRasterCoord(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- int i = 0;
- double cw[2] = {0};
- double _cr[2] = {0};
- int cr[2] = {0};
- bool skewed = false;
-
- TupleDesc tupdesc;
- int values_length = 2;
- Datum values[values_length];
- bool nulls[values_length];
- HeapTuple tuple;
- Datum result;
-
- POSTGIS_RT_DEBUG(3, "RASTER_worldToRasterCoord: Starting");
-
- /* pgraster is null, return null */
- if (PG_ARGISNULL(0))
- PG_RETURN_NULL();
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
-
- /* raster */
- raster = rt_raster_deserialize(pgraster, TRUE);
- if (!raster) {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_worldToRasterCoord: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- /* raster skewed? */
- skewed = FLT_NEQ(rt_raster_get_x_skew(raster), 0) ? true : false;
- if (!skewed)
- skewed = FLT_NEQ(rt_raster_get_y_skew(raster), 0) ? true : false;
-
- /* longitude and latitude */
- for (i = 1; i <= 2; i++) {
- if (PG_ARGISNULL(i)) {
- /* if skewed, parameter is required */
- if (skewed) {
- elog(NOTICE, "Latitude and longitude required for computing pixel row and column of a rotated raster");
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- PG_RETURN_NULL();
- }
-
- continue;
- }
-
- cw[i - 1] = PG_GETARG_FLOAT8(i);
- }
-
- /* return pixel row and column values are 0-based */
- if (rt_raster_geopoint_to_cell(
- raster,
- cw[0], cw[1],
- &(_cr[0]), &(_cr[1]),
- NULL
- ) != ES_NONE) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_worldToRasterCoord: Could not compute pixel row and column from longitude and latitude");
- PG_RETURN_NULL();
- }
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- /* force to integer and add one to make 1-based */
- cr[0] = ((int) _cr[0]) + 1;
- cr[1] = ((int) _cr[1]) + 1;
-
- /* 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);
-
- values[0] = Int32GetDatum(cr[0]);
- values[1] = Int32GetDatum(cr[1]);
-
- memset(nulls, FALSE, sizeof(bool) * values_length);
-
- /* build a tuple */
- tuple = heap_form_tuple(tupdesc, values, nulls);
-
- /* make the tuple into a datum */
- result = HeapTupleGetDatum(tuple);
-
- PG_RETURN_DATUM(result);
-}
-
-/**
- * See if two rasters intersect
- */
-PG_FUNCTION_INFO_V1(RASTER_intersects);
-Datum RASTER_intersects(PG_FUNCTION_ARGS)
-{
- const int set_count = 2;
- rt_pgraster *pgrast[2];
- int pgrastpos[2] = {-1, -1};
- rt_raster rast[2] = {NULL};
- uint32_t bandindex[2] = {0};
- uint32_t hasbandindex[2] = {0};
-
- uint32_t i;
- uint32_t j;
- uint32_t k;
- uint32_t numBands;
- int rtn;
- int result;
-
- for (i = 0, j = 0; i < set_count; i++) {
- /* pgrast is null, return null */
- if (PG_ARGISNULL(j)) {
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
- pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(j));
- pgrastpos[i] = j;
- j++;
-
- /* raster */
- rast[i] = rt_raster_deserialize(pgrast[i], FALSE);
- if (!rast[i]) {
- for (k = 0; k <= i; k++) {
- if (k < i)
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "RASTER_intersects: Could not deserialize the %s raster", i < 1 ? "first" : "second");
- PG_RETURN_NULL();
- }
-
- /* numbands */
- numBands = rt_raster_get_num_bands(rast[i]);
- if (numBands < 1) {
- elog(NOTICE, "The %s raster provided has no bands", i < 1 ? "first" : "second");
- if (i > 0) i++;
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- /* band index */
- if (!PG_ARGISNULL(j)) {
- bandindex[i] = PG_GETARG_INT32(j);
- if (bandindex[i] < 1 || bandindex[i] > numBands) {
- elog(NOTICE, "Invalid band index (must use 1-based) for the %s raster. Returning NULL", i < 1 ? "first" : "second");
- if (i > 0) i++;
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
- hasbandindex[i] = 1;
- }
- else
- hasbandindex[i] = 0;
- POSTGIS_RT_DEBUGF(4, "hasbandindex[%d] = %d", i, hasbandindex[i]);
- POSTGIS_RT_DEBUGF(4, "bandindex[%d] = %d", i, bandindex[i]);
- j++;
- }
-
- /* hasbandindex must be balanced */
- if (
- (hasbandindex[0] && !hasbandindex[1]) ||
- (!hasbandindex[0] && hasbandindex[1])
- ) {
- elog(NOTICE, "Missing band index. Band indices must be provided for both rasters if any one is provided");
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- /* SRID must match */
- if (rt_raster_get_srid(rast[0]) != rt_raster_get_srid(rast[1])) {
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "The two rasters provided have different SRIDs");
- PG_RETURN_NULL();
- }
-
- rtn = rt_raster_intersects(
- rast[0], (hasbandindex[0] ? bandindex[0] - 1 : -1),
- rast[1], (hasbandindex[1] ? bandindex[1] - 1 : -1),
- &result
- );
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
-
- if (rtn != ES_NONE) {
- elog(ERROR, "RASTER_intersects: Could not test for intersection on the two rasters");
- PG_RETURN_NULL();
- }
-
- PG_RETURN_BOOL(result);
-}
-
-/**
- * See if two rasters overlap
- */
-PG_FUNCTION_INFO_V1(RASTER_overlaps);
-Datum RASTER_overlaps(PG_FUNCTION_ARGS)
-{
- const int set_count = 2;
- rt_pgraster *pgrast[2];
- int pgrastpos[2] = {-1, -1};
- rt_raster rast[2] = {NULL};
- uint32_t bandindex[2] = {0};
- uint32_t hasbandindex[2] = {0};
-
- uint32_t i;
- uint32_t j;
- uint32_t k;
- uint32_t numBands;
- int rtn;
- int result;
-
- for (i = 0, j = 0; i < set_count; i++) {
- /* pgrast is null, return null */
- if (PG_ARGISNULL(j)) {
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
- pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(j));
- pgrastpos[i] = j;
- j++;
-
- /* raster */
- rast[i] = rt_raster_deserialize(pgrast[i], FALSE);
- if (!rast[i]) {
- for (k = 0; k <= i; k++) {
- if (k < i)
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "RASTER_overlaps: Could not deserialize the %s raster", i < 1 ? "first" : "second");
- PG_RETURN_NULL();
- }
-
- /* numbands */
- numBands = rt_raster_get_num_bands(rast[i]);
- if (numBands < 1) {
- elog(NOTICE, "The %s raster provided has no bands", i < 1 ? "first" : "second");
- if (i > 0) i++;
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- /* band index */
- if (!PG_ARGISNULL(j)) {
- bandindex[i] = PG_GETARG_INT32(j);
- if (bandindex[i] < 1 || bandindex[i] > numBands) {
- elog(NOTICE, "Invalid band index (must use 1-based) for the %s raster. Returning NULL", i < 1 ? "first" : "second");
- if (i > 0) i++;
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
- hasbandindex[i] = 1;
- }
- else
- hasbandindex[i] = 0;
- POSTGIS_RT_DEBUGF(4, "hasbandindex[%d] = %d", i, hasbandindex[i]);
- POSTGIS_RT_DEBUGF(4, "bandindex[%d] = %d", i, bandindex[i]);
- j++;
- }
-
- /* hasbandindex must be balanced */
- if (
- (hasbandindex[0] && !hasbandindex[1]) ||
- (!hasbandindex[0] && hasbandindex[1])
- ) {
- elog(NOTICE, "Missing band index. Band indices must be provided for both rasters if any one is provided");
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- /* SRID must match */
- if (rt_raster_get_srid(rast[0]) != rt_raster_get_srid(rast[1])) {
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "The two rasters provided have different SRIDs");
- PG_RETURN_NULL();
- }
-
- rtn = rt_raster_overlaps(
- rast[0], (hasbandindex[0] ? bandindex[0] - 1 : -1),
- rast[1], (hasbandindex[1] ? bandindex[1] - 1 : -1),
- &result
- );
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
-
- if (rtn != ES_NONE) {
- elog(ERROR, "RASTER_overlaps: Could not test for overlap on the two rasters");
- PG_RETURN_NULL();
- }
-
- PG_RETURN_BOOL(result);
-}
-
-/**
- * See if two rasters touch
- */
-PG_FUNCTION_INFO_V1(RASTER_touches);
-Datum RASTER_touches(PG_FUNCTION_ARGS)
-{
- const int set_count = 2;
- rt_pgraster *pgrast[2];
- int pgrastpos[2] = {-1, -1};
- rt_raster rast[2] = {NULL};
- uint32_t bandindex[2] = {0};
- uint32_t hasbandindex[2] = {0};
-
- uint32_t i;
- uint32_t j;
- uint32_t k;
- uint32_t numBands;
- int rtn;
- int result;
-
- for (i = 0, j = 0; i < set_count; i++) {
- /* pgrast is null, return null */
- if (PG_ARGISNULL(j)) {
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
- pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(j));
- pgrastpos[i] = j;
- j++;
-
- /* raster */
- rast[i] = rt_raster_deserialize(pgrast[i], FALSE);
- if (!rast[i]) {
- for (k = 0; k <= i; k++) {
- if (k < i)
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "RASTER_touches: Could not deserialize the %s raster", i < 1 ? "first" : "second");
- PG_RETURN_NULL();
- }
-
- /* numbands */
- numBands = rt_raster_get_num_bands(rast[i]);
- if (numBands < 1) {
- elog(NOTICE, "The %s raster provided has no bands", i < 1 ? "first" : "second");
- if (i > 0) i++;
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- /* band index */
- if (!PG_ARGISNULL(j)) {
- bandindex[i] = PG_GETARG_INT32(j);
- if (bandindex[i] < 1 || bandindex[i] > numBands) {
- elog(NOTICE, "Invalid band index (must use 1-based) for the %s raster. Returning NULL", i < 1 ? "first" : "second");
- if (i > 0) i++;
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
- hasbandindex[i] = 1;
- }
- else
- hasbandindex[i] = 0;
- POSTGIS_RT_DEBUGF(4, "hasbandindex[%d] = %d", i, hasbandindex[i]);
- POSTGIS_RT_DEBUGF(4, "bandindex[%d] = %d", i, bandindex[i]);
- j++;
- }
-
- /* hasbandindex must be balanced */
- if (
- (hasbandindex[0] && !hasbandindex[1]) ||
- (!hasbandindex[0] && hasbandindex[1])
- ) {
- elog(NOTICE, "Missing band index. Band indices must be provided for both rasters if any one is provided");
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- /* SRID must match */
- if (rt_raster_get_srid(rast[0]) != rt_raster_get_srid(rast[1])) {
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "The two rasters provided have different SRIDs");
- PG_RETURN_NULL();
- }
-
- rtn = rt_raster_touches(
- rast[0], (hasbandindex[0] ? bandindex[0] - 1 : -1),
- rast[1], (hasbandindex[1] ? bandindex[1] - 1 : -1),
- &result
- );
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
-
- if (rtn != ES_NONE) {
- elog(ERROR, "RASTER_touches: Could not test for touch on the two rasters");
- PG_RETURN_NULL();
- }
-
- PG_RETURN_BOOL(result);
-}
-
-/**
- * See if the first raster contains the second raster
- */
-PG_FUNCTION_INFO_V1(RASTER_contains);
-Datum RASTER_contains(PG_FUNCTION_ARGS)
-{
- const int set_count = 2;
- rt_pgraster *pgrast[2];
- int pgrastpos[2] = {-1, -1};
- rt_raster rast[2] = {NULL};
- uint32_t bandindex[2] = {0};
- uint32_t hasbandindex[2] = {0};
-
- uint32_t i;
- uint32_t j;
- uint32_t k;
- uint32_t numBands;
- int rtn;
- int result;
-
- for (i = 0, j = 0; i < set_count; i++) {
- /* pgrast is null, return null */
- if (PG_ARGISNULL(j)) {
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
- pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(j));
- pgrastpos[i] = j;
- j++;
-
- /* raster */
- rast[i] = rt_raster_deserialize(pgrast[i], FALSE);
- if (!rast[i]) {
- for (k = 0; k <= i; k++) {
- if (k < i)
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "RASTER_contains: Could not deserialize the %s raster", i < 1 ? "first" : "second");
- PG_RETURN_NULL();
- }
-
- /* numbands */
- numBands = rt_raster_get_num_bands(rast[i]);
- if (numBands < 1) {
- elog(NOTICE, "The %s raster provided has no bands", i < 1 ? "first" : "second");
- if (i > 0) i++;
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- /* band index */
- if (!PG_ARGISNULL(j)) {
- bandindex[i] = PG_GETARG_INT32(j);
- if (bandindex[i] < 1 || bandindex[i] > numBands) {
- elog(NOTICE, "Invalid band index (must use 1-based) for the %s raster. Returning NULL", i < 1 ? "first" : "second");
- if (i > 0) i++;
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
- hasbandindex[i] = 1;
- }
- else
- hasbandindex[i] = 0;
- POSTGIS_RT_DEBUGF(4, "hasbandindex[%d] = %d", i, hasbandindex[i]);
- POSTGIS_RT_DEBUGF(4, "bandindex[%d] = %d", i, bandindex[i]);
- j++;
- }
-
- /* hasbandindex must be balanced */
- if (
- (hasbandindex[0] && !hasbandindex[1]) ||
- (!hasbandindex[0] && hasbandindex[1])
- ) {
- elog(NOTICE, "Missing band index. Band indices must be provided for both rasters if any one is provided");
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- /* SRID must match */
- if (rt_raster_get_srid(rast[0]) != rt_raster_get_srid(rast[1])) {
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "The two rasters provided have different SRIDs");
- PG_RETURN_NULL();
- }
-
- rtn = rt_raster_contains(
- rast[0], (hasbandindex[0] ? bandindex[0] - 1 : -1),
- rast[1], (hasbandindex[1] ? bandindex[1] - 1 : -1),
- &result
- );
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
-
- if (rtn != ES_NONE) {
- elog(ERROR, "RASTER_contains: Could not test that the first raster contains the second raster");
- PG_RETURN_NULL();
- }
-
- PG_RETURN_BOOL(result);
-}
-
-/**
- * See if the first raster contains properly the second raster
- */
-PG_FUNCTION_INFO_V1(RASTER_containsProperly);
-Datum RASTER_containsProperly(PG_FUNCTION_ARGS)
-{
- const int set_count = 2;
- rt_pgraster *pgrast[2];
- int pgrastpos[2] = {-1, -1};
- rt_raster rast[2] = {NULL};
- uint32_t bandindex[2] = {0};
- uint32_t hasbandindex[2] = {0};
-
- uint32_t i;
- uint32_t j;
- uint32_t k;
- uint32_t numBands;
- int rtn;
- int result;
-
- for (i = 0, j = 0; i < set_count; i++) {
- /* pgrast is null, return null */
- if (PG_ARGISNULL(j)) {
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
- pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(j));
- pgrastpos[i] = j;
- j++;
-
- /* raster */
- rast[i] = rt_raster_deserialize(pgrast[i], FALSE);
- if (!rast[i]) {
- for (k = 0; k <= i; k++) {
- if (k < i)
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "RASTER_containsProperly: Could not deserialize the %s raster", i < 1 ? "first" : "second");
- PG_RETURN_NULL();
- }
-
- /* numbands */
- numBands = rt_raster_get_num_bands(rast[i]);
- if (numBands < 1) {
- elog(NOTICE, "The %s raster provided has no bands", i < 1 ? "first" : "second");
- if (i > 0) i++;
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- /* band index */
- if (!PG_ARGISNULL(j)) {
- bandindex[i] = PG_GETARG_INT32(j);
- if (bandindex[i] < 1 || bandindex[i] > numBands) {
- elog(NOTICE, "Invalid band index (must use 1-based) for the %s raster. Returning NULL", i < 1 ? "first" : "second");
- if (i > 0) i++;
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
- hasbandindex[i] = 1;
- }
- else
- hasbandindex[i] = 0;
- POSTGIS_RT_DEBUGF(4, "hasbandindex[%d] = %d", i, hasbandindex[i]);
- POSTGIS_RT_DEBUGF(4, "bandindex[%d] = %d", i, bandindex[i]);
- j++;
- }
-
- /* hasbandindex must be balanced */
- if (
- (hasbandindex[0] && !hasbandindex[1]) ||
- (!hasbandindex[0] && hasbandindex[1])
- ) {
- elog(NOTICE, "Missing band index. Band indices must be provided for both rasters if any one is provided");
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- /* SRID must match */
- if (rt_raster_get_srid(rast[0]) != rt_raster_get_srid(rast[1])) {
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "The two rasters provided have different SRIDs");
- PG_RETURN_NULL();
- }
-
- rtn = rt_raster_contains_properly(
- rast[0], (hasbandindex[0] ? bandindex[0] - 1 : -1),
- rast[1], (hasbandindex[1] ? bandindex[1] - 1 : -1),
- &result
- );
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
-
- if (rtn != ES_NONE) {
- elog(ERROR, "RASTER_containsProperly: Could not test that the first raster contains properly the second raster");
- PG_RETURN_NULL();
- }
-
- PG_RETURN_BOOL(result);
-}
-
-/**
- * See if the first raster covers the second raster
- */
-PG_FUNCTION_INFO_V1(RASTER_covers);
-Datum RASTER_covers(PG_FUNCTION_ARGS)
-{
- const int set_count = 2;
- rt_pgraster *pgrast[2];
- int pgrastpos[2] = {-1, -1};
- rt_raster rast[2] = {NULL};
- uint32_t bandindex[2] = {0};
- uint32_t hasbandindex[2] = {0};
-
- uint32_t i;
- uint32_t j;
- uint32_t k;
- uint32_t numBands;
- int rtn;
- int result;
-
- for (i = 0, j = 0; i < set_count; i++) {
- /* pgrast is null, return null */
- if (PG_ARGISNULL(j)) {
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
- pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(j));
- pgrastpos[i] = j;
- j++;
-
- /* raster */
- rast[i] = rt_raster_deserialize(pgrast[i], FALSE);
- if (!rast[i]) {
- for (k = 0; k <= i; k++) {
- if (k < i)
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "RASTER_covers: Could not deserialize the %s raster", i < 1 ? "first" : "second");
- PG_RETURN_NULL();
- }
-
- /* numbands */
- numBands = rt_raster_get_num_bands(rast[i]);
- if (numBands < 1) {
- elog(NOTICE, "The %s raster provided has no bands", i < 1 ? "first" : "second");
- if (i > 0) i++;
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- /* band index */
- if (!PG_ARGISNULL(j)) {
- bandindex[i] = PG_GETARG_INT32(j);
- if (bandindex[i] < 1 || bandindex[i] > numBands) {
- elog(NOTICE, "Invalid band index (must use 1-based) for the %s raster. Returning NULL", i < 1 ? "first" : "second");
- if (i > 0) i++;
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
- hasbandindex[i] = 1;
- }
- else
- hasbandindex[i] = 0;
- POSTGIS_RT_DEBUGF(4, "hasbandindex[%d] = %d", i, hasbandindex[i]);
- POSTGIS_RT_DEBUGF(4, "bandindex[%d] = %d", i, bandindex[i]);
- j++;
- }
-
- /* hasbandindex must be balanced */
- if (
- (hasbandindex[0] && !hasbandindex[1]) ||
- (!hasbandindex[0] && hasbandindex[1])
- ) {
- elog(NOTICE, "Missing band index. Band indices must be provided for both rasters if any one is provided");
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- /* SRID must match */
- if (rt_raster_get_srid(rast[0]) != rt_raster_get_srid(rast[1])) {
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "The two rasters provided have different SRIDs");
- PG_RETURN_NULL();
- }
-
- rtn = rt_raster_covers(
- rast[0], (hasbandindex[0] ? bandindex[0] - 1 : -1),
- rast[1], (hasbandindex[1] ? bandindex[1] - 1 : -1),
- &result
- );
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
-
- if (rtn != ES_NONE) {
- elog(ERROR, "RASTER_covers: Could not test that the first raster covers the second raster");
- PG_RETURN_NULL();
- }
-
- PG_RETURN_BOOL(result);
-}
-
-/**
- * See if the first raster is covered by the second raster
- */
-PG_FUNCTION_INFO_V1(RASTER_coveredby);
-Datum RASTER_coveredby(PG_FUNCTION_ARGS)
-{
- const int set_count = 2;
- rt_pgraster *pgrast[2];
- int pgrastpos[2] = {-1, -1};
- rt_raster rast[2] = {NULL};
- uint32_t bandindex[2] = {0};
- uint32_t hasbandindex[2] = {0};
-
- uint32_t i;
- uint32_t j;
- uint32_t k;
- uint32_t numBands;
- int rtn;
- int result;
-
- for (i = 0, j = 0; i < set_count; i++) {
- /* pgrast is null, return null */
- if (PG_ARGISNULL(j)) {
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
- pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(j));
- pgrastpos[i] = j;
- j++;
-
- /* raster */
- rast[i] = rt_raster_deserialize(pgrast[i], FALSE);
- if (!rast[i]) {
- for (k = 0; k <= i; k++) {
- if (k < i)
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "RASTER_coveredby: Could not deserialize the %s raster", i < 1 ? "first" : "second");
- PG_RETURN_NULL();
- }
-
- /* numbands */
- numBands = rt_raster_get_num_bands(rast[i]);
- if (numBands < 1) {
- elog(NOTICE, "The %s raster provided has no bands", i < 1 ? "first" : "second");
- if (i > 0) i++;
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- /* band index */
- if (!PG_ARGISNULL(j)) {
- bandindex[i] = PG_GETARG_INT32(j);
- if (bandindex[i] < 1 || bandindex[i] > numBands) {
- elog(NOTICE, "Invalid band index (must use 1-based) for the %s raster. Returning NULL", i < 1 ? "first" : "second");
- if (i > 0) i++;
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
- hasbandindex[i] = 1;
- }
- else
- hasbandindex[i] = 0;
- POSTGIS_RT_DEBUGF(4, "hasbandindex[%d] = %d", i, hasbandindex[i]);
- POSTGIS_RT_DEBUGF(4, "bandindex[%d] = %d", i, bandindex[i]);
- j++;
- }
-
- /* hasbandindex must be balanced */
- if (
- (hasbandindex[0] && !hasbandindex[1]) ||
- (!hasbandindex[0] && hasbandindex[1])
- ) {
- elog(NOTICE, "Missing band index. Band indices must be provided for both rasters if any one is provided");
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- /* SRID must match */
- if (rt_raster_get_srid(rast[0]) != rt_raster_get_srid(rast[1])) {
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "The two rasters provided have different SRIDs");
- PG_RETURN_NULL();
- }
-
- rtn = rt_raster_coveredby(
- rast[0], (hasbandindex[0] ? bandindex[0] - 1 : -1),
- rast[1], (hasbandindex[1] ? bandindex[1] - 1 : -1),
- &result
- );
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
-
- if (rtn != ES_NONE) {
- elog(ERROR, "RASTER_coveredby: Could not test that the first raster is covered by the second raster");
- PG_RETURN_NULL();
- }
-
- PG_RETURN_BOOL(result);
-}
-
-/**
- * See if the two rasters are within the specified distance of each other
- */
-PG_FUNCTION_INFO_V1(RASTER_dwithin);
-Datum RASTER_dwithin(PG_FUNCTION_ARGS)
-{
- const int set_count = 2;
- rt_pgraster *pgrast[2];
- int pgrastpos[2] = {-1, -1};
- rt_raster rast[2] = {NULL};
- uint32_t bandindex[2] = {0};
- uint32_t hasbandindex[2] = {0};
- double distance = 0;
-
- uint32_t i;
- uint32_t j;
- uint32_t k;
- uint32_t numBands;
- int rtn;
- int result;
-
- for (i = 0, j = 0; i < set_count; i++) {
- /* pgrast is null, return null */
- if (PG_ARGISNULL(j)) {
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
- pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(j));
- pgrastpos[i] = j;
- j++;
-
- /* raster */
- rast[i] = rt_raster_deserialize(pgrast[i], FALSE);
- if (!rast[i]) {
- for (k = 0; k <= i; k++) {
- if (k < i)
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "RASTER_dwithin: Could not deserialize the %s raster", i < 1 ? "first" : "second");
- PG_RETURN_NULL();
- }
-
- /* numbands */
- numBands = rt_raster_get_num_bands(rast[i]);
- if (numBands < 1) {
- elog(NOTICE, "The %s raster provided has no bands", i < 1 ? "first" : "second");
- if (i > 0) i++;
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- /* band index */
- if (!PG_ARGISNULL(j)) {
- bandindex[i] = PG_GETARG_INT32(j);
- if (bandindex[i] < 1 || bandindex[i] > numBands) {
- elog(NOTICE, "Invalid band index (must use 1-based) for the %s raster. Returning NULL", i < 1 ? "first" : "second");
- if (i > 0) i++;
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
- hasbandindex[i] = 1;
- }
- else
- hasbandindex[i] = 0;
- POSTGIS_RT_DEBUGF(4, "hasbandindex[%d] = %d", i, hasbandindex[i]);
- POSTGIS_RT_DEBUGF(4, "bandindex[%d] = %d", i, bandindex[i]);
- j++;
- }
-
- /* distance */
- if (PG_ARGISNULL(4)) {
- elog(NOTICE, "Distance cannot be NULL. Returning NULL");
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- distance = PG_GETARG_FLOAT8(4);
- if (distance < 0) {
- elog(NOTICE, "Distance cannot be less than zero. Returning NULL");
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- /* hasbandindex must be balanced */
- if (
- (hasbandindex[0] && !hasbandindex[1]) ||
- (!hasbandindex[0] && hasbandindex[1])
- ) {
- elog(NOTICE, "Missing band index. Band indices must be provided for both rasters if any one is provided");
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- /* SRID must match */
- if (rt_raster_get_srid(rast[0]) != rt_raster_get_srid(rast[1])) {
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "The two rasters provided have different SRIDs");
- PG_RETURN_NULL();
- }
-
- rtn = rt_raster_within_distance(
- rast[0], (hasbandindex[0] ? bandindex[0] - 1 : -1),
- rast[1], (hasbandindex[1] ? bandindex[1] - 1 : -1),
- distance,
- &result
- );
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
-
- if (rtn != ES_NONE) {
- elog(ERROR, "RASTER_dwithin: Could not test that the two rasters are within the specified distance of each other");
- PG_RETURN_NULL();
- }
-
- PG_RETURN_BOOL(result);
-}
-
-/**
- * See if the two rasters are fully within the specified distance of each other
- */
-PG_FUNCTION_INFO_V1(RASTER_dfullywithin);
-Datum RASTER_dfullywithin(PG_FUNCTION_ARGS)
-{
- const int set_count = 2;
- rt_pgraster *pgrast[2];
- int pgrastpos[2] = {-1, -1};
- rt_raster rast[2] = {NULL};
- uint32_t bandindex[2] = {0};
- uint32_t hasbandindex[2] = {0};
- double distance = 0;
-
- uint32_t i;
- uint32_t j;
- uint32_t k;
- uint32_t numBands;
- int rtn;
- int result;
-
- for (i = 0, j = 0; i < set_count; i++) {
- /* pgrast is null, return null */
- if (PG_ARGISNULL(j)) {
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
- pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(j));
- pgrastpos[i] = j;
- j++;
-
- /* raster */
- rast[i] = rt_raster_deserialize(pgrast[i], FALSE);
- if (!rast[i]) {
- for (k = 0; k <= i; k++) {
- if (k < i)
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "RASTER_dfullywithin: Could not deserialize the %s raster", i < 1 ? "first" : "second");
- PG_RETURN_NULL();
- }
-
- /* numbands */
- numBands = rt_raster_get_num_bands(rast[i]);
- if (numBands < 1) {
- elog(NOTICE, "The %s raster provided has no bands", i < 1 ? "first" : "second");
- if (i > 0) i++;
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- /* band index */
- if (!PG_ARGISNULL(j)) {
- bandindex[i] = PG_GETARG_INT32(j);
- if (bandindex[i] < 1 || bandindex[i] > numBands) {
- elog(NOTICE, "Invalid band index (must use 1-based) for the %s raster. Returning NULL", i < 1 ? "first" : "second");
- if (i > 0) i++;
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
- hasbandindex[i] = 1;
- }
- else
- hasbandindex[i] = 0;
- POSTGIS_RT_DEBUGF(4, "hasbandindex[%d] = %d", i, hasbandindex[i]);
- POSTGIS_RT_DEBUGF(4, "bandindex[%d] = %d", i, bandindex[i]);
- j++;
- }
-
- /* distance */
- if (PG_ARGISNULL(4)) {
- elog(NOTICE, "Distance cannot be NULL. Returning NULL");
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- distance = PG_GETARG_FLOAT8(4);
- if (distance < 0) {
- elog(NOTICE, "Distance cannot be less than zero. Returning NULL");
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- /* hasbandindex must be balanced */
- if (
- (hasbandindex[0] && !hasbandindex[1]) ||
- (!hasbandindex[0] && hasbandindex[1])
- ) {
- elog(NOTICE, "Missing band index. Band indices must be provided for both rasters if any one is provided");
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- /* SRID must match */
- if (rt_raster_get_srid(rast[0]) != rt_raster_get_srid(rast[1])) {
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "The two rasters provided have different SRIDs");
- PG_RETURN_NULL();
- }
-
- rtn = rt_raster_fully_within_distance(
- rast[0], (hasbandindex[0] ? bandindex[0] - 1 : -1),
- rast[1], (hasbandindex[1] ? bandindex[1] - 1 : -1),
- distance,
- &result
- );
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
-
- if (rtn != ES_NONE) {
- elog(ERROR, "RASTER_dfullywithin: Could not test that the two rasters are fully within the specified distance of each other");
- PG_RETURN_NULL();
- }
-
- PG_RETURN_BOOL(result);
-}
-
-/**
- * See if two rasters are aligned
- */
-PG_FUNCTION_INFO_V1(RASTER_sameAlignment);
-Datum RASTER_sameAlignment(PG_FUNCTION_ARGS)
-{
- const int set_count = 2;
- rt_pgraster *pgrast[2];
- int pgrastpos[2] = {-1, -1};
- rt_raster rast[2] = {NULL};
-
- uint32_t i;
- uint32_t j;
- uint32_t k;
- int rtn;
- int aligned = 0;
- char *reason = NULL;
-
- for (i = 0, j = 0; i < set_count; i++) {
- /* pgrast is null, return null */
- if (PG_ARGISNULL(j)) {
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
- pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(j), 0, sizeof(struct rt_raster_serialized_t));
- pgrastpos[i] = j;
- j++;
-
- /* raster */
- rast[i] = rt_raster_deserialize(pgrast[i], TRUE);
- if (!rast[i]) {
- for (k = 0; k <= i; k++) {
- if (k < i)
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "RASTER_sameAlignment: Could not deserialize the %s raster", i < 1 ? "first" : "second");
- PG_RETURN_NULL();
- }
- }
-
- rtn = rt_raster_same_alignment(
- rast[0],
- rast[1],
- &aligned,
- &reason
- );
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
-
- if (rtn != ES_NONE) {
- elog(ERROR, "RASTER_sameAlignment: Could not test for alignment on the two rasters");
- PG_RETURN_NULL();
- }
-
- /* only output reason if not aligned */
- if (reason != NULL && !aligned)
- elog(NOTICE, "%s", reason);
-
- PG_RETURN_BOOL(aligned);
-}
-
-/**
- * Return a reason why two rasters are not aligned
- */
-PG_FUNCTION_INFO_V1(RASTER_notSameAlignmentReason);
-Datum RASTER_notSameAlignmentReason(PG_FUNCTION_ARGS)
-{
- const int set_count = 2;
- rt_pgraster *pgrast[2];
- int pgrastpos[2] = {-1, -1};
- rt_raster rast[2] = {NULL};
-
- uint32_t i;
- uint32_t j;
- uint32_t k;
- int rtn;
- int aligned = 0;
- char *reason = NULL;
- text *result = NULL;
-
- for (i = 0, j = 0; i < set_count; i++) {
- /* pgrast is null, return null */
- if (PG_ARGISNULL(j)) {
- for (k = 0; k < i; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
- pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(j), 0, sizeof(struct rt_raster_serialized_t));
- pgrastpos[i] = j;
- j++;
-
- /* raster */
- rast[i] = rt_raster_deserialize(pgrast[i], TRUE);
- if (!rast[i]) {
- for (k = 0; k <= i; k++) {
- if (k < i)
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "RASTER_notSameAlignmentReason: Could not deserialize the %s raster", i < 1 ? "first" : "second");
- PG_RETURN_NULL();
- }
- }
-
- rtn = rt_raster_same_alignment(
- rast[0],
- rast[1],
- &aligned,
- &reason
- );
- for (k = 0; k < set_count; k++) {
- rt_raster_destroy(rast[k]);
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
-
- if (rtn != ES_NONE) {
- elog(ERROR, "RASTER_notSameAlignmentReason: Could not test for alignment on the two rasters");
- PG_RETURN_NULL();
- }
-
- result = cstring2text(reason);
- PG_RETURN_TEXT_P(result);
-}
-
-/**
- * Two raster MapAlgebra
- */
-PG_FUNCTION_INFO_V1(RASTER_mapAlgebra2);
-Datum RASTER_mapAlgebra2(PG_FUNCTION_ARGS)
-{
- const int set_count = 2;
- rt_pgraster *pgrast[2];
- int pgrastpos[2] = {-1, -1};
- rt_pgraster *pgrtn;
- rt_raster rast[2] = {NULL};
- int _isempty[2] = {0};
- 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};
- int _pos[2][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;
- double gt[6] = {0.};
-
- Oid calltype = InvalidOid;
-
- const int spi_count = 3;
- uint16_t spi_exprpos[3] = {4, 7, 8};
- uint32_t spi_argcount[3] = {0};
- char *expr = NULL;
- char *sql = NULL;
- SPIPlanPtr spi_plan[3] = {NULL};
- uint16_t spi_empty = 0;
- Oid *argtype = NULL;
- const int argkwcount = 8;
- uint8_t argpos[3][8] = {{0}};
- char *argkw[] = {"[rast1.x]", "[rast1.y]", "[rast1.val]", "[rast1]", "[rast2.x]", "[rast2.y]", "[rast2.val]", "[rast2]"};
- Datum values[argkwcount];
- bool nulls[argkwcount];
- TupleDesc tupdesc;
- SPITupleTable *tuptable = NULL;
- HeapTuple tuple;
- Datum datum;
- bool isnull = FALSE;
- int hasargval[3] = {0};
- double argval[3] = {0.};
- int hasnodatanodataval = 0;
- double nodatanodataval = 0;
- int isnodata = 0;
-
- Oid ufc_noid = InvalidOid;
- FmgrInfo ufl_info;
- FunctionCallInfoData ufc_info;
- int ufc_nullcount = 0;
-
- int idx = 0;
- uint32_t i = 0;
- uint32_t j = 0;
- uint32_t k = 0;
- uint32_t x = 0;
- uint32_t y = 0;
- int _x = 0;
- int _y = 0;
- int err;
- int aligned = 0;
- int len = 0;
-
- POSTGIS_RT_DEBUG(3, "Starting RASTER_mapAlgebra2");
-
- for (i = 0, j = 0; i < set_count; i++) {
- if (!PG_ARGISNULL(j)) {
- pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(j));
- pgrastpos[i] = j;
- j++;
-
- /* raster */
- rast[i] = rt_raster_deserialize(pgrast[i], FALSE);
- if (!rast[i]) {
- for (k = 0; k <= i; k++) {
- if (k < i && rast[k] != NULL)
- rt_raster_destroy(rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "RASTER_mapAlgebra2: Could not deserialize the %s raster", i < 1 ? "first" : "second");
- PG_RETURN_NULL();
- }
-
- /* empty */
- _isempty[i] = rt_raster_is_empty(rast[i]);
-
- /* band index */
- if (!PG_ARGISNULL(j)) {
- bandindex[i] = PG_GETARG_INT32(j);
- }
- j++;
- }
- else {
- _isempty[i] = 1;
- j += 2;
- }
-
- POSTGIS_RT_DEBUGF(3, "_isempty[%d] = %d", i, _isempty[i]);
- }
-
- /* both rasters are NULL */
- if (rast[0] == NULL && rast[1] == NULL) {
- elog(NOTICE, "The two rasters provided are NULL. Returning NULL");
- for (k = 0; k < set_count; k++) {
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- /* both rasters are empty */
- if (_isempty[0] && _isempty[1]) {
- elog(NOTICE, "The two rasters provided are empty. Returning empty raster");
-
- raster = rt_raster_new(0, 0);
- if (raster == NULL) {
- for (k = 0; k < set_count; k++) {
- if (rast[k] != NULL)
- rt_raster_destroy(rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "RASTER_mapAlgebra2: Could not create empty raster");
- PG_RETURN_NULL();
- }
- rt_raster_set_scale(raster, 0, 0);
-
- pgrtn = rt_raster_serialize(raster);
- rt_raster_destroy(raster);
- if (!pgrtn)
- PG_RETURN_NULL();
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
- /* replace the empty or NULL raster with one matching the other */
- if (
- (rast[0] == NULL || _isempty[0]) ||
- (rast[1] == NULL || _isempty[1])
- ) {
- /* first raster is empty */
- if (rast[0] == NULL || _isempty[0]) {
- i = 0;
- j = 1;
- }
- /* second raster is empty */
- else {
- i = 1;
- j = 0;
- }
-
- _rast[j] = rast[j];
-
- /* raster is empty, destroy it */
- if (_rast[i] != NULL)
- rt_raster_destroy(_rast[i]);
-
- _dim[i][0] = rt_raster_get_width(_rast[j]);
- _dim[i][1] = rt_raster_get_height(_rast[j]);
- _dim[j][0] = rt_raster_get_width(_rast[j]);
- _dim[j][1] = rt_raster_get_height(_rast[j]);
-
- _rast[i] = rt_raster_new(
- _dim[j][0],
- _dim[j][1]
- );
- if (_rast[i] == NULL) {
- rt_raster_destroy(_rast[j]);
- for (k = 0; k < set_count; k++) {
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "RASTER_mapAlgebra2: Could not create NODATA raster");
- PG_RETURN_NULL();
- }
- rt_raster_set_srid(_rast[i], rt_raster_get_srid(_rast[j]));
-
- rt_raster_get_geotransform_matrix(_rast[j], gt);
- rt_raster_set_geotransform_matrix(_rast[i], gt);
- }
- else {
- _rast[0] = rast[0];
- _dim[0][0] = rt_raster_get_width(_rast[0]);
- _dim[0][1] = rt_raster_get_height(_rast[0]);
-
- _rast[1] = rast[1];
- _dim[1][0] = rt_raster_get_width(_rast[1]);
- _dim[1][1] = rt_raster_get_height(_rast[1]);
- }
-
- /* SRID must match */
- /*
- if (rt_raster_get_srid(_rast[0]) != rt_raster_get_srid(_rast[1])) {
- elog(NOTICE, "The two rasters provided have different SRIDs. Returning NULL");
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
- */
-
- /* same alignment */
- if (rt_raster_same_alignment(_rast[0], _rast[1], &aligned, NULL) != ES_NONE) {
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "RASTER_mapAlgebra2: Could not test for alignment on the two rasters");
- PG_RETURN_NULL();
- }
- if (!aligned) {
- elog(NOTICE, "The two rasters provided do not have the same alignment. Returning NULL");
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- PG_RETURN_NULL();
- }
-
- /* pixel type */
- if (!PG_ARGISNULL(5)) {
- pixtypename = text_to_cstring(PG_GETARG_TEXT_P(5));
- /* Get the pixel type index */
- pixtype = rt_pixtype_index_from_name(pixtypename);
- if (pixtype == PT_END ) {
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "RASTER_mapAlgebra2: Invalid pixel type: %s", pixtypename);
- PG_RETURN_NULL();
- }
- }
-
- /* extent type */
- if (!PG_ARGISNULL(6)) {
- extenttypename = rtpg_strtoupper(rtpg_trim(text_to_cstring(PG_GETARG_TEXT_P(6))));
- extenttype = rt_util_extent_type(extenttypename);
- }
- POSTGIS_RT_DEBUGF(3, "extenttype: %d %s", extenttype, extenttypename);
-
- /* computed raster from extent type */
- err = rt_raster_from_two_rasters(
- _rast[0], _rast[1],
- extenttype,
- &raster, _offset
- );
- if (err != ES_NONE) {
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "RASTER_mapAlgebra2: Could not get output raster of correct extent");
- PG_RETURN_NULL();
- }
-
- /* copy offsets */
- _rastoffset[0][0] = _offset[0];
- _rastoffset[0][1] = _offset[1];
- _rastoffset[1][0] = _offset[2];
- _rastoffset[1][1] = _offset[3];
-
- /* get output raster dimensions */
- dim[0] = rt_raster_get_width(raster);
- dim[1] = rt_raster_get_height(raster);
-
- i = 2;
- /* handle special cases for extent */
- switch (extenttype) {
- case ET_FIRST:
- i = 0;
- case ET_SECOND:
- if (i > 1)
- i = 1;
-
- if (
- _isempty[i] && (
- (extenttype == ET_FIRST && i == 0) ||
- (extenttype == ET_SECOND && i == 1)
- )
- ) {
- elog(NOTICE, "The %s raster is NULL. Returning NULL", (i != 1 ? "FIRST" : "SECOND"));
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- rt_raster_destroy(raster);
- PG_RETURN_NULL();
- }
-
- /* specified band not found */
- if (!rt_raster_has_band(_rast[i], bandindex[i] - 1)) {
- elog(NOTICE, "The %s raster does not have the band at index %d. Returning no band raster of correct extent",
- (i != 1 ? "FIRST" : "SECOND"), bandindex[i]
- );
-
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
-
- pgrtn = rt_raster_serialize(raster);
- rt_raster_destroy(raster);
- if (!pgrtn) PG_RETURN_NULL();
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
- break;
- case ET_UNION:
- break;
- case ET_INTERSECTION:
- /* no intersection */
- if (
- _isempty[0] || _isempty[1] ||
- !dim[0] || !dim[1]
- ) {
- elog(NOTICE, "The two rasters provided have no intersection. Returning no band raster");
-
- /* raster has dimension, replace with no band raster */
- if (dim[0] || dim[1]) {
- rt_raster_destroy(raster);
-
- raster = rt_raster_new(0, 0);
- if (raster == NULL) {
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "RASTER_mapAlgebra2: Could not create no band raster");
- PG_RETURN_NULL();
- }
-
- rt_raster_set_scale(raster, 0, 0);
- rt_raster_set_srid(raster, rt_raster_get_srid(_rast[0]));
- }
-
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
-
- pgrtn = rt_raster_serialize(raster);
- rt_raster_destroy(raster);
- if (!pgrtn) PG_RETURN_NULL();
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
- break;
- case ET_LAST:
- case ET_CUSTOM:
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- elog(ERROR, "RASTER_mapAlgebra2: ET_LAST and ET_CUSTOM are not implemented");
- PG_RETURN_NULL();
- break;
- }
-
- /* both rasters do not have specified bands */
- if (
- (!_isempty[0] && !rt_raster_has_band(_rast[0], bandindex[0] - 1)) &&
- (!_isempty[1] && !rt_raster_has_band(_rast[1], bandindex[1] - 1))
- ) {
- elog(NOTICE, "The two rasters provided do not have the respectively specified band indices. Returning no band raster of correct extent");
-
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
-
- pgrtn = rt_raster_serialize(raster);
- rt_raster_destroy(raster);
- if (!pgrtn) PG_RETURN_NULL();
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
- /* get bands */
- for (i = 0; i < set_count; i++) {
- if (_isempty[i] || !rt_raster_has_band(_rast[i], bandindex[i] - 1)) {
- _hasnodata[i] = 1;
- _nodataval[i] = 0;
-
- continue;
- }
-
- _band[i] = rt_raster_get_band(_rast[i], bandindex[i] - 1);
- if (_band[i] == NULL) {
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- rt_raster_destroy(raster);
- elog(ERROR, "RASTER_mapAlgebra2: Could not get band %d of the %s raster",
- bandindex[i],
- (i < 1 ? "FIRST" : "SECOND")
- );
- PG_RETURN_NULL();
- }
-
- _hasnodata[i] = rt_band_get_hasnodata_flag(_band[i]);
- if (_hasnodata[i])
- rt_band_get_nodata(_band[i], &(_nodataval[i]));
- }
-
- /* pixtype is PT_END, get pixtype based upon extent */
- if (pixtype == PT_END) {
- if ((extenttype == ET_SECOND && !_isempty[1]) || _isempty[0])
- pixtype = rt_band_get_pixtype(_band[1]);
- else
- pixtype = rt_band_get_pixtype(_band[0]);
- }
-
- /* nodata value for new band */
- if (extenttype == ET_SECOND && !_isempty[1] && _hasnodata[1]) {
- nodataval = _nodataval[1];
- }
- else if (!_isempty[0] && _hasnodata[0]) {
- nodataval = _nodataval[0];
- }
- else if (!_isempty[1] && _hasnodata[1]) {
- nodataval = _nodataval[1];
- }
- else {
- elog(NOTICE, "Neither raster provided has a NODATA value for the specified band indices. NODATA value set to minimum possible for %s", rt_pixtype_name(pixtype));
- nodataval = rt_pixtype_get_min_value(pixtype);
- }
-
- /* add band to output raster */
- if (rt_raster_generate_new_band(
- raster,
- pixtype,
- nodataval,
- 1, nodataval,
- 0
- ) < 0) {
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- rt_raster_destroy(raster);
- elog(ERROR, "RASTER_mapAlgebra2: Could not add new band to output raster");
- PG_RETURN_NULL();
- }
-
- /* get output band */
- band = rt_raster_get_band(raster, 0);
- if (band == NULL) {
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- rt_raster_destroy(raster);
- elog(ERROR, "RASTER_mapAlgebra2: Could not get newly added band of output raster");
- PG_RETURN_NULL();
- }
-
- POSTGIS_RT_DEBUGF(4, "offsets = (%d, %d, %d, %d)",
- (int) _rastoffset[0][0],
- (int) _rastoffset[0][1],
- (int) _rastoffset[1][0],
- (int) _rastoffset[1][1]
- );
-
- POSTGIS_RT_DEBUGF(4, "metadata = (%f, %f, %d, %d, %f, %f, %f, %f, %d)",
- rt_raster_get_x_offset(raster),
- rt_raster_get_y_offset(raster),
- rt_raster_get_width(raster),
- rt_raster_get_height(raster),
- rt_raster_get_x_scale(raster),
- rt_raster_get_y_scale(raster),
- rt_raster_get_x_skew(raster),
- rt_raster_get_y_skew(raster),
- rt_raster_get_srid(raster)
- );
-
- /*
- determine who called this function
- Arg 4 will either be text or regprocedure
- */
- 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\"!");
-
- /* connect SPI */
- if (SPI_connect() != SPI_OK_CONNECT) {
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- rt_raster_destroy(raster);
- elog(ERROR, "RASTER_mapAlgebra2: Could not connect to the SPI manager");
- PG_RETURN_NULL();
- }
-
- /* reset hasargval */
- memset(hasargval, 0, sizeof(int) * spi_count);
-
- /*
- process expressions
-
- spi_exprpos elements are:
- 4 - expression => spi_plan[0]
- 7 - nodata1expr => spi_plan[1]
- 8 - nodata2expr => spi_plan[2]
- */
- for (i = 0; i < spi_count; i++) {
- if (!PG_ARGISNULL(spi_exprpos[i])) {
- char *tmp = NULL;
- char place[5] = "$1";
- expr = text_to_cstring(PG_GETARG_TEXT_P(spi_exprpos[i]));
- POSTGIS_RT_DEBUGF(3, "raw expr #%d: %s", i, expr);
-
- for (j = 0, k = 1; j < argkwcount; j++) {
- /* attempt to replace keyword with placeholder */
- len = 0;
- tmp = rtpg_strreplace(expr, argkw[j], place, &len);
- pfree(expr);
- expr = tmp;
-
- if (len) {
- spi_argcount[i]++;
- argpos[i][j] = k++;
-
- sprintf(place, "$%d", k);
- }
- else
- argpos[i][j] = 0;
- }
-
- len = strlen("SELECT (") + strlen(expr) + strlen(")::double precision");
- sql = (char *) palloc(len + 1);
- if (sql == NULL) {
-
- for (k = 0; k < spi_count; k++) SPI_freeplan(spi_plan[k]);
- SPI_finish();
-
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- rt_raster_destroy(raster);
-
- elog(ERROR, "RASTER_mapAlgebra2: Could not allocate memory for expression parameter %d", spi_exprpos[i]);
- 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';
-
- POSTGIS_RT_DEBUGF(3, "sql #%d: %s", i, sql);
-
- /* create prepared plan */
- if (spi_argcount[i]) {
- argtype = (Oid *) palloc(spi_argcount[i] * sizeof(Oid));
- if (argtype == NULL) {
-
- pfree(sql);
- for (k = 0; k < spi_count; k++) SPI_freeplan(spi_plan[k]);
- SPI_finish();
-
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- rt_raster_destroy(raster);
-
- elog(ERROR, "RASTER_mapAlgebra2: Could not allocate memory for prepared plan argtypes of expression parameter %d", spi_exprpos[i]);
- PG_RETURN_NULL();
- }
-
- /* specify datatypes of parameters */
- for (j = 0, k = 0; j < argkwcount; j++) {
- if (argpos[i][j] < 1) continue;
-
- /* positions are INT4 */
- if (
- (strstr(argkw[j], "[rast1.x]") != NULL) ||
- (strstr(argkw[j], "[rast1.y]") != NULL) ||
- (strstr(argkw[j], "[rast2.x]") != NULL) ||
- (strstr(argkw[j], "[rast2.y]") != NULL)
- ) {
- argtype[k] = INT4OID;
- }
- /* everything else is FLOAT8 */
- else {
- argtype[k] = FLOAT8OID;
- }
-
- k++;
- }
-
- spi_plan[i] = SPI_prepare(sql, spi_argcount[i], argtype);
- pfree(argtype);
-
- if (spi_plan[i] == NULL) {
-
- pfree(sql);
- for (k = 0; k < spi_count; k++) SPI_freeplan(spi_plan[k]);
- SPI_finish();
-
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- rt_raster_destroy(raster);
-
- elog(ERROR, "RASTER_mapAlgebra2: Could not create prepared plan of expression parameter %d", spi_exprpos[i]);
- PG_RETURN_NULL();
- }
- }
- /* no args, just execute query */
- else {
- err = SPI_execute(sql, TRUE, 0);
- if (err != SPI_OK_SELECT || SPI_tuptable == NULL || SPI_processed != 1) {
-
- pfree(sql);
- for (k = 0; k < spi_count; k++) SPI_freeplan(spi_plan[k]);
- SPI_finish();
-
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- rt_raster_destroy(raster);
-
- elog(ERROR, "RASTER_mapAlgebra2: Could not evaluate expression parameter %d", spi_exprpos[i]);
- 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) {
-
- pfree(sql);
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- for (k = 0; k < spi_count; k++) SPI_freeplan(spi_plan[k]);
- SPI_finish();
-
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- rt_raster_destroy(raster);
-
- elog(ERROR, "RASTER_mapAlgebra2: Could not get result of expression parameter %d", spi_exprpos[i]);
- PG_RETURN_NULL();
- }
-
- if (!isnull) {
- hasargval[i] = 1;
- argval[i] = DatumGetFloat8(datum);
- }
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- }
-
- pfree(sql);
- }
- else
- spi_empty++;
- }
-
- /* 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)) {
-
- ufc_nullcount = 0;
- ufc_noid = PG_GETARG_OID(4);
-
- /* get function info */
- fmgr_info(ufc_noid, &ufl_info);
-
- /* function cannot return set */
- err = 0;
- if (ufl_info.fn_retset) {
- err = 1;
- }
- /* function should have correct # of args */
- else if (ufl_info.fn_nargs < 3 || ufl_info.fn_nargs > 4) {
- err = 2;
- }
-
- /*
- TODO: consider adding checks of the userfunction parameters
- should be able to use get_fn_expr_argtype() of fmgr.c
- */
-
- if (err > 0) {
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- rt_raster_destroy(raster);
-
- if (err > 1)
- elog(ERROR, "RASTER_mapAlgebra2: Function provided must have three or four input parameters");
- else
- elog(ERROR, "RASTER_mapAlgebra2: Function provided must return double precision not resultset");
- PG_RETURN_NULL();
- }
-
- if (func_volatile(ufc_noid) == 'v') {
- elog(NOTICE, "Function provided is VOLATILE. Unless required and for best performance, function should be IMMUTABLE or STABLE");
- }
-
- /* prep function call data */
-#if POSTGIS_PGSQL_VERSION <= 90
- InitFunctionCallInfoData(ufc_info, &ufl_info, ufl_info.fn_nargs, InvalidOid, NULL);
-#else
- InitFunctionCallInfoData(ufc_info, &ufl_info, ufl_info.fn_nargs, InvalidOid, NULL, NULL);
-#endif
- memset(ufc_info.argnull, FALSE, sizeof(bool) * ufl_info.fn_nargs);
-
- if (ufl_info.fn_nargs != 4)
- k = 2;
- else
- k = 3;
- if (!PG_ARGISNULL(7)) {
- ufc_info.arg[k] = PG_GETARG_DATUM(7);
- }
- else {
- ufc_info.arg[k] = (Datum) NULL;
- ufc_info.argnull[k] = TRUE;
- ufc_nullcount++;
- }
- }
- break;
- }
- default:
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- rt_raster_destroy(raster);
- elog(ERROR, "RASTER_mapAlgebra2: Invalid data type for expression or userfunction");
- PG_RETURN_NULL();
- break;
- }
-
- /* loop over pixels */
- /* if any expression present, run */
- if ((
- (calltype == TEXTOID) && (
- (spi_empty != spi_count) || hasnodatanodataval
- )
- ) || (
- (calltype == REGPROCEDUREOID) && (ufc_noid != InvalidOid)
- )) {
- for (x = 0; x < dim[0]; x++) {
- for (y = 0; y < dim[1]; y++) {
-
- /* get pixel from each raster */
- for (i = 0; i < set_count; i++) {
- _haspixel[i] = 0;
- _pixel[i] = 0;
-
- /* row/column */
- _x = x - (int) _rastoffset[i][0];
- _y = y - (int) _rastoffset[i][1];
-
- /* store _x and _y in 1-based */
- _pos[i][0] = _x + 1;
- _pos[i][1] = _y + 1;
-
- /* get pixel value */
- if (_band[i] == NULL) {
- if (!_hasnodata[i]) {
- _haspixel[i] = 1;
- _pixel[i] = _nodataval[i];
- }
- }
- else if (
- !_isempty[i] &&
- (_x >= 0 && _x < _dim[i][0]) &&
- (_y >= 0 && _y < _dim[i][1])
- ) {
- err = rt_band_get_pixel(_band[i], _x, _y, &(_pixel[i]), &isnodata);
- if (err != ES_NONE) {
-
- if (calltype == TEXTOID) {
- for (k = 0; k < spi_count; k++) SPI_freeplan(spi_plan[k]);
- SPI_finish();
- }
-
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- rt_raster_destroy(raster);
-
- elog(ERROR, "RASTER_mapAlgebra2: Could not get pixel of %s raster", (i < 1 ? "FIRST" : "SECOND"));
- PG_RETURN_NULL();
- }
-
- if (!_hasnodata[i] || !isnodata)
- _haspixel[i] = 1;
- }
-
- POSTGIS_RT_DEBUGF(5, "pixel r%d(%d, %d) = %d, %f",
- i,
- _x, _y,
- _haspixel[i],
- _pixel[i]
- );
- }
-
- haspixel = 0;
-
- switch (calltype) {
- case TEXTOID: {
- /* which prepared plan to use? */
- /* !pixel0 && !pixel1 */
- /* use nodatanodataval */
- if (!_haspixel[0] && !_haspixel[1])
- i = 3;
- /* pixel0 && !pixel1 */
- /* run spi_plan[2] (nodata2expr) */
- else if (_haspixel[0] && !_haspixel[1])
- i = 2;
- /* !pixel0 && pixel1 */
- /* run spi_plan[1] (nodata1expr) */
- else if (!_haspixel[0] && _haspixel[1])
- i = 1;
- /* pixel0 && pixel1 */
- /* run spi_plan[0] (expression) */
- else
- i = 0;
-
- /* 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 (spi_plan[i] != NULL) {
- POSTGIS_RT_DEBUGF(4, "Using prepared plan: %d", i);
-
- /* expression has argument(s) */
- if (spi_argcount[i]) {
- /* reset values to (Datum) NULL */
- memset(values, (Datum) NULL, sizeof(Datum) * argkwcount);
- /* reset nulls to FALSE */
- memset(nulls, FALSE, sizeof(bool) * argkwcount);
-
- /* set values and nulls */
- for (j = 0; j < argkwcount; j++) {
- idx = argpos[i][j];
- if (idx < 1) continue;
- idx--; /* 1-based becomes 0-based */
-
- if (strstr(argkw[j], "[rast1.x]") != NULL) {
- values[idx] = _pos[0][0];
- }
- else if (strstr(argkw[j], "[rast1.y]") != NULL) {
- values[idx] = _pos[0][1];
- }
- else if (
- (strstr(argkw[j], "[rast1.val]") != NULL) ||
- (strstr(argkw[j], "[rast1]") != NULL)
- ) {
- if (_isempty[0] || !_haspixel[0])
- nulls[idx] = TRUE;
- else
- values[idx] = Float8GetDatum(_pixel[0]);
- }
- else if (strstr(argkw[j], "[rast2.x]") != NULL) {
- values[idx] = _pos[1][0];
- }
- else if (strstr(argkw[j], "[rast2.y]") != NULL) {
- values[idx] = _pos[1][1];
- }
- else if (
- (strstr(argkw[j], "[rast2.val]") != NULL) ||
- (strstr(argkw[j], "[rast2]") != NULL)
- ) {
- if (_isempty[1] || !_haspixel[1])
- nulls[idx] = TRUE;
- else
- values[idx] = Float8GetDatum(_pixel[1]);
- }
- }
- }
-
- /* run prepared plan */
- err = SPI_execute_plan(spi_plan[i], values, nulls, TRUE, 1);
- if (err != SPI_OK_SELECT || SPI_tuptable == NULL || SPI_processed != 1) {
-
- for (k = 0; k < spi_count; k++) SPI_freeplan(spi_plan[k]);
- SPI_finish();
-
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- rt_raster_destroy(raster);
-
- elog(ERROR, "RASTER_mapAlgebra2: Unexpected error when running prepared statement %d", i);
- 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) {
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- for (k = 0; k < spi_count; k++) SPI_freeplan(spi_plan[k]);
- SPI_finish();
-
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- rt_raster_destroy(raster);
-
- elog(ERROR, "RASTER_mapAlgebra2: Could not get result of prepared statement %d", i);
- PG_RETURN_NULL();
- }
-
- if (!isnull) {
- haspixel = 1;
- pixel = DatumGetFloat8(datum);
- }
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- }
- } break;
- case REGPROCEDUREOID: {
- Datum d[4];
- ArrayType *a;
-
- /* build fcnarg */
- for (i = 0; i < set_count; i++) {
- ufc_info.arg[i] = Float8GetDatum(_pixel[i]);
-
- if (_haspixel[i]) {
- ufc_info.argnull[i] = FALSE;
- ufc_nullcount--;
- }
- else {
- ufc_info.argnull[i] = TRUE;
- ufc_nullcount++;
- }
- }
-
- /* function is strict and null parameter is passed */
- /* http://archives.postgresql.org/pgsql-general/2011-11/msg00424.php */
- if (ufl_info.fn_strict && ufc_nullcount)
- break;
-
- /* 4 parameters, add position */
- if (ufl_info.fn_nargs == 4) {
- /* Datum of 4 element array */
- /* array is (x1, y1, x2, y2) */
- for (i = 0; i < set_count; i++) {
- if (i < 1) {
- d[0] = Int32GetDatum(_pos[i][0]);
- d[1] = Int32GetDatum(_pos[i][1]);
- }
- else {
- d[2] = Int32GetDatum(_pos[i][0]);
- d[3] = Int32GetDatum(_pos[i][1]);
- }
- }
-
- a = construct_array(d, 4, INT4OID, sizeof(int32), true, 'i');
- ufc_info.arg[2] = PointerGetDatum(a);
- ufc_info.argnull[2] = FALSE;
- }
-
- datum = FunctionCallInvoke(&ufc_info);
-
- /* result is not null*/
- if (!ufc_info.isnull) {
- haspixel = 1;
- pixel = DatumGetFloat8(datum);
- }
- } break;
- }
-
- /* burn pixel if haspixel != 0 */
- if (haspixel) {
- if (rt_band_set_pixel(band, x, y, pixel, NULL) != ES_NONE) {
-
- if (calltype == TEXTOID) {
- for (k = 0; k < spi_count; k++) SPI_freeplan(spi_plan[k]);
- SPI_finish();
- }
-
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
- rt_raster_destroy(raster);
-
- elog(ERROR, "RASTER_mapAlgebra2: Could not set pixel value of output raster");
- PG_RETURN_NULL();
- }
- }
-
- POSTGIS_RT_DEBUGF(5, "(x, y, val) = (%d, %d, %f)", x, y, haspixel ? pixel : nodataval);
-
- } /* y: height */
- } /* x: width */
- }
-
- /* CLEANUP */
- if (calltype == TEXTOID) {
- for (i = 0; i < spi_count; i++) {
- if (spi_plan[i] != NULL) SPI_freeplan(spi_plan[i]);
- }
- SPI_finish();
- }
-
- for (k = 0; k < set_count; k++) {
- if (_rast[k] != NULL)
- rt_raster_destroy(_rast[k]);
- if (pgrastpos[k] != -1)
- PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
- }
-
- pgrtn = rt_raster_serialize(raster);
- rt_raster_destroy(raster);
- if (!pgrtn) PG_RETURN_NULL();
-
- POSTGIS_RT_DEBUG(3, "Finished RASTER_mapAlgebra2");
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
-}
-
-/**
- * One raster neighborhood MapAlgebra
- */
-PG_FUNCTION_INFO_V1(RASTER_mapAlgebraFctNgb);
-Datum RASTER_mapAlgebraFctNgb(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- rt_pgraster *pgrtn = NULL;
- rt_raster raster = NULL;
- rt_raster newrast = NULL;
- rt_band band = NULL;
- rt_band newband = NULL;
- int x, y, nband, width, height, ngbwidth, ngbheight, winwidth, winheight, u, v, nIndex, nNullItems;
- double r, rpix;
- double newnodatavalue = 0.0;
- double newinitialvalue = 0.0;
- double newval = 0.0;
- rt_pixtype newpixeltype;
- int ret = -1;
- Oid oid;
- FmgrInfo cbinfo;
- FunctionCallInfoData cbdata;
- Datum tmpnewval;
- ArrayType * neighborDatum;
- char * strFromText = NULL;
- text * txtNodataMode = NULL;
- text * txtCallbackParam = NULL;
- int intReplace = 0;
- float fltReplace = 0;
- bool valuereplace = false, pixelreplace, nNodataOnly = true, nNullSkip = false;
- Datum * neighborData = NULL;
- bool * neighborNulls = NULL;
- int neighborDims[2];
- int neighborLbs[2];
- int16 typlen;
- bool typbyval;
- char typalign;
-
- POSTGIS_RT_DEBUG(2, "RASTER_mapAlgebraFctNgb: STARTING...");
-
- /* Check raster */
- if (PG_ARGISNULL(0)) {
- elog(WARNING, "Raster is NULL. Returning NULL");
- PG_RETURN_NULL();
- }
-
-
- /* Deserialize raster */
- pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
- raster = rt_raster_deserialize(pgraster, FALSE);
- if (NULL == raster)
- {
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_mapAlgebraFctNgb: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFctNgb: Getting arguments...");
-
- /* Get the rest of the arguments */
-
- if (PG_ARGISNULL(1))
- nband = 1;
- else
- nband = PG_GETARG_INT32(1);
-
- if (nband < 1)
- nband = 1;
-
- POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFctNgb: Creating new empty raster...");
-
- /**
- * Create a new empty raster with having the same georeference as the
- * provided raster
- **/
- width = rt_raster_get_width(raster);
- height = rt_raster_get_height(raster);
-
- newrast = rt_raster_new(width, height);
-
- if ( NULL == newrast ) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_mapAlgebraFctNgb: Could not create a new raster");
- PG_RETURN_NULL();
- }
-
- rt_raster_set_scale(newrast,
- rt_raster_get_x_scale(raster),
- rt_raster_get_y_scale(raster));
-
- rt_raster_set_offsets(newrast,
- rt_raster_get_x_offset(raster),
- rt_raster_get_y_offset(raster));
-
- rt_raster_set_skews(newrast,
- rt_raster_get_x_skew(raster),
- rt_raster_get_y_skew(raster));
-
- rt_raster_set_srid(newrast, rt_raster_get_srid(raster));
-
-
- /**
- * If this new raster is empty (width = 0 OR height = 0) then there is
- * nothing to compute and we return it right now
- **/
- if (rt_raster_is_empty(newrast))
- {
- elog(NOTICE, "Raster is empty. Returning an empty raster");
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- pgrtn = rt_raster_serialize(newrast);
- rt_raster_destroy(newrast);
- if (NULL == pgrtn) {
- elog(ERROR, "RASTER_mapAlgebraFctNgb: Could not serialize raster");
- PG_RETURN_NULL();
- }
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
- POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFctNgb: Getting raster band %d...", nband);
-
- /**
- * Check if the raster has the required band. Otherwise, return a raster
- * without band
- **/
- if (!rt_raster_has_band(raster, nband - 1)) {
- elog(NOTICE, "Raster does not have the required band. Returning a raster "
- "without a band");
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- pgrtn = rt_raster_serialize(newrast);
- rt_raster_destroy(newrast);
- if (NULL == pgrtn) {
- elog(ERROR, "RASTER_mapAlgebraFctNgb: Could not serialize raster");
- PG_RETURN_NULL();
- }
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
- /* Get the raster band */
- band = rt_raster_get_band(raster, nband - 1);
- if ( NULL == band ) {
- elog(NOTICE, "Could not get the required band. Returning a raster "
- "without a band");
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- pgrtn = rt_raster_serialize(newrast);
- rt_raster_destroy(newrast);
- if (NULL == pgrtn) {
- elog(ERROR, "RASTER_mapAlgebraFctNgb: Could not serialize raster");
- PG_RETURN_NULL();
- }
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
- /*
- * Get NODATA value
- */
- POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFctNgb: Getting NODATA value for band...");
-
- if (rt_band_get_hasnodata_flag(band)) {
- rt_band_get_nodata(band, &newnodatavalue);
- }
-
- else {
- newnodatavalue = rt_band_get_min_value(band);
- }
-
- POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFctNgb: NODATA value for band: %f",
- newnodatavalue);
- /**
- * We set the initial value of the future band to nodata value. If nodata
- * value is null, then the raster will be initialized to
- * rt_band_get_min_value but all the values should be recomputed anyway
- **/
- newinitialvalue = newnodatavalue;
-
- /**
- * Set the new pixeltype
- **/
- POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFctNgb: Setting pixeltype...");
-
- if (PG_ARGISNULL(2)) {
- newpixeltype = rt_band_get_pixtype(band);
- }
-
- else {
- strFromText = text_to_cstring(PG_GETARG_TEXT_P(2));
- POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFctNgb: Pixeltype parameter: %s", strFromText);
- newpixeltype = rt_pixtype_index_from_name(strFromText);
- pfree(strFromText);
- if (newpixeltype == PT_END)
- newpixeltype = rt_band_get_pixtype(band);
- }
-
- if (newpixeltype == PT_END) {
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- rt_raster_destroy(newrast);
-
- elog(ERROR, "RASTER_mapAlgebraFctNgb: Invalid pixeltype");
- PG_RETURN_NULL();
- }
-
- POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFctNgb: Pixeltype set to %s (%d)",
- rt_pixtype_name(newpixeltype), newpixeltype);
-
- /* Get the name of the callback userfunction */
- if (PG_ARGISNULL(5)) {
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- rt_raster_destroy(newrast);
-
- elog(ERROR, "RASTER_mapAlgebraFctNgb: Required function is missing");
- PG_RETURN_NULL();
- }
-
- oid = PG_GETARG_OID(5);
- if (oid == InvalidOid) {
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- rt_raster_destroy(newrast);
-
- elog(ERROR, "RASTER_mapAlgebraFctNgb: Got invalid function object id");
- PG_RETURN_NULL();
- }
-
- fmgr_info(oid, &cbinfo);
-
- /* function cannot return set */
- if (cbinfo.fn_retset) {
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- rt_raster_destroy(newrast);
-
- elog(ERROR, "RASTER_mapAlgebraFctNgb: Function provided must return double precision not resultset");
- PG_RETURN_NULL();
- }
- /* function should have correct # of args */
- else if (cbinfo.fn_nargs != 3) {
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- rt_raster_destroy(newrast);
-
- elog(ERROR, "RASTER_mapAlgebraFctNgb: Function does not have three input parameters");
- PG_RETURN_NULL();
- }
-
- if (func_volatile(oid) == 'v') {
- elog(NOTICE, "Function provided is VOLATILE. Unless required and for best performance, function should be IMMUTABLE or STABLE");
- }
-
- /* prep function call data */
-#if POSTGIS_PGSQL_VERSION <= 90
- InitFunctionCallInfoData(cbdata, &cbinfo, 3, InvalidOid, NULL);
-#else
- InitFunctionCallInfoData(cbdata, &cbinfo, 3, InvalidOid, NULL, NULL);
-#endif
- memset(cbdata.argnull, FALSE, sizeof(bool) * 3);
-
- /* check that the function isn't strict if the args are null. */
- if (PG_ARGISNULL(7)) {
- if (cbinfo.fn_strict) {
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
- rt_raster_destroy(newrast);
-
- elog(ERROR, "RASTER_mapAlgebraFctNgb: Strict callback functions cannot have NULL parameters");
- PG_RETURN_NULL();
- }
-
- cbdata.arg[2] = (Datum)NULL;
- cbdata.argnull[2] = TRUE;
- }
- else {
- cbdata.arg[2] = PG_GETARG_DATUM(7);
- }
-
- /**
- * Optimization: If the raster is only filled with nodata values return
- * right now a raster filled with the nodatavalueexpr
- * TODO: Call rt_band_check_isnodata instead?
- **/
- if (rt_band_get_isnodata_flag(band)) {
-
- POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFctNgb: Band is a nodata band, returning "
- "a raster filled with nodata");
-
- rt_raster_generate_new_band(newrast, newpixeltype,
- newinitialvalue, TRUE, newnodatavalue, 0);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- /* Serialize created raster */
- pgrtn = rt_raster_serialize(newrast);
- rt_raster_destroy(newrast);
- if (NULL == pgrtn) {
- elog(ERROR, "RASTER_mapAlgebraFctNgb: Could not serialize raster");
- PG_RETURN_NULL();
- }
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
-
- /**
- * Create the raster receiving all the computed values. Initialize it to the
- * new initial value
- **/
- rt_raster_generate_new_band(newrast, newpixeltype,
- newinitialvalue, TRUE, newnodatavalue, 0);
-
- /* Get the new raster band */
- newband = rt_raster_get_band(newrast, 0);
- if ( NULL == newband ) {
- elog(NOTICE, "Could not modify band for new raster. Returning new "
- "raster with the original band");
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- /* Serialize created raster */
- pgrtn = rt_raster_serialize(newrast);
- rt_raster_destroy(newrast);
- if (NULL == pgrtn) {
- elog(ERROR, "RASTER_mapAlgebraFctNgb: Could not serialize raster");
- PG_RETURN_NULL();
- }
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
- /* Get the width of the neighborhood */
- if (PG_ARGISNULL(3) || PG_GETARG_INT32(3) <= 0) {
- elog(NOTICE, "Neighborhood width is NULL or <= 0. Returning new "
- "raster with the original band");
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- /* Serialize created raster */
- pgrtn = rt_raster_serialize(newrast);
- rt_raster_destroy(newrast);
- if (NULL == pgrtn) {
- elog(ERROR, "RASTER_mapAlgebraFctNgb: Could not serialize raster");
- PG_RETURN_NULL();
- }
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
- ngbwidth = PG_GETARG_INT32(3);
- winwidth = ngbwidth * 2 + 1;
-
- /* Get the height of the neighborhood */
- if (PG_ARGISNULL(4) || PG_GETARG_INT32(4) <= 0) {
- elog(NOTICE, "Neighborhood height is NULL or <= 0. Returning new "
- "raster with the original band");
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- /* Serialize created raster */
- pgrtn = rt_raster_serialize(newrast);
- rt_raster_destroy(newrast);
- if (NULL == pgrtn) {
- elog(ERROR, "RASTER_mapAlgebraFctNgb: Could not serialize raster");
- PG_RETURN_NULL();
- }
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
- ngbheight = PG_GETARG_INT32(4);
- winheight = ngbheight * 2 + 1;
-
- /* Get the type of NODATA behavior for the neighborhoods. */
- if (PG_ARGISNULL(6)) {
- elog(NOTICE, "Neighborhood NODATA behavior defaulting to 'ignore'");
- txtNodataMode = cstring_to_text("ignore");
- }
- else {
- txtNodataMode = PG_GETARG_TEXT_P(6);
- }
-
- txtCallbackParam = (text*)palloc(VARSIZE(txtNodataMode));
- SET_VARSIZE(txtCallbackParam, VARSIZE(txtNodataMode));
- memcpy((void *)VARDATA(txtCallbackParam), (void *)VARDATA(txtNodataMode), VARSIZE(txtNodataMode) - VARHDRSZ);
-
- /* pass the nodata mode into the user function */
- cbdata.arg[1] = CStringGetDatum(txtCallbackParam);
-
- strFromText = text_to_cstring(txtNodataMode);
- strFromText = rtpg_strtoupper(strFromText);
-
- if (strcmp(strFromText, "VALUE") == 0)
- valuereplace = true;
- else if (strcmp(strFromText, "IGNORE") != 0 && strcmp(strFromText, "NULL") != 0) {
- /* if the text is not "IGNORE" or "NULL", it may be a numerical value */
- if (sscanf(strFromText, "%d", &intReplace) <= 0 && sscanf(strFromText, "%f", &fltReplace) <= 0) {
- /* the value is NOT an integer NOR a floating point */
- elog(NOTICE, "Neighborhood NODATA mode is not recognized. Must be one of 'value', 'ignore', "
- "'NULL', or a numeric value. Returning new raster with the original band");
-
- /* clean up the nodatamode string */
- pfree(txtCallbackParam);
- pfree(strFromText);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- /* Serialize created raster */
- pgrtn = rt_raster_serialize(newrast);
- rt_raster_destroy(newrast);
- if (NULL == pgrtn) {
- elog(ERROR, "RASTER_mapAlgebraFctNgb: Could not serialize raster");
- PG_RETURN_NULL();
- }
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
- }
- else if (strcmp(strFromText, "NULL") == 0) {
- /* this setting means that the neighborhood should be skipped if any of the values are null */
- nNullSkip = true;
- }
-
- POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFctNgb: Main computing loop (%d x %d)",
- width, height);
-
- /* Allocate room for the neighborhood. */
- neighborData = (Datum *)palloc(winwidth * winheight * sizeof(Datum));
- neighborNulls = (bool *)palloc(winwidth * winheight * sizeof(bool));
-
- /* The dimensions of the neighborhood array, for creating a multi-dimensional array. */
- neighborDims[0] = winwidth;
- neighborDims[1] = winheight;
-
- /* The lower bounds for the new multi-dimensional array. */
- neighborLbs[0] = 1;
- neighborLbs[1] = 1;
-
- /* Get information about the type of item in the multi-dimensional array (float8). */
- get_typlenbyvalalign(FLOAT8OID, &typlen, &typbyval, &typalign);
-
- for (x = 0 + ngbwidth; x < width - ngbwidth; x++) {
- for(y = 0 + ngbheight; y < height - ngbheight; y++) {
- /* populate an array with the pixel values in the neighborhood */
- nIndex = 0;
- nNullItems = 0;
- nNodataOnly = true;
- pixelreplace = false;
- if (valuereplace) {
- ret = rt_band_get_pixel(band, x, y, &rpix, NULL);
- if (ret == ES_NONE && FLT_NEQ(rpix, newnodatavalue)) {
- pixelreplace = true;
- }
- }
- for (u = x - ngbwidth; u <= x + ngbwidth; u++) {
- for (v = y - ngbheight; v <= y + ngbheight; v++) {
- ret = rt_band_get_pixel(band, u, v, &r, NULL);
- if (ret == ES_NONE) {
- if (FLT_NEQ(r, newnodatavalue)) {
- /* If the pixel value for this neighbor cell is not NODATA */
- neighborData[nIndex] = Float8GetDatum((double)r);
- neighborNulls[nIndex] = false;
- nNodataOnly = false;
- }
- else {
- /* If the pixel value for this neighbor cell is NODATA */
- if (valuereplace && pixelreplace) {
- /* Replace the NODATA value with the currently processing pixel. */
- neighborData[nIndex] = Float8GetDatum((double)rpix);
- neighborNulls[nIndex] = false;
- /* do not increment nNullItems, since the user requested that the */
- /* neighborhood replace NODATA values with the central pixel value */
- }
- else {
- neighborData[nIndex] = PointerGetDatum(NULL);
- neighborNulls[nIndex] = true;
- nNullItems++;
- }
- }
- }
- else {
- /* Fill this will NULL if we can't read the raster pixel. */
- neighborData[nIndex] = PointerGetDatum(NULL);
- neighborNulls[nIndex] = true;
- nNullItems++;
- }
- /* Next neighbor position */
- nIndex++;
- }
- }
-
- /**
- * We compute a value only for the withdata value neighborhood since the
- * nodata value has already been set by the first optimization
- **/
- if (!(nNodataOnly || /* neighborhood only contains NODATA -- OR -- */
- (nNullSkip && nNullItems > 0) || /* neighborhood should skip any NODATA cells, and a NODATA cell was detected -- OR -- */
- (valuereplace && nNullItems > 0))) { /* neighborhood should replace NODATA cells with the central pixel value, and a NODATA cell was detected */
- POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFctNgb: (%dx%d), %dx%d neighborhood",
- x, y, winwidth, winheight);
-
- neighborDatum = construct_md_array((void *)neighborData, neighborNulls, 2, neighborDims, neighborLbs,
- FLOAT8OID, typlen, typbyval, typalign);
-
- /* Assign the neighbor matrix as the first argument to the user function */
- cbdata.arg[0] = PointerGetDatum(neighborDatum);
-
- /* Invoke the user function */
- tmpnewval = FunctionCallInvoke(&cbdata);
-
- /* Get the return value of the user function */
- if (cbdata.isnull) {
- newval = newnodatavalue;
- }
- else {
- newval = DatumGetFloat8(tmpnewval);
- }
-
- POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFctNgb: new value = %f",
- newval);
-
- rt_band_set_pixel(newband, x, y, newval, NULL);
- }
-
- /* reset the number of null items in the neighborhood */
- nNullItems = 0;
- }
- }
-
-
- /* clean up */
- pfree(neighborNulls);
- pfree(neighborData);
- pfree(strFromText);
- pfree(txtCallbackParam);
-
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 0);
-
- /* The newrast band has been modified */
-
- POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFctNgb: raster modified, serializing it.");
- /* Serialize created raster */
-
- pgrtn = rt_raster_serialize(newrast);
- rt_raster_destroy(newrast);
- if (NULL == pgrtn)
- PG_RETURN_NULL();
-
- POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFctNgb: raster serialized");
- POSTGIS_RT_DEBUG(4, "RASTER_mapAlgebraFctNgb: returning raster");
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
-}
-
-/* ---------------------------------------------------------------- */
-/* n-raster MapAlgebra */
-/* ---------------------------------------------------------------- */
-
-typedef struct {
- Oid ufc_noid;
- FmgrInfo ufl_info;
- FunctionCallInfoData ufc_info;
- int ufc_nullcount;
-} rtpg_nmapalgebra_callback_arg;
-
-typedef struct rtpg_nmapalgebra_arg_t *rtpg_nmapalgebra_arg;
-struct rtpg_nmapalgebra_arg_t {
- int numraster;
- rt_pgraster **pgraster;
- rt_raster *raster;
- uint8_t *isempty; /* flag indicating if raster is empty */
- uint8_t *ownsdata; /* is the raster self owned or just a pointer to another raster */
- int *nband; /* source raster's band index, 0-based */
- uint8_t *hasband; /* does source raster have band at index nband? */
-
- rt_pixtype pixtype; /* output raster band's pixel type */
- int hasnodata; /* NODATA flag */
- double nodataval; /* NODATA value */
-
- int distance[2]; /* distance in X and Y axis */
-
- rt_extenttype extenttype; /* ouput raster's extent type */
- rt_pgraster *pgcextent; /* custom extent of type rt_pgraster */
- rt_raster cextent; /* custom extent of type rt_raster */
-
- rtpg_nmapalgebra_callback_arg callback;
-};
-
-static rtpg_nmapalgebra_arg rtpg_nmapalgebra_arg_init() {
- rtpg_nmapalgebra_arg arg = NULL;
-
- arg = palloc(sizeof(struct rtpg_nmapalgebra_arg_t));
- if (arg == NULL) {
- elog(ERROR, "rtpg_nmapalgebra_arg_init: Could not allocate memory for arguments");
- return NULL;
- }
-
- arg->numraster = 0;
- arg->pgraster = NULL;
- arg->raster = NULL;
- arg->isempty = NULL;
- arg->ownsdata = NULL;
- arg->nband = NULL;
- arg->hasband = NULL;
-
- arg->pixtype = PT_END;
- arg->hasnodata = 1;
- arg->nodataval = 0;
-
- arg->distance[0] = 0;
- arg->distance[1] = 0;
-
- arg->extenttype = ET_INTERSECTION;
- arg->pgcextent = NULL;
- arg->cextent = NULL;
-
- arg->callback.ufc_noid = InvalidOid;
- arg->callback.ufc_nullcount = 0;
-
- return arg;
-}
-
-static void rtpg_nmapalgebra_arg_destroy(rtpg_nmapalgebra_arg arg) {
- int i = 0;
-
- if (arg->raster != NULL) {
- for (i = 0; i < arg->numraster; i++) {
- if (arg->raster[i] == NULL || !arg->ownsdata[i])
- continue;
-
- rt_raster_destroy(arg->raster[i]);
- }
-
- pfree(arg->raster);
- pfree(arg->pgraster);
- pfree(arg->isempty);
- pfree(arg->ownsdata);
- pfree(arg->nband);
- }
-
- if (arg->cextent != NULL)
- rt_raster_destroy(arg->cextent);
-
- pfree(arg);
-}
-
-static int rtpg_nmapalgebra_rastbandarg_process(rtpg_nmapalgebra_arg arg, ArrayType *array, int *allnull, int *allempty, int *noband) {
- Oid etype;
- Datum *e;
- bool *nulls;
- int16 typlen;
- bool typbyval;
- char typalign;
- int n = 0;
-
- HeapTupleHeader tup;
- bool isnull;
- Datum tupv;
-
- int i;
- int j;
- int nband;
-
- if (arg == NULL || array == NULL) {
- elog(ERROR, "rtpg_nmapalgebra_rastbandarg_process: NULL values not permitted for parameters");
- return 0;
- }
-
- etype = ARR_ELEMTYPE(array);
- get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
-
- deconstruct_array(
- array,
- etype,
- typlen, typbyval, typalign,
- &e, &nulls, &n
- );
-
- if (!n) {
- elog(ERROR, "rtpg_nmapalgebra_rastbandarg_process: Invalid argument for rastbandarg");
- return 0;
- }
-
- /* prep arg */
- arg->numraster = n;
- arg->pgraster = palloc(sizeof(rt_pgraster *) * arg->numraster);
- arg->raster = palloc(sizeof(rt_raster) * arg->numraster);
- arg->isempty = palloc(sizeof(uint8_t) * arg->numraster);
- arg->ownsdata = palloc(sizeof(uint8_t) * arg->numraster);
- arg->nband = palloc(sizeof(int) * arg->numraster);
- arg->hasband = palloc(sizeof(uint8_t) * arg->numraster);
- if (
- arg->pgraster == NULL ||
- arg->raster == NULL ||
- arg->isempty == NULL ||
- arg->ownsdata == NULL ||
- arg->nband == NULL ||
- arg->hasband == NULL
- ) {
- elog(ERROR, "rtpg_nmapalgebra_rastbandarg_process: Could not allocate memory for processing rastbandarg");
- return 0;
- }
-
- *allnull = 0;
- *allempty = 0;
- *noband = 0;
-
- /* process each element */
- for (i = 0; i < n; i++) {
- if (nulls[i]) {
- arg->numraster--;
- continue;
- }
-
- POSTGIS_RT_DEBUGF(4, "Processing rastbandarg at index %d", i);
-
- arg->raster[i] = NULL;
- arg->isempty[i] = 0;
- arg->ownsdata[i] = 1;
- arg->nband[i] = 0;
- arg->hasband[i] = 0;
-
- /* each element is a tuple */
- tup = (HeapTupleHeader) DatumGetPointer(e[i]);
- if (NULL == tup) {
- elog(ERROR, "rtpg_nmapalgebra_rastbandarg_process: Invalid argument for rastbandarg at index %d", i);
- return 0;
- }
-
- /* first element, raster */
- POSTGIS_RT_DEBUG(4, "Processing first element (raster)");
- tupv = GetAttributeByName(tup, "rast", &isnull);
- if (isnull) {
- elog(NOTICE, "First argument (nband) of rastbandarg at index %d is NULL. Assuming NULL raster", i);
- arg->isempty[i] = 1;
- arg->ownsdata[i] = 0;
-
- (*allnull)++;
- (*allempty)++;
- (*noband)++;
-
- continue;
- }
-
- arg->pgraster[i] = (rt_pgraster *) PG_DETOAST_DATUM(tupv);
-
- /* see if this is a copy of an existing pgraster */
- for (j = 0; j < i; j++) {
- if (!arg->isempty[j] && (arg->pgraster[i] == arg->pgraster[j])) {
- POSTGIS_RT_DEBUG(4, "raster matching existing same raster found");
- arg->raster[i] = arg->raster[j];
- arg->ownsdata[i] = 0;
- break;
- }
- }
-
- if (arg->ownsdata[i]) {
- POSTGIS_RT_DEBUG(4, "deserializing raster");
- arg->raster[i] = rt_raster_deserialize(arg->pgraster[i], FALSE);
- if (arg->raster[i] == NULL) {
- elog(ERROR, "rtpg_nmapalgebra_rastbandarg_process: Could not deserialize raster at index %d", i);
- return 0;
- }
- }
-
- /* is raster empty? */
- arg->isempty[i] = rt_raster_is_empty(arg->raster[i]);
- if (arg->isempty[i]) {
- (*allempty)++;
- (*noband)++;
-
- continue;
- }
-
- /* second element, nband */
- POSTGIS_RT_DEBUG(4, "Processing second element (nband)");
- tupv = GetAttributeByName(tup, "nband", &isnull);
- if (isnull) {
- nband = 1;
- elog(NOTICE, "First argument (nband) of rastbandarg at index %d is NULL. Assuming nband = %d", i, nband);
- }
- else
- nband = DatumGetInt32(tupv);
-
- if (nband < 1) {
- elog(ERROR, "rtpg_nmapalgebra_rastbandarg_process: Band number provided for rastbandarg at index %d must be greater than zero (1-based)", i);
- return 0;
- }
-
- arg->nband[i] = nband - 1;
- arg->hasband[i] = rt_raster_has_band(arg->raster[i], arg->nband[i]);
- if (!arg->hasband[i]) {
- (*noband)++;
- POSTGIS_RT_DEBUGF(4, "Band at index %d not found in raster", nband);
- }
- }
-
- if (arg->numraster < n) {
- arg->pgraster = repalloc(arg->pgraster, sizeof(rt_pgraster *) * arg->numraster);
- arg->raster = repalloc(arg->raster, sizeof(rt_raster) * arg->numraster);
- arg->isempty = repalloc(arg->isempty, sizeof(uint8_t) * arg->numraster);
- arg->ownsdata = repalloc(arg->ownsdata, sizeof(uint8_t) * arg->numraster);
- arg->nband = repalloc(arg->nband, sizeof(int) * arg->numraster);
- arg->hasband = repalloc(arg->hasband, sizeof(uint8_t) * arg->numraster);
- if (
- arg->pgraster == NULL ||
- arg->raster == NULL ||
- arg->isempty == NULL ||
- arg->ownsdata == NULL ||
- arg->nband == NULL ||
- arg->hasband == NULL
- ) {
- elog(ERROR, "rtpg_nmapalgebra_rastbandarg_process: Could not reallocate memory for processed rastbandarg");
- return 0;
- }
- }
-
- POSTGIS_RT_DEBUGF(4, "arg->numraster = %d", arg->numraster);
-
- return 1;
-}
-
-/*
- Callback for RASTER_nMapAlgebra
-*/
-static int rtpg_nmapalgebra_callback(
- rt_iterator_arg arg, void *userarg,
- double *value, int *nodata
-) {
- rtpg_nmapalgebra_callback_arg *callback = (rtpg_nmapalgebra_callback_arg *) userarg;
-
- int16 typlen;
- bool typbyval;
- char typalign;
-
- ArrayType *mdValues = NULL;
- Datum *_values = NULL;
- bool *_nodata = NULL;
-
- ArrayType *mdPos = NULL;
- Datum *_pos = NULL;
- bool *_null = NULL;
-
- int i = 0;
- int x = 0;
- int y = 0;
- int z = 0;
- int dim[3] = {0};
- int lbound[3] = {1, 1, 1};
- Datum datum = (Datum) NULL;
-
- if (arg == NULL)
- return 0;
-
- *value = 0;
- *nodata = 0;
-
- dim[0] = arg->rasters;
- dim[1] = arg->rows;
- dim[2] = arg->columns;
-
- _values = palloc(sizeof(Datum) * arg->rasters * arg->rows * arg->columns);
- _nodata = palloc(sizeof(bool) * arg->rasters * arg->rows * arg->columns);
- if (_values == NULL || _nodata == NULL) {
- elog(ERROR, "rtpg_nmapalgebra_callback: Could not allocate memory for values array");
- return 0;
- }
-
- /* build mdValues */
- i = 0;
- /* raster */
- for (z = 0; z < arg->rasters; z++) {
- /* Y axis */
- for (y = 0; y < arg->rows; y++) {
- /* X axis */
- for (x = 0; x < arg->columns; x++) {
- POSTGIS_RT_DEBUGF(4, "(z, y ,x) = (%d, %d, %d)", z, y, x);
- POSTGIS_RT_DEBUGF(4, "(value, nodata) = (%f, %d)", arg->values[z][y][x], arg->nodata[z][y][x]);
-
- _nodata[i] = (bool) arg->nodata[z][y][x];
- if (!_nodata[i])
- _values[i] = Float8GetDatum(arg->values[z][y][x]);
- else
- _values[i] = (Datum) NULL;
-
- i++;
- }
- }
- }
-
- /* info about the type of item in the multi-dimensional array (float8). */
- get_typlenbyvalalign(FLOAT8OID, &typlen, &typbyval, &typalign);
-
- /* construct mdValues */
- mdValues = construct_md_array(
- _values, _nodata,
- 3, dim, lbound,
- FLOAT8OID,
- typlen, typbyval, typalign
- );
- pfree(_nodata);
- pfree(_values);
-
- _pos = palloc(sizeof(Datum) * (arg->rasters + 1) * 2);
- _null = palloc(sizeof(bool) * (arg->rasters + 1) * 2);
- if (_pos == NULL || _null == NULL) {
- pfree(mdValues);
- elog(ERROR, "rtpg_nmapalgebra_callback: Could not allocate memory for position array");
- return 0;
- }
- memset(_null, 0, sizeof(bool) * (arg->rasters + 1) * 2);
-
- /* build mdPos */
- i = 0;
- _pos[i] = arg->dst_pixel[0] + 1;
- i++;
- _pos[i] = arg->dst_pixel[1] + 1;
- i++;
-
- for (z = 0; z < arg->rasters; z++) {
- _pos[i] = arg->src_pixel[z][0] + 1;
- i++;
-
- _pos[i] = arg->src_pixel[z][1] + 1;
- i++;
- }
-
- /* info about the type of item in the multi-dimensional array (int4). */
- get_typlenbyvalalign(INT4OID, &typlen, &typbyval, &typalign);
-
- /* reuse dim and lbound, just tweak to what we need */
- dim[0] = arg->rasters + 1;
- dim[1] = 2;
- lbound[0] = 0;
-
- /* construct mdPos */
- mdPos = construct_md_array(
- _pos, _null,
- 2, dim, lbound,
- INT4OID,
- typlen, typbyval, typalign
- );
- pfree(_pos);
- pfree(_null);
-
- callback->ufc_info.arg[0] = PointerGetDatum(mdValues);
- callback->ufc_info.arg[1] = PointerGetDatum(mdPos);
-
- /* function is strict and null parameter is passed */
- /* http://archives.postgresql.org/pgsql-general/2011-11/msg00424.php */
- if (callback->ufl_info.fn_strict && callback->ufc_nullcount) {
- *nodata = 1;
-
- pfree(mdValues);
- pfree(mdPos);
-
- return 1;
- }
-
- /* call user callback function */
- datum = FunctionCallInvoke(&(callback->ufc_info));
- pfree(mdValues);
- pfree(mdPos);
-
- /* result is not null*/
- if (!callback->ufc_info.isnull)
- *value = DatumGetFloat8(datum);
- else
- *nodata = 1;
-
- return 1;
-}
-
-/*
- ST_MapAlgebra for n rasters
-*/
-PG_FUNCTION_INFO_V1(RASTER_nMapAlgebra);
-Datum RASTER_nMapAlgebra(PG_FUNCTION_ARGS)
-{
- rtpg_nmapalgebra_arg arg = NULL;
- rt_iterator itrset;
- int i = 0;
- int noerr = 0;
- int allnull = 0;
- int allempty = 0;
- int noband = 0;
-
- rt_raster raster = NULL;
- rt_band band = NULL;
- rt_pgraster *pgraster = NULL;
-
- POSTGIS_RT_DEBUG(3, "Starting...");
-
- if (PG_ARGISNULL(0))
- PG_RETURN_NULL();
-
- /* init argument struct */
- arg = rtpg_nmapalgebra_arg_init();
- if (arg == NULL) {
- elog(ERROR, "RASTER_nMapAlgebra: Could not initialize argument structure");
- PG_RETURN_NULL();
- }
-
- /* let helper function process rastbandarg (0) */
- if (!rtpg_nmapalgebra_rastbandarg_process(arg, PG_GETARG_ARRAYTYPE_P(0), &allnull, &allempty, &noband)) {
- rtpg_nmapalgebra_arg_destroy(arg);
- elog(ERROR, "RASTER_nMapAlgebra: Could not process rastbandarg");
- PG_RETURN_NULL();
- }
-
- POSTGIS_RT_DEBUGF(4, "allnull, allempty, noband = %d, %d, %d", allnull, allempty, noband);
-
- /* all rasters are NULL, return NULL */
- if (allnull == arg->numraster) {
- elog(NOTICE, "All input rasters are NULL. Returning NULL");
- rtpg_nmapalgebra_arg_destroy(arg);
- PG_RETURN_NULL();
- }
-
- /* pixel type (2) */
- if (!PG_ARGISNULL(2)) {
- char *pixtypename = text_to_cstring(PG_GETARG_TEXT_P(2));
-
- /* Get the pixel type index */
- arg->pixtype = rt_pixtype_index_from_name(pixtypename);
- if (arg->pixtype == PT_END) {
- rtpg_nmapalgebra_arg_destroy(arg);
- elog(ERROR, "RASTER_nMapAlgebra: Invalid pixel type: %s", pixtypename);
- PG_RETURN_NULL();
- }
- }
-
- /* distancex (3) */
- if (!PG_ARGISNULL(3))
- arg->distance[0] = PG_GETARG_INT32(3);
- /* distancey (4) */
- if (!PG_ARGISNULL(4))
- arg->distance[1] = PG_GETARG_INT32(4);
-
- if (arg->distance[0] < 0 || arg->distance[1] < 0) {
- rtpg_nmapalgebra_arg_destroy(arg);
- elog(ERROR, "RASTER_nMapAlgebra: Distance for X and Y axis must be greater than or equal to zero");
- PG_RETURN_NULL();
- }
-
- /* extent type (5) */
- if (!PG_ARGISNULL(5)) {
- char *extenttypename = rtpg_strtoupper(rtpg_trim(text_to_cstring(PG_GETARG_TEXT_P(5))));
- arg->extenttype = rt_util_extent_type(extenttypename);
- }
- POSTGIS_RT_DEBUGF(4, "extenttype: %d", arg->extenttype);
-
- /* custom extent (6) */
- if (arg->extenttype == ET_CUSTOM) {
- if (PG_ARGISNULL(6)) {
- elog(NOTICE, "Custom extent is NULL. Returning NULL");
- rtpg_nmapalgebra_arg_destroy(arg);
- PG_RETURN_NULL();
- }
-
- arg->pgcextent = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(6));
-
- /* only need the raster header */
- arg->cextent = rt_raster_deserialize(arg->pgcextent, TRUE);
- if (arg->cextent == NULL) {
- rtpg_nmapalgebra_arg_destroy(arg);
- elog(ERROR, "RASTER_nMapAlgebra: Could not deserialize custom extent");
- PG_RETURN_NULL();
- }
- else if (rt_raster_is_empty(arg->cextent)) {
- elog(NOTICE, "Custom extent is an empty raster. Returning empty raster");
- rtpg_nmapalgebra_arg_destroy(arg);
-
- raster = rt_raster_new(0, 0);
- if (raster == NULL) {
- elog(ERROR, "RASTER_nMapAlgebra: Could not create empty raster");
- PG_RETURN_NULL();
- }
-
- pgraster = rt_raster_serialize(raster);
- rt_raster_destroy(raster);
- if (!pgraster) PG_RETURN_NULL();
-
- SET_VARSIZE(pgraster, pgraster->size);
- PG_RETURN_POINTER(pgraster);
- }
- }
-
- noerr = 1;
- /* all rasters are empty, return empty raster */
- if (allempty == arg->numraster) {
- elog(NOTICE, "All input rasters are empty. Returning empty raster");
- noerr = 0;
- }
- /* all rasters don't have indicated band, return empty raster */
- else if (noband == arg->numraster) {
- elog(NOTICE, "All input rasters do not have bands at indicated indexes. Returning empty raster");
- noerr = 0;
- }
- if (!noerr) {
- rtpg_nmapalgebra_arg_destroy(arg);
-
- raster = rt_raster_new(0, 0);
- if (raster == NULL) {
- elog(ERROR, "RASTER_nMapAlgebra: Could not create empty raster");
- PG_RETURN_NULL();
- }
-
- pgraster = rt_raster_serialize(raster);
- rt_raster_destroy(raster);
- if (!pgraster) PG_RETURN_NULL();
-
- SET_VARSIZE(pgraster, pgraster->size);
- PG_RETURN_POINTER(pgraster);
- }
-
- /* do regprocedure last (1) */
- if (!PG_ARGISNULL(1) || get_fn_expr_argtype(fcinfo->flinfo, 1) == REGPROCEDUREOID) {
- POSTGIS_RT_DEBUG(4, "processing callbackfunc");
- arg->callback.ufc_noid = PG_GETARG_OID(1);
-
- /* get function info */
- fmgr_info(arg->callback.ufc_noid, &(arg->callback.ufl_info));
-
- /* function cannot return set */
- noerr = 0;
- if (arg->callback.ufl_info.fn_retset) {
- noerr = 1;
- }
- /* function should have correct # of args */
- else if (arg->callback.ufl_info.fn_nargs != 3) {
- noerr = 2;
- }
-
- /*
- TODO: consider adding checks of the userfunction parameters
- should be able to use get_fn_expr_argtype() of fmgr.c
- */
-
- if (noerr > 0) {
- rtpg_nmapalgebra_arg_destroy(arg);
- if (noerr > 1)
- elog(ERROR, "RASTER_nMapAlgebra: Function provided must have three input parameters");
- else
- elog(ERROR, "RASTER_nMapAlgebra: Function provided must return double precision, not resultset");
- PG_RETURN_NULL();
- }
-
- if (func_volatile(arg->callback.ufc_noid) == 'v')
- elog(NOTICE, "Function provided is VOLATILE. Unless required and for best performance, function should be IMMUTABLE or STABLE");
-
- /* prep function call data */
-#if POSTGIS_PGSQL_VERSION > 90
- InitFunctionCallInfoData(arg->callback.ufc_info, &(arg->callback.ufl_info), arg->callback.ufl_info.fn_nargs, InvalidOid, NULL, NULL);
-#else
- InitFunctionCallInfoData(arg->callback.ufc_info, &(arg->callback.ufl_info), arg->callback.ufl_info.fn_nargs, NULL, NULL);
-#endif
- memset(arg->callback.ufc_info.argnull, FALSE, sizeof(bool) * arg->callback.ufl_info.fn_nargs);
-
- /* userargs (7) */
- if (!PG_ARGISNULL(7))
- arg->callback.ufc_info.arg[2] = PG_GETARG_DATUM(7);
- else {
- arg->callback.ufc_info.arg[2] = (Datum) NULL;
- arg->callback.ufc_info.argnull[2] = TRUE;
- arg->callback.ufc_nullcount++;
- }
- }
- else {
- rtpg_nmapalgebra_arg_destroy(arg);
- elog(ERROR, "RASTER_nMapAlgebra: callbackfunc must be provided");
- PG_RETURN_NULL();
- }
-
- /* determine nodataval and possibly pixtype */
- /* band to check */
- switch (arg->extenttype) {
- case ET_LAST:
- i = arg->numraster - 1;
- break;
- case ET_SECOND:
- if (arg->numraster > 1) {
- i = 1;
- break;
- }
- default:
- i = 0;
- break;
- }
- /* find first viable band */
- if (!arg->hasband[i]) {
- for (i = 0; i < arg->numraster; i++) {
- if (arg->hasband[i])
- break;
- }
- if (i >= arg->numraster)
- i = arg->numraster - 1;
- }
- band = rt_raster_get_band(arg->raster[i], arg->nband[i]);
-
- /* set pixel type if PT_END */
- if (arg->pixtype == PT_END)
- arg->pixtype = rt_band_get_pixtype(band);
-
- /* set hasnodata and nodataval */
- arg->hasnodata = 1;
- if (rt_band_get_hasnodata_flag(band))
- rt_band_get_nodata(band, &(arg->nodataval));
- else
- arg->nodataval = rt_band_get_min_value(band);
-
- POSTGIS_RT_DEBUGF(4, "pixtype, hasnodata, nodataval: %s, %d, %f", rt_pixtype_name(arg->pixtype), arg->hasnodata, arg->nodataval);
-
- /* init itrset */
- itrset = palloc(sizeof(struct rt_iterator_t) * arg->numraster);
- if (itrset == NULL) {
- rtpg_nmapalgebra_arg_destroy(arg);
- elog(ERROR, "RASTER_nMapAlgebra: Could not allocate memory for iterator arguments");
- PG_RETURN_NULL();
- }
-
- /* set itrset */
- for (i = 0; i < arg->numraster; i++) {
- itrset[i].raster = arg->raster[i];
- itrset[i].nband = arg->nband[i];
- itrset[i].nbnodata = 1;
- }
-
- /* pass everything to iterator */
- noerr = rt_raster_iterator(
- itrset, arg->numraster,
- arg->extenttype, arg->cextent,
- arg->pixtype,
- arg->hasnodata, arg->nodataval,
- arg->distance[0], arg->distance[1],
- &(arg->callback),
- rtpg_nmapalgebra_callback,
- &raster
- );
-
- /* cleanup */
- pfree(itrset);
- rtpg_nmapalgebra_arg_destroy(arg);
-
- if (noerr != ES_NONE) {
- elog(ERROR, "RASTER_nMapAlgebra: Could not run raster iterator function");
- PG_RETURN_NULL();
- }
- else if (raster == NULL)
- PG_RETURN_NULL();
-
- pgraster = rt_raster_serialize(raster);
- rt_raster_destroy(raster);
-
- POSTGIS_RT_DEBUG(3, "Finished");
-
- if (!pgraster)
- PG_RETURN_NULL();
-
- SET_VARSIZE(pgraster, pgraster->size);
- PG_RETURN_POINTER(pgraster);
-}
-
-/* ---------------------------------------------------------------- */
-/* expression ST_MapAlgebra for n rasters */
-/* ---------------------------------------------------------------- */
-
-typedef struct {
- int exprcount;
-
- struct {
- SPIPlanPtr spi_plan;
- uint32_t spi_argcount;
- uint8_t *spi_argpos;
-
- int hasval;
- double val;
- } expr[3];
-
- struct {
- int hasval;
- double val;
- } nodatanodata;
-
- struct {
- int count;
- char **val;
- } kw;
-
-} rtpg_nmapalgebraexpr_callback_arg;
-
-typedef struct rtpg_nmapalgebraexpr_arg_t *rtpg_nmapalgebraexpr_arg;
-struct rtpg_nmapalgebraexpr_arg_t {
- rtpg_nmapalgebra_arg bandarg;
-
- rtpg_nmapalgebraexpr_callback_arg callback;
-};
-
-static rtpg_nmapalgebraexpr_arg rtpg_nmapalgebraexpr_arg_init(int cnt, char **kw) {
- rtpg_nmapalgebraexpr_arg arg = NULL;
- int i = 0;
-
- arg = palloc(sizeof(struct rtpg_nmapalgebraexpr_arg_t));
- if (arg == NULL) {
- elog(ERROR, "rtpg_nmapalgebraexpr_arg_init: Could not allocate memory for arguments");
- return NULL;
- }
-
- arg->bandarg = rtpg_nmapalgebra_arg_init();
- if (arg->bandarg == NULL) {
- elog(ERROR, "rtpg_nmapalgebraexpr_arg_init: Could not allocate memory for arg->bandarg");
- return NULL;
- }
-
- arg->callback.kw.count = cnt;
- arg->callback.kw.val = kw;
-
- arg->callback.exprcount = 3;
- for (i = 0; i < arg->callback.exprcount; i++) {
- arg->callback.expr[i].spi_plan = NULL;
- arg->callback.expr[i].spi_argcount = 0;
- arg->callback.expr[i].spi_argpos = palloc(cnt * sizeof(uint8_t));
- if (arg->callback.expr[i].spi_argpos == NULL) {
- elog(ERROR, "rtpg_nmapalgebraexpr_arg_init: Could not allocate memory for spi_argpos");
- return NULL;
- }
- memset(arg->callback.expr[i].spi_argpos, 0, sizeof(uint8_t) * cnt);
- arg->callback.expr[i].hasval = 0;
- arg->callback.expr[i].val = 0;
- }
-
- arg->callback.nodatanodata.hasval = 0;
- arg->callback.nodatanodata.val = 0;
-
- return arg;
-}
-
-static void rtpg_nmapalgebraexpr_arg_destroy(rtpg_nmapalgebraexpr_arg arg) {
- int i = 0;
-
- rtpg_nmapalgebra_arg_destroy(arg->bandarg);
-
- for (i = 0; i < arg->callback.exprcount; i++) {
- if (arg->callback.expr[i].spi_plan)
- SPI_freeplan(arg->callback.expr[i].spi_plan);
- if (arg->callback.kw.count)
- pfree(arg->callback.expr[i].spi_argpos);
- }
-
- pfree(arg);
-}
-
-static int rtpg_nmapalgebraexpr_callback(
- rt_iterator_arg arg, void *userarg,
- double *value, int *nodata
-) {
- rtpg_nmapalgebraexpr_callback_arg *callback = (rtpg_nmapalgebraexpr_callback_arg *) userarg;
- SPIPlanPtr plan = NULL;
- int i = 0;
- int id = -1;
-
- if (arg == NULL)
- return 0;
-
- *value = 0;
- *nodata = 0;
-
- /* 2 raster */
- if (arg->rasters > 1) {
- /* nodata1 = 1 AND nodata2 = 1, nodatanodataval */
- if (arg->nodata[0][0][0] && arg->nodata[1][0][0]) {
- if (callback->nodatanodata.hasval)
- *value = callback->nodatanodata.val;
- else
- *nodata = 1;
- }
- /* nodata1 = 1 AND nodata2 != 1, nodata1expr */
- else if (arg->nodata[0][0][0] && !arg->nodata[1][0][0]) {
- id = 1;
- if (callback->expr[id].hasval)
- *value = callback->expr[id].val;
- else if (callback->expr[id].spi_plan)
- plan = callback->expr[id].spi_plan;
- else
- *nodata = 1;
- }
- /* nodata1 != 1 AND nodata2 = 1, nodata2expr */
- else if (!arg->nodata[0][0][0] && arg->nodata[1][0][0]) {
- id = 2;
- if (callback->expr[id].hasval)
- *value = callback->expr[id].val;
- else if (callback->expr[id].spi_plan)
- plan = callback->expr[id].spi_plan;
- else
- *nodata = 1;
- }
- /* expression */
- else {
- id = 0;
- if (callback->expr[id].hasval)
- *value = callback->expr[id].val;
- else if (callback->expr[id].spi_plan)
- plan = callback->expr[id].spi_plan;
- else {
- if (callback->nodatanodata.hasval)
- *value = callback->nodatanodata.val;
- else
- *nodata = 1;
- }
- }
- }
- /* 1 raster */
- else {
- /* nodata = 1, nodata1expr */
- if (arg->nodata[0][0][0]) {
- id = 1;
- if (callback->expr[id].hasval)
- *value = callback->expr[id].val;
- else if (callback->expr[id].spi_plan)
- plan = callback->expr[id].spi_plan;
- else
- *nodata = 1;
- }
- /* expression */
- else {
- id = 0;
- if (callback->expr[id].hasval)
- *value = callback->expr[id].val;
- else if (callback->expr[id].spi_plan)
- plan = callback->expr[id].spi_plan;
- else {
- /* see if nodata1expr is available */
- id = 1;
- if (callback->expr[id].hasval)
- *value = callback->expr[id].val;
- else if (callback->expr[id].spi_plan)
- plan = callback->expr[id].spi_plan;
- else
- *nodata = 1;
- }
- }
- }
-
- /* run prepared plan */
- if (plan != NULL) {
- Datum values[12];
- bool nulls[12];
- int err = 0;
-
- TupleDesc tupdesc;
- SPITupleTable *tuptable = NULL;
- HeapTuple tuple;
- Datum datum;
- bool isnull = FALSE;
-
- POSTGIS_RT_DEBUGF(4, "Running plan %d", id);
-
- /* init values and nulls */
- memset(values, (Datum) NULL, sizeof(Datum) * callback->kw.count);
- memset(nulls, FALSE, sizeof(bool) * callback->kw.count);
-
- if (callback->expr[id].spi_argcount) {
- int idx = 0;
-
- for (i = 0; i < callback->kw.count; i++) {
- idx = callback->expr[id].spi_argpos[i];
- if (idx < 1) continue;
- idx--; /* 1-based now 0-based */
-
- switch (i) {
- /* [rast.x] */
- case 0:
- values[idx] = Int32GetDatum(arg->src_pixel[0][0] + 1);
- break;
- /* [rast.y] */
- case 1:
- values[idx] = Int32GetDatum(arg->src_pixel[0][1] + 1);
- break;
- /* [rast.val] */
- case 2:
- /* [rast] */
- case 3:
- if (!arg->nodata[0][0][0])
- values[idx] = Float8GetDatum(arg->values[0][0][0]);
- else
- nulls[idx] = TRUE;
- break;
-
- /* [rast1.x] */
- case 4:
- values[idx] = Int32GetDatum(arg->src_pixel[0][0] + 1);
- break;
- /* [rast1.y] */
- case 5:
- values[idx] = Int32GetDatum(arg->src_pixel[0][1] + 1);
- break;
- /* [rast1.val] */
- case 6:
- /* [rast1] */
- case 7:
- if (!arg->nodata[0][0][0])
- values[idx] = Float8GetDatum(arg->values[0][0][0]);
- else
- nulls[idx] = TRUE;
- break;
-
- /* [rast2.x] */
- case 8:
- values[idx] = Int32GetDatum(arg->src_pixel[1][0] + 1);
- break;
- /* [rast2.y] */
- case 9:
- values[idx] = Int32GetDatum(arg->src_pixel[1][1] + 1);
- break;
- /* [rast2.val] */
- case 10:
- /* [rast2] */
- case 11:
- if (!arg->nodata[1][0][0])
- values[idx] = Float8GetDatum(arg->values[1][0][0]);
- else
- nulls[idx] = TRUE;
- break;
- }
-
- }
- }
-
- /* run prepared plan */
- err = SPI_execute_plan(plan, values, nulls, TRUE, 1);
- if (err != SPI_OK_SELECT || SPI_tuptable == NULL || SPI_processed != 1) {
- elog(ERROR, "rtpg_nmapalgebraexpr_callback: Unexpected error when running prepared statement %d", id);
- return 0;
- }
-
- /* 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) {
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- elog(ERROR, "rtpg_nmapalgebraexpr_callback: Could not get result of prepared statement %d", id);
- return 0;
- }
-
- if (!isnull) {
- *value = DatumGetFloat8(datum);
- POSTGIS_RT_DEBUG(4, "Getting value from Datum");
- }
- else {
- /* 2 raster, check nodatanodataval */
- if (arg->rasters > 1) {
- if (callback->nodatanodata.hasval)
- *value = callback->nodatanodata.val;
- else
- *nodata = 1;
- }
- /* 1 raster, check nodataval */
- else {
- if (callback->expr[1].hasval)
- *value = callback->expr[1].val;
- else
- *nodata = 1;
- }
- }
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- }
-
- POSTGIS_RT_DEBUGF(4, "(value, nodata) = (%f, %d)", *value, *nodata);
- return 1;
-}
-
-PG_FUNCTION_INFO_V1(RASTER_nMapAlgebraExpr);
-Datum RASTER_nMapAlgebraExpr(PG_FUNCTION_ARGS)
-{
- MemoryContext mainMemCtx = CurrentMemoryContext;
- rtpg_nmapalgebraexpr_arg arg = NULL;
- rt_iterator itrset;
- uint16_t exprpos[3] = {1, 4, 5};
-
- int i = 0;
- int j = 0;
- int k = 0;
-
- int numraster = 0;
- int err = 0;
- int allnull = 0;
- int allempty = 0;
- int noband = 0;
- int len = 0;
-
- TupleDesc tupdesc;
- SPITupleTable *tuptable = NULL;
- HeapTuple tuple;
- Datum datum;
- bool isnull = FALSE;
-
- rt_raster raster = NULL;
- rt_band band = NULL;
- rt_pgraster *pgraster = NULL;
-
- const int argkwcount = 12;
- char *argkw[] = {
- "[rast.x]",
- "[rast.y]",
- "[rast.val]",
- "[rast]",
- "[rast1.x]",
- "[rast1.y]",
- "[rast1.val]",
- "[rast1]",
- "[rast2.x]",
- "[rast2.y]",
- "[rast2.val]",
- "[rast2]"
- };
-
- if (PG_ARGISNULL(0))
- PG_RETURN_NULL();
-
- /* init argument struct */
- arg = rtpg_nmapalgebraexpr_arg_init(argkwcount, argkw);
- if (arg == NULL) {
- elog(ERROR, "RASTER_nMapAlgebraExpr: Could not initialize argument structure");
- PG_RETURN_NULL();
- }
-
- /* let helper function process rastbandarg (0) */
- if (!rtpg_nmapalgebra_rastbandarg_process(arg->bandarg, PG_GETARG_ARRAYTYPE_P(0), &allnull, &allempty, &noband)) {
- rtpg_nmapalgebraexpr_arg_destroy(arg);
- elog(ERROR, "RASTER_nMapAlgebra: Could not process rastbandarg");
- PG_RETURN_NULL();
- }
-
- POSTGIS_RT_DEBUGF(4, "allnull, allempty, noband = %d, %d, %d", allnull, allempty, noband);
-
- /* all rasters are NULL, return NULL */
- if (allnull == arg->bandarg->numraster) {
- elog(NOTICE, "All input rasters are NULL. Returning NULL");
- rtpg_nmapalgebraexpr_arg_destroy(arg);
- PG_RETURN_NULL();
- }
-
- /* only work on one or two rasters */
- if (arg->bandarg->numraster > 1)
- numraster = 2;
- else
- numraster = 1;
-
- /* pixel type (2) */
- if (!PG_ARGISNULL(2)) {
- char *pixtypename = text_to_cstring(PG_GETARG_TEXT_P(2));
-
- /* Get the pixel type index */
- arg->bandarg->pixtype = rt_pixtype_index_from_name(pixtypename);
- if (arg->bandarg->pixtype == PT_END) {
- rtpg_nmapalgebraexpr_arg_destroy(arg);
- elog(ERROR, "RASTER_nMapAlgebraExpr: Invalid pixel type: %s", pixtypename);
- PG_RETURN_NULL();
- }
- }
- POSTGIS_RT_DEBUGF(4, "pixeltype: %d", arg->bandarg->pixtype);
-
- /* extent type (3) */
- if (!PG_ARGISNULL(3)) {
- char *extenttypename = rtpg_strtoupper(rtpg_trim(text_to_cstring(PG_GETARG_TEXT_P(3))));
- arg->bandarg->extenttype = rt_util_extent_type(extenttypename);
- }
-
- if (arg->bandarg->extenttype == ET_CUSTOM) {
- if (numraster < 2) {
- elog(NOTICE, "CUSTOM extent type not supported. Defaulting to FIRST");
- arg->bandarg->extenttype = ET_FIRST;
- }
- else {
- elog(NOTICE, "CUSTOM extent type not supported. Defaulting to INTERSECTION");
- arg->bandarg->extenttype = ET_INTERSECTION;
- }
- }
- else if (numraster < 2)
- arg->bandarg->extenttype = ET_FIRST;
-
- POSTGIS_RT_DEBUGF(4, "extenttype: %d", arg->bandarg->extenttype);
-
- /* nodatanodataval (6) */
- if (!PG_ARGISNULL(6)) {
- arg->callback.nodatanodata.hasval = 1;
- arg->callback.nodatanodata.val = PG_GETARG_FLOAT8(6);
- }
-
- err = 0;
- /* all rasters are empty, return empty raster */
- if (allempty == arg->bandarg->numraster) {
- elog(NOTICE, "All input rasters are empty. Returning empty raster");
- err = 1;
- }
- /* all rasters don't have indicated band, return empty raster */
- else if (noband == arg->bandarg->numraster) {
- elog(NOTICE, "All input rasters do not have bands at indicated indexes. Returning empty raster");
- err = 1;
- }
- if (err) {
- rtpg_nmapalgebraexpr_arg_destroy(arg);
-
- raster = rt_raster_new(0, 0);
- if (raster == NULL) {
- elog(ERROR, "RASTER_nMapAlgebraExpr: Could not create empty raster");
- PG_RETURN_NULL();
- }
-
- pgraster = rt_raster_serialize(raster);
- rt_raster_destroy(raster);
- if (!pgraster) PG_RETURN_NULL();
-
- SET_VARSIZE(pgraster, pgraster->size);
- PG_RETURN_POINTER(pgraster);
- }
-
- /* connect SPI */
- if (SPI_connect() != SPI_OK_CONNECT) {
- rtpg_nmapalgebraexpr_arg_destroy(arg);
- elog(ERROR, "RASTER_nMapAlgebraExpr: Could not connect to the SPI manager");
- PG_RETURN_NULL();
- }
-
- /*
- process expressions
-
- exprpos elements are:
- 1 - expression => spi_plan[0]
- 4 - nodata1expr => spi_plan[1]
- 5 - nodata2expr => spi_plan[2]
- */
- for (i = 0; i < arg->callback.exprcount; i++) {
- char *expr = NULL;
- char *tmp = NULL;
- char *sql = NULL;
- char place[5] = "$1";
-
- if (PG_ARGISNULL(exprpos[i]))
- continue;
-
- expr = text_to_cstring(PG_GETARG_TEXT_P(exprpos[i]));
- POSTGIS_RT_DEBUGF(3, "raw expr of argument #%d: %s", exprpos[i], expr);
-
- for (j = 0, k = 1; j < argkwcount; j++) {
- /* attempt to replace keyword with placeholder */
- len = 0;
- tmp = rtpg_strreplace(expr, argkw[j], place, &len);
- pfree(expr);
- expr = tmp;
-
- if (len) {
- POSTGIS_RT_DEBUGF(4, "kw #%d (%s) at pos $%d", j, argkw[j], k);
- arg->callback.expr[i].spi_argcount++;
- arg->callback.expr[i].spi_argpos[j] = k++;
-
- sprintf(place, "$%d", k);
- }
- else
- arg->callback.expr[i].spi_argpos[j] = 0;
- }
-
- len = strlen("SELECT (") + strlen(expr) + strlen(")::double precision");
- sql = (char *) palloc(len + 1);
- if (sql == NULL) {
- rtpg_nmapalgebraexpr_arg_destroy(arg);
- SPI_finish();
- elog(ERROR, "RASTER_nMapAlgebraExpr: Could not allocate memory for expression parameter %d", exprpos[i]);
- 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';
-
- POSTGIS_RT_DEBUGF(3, "sql #%d: %s", exprpos[i], sql);
-
- /* prepared plan */
- if (arg->callback.expr[i].spi_argcount) {
- Oid *argtype = (Oid *) palloc(arg->callback.expr[i].spi_argcount * sizeof(Oid));
- POSTGIS_RT_DEBUGF(3, "expression parameter %d is a prepared plan", exprpos[i]);
- if (argtype == NULL) {
- pfree(sql);
- rtpg_nmapalgebraexpr_arg_destroy(arg);
- SPI_finish();
- elog(ERROR, "RASTER_nMapAlgebraExpr: Could not allocate memory for prepared plan argtypes of expression parameter %d", exprpos[i]);
- PG_RETURN_NULL();
- }
-
- /* specify datatypes of parameters */
- for (j = 0, k = 0; j < argkwcount; j++) {
- if (arg->callback.expr[i].spi_argpos[j] < 1) continue;
-
- /* positions are INT4 */
- if (
- (strstr(argkw[j], "[rast.x]") != NULL) ||
- (strstr(argkw[j], "[rast.y]") != NULL) ||
- (strstr(argkw[j], "[rast1.x]") != NULL) ||
- (strstr(argkw[j], "[rast1.y]") != NULL) ||
- (strstr(argkw[j], "[rast2.x]") != NULL) ||
- (strstr(argkw[j], "[rast2.y]") != NULL)
- )
- argtype[k] = INT4OID;
- /* everything else is FLOAT8 */
- else
- argtype[k] = FLOAT8OID;
-
- k++;
- }
-
- arg->callback.expr[i].spi_plan = SPI_prepare(sql, arg->callback.expr[i].spi_argcount, argtype);
- pfree(argtype);
- pfree(sql);
-
- if (arg->callback.expr[i].spi_plan == NULL) {
- rtpg_nmapalgebraexpr_arg_destroy(arg);
- SPI_finish();
- elog(ERROR, "RASTER_nMapAlgebraExpr: Could not create prepared plan of expression parameter %d", exprpos[i]);
- PG_RETURN_NULL();
- }
- }
- /* no args, just execute query */
- else {
- POSTGIS_RT_DEBUGF(3, "expression parameter %d has no args, simply executing", exprpos[i]);
- err = SPI_execute(sql, TRUE, 0);
- pfree(sql);
-
- if (err != SPI_OK_SELECT || SPI_tuptable == NULL || SPI_processed != 1) {
- rtpg_nmapalgebraexpr_arg_destroy(arg);
- SPI_finish();
- elog(ERROR, "RASTER_nMapAlgebraExpr: Could not evaluate expression parameter %d", exprpos[i]);
- 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) {
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- rtpg_nmapalgebraexpr_arg_destroy(arg);
- SPI_finish();
- elog(ERROR, "RASTER_nMapAlgebraExpr: Could not get result of expression parameter %d", exprpos[i]);
- PG_RETURN_NULL();
- }
-
- if (!isnull) {
- arg->callback.expr[i].hasval = 1;
- arg->callback.expr[i].val = DatumGetFloat8(datum);
- }
-
- if (SPI_tuptable) SPI_freetuptable(tuptable);
- }
- }
-
- /* determine nodataval and possibly pixtype */
- /* band to check */
- switch (arg->bandarg->extenttype) {
- case ET_LAST:
- case ET_SECOND:
- if (numraster > 1)
- i = 1;
- else
- i = 0;
- break;
- default:
- i = 0;
- break;
- }
- /* find first viable band */
- if (!arg->bandarg->hasband[i]) {
- for (i = 0; i < numraster; i++) {
- if (arg->bandarg->hasband[i])
- break;
- }
- if (i >= numraster)
- i = numraster - 1;
- }
- band = rt_raster_get_band(arg->bandarg->raster[i], arg->bandarg->nband[i]);
-
- /* set pixel type if PT_END */
- if (arg->bandarg->pixtype == PT_END)
- arg->bandarg->pixtype = rt_band_get_pixtype(band);
-
- /* set hasnodata and nodataval */
- arg->bandarg->hasnodata = 1;
- if (rt_band_get_hasnodata_flag(band))
- rt_band_get_nodata(band, &(arg->bandarg->nodataval));
- else
- arg->bandarg->nodataval = rt_band_get_min_value(band);
-
- POSTGIS_RT_DEBUGF(4, "pixtype, hasnodata, nodataval: %s, %d, %f", rt_pixtype_name(arg->bandarg->pixtype), arg->bandarg->hasnodata, arg->bandarg->nodataval);
-
- /* init itrset */
- itrset = palloc(sizeof(struct rt_iterator_t) * numraster);
- if (itrset == NULL) {
- rtpg_nmapalgebraexpr_arg_destroy(arg);
- SPI_finish();
- elog(ERROR, "RASTER_nMapAlgebra: Could not allocate memory for iterator arguments");
- PG_RETURN_NULL();
- }
-
- /* set itrset */
- for (i = 0; i < numraster; i++) {
- itrset[i].raster = arg->bandarg->raster[i];
- itrset[i].nband = arg->bandarg->nband[i];
- itrset[i].nbnodata = 1;
- }
-
- /* pass everything to iterator */
- err = rt_raster_iterator(
- itrset, numraster,
- arg->bandarg->extenttype, arg->bandarg->cextent,
- arg->bandarg->pixtype,
- arg->bandarg->hasnodata, arg->bandarg->nodataval,
- 0, 0,
- &(arg->callback),
- rtpg_nmapalgebraexpr_callback,
- &raster
- );
-
- pfree(itrset);
- rtpg_nmapalgebraexpr_arg_destroy(arg);
-
- if (err != ES_NONE) {
- SPI_finish();
- elog(ERROR, "RASTER_nMapAlgebraExpr: Could not run raster iterator function");
- PG_RETURN_NULL();
- }
- else if (raster == NULL) {
- SPI_finish();
- PG_RETURN_NULL();
- }
-
- /* switch to prior memory context to ensure memory allocated in correct context */
- MemoryContextSwitchTo(mainMemCtx);
-
- pgraster = rt_raster_serialize(raster);
- rt_raster_destroy(raster);
-
- /* finish SPI */
- SPI_finish();
-
- if (!pgraster)
- PG_RETURN_NULL();
-
- SET_VARSIZE(pgraster, pgraster->size);
- PG_RETURN_POINTER(pgraster);
-}
-
-/* ---------------------------------------------------------------- */
-/* ST_Union aggregate functions */
-/* ---------------------------------------------------------------- */
-
-typedef enum {
- UT_LAST = 0,
- UT_FIRST,
- UT_MIN,
- UT_MAX,
- UT_COUNT,
- UT_SUM,
- UT_MEAN,
- UT_RANGE
-} rtpg_union_type;
-
-/* internal function translating text of UNION type to enum */
-static rtpg_union_type rtpg_uniontype_index_from_name(const char *cutype) {
- assert(cutype && strlen(cutype) > 0);
-
- if (strcmp(cutype, "LAST") == 0)
- return UT_LAST;
- else if (strcmp(cutype, "FIRST") == 0)
- return UT_FIRST;
- else if (strcmp(cutype, "MIN") == 0)
- return UT_MIN;
- else if (strcmp(cutype, "MAX") == 0)
- return UT_MAX;
- else if (strcmp(cutype, "COUNT") == 0)
- return UT_COUNT;
- else if (strcmp(cutype, "SUM") == 0)
- return UT_SUM;
- else if (strcmp(cutype, "MEAN") == 0)
- return UT_MEAN;
- else if (strcmp(cutype, "RANGE") == 0)
- return UT_RANGE;
-
- return UT_LAST;
-}
-
-typedef struct rtpg_union_band_arg_t *rtpg_union_band_arg;
-struct rtpg_union_band_arg_t {
- int nband; /* source raster's band index, 0-based */
- rtpg_union_type uniontype;
-
- int numraster;
- rt_raster *raster;
-};
-
-typedef struct rtpg_union_arg_t *rtpg_union_arg;
-struct rtpg_union_arg_t {
- int numband; /* number of bandargs */
- rtpg_union_band_arg bandarg;
-};
-
-static void rtpg_union_arg_destroy(rtpg_union_arg arg) {
- int i = 0;
- int j = 0;
- int k = 0;
-
- if (arg->bandarg != NULL) {
- for (i = 0; i < arg->numband; i++) {
- if (!arg->bandarg[i].numraster)
- continue;
-
- for (j = 0; j < arg->bandarg[i].numraster; j++) {
- if (arg->bandarg[i].raster[j] == NULL)
- continue;
-
- for (k = rt_raster_get_num_bands(arg->bandarg[i].raster[j]) - 1; k >= 0; k--)
- rt_band_destroy(rt_raster_get_band(arg->bandarg[i].raster[j], k));
- rt_raster_destroy(arg->bandarg[i].raster[j]);
- }
-
- pfree(arg->bandarg[i].raster);
- }
-
- pfree(arg->bandarg);
- }
-
- pfree(arg);
-}
-
-static int rtpg_union_callback(
- rt_iterator_arg arg, void *userarg,
- double *value, int *nodata
-) {
- rtpg_union_type *utype = (rtpg_union_type *) userarg;
-
- if (arg == NULL)
- return 0;
-
- if (
- arg->rasters != 2 ||
- arg->rows != 1 ||
- arg->columns != 1
- ) {
- elog(ERROR, "rtpg_union_callback: Invalid arguments passed to callback");
- return 0;
- }
-
- *value = 0;
- *nodata = 0;
-
- /* handle NODATA situations except for COUNT, which is a special case */
- if (*utype != UT_COUNT) {
- /* both NODATA */
- if (arg->nodata[0][0][0] && arg->nodata[1][0][0]) {
- *nodata = 1;
- POSTGIS_RT_DEBUGF(4, "value, nodata = %f, %d", *value, *nodata);
- return 1;
- }
- /* second NODATA */
- else if (!arg->nodata[0][0][0] && arg->nodata[1][0][0]) {
- *value = arg->values[0][0][0];
- POSTGIS_RT_DEBUGF(4, "value, nodata = %f, %d", *value, *nodata);
- return 1;
- }
- /* first NODATA */
- else if (arg->nodata[0][0][0] && !arg->nodata[1][0][0]) {
- *value = arg->values[1][0][0];
- POSTGIS_RT_DEBUGF(4, "value, nodata = %f, %d", *value, *nodata);
- return 1;
- }
- }
-
- switch (*utype) {
- case UT_FIRST:
- *value = arg->values[0][0][0];
- break;
- case UT_MIN:
- if (arg->values[0][0][0] < arg->values[1][0][0])
- *value = arg->values[0][0][0];
- else
- *value = arg->values[1][0][0];
- break;
- case UT_MAX:
- if (arg->values[0][0][0] > arg->values[1][0][0])
- *value = arg->values[0][0][0];
- else
- *value = arg->values[1][0][0];
- break;
- case UT_COUNT:
- /* both NODATA */
- if (arg->nodata[0][0][0] && arg->nodata[1][0][0])
- *value = 0;
- /* second NODATA */
- else if (!arg->nodata[0][0][0] && arg->nodata[1][0][0])
- *value = arg->values[0][0][0];
- /* first NODATA */
- else if (arg->nodata[0][0][0] && !arg->nodata[1][0][0])
- *value = 1;
- /* has value, increment */
- else
- *value = arg->values[0][0][0] + 1;
- break;
- case UT_SUM:
- *value = arg->values[0][0][0] + arg->values[1][0][0];
- break;
- case UT_MEAN:
- case UT_RANGE:
- break;
- case UT_LAST:
- default:
- *value = arg->values[1][0][0];
- break;
- }
-
- POSTGIS_RT_DEBUGF(4, "value, nodata = %f, %d", *value, *nodata);
-
-
- return 1;
-}
-
-static int rtpg_union_mean_callback(
- rt_iterator_arg arg, void *userarg,
- double *value, int *nodata
-) {
- if (arg == NULL)
- return 0;
-
- if (
- arg->rasters != 2 ||
- arg->rows != 1 ||
- arg->columns != 1
- ) {
- elog(ERROR, "rtpg_union_mean_callback: Invalid arguments passed to callback");
- return 0;
- }
-
- *value = 0;
- *nodata = 1;
-
- POSTGIS_RT_DEBUGF(4, "rast0: %f %d", arg->values[0][0][0], arg->nodata[0][0][0]);
- POSTGIS_RT_DEBUGF(4, "rast1: %f %d", arg->values[1][0][0], arg->nodata[1][0][0]);
-
- if (
- !arg->nodata[0][0][0] &&
- FLT_NEQ(arg->values[0][0][0], 0) &&
- !arg->nodata[1][0][0]
- ) {
- *value = arg->values[1][0][0] / arg->values[0][0][0];
- *nodata = 0;
- }
-
- POSTGIS_RT_DEBUGF(4, "value, nodata = (%f, %d)", *value, *nodata);
-
- return 1;
-}
-
-static int rtpg_union_range_callback(
- rt_iterator_arg arg, void *userarg,
- double *value, int *nodata
-) {
- if (arg == NULL)
- return 0;
-
- if (
- arg->rasters != 2 ||
- arg->rows != 1 ||
- arg->columns != 1
- ) {
- elog(ERROR, "rtpg_union_range_callback: Invalid arguments passed to callback");
- return 0;
- }
-
- *value = 0;
- *nodata = 1;
-
- POSTGIS_RT_DEBUGF(4, "rast0: %f %d", arg->values[0][0][0], arg->nodata[0][0][0]);
- POSTGIS_RT_DEBUGF(4, "rast1: %f %d", arg->values[1][0][0], arg->nodata[1][0][0]);
-
- if (
- !arg->nodata[0][0][0] &&
- !arg->nodata[1][0][0]
- ) {
- *value = arg->values[1][0][0] - arg->values[0][0][0];
- *nodata = 0;
- }
-
- POSTGIS_RT_DEBUGF(4, "value, nodata = (%f, %d)", *value, *nodata);
-
- return 1;
-}
-
-/* called for ST_Union(raster, unionarg[]) */
-static int rtpg_union_unionarg_process(rtpg_union_arg arg, ArrayType *array) {
- Oid etype;
- Datum *e;
- bool *nulls;
- int16 typlen;
- bool typbyval;
- char typalign;
- int n = 0;
-
- HeapTupleHeader tup;
- bool isnull;
- Datum tupv;
-
- int i;
- int nband = 1;
- char *utypename = NULL;
- rtpg_union_type utype = UT_LAST;
-
- etype = ARR_ELEMTYPE(array);
- get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
-
- deconstruct_array(
- array,
- etype,
- typlen, typbyval, typalign,
- &e, &nulls, &n
- );
-
- if (!n) {
- elog(ERROR, "rtpg_union_unionarg_process: Invalid argument for unionarg");
- return 0;
- }
-
- /* prep arg */
- arg->numband = n;
- arg->bandarg = palloc(sizeof(struct rtpg_union_band_arg_t) * arg->numband);
- if (arg->bandarg == NULL) {
- elog(ERROR, "rtpg_union_unionarg_process: Could not allocate memory for band information");
- return 0;
- }
-
- /* process each element */
- for (i = 0; i < n; i++) {
- if (nulls[i]) {
- arg->numband--;
- continue;
- }
-
- POSTGIS_RT_DEBUGF(4, "Processing unionarg at index %d", i);
-
- /* each element is a tuple */
- tup = (HeapTupleHeader) DatumGetPointer(e[i]);
- if (NULL == tup) {
- elog(ERROR, "rtpg_union_unionarg_process: Invalid argument for unionarg");
- return 0;
- }
-
- /* first element, bandnum */
- tupv = GetAttributeByName(tup, "nband", &isnull);
- if (isnull) {
- nband = i + 1;
- elog(NOTICE, "First argument (nband) of unionarg is NULL. Assuming nband = %d", nband);
- }
- else
- nband = DatumGetInt32(tupv);
-
- if (nband < 1) {
- elog(ERROR, "rtpg_union_unionarg_process: Band number must be greater than zero (1-based)");
- return 0;
- }
-
- /* second element, uniontype */
- tupv = GetAttributeByName(tup, "uniontype", &isnull);
- if (isnull) {
- elog(NOTICE, "Second argument (uniontype) of unionarg is NULL. Assuming uniontype = LAST");
- utype = UT_LAST;
- }
- else {
- utypename = text_to_cstring((text *) DatumGetPointer(tupv));
- utype = rtpg_uniontype_index_from_name(rtpg_strtoupper(utypename));
- }
-
- arg->bandarg[i].uniontype = utype;
- arg->bandarg[i].nband = nband - 1;
- arg->bandarg[i].raster = NULL;
-
- if (
- utype != UT_MEAN &&
- utype != UT_RANGE
- ) {
- arg->bandarg[i].numraster = 1;
- }
- else
- arg->bandarg[i].numraster = 2;
- }
-
- if (arg->numband < n) {
- arg->bandarg = repalloc(arg->bandarg, sizeof(struct rtpg_union_band_arg_t) * arg->numband);
- if (arg->bandarg == NULL) {
- elog(ERROR, "rtpg_union_unionarg_process: Could not reallocate memory for band information");
- return 0;
- }
- }
-
- return 1;
-}
-
-/* called for ST_Union(raster) */
-static int rtpg_union_noarg(rtpg_union_arg arg, rt_raster raster) {
- int numbands;
- int i;
-
- if (rt_raster_is_empty(raster))
- return 1;
-
- numbands = rt_raster_get_num_bands(raster);
- if (numbands <= arg->numband)
- return 1;
-
- /* more bands to process */
- POSTGIS_RT_DEBUG(4, "input raster has more bands, adding more bandargs");
- if (arg->numband)
- arg->bandarg = repalloc(arg->bandarg, sizeof(struct rtpg_union_band_arg_t) * numbands);
- else
- arg->bandarg = palloc(sizeof(struct rtpg_union_band_arg_t) * numbands);
- if (arg->bandarg == NULL) {
- elog(ERROR, "rtpg_union_noarg: Could not reallocate memory for band information");
- return 0;
- }
-
- i = arg->numband;
- arg->numband = numbands;
- for (; i < arg->numband; i++) {
- POSTGIS_RT_DEBUGF(4, "Adding bandarg for band at index %d", i);
- arg->bandarg[i].uniontype = UT_LAST;
- arg->bandarg[i].nband = i;
- arg->bandarg[i].numraster = 1;
-
- arg->bandarg[i].raster = (rt_raster *) palloc(sizeof(rt_raster) * arg->bandarg[i].numraster);
- if (arg->bandarg[i].raster == NULL) {
- elog(ERROR, "rtpg_union_noarg: Could not allocate memory for working rasters");
- return 0;
- }
- memset(arg->bandarg[i].raster, 0, sizeof(rt_raster) * arg->bandarg[i].numraster);
-
- /* add new working rt_raster but only if working raster already exists */
- if (!rt_raster_is_empty(arg->bandarg[0].raster[0])) {
- arg->bandarg[i].raster[0] = rt_raster_clone(arg->bandarg[0].raster[0], 0); /* shallow clone */
- if (arg->bandarg[i].raster[0] == NULL) {
- elog(ERROR, "rtpg_union_noarg: Could not create working raster");
- return 0;
- }
- }
- }
-
- return 1;
-}
-
-/* UNION aggregate transition function */
-PG_FUNCTION_INFO_V1(RASTER_union_transfn);
-Datum RASTER_union_transfn(PG_FUNCTION_ARGS)
-{
- MemoryContext aggcontext;
- MemoryContext oldcontext;
- rtpg_union_arg iwr = NULL;
- int skiparg = 0;
-
- rt_pgraster *pgraster = NULL;
- rt_raster raster = NULL;
- rt_raster _raster = NULL;
- rt_band _band = NULL;
- int nband = 1;
- int noerr = 1;
- int isempty[2] = {0};
- int hasband[2] = {0};
- int nargs = 0;
- double _offset[4] = {0.};
- int nbnodata = 0; /* 1 if adding bands */
-
- int i = 0;
- int j = 0;
- int k = 0;
-
- rt_iterator itrset;
- char *utypename = NULL;
- rtpg_union_type utype = UT_LAST;
- rt_pixtype pixtype = PT_END;
- int hasnodata = 1;
- double nodataval = 0;
-
- rt_raster iraster = NULL;
- rt_band iband = NULL;
- int reuserast = 0;
- int y = 0;
- uint16_t _dim[2] = {0};
- void *vals = NULL;
- uint16_t nvals = 0;
-
- POSTGIS_RT_DEBUG(3, "Starting...");
-
- /* cannot be called directly as this is exclusive aggregate function */
- if (!AggCheckCallContext(fcinfo, &aggcontext)) {
- elog(ERROR, "RASTER_union_transfn: Cannot be called in a non-aggregate context");
- PG_RETURN_NULL();
- }
-
- /* switch to aggcontext */
- oldcontext = MemoryContextSwitchTo(aggcontext);
-
- if (PG_ARGISNULL(0)) {
- POSTGIS_RT_DEBUG(3, "Creating state variable");
- /* allocate container in aggcontext */
- iwr = palloc(sizeof(struct rtpg_union_arg_t));
- if (iwr == NULL) {
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_union_transfn: Could not allocate memory for state variable");
- PG_RETURN_NULL();
- }
-
- iwr->numband = 0;
- iwr->bandarg = NULL;
-
- skiparg = 0;
- }
- else {
- POSTGIS_RT_DEBUG(3, "State variable already exists");
- iwr = (rtpg_union_arg) PG_GETARG_POINTER(0);
- skiparg = 1;
- }
-
- /* raster arg is NOT NULL */
- if (!PG_ARGISNULL(1)) {
- /* deserialize raster */
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
-
- /* Get raster object */
- raster = rt_raster_deserialize(pgraster, FALSE);
- if (raster == NULL) {
-
- rtpg_union_arg_destroy(iwr);
- PG_FREE_IF_COPY(pgraster, 1);
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_union_transfn: Could not deserialize raster");
- PG_RETURN_NULL();
- }
- }
-
- /* process additional args if needed */
- nargs = PG_NARGS();
- POSTGIS_RT_DEBUGF(4, "nargs = %d", nargs);
- if (nargs > 2) {
- POSTGIS_RT_DEBUG(4, "processing additional arguments");
-
- /* if more than 2 arguments, determine the type of argument 3 */
- /* band number, UNION type or unionarg */
- if (!PG_ARGISNULL(2)) {
- Oid calltype = get_fn_expr_argtype(fcinfo->flinfo, 2);
-
- switch (calltype) {
- /* UNION type */
- case TEXTOID: {
- int idx = 0;
- int numband = 0;
-
- POSTGIS_RT_DEBUG(4, "Processing arg 3 as UNION type");
- nbnodata = 1;
-
- utypename = text_to_cstring(PG_GETARG_TEXT_P(2));
- utype = rtpg_uniontype_index_from_name(rtpg_strtoupper(utypename));
- POSTGIS_RT_DEBUGF(4, "Union type: %s", utypename);
-
- POSTGIS_RT_DEBUGF(4, "iwr->numband: %d", iwr->numband);
- /* see if we need to append new bands */
- if (raster) {
- idx = iwr->numband;
- numband = rt_raster_get_num_bands(raster);
- POSTGIS_RT_DEBUGF(4, "numband: %d", numband);
-
- /* only worry about appended bands */
- if (numband > iwr->numband)
- iwr->numband = numband;
- }
-
- if (!iwr->numband)
- iwr->numband = 1;
- POSTGIS_RT_DEBUGF(4, "iwr->numband: %d", iwr->numband);
- POSTGIS_RT_DEBUGF(4, "numband, idx: %d, %d", numband, idx);
-
- /* bandarg set. only possible after the first call to function */
- if (iwr->bandarg) {
- /* only reallocate if new bands need to be added */
- if (numband > idx) {
- POSTGIS_RT_DEBUG(4, "Reallocating iwr->bandarg");
- iwr->bandarg = repalloc(iwr->bandarg, sizeof(struct rtpg_union_band_arg_t) * iwr->numband);
- }
- /* prevent initial values step happening below */
- else
- idx = iwr->numband;
- }
- /* bandarg not set, first call to function */
- else {
- POSTGIS_RT_DEBUG(4, "Allocating iwr->bandarg");
- iwr->bandarg = palloc(sizeof(struct rtpg_union_band_arg_t) * iwr->numband);
- }
- if (iwr->bandarg == NULL) {
-
- rtpg_union_arg_destroy(iwr);
- if (raster != NULL) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 1);
- }
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_union_transfn: Could not allocate memory for band information");
- PG_RETURN_NULL();
- }
-
- /* set initial values for bands that are "new" */
- for (i = idx; i < iwr->numband; i++) {
- iwr->bandarg[i].uniontype = utype;
- iwr->bandarg[i].nband = i;
-
- if (
- utype == UT_MEAN ||
- utype == UT_RANGE
- ) {
- iwr->bandarg[i].numraster = 2;
- }
- else
- iwr->bandarg[i].numraster = 1;
- iwr->bandarg[i].raster = NULL;
- }
-
- break;
- }
- /* band number */
- case INT2OID:
- case INT4OID:
- if (skiparg)
- break;
-
- POSTGIS_RT_DEBUG(4, "Processing arg 3 as band number");
- nband = PG_GETARG_INT32(2);
- if (nband < 1) {
-
- rtpg_union_arg_destroy(iwr);
- if (raster != NULL) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 1);
- }
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_union_transfn: Band number must be greater than zero (1-based)");
- PG_RETURN_NULL();
- }
-
- iwr->numband = 1;
- iwr->bandarg = palloc(sizeof(struct rtpg_union_band_arg_t) * iwr->numband);
- if (iwr->bandarg == NULL) {
-
- rtpg_union_arg_destroy(iwr);
- if (raster != NULL) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 1);
- }
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_union_transfn: Could not allocate memory for band information");
- PG_RETURN_NULL();
- }
-
- iwr->bandarg[0].uniontype = UT_LAST;
- iwr->bandarg[0].nband = nband - 1;
-
- iwr->bandarg[0].numraster = 1;
- iwr->bandarg[0].raster = NULL;
- break;
- /* only other type allowed is unionarg */
- default:
- if (skiparg)
- break;
-
- POSTGIS_RT_DEBUG(4, "Processing arg 3 as unionarg");
- if (!rtpg_union_unionarg_process(iwr, PG_GETARG_ARRAYTYPE_P(2))) {
-
- rtpg_union_arg_destroy(iwr);
- if (raster != NULL) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 1);
- }
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_union_transfn: Could not process unionarg");
- PG_RETURN_NULL();
- }
-
- break;
- }
- }
-
- /* UNION type */
- if (nargs > 3 && !PG_ARGISNULL(3)) {
- utypename = text_to_cstring(PG_GETARG_TEXT_P(3));
- utype = rtpg_uniontype_index_from_name(rtpg_strtoupper(utypename));
- iwr->bandarg[0].uniontype = utype;
- POSTGIS_RT_DEBUGF(4, "Union type: %s", utypename);
-
- if (
- utype == UT_MEAN ||
- utype == UT_RANGE
- ) {
- iwr->bandarg[0].numraster = 2;
- }
- }
-
- /* allocate space for pointers to rt_raster */
- for (i = 0; i < iwr->numband; i++) {
- POSTGIS_RT_DEBUGF(4, "iwr->bandarg[%d].raster @ %p", i, iwr->bandarg[i].raster);
-
- /* no need to allocate */
- if (iwr->bandarg[i].raster != NULL)
- continue;
-
- POSTGIS_RT_DEBUGF(4, "Allocating space for working rasters of band %d", i);
-
- iwr->bandarg[i].raster = (rt_raster *) palloc(sizeof(rt_raster) * iwr->bandarg[i].numraster);
- if (iwr->bandarg[i].raster == NULL) {
-
- rtpg_union_arg_destroy(iwr);
- if (raster != NULL) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 1);
- }
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_union_transfn: Could not allocate memory for working raster(s)");
- PG_RETURN_NULL();
- }
-
- memset(iwr->bandarg[i].raster, 0, sizeof(rt_raster) * iwr->bandarg[i].numraster);
-
- /* add new working rt_raster but only if working raster already exists */
- if (i > 0 && !rt_raster_is_empty(iwr->bandarg[0].raster[0])) {
- for (j = 0; j < iwr->bandarg[i].numraster; j++) {
- iwr->bandarg[i].raster[j] = rt_raster_clone(iwr->bandarg[0].raster[0], 0); /* shallow clone */
- if (iwr->bandarg[i].raster[j] == NULL) {
-
- rtpg_union_arg_destroy(iwr);
- if (raster != NULL) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 1);
- }
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_union_transfn: Could not create working raster");
- PG_RETURN_NULL();
- }
- }
- }
- }
- }
- /* only raster, no additional args */
- /* only do this if raster isn't empty */
- else {
- POSTGIS_RT_DEBUG(4, "no additional args, checking input raster");
- nbnodata = 1;
- if (!rtpg_union_noarg(iwr, raster)) {
-
- rtpg_union_arg_destroy(iwr);
- if (raster != NULL) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 1);
- }
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_union_transfn: Could not check and balance number of bands");
- PG_RETURN_NULL();
- }
- }
-
- /* init itrset */
- itrset = palloc(sizeof(struct rt_iterator_t) * 2);
- if (itrset == NULL) {
-
- rtpg_union_arg_destroy(iwr);
- if (raster != NULL) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 1);
- }
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_union_transfn: Could not allocate memory for iterator arguments");
- PG_RETURN_NULL();
- }
-
- /* by band to UNION */
- for (i = 0; i < iwr->numband; i++) {
-
- /* by raster */
- for (j = 0; j < iwr->bandarg[i].numraster; j++) {
- reuserast = 0;
-
- /* type of union */
- utype = iwr->bandarg[i].uniontype;
-
- /* raster flags */
- isempty[0] = rt_raster_is_empty(iwr->bandarg[i].raster[j]);
- isempty[1] = rt_raster_is_empty(raster);
-
- if (!isempty[0])
- hasband[0] = rt_raster_has_band(iwr->bandarg[i].raster[j], 0);
- if (!isempty[1])
- hasband[1] = rt_raster_has_band(raster, iwr->bandarg[i].nband);
-
- /* determine pixtype, hasnodata and nodataval */
- _band = NULL;
- if (!isempty[0] && hasband[0])
- _band = rt_raster_get_band(iwr->bandarg[i].raster[j], 0);
- else if (!isempty[1] && hasband[1])
- _band = rt_raster_get_band(raster, iwr->bandarg[i].nband);
- else {
- pixtype = PT_64BF;
- hasnodata = 1;
- nodataval = rt_pixtype_get_min_value(pixtype);
- }
- if (_band != NULL) {
- pixtype = rt_band_get_pixtype(_band);
- hasnodata = 1;
- if (rt_band_get_hasnodata_flag(_band))
- rt_band_get_nodata(_band, &nodataval);
- else
- nodataval = rt_band_get_min_value(_band);
- }
-
- /* UT_MEAN and UT_RANGE require two passes */
- /* UT_MEAN: first for UT_COUNT and second for UT_SUM */
- if (iwr->bandarg[i].uniontype == UT_MEAN) {
- /* first pass, UT_COUNT */
- if (j < 1)
- utype = UT_COUNT;
- else
- utype = UT_SUM;
- }
- /* UT_RANGE: first for UT_MIN and second for UT_MAX */
- else if (iwr->bandarg[i].uniontype == UT_RANGE) {
- /* first pass, UT_MIN */
- if (j < 1)
- utype = UT_MIN;
- else
- utype = UT_MAX;
- }
-
- /* force band settings for UT_COUNT */
- if (utype == UT_COUNT) {
- pixtype = PT_32BUI;
- hasnodata = 0;
- nodataval = 0;
- }
-
- POSTGIS_RT_DEBUGF(4, "(pixtype, hasnodata, nodataval) = (%s, %d, %f)", rt_pixtype_name(pixtype), hasnodata, nodataval);
-
- /* set itrset */
- itrset[0].raster = iwr->bandarg[i].raster[j];
- itrset[0].nband = 0;
- itrset[1].raster = raster;
- itrset[1].nband = iwr->bandarg[i].nband;
-
- /* allow use NODATA to replace missing bands */
- if (nbnodata) {
- itrset[0].nbnodata = 1;
- itrset[1].nbnodata = 1;
- }
- /* missing bands are not ignored */
- else {
- itrset[0].nbnodata = 0;
- itrset[1].nbnodata = 0;
- }
-
- /* if rasters AND bands are present, use copy approach */
- if (!isempty[0] && !isempty[1] && hasband[0] && hasband[1]) {
- POSTGIS_RT_DEBUG(3, "using line method");
-
- /* generate empty out raster */
- if (rt_raster_from_two_rasters(
- iwr->bandarg[i].raster[j], raster,
- ET_UNION,
- &iraster, _offset
- ) != ES_NONE) {
-
- pfree(itrset);
- rtpg_union_arg_destroy(iwr);
- if (raster != NULL) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 1);
- }
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_union_transfn: Could not create internal raster");
- PG_RETURN_NULL();
- }
- POSTGIS_RT_DEBUGF(4, "_offset = %f, %f, %f, %f",
- _offset[0], _offset[1], _offset[2], _offset[3]);
-
- /* rasters are spatially the same? */
- if (
- rt_raster_get_width(iwr->bandarg[i].raster[j]) == rt_raster_get_width(iraster) &&
- rt_raster_get_height(iwr->bandarg[i].raster[j]) == rt_raster_get_height(iraster)
- ) {
- double igt[6] = {0};
- double gt[6] = {0};
-
- rt_raster_get_geotransform_matrix(iwr->bandarg[i].raster[j], gt);
- rt_raster_get_geotransform_matrix(iraster, igt);
-
- reuserast = rt_util_same_geotransform_matrix(gt, igt);
- }
-
- /* use internal raster */
- if (!reuserast) {
- /* create band of same type */
- if (rt_raster_generate_new_band(
- iraster,
- pixtype,
- nodataval,
- hasnodata, nodataval,
- 0
- ) == -1) {
-
- pfree(itrset);
- rtpg_union_arg_destroy(iwr);
- rt_raster_destroy(iraster);
- if (raster != NULL) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 1);
- }
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_union_transfn: Could not add new band to internal raster");
- PG_RETURN_NULL();
- }
- iband = rt_raster_get_band(iraster, 0);
-
- /* copy working raster to output raster */
- _dim[0] = rt_raster_get_width(iwr->bandarg[i].raster[j]);
- _dim[1] = rt_raster_get_height(iwr->bandarg[i].raster[j]);
- for (y = 0; y < _dim[1]; y++) {
- POSTGIS_RT_DEBUGF(4, "Getting pixel line of working raster at (x, y, length) = (0, %d, %d)", y, _dim[0]);
- if (rt_band_get_pixel_line(
- _band,
- 0, y,
- _dim[0],
- &vals, &nvals
- ) != ES_NONE) {
-
- pfree(itrset);
- rtpg_union_arg_destroy(iwr);
- rt_band_destroy(iband);
- rt_raster_destroy(iraster);
- if (raster != NULL) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 1);
- }
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_union_transfn: Could not get pixel line from band of working raster");
- PG_RETURN_NULL();
- }
-
- POSTGIS_RT_DEBUGF(4, "Setting pixel line at (x, y, length) = (%d, %d, %d)", (int) _offset[0], (int) _offset[1] + y, nvals);
- if (rt_band_set_pixel_line(
- iband,
- (int) _offset[0], (int) _offset[1] + y,
- vals, nvals
- ) != ES_NONE) {
-
- pfree(itrset);
- rtpg_union_arg_destroy(iwr);
- rt_band_destroy(iband);
- rt_raster_destroy(iraster);
- if (raster != NULL) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 1);
- }
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_union_transfn: Could not set pixel line to band of internal raster");
- PG_RETURN_NULL();
- }
- }
- }
- else {
- rt_raster_destroy(iraster);
- iraster = iwr->bandarg[i].raster[j];
- iband = rt_raster_get_band(iraster, 0);
- }
-
- /* run iterator for extent of input raster */
- noerr = rt_raster_iterator(
- itrset, 2,
- ET_LAST, NULL,
- pixtype,
- hasnodata, nodataval,
- 0, 0,
- &utype,
- rtpg_union_callback,
- &_raster
- );
- if (noerr != ES_NONE) {
-
- pfree(itrset);
- rtpg_union_arg_destroy(iwr);
- if (!reuserast) {
- rt_band_destroy(iband);
- rt_raster_destroy(iraster);
- }
- if (raster != NULL) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 1);
- }
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_union_transfn: Could not run raster iterator function");
- PG_RETURN_NULL();
- }
-
- /* with iterator raster, copy data to output raster */
- _band = rt_raster_get_band(_raster, 0);
- _dim[0] = rt_raster_get_width(_raster);
- _dim[1] = rt_raster_get_height(_raster);
- for (y = 0; y < _dim[1]; y++) {
- POSTGIS_RT_DEBUGF(4, "Getting pixel line of iterator raster at (x, y, length) = (0, %d, %d)", y, _dim[0]);
- if (rt_band_get_pixel_line(
- _band,
- 0, y,
- _dim[0],
- &vals, &nvals
- ) != ES_NONE) {
-
- pfree(itrset);
- rtpg_union_arg_destroy(iwr);
- if (!reuserast) {
- rt_band_destroy(iband);
- rt_raster_destroy(iraster);
- }
- if (raster != NULL) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 1);
- }
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_union_transfn: Could not get pixel line from band of working raster");
- PG_RETURN_NULL();
- }
-
- POSTGIS_RT_DEBUGF(4, "Setting pixel line at (x, y, length) = (%d, %d, %d)", (int) _offset[2], (int) _offset[3] + y, nvals);
- if (rt_band_set_pixel_line(
- iband,
- (int) _offset[2], (int) _offset[3] + y,
- vals, nvals
- ) != ES_NONE) {
-
- pfree(itrset);
- rtpg_union_arg_destroy(iwr);
- if (!reuserast) {
- rt_band_destroy(iband);
- rt_raster_destroy(iraster);
- }
- if (raster != NULL) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 1);
- }
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_union_transfn: Could not set pixel line to band of internal raster");
- PG_RETURN_NULL();
- }
- }
-
- /* free _raster */
- rt_band_destroy(_band);
- rt_raster_destroy(_raster);
-
- /* replace working raster with output raster */
- _raster = iraster;
- }
- else {
- POSTGIS_RT_DEBUG(3, "using pixel method");
-
- /* pass everything to iterator */
- noerr = rt_raster_iterator(
- itrset, 2,
- ET_UNION, NULL,
- pixtype,
- hasnodata, nodataval,
- 0, 0,
- &utype,
- rtpg_union_callback,
- &_raster
- );
-
- if (noerr != ES_NONE) {
-
- pfree(itrset);
- rtpg_union_arg_destroy(iwr);
- if (raster != NULL) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 1);
- }
-
- MemoryContextSwitchTo(oldcontext);
- elog(ERROR, "RASTER_union_transfn: Could not run raster iterator function");
- PG_RETURN_NULL();
- }
- }
-
- /* replace working raster */
- if (iwr->bandarg[i].raster[j] != NULL && !reuserast) {
- for (k = rt_raster_get_num_bands(iwr->bandarg[i].raster[j]) - 1; k >= 0; k--)
- rt_band_destroy(rt_raster_get_band(iwr->bandarg[i].raster[j], k));
- rt_raster_destroy(iwr->bandarg[i].raster[j]);
- }
- iwr->bandarg[i].raster[j] = _raster;
- }
-
- }
-
- pfree(itrset);
- if (raster != NULL) {
- rt_raster_destroy(raster);
- PG_FREE_IF_COPY(pgraster, 1);
- }
-
- /* switch back to local context */
- MemoryContextSwitchTo(oldcontext);
-
- POSTGIS_RT_DEBUG(3, "Finished");
-
- PG_RETURN_POINTER(iwr);
-}
-
-/* UNION aggregate final function */
-PG_FUNCTION_INFO_V1(RASTER_union_finalfn);
-Datum RASTER_union_finalfn(PG_FUNCTION_ARGS)
-{
- rtpg_union_arg iwr;
- rt_raster _rtn = NULL;
- rt_raster _raster = NULL;
- rt_pgraster *pgraster = NULL;
-
- int i = 0;
- int j = 0;
- rt_iterator itrset = NULL;
- rt_band _band = NULL;
- int noerr = 1;
- int status = 0;
- rt_pixtype pixtype = PT_END;
- int hasnodata = 0;
- double nodataval = 0;
-
- POSTGIS_RT_DEBUG(3, "Starting...");
-
- /* cannot be called directly as this is exclusive aggregate function */
- if (!AggCheckCallContext(fcinfo, NULL)) {
- elog(ERROR, "RASTER_union_finalfn: Cannot be called in a non-aggregate context");
- PG_RETURN_NULL();
- }
-
- /* NULL, return null */
- if (PG_ARGISNULL(0))
- PG_RETURN_NULL();
-
- iwr = (rtpg_union_arg) PG_GETARG_POINTER(0);
-
- /* init itrset */
- itrset = palloc(sizeof(struct rt_iterator_t) * 2);
- if (itrset == NULL) {
- rtpg_union_arg_destroy(iwr);
- elog(ERROR, "RASTER_union_finalfn: Could not allocate memory for iterator arguments");
- PG_RETURN_NULL();
- }
-
- for (i = 0; i < iwr->numband; i++) {
- if (
- iwr->bandarg[i].uniontype == UT_MEAN ||
- iwr->bandarg[i].uniontype == UT_RANGE
- ) {
- /* raster containing the SUM or MAX is at index 1 */
- _band = rt_raster_get_band(iwr->bandarg[i].raster[1], 0);
-
- pixtype = rt_band_get_pixtype(_band);
- hasnodata = rt_band_get_hasnodata_flag(_band);
- if (hasnodata)
- rt_band_get_nodata(_band, &nodataval);
- POSTGIS_RT_DEBUGF(4, "(pixtype, hasnodata, nodataval) = (%s, %d, %f)", rt_pixtype_name(pixtype), hasnodata, nodataval);
-
- itrset[0].raster = iwr->bandarg[i].raster[0];
- itrset[0].nband = 0;
- itrset[1].raster = iwr->bandarg[i].raster[1];
- itrset[1].nband = 0;
-
- /* pass everything to iterator */
- if (iwr->bandarg[i].uniontype == UT_MEAN) {
- noerr = rt_raster_iterator(
- itrset, 2,
- ET_UNION, NULL,
- pixtype,
- hasnodata, nodataval,
- 0, 0,
- NULL,
- rtpg_union_mean_callback,
- &_raster
- );
- }
- else if (iwr->bandarg[i].uniontype == UT_RANGE) {
- noerr = rt_raster_iterator(
- itrset, 2,
- ET_UNION, NULL,
- pixtype,
- hasnodata, nodataval,
- 0, 0,
- NULL,
- rtpg_union_range_callback,
- &_raster
- );
- }
-
- if (noerr != ES_NONE) {
- pfree(itrset);
- rtpg_union_arg_destroy(iwr);
- if (_rtn != NULL)
- rt_raster_destroy(_rtn);
- elog(ERROR, "RASTER_union_finalfn: Could not run raster iterator function");
- PG_RETURN_NULL();
- }
- }
- else
- _raster = iwr->bandarg[i].raster[0];
-
- /* first band, _rtn doesn't exist */
- if (i < 1) {
- uint32_t bandNums[1] = {0};
- _rtn = rt_raster_from_band(_raster, bandNums, 1);
- status = (_rtn == NULL) ? -1 : 0;
- }
- else
- status = rt_raster_copy_band(_rtn, _raster, 0, i);
-
- POSTGIS_RT_DEBUG(4, "destroying source rasters");
-
- /* destroy source rasters */
- if (
- iwr->bandarg[i].uniontype == UT_MEAN ||
- iwr->bandarg[i].uniontype == UT_RANGE
- ) {
- rt_raster_destroy(_raster);
- }
-
- for (j = 0; j < iwr->bandarg[i].numraster; j++) {
- if (iwr->bandarg[i].raster[j] == NULL)
- continue;
- rt_raster_destroy(iwr->bandarg[i].raster[j]);
- iwr->bandarg[i].raster[j] = NULL;
- }
-
- if (status < 0) {
- rtpg_union_arg_destroy(iwr);
- rt_raster_destroy(_rtn);
- elog(ERROR, "RASTER_union_finalfn: Could not add band to final raster");
- PG_RETURN_NULL();
- }
- }
-
- /* cleanup */
- pfree(itrset);
- rtpg_union_arg_destroy(iwr);
-
- pgraster = rt_raster_serialize(_rtn);
- rt_raster_destroy(_rtn);
-
- POSTGIS_RT_DEBUG(3, "Finished");
-
- if (!pgraster)
- PG_RETURN_NULL();
-
- SET_VARSIZE(pgraster, pgraster->size);
- PG_RETURN_POINTER(pgraster);
-}
-
-/* ---------------------------------------------------------------- */
-/* Clip raster with geometry */
-/* ---------------------------------------------------------------- */
-
-typedef struct rtpg_clip_band_t *rtpg_clip_band;
-struct rtpg_clip_band_t {
- int nband; /* band index */
- int hasnodata; /* is there a user-specified NODATA? */
- double nodataval; /* user-specified NODATA */
-};
-
-typedef struct rtpg_clip_arg_t *rtpg_clip_arg;
-struct rtpg_clip_arg_t {
- rt_extenttype extenttype;
- rt_raster raster;
- rt_raster mask;
-
- int numbands; /* number of bandargs */
- rtpg_clip_band band;
-};
-
-static rtpg_clip_arg rtpg_clip_arg_init() {
- rtpg_clip_arg arg = NULL;
-
- arg = palloc(sizeof(struct rtpg_clip_arg_t));
- if (arg == NULL) {
- elog(ERROR, "rtpg_clip_arg_init: Could not allocate memory for function arguments");
- return NULL;
- }
-
- arg->extenttype = ET_INTERSECTION;
- arg->raster = NULL;
- arg->mask = NULL;
- arg->numbands = 0;
- arg->band = NULL;
-
- return arg;
-}
-
-static void rtpg_clip_arg_destroy(rtpg_clip_arg arg) {
- if (arg->band != NULL)
- pfree(arg->band);
-
- if (arg->raster != NULL)
- rt_raster_destroy(arg->raster);
- if (arg->mask != NULL)
- rt_raster_destroy(arg->mask);
-
- pfree(arg);
-}
-
-static int rtpg_clip_callback(
- rt_iterator_arg arg, void *userarg,
- double *value, int *nodata
-) {
- *value = 0;
- *nodata = 0;
-
- /* either is NODATA, output is NODATA */
- if (arg->nodata[0][0][0] || arg->nodata[1][0][0])
- *nodata = 1;
- /* set to value */
- else
- *value = arg->values[0][0][0];
-
- return 1;
-}
-
-PG_FUNCTION_INFO_V1(RASTER_clip);
-Datum RASTER_clip(PG_FUNCTION_ARGS)
-{
- rt_pgraster *pgraster = NULL;
- LWGEOM *rastgeom = NULL;
- double gt[6] = {0};
- int srid = SRID_UNKNOWN;
-
- rt_pgraster *pgrtn = NULL;
- rt_raster rtn = NULL;
-
- GSERIALIZED *gser = NULL;
- LWGEOM *geom = NULL;
- unsigned char *wkb = NULL;
- size_t wkb_len;
-
- ArrayType *array;
- Oid etype;
- Datum *e;
- bool *nulls;
-
- int16 typlen;
- bool typbyval;
- char typalign;
-
- int i = 0;
- int j = 0;
- int k = 0;
- rtpg_clip_arg arg = NULL;
- LWGEOM *tmpgeom = NULL;
- rt_iterator itrset;
-
- rt_raster _raster = NULL;
- rt_band band = NULL;
- rt_pixtype pixtype;
- int hasnodata;
- double nodataval;
- int noerr = 0;
-
- POSTGIS_RT_DEBUG(3, "Starting...");
-
- /* raster or geometry is NULL, return NULL */
- if (PG_ARGISNULL(0) || PG_ARGISNULL(2))
- PG_RETURN_NULL();
-
- /* init arg */
- arg = rtpg_clip_arg_init();
- if (arg == NULL) {
- elog(ERROR, "RASTER_clip: Could not initialize argument structure");
- PG_RETURN_NULL();
- }
-
- /* raster (0) */
- pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
-
- /* Get raster object */
- arg->raster = rt_raster_deserialize(pgraster, FALSE);
- if (arg->raster == NULL) {
- rtpg_clip_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_clip: Could not deserialize raster");
- PG_RETURN_NULL();
- }
-
- /* raster is empty, return empty raster */
- if (rt_raster_is_empty(arg->raster)) {
- elog(NOTICE, "Input raster is empty. Returning empty raster");
-
- rtpg_clip_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
-
- rtn = rt_raster_new(0, 0);
- if (rtn == NULL) {
- elog(ERROR, "RASTER_clip: Could not create empty raster");
- PG_RETURN_NULL();
- }
-
- pgrtn = rt_raster_serialize(rtn);
- rt_raster_destroy(rtn);
- if (NULL == pgrtn)
- PG_RETURN_NULL();
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
- /* metadata */
- rt_raster_get_geotransform_matrix(arg->raster, gt);
- srid = clamp_srid(rt_raster_get_srid(arg->raster));
-
- /* geometry (2) */
- gser = (GSERIALIZED *) PG_DETOAST_DATUM(PG_GETARG_DATUM(2));
- geom = lwgeom_from_gserialized(gser);
-
- /* Get a 2D version of the geometry if necessary */
- if (lwgeom_ndims(geom) > 2) {
- LWGEOM *geom2d = lwgeom_force_2d(geom);
- lwgeom_free(geom);
- geom = geom2d;
- }
-
- /* check that SRIDs match */
- if (srid != clamp_srid(gserialized_get_srid(gser))) {
- elog(NOTICE, "Geometry provided does not have the same SRID as the raster. Returning NULL");
-
- rtpg_clip_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- lwgeom_free(geom);
- PG_FREE_IF_COPY(gser, 2);
-
- PG_RETURN_NULL();
- }
-
- /* crop (4) */
- if (!PG_ARGISNULL(4) && !PG_GETARG_BOOL(4))
- arg->extenttype = ET_FIRST;
-
- /* get intersection geometry of input raster and input geometry */
- if (rt_raster_get_convex_hull(arg->raster, &rastgeom) != ES_NONE) {
-
- rtpg_clip_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- lwgeom_free(geom);
- PG_FREE_IF_COPY(gser, 2);
-
- elog(ERROR, "RASTER_clip: Could not get convex hull of raster");
- PG_RETURN_NULL();
- }
-
- tmpgeom = lwgeom_intersection(rastgeom, geom);
- lwgeom_free(rastgeom);
- lwgeom_free(geom);
- PG_FREE_IF_COPY(gser, 2);
- geom = tmpgeom;
-
- /* intersection is empty AND extent type is INTERSECTION, return empty */
- if (lwgeom_is_empty(geom) && arg->extenttype == ET_INTERSECTION) {
- elog(NOTICE, "The input raster and input geometry do not intersect. Returning empty raster");
-
- rtpg_clip_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- lwgeom_free(geom);
-
- rtn = rt_raster_new(0, 0);
- if (rtn == NULL) {
- elog(ERROR, "RASTER_clip: Could not create empty raster");
- PG_RETURN_NULL();
- }
-
- pgrtn = rt_raster_serialize(rtn);
- rt_raster_destroy(rtn);
- if (NULL == pgrtn)
- PG_RETURN_NULL();
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
- }
-
- /* nband (1) */
- 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_clip_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- lwgeom_free(geom);
- elog(ERROR, "RASTER_clip: Invalid data type for band indexes");
- PG_RETURN_NULL();
- break;
- }
-
- deconstruct_array(
- array, etype,
- typlen, typbyval, typalign,
- &e, &nulls, &(arg->numbands)
- );
-
- arg->band = palloc(sizeof(struct rtpg_clip_band_t) * arg->numbands);
- if (arg->band == NULL) {
- rtpg_clip_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- lwgeom_free(geom);
- elog(ERROR, "RASTER_clip: Could not allocate memory for band arguments");
- PG_RETURN_NULL();
- }
-
- for (i = 0, j = 0; i < arg->numbands; i++) {
- if (nulls[i]) continue;
-
- switch (etype) {
- case INT2OID:
- arg->band[j].nband = DatumGetInt16(e[i]) - 1;
- break;
- case INT4OID:
- arg->band[j].nband = DatumGetInt32(e[i]) - 1;
- break;
- }
-
- j++;
- }
-
- if (j < arg->numbands) {
- arg->band = repalloc(arg->band, sizeof(struct rtpg_clip_band_t) * j);
- if (arg->band == NULL) {
- rtpg_clip_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- lwgeom_free(geom);
- elog(ERROR, "RASTER_clip: Could not reallocate memory for band arguments");
- PG_RETURN_NULL();
- }
-
- arg->numbands = j;
- }
-
- /* validate band */
- for (i = 0; i < arg->numbands; i++) {
- if (!rt_raster_has_band(arg->raster, arg->band[i].nband)) {
- elog(NOTICE, "Band at index %d not found in raster", arg->band[i].nband + 1);
- rtpg_clip_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- lwgeom_free(geom);
- PG_RETURN_NULL();
- }
-
- arg->band[i].hasnodata = 0;
- arg->band[i].nodataval = 0;
- }
- }
- else {
- arg->numbands = rt_raster_get_num_bands(arg->raster);
-
- /* raster may have no bands */
- if (arg->numbands) {
- arg->band = palloc(sizeof(struct rtpg_clip_band_t) * arg->numbands);
- if (arg->band == NULL) {
-
- rtpg_clip_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- lwgeom_free(geom);
-
- elog(ERROR, "RASTER_clip: Could not allocate memory for band arguments");
- PG_RETURN_NULL();
- }
-
- for (i = 0; i < arg->numbands; i++) {
- arg->band[i].nband = i;
- arg->band[i].hasnodata = 0;
- arg->band[i].nodataval = 0;
- }
- }
- }
-
- /* nodataval (3) */
- if (!PG_ARGISNULL(3)) {
- array = PG_GETARG_ARRAYTYPE_P(3);
- etype = ARR_ELEMTYPE(array);
- get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
-
- switch (etype) {
- case FLOAT4OID:
- case FLOAT8OID:
- break;
- default:
- rtpg_clip_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- lwgeom_free(geom);
- elog(ERROR, "RASTER_clip: Invalid data type for NODATA values");
- PG_RETURN_NULL();
- break;
- }
-
- deconstruct_array(
- array, etype,
- typlen, typbyval, typalign,
- &e, &nulls, &k
- );
-
- /* it doesn't matter if there are more nodataval */
- for (i = 0, j = 0; i < arg->numbands; i++, j++) {
- /* cap j to the last nodataval element */
- if (j >= k)
- j = k - 1;
-
- if (nulls[j])
- continue;
-
- arg->band[i].hasnodata = 1;
- switch (etype) {
- case FLOAT4OID:
- arg->band[i].nodataval = DatumGetFloat4(e[j]);
- break;
- case FLOAT8OID:
- arg->band[i].nodataval = DatumGetFloat8(e[j]);
- break;
- }
- }
- }
-
- /* get wkb of geometry */
- POSTGIS_RT_DEBUG(3, "getting wkb of geometry");
- wkb = lwgeom_to_wkb(geom, WKB_SFSQL, &wkb_len);
- lwgeom_free(geom);
-
- /* rasterize geometry */
- arg->mask = rt_raster_gdal_rasterize(
- wkb, wkb_len,
- 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 (arg->mask == NULL) {
- rtpg_clip_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_clip: Could not rasterize intersection geometry");
- PG_RETURN_NULL();
- }
-
- /* set SRID */
- rt_raster_set_srid(arg->mask, srid);
-
- /* run iterator */
-
- /* init itrset */
- itrset = palloc(sizeof(struct rt_iterator_t) * 2);
- if (itrset == NULL) {
- rtpg_clip_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_clip: Could not allocate memory for iterator arguments");
- PG_RETURN_NULL();
- }
-
- /* one band at a time */
- for (i = 0; i < arg->numbands; i++) {
- POSTGIS_RT_DEBUGF(4, "band arg %d (nband, hasnodata, nodataval) = (%d, %d, %f)",
- i, arg->band[i].nband, arg->band[i].hasnodata, arg->band[i].nodataval);
-
- band = rt_raster_get_band(arg->raster, arg->band[i].nband);
-
- /* band metadata */
- pixtype = rt_band_get_pixtype(band);
-
- if (arg->band[i].hasnodata) {
- hasnodata = 1;
- nodataval = arg->band[i].nodataval;
- }
- else if (rt_band_get_hasnodata_flag(band)) {
- hasnodata = 1;
- rt_band_get_nodata(band, &nodataval);
- }
- else {
- hasnodata = 0;
- nodataval = rt_band_get_min_value(band);
- }
-
- /* band is NODATA, create NODATA band and continue */
- if (rt_band_get_isnodata_flag(band)) {
- /* create raster */
- if (rtn == NULL) {
- noerr = rt_raster_from_two_rasters(arg->raster, arg->mask, arg->extenttype, &rtn, NULL);
- if (noerr != ES_NONE) {
- rtpg_clip_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_clip: Could not create output raster");
- PG_RETURN_NULL();
- }
- }
-
- /* create NODATA band */
- if (rt_raster_generate_new_band(rtn, pixtype, nodataval, hasnodata, nodataval, i) < 0) {
- rt_raster_destroy(rtn);
- rtpg_clip_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_clip: Could not add NODATA band to output raster");
- PG_RETURN_NULL();
- }
-
- continue;
- }
-
- /* raster */
- itrset[0].raster = arg->raster;
- itrset[0].nband = arg->band[i].nband;
- itrset[0].nbnodata = 1;
-
- /* mask */
- itrset[1].raster = arg->mask;
- itrset[1].nband = 0;
- itrset[1].nbnodata = 1;
-
- /* pass to iterator */
- noerr = rt_raster_iterator(
- itrset, 2,
- arg->extenttype, NULL,
- pixtype,
- hasnodata, nodataval,
- 0, 0,
- NULL,
- rtpg_clip_callback,
- &_raster
- );
-
- if (noerr != ES_NONE) {
- pfree(itrset);
- rtpg_clip_arg_destroy(arg);
- if (rtn != NULL) rt_raster_destroy(rtn);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_clip: Could not run raster iterator function");
- PG_RETURN_NULL();
- }
-
- /* new raster */
- if (rtn == NULL)
- rtn = _raster;
- /* copy band */
- else {
- band = rt_raster_get_band(_raster, 0);
- if (band == NULL) {
- pfree(itrset);
- rtpg_clip_arg_destroy(arg);
- rt_raster_destroy(_raster);
- rt_raster_destroy(rtn);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_clip: Could not get band from working raster");
- PG_RETURN_NULL();
- }
-
- if (rt_raster_add_band(rtn, band, i) < 0) {
- pfree(itrset);
- rtpg_clip_arg_destroy(arg);
- rt_raster_destroy(_raster);
- rt_raster_destroy(rtn);
- PG_FREE_IF_COPY(pgraster, 0);
- elog(ERROR, "RASTER_clip: Could not add new band to output raster");
- PG_RETURN_NULL();
- }
-
- rt_raster_destroy(_raster);
- }
- }
-
- pfree(itrset);
- rtpg_clip_arg_destroy(arg);
- PG_FREE_IF_COPY(pgraster, 0);
-
- pgrtn = rt_raster_serialize(rtn);
- rt_raster_destroy(rtn);
-
- POSTGIS_RT_DEBUG(3, "Finished");
-
- if (!pgrtn)
- PG_RETURN_NULL();
-
- SET_VARSIZE(pgrtn, pgrtn->size);
- PG_RETURN_POINTER(pgrtn);
-}
-
-/* ---------------------------------------------------------------- */
-/* Memory allocation / error reporting hooks */
-/* TODO: reuse the ones in libpgcommon ? */
-/* ---------------------------------------------------------------- */
-
-static void *
-rt_pg_alloc(size_t size)
-{
- void * result;
-
- POSTGIS_RT_DEBUGF(5, "rt_pgalloc(%ld) called", (long int) size);
-
- result = palloc(size);
-
- return result;
-}
-
-static void *
-rt_pg_realloc(void *mem, size_t size)
-{
- void * result;
-
- POSTGIS_RT_DEBUGF(5, "rt_pg_realloc(%ld) called", (long int) size);
-
- if (mem)
- result = repalloc(mem, size);
-
- else
- result = palloc(size);
-
- return result;
-}
-
-static void
-rt_pg_free(void *ptr)
-{
- POSTGIS_RT_DEBUG(5, "rt_pfree called");
- pfree(ptr);
-}
-
-static void
-rt_pg_error(const char *fmt, va_list ap)
-{
-#define ERRMSG_MAXLEN 256
-
- char errmsg[ERRMSG_MAXLEN+1];
-
- vsnprintf (errmsg, ERRMSG_MAXLEN, fmt, ap);
-
- errmsg[ERRMSG_MAXLEN]='\0';
- ereport(ERROR, (errmsg_internal("%s", errmsg)));
-}
-
-static void
-rt_pg_notice(const char *fmt, va_list ap)
-{
- char *msg;
-
- /*
- * This is a GNU extension.
- * Dunno how to handle errors here.
- */
- if (!lw_vasprintf (&msg, fmt, ap))
- {
- va_end (ap);
- return;
- }
- ereport(NOTICE, (errmsg_internal("%s", msg)));
- free(msg);
-}
-
-
-void
-rt_init_allocators(void)
-{
- /* raster callback - install raster handlers */
- rt_set_handlers(rt_pg_alloc, rt_pg_realloc, rt_pg_free, rt_pg_error,
- rt_pg_notice, rt_pg_notice);
-}
-
--- /dev/null
+/*
+ * $Id$
+ *
+ * WKTRaster - Raster Types for PostGIS
+ * http://www.postgis.org/support/wiki/index.php?WKTRasterHomePage
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <postgres.h>
+#include <fmgr.h>
+#include <funcapi.h>
+#include <utils/builtins.h>
+#include "utils/lsyscache.h" /* for get_typlenbyvalalign */
+#include "utils/array.h" /* for ArrayType */
+#include "catalog/pg_type.h" /* for INT2OID, INT4OID, FLOAT4OID, FLOAT8OID and TEXTOID */
+
+#include "rtpostgis.h"
+
+/* Get all the properties of a raster band */
+Datum RASTER_getBandPixelType(PG_FUNCTION_ARGS);
+Datum RASTER_getBandPixelTypeName(PG_FUNCTION_ARGS);
+Datum RASTER_getBandNoDataValue(PG_FUNCTION_ARGS);
+Datum RASTER_getBandPath(PG_FUNCTION_ARGS);
+Datum RASTER_bandIsNoData(PG_FUNCTION_ARGS);
+
+/* get raster band's meta data */
+Datum RASTER_bandmetadata(PG_FUNCTION_ARGS);
+
+/* Set all the properties of a raster band */
+Datum RASTER_setBandIsNoData(PG_FUNCTION_ARGS);
+Datum RASTER_setBandNoDataValue(PG_FUNCTION_ARGS);
+
+/**
+ * Return pixel type of the specified band of raster.
+ * Band index is 1-based.
+ */
+PG_FUNCTION_INFO_V1(RASTER_getBandPixelType);
+Datum RASTER_getBandPixelType(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ rt_band band = NULL;
+ rt_pixtype pixtype;
+ int32_t bandindex;
+
+ /* Deserialize raster */
+ if (PG_ARGISNULL(0)) PG_RETURN_NULL();
+ pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(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_FREE_IF_COPY(pgraster, 0);
+ PG_RETURN_NULL();
+ }
+
+ raster = rt_raster_deserialize(pgraster, FALSE);
+ if ( ! raster ) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_getBandPixelType: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ /* Fetch requested band and its pixel type */
+ band = rt_raster_get_band(raster, bandindex - 1);
+ if ( ! band ) {
+ elog(NOTICE, "Could not find raster band of index %d when getting pixel type. Returning NULL", bandindex);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ PG_RETURN_NULL();
+ }
+
+ pixtype = rt_band_get_pixtype(band);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ PG_RETURN_INT32(pixtype);
+}
+
+/**
+ * Return name of pixel type of the specified band of raster.
+ * Band index is 1-based.
+ * NOTE: This is unofficial utility not included in the spec.
+ */
+PG_FUNCTION_INFO_V1(RASTER_getBandPixelTypeName);
+Datum RASTER_getBandPixelTypeName(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ rt_band band = NULL;
+ rt_pixtype pixtype;
+ int32_t bandindex;
+ const size_t name_size = 8; /* size of type name */
+ size_t size = 0;
+ char *ptr = NULL;
+ text *result = NULL;
+
+ /* Deserialize raster */
+ if (PG_ARGISNULL(0)) PG_RETURN_NULL();
+ pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(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_FREE_IF_COPY(pgraster, 0);
+ PG_RETURN_NULL();
+ }
+
+ raster = rt_raster_deserialize(pgraster, FALSE);
+ if ( ! raster ) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_getBandPixelTypeName: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ /* Fetch requested band and its pixel type */
+ band = rt_raster_get_band(raster, bandindex - 1);
+ if ( ! band ) {
+ elog(NOTICE, "Could not find raster band of index %d when getting pixel type name. Returning NULL", bandindex);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ PG_RETURN_NULL();
+ }
+
+ pixtype = rt_band_get_pixtype(band);
+
+ result = palloc(VARHDRSZ + name_size);
+ /* We don't need to check for NULL pointer, because if out of memory, palloc
+ * exit via elog(ERROR). It never returns NULL.
+ */
+
+ memset(VARDATA(result), 0, name_size);
+ ptr = (char *)result + VARHDRSZ;
+ strcpy(ptr, rt_pixtype_name(pixtype));
+
+ size = VARHDRSZ + strlen(ptr);
+ SET_VARSIZE(result, size);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ PG_RETURN_TEXT_P(result);
+}
+
+/**
+ * Return nodata value of the specified band of raster.
+ * The value is always returned as FLOAT32 even if the pixel type is INTEGER.
+ */
+PG_FUNCTION_INFO_V1(RASTER_getBandNoDataValue);
+Datum RASTER_getBandNoDataValue(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ rt_band band = NULL;
+ int32_t bandindex;
+ double nodata;
+
+ /* Deserialize raster */
+ if (PG_ARGISNULL(0)) PG_RETURN_NULL();
+ pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(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_FREE_IF_COPY(pgraster, 0);
+ PG_RETURN_NULL();
+ }
+
+ raster = rt_raster_deserialize(pgraster, FALSE);
+ if ( ! raster ) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_getBandNoDataValue: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ /* Fetch requested band and its nodata value */
+ band = rt_raster_get_band(raster, bandindex - 1);
+ if ( ! band ) {
+ elog(NOTICE, "Could not find raster band of index %d when getting band nodata value. Returning NULL", bandindex);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ PG_RETURN_NULL();
+ }
+
+ if ( ! rt_band_get_hasnodata_flag(band) ) {
+ /* Raster does not have a nodata value set so we return NULL */
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ PG_RETURN_NULL();
+ }
+
+ rt_band_get_nodata(band, &nodata);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ PG_RETURN_FLOAT8(nodata);
+}
+
+
+PG_FUNCTION_INFO_V1(RASTER_bandIsNoData);
+Datum RASTER_bandIsNoData(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ rt_band band = NULL;
+ int32_t bandindex;
+ bool forcechecking = FALSE;
+ bool bandisnodata = FALSE;
+
+ /* 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();
+ }
+
+ /* 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_bandIsNoData: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ /* Fetch requested band and its nodata value */
+ band = rt_raster_get_band(raster, bandindex - 1);
+ if ( ! band ) {
+ elog(NOTICE, "Could not find raster band of index %d when determining if band is nodata. Returning NULL", bandindex);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ PG_RETURN_NULL();
+ }
+
+ forcechecking = PG_GETARG_BOOL(2);
+
+ bandisnodata = (forcechecking) ?
+ rt_band_check_is_nodata(band) : rt_band_get_isnodata_flag(band);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ PG_RETURN_BOOL(bandisnodata);
+}
+
+/**
+ * Return the path of the raster for out-db raster.
+ */
+PG_FUNCTION_INFO_V1(RASTER_getBandPath);
+Datum RASTER_getBandPath(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ rt_band band = NULL;
+ int32_t bandindex;
+ const char *bandpath;
+ text *result;
+
+ /* 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();
+ }
+
+ /* 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_getBandPath: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ /* Fetch requested band and its nodata value */
+ band = rt_raster_get_band(raster, bandindex - 1);
+ if ( ! band ) {
+ elog(NOTICE, "Could not find raster band of index %d when getting band path. Returning NULL", bandindex);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ PG_RETURN_NULL();
+ }
+
+ bandpath = rt_band_get_ext_path(band);
+ rt_band_destroy(band);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ if ( ! bandpath )
+ {
+ PG_RETURN_NULL();
+ }
+
+ result = (text *) palloc(VARHDRSZ + strlen(bandpath) + 1);
+
+ SET_VARSIZE(result, VARHDRSZ + strlen(bandpath) + 1);
+
+ strcpy((char *) VARDATA(result), bandpath);
+
+ PG_RETURN_TEXT_P(result);
+}
+
+/**
+ * Get raster bands' meta data
+ */
+PG_FUNCTION_INFO_V1(RASTER_bandmetadata);
+Datum RASTER_bandmetadata(PG_FUNCTION_ARGS)
+{
+ FuncCallContext *funcctx;
+ TupleDesc tupdesc;
+ int call_cntr;
+ int max_calls;
+
+ struct bandmetadata {
+ uint32_t bandnum;
+ char *pixeltype;
+ bool hasnodata;
+ double nodataval;
+ bool isoutdb;
+ char *bandpath;
+ };
+ struct bandmetadata *bmd = NULL;
+ struct bandmetadata *bmd2 = NULL;
+
+ HeapTuple tuple;
+ Datum result;
+
+ if (SRF_IS_FIRSTCALL()) {
+ MemoryContext oldcontext;
+
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ rt_band band = NULL;
+
+ ArrayType *array;
+ Oid etype;
+ Datum *e;
+ bool *nulls;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ int i = 0;
+ int j = 0;
+ int n = 0;
+
+ uint32_t numBands;
+ uint32_t idx = 1;
+ uint32_t *bandNums = NULL;
+ const char *tmp = NULL;
+
+ POSTGIS_RT_DEBUG(3, "RASTER_bandmetadata: Starting");
+
+ /* 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);
+
+ /* pgraster is null, return null */
+ if (PG_ARGISNULL(0)) {
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+ /* raster */
+ raster = rt_raster_deserialize(pgraster, FALSE);
+ if (!raster) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_bandmetadata: Could not deserialize raster");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* numbands */
+ numBands = rt_raster_get_num_bands(raster);
+ if (numBands < 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 */
+ array = PG_GETARG_ARRAYTYPE_P(1);
+ etype = ARR_ELEMTYPE(array);
+ get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
+
+ switch (etype) {
+ case INT2OID:
+ case INT4OID:
+ break;
+ default:
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_bandmetadata: Invalid data type for band number(s)");
+ SRF_RETURN_DONE(funcctx);
+ break;
+ }
+
+ deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
+ &nulls, &n);
+
+ bandNums = palloc(sizeof(uint32_t) * n);
+ for (i = 0, j = 0; i < n; i++) {
+ if (nulls[i]) continue;
+
+ switch (etype) {
+ case INT2OID:
+ idx = (uint32_t) DatumGetInt16(e[i]);
+ break;
+ case INT4OID:
+ idx = (uint32_t) DatumGetInt32(e[i]);
+ break;
+ }
+
+ POSTGIS_RT_DEBUGF(3, "band idx (before): %d", idx);
+ if (idx > numBands || idx < 1) {
+ elog(NOTICE, "Invalid band index: %d. Indices must be 1-based. Returning NULL", idx);
+ pfree(bandNums);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ bandNums[j] = idx;
+ POSTGIS_RT_DEBUGF(3, "bandNums[%d] = %d", j, bandNums[j]);
+ j++;
+ }
+
+ if (j < 1) {
+ j = numBands;
+ bandNums = repalloc(bandNums, sizeof(uint32_t) * j);
+ for (i = 0; i < j; i++)
+ bandNums[i] = i + 1;
+ }
+ else if (j < n)
+ bandNums = repalloc(bandNums, sizeof(uint32_t) * j);
+
+ bmd = (struct bandmetadata *) palloc(sizeof(struct bandmetadata) * j);
+
+ for (i = 0; i < j; i++) {
+ band = rt_raster_get_band(raster, bandNums[i] - 1);
+ if (NULL == band) {
+ elog(NOTICE, "Could not get raster band at index %d", bandNums[i]);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* bandnum */
+ bmd[i].bandnum = bandNums[i];
+
+ /* pixeltype */
+ tmp = rt_pixtype_name(rt_band_get_pixtype(band));
+ bmd[i].pixeltype = palloc(sizeof(char) * (strlen(tmp) + 1));
+ strncpy(bmd[i].pixeltype, tmp, strlen(tmp) + 1);
+
+ /* hasnodatavalue */
+ if (rt_band_get_hasnodata_flag(band))
+ bmd[i].hasnodata = TRUE;
+ else
+ bmd[i].hasnodata = FALSE;
+
+ /* nodatavalue */
+ if (bmd[i].hasnodata)
+ rt_band_get_nodata(band, &(bmd[i].nodataval));
+ else
+ bmd[i].nodataval = 0;
+
+ /* path */
+ tmp = rt_band_get_ext_path(band);
+ if (tmp) {
+ bmd[i].bandpath = palloc(sizeof(char) * (strlen(tmp) + 1));
+ strncpy(bmd[i].bandpath, tmp, strlen(tmp) + 1);
+ }
+ else
+ bmd[i].bandpath = NULL;
+
+ /* isoutdb */
+ bmd[i].isoutdb = bmd[i].bandpath ? TRUE : FALSE;
+
+ rt_band_destroy(band);
+ }
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ /* Store needed information */
+ funcctx->user_fctx = bmd;
+
+ /* total number of tuples to be returned */
+ funcctx->max_calls = j;
+
+ /* 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;
+ bmd2 = funcctx->user_fctx;
+
+ /* do when there is more left to send */
+ if (call_cntr < max_calls) {
+ int values_length = 5;
+ Datum values[values_length];
+ bool nulls[values_length];
+
+ memset(nulls, FALSE, sizeof(bool) * values_length);
+
+ values[0] = UInt32GetDatum(bmd2[call_cntr].bandnum);
+ values[1] = CStringGetTextDatum(bmd2[call_cntr].pixeltype);
+
+ if (bmd2[call_cntr].hasnodata)
+ values[2] = Float8GetDatum(bmd2[call_cntr].nodataval);
+ else
+ nulls[2] = TRUE;
+
+ values[3] = BoolGetDatum(bmd2[call_cntr].isoutdb);
+ if (bmd2[call_cntr].bandpath && strlen(bmd2[call_cntr].bandpath))
+ values[4] = CStringGetTextDatum(bmd2[call_cntr].bandpath);
+ else
+ nulls[4] = TRUE;
+
+ /* build a tuple */
+ tuple = heap_form_tuple(tupdesc, values, nulls);
+
+ /* make the tuple into a datum */
+ result = HeapTupleGetDatum(tuple);
+
+ /* clean up */
+ pfree(bmd2[call_cntr].pixeltype);
+ if (bmd2[call_cntr].bandpath) pfree(bmd2[call_cntr].bandpath);
+
+ SRF_RETURN_NEXT(funcctx, result);
+ }
+ /* do when there is no more left */
+ else {
+ pfree(bmd2);
+ SRF_RETURN_DONE(funcctx);
+ }
+}
+
+/**
+ * Set the nodata value of the specified band of raster.
+ */
+PG_FUNCTION_INFO_V1(RASTER_setBandNoDataValue);
+Datum RASTER_setBandNoDataValue(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_pgraster *pgrtn = NULL;
+ rt_raster raster = NULL;
+ rt_band band = NULL;
+ double nodata;
+ int32_t bandindex;
+ bool forcechecking = FALSE;
+ bool skipset = FALSE;
+
+ /* Deserialize raster */
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+ /* Check index is not NULL or smaller than 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). Nodata value not set. Returning original raster");
+ skipset = TRUE;
+ }
+
+ raster = rt_raster_deserialize(pgraster, FALSE);
+ if (!raster) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_setBandNoDataValue: 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. Nodata value not set. Returning original raster", bandindex);
+ }
+ else {
+ if (!PG_ARGISNULL(3))
+ forcechecking = PG_GETARG_BOOL(3);
+
+ if (PG_ARGISNULL(2)) {
+ /* Set the hasnodata flag to FALSE */
+ rt_band_set_hasnodata_flag(band, FALSE);
+ POSTGIS_RT_DEBUGF(3, "Raster band %d does not have a nodata value", bandindex);
+ }
+ else {
+ /* Get the nodata value */
+ nodata = PG_GETARG_FLOAT8(2);
+
+ /* Set the band's nodata value */
+ rt_band_set_nodata(band, nodata, NULL);
+
+ /* Recheck all pixels if requested */
+ if (forcechecking)
+ rt_band_check_is_nodata(band);
+ }
+ }
+ }
+
+ 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);
+}
+
+PG_FUNCTION_INFO_V1(RASTER_setBandIsNoData);
+Datum RASTER_setBandIsNoData(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_pgraster *pgrtn = NULL;
+ rt_raster raster = NULL;
+ rt_band band = NULL;
+ int32_t bandindex;
+
+ 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_setBandIsNoData: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ /* Check index is not NULL or smaller than 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). Isnodata flag not set. Returning original raster");
+ else {
+ /* Fetch requested band */
+ band = rt_raster_get_band(raster, bandindex - 1);
+
+ if (!band)
+ elog(NOTICE, "Could not find raster band of index %d. Isnodata flag not set. Returning original raster", bandindex);
+ else {
+ if (!rt_band_get_hasnodata_flag(band)) {
+ elog(NOTICE, "Band of index %d has no NODATA so cannot be NODATA. Returning original raster", bandindex);
+ }
+ /* Set the band's nodata value */
+ else {
+ rt_band_set_isnodata_flag(band, 1);
+ }
+ }
+ }
+
+ /* Serialize raster again */
+ 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);
+}
+
--- /dev/null
+/*
+ * $Id$
+ *
+ * WKTRaster - Raster Types for PostGIS
+ * http://www.postgis.org/support/wiki/index.php?WKTRasterHomePage
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <postgres.h>
+#include <fmgr.h>
+#include <funcapi.h>
+#include <utils/builtins.h> /* for text_to_cstring() */
+#include "utils/lsyscache.h" /* for get_typlenbyvalalign */
+#include "utils/array.h" /* for ArrayType */
+#include "catalog/pg_type.h" /* for INT2OID, INT4OID, FLOAT4OID, FLOAT8OID and TEXTOID */
+
+#include "rtpostgis.h"
+
+/* Raster and band creation */
+Datum RASTER_makeEmpty(PG_FUNCTION_ARGS);
+Datum RASTER_addBand(PG_FUNCTION_ARGS);
+Datum RASTER_addBandRasterArray(PG_FUNCTION_ARGS);
+Datum RASTER_addBandOutDB(PG_FUNCTION_ARGS);
+Datum RASTER_copyBand(PG_FUNCTION_ARGS);
+Datum RASTER_tile(PG_FUNCTION_ARGS);
+
+/* create new raster from existing raster's bands */
+Datum RASTER_band(PG_FUNCTION_ARGS);
+
+/**
+ * Make a new raster with no bands
+ */
+PG_FUNCTION_INFO_V1(RASTER_makeEmpty);
+Datum RASTER_makeEmpty(PG_FUNCTION_ARGS)
+{
+ uint16 width = 0, height = 0;
+ double ipx = 0, ipy = 0, scalex = 0, scaley = 0, skewx = 0, skewy = 0;
+ int32_t srid = SRID_UNKNOWN;
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster;
+
+ if (PG_NARGS() < 9) {
+ elog(ERROR, "RASTER_makeEmpty: ST_MakeEmptyRaster requires 9 args");
+ PG_RETURN_NULL();
+ }
+
+ if (!PG_ARGISNULL(0))
+ width = PG_GETARG_UINT16(0);
+
+ if (!PG_ARGISNULL(1))
+ height = PG_GETARG_UINT16(1);
+
+ if (!PG_ARGISNULL(2))
+ ipx = PG_GETARG_FLOAT8(2);
+
+ if (!PG_ARGISNULL(3))
+ ipy = PG_GETARG_FLOAT8(3);
+
+ if (!PG_ARGISNULL(4))
+ scalex = PG_GETARG_FLOAT8(4);
+
+ if (!PG_ARGISNULL(5))
+ scaley = PG_GETARG_FLOAT8(5);
+
+ if (!PG_ARGISNULL(6))
+ skewx = PG_GETARG_FLOAT8(6);
+
+ if (!PG_ARGISNULL(7))
+ skewy = PG_GETARG_FLOAT8(7);
+
+ if (!PG_ARGISNULL(8))
+ srid = PG_GETARG_INT32(8);
+
+ POSTGIS_RT_DEBUGF(4, "%dx%d, ip:%g,%g, scale:%g,%g, skew:%g,%g srid:%d",
+ width, height, ipx, ipy, scalex, scaley,
+ skewx, skewy, srid);
+
+ raster = rt_raster_new(width, height);
+ if (raster == NULL)
+ PG_RETURN_NULL(); /* error was supposedly printed already */
+
+ rt_raster_set_scale(raster, scalex, scaley);
+ rt_raster_set_offsets(raster, ipx, ipy);
+ rt_raster_set_skews(raster, skewx, skewy);
+ rt_raster_set_srid(raster, srid);
+
+ pgraster = rt_raster_serialize(raster);
+ rt_raster_destroy(raster);
+ if (!pgraster)
+ PG_RETURN_NULL();
+
+ SET_VARSIZE(pgraster, pgraster->size);
+ PG_RETURN_POINTER(pgraster);
+}
+
+/**
+ * Add band(s) to the given raster at the given position(s).
+ */
+PG_FUNCTION_INFO_V1(RASTER_addBand);
+Datum RASTER_addBand(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_pgraster *pgrtn = NULL;
+ rt_raster raster = NULL;
+ int bandindex = 0;
+ int maxbandindex = 0;
+ int numbands = 0;
+ int lastnumbands = 0;
+
+ text *text_pixtype = NULL;
+ char *char_pixtype = NULL;
+
+ struct addbandarg {
+ int index;
+ bool append;
+ rt_pixtype pixtype;
+ double initialvalue;
+ bool hasnodata;
+ double nodatavalue;
+ };
+ struct addbandarg *arg = NULL;
+
+ ArrayType *array;
+ Oid etype;
+ Datum *e;
+ bool *nulls;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ int n = 0;
+
+ HeapTupleHeader tup;
+ bool isnull;
+ Datum tupv;
+
+ int i = 0;
+
+ /* pgraster is null, return null */
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+ /* raster */
+ raster = rt_raster_deserialize(pgraster, FALSE);
+ if (!raster) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_addBand: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ /* process set of addbandarg */
+ POSTGIS_RT_DEBUG(3, "Processing Arg 1 (addbandargset)");
+ array = PG_GETARG_ARRAYTYPE_P(1);
+ etype = ARR_ELEMTYPE(array);
+ get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
+
+ deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
+ &nulls, &n);
+
+ if (!n) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_addBand: Invalid argument for addbandargset");
+ PG_RETURN_NULL();
+ }
+
+ /* allocate addbandarg */
+ arg = (struct addbandarg *) palloc(sizeof(struct addbandarg) * n);
+ if (arg == NULL) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_addBand: Could not allocate memory for addbandarg");
+ PG_RETURN_NULL();
+ }
+
+ /*
+ process each element of addbandargset
+ each element is the index of where to add the new band,
+ new band's pixeltype, the new band's initial value and
+ the new band's NODATA value if NOT NULL
+ */
+ for (i = 0; i < n; i++) {
+ if (nulls[i]) continue;
+
+ POSTGIS_RT_DEBUGF(4, "Processing addbandarg at index %d", i);
+
+ /* each element is a tuple */
+ tup = (HeapTupleHeader) DatumGetPointer(e[i]);
+ if (NULL == tup) {
+ pfree(arg);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_addBand: Invalid argument for addbandargset");
+ PG_RETURN_NULL();
+ }
+
+ /* new band index, 1-based */
+ arg[i].index = 0;
+ arg[i].append = TRUE;
+ tupv = GetAttributeByName(tup, "index", &isnull);
+ if (!isnull) {
+ arg[i].index = DatumGetInt32(tupv);
+ arg[i].append = FALSE;
+ }
+
+ /* for now, only check that band index is 1-based */
+ if (!arg[i].append && arg[i].index < 1) {
+ pfree(arg);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_addBand: Invalid argument for addbandargset. Invalid band index (must be 1-based) for addbandarg of index %d", i);
+ PG_RETURN_NULL();
+ }
+
+ /* new band pixeltype */
+ arg[i].pixtype = PT_END;
+ tupv = GetAttributeByName(tup, "pixeltype", &isnull);
+ if (isnull) {
+ pfree(arg);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_addBand: Invalid argument for addbandargset. Pixel type cannot be NULL for addbandarg of index %d", i);
+ PG_RETURN_NULL();
+ }
+ text_pixtype = (text *) DatumGetPointer(tupv);
+ if (text_pixtype == NULL) {
+ pfree(arg);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_addBand: Invalid argument for addbandargset. Pixel type cannot be NULL for addbandarg of index %d", i);
+ PG_RETURN_NULL();
+ }
+ char_pixtype = text_to_cstring(text_pixtype);
+
+ arg[i].pixtype = rt_pixtype_index_from_name(char_pixtype);
+ pfree(char_pixtype);
+ if (arg[i].pixtype == PT_END) {
+ pfree(arg);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_addBand: Invalid argument for addbandargset. Invalid pixel type for addbandarg of index %d", i);
+ PG_RETURN_NULL();
+ }
+
+ /* new band initialvalue */
+ arg[i].initialvalue = 0;
+ tupv = GetAttributeByName(tup, "initialvalue", &isnull);
+ if (!isnull)
+ arg[i].initialvalue = DatumGetFloat8(tupv);
+
+ /* new band NODATA value */
+ arg[i].hasnodata = FALSE;
+ arg[i].nodatavalue = 0;
+ tupv = GetAttributeByName(tup, "nodataval", &isnull);
+ if (!isnull) {
+ arg[i].hasnodata = TRUE;
+ arg[i].nodatavalue = DatumGetFloat8(tupv);
+ }
+ }
+
+ /* add new bands to raster */
+ lastnumbands = rt_raster_get_num_bands(raster);
+ for (i = 0; i < n; i++) {
+ if (nulls[i]) continue;
+
+ POSTGIS_RT_DEBUGF(3, "%d bands in old raster", lastnumbands);
+ maxbandindex = lastnumbands + 1;
+
+ /* check that new band's index doesn't exceed maxbandindex */
+ if (!arg[i].append) {
+ if (arg[i].index > maxbandindex) {
+ elog(NOTICE, "Band index for addbandarg of index %d exceeds possible value. Adding band at index %d", i, maxbandindex);
+ arg[i].index = maxbandindex;
+ }
+ }
+ /* append, so use maxbandindex */
+ else
+ arg[i].index = maxbandindex;
+
+ POSTGIS_RT_DEBUGF(4, "new band (index, pixtype, initialvalue, hasnodata, nodatavalue) = (%d, %s, %f, %s, %f)",
+ arg[i].index,
+ rt_pixtype_name(arg[i].pixtype),
+ arg[i].initialvalue,
+ arg[i].hasnodata ? "TRUE" : "FALSE",
+ arg[i].nodatavalue
+ );
+
+ bandindex = rt_raster_generate_new_band(
+ raster,
+ arg[i].pixtype, arg[i].initialvalue,
+ arg[i].hasnodata, arg[i].nodatavalue,
+ arg[i].index - 1
+ );
+
+ numbands = rt_raster_get_num_bands(raster);
+ if (numbands == lastnumbands || bandindex == -1) {
+ pfree(arg);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_addBand: Could not add band defined by addbandarg of index %d to raster", i);
+ PG_RETURN_NULL();
+ }
+
+ lastnumbands = numbands;
+ POSTGIS_RT_DEBUGF(3, "%d bands in new raster", lastnumbands);
+ }
+
+ pfree(arg);
+
+ 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);
+}
+
+/**
+ * Add bands from array of rasters to a destination raster
+ */
+PG_FUNCTION_INFO_V1(RASTER_addBandRasterArray);
+Datum RASTER_addBandRasterArray(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_pgraster *pgsrc = NULL;
+ rt_pgraster *pgrtn = NULL;
+
+ rt_raster raster = NULL;
+ rt_raster src = NULL;
+
+ int srcnband = 1;
+ bool appendband = FALSE;
+ int dstnband = 1;
+ int srcnumbands = 0;
+ int dstnumbands = 0;
+
+ ArrayType *array;
+ Oid etype;
+ Datum *e;
+ bool *nulls;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ int n = 0;
+
+ int rtn = 0;
+ int i = 0;
+
+ /* destination raster */
+ if (!PG_ARGISNULL(0)) {
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+ /* raster */
+ raster = rt_raster_deserialize(pgraster, FALSE);
+ if (!raster) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_addBandRasterArray: Could not deserialize destination raster");
+ PG_RETURN_NULL();
+ }
+
+ POSTGIS_RT_DEBUG(4, "destination raster isn't NULL");
+ }
+
+ /* source rasters' band index, 1-based */
+ if (!PG_ARGISNULL(2))
+ srcnband = PG_GETARG_INT32(2);
+ if (srcnband < 1) {
+ elog(NOTICE, "Invalid band index for source rasters (must be 1-based). Returning original raster");
+ if (raster != NULL) {
+ rt_raster_destroy(raster);
+ PG_RETURN_POINTER(pgraster);
+ }
+ else
+ PG_RETURN_NULL();
+ }
+ POSTGIS_RT_DEBUGF(4, "srcnband = %d", srcnband);
+
+ /* destination raster's band index, 1-based */
+ if (!PG_ARGISNULL(3)) {
+ dstnband = PG_GETARG_INT32(3);
+ appendband = FALSE;
+
+ if (dstnband < 1) {
+ elog(NOTICE, "Invalid band index for destination raster (must be 1-based). Returning original raster");
+ if (raster != NULL) {
+ rt_raster_destroy(raster);
+ PG_RETURN_POINTER(pgraster);
+ }
+ else
+ PG_RETURN_NULL();
+ }
+ }
+ else
+ appendband = TRUE;
+
+ /* additional processing of dstnband */
+ if (raster != NULL) {
+ dstnumbands = rt_raster_get_num_bands(raster);
+
+ if (dstnumbands < 1) {
+ appendband = TRUE;
+ dstnband = 1;
+ }
+ else if (appendband)
+ dstnband = dstnumbands + 1;
+ else if (dstnband > dstnumbands) {
+ elog(NOTICE, "Band index provided for destination raster is greater than the number of bands in the raster. Bands will be appended");
+ appendband = TRUE;
+ dstnband = dstnumbands + 1;
+ }
+ }
+ POSTGIS_RT_DEBUGF(4, "appendband = %d", appendband);
+ POSTGIS_RT_DEBUGF(4, "dstnband = %d", dstnband);
+
+ /* process set of source rasters */
+ POSTGIS_RT_DEBUG(3, "Processing array of source rasters");
+ array = PG_GETARG_ARRAYTYPE_P(1);
+ etype = ARR_ELEMTYPE(array);
+ get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
+
+ deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
+ &nulls, &n);
+
+ /* decrement srcnband and dstnband by 1, now 0-based */
+ srcnband--;
+ dstnband--;
+ POSTGIS_RT_DEBUGF(4, "0-based nband (src, dst) = (%d, %d)", srcnband, dstnband);
+
+ /* time to copy bands */
+ for (i = 0; i < n; i++) {
+ if (nulls[i]) continue;
+ src = NULL;
+
+ pgsrc = (rt_pgraster *) PG_DETOAST_DATUM(e[i]);
+ src = rt_raster_deserialize(pgsrc, FALSE);
+ if (src == NULL) {
+ pfree(nulls);
+ pfree(e);
+ if (raster != NULL)
+ rt_raster_destroy(raster);
+ if (pgraster != NULL)
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_addBandRasterArray: Could not deserialize source raster at index %d", i + 1);
+ PG_RETURN_NULL();
+ }
+
+ srcnumbands = rt_raster_get_num_bands(src);
+ POSTGIS_RT_DEBUGF(4, "source raster %d has %d bands", i + 1, srcnumbands);
+
+ /* band index isn't valid */
+ if (srcnband > srcnumbands - 1) {
+ elog(NOTICE, "Invalid band index for source raster at index %d. Returning original raster", i + 1);
+ pfree(nulls);
+ pfree(e);
+ rt_raster_destroy(src);
+ if (raster != NULL) {
+ rt_raster_destroy(raster);
+ PG_RETURN_POINTER(pgraster);
+ }
+ else
+ PG_RETURN_NULL();
+ }
+
+ /* destination raster is empty, new raster */
+ if (raster == NULL) {
+ uint32_t srcnbands[1] = {srcnband};
+
+ POSTGIS_RT_DEBUG(4, "empty destination raster, using rt_raster_from_band");
+
+ raster = rt_raster_from_band(src, srcnbands, 1);
+ rt_raster_destroy(src);
+ if (raster == NULL) {
+ pfree(nulls);
+ pfree(e);
+ if (pgraster != NULL)
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_addBandRasterArray: Could not create raster from source raster at index %d", i + 1);
+ PG_RETURN_NULL();
+ }
+ }
+ /* copy band */
+ else {
+ rtn = rt_raster_copy_band(
+ raster, src,
+ srcnband, dstnband
+ );
+ rt_raster_destroy(src);
+
+ if (rtn == -1 || rt_raster_get_num_bands(raster) == dstnumbands) {
+ elog(NOTICE, "Could not add band from source raster at index %d to destination raster. Returning original raster", i + 1);
+ rt_raster_destroy(raster);
+ pfree(nulls);
+ pfree(e);
+ if (pgraster != NULL)
+ PG_RETURN_POINTER(pgraster);
+ else
+ PG_RETURN_NULL();
+ }
+ }
+
+ dstnband++;
+ dstnumbands++;
+ }
+
+ if (raster != NULL) {
+ pgrtn = rt_raster_serialize(raster);
+ rt_raster_destroy(raster);
+ if (pgraster != NULL)
+ PG_FREE_IF_COPY(pgraster, 0);
+ if (!pgrtn)
+ PG_RETURN_NULL();
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+ PG_RETURN_NULL();
+}
+
+/**
+ * Add out-db band to the given raster at the given position
+ */
+PG_FUNCTION_INFO_V1(RASTER_addBandOutDB);
+Datum RASTER_addBandOutDB(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_pgraster *pgrtn = NULL;
+
+ rt_raster raster = NULL;
+ rt_band band = NULL;
+ int numbands = 0;
+ int dstnband = 1; /* 1-based */
+ int appendband = FALSE;
+ char *outdbfile = NULL;
+ int *srcnband = NULL; /* 1-based */
+ int numsrcnband = 0;
+ int allbands = FALSE;
+ int hasnodata = FALSE;
+ double nodataval = 0.;
+ uint16_t width = 0;
+ uint16_t height = 0;
+ char *authname = NULL;
+ char *authcode = NULL;
+
+ int i = 0;
+ int j = 0;
+
+ GDALDatasetH hdsOut;
+ GDALRasterBandH hbandOut;
+ GDALDataType gdpixtype;
+
+ rt_pixtype pt = PT_END;
+ double gt[6] = {0.};
+ double ogt[6] = {0.};
+ rt_raster _rast = NULL;
+ int aligned = 0;
+ int err = 0;
+
+ /* destination raster */
+ if (!PG_ARGISNULL(0)) {
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+ /* raster */
+ raster = rt_raster_deserialize(pgraster, FALSE);
+ if (!raster) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_addBandOutDB: Could not deserialize destination raster");
+ PG_RETURN_NULL();
+ }
+
+ POSTGIS_RT_DEBUG(4, "destination raster isn't NULL");
+ }
+
+ /* destination band index (1) */
+ if (!PG_ARGISNULL(1))
+ dstnband = PG_GETARG_INT32(1);
+ else
+ appendband = TRUE;
+
+ /* outdb file (2) */
+ if (PG_ARGISNULL(2)) {
+ elog(NOTICE, "Out-db raster file not provided. Returning original raster");
+ if (pgraster != NULL) {
+ rt_raster_destroy(raster);
+ PG_RETURN_POINTER(pgraster);
+ }
+ else
+ PG_RETURN_NULL();
+ }
+ else {
+ outdbfile = text_to_cstring(PG_GETARG_TEXT_P(2));
+ if (!strlen(outdbfile)) {
+ elog(NOTICE, "Out-db raster file not provided. Returning original raster");
+ if (pgraster != NULL) {
+ rt_raster_destroy(raster);
+ PG_RETURN_POINTER(pgraster);
+ }
+ else
+ PG_RETURN_NULL();
+ }
+ }
+
+ /* outdb band index (3) */
+ if (!PG_ARGISNULL(3)) {
+ ArrayType *array;
+ Oid etype;
+ Datum *e;
+ bool *nulls;
+
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+
+ allbands = FALSE;
+
+ array = PG_GETARG_ARRAYTYPE_P(3);
+ etype = ARR_ELEMTYPE(array);
+ get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
+
+ switch (etype) {
+ case INT2OID:
+ case INT4OID:
+ break;
+ default:
+ if (pgraster != NULL) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ }
+ elog(ERROR, "RASTER_addBandOutDB: Invalid data type for band indexes");
+ PG_RETURN_NULL();
+ break;
+ }
+
+ deconstruct_array(array, etype, typlen, typbyval, typalign, &e, &nulls, &numsrcnband);
+
+ srcnband = palloc(sizeof(int) * numsrcnband);
+ if (srcnband == NULL) {
+ if (pgraster != NULL) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ }
+ elog(ERROR, "RASTER_addBandOutDB: Could not allocate memory for band indexes");
+ PG_RETURN_NULL();
+ }
+
+ for (i = 0, j = 0; i < numsrcnband; i++) {
+ if (nulls[i]) continue;
+
+ switch (etype) {
+ case INT2OID:
+ srcnband[j] = DatumGetInt16(e[i]);
+ break;
+ case INT4OID:
+ srcnband[j] = DatumGetInt32(e[i]);
+ break;
+ }
+ j++;
+ }
+
+ if (j < numsrcnband) {
+ srcnband = repalloc(srcnband, sizeof(int) * j);
+ if (srcnband == NULL) {
+ if (pgraster != NULL) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ }
+ elog(ERROR, "RASTER_addBandOutDB: Could not reallocate memory for band indexes");
+ PG_RETURN_NULL();
+ }
+
+ numsrcnband = j;
+ }
+ }
+ else
+ allbands = TRUE;
+
+ /* nodataval (4) */
+ if (!PG_ARGISNULL(4)) {
+ hasnodata = TRUE;
+ nodataval = PG_GETARG_FLOAT8(4);
+ }
+ else
+ hasnodata = FALSE;
+
+ /* validate input */
+
+ /* make sure dstnband is valid */
+ if (raster != NULL) {
+ numbands = rt_raster_get_num_bands(raster);
+ if (!appendband) {
+ if (dstnband < 1) {
+ elog(NOTICE, "Invalid band index %d for adding bands. Using band index 1", dstnband);
+ dstnband = 1;
+ }
+ else if (dstnband > numbands) {
+ elog(NOTICE, "Invalid band index %d for adding bands. Using band index %d", dstnband, numbands);
+ dstnband = numbands + 1;
+ }
+ }
+ else
+ dstnband = numbands + 1;
+ }
+
+ /* open outdb raster file */
+ rt_util_gdal_register_all();
+ hdsOut = GDALOpenShared(outdbfile, GA_ReadOnly);
+ if (hdsOut == NULL) {
+ if (pgraster != NULL) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ }
+ elog(ERROR, "RASTER_addBandOutDB: Could not open out-db file with GDAL");
+ PG_RETURN_NULL();
+ }
+
+ /* get offline raster's geotransform */
+ if (GDALGetGeoTransform(hdsOut, ogt) != CE_None) {
+ ogt[0] = 0;
+ ogt[1] = 1;
+ ogt[2] = 0;
+ ogt[3] = 0;
+ ogt[4] = 0;
+ ogt[5] = -1;
+ }
+
+ /* raster doesn't exist, create it now */
+ if (raster == NULL) {
+ raster = rt_raster_new(GDALGetRasterXSize(hdsOut), GDALGetRasterYSize(hdsOut));
+ if (rt_raster_is_empty(raster)) {
+ elog(ERROR, "RASTER_addBandOutDB: Could not create new raster");
+ PG_RETURN_NULL();
+ }
+ rt_raster_set_geotransform_matrix(raster, ogt);
+ rt_raster_get_geotransform_matrix(raster, gt);
+
+ if (rt_util_gdal_sr_auth_info(hdsOut, &authname, &authcode) == ES_NONE) {
+ if (
+ authname != NULL &&
+ strcmp(authname, "EPSG") == 0 &&
+ authcode != NULL
+ ) {
+ rt_raster_set_srid(raster, atoi(authcode));
+ }
+ else
+ elog(INFO, "Unknown SRS auth name and code from out-db file. Defaulting SRID of new raster to %d", SRID_UNKNOWN);
+ }
+ else
+ elog(INFO, "Could not get SRS auth name and code from out-db file. Defaulting SRID of new raster to %d", SRID_UNKNOWN);
+ }
+
+ /* some raster info */
+ width = rt_raster_get_width(raster);
+ height = rt_raster_get_width(raster);
+
+ /* are rasters aligned? */
+ _rast = rt_raster_new(1, 1);
+ rt_raster_set_geotransform_matrix(_rast, ogt);
+ rt_raster_set_srid(_rast, rt_raster_get_srid(raster));
+ err = rt_raster_same_alignment(raster, _rast, &aligned, NULL);
+ rt_raster_destroy(_rast);
+
+ if (err != ES_NONE) {
+ GDALClose(hdsOut);
+ if (raster != NULL)
+ rt_raster_destroy(raster);
+ if (pgraster != NULL)
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_addBandOutDB: Could not test alignment of out-db file");
+ return ES_ERROR;
+ }
+ else if (!aligned)
+ elog(WARNING, "The in-db representation of the out-db raster is not aligned. Band data may be incorrect");
+
+ numbands = GDALGetRasterCount(hdsOut);
+
+ /* build up srcnband */
+ if (allbands) {
+ numsrcnband = numbands;
+ srcnband = palloc(sizeof(int) * numsrcnband);
+ if (srcnband == NULL) {
+ GDALClose(hdsOut);
+ if (raster != NULL)
+ rt_raster_destroy(raster);
+ if (pgraster != NULL)
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_addBandOutDB: Could not allocate memory for band indexes");
+ PG_RETURN_NULL();
+ }
+
+ for (i = 0, j = 1; i < numsrcnband; i++, j++)
+ srcnband[i] = j;
+ }
+
+ /* check band properties and add band */
+ for (i = 0, j = dstnband - 1; i < numsrcnband; i++, j++) {
+ /* valid index? */
+ if (srcnband[i] < 1 || srcnband[i] > numbands) {
+ elog(NOTICE, "Out-db file does not have a band at index %d. Returning original raster", srcnband[i]);
+ GDALClose(hdsOut);
+ if (raster != NULL)
+ rt_raster_destroy(raster);
+ if (pgraster != NULL)
+ PG_RETURN_POINTER(pgraster);
+ else
+ PG_RETURN_NULL();
+ }
+
+ /* get outdb band */
+ hbandOut = NULL;
+ hbandOut = GDALGetRasterBand(hdsOut, srcnband[i]);
+ if (NULL == hbandOut) {
+ GDALClose(hdsOut);
+ if (raster != NULL)
+ rt_raster_destroy(raster);
+ if (pgraster != NULL)
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_addBandOutDB: Could not get band %d from GDAL dataset", srcnband[i]);
+ PG_RETURN_NULL();
+ }
+
+ /* supported pixel type */
+ gdpixtype = GDALGetRasterDataType(hbandOut);
+ pt = rt_util_gdal_datatype_to_pixtype(gdpixtype);
+ if (pt == PT_END) {
+ elog(NOTICE, "Pixel type %s of band %d from GDAL dataset is not supported. Returning original raster", GDALGetDataTypeName(gdpixtype), srcnband[i]);
+ GDALClose(hdsOut);
+ if (raster != NULL)
+ rt_raster_destroy(raster);
+ if (pgraster != NULL)
+ PG_RETURN_POINTER(pgraster);
+ else
+ PG_RETURN_NULL();
+ }
+
+ /* use out-db band's nodata value if nodataval not already set */
+ if (!hasnodata)
+ nodataval = GDALGetRasterNoDataValue(hbandOut, &hasnodata);
+
+ /* add band */
+ band = rt_band_new_offline(
+ width, height,
+ pt,
+ hasnodata, nodataval,
+ srcnband[i] - 1, outdbfile
+ );
+ if (band == NULL) {
+ GDALClose(hdsOut);
+ if (raster != NULL)
+ rt_raster_destroy(raster);
+ if (pgraster != NULL)
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_addBandOutDB: Could not create new out-db band");
+ PG_RETURN_NULL();
+ }
+
+ if (rt_raster_add_band(raster, band, j) < 0) {
+ GDALClose(hdsOut);
+ if (raster != NULL)
+ rt_raster_destroy(raster);
+ if (pgraster != NULL)
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_addBandOutDB: Could not add new out-db band to raster");
+ PG_RETURN_NULL();
+ }
+ }
+
+ pgrtn = rt_raster_serialize(raster);
+ rt_raster_destroy(raster);
+ if (pgraster != NULL)
+ PG_FREE_IF_COPY(pgraster, 0);
+ if (!pgrtn)
+ PG_RETURN_NULL();
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+}
+
+/**
+ * Copy a band from one raster to another one at the given position.
+ */
+PG_FUNCTION_INFO_V1(RASTER_copyBand);
+Datum RASTER_copyBand(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgto = NULL;
+ rt_pgraster *pgfrom = NULL;
+ rt_pgraster *pgrtn = NULL;
+ rt_raster torast = NULL;
+ rt_raster fromrast = NULL;
+ int toindex = 0;
+ int fromband = 0;
+ int oldtorastnumbands = 0;
+ int newtorastnumbands = 0;
+ int newbandindex = 0;
+
+ /* Deserialize torast */
+ if (PG_ARGISNULL(0)) PG_RETURN_NULL();
+ pgto = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+ torast = rt_raster_deserialize(pgto, FALSE);
+ if (!torast) {
+ PG_FREE_IF_COPY(pgto, 0);
+ elog(ERROR, "RASTER_copyBand: Could not deserialize first raster");
+ PG_RETURN_NULL();
+ }
+
+ /* Deserialize fromrast */
+ if (!PG_ARGISNULL(1)) {
+ pgfrom = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+ fromrast = rt_raster_deserialize(pgfrom, FALSE);
+ if (!fromrast) {
+ rt_raster_destroy(torast);
+ PG_FREE_IF_COPY(pgfrom, 1);
+ PG_FREE_IF_COPY(pgto, 0);
+ elog(ERROR, "RASTER_copyBand: Could not deserialize second raster");
+ PG_RETURN_NULL();
+ }
+
+ oldtorastnumbands = rt_raster_get_num_bands(torast);
+
+ if (PG_ARGISNULL(2))
+ fromband = 1;
+ else
+ fromband = PG_GETARG_INT32(2);
+
+ if (PG_ARGISNULL(3))
+ toindex = oldtorastnumbands + 1;
+ else
+ toindex = PG_GETARG_INT32(3);
+
+ /* Copy band fromrast torast */
+ newbandindex = rt_raster_copy_band(
+ torast, fromrast,
+ fromband - 1, toindex - 1
+ );
+
+ newtorastnumbands = rt_raster_get_num_bands(torast);
+ if (newtorastnumbands == oldtorastnumbands || newbandindex == -1) {
+ elog(NOTICE, "RASTER_copyBand: Could not add band to raster. "
+ "Returning original raster."
+ );
+ }
+
+ rt_raster_destroy(fromrast);
+ PG_FREE_IF_COPY(pgfrom, 1);
+ }
+
+ /* Serialize and return torast */
+ pgrtn = rt_raster_serialize(torast);
+ rt_raster_destroy(torast);
+ PG_FREE_IF_COPY(pgto, 0);
+ if (!pgrtn) PG_RETURN_NULL();
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+}
+
+/**
+ * Break up a raster into smaller tiles. SRF function
+ */
+PG_FUNCTION_INFO_V1(RASTER_tile);
+Datum RASTER_tile(PG_FUNCTION_ARGS)
+{
+ FuncCallContext *funcctx;
+ int call_cntr;
+ int max_calls;
+ int i = 0;
+ int j = 0;
+
+ struct tile_arg_t {
+
+ struct {
+ rt_raster raster;
+ double gt[6];
+ int srid;
+ int width;
+ int height;
+ } raster;
+
+ struct {
+ int width;
+ int height;
+
+ int nx;
+ int ny;
+ } tile;
+
+ int numbands;
+ int *nbands;
+
+ struct {
+ int pad;
+ double hasnodata;
+ double nodataval;
+ } pad;
+ };
+ struct tile_arg_t *arg1 = NULL;
+ struct tile_arg_t *arg2 = NULL;
+
+ if (SRF_IS_FIRSTCALL()) {
+ MemoryContext oldcontext;
+ rt_pgraster *pgraster = NULL;
+ int numbands;
+
+ ArrayType *array;
+ Oid etype;
+ Datum *e;
+ bool *nulls;
+
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+
+ POSTGIS_RT_DEBUG(2, "RASTER_tile: 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);
+ }
+
+ /* allocate arg1 */
+ arg1 = palloc(sizeof(struct tile_arg_t));
+ if (arg1 == NULL) {
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_tile: Could not allocate memory for arguments");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0));
+ arg1->raster.raster = rt_raster_deserialize(pgraster, FALSE);
+ if (!arg1->raster.raster) {
+ ereport(ERROR, (
+ errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("Could not deserialize raster")
+ ));
+ pfree(arg1);
+ PG_FREE_IF_COPY(pgraster, 0);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* raster has bands */
+ numbands = rt_raster_get_num_bands(arg1->raster.raster);
+ /*
+ if (!numbands) {
+ elog(NOTICE, "Raster provided has no bands");
+ rt_raster_destroy(arg1->raster.raster);
+ pfree(arg1);
+ PG_FREE_IF_COPY(pgraster, 0);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ */
+
+ /* width (1) */
+ if (PG_ARGISNULL(1)) {
+ elog(NOTICE, "Width cannot be NULL. Returning NULL");
+ rt_raster_destroy(arg1->raster.raster);
+ pfree(arg1);
+ PG_FREE_IF_COPY(pgraster, 0);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ arg1->tile.width = PG_GETARG_INT32(1);
+ if (arg1->tile.width < 1) {
+ elog(NOTICE, "Width must be greater than zero. Returning NULL");
+ rt_raster_destroy(arg1->raster.raster);
+ pfree(arg1);
+ PG_FREE_IF_COPY(pgraster, 0);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* height (2) */
+ if (PG_ARGISNULL(2)) {
+ elog(NOTICE, "Height cannot be NULL. Returning NULL");
+ rt_raster_destroy(arg1->raster.raster);
+ pfree(arg1);
+ PG_FREE_IF_COPY(pgraster, 0);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ arg1->tile.height = PG_GETARG_INT32(2);
+ if (arg1->tile.height < 1) {
+ elog(NOTICE, "Height must be greater than zero. Returning NULL");
+ rt_raster_destroy(arg1->raster.raster);
+ pfree(arg1);
+ PG_FREE_IF_COPY(pgraster, 0);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* nband, array (3) */
+ if (numbands && !PG_ARGISNULL(3)) {
+ array = PG_GETARG_ARRAYTYPE_P(3);
+ etype = ARR_ELEMTYPE(array);
+ get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
+
+ switch (etype) {
+ case INT2OID:
+ case INT4OID:
+ break;
+ default:
+ rt_raster_destroy(arg1->raster.raster);
+ pfree(arg1);
+ PG_FREE_IF_COPY(pgraster, 0);
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_tile: 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) {
+ rt_raster_destroy(arg1->raster.raster);
+ pfree(arg1);
+ PG_FREE_IF_COPY(pgraster, 0);
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_tile: 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) {
+ rt_raster_destroy(arg1->raster.raster);
+ pfree(arg1);
+ PG_FREE_IF_COPY(pgraster, 0);
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_tile: 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(arg1->raster.raster, arg1->nbands[i])) {
+ elog(NOTICE, "Band at index %d not found in raster", arg1->nbands[i] + 1);
+ rt_raster_destroy(arg1->raster.raster);
+ pfree(arg1->nbands);
+ pfree(arg1);
+ PG_FREE_IF_COPY(pgraster, 0);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ }
+ }
+ else {
+ arg1->numbands = numbands;
+
+ if (numbands) {
+ arg1->nbands = palloc(sizeof(int) * arg1->numbands);
+
+ if (arg1->nbands == NULL) {
+ rt_raster_destroy(arg1->raster.raster);
+ pfree(arg1);
+ PG_FREE_IF_COPY(pgraster, 0);
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_dumpValues: Could not allocate memory for pixel values");
+ 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);
+ }
+ }
+ }
+
+ /* pad (4) and padnodata (5) */
+ if (!PG_ARGISNULL(4)) {
+ arg1->pad.pad = PG_GETARG_BOOL(4) ? 1 : 0;
+
+ if (arg1->pad.pad && !PG_ARGISNULL(5)) {
+ arg1->pad.hasnodata = 1;
+ arg1->pad.nodataval = PG_GETARG_FLOAT8(5);
+ }
+ else {
+ arg1->pad.hasnodata = 0;
+ arg1->pad.nodataval = 0;
+ }
+ }
+ else {
+ arg1->pad.pad = 0;
+ arg1->pad.hasnodata = 0;
+ arg1->pad.nodataval = 0;
+ }
+
+ /* store some additional metadata */
+ arg1->raster.srid = rt_raster_get_srid(arg1->raster.raster);
+ arg1->raster.width = rt_raster_get_width(arg1->raster.raster);
+ arg1->raster.height = rt_raster_get_height(arg1->raster.raster);
+ rt_raster_get_geotransform_matrix(arg1->raster.raster, arg1->raster.gt);
+
+ /* determine maximum number of tiles from raster */
+ arg1->tile.nx = ceil(arg1->raster.width / (double) arg1->tile.width);
+ arg1->tile.ny = ceil(arg1->raster.height / (double) arg1->tile.height);
+ POSTGIS_RT_DEBUGF(4, "# of tiles (x, y) = (%d, %d)", arg1->tile.nx, arg1->tile.ny);
+
+ /* Store needed information */
+ funcctx->user_fctx = arg1;
+
+ /* total number of tuples to be returned */
+ funcctx->max_calls = (arg1->tile.nx * arg1->tile.ny);
+
+ MemoryContextSwitchTo(oldcontext);
+ }
+
+ /* stuff done on every call of the function */
+ funcctx = SRF_PERCALL_SETUP();
+
+ call_cntr = funcctx->call_cntr;
+ max_calls = funcctx->max_calls;
+ arg2 = funcctx->user_fctx;
+
+ /* do when there is more left to send */
+ if (call_cntr < max_calls) {
+ rt_pgraster *pgtile = NULL;
+ rt_raster tile = NULL;
+ rt_band _band = NULL;
+ rt_band band = NULL;
+ rt_pixtype pixtype = PT_END;
+ int hasnodata = 0;
+ double nodataval = 0;
+ int width = 0;
+ int height = 0;
+
+ int k = 0;
+ int tx = 0;
+ int ty = 0;
+ int rx = 0;
+ int ry = 0;
+ int ex = 0; /* edge tile on right */
+ int ey = 0; /* edge tile on bottom */
+ double ulx = 0;
+ double uly = 0;
+ uint16_t len = 0;
+ void *vals = NULL;
+ uint16_t nvals;
+
+ POSTGIS_RT_DEBUGF(3, "call number %d", call_cntr);
+
+ /*
+ find offset based upon tile #
+
+ 0 1 2
+ 3 4 5
+ 6 7 8
+ */
+ ty = call_cntr / arg2->tile.nx;
+ tx = call_cntr % arg2->tile.nx;
+ POSTGIS_RT_DEBUGF(4, "tile (x, y) = (%d, %d)", tx, ty);
+
+ /* edge tile? only important if padding is false */
+ if (!arg2->pad.pad) {
+ if (ty + 1 == arg2->tile.ny)
+ ey = 1;
+ if (tx + 1 == arg2->tile.nx)
+ ex = 1;
+ }
+
+ /* upper-left of tile in raster coordinates */
+ rx = tx * arg2->tile.width;
+ ry = ty * arg2->tile.height;
+ POSTGIS_RT_DEBUGF(4, "raster coordinates = %d, %d", rx, ry);
+
+ /* determine tile width and height */
+ /* default to user-defined */
+ width = arg2->tile.width;
+ height = arg2->tile.height;
+
+ /* override user-defined if edge tile (only possible if padding is false */
+ if (ex || ey) {
+ /* right edge */
+ if (ex)
+ width = arg2->raster.width - rx;
+ /* bottom edge */
+ if (ey)
+ height = arg2->raster.height - ry;
+ }
+
+ /* create empty raster */
+ tile = rt_raster_new(width, height);
+ rt_raster_set_geotransform_matrix(tile, arg2->raster.gt);
+ rt_raster_set_srid(tile, arg2->raster.srid);
+
+ /* upper-left of tile in spatial coordinates */
+ if (rt_raster_cell_to_geopoint(arg2->raster.raster, rx, ry, &ulx, &uly, arg2->raster.gt) != ES_NONE) {
+ rt_raster_destroy(tile);
+ rt_raster_destroy(arg2->raster.raster);
+ if (arg2->numbands) pfree(arg2->nbands);
+ pfree(arg2);
+ elog(ERROR, "RASTER_tile: Could not compute the coordinates of the upper-left corner of the output tile");
+ SRF_RETURN_DONE(funcctx);
+ }
+ rt_raster_set_offsets(tile, ulx, uly);
+ POSTGIS_RT_DEBUGF(4, "spatial coordinates = %f, %f", ulx, uly);
+
+ /* compute length of pixel line to read */
+ len = arg2->tile.width;
+ if (rx + arg2->tile.width >= arg2->raster.width)
+ len = arg2->raster.width - rx;
+ POSTGIS_RT_DEBUGF(3, "read line len = %d", len);
+
+ /* copy bands to tile */
+ for (i = 0; i < arg2->numbands; i++) {
+ POSTGIS_RT_DEBUGF(4, "copying band %d to tile %d", arg2->nbands[i], call_cntr);
+
+ _band = rt_raster_get_band(arg2->raster.raster, arg2->nbands[i]);
+ if (_band == NULL) {
+ int nband = arg2->nbands[i] + 1;
+ rt_raster_destroy(tile);
+ rt_raster_destroy(arg2->raster.raster);
+ if (arg2->numbands) pfree(arg2->nbands);
+ pfree(arg2);
+ elog(ERROR, "RASTER_tile: Could not get band %d from source raster", nband);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ pixtype = rt_band_get_pixtype(_band);
+ hasnodata = rt_band_get_hasnodata_flag(_band);
+ if (hasnodata)
+ rt_band_get_nodata(_band, &nodataval);
+ else if (arg2->pad.pad && arg2->pad.hasnodata) {
+ hasnodata = 1;
+ nodataval = arg2->pad.nodataval;
+ }
+ else
+ nodataval = rt_band_get_min_value(_band);
+
+ /* inline band */
+ if (!rt_band_is_offline(_band)) {
+ if (rt_raster_generate_new_band(tile, pixtype, nodataval, hasnodata, nodataval, i) < 0) {
+ rt_raster_destroy(tile);
+ rt_raster_destroy(arg2->raster.raster);
+ pfree(arg2->nbands);
+ pfree(arg2);
+ elog(ERROR, "RASTER_tile: Could not add new band to output tile");
+ SRF_RETURN_DONE(funcctx);
+ }
+ band = rt_raster_get_band(tile, i);
+ if (band == NULL) {
+ rt_raster_destroy(tile);
+ rt_raster_destroy(arg2->raster.raster);
+ if (arg2->numbands) pfree(arg2->nbands);
+ pfree(arg2);
+ elog(ERROR, "RASTER_tile: Could not get newly added band from output tile");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* if isnodata, set flag and continue */
+ if (rt_band_get_isnodata_flag(_band)) {
+ rt_band_set_isnodata_flag(band, 1);
+ continue;
+ }
+
+ /* copy data */
+ for (j = 0; j < arg2->tile.height; j++) {
+ k = ry + j;
+
+ if (k >= arg2->raster.height) {
+ POSTGIS_RT_DEBUGF(4, "row %d is beyond extent of source raster. skipping", k);
+ continue;
+ }
+
+ POSTGIS_RT_DEBUGF(4, "getting pixel line %d, %d for %d pixels", rx, k, len);
+ if (rt_band_get_pixel_line(_band, rx, k, len, &vals, &nvals) != ES_NONE) {
+ rt_raster_destroy(tile);
+ rt_raster_destroy(arg2->raster.raster);
+ if (arg2->numbands) pfree(arg2->nbands);
+ pfree(arg2);
+ elog(ERROR, "RASTER_tile: Could not get pixel line from source raster");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ if (nvals && rt_band_set_pixel_line(band, 0, j, vals, nvals) != ES_NONE) {
+ rt_raster_destroy(tile);
+ rt_raster_destroy(arg2->raster.raster);
+ if (arg2->numbands) pfree(arg2->nbands);
+ pfree(arg2);
+ elog(ERROR, "RASTER_tile: Could not set pixel line of output tile");
+ SRF_RETURN_DONE(funcctx);
+ }
+ }
+ }
+ /* offline */
+ else {
+ uint8_t bandnum = 0;
+ rt_band_get_ext_band_num(_band, &bandnum);
+
+ band = rt_band_new_offline(
+ width, height,
+ pixtype,
+ hasnodata, nodataval,
+ bandnum, rt_band_get_ext_path(_band)
+ );
+
+ if (band == NULL) {
+ rt_raster_destroy(tile);
+ rt_raster_destroy(arg2->raster.raster);
+ if (arg2->numbands) pfree(arg2->nbands);
+ pfree(arg2);
+ elog(ERROR, "RASTER_tile: Could not create new offline band for output tile");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ if (rt_raster_add_band(tile, band, i) < 0) {
+ rt_band_destroy(band);
+ rt_raster_destroy(tile);
+ rt_raster_destroy(arg2->raster.raster);
+ if (arg2->numbands) pfree(arg2->nbands);
+ pfree(arg2);
+ elog(ERROR, "RASTER_tile: Could not add new offline band to output tile");
+ SRF_RETURN_DONE(funcctx);
+ }
+ }
+ }
+
+ pgtile = rt_raster_serialize(tile);
+ rt_raster_destroy(tile);
+ if (!pgtile) {
+ rt_raster_destroy(arg2->raster.raster);
+ if (arg2->numbands) pfree(arg2->nbands);
+ pfree(arg2);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ SET_VARSIZE(pgtile, pgtile->size);
+ SRF_RETURN_NEXT(funcctx, PointerGetDatum(pgtile));
+ }
+ /* do when there is no more left */
+ else {
+ rt_raster_destroy(arg2->raster.raster);
+ if (arg2->numbands) pfree(arg2->nbands);
+ pfree(arg2);
+ SRF_RETURN_DONE(funcctx);
+ }
+}
+
+/**
+ * Return new raster from selected bands of existing raster through ST_Band.
+ * second argument is an array of band numbers (1 based)
+ */
+PG_FUNCTION_INFO_V1(RASTER_band);
+Datum RASTER_band(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster;
+ rt_pgraster *pgrast;
+ rt_raster raster;
+ rt_raster rast;
+
+ bool skip = FALSE;
+ ArrayType *array;
+ Oid etype;
+ Datum *e;
+ bool *nulls;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+
+ uint32_t numBands;
+ uint32_t *bandNums;
+ uint32 idx = 0;
+ int n;
+ int i = 0;
+ int j = 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_band: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ /* process bandNums */
+ if (PG_ARGISNULL(1)) {
+ elog(NOTICE, "Band number(s) not provided. Returning original raster");
+ skip = TRUE;
+ }
+ do {
+ if (skip) break;
+
+ numBands = rt_raster_get_num_bands(raster);
+
+ array = PG_GETARG_ARRAYTYPE_P(1);
+ etype = ARR_ELEMTYPE(array);
+ get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
+
+ switch (etype) {
+ case INT2OID:
+ case INT4OID:
+ break;
+ default:
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_band: Invalid data type for band number(s)");
+ PG_RETURN_NULL();
+ break;
+ }
+
+ deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
+ &nulls, &n);
+
+ bandNums = palloc(sizeof(uint32_t) * n);
+ for (i = 0, j = 0; i < n; i++) {
+ if (nulls[i]) continue;
+
+ switch (etype) {
+ case INT2OID:
+ idx = (uint32_t) DatumGetInt16(e[i]);
+ break;
+ case INT4OID:
+ idx = (uint32_t) DatumGetInt32(e[i]);
+ break;
+ }
+
+ POSTGIS_RT_DEBUGF(3, "band idx (before): %d", idx);
+ if (idx > numBands || idx < 1) {
+ elog(NOTICE, "Invalid band index (must use 1-based). Returning original raster");
+ skip = TRUE;
+ break;
+ }
+
+ bandNums[j] = idx - 1;
+ POSTGIS_RT_DEBUGF(3, "bandNums[%d] = %d", j, bandNums[j]);
+ j++;
+ }
+
+ if (skip || j < 1) {
+ pfree(bandNums);
+ skip = TRUE;
+ }
+ }
+ while (0);
+
+ if (!skip) {
+ rast = rt_raster_from_band(raster, bandNums, j);
+ pfree(bandNums);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ if (!rast) {
+ elog(ERROR, "RASTER_band: Could not create new raster");
+ PG_RETURN_NULL();
+ }
+
+ pgrast = rt_raster_serialize(rast);
+ rt_raster_destroy(rast);
+
+ if (!pgrast)
+ PG_RETURN_NULL();
+
+ SET_VARSIZE(pgrast, pgrast->size);
+ PG_RETURN_POINTER(pgrast);
+ }
+
+ PG_RETURN_POINTER(pgraster);
+}
--- /dev/null
+/*
+ * $Id$
+ *
+ * WKTRaster - Raster Types for PostGIS
+ * http://www.postgis.org/support/wiki/index.php?WKTRasterHomePage
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <postgres.h>
+#include <fmgr.h>
+#include <funcapi.h> /* for SRF */
+#include <utils/builtins.h> /* for text_to_cstring() */
+#include "utils/lsyscache.h" /* for get_typlenbyvalalign */
+#include "utils/array.h" /* for ArrayType */
+#include "catalog/pg_type.h" /* for INT2OID, INT4OID, FLOAT4OID, FLOAT8OID and TEXTOID */
+
+#include "rtpostgis.h"
+#include "rtpg_internal.h"
+
+/* convert GDAL raster to raster */
+Datum RASTER_fromGDALRaster(PG_FUNCTION_ARGS);
+
+/* convert raster to GDAL raster */
+Datum RASTER_asGDALRaster(PG_FUNCTION_ARGS);
+Datum RASTER_getGDALDrivers(PG_FUNCTION_ARGS);
+
+/* warp a raster using GDAL Warp API */
+Datum RASTER_GDALWarp(PG_FUNCTION_ARGS);
+
+/* ---------------------------------------------------------------- */
+/* Returns raster from GDAL raster */
+/* ---------------------------------------------------------------- */
+PG_FUNCTION_INFO_V1(RASTER_fromGDALRaster);
+Datum RASTER_fromGDALRaster(PG_FUNCTION_ARGS)
+{
+ bytea *bytea_data;
+ uint8_t *data;
+ int data_len = 0;
+ VSILFILE *vsifp = NULL;
+ GDALDatasetH hdsSrc;
+ int srid = -1; /* -1 for NULL */
+
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster;
+
+ /* NULL if NULL */
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+
+ /* get data */
+ bytea_data = (bytea *) PG_GETARG_BYTEA_P(0);
+ data = (uint8_t *) VARDATA(bytea_data);
+ data_len = VARSIZE(bytea_data) - VARHDRSZ;
+
+ /* process srid */
+ /* NULL srid means try to determine SRID from bytea */
+ if (!PG_ARGISNULL(1))
+ srid = clamp_srid(PG_GETARG_INT32(1));
+
+ /* create memory "file" */
+ vsifp = VSIFileFromMemBuffer("/vsimem/in.dat", data, data_len, FALSE);
+ if (vsifp == NULL) {
+ PG_FREE_IF_COPY(bytea_data, 0);
+ elog(ERROR, "RASTER_fromGDALRaster: Could not load bytea into memory file for use by GDAL");
+ PG_RETURN_NULL();
+ }
+
+ /* register all GDAL drivers */
+ rt_util_gdal_register_all();
+
+ /* open GDAL raster */
+ hdsSrc = GDALOpenShared("/vsimem/in.dat", GA_ReadOnly);
+ if (hdsSrc == NULL) {
+ VSIFCloseL(vsifp);
+ PG_FREE_IF_COPY(bytea_data, 0);
+ elog(ERROR, "RASTER_fromGDALRaster: Could not open bytea with GDAL. Check that the bytea is of a GDAL supported format");
+ PG_RETURN_NULL();
+ }
+
+#if POSTGIS_DEBUG_LEVEL > 3
+ {
+ GDALDriverH hdrv = GDALGetDatasetDriver(hdsSrc);
+
+ POSTGIS_RT_DEBUGF(4, "Input GDAL Raster info: %s, (%d x %d)",
+ GDALGetDriverShortName(hdrv),
+ GDALGetRasterXSize(hdsSrc),
+ GDALGetRasterYSize(hdsSrc)
+ );
+ }
+#endif
+
+ /* convert GDAL raster to raster */
+ raster = rt_raster_from_gdal_dataset(hdsSrc);
+
+ GDALClose(hdsSrc);
+ VSIFCloseL(vsifp);
+ PG_FREE_IF_COPY(bytea_data, 0);
+
+ if (raster == NULL) {
+ elog(ERROR, "RASTER_fromGDALRaster: Could not convert GDAL raster to raster");
+ PG_RETURN_NULL();
+ }
+
+ /* apply SRID if set */
+ if (srid != -1)
+ rt_raster_set_srid(raster, srid);
+
+ pgraster = rt_raster_serialize(raster);
+ rt_raster_destroy(raster);
+ if (!pgraster)
+ PG_RETURN_NULL();
+
+ SET_VARSIZE(pgraster, pgraster->size);
+ PG_RETURN_POINTER(pgraster);
+}
+
+/**
+ * Returns formatted GDAL raster as bytea object of raster
+ */
+PG_FUNCTION_INFO_V1(RASTER_asGDALRaster);
+Datum RASTER_asGDALRaster(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster;
+
+ text *formattext = NULL;
+ char *format = NULL;
+ char **options = NULL;
+ text *optiontext = NULL;
+ char *option = NULL;
+ int srid = SRID_UNKNOWN;
+ char *srs = NULL;
+
+ ArrayType *array;
+ Oid etype;
+ Datum *e;
+ bool *nulls;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ int n = 0;
+ int i = 0;
+ int j = 0;
+
+ uint8_t *gdal = NULL;
+ uint64_t gdal_size = 0;
+ bytea *result = NULL;
+ uint64_t result_size = 0;
+
+ POSTGIS_RT_DEBUG(3, "RASTER_asGDALRaster: Starting");
+
+ /* pgraster is null, return null */
+ 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_asGDALRaster: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ /* format is required */
+ if (PG_ARGISNULL(1)) {
+ elog(NOTICE, "Format must be provided");
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ PG_RETURN_NULL();
+ }
+ else {
+ formattext = PG_GETARG_TEXT_P(1);
+ format = text_to_cstring(formattext);
+ }
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_asGDALRaster: Arg 1 (format) is %s", format);
+
+ /* process options */
+ if (!PG_ARGISNULL(2)) {
+ POSTGIS_RT_DEBUG(3, "RASTER_asGDALRaster: Processing Arg 2 (options)");
+ array = PG_GETARG_ARRAYTYPE_P(2);
+ etype = ARR_ELEMTYPE(array);
+ get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
+
+ switch (etype) {
+ case TEXTOID:
+ break;
+ default:
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_asGDALRaster: Invalid data type for options");
+ PG_RETURN_NULL();
+ break;
+ }
+
+ deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
+ &nulls, &n);
+
+ if (n) {
+ options = (char **) palloc(sizeof(char *) * (n + 1));
+ if (options == NULL) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_asGDALRaster: Could not allocate memory for options");
+ PG_RETURN_NULL();
+ }
+
+ /* clean each option */
+ for (i = 0, j = 0; i < n; i++) {
+ if (nulls[i]) continue;
+
+ option = NULL;
+ switch (etype) {
+ case TEXTOID:
+ optiontext = (text *) DatumGetPointer(e[i]);
+ if (NULL == optiontext) break;
+ option = text_to_cstring(optiontext);
+
+ /* trim string */
+ option = rtpg_trim(option);
+ POSTGIS_RT_DEBUGF(3, "RASTER_asGDALRaster: option is '%s'", option);
+ break;
+ }
+
+ if (strlen(option)) {
+ options[j] = (char *) palloc(sizeof(char) * (strlen(option) + 1));
+ options[j] = option;
+ j++;
+ }
+ }
+
+ if (j > 0) {
+ /* trim allocation */
+ options = repalloc(options, (j + 1) * sizeof(char *));
+
+ /* add NULL to end */
+ options[j] = NULL;
+
+ }
+ else {
+ pfree(options);
+ options = NULL;
+ }
+ }
+ }
+
+ /* process srid */
+ /* NULL srid means use raster's srid */
+ if (PG_ARGISNULL(3))
+ srid = rt_raster_get_srid(raster);
+ else
+ srid = PG_GETARG_INT32(3);
+
+ /* get srs from srid */
+ if (clamp_srid(srid) != SRID_UNKNOWN) {
+ srs = rtpg_getSR(srid);
+ if (NULL == srs) {
+ if (NULL != options) {
+ for (i = j - 1; i >= 0; i--) pfree(options[i]);
+ pfree(options);
+ }
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_asGDALRaster: Could not find srtext for SRID (%d)", srid);
+ PG_RETURN_NULL();
+ }
+ POSTGIS_RT_DEBUGF(3, "RASTER_asGDALRaster: Arg 3 (srs) is %s", srs);
+ }
+ else
+ srs = NULL;
+
+ POSTGIS_RT_DEBUG(3, "RASTER_asGDALRaster: Generating GDAL raster");
+ gdal = rt_raster_to_gdal(raster, srs, format, options, &gdal_size);
+
+ /* free memory */
+ if (NULL != options) {
+ for (i = j - 1; i >= 0; i--) pfree(options[i]);
+ pfree(options);
+ }
+ if (NULL != srs) pfree(srs);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ if (!gdal) {
+ elog(ERROR, "RASTER_asGDALRaster: Could not allocate and generate GDAL raster");
+ PG_RETURN_NULL();
+ }
+ POSTGIS_RT_DEBUGF(3, "RASTER_asGDALRaster: GDAL raster generated with %d bytes", (int) gdal_size);
+
+ /* result is a varlena */
+ result_size = gdal_size + VARHDRSZ;
+ result = (bytea *) palloc(result_size);
+ if (NULL == result) {
+ elog(ERROR, "RASTER_asGDALRaster: Insufficient virtual memory for GDAL raster");
+ PG_RETURN_NULL();
+ }
+ SET_VARSIZE(result, result_size);
+ memcpy(VARDATA(result), gdal, VARSIZE(result) - VARHDRSZ);
+
+ /* for test output
+ FILE *fh = NULL;
+ fh = fopen("/tmp/out.dat", "w");
+ fwrite(gdal, sizeof(uint8_t), gdal_size, fh);
+ fclose(fh);
+ */
+
+ /* free gdal mem buffer */
+ if (gdal) CPLFree(gdal);
+
+ POSTGIS_RT_DEBUG(3, "RASTER_asGDALRaster: Returning pointer to GDAL raster");
+ PG_RETURN_POINTER(result);
+}
+
+/**
+ * Returns available GDAL drivers
+ */
+PG_FUNCTION_INFO_V1(RASTER_getGDALDrivers);
+Datum RASTER_getGDALDrivers(PG_FUNCTION_ARGS)
+{
+ FuncCallContext *funcctx;
+ TupleDesc tupdesc;
+
+ uint32_t drv_count;
+ rt_gdaldriver drv_set;
+ rt_gdaldriver drv_set2;
+ int call_cntr;
+ int max_calls;
+
+ /* first call of function */
+ if (SRF_IS_FIRSTCALL()) {
+ MemoryContext oldcontext;
+
+ /* 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);
+
+ drv_set = rt_raster_gdal_drivers(&drv_count, 1);
+ if (NULL == drv_set || !drv_count) {
+ elog(NOTICE, "No GDAL drivers found");
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ POSTGIS_RT_DEBUGF(3, "%d drivers returned", (int) drv_count);
+
+ /* Store needed information */
+ funcctx->user_fctx = drv_set;
+
+ /* total number of tuples to be returned */
+ funcctx->max_calls = drv_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;
+ drv_set2 = funcctx->user_fctx;
+
+ /* do when there is more left to send */
+ if (call_cntr < max_calls) {
+ int values_length = 4;
+ Datum values[values_length];
+ bool nulls[values_length];
+ HeapTuple tuple;
+ Datum result;
+
+ POSTGIS_RT_DEBUGF(3, "Result %d", call_cntr);
+
+ memset(nulls, FALSE, sizeof(bool) * values_length);
+
+ values[0] = Int32GetDatum(drv_set2[call_cntr].idx);
+ values[1] = CStringGetTextDatum(drv_set2[call_cntr].short_name);
+ values[2] = CStringGetTextDatum(drv_set2[call_cntr].long_name);
+ values[3] = CStringGetTextDatum(drv_set2[call_cntr].create_options);
+
+ POSTGIS_RT_DEBUGF(4, "Result %d, Index %d", call_cntr, drv_set2[call_cntr].idx);
+ POSTGIS_RT_DEBUGF(4, "Result %d, Short Name %s", call_cntr, drv_set2[call_cntr].short_name);
+ POSTGIS_RT_DEBUGF(4, "Result %d, Full Name %s", call_cntr, drv_set2[call_cntr].long_name);
+ POSTGIS_RT_DEBUGF(5, "Result %d, Create Options %s", call_cntr, drv_set2[call_cntr].create_options);
+
+ /* build a tuple */
+ tuple = heap_form_tuple(tupdesc, values, nulls);
+
+ /* make the tuple into a datum */
+ result = HeapTupleGetDatum(tuple);
+
+ /* clean up */
+ pfree(drv_set2[call_cntr].short_name);
+ pfree(drv_set2[call_cntr].long_name);
+ pfree(drv_set2[call_cntr].create_options);
+
+ SRF_RETURN_NEXT(funcctx, result);
+ }
+ /* do when there is no more left */
+ else {
+ pfree(drv_set2);
+ SRF_RETURN_DONE(funcctx);
+ }
+}
+
+/**
+ * warp a raster using GDAL Warp API
+ */
+PG_FUNCTION_INFO_V1(RASTER_GDALWarp);
+Datum RASTER_GDALWarp(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_pgraster *pgrast = NULL;
+ rt_raster raster = NULL;
+ rt_raster rast = NULL;
+
+ text *algtext = NULL;
+ char *algchar = NULL;
+ GDALResampleAlg alg = GRA_NearestNeighbour;
+ double max_err = 0.125;
+
+ int src_srid = SRID_UNKNOWN;
+ char *src_srs = NULL;
+ int dst_srid = SRID_UNKNOWN;
+ char *dst_srs = NULL;
+ int no_srid = 0;
+
+ double scale[2] = {0};
+ double *scale_x = NULL;
+ double *scale_y = NULL;
+
+ double gridw[2] = {0};
+ double *grid_xw = NULL;
+ double *grid_yw = NULL;
+
+ double skew[2] = {0};
+ double *skew_x = NULL;
+ double *skew_y = NULL;
+
+ int dim[2] = {0};
+ int *dim_x = NULL;
+ int *dim_y = NULL;
+
+ POSTGIS_RT_DEBUG(3, "RASTER_GDALWarp: Starting");
+
+ /* pgraster is null, return null */
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+ /* raster */
+ raster = rt_raster_deserialize(pgraster, FALSE);
+ if (!raster) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_GDALWarp: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ /* resampling algorithm */
+ if (!PG_ARGISNULL(1)) {
+ algtext = PG_GETARG_TEXT_P(1);
+ algchar = rtpg_trim(rtpg_strtoupper(text_to_cstring(algtext)));
+ alg = rt_util_gdal_resample_alg(algchar);
+ }
+ POSTGIS_RT_DEBUGF(4, "Resampling algorithm: %d", alg);
+
+ /* max error */
+ if (!PG_ARGISNULL(2)) {
+ max_err = PG_GETARG_FLOAT8(2);
+ if (max_err < 0.) max_err = 0.;
+ }
+ POSTGIS_RT_DEBUGF(4, "max_err: %f", max_err);
+
+ /* source SRID */
+ src_srid = clamp_srid(rt_raster_get_srid(raster));
+ POSTGIS_RT_DEBUGF(4, "source SRID: %d", src_srid);
+
+ /* target SRID */
+ if (!PG_ARGISNULL(3)) {
+ dst_srid = clamp_srid(PG_GETARG_INT32(3));
+ if (dst_srid == SRID_UNKNOWN) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_GDALWarp: %d is an invalid target SRID", dst_srid);
+ PG_RETURN_NULL();
+ }
+ }
+ else
+ dst_srid = src_srid;
+ POSTGIS_RT_DEBUGF(4, "destination SRID: %d", dst_srid);
+
+ /* target SRID != src SRID, error */
+ if (src_srid == SRID_UNKNOWN && dst_srid != src_srid) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_GDALWarp: Input raster has unknown (%d) SRID", src_srid);
+ PG_RETURN_NULL();
+ }
+ /* target SRID == src SRID, no reprojection */
+ else if (dst_srid == src_srid) {
+ no_srid = 1;
+ }
+
+ /* scale x */
+ if (!PG_ARGISNULL(4)) {
+ scale[0] = PG_GETARG_FLOAT8(4);
+ if (FLT_NEQ(scale[0], 0)) scale_x = &scale[0];
+ }
+
+ /* scale y */
+ if (!PG_ARGISNULL(5)) {
+ scale[1] = PG_GETARG_FLOAT8(5);
+ if (FLT_NEQ(scale[1], 0)) scale_y = &scale[1];
+ }
+
+ /* grid alignment x */
+ if (!PG_ARGISNULL(6)) {
+ gridw[0] = PG_GETARG_FLOAT8(6);
+ grid_xw = &gridw[0];
+ }
+
+ /* grid alignment y */
+ if (!PG_ARGISNULL(7)) {
+ gridw[1] = PG_GETARG_FLOAT8(7);
+ grid_yw = &gridw[1];
+ }
+
+ /* skew x */
+ if (!PG_ARGISNULL(8)) {
+ skew[0] = PG_GETARG_FLOAT8(8);
+ if (FLT_NEQ(skew[0], 0)) skew_x = &skew[0];
+ }
+
+ /* skew y */
+ if (!PG_ARGISNULL(9)) {
+ skew[1] = PG_GETARG_FLOAT8(9);
+ if (FLT_NEQ(skew[1], 0)) skew_y = &skew[1];
+ }
+
+ /* width */
+ if (!PG_ARGISNULL(10)) {
+ dim[0] = PG_GETARG_INT32(10);
+ if (dim[0] < 0) dim[0] = 0;
+ if (dim[0] > 0) dim_x = &dim[0];
+ }
+
+ /* height */
+ if (!PG_ARGISNULL(11)) {
+ dim[1] = PG_GETARG_INT32(11);
+ if (dim[1] < 0) dim[1] = 0;
+ if (dim[1] > 0) dim_y = &dim[1];
+ }
+
+ /* check that at least something is to be done */
+ if (
+ (dst_srid == SRID_UNKNOWN) &&
+ (scale_x == NULL) &&
+ (scale_y == NULL) &&
+ (grid_xw == NULL) &&
+ (grid_yw == NULL) &&
+ (skew_x == NULL) &&
+ (skew_y == NULL) &&
+ (dim_x == NULL) &&
+ (dim_y == NULL)
+ ) {
+ elog(NOTICE, "No resampling parameters provided. Returning original raster");
+ rt_raster_destroy(raster);
+ PG_RETURN_POINTER(pgraster);
+ }
+ /* both values of alignment must be provided if any one is provided */
+ else if (
+ (grid_xw != NULL && grid_yw == NULL) ||
+ (grid_xw == NULL && grid_yw != NULL)
+ ) {
+ elog(NOTICE, "Values must be provided for both X and Y when specifying the alignment. Returning original raster");
+ rt_raster_destroy(raster);
+ PG_RETURN_POINTER(pgraster);
+ }
+ /* both values of scale must be provided if any one is provided */
+ else if (
+ (scale_x != NULL && scale_y == NULL) ||
+ (scale_x == NULL && scale_y != NULL)
+ ) {
+ elog(NOTICE, "Values must be provided for both X and Y when specifying the scale. Returning original raster");
+ rt_raster_destroy(raster);
+ PG_RETURN_POINTER(pgraster);
+ }
+ /* scale and width/height provided */
+ else if (
+ (scale_x != NULL || scale_y != NULL) &&
+ (dim_x != NULL || dim_y != NULL)
+ ) {
+ elog(NOTICE, "Scale X/Y and width/height are mutually exclusive. Only provide one. Returning original raster");
+ rt_raster_destroy(raster);
+ PG_RETURN_POINTER(pgraster);
+ }
+
+ /* get srses from srids */
+ if (!no_srid) {
+ /* source srs */
+ src_srs = rtpg_getSR(src_srid);
+ if (NULL == src_srs) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_GDALWarp: Input raster has unknown SRID (%d)", src_srid);
+ PG_RETURN_NULL();
+ }
+ POSTGIS_RT_DEBUGF(4, "src srs: %s", src_srs);
+
+ dst_srs = rtpg_getSR(dst_srid);
+ if (NULL == dst_srs) {
+ if (!no_srid) pfree(src_srs);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_GDALWarp: Target SRID (%d) is unknown", dst_srid);
+ PG_RETURN_NULL();
+ }
+ POSTGIS_RT_DEBUGF(4, "dst srs: %s", dst_srs);
+ }
+
+ rast = rt_raster_gdal_warp(
+ raster,
+ src_srs, dst_srs,
+ scale_x, scale_y,
+ dim_x, dim_y,
+ NULL, NULL,
+ grid_xw, grid_yw,
+ skew_x, skew_y,
+ alg, max_err);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ if (!no_srid) {
+ pfree(src_srs);
+ pfree(dst_srs);
+ }
+ if (!rast) {
+ elog(ERROR, "RASTER_band: Could not create transformed raster");
+ PG_RETURN_NULL();
+ }
+
+ /* add target SRID */
+ rt_raster_set_srid(rast, dst_srid);
+
+ pgrast = rt_raster_serialize(rast);
+ rt_raster_destroy(rast);
+
+ if (NULL == pgrast) PG_RETURN_NULL();
+
+ POSTGIS_RT_DEBUG(3, "RASTER_GDALWarp: done");
+
+ SET_VARSIZE(pgrast, pgrast->size);
+ PG_RETURN_POINTER(pgrast);
+}
+
--- /dev/null
+/*
+ * $Id$
+ *
+ * WKTRaster - Raster Types for PostGIS
+ * http://www.postgis.org/support/wiki/index.php?WKTRasterHomePage
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <postgres.h>
+#include <fmgr.h>
+#include <funcapi.h>
+#include <utils/lsyscache.h> /* for get_typlenbyvalalign */
+#include <utils/array.h> /* for ArrayType */
+#include <catalog/pg_type.h> /* for INT2OID, INT4OID, FLOAT4OID, FLOAT8OID and TEXTOID */
+#include <utils/builtins.h> /* for text_to_cstring() */
+
+#include "../../postgis_config.h"
+#include "lwgeom_pg.h"
+
+#include "rtpostgis.h"
+#include "rtpg_internal.h"
+
+Datum RASTER_convex_hull(PG_FUNCTION_ARGS);
+Datum RASTER_dumpAsPolygons(PG_FUNCTION_ARGS);
+
+/* Get pixel geographical shape */
+Datum RASTER_getPixelPolygons(PG_FUNCTION_ARGS);
+
+/* Get raster band's polygon */
+Datum RASTER_getPolygon(PG_FUNCTION_ARGS);
+
+/* rasterize a geometry */
+Datum RASTER_asRaster(PG_FUNCTION_ARGS);
+
+/**
+ * Return the convex hull of this raster
+ */
+PG_FUNCTION_INFO_V1(RASTER_convex_hull);
+Datum RASTER_convex_hull(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster;
+ rt_raster raster;
+ LWGEOM *geom = NULL;
+ GSERIALIZED* gser = NULL;
+ size_t gser_size;
+ int err = ES_NONE;
+
+ bool minhull = FALSE;
+
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+
+ /* # of args */
+ if (PG_NARGS() > 1)
+ minhull = TRUE;
+
+ if (!minhull) {
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
+ raster = rt_raster_deserialize(pgraster, TRUE);
+ }
+ else {
+ 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_convex_hull: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ if (!minhull)
+ err = rt_raster_get_convex_hull(raster, &geom);
+ else {
+ int nband = -1;
+
+ /* get arg 1 */
+ if (!PG_ARGISNULL(1)) {
+ nband = PG_GETARG_INT32(1);
+ if (!rt_raster_has_band(raster, nband - 1)) {
+ elog(NOTICE, "Invalid band index (must use 1-based). Returning NULL");
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ PG_RETURN_NULL();
+ }
+ nband = nband - 1;
+ }
+
+ err = rt_raster_get_perimeter(raster, nband, &geom);
+ }
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ if (err != ES_NONE) {
+ elog(ERROR, "RASTER_convex_hull: Could not get raster's convex hull");
+ PG_RETURN_NULL();
+ }
+ else if (geom == NULL) {
+ elog(NOTICE, "Raster's convex hull is NULL");
+ PG_RETURN_NULL();
+ }
+
+ gser = gserialized_from_lwgeom(geom, 0, &gser_size);
+ lwgeom_free(geom);
+
+ SET_VARSIZE(gser, gser_size);
+ PG_RETURN_POINTER(gser);
+}
+
+PG_FUNCTION_INFO_V1(RASTER_dumpAsPolygons);
+Datum RASTER_dumpAsPolygons(PG_FUNCTION_ARGS) {
+ FuncCallContext *funcctx;
+ TupleDesc tupdesc;
+ rt_geomval geomval;
+ rt_geomval geomval2;
+ int call_cntr;
+ int max_calls;
+
+ /* stuff done only on the first call of the function */
+ if (SRF_IS_FIRSTCALL()) {
+ MemoryContext oldcontext;
+ int numbands;
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ int nband;
+ bool exclude_nodata_value = TRUE;
+ int nElements;
+
+ POSTGIS_RT_DEBUG(2, "RASTER_dumpAsPolygons first call");
+
+ /* 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);
+ }
+
+ if (!PG_ARGISNULL(1))
+ nband = PG_GETARG_UINT32(1);
+ else
+ nband = 1; /* By default, first band */
+
+ POSTGIS_RT_DEBUGF(3, "band %d", nband);
+ numbands = rt_raster_get_num_bands(raster);
+
+ if (nband < 1 || nband > numbands) {
+ elog(NOTICE, "Invalid band index (must use 1-based). Returning NULL");
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ if (!PG_ARGISNULL(2))
+ exclude_nodata_value = PG_GETARG_BOOL(2);
+
+ /* see if band is NODATA */
+ if (rt_band_get_isnodata_flag(rt_raster_get_band(raster, nband - 1))) {
+ POSTGIS_RT_DEBUGF(3, "Band at index %d is NODATA. Returning NULL", nband);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* Polygonize raster */
+
+ /**
+ * Dump raster
+ */
+ geomval = rt_raster_gdal_polygonize(raster, nband - 1, exclude_nodata_value, &nElements);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ if (NULL == geomval) {
+ ereport(ERROR, (
+ errcode(ERRCODE_NO_DATA_FOUND),
+ errmsg("Could not polygonize raster")
+ ));
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ POSTGIS_RT_DEBUGF(3, "raster dump, %d elements returned", nElements);
+
+ /* Store needed information */
+ funcctx->user_fctx = geomval;
+
+ /* total number of tuples to be returned */
+ funcctx->max_calls = nElements;
+
+ /* 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;
+ geomval2 = funcctx->user_fctx;
+
+ /* do when there is more left to send */
+ if (call_cntr < max_calls) {
+ int values_length = 2;
+ Datum values[values_length];
+ bool nulls[values_length];
+ HeapTuple tuple;
+ Datum result;
+
+ GSERIALIZED *gser = NULL;
+ size_t gser_size = 0;
+
+ POSTGIS_RT_DEBUGF(3, "call number %d", call_cntr);
+
+ memset(nulls, FALSE, sizeof(bool) * values_length);
+
+ /* convert LWGEOM to GSERIALIZED */
+ gser = gserialized_from_lwgeom(lwpoly_as_lwgeom(geomval2[call_cntr].geom), 0, &gser_size);
+ lwgeom_free(lwpoly_as_lwgeom(geomval2[call_cntr].geom));
+
+ values[0] = PointerGetDatum(gser);
+ values[1] = Float8GetDatum(geomval2[call_cntr].val);
+
+ /* build a tuple */
+ tuple = heap_form_tuple(tupdesc, values, nulls);
+
+ /* make the tuple into a datum */
+ result = HeapTupleGetDatum(tuple);
+
+ SRF_RETURN_NEXT(funcctx, result);
+ }
+ /* do when there is no more left */
+ else {
+ pfree(geomval2);
+ SRF_RETURN_DONE(funcctx);
+ }
+}
+
+/**
+ * Return the geographical shape of all pixels
+ */
+PG_FUNCTION_INFO_V1(RASTER_getPixelPolygons);
+Datum RASTER_getPixelPolygons(PG_FUNCTION_ARGS)
+{
+ FuncCallContext *funcctx;
+ TupleDesc tupdesc;
+ rt_pixel pix = NULL;
+ rt_pixel pix2;
+ int call_cntr;
+ int max_calls;
+ int i = 0;
+
+ /* stuff done only on the first call of the function */
+ if (SRF_IS_FIRSTCALL()) {
+ MemoryContext oldcontext;
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ rt_band band = NULL;
+ int nband = 1;
+ int numbands;
+ bool noband = FALSE;
+ bool exclude_nodata_value = TRUE;
+ bool nocolumnx = FALSE;
+ bool norowy = FALSE;
+ int x = 0;
+ int y = 0;
+ int bounds[4] = {0};
+ int pixcount = 0;
+ int isnodata = 0;
+
+ LWPOLY *poly;
+
+ /* create a function context for cross-call persistence */
+ funcctx = SRF_FIRSTCALL_INIT();
+
+ /* switch to memory context appropriate for multiple function calls */
+ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+ if (PG_ARGISNULL(0)) {
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+ /* band */
+ if (PG_ARGISNULL(1))
+ noband = TRUE;
+ else {
+ nband = PG_GETARG_INT32(1);
+ noband = FALSE;
+ }
+
+ /* column */
+ if (PG_ARGISNULL(2))
+ nocolumnx = TRUE;
+ else {
+ bounds[0] = PG_GETARG_INT32(2);
+ bounds[1] = bounds[0];
+ }
+
+ /* row */
+ if (PG_ARGISNULL(3))
+ norowy = TRUE;
+ else {
+ bounds[2] = PG_GETARG_INT32(3);
+ bounds[3] = bounds[2];
+ }
+
+ /* exclude NODATA */
+ if (!PG_ARGISNULL(4))
+ exclude_nodata_value = PG_GETARG_BOOL(4);
+
+ raster = rt_raster_deserialize(pgraster, FALSE);
+ if (!raster) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ ereport(ERROR, (
+ errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("Could not deserialize raster")
+ ));
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* raster empty, return NULL */
+ if (rt_raster_is_empty(raster)) {
+ elog(NOTICE, "Raster is empty. Returning NULL");
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* band specified, load band and info */
+ if (!noband) {
+ do {
+ numbands = rt_raster_get_num_bands(raster);
+ POSTGIS_RT_DEBUGF(3, "band %d", nband);
+ POSTGIS_RT_DEBUGF(3, "# of bands %d", numbands);
+
+ if (nband < 1 || nband > numbands) {
+ elog(NOTICE, "Invalid band index (must use 1-based). Returning pixel values will be NULL");
+ noband = TRUE;
+ break;
+ }
+
+ band = rt_raster_get_band(raster, nband - 1);
+ if (!band) {
+ elog(NOTICE, "Could not find band at index %d. Returning pixel values will be NULL", nband);
+ noband = TRUE;
+ break;
+ }
+
+ if (!rt_band_get_hasnodata_flag(band))
+ exclude_nodata_value = FALSE;
+ }
+ while (0);
+ }
+
+ /* set bounds if columnx, rowy not set */
+ if (nocolumnx) {
+ bounds[0] = 1;
+ bounds[1] = rt_raster_get_width(raster);
+ }
+ if (norowy) {
+ bounds[2] = 1;
+ bounds[3] = rt_raster_get_height(raster);
+ }
+ POSTGIS_RT_DEBUGF(3, "bounds (min x, max x, min y, max y) = (%d, %d, %d, %d)",
+ bounds[0], bounds[1], bounds[2], bounds[3]);
+
+ /* rowy */
+ pixcount = 0;
+ for (y = bounds[2]; y <= bounds[3]; y++) {
+ /* columnx */
+ for (x = bounds[0]; x <= bounds[1]; x++) {
+ /* geometry */
+ poly = rt_raster_pixel_as_polygon(raster, x - 1, y - 1);
+ if (!poly) {
+ for (i = 0; i < pixcount; i++)
+ lwgeom_free(pix[i].geom);
+ if (pixcount) pfree(pix);
+
+ if (!noband) rt_band_destroy(band);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_getPixelPolygons: Could not get pixel polygon");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ if (!pixcount)
+ pix = palloc(sizeof(struct rt_pixel_t) * (pixcount + 1));
+ else
+ pix = repalloc(pix, sizeof(struct rt_pixel_t) * (pixcount + 1));
+ if (pix == NULL) {
+
+ lwpoly_free(poly);
+ if (!noband) rt_band_destroy(band);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_getPixelPolygons: Could not allocate memory for storing pixel polygons");
+ SRF_RETURN_DONE(funcctx);
+ }
+ pix[pixcount].geom = (LWGEOM *) poly;
+ /*
+ POSTGIS_RT_DEBUGF(4, "poly @ %p", poly);
+ POSTGIS_RT_DEBUGF(4, "geom @ %p", pix[pixcount].geom);
+ */
+
+ /* x, y */
+ pix[pixcount].x = x;
+ pix[pixcount].y = y;
+
+ /* value, NODATA flag */
+ if (!noband) {
+ if (rt_band_get_pixel(band, x - 1, y - 1, &(pix[pixcount].value), &isnodata) != ES_NONE) {
+
+ for (i = 0; i < pixcount; i++)
+ lwgeom_free(pix[i].geom);
+ if (pixcount) pfree(pix);
+
+ if (!noband) rt_band_destroy(band);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_getPixelPolygons: Could not get pixel value");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ if (!exclude_nodata_value || !isnodata) {
+ pix[pixcount].nodata = 0;
+ }
+ else {
+ pix[pixcount].nodata = 1;
+ }
+ }
+ else {
+ pix[pixcount].nodata = 1;
+ }
+
+ pixcount++;
+ }
+ }
+
+ if (!noband) rt_band_destroy(band);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ /* Store needed information */
+ funcctx->user_fctx = pix;
+
+ /* total number of tuples to be returned */
+ funcctx->max_calls = pixcount;
+
+ /* Build a tuple descriptor for our result type */
+ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) {
+ ereport(ERROR, (
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("function returning record called in context that cannot accept type record")
+ ));
+ }
+
+ BlessTupleDesc(tupdesc);
+ funcctx->tuple_desc = tupdesc;
+
+ MemoryContextSwitchTo(oldcontext);
+ }
+
+ /* stuff done on every call of the function */
+ funcctx = SRF_PERCALL_SETUP();
+
+ call_cntr = funcctx->call_cntr;
+ max_calls = funcctx->max_calls;
+ tupdesc = funcctx->tuple_desc;
+ pix2 = funcctx->user_fctx;
+
+ /* do when there is more left to send */
+ if (call_cntr < max_calls) {
+ int values_length = 4;
+ Datum values[values_length];
+ bool nulls[values_length];
+ HeapTuple tuple;
+ Datum result;
+
+ GSERIALIZED *gser = NULL;
+ size_t gser_size = 0;
+
+ POSTGIS_RT_DEBUGF(3, "call number %d", call_cntr);
+
+ memset(nulls, FALSE, sizeof(bool) * values_length);
+
+ /* convert LWGEOM to GSERIALIZED */
+ gser = gserialized_from_lwgeom(pix2[call_cntr].geom, 0, &gser_size);
+ lwgeom_free(pix2[call_cntr].geom);
+
+ values[0] = PointerGetDatum(gser);
+ if (pix2[call_cntr].nodata)
+ nulls[1] = TRUE;
+ else
+ values[1] = Float8GetDatum(pix2[call_cntr].value);
+ values[2] = Int32GetDatum(pix2[call_cntr].x);
+ values[3] = Int32GetDatum(pix2[call_cntr].y);
+
+ /* build a tuple */
+ tuple = heap_form_tuple(tupdesc, values, nulls);
+
+ /* make the tuple into a datum */
+ result = HeapTupleGetDatum(tuple);
+
+ SRF_RETURN_NEXT(funcctx, result);
+ }
+ /* do when there is no more left */
+ else {
+ pfree(pix2);
+ SRF_RETURN_DONE(funcctx);
+ }
+}
+
+/**
+ * Get raster band's polygon
+ */
+PG_FUNCTION_INFO_V1(RASTER_getPolygon);
+Datum RASTER_getPolygon(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ int num_bands = 0;
+ int nband = 1;
+ int err;
+ LWMPOLY *surface = NULL;
+ GSERIALIZED *rtn = NULL;
+
+ /* 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_getPolygon: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ /* 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);
+ PG_RETURN_NULL();
+ }
+
+ /* 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);
+ PG_RETURN_NULL();
+ }
+
+ /* get band surface */
+ err = rt_raster_surface(raster, nband - 1, &surface);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ if (err != ES_NONE) {
+ elog(ERROR, "RASTER_getPolygon: Could not get raster band's surface");
+ PG_RETURN_NULL();
+ }
+ else if (surface == NULL) {
+ elog(NOTICE, "Raster is empty or all pixels of band are NODATA. Returning NULL");
+ PG_RETURN_NULL();
+ }
+
+ rtn = geometry_serialize(lwmpoly_as_lwgeom(surface));
+ lwmpoly_free(surface);
+
+ PG_RETURN_POINTER(rtn);
+}
+
+/**
+ * Rasterize a geometry
+ */
+PG_FUNCTION_INFO_V1(RASTER_asRaster);
+Datum RASTER_asRaster(PG_FUNCTION_ARGS)
+{
+ GSERIALIZED *gser = NULL;
+
+ LWGEOM *geom = NULL;
+ rt_raster rast = NULL;
+ rt_pgraster *pgrast = NULL;
+
+ unsigned char *wkb;
+ size_t wkb_len = 0;
+ unsigned char variant = WKB_SFSQL;
+
+ double scale[2] = {0};
+ double *scale_x = NULL;
+ double *scale_y = NULL;
+
+ int dim[2] = {0};
+ int *dim_x = NULL;
+ int *dim_y = NULL;
+
+ ArrayType *array;
+ Oid etype;
+ Datum *e;
+ bool *nulls;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ int n = 0;
+ int i = 0;
+ int j = 0;
+ int haserr = 0;
+
+ text *pixeltypetext = NULL;
+ char *pixeltype = NULL;
+ rt_pixtype pixtype = PT_END;
+ rt_pixtype *pixtypes = NULL;
+ int pixtypes_len = 0;
+
+ double *values = NULL;
+ int values_len = 0;
+
+ uint8_t *hasnodatas = NULL;
+ double *nodatavals = NULL;
+ int nodatavals_len = 0;
+
+ double ulw[2] = {0};
+ double *ul_xw = NULL;
+ double *ul_yw = NULL;
+
+ double gridw[2] = {0};
+ double *grid_xw = NULL;
+ double *grid_yw = NULL;
+
+ double skew[2] = {0};
+ double *skew_x = NULL;
+ double *skew_y = NULL;
+
+ char **options = NULL;
+ int options_len = 0;
+
+ uint32_t num_bands = 0;
+
+ int srid = SRID_UNKNOWN;
+ char *srs = NULL;
+
+ POSTGIS_RT_DEBUG(3, "RASTER_asRaster: Starting");
+
+ /* based upon LWGEOM_asBinary function in postgis/lwgeom_ogc.c */
+
+ /* Get the geometry */
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+
+ gser = (GSERIALIZED *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+ geom = lwgeom_from_gserialized(gser);
+
+ /* Get a 2D version of the geometry if necessary */
+ if (lwgeom_ndims(geom) > 2) {
+ LWGEOM *geom2d = lwgeom_force_2d(geom);
+ lwgeom_free(geom);
+ geom = geom2d;
+ }
+
+ /* empty geometry, return empty raster */
+ if (lwgeom_is_empty(geom)) {
+ POSTGIS_RT_DEBUG(3, "Input geometry is empty. Returning empty raster");
+ lwgeom_free(geom);
+ PG_FREE_IF_COPY(gser, 0);
+
+ rast = rt_raster_new(0, 0);
+ if (rast == NULL)
+ PG_RETURN_NULL();
+
+ pgrast = rt_raster_serialize(rast);
+ rt_raster_destroy(rast);
+
+ if (NULL == pgrast)
+ PG_RETURN_NULL();
+
+ SET_VARSIZE(pgrast, pgrast->size);
+ PG_RETURN_POINTER(pgrast);
+ }
+
+ /* scale x */
+ if (!PG_ARGISNULL(1)) {
+ scale[0] = PG_GETARG_FLOAT8(1);
+ if (FLT_NEQ(scale[0], 0)) scale_x = &scale[0];
+ }
+
+ /* scale y */
+ if (!PG_ARGISNULL(2)) {
+ scale[1] = PG_GETARG_FLOAT8(2);
+ if (FLT_NEQ(scale[1], 0)) scale_y = &scale[1];
+ }
+ POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: scale (x, y) = %f, %f", scale[0], scale[1]);
+
+ /* width */
+ if (!PG_ARGISNULL(3)) {
+ dim[0] = PG_GETARG_INT32(3);
+ if (dim[0] < 0) dim[0] = 0;
+ if (dim[0] != 0) dim_x = &dim[0];
+ }
+
+ /* height */
+ if (!PG_ARGISNULL(4)) {
+ dim[1] = PG_GETARG_INT32(4);
+ if (dim[1] < 0) dim[1] = 0;
+ if (dim[1] != 0) dim_y = &dim[1];
+ }
+ POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: dim (x, y) = %d, %d", dim[0], dim[1]);
+
+ /* pixeltype */
+ if (!PG_ARGISNULL(5)) {
+ array = PG_GETARG_ARRAYTYPE_P(5);
+ etype = ARR_ELEMTYPE(array);
+ get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
+
+ switch (etype) {
+ case TEXTOID:
+ break;
+ default:
+
+ lwgeom_free(geom);
+ PG_FREE_IF_COPY(gser, 0);
+
+ elog(ERROR, "RASTER_asRaster: Invalid data type for pixeltype");
+ PG_RETURN_NULL();
+ break;
+ }
+
+ deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
+ &nulls, &n);
+
+ if (n) {
+ pixtypes = (rt_pixtype *) palloc(sizeof(rt_pixtype) * n);
+ /* clean each pixeltype */
+ for (i = 0, j = 0; i < n; i++) {
+ if (nulls[i]) {
+ pixtypes[j++] = PT_64BF;
+ continue;
+ }
+
+ pixeltype = NULL;
+ switch (etype) {
+ case TEXTOID:
+ pixeltypetext = (text *) DatumGetPointer(e[i]);
+ if (NULL == pixeltypetext) break;
+ pixeltype = text_to_cstring(pixeltypetext);
+
+ /* trim string */
+ pixeltype = rtpg_trim(pixeltype);
+ POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: pixeltype is '%s'", pixeltype);
+ break;
+ }
+
+ if (strlen(pixeltype)) {
+ pixtype = rt_pixtype_index_from_name(pixeltype);
+ if (pixtype == PT_END) {
+
+ pfree(pixtypes);
+
+ lwgeom_free(geom);
+ PG_FREE_IF_COPY(gser, 0);
+
+ elog(ERROR, "RASTER_asRaster: Invalid pixel type provided: %s", pixeltype);
+ PG_RETURN_NULL();
+ }
+
+ pixtypes[j] = pixtype;
+ j++;
+ }
+ }
+
+ if (j > 0) {
+ /* trim allocation */
+ pixtypes = repalloc(pixtypes, j * sizeof(rt_pixtype));
+ pixtypes_len = j;
+ }
+ else {
+ pfree(pixtypes);
+ pixtypes = NULL;
+ pixtypes_len = 0;
+ }
+ }
+ }
+#if POSTGIS_DEBUG_LEVEL > 0
+ for (i = 0; i < pixtypes_len; i++)
+ POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: pixtypes[%d] = %d", i, (int) pixtypes[i]);
+#endif
+
+ /* value */
+ if (!PG_ARGISNULL(6)) {
+ array = PG_GETARG_ARRAYTYPE_P(6);
+ etype = ARR_ELEMTYPE(array);
+ get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
+
+ switch (etype) {
+ case FLOAT4OID:
+ case FLOAT8OID:
+ break;
+ default:
+
+ if (pixtypes_len) pfree(pixtypes);
+
+ lwgeom_free(geom);
+ PG_FREE_IF_COPY(gser, 0);
+
+ elog(ERROR, "RASTER_asRaster: Invalid data type for value");
+ PG_RETURN_NULL();
+ break;
+ }
+
+ deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
+ &nulls, &n);
+
+ if (n) {
+ values = (double *) palloc(sizeof(double) * n);
+ for (i = 0, j = 0; i < n; i++) {
+ if (nulls[i]) {
+ values[j++] = 1;
+ continue;
+ }
+
+ switch (etype) {
+ case FLOAT4OID:
+ values[j] = (double) DatumGetFloat4(e[i]);
+ break;
+ case FLOAT8OID:
+ values[j] = (double) DatumGetFloat8(e[i]);
+ break;
+ }
+ POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: values[%d] = %f", j, values[j]);
+
+ j++;
+ }
+
+ if (j > 0) {
+ /* trim allocation */
+ values = repalloc(values, j * sizeof(double));
+ values_len = j;
+ }
+ else {
+ pfree(values);
+ values = NULL;
+ values_len = 0;
+ }
+ }
+ }
+#if POSTGIS_DEBUG_LEVEL > 0
+ for (i = 0; i < values_len; i++)
+ POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: values[%d] = %f", i, values[i]);
+#endif
+
+ /* nodataval */
+ if (!PG_ARGISNULL(7)) {
+ array = PG_GETARG_ARRAYTYPE_P(7);
+ etype = ARR_ELEMTYPE(array);
+ get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
+
+ switch (etype) {
+ case FLOAT4OID:
+ case FLOAT8OID:
+ break;
+ default:
+
+ if (pixtypes_len) pfree(pixtypes);
+ if (values_len) pfree(values);
+
+ lwgeom_free(geom);
+ PG_FREE_IF_COPY(gser, 0);
+
+ elog(ERROR, "RASTER_asRaster: Invalid data type for nodataval");
+ PG_RETURN_NULL();
+ break;
+ }
+
+ deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
+ &nulls, &n);
+
+ if (n) {
+ nodatavals = (double *) palloc(sizeof(double) * n);
+ hasnodatas = (uint8_t *) palloc(sizeof(uint8_t) * n);
+ for (i = 0, j = 0; i < n; i++) {
+ if (nulls[i]) {
+ hasnodatas[j] = 0;
+ nodatavals[j] = 0;
+ j++;
+ continue;
+ }
+
+ hasnodatas[j] = 1;
+ switch (etype) {
+ case FLOAT4OID:
+ nodatavals[j] = (double) DatumGetFloat4(e[i]);
+ break;
+ case FLOAT8OID:
+ nodatavals[j] = (double) DatumGetFloat8(e[i]);
+ break;
+ }
+ POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: hasnodatas[%d] = %d", j, hasnodatas[j]);
+ POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: nodatavals[%d] = %f", j, nodatavals[j]);
+
+ j++;
+ }
+
+ if (j > 0) {
+ /* trim allocation */
+ nodatavals = repalloc(nodatavals, j * sizeof(double));
+ hasnodatas = repalloc(hasnodatas, j * sizeof(uint8_t));
+ nodatavals_len = j;
+ }
+ else {
+ pfree(nodatavals);
+ pfree(hasnodatas);
+ nodatavals = NULL;
+ hasnodatas = NULL;
+ nodatavals_len = 0;
+ }
+ }
+ }
+#if POSTGIS_DEBUG_LEVEL > 0
+ for (i = 0; i < nodatavals_len; i++) {
+ POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: hasnodatas[%d] = %d", i, hasnodatas[i]);
+ POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: nodatavals[%d] = %f", i, nodatavals[i]);
+ }
+#endif
+
+ /* upperleftx */
+ if (!PG_ARGISNULL(8)) {
+ ulw[0] = PG_GETARG_FLOAT8(8);
+ ul_xw = &ulw[0];
+ }
+
+ /* upperlefty */
+ if (!PG_ARGISNULL(9)) {
+ ulw[1] = PG_GETARG_FLOAT8(9);
+ ul_yw = &ulw[1];
+ }
+ POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: upperleft (x, y) = %f, %f", ulw[0], ulw[1]);
+
+ /* gridx */
+ if (!PG_ARGISNULL(10)) {
+ gridw[0] = PG_GETARG_FLOAT8(10);
+ grid_xw = &gridw[0];
+ }
+
+ /* gridy */
+ if (!PG_ARGISNULL(11)) {
+ gridw[1] = PG_GETARG_FLOAT8(11);
+ grid_yw = &gridw[1];
+ }
+ POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: grid (x, y) = %f, %f", gridw[0], gridw[1]);
+
+ /* check dependent variables */
+ haserr = 0;
+ do {
+ /* only part of scale provided */
+ if (
+ (scale_x == NULL && scale_y != NULL) ||
+ (scale_x != NULL && scale_y == NULL)
+ ) {
+ elog(NOTICE, "Values must be provided for both X and Y of scale if one is specified");
+ haserr = 1;
+ break;
+ }
+
+ /* only part of dimension provided */
+ if (
+ (dim_x == NULL && dim_y != NULL) ||
+ (dim_x != NULL && dim_y == NULL)
+ ) {
+ elog(NOTICE, "Values must be provided for both width and height if one is specified");
+ haserr = 1;
+ break;
+ }
+
+ /* scale and dimension provided */
+ if (
+ (scale_x != NULL && scale_y != NULL) &&
+ (dim_x != NULL && dim_y != NULL)
+ ) {
+ elog(NOTICE, "Values provided for X and Y of scale and width and height. Using the width and height");
+ scale_x = NULL;
+ scale_y = NULL;
+ break;
+ }
+
+ /* neither scale or dimension provided */
+ if (
+ (scale_x == NULL && scale_y == NULL) &&
+ (dim_x == NULL && dim_y == NULL)
+ ) {
+ elog(NOTICE, "Values must be provided for X and Y of scale or width and height");
+ haserr = 1;
+ break;
+ }
+
+ /* only part of upper-left provided */
+ if (
+ (ul_xw == NULL && ul_yw != NULL) ||
+ (ul_xw != NULL && ul_yw == NULL)
+ ) {
+ elog(NOTICE, "Values must be provided for both X and Y when specifying the upper-left corner");
+ haserr = 1;
+ break;
+ }
+
+ /* only part of alignment provided */
+ if (
+ (grid_xw == NULL && grid_yw != NULL) ||
+ (grid_xw != NULL && grid_yw == NULL)
+ ) {
+ elog(NOTICE, "Values must be provided for both X and Y when specifying the alignment");
+ haserr = 1;
+ break;
+ }
+
+ /* upper-left and alignment provided */
+ if (
+ (ul_xw != NULL && ul_yw != NULL) &&
+ (grid_xw != NULL && grid_yw != NULL)
+ ) {
+ elog(NOTICE, "Values provided for both X and Y of upper-left corner and alignment. Using the values of upper-left corner");
+ grid_xw = NULL;
+ grid_yw = NULL;
+ break;
+ }
+ }
+ while (0);
+
+ if (haserr) {
+ if (pixtypes_len) pfree(pixtypes);
+ if (values_len) pfree(values);
+ if (nodatavals_len) {
+ pfree(nodatavals);
+ pfree(hasnodatas);
+ }
+
+ lwgeom_free(geom);
+ PG_FREE_IF_COPY(gser, 0);
+
+ PG_RETURN_NULL();
+ }
+
+ /* skewx */
+ if (!PG_ARGISNULL(12)) {
+ skew[0] = PG_GETARG_FLOAT8(12);
+ if (FLT_NEQ(skew[0], 0)) skew_x = &skew[0];
+ }
+
+ /* skewy */
+ if (!PG_ARGISNULL(13)) {
+ skew[1] = PG_GETARG_FLOAT8(13);
+ if (FLT_NEQ(skew[1], 0)) skew_y = &skew[1];
+ }
+ POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: skew (x, y) = %f, %f", skew[0], skew[1]);
+
+ /* all touched */
+ if (!PG_ARGISNULL(14) && PG_GETARG_BOOL(14) == TRUE) {
+ if (options_len < 1) {
+ options_len = 1;
+ options = (char **) palloc(sizeof(char *) * options_len);
+ }
+ else {
+ options_len++;
+ options = (char **) repalloc(options, sizeof(char *) * options_len);
+ }
+
+ options[options_len - 1] = palloc(sizeof(char*) * (strlen("ALL_TOUCHED=TRUE") + 1));
+ options[options_len - 1] = "ALL_TOUCHED=TRUE";
+ }
+
+ if (options_len) {
+ options_len++;
+ options = (char **) repalloc(options, sizeof(char *) * options_len);
+ options[options_len - 1] = NULL;
+ }
+
+ /* get geometry's srid */
+ srid = gserialized_get_srid(gser);
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: srid = %d", srid);
+ if (clamp_srid(srid) != SRID_UNKNOWN) {
+ srs = rtpg_getSR(srid);
+ if (NULL == srs) {
+
+ if (pixtypes_len) pfree(pixtypes);
+ if (values_len) pfree(values);
+ if (nodatavals_len) {
+ pfree(hasnodatas);
+ pfree(nodatavals);
+ }
+ if (options_len) pfree(options);
+
+ lwgeom_free(geom);
+ PG_FREE_IF_COPY(gser, 0);
+
+ elog(ERROR, "RASTER_asRaster: Could not find srtext for SRID (%d)", srid);
+ PG_RETURN_NULL();
+ }
+ POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: srs is %s", srs);
+ }
+ else
+ srs = NULL;
+
+ /* determine number of bands */
+ /* MIN macro is from GDAL's cpl_port.h */
+ num_bands = MIN(pixtypes_len, values_len);
+ num_bands = MIN(num_bands, nodatavals_len);
+ POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: pixtypes_len = %d", pixtypes_len);
+ POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: values_len = %d", values_len);
+ POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: nodatavals_len = %d", nodatavals_len);
+ POSTGIS_RT_DEBUGF(3, "RASTER_asRaster: num_bands = %d", num_bands);
+
+ /* warn of imbalanced number of band elements */
+ if (!(
+ (pixtypes_len == values_len) &&
+ (values_len == nodatavals_len)
+ )) {
+ elog(
+ NOTICE,
+ "Imbalanced number of values provided for pixeltype (%d), value (%d) and nodataval (%d). Using the first %d values of each parameter",
+ pixtypes_len,
+ values_len,
+ nodatavals_len,
+ num_bands
+ );
+ }
+
+ /* get wkb of geometry */
+ POSTGIS_RT_DEBUG(3, "RASTER_asRaster: getting wkb of geometry");
+ wkb = lwgeom_to_wkb(geom, variant, &wkb_len);
+ lwgeom_free(geom);
+ PG_FREE_IF_COPY(gser, 0);
+
+ /* rasterize geometry */
+ POSTGIS_RT_DEBUG(3, "RASTER_asRaster: rasterizing geometry");
+ /* use nodatavals for the init parameter */
+ rast = rt_raster_gdal_rasterize(wkb,
+ (uint32_t) wkb_len, srs,
+ num_bands, pixtypes,
+ nodatavals, values,
+ nodatavals, hasnodatas,
+ dim_x, dim_y,
+ scale_x, scale_y,
+ ul_xw, ul_yw,
+ grid_xw, grid_yw,
+ skew_x, skew_y,
+ options
+ );
+
+ if (pixtypes_len) pfree(pixtypes);
+ if (values_len) pfree(values);
+ if (nodatavals_len) {
+ pfree(hasnodatas);
+ pfree(nodatavals);
+ }
+ if (options_len) pfree(options);
+
+ if (!rast) {
+ elog(ERROR, "RASTER_asRaster: Could not rasterize geometry");
+ PG_RETURN_NULL();
+ }
+
+ /* add target srid */
+ rt_raster_set_srid(rast, srid);
+
+ pgrast = rt_raster_serialize(rast);
+ rt_raster_destroy(rast);
+
+ if (NULL == pgrast) PG_RETURN_NULL();
+
+ POSTGIS_RT_DEBUG(3, "RASTER_asRaster: done");
+
+ SET_VARSIZE(pgrast, pgrast->size);
+ PG_RETURN_POINTER(pgrast);
+}
+
--- /dev/null
+/*
+ * $Id$
+ *
+ * WKTRaster - Raster Types for PostGIS
+ * http://www.postgis.org/support/wiki/index.php?WKTRasterHomePage
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <postgres.h>
+#include <fmgr.h>
+
+#include "rtpostgis.h"
+
+Datum RASTER_in(PG_FUNCTION_ARGS);
+Datum RASTER_out(PG_FUNCTION_ARGS);
+
+Datum RASTER_to_bytea(PG_FUNCTION_ARGS);
+Datum RASTER_to_binary(PG_FUNCTION_ARGS);
+
+/**
+ * Input is a string with hex chars in it.
+ * Convert to binary and put in the result
+ */
+PG_FUNCTION_INFO_V1(RASTER_in);
+Datum RASTER_in(PG_FUNCTION_ARGS)
+{
+ rt_raster raster;
+ char *hexwkb = PG_GETARG_CSTRING(0);
+ void *result = NULL;
+
+ POSTGIS_RT_DEBUG(3, "Starting");
+
+ raster = rt_raster_from_hexwkb(hexwkb, strlen(hexwkb));
+ if (raster == NULL)
+ PG_RETURN_NULL();
+
+ result = rt_raster_serialize(raster);
+ rt_raster_destroy(raster);
+ if (result == NULL)
+ PG_RETURN_NULL();
+
+ SET_VARSIZE(result, ((rt_pgraster*)result)->size);
+ PG_RETURN_POINTER(result);
+}
+
+/**
+ * Given a RASTER structure, convert it to Hex and put it in a string
+ */
+PG_FUNCTION_INFO_V1(RASTER_out);
+Datum RASTER_out(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ uint32_t hexwkbsize = 0;
+ char *hexwkb = NULL;
+
+ POSTGIS_RT_DEBUG(3, "Starting");
+
+ 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_out: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ hexwkb = rt_raster_to_hexwkb(raster, FALSE, &hexwkbsize);
+ if (!hexwkb) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_out: Could not HEX-WKBize raster");
+ PG_RETURN_NULL();
+ }
+
+ /* Free the raster objects used */
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ PG_RETURN_CSTRING(hexwkb);
+}
+
+/**
+ * Return bytea object with raster in Well-Known-Binary form.
+ */
+PG_FUNCTION_INFO_V1(RASTER_to_bytea);
+Datum RASTER_to_bytea(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ uint8_t *wkb = NULL;
+ uint32_t wkb_size = 0;
+ bytea *result = NULL;
+ int result_size = 0;
+
+ if (PG_ARGISNULL(0)) PG_RETURN_NULL();
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+ /* Get raster object */
+ raster = rt_raster_deserialize(pgraster, FALSE);
+ if (!raster) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_to_bytea: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ /* Parse raster to wkb object */
+ wkb = rt_raster_to_wkb(raster, FALSE, &wkb_size);
+ if (!wkb) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_to_bytea: Could not allocate and generate WKB data");
+ PG_RETURN_NULL();
+ }
+
+ /* Create varlena object */
+ result_size = wkb_size + VARHDRSZ;
+ result = (bytea *)palloc(result_size);
+ SET_VARSIZE(result, result_size);
+ memcpy(VARDATA(result), wkb, VARSIZE(result) - VARHDRSZ);
+
+ /* Free raster objects used */
+ rt_raster_destroy(raster);
+ pfree(wkb);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ PG_RETURN_POINTER(result);
+}
+
+/**
+ * Return bytea object with raster in Well-Known-Binary form requested using ST_AsBinary function.
+ */
+PG_FUNCTION_INFO_V1(RASTER_to_binary);
+Datum RASTER_to_binary(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ uint8_t *wkb = NULL;
+ uint32_t wkb_size = 0;
+ char *result = NULL;
+ int result_size = 0;
+ int outasin = FALSE;
+
+ if (PG_ARGISNULL(0)) PG_RETURN_NULL();
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+ /* Get raster object */
+ raster = rt_raster_deserialize(pgraster, FALSE);
+ if (!raster) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_to_binary: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ if (!PG_ARGISNULL(1))
+ outasin = PG_GETARG_BOOL(1);
+
+ /* Parse raster to wkb object */
+ wkb = rt_raster_to_wkb(raster, outasin, &wkb_size);
+ if (!wkb) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_to_binary: Could not allocate and generate WKB data");
+ PG_RETURN_NULL();
+ }
+
+ /* Create varlena object */
+ result_size = wkb_size + VARHDRSZ;
+ result = (char *)palloc(result_size);
+ SET_VARSIZE(result, result_size);
+ memcpy(VARDATA(result), wkb, VARSIZE(result) - VARHDRSZ);
+
+ /* Free raster objects used */
+ rt_raster_destroy(raster);
+ pfree(wkb);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ PG_RETURN_POINTER(result);
+}
+
--- /dev/null
+/*
+ * $Id$
+ *
+ * WKTRaster - Raster Types for PostGIS
+ * http://www.postgis.org/support/wiki/index.php?WKTRasterHomePage
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <ctype.h> /* for isspace */
+#include <postgres.h> /* for palloc */
+#include <executor/spi.h>
+
+#include "rtpg_internal.h"
+
+/* string replacement function taken from
+ * http://ubuntuforums.org/showthread.php?s=aa6f015109fd7e4c7e30d2fd8b717497&t=141670&page=3
+ */
+/* ---------------------------------------------------------------------------
+ Name : replace - Search & replace a substring by another one.
+ Creation : Thierry Husson, Sept 2010
+ Parameters :
+ str : Big string where we search
+ oldstr : Substring we are looking for
+ newstr : Substring we want to replace with
+ count : Optional pointer to int (input / output value). NULL to ignore.
+ Input: Maximum replacements to be done. NULL or < 1 to do all.
+ Output: Number of replacements done or -1 if not enough memory.
+ Returns : Pointer to the new string or NULL if error.
+ Notes :
+ - Case sensitive - Otherwise, replace functions "strstr" by "strcasestr"
+ - Always allocates memory for the result.
+--------------------------------------------------------------------------- */
+char*
+rtpg_strreplace(
+ const char *str,
+ const char *oldstr, const char *newstr,
+ int *count
+) {
+ const char *tmp = str;
+ char *result;
+ int found = 0;
+ int length, reslen;
+ int oldlen = strlen(oldstr);
+ int newlen = strlen(newstr);
+ int limit = (count != NULL && *count > 0) ? *count : -1;
+
+ tmp = str;
+ while ((tmp = strstr(tmp, oldstr)) != NULL && found != limit)
+ found++, tmp += oldlen;
+
+ length = strlen(str) + found * (newlen - oldlen);
+ if ((result = (char *) palloc(length + 1)) == NULL) {
+ fprintf(stderr, "Not enough memory\n");
+ found = -1;
+ }
+ else {
+ tmp = str;
+ limit = found; /* Countdown */
+ reslen = 0; /* length of current result */
+
+ /* Replace each old string found with new string */
+ while ((limit-- > 0) && (tmp = strstr(tmp, oldstr)) != NULL) {
+ length = (tmp - str); /* Number of chars to keep intouched */
+ strncpy(result + reslen, str, length); /* Original part keeped */
+ strcpy(result + (reslen += length), newstr); /* Insert new string */
+
+ reslen += newlen;
+ tmp += oldlen;
+ str = tmp;
+ }
+ strcpy(result + reslen, str); /* Copies last part and ending null char */
+ }
+
+ if (count != NULL) *count = found;
+ return result;
+}
+
+char *
+rtpg_strtoupper(char * str) {
+ int j;
+
+ for (j = strlen(str) - 1; j >= 0; j--)
+ str[j] = toupper(str[j]);
+
+ return str;
+}
+
+char*
+rtpg_chartrim(const char *input, char *remove) {
+ char *rtn = NULL;
+ char *ptr = NULL;
+ uint32_t offset = 0;
+
+ if (!input)
+ return NULL;
+ else if (!*input)
+ return (char *) input;
+
+ /* trim left */
+ while (strchr(remove, *input) != NULL)
+ input++;
+
+ /* trim right */
+ ptr = ((char *) input) + strlen(input);
+ while (strchr(remove, *--ptr) != NULL)
+ offset++;
+
+ rtn = palloc(sizeof(char) * (strlen(input) - offset + 1));
+ if (rtn == NULL) {
+ fprintf(stderr, "Not enough memory\n");
+ return NULL;
+ }
+ strncpy(rtn, input, strlen(input) - offset);
+ rtn[strlen(input) - offset] = '\0';
+
+ return rtn;
+}
+
+/* split a string based on a delimiter */
+char**
+rtpg_strsplit(const char *str, const char *delimiter, int *n) {
+ char *tmp = NULL;
+ char **rtn = NULL;
+ char *token = NULL;
+
+ *n = 0;
+ if (!str)
+ return NULL;
+
+ /* copy str to tmp as strtok will mangle the string */
+ tmp = palloc(sizeof(char) * (strlen(str) + 1));
+ if (NULL == tmp) {
+ fprintf(stderr, "Not enough memory\n");
+ return NULL;
+ }
+ strcpy(tmp, str);
+
+ if (!strlen(tmp) || !delimiter || !strlen(delimiter)) {
+ *n = 1;
+ rtn = (char **) palloc(*n * sizeof(char *));
+ if (NULL == rtn) {
+ fprintf(stderr, "Not enough memory\n");
+ return NULL;
+ }
+ rtn[0] = (char *) palloc(sizeof(char) * (strlen(tmp) + 1));
+ if (NULL == rtn[0]) {
+ fprintf(stderr, "Not enough memory\n");
+ return NULL;
+ }
+ strcpy(rtn[0], tmp);
+ pfree(tmp);
+ return rtn;
+ }
+
+ token = strtok(tmp, delimiter);
+ while (token != NULL) {
+ if (*n < 1) {
+ rtn = (char **) palloc(sizeof(char *));
+ }
+ else {
+ rtn = (char **) repalloc(rtn, (*n + 1) * sizeof(char *));
+ }
+ if (NULL == rtn) {
+ fprintf(stderr, "Not enough memory\n");
+ return NULL;
+ }
+
+ rtn[*n] = NULL;
+ rtn[*n] = (char *) palloc(sizeof(char) * (strlen(token) + 1));
+ if (NULL == rtn[*n]) {
+ fprintf(stderr, "Not enough memory\n");
+ return NULL;
+ }
+
+ strcpy(rtn[*n], token);
+ *n = *n + 1;
+
+ token = strtok(NULL, delimiter);
+ }
+
+ pfree(tmp);
+ return rtn;
+}
+
+char *
+rtpg_removespaces(char *str) {
+ char *rtn;
+ char *tmp;
+
+ rtn = rtpg_strreplace(str, " ", "", NULL);
+
+ tmp = rtpg_strreplace(rtn, "\n", "", NULL);
+ pfree(rtn);
+ rtn = rtpg_strreplace(tmp, "\t", "", NULL);
+ pfree(tmp);
+ tmp = rtpg_strreplace(rtn, "\f", "", NULL);
+ pfree(rtn);
+ rtn = rtpg_strreplace(tmp, "\r", "", NULL);
+ pfree(tmp);
+
+ return rtn;
+}
+
+char*
+rtpg_trim(const char *input) {
+ char *rtn;
+ char *ptr;
+ uint32_t offset = 0;
+ int inputlen = 0;
+
+ if (!input)
+ return NULL;
+ else if (!*input)
+ return (char *) input;
+
+ /* trim left */
+ while (isspace(*input) && *input != '\0')
+ input++;
+
+ /* trim right */
+ inputlen = strlen(input);
+ if (inputlen) {
+ ptr = ((char *) input) + inputlen;
+ while (isspace(*--ptr))
+ offset++;
+ }
+
+ rtn = palloc(sizeof(char) * (inputlen - offset + 1));
+ if (rtn == NULL) {
+ fprintf(stderr, "Not enough memory\n");
+ return NULL;
+ }
+ strncpy(rtn, input, inputlen - offset);
+ rtn[inputlen - offset] = '\0';
+
+ return rtn;
+}
+
+char*
+rtpg_getSR(int srid) {
+ int i = 0;
+ int len = 0;
+ char *sql = NULL;
+ int spi_result;
+ TupleDesc tupdesc;
+ SPITupleTable *tuptable = NULL;
+ HeapTuple tuple;
+ char *tmp = NULL;
+ char *srs = NULL;
+
+/*
+SELECT
+ CASE
+ WHEN (upper(auth_name) = 'EPSG' OR upper(auth_name) = 'EPSGA') AND length(COALESCE(auth_srid::text, '')) > 0
+ THEN upper(auth_name) || ':' || auth_srid
+ WHEN length(COALESCE(auth_name, '') || COALESCE(auth_srid::text, '')) > 0
+ THEN COALESCE(auth_name, '') || COALESCE(auth_srid::text, '')
+ ELSE ''
+ END,
+ proj4text,
+ srtext
+FROM spatial_ref_sys
+WHERE srid = X
+LIMIT 1
+*/
+
+ len = sizeof(char) * (strlen("SELECT CASE WHEN (upper(auth_name) = 'EPSG' OR upper(auth_name) = 'EPSGA') AND length(COALESCE(auth_srid::text, '')) > 0 THEN upper(auth_name) || ':' || auth_srid WHEN length(COALESCE(auth_name, '') || COALESCE(auth_srid::text, '')) > 0 THEN COALESCE(auth_name, '') || COALESCE(auth_srid::text, '') ELSE '' END, proj4text, srtext FROM spatial_ref_sys WHERE srid = LIMIT 1") + MAX_INT_CHARLEN + 1);
+ sql = (char *) palloc(len);
+ if (NULL == sql) {
+ elog(ERROR, "rtpg_getSR: Could not allocate memory for sql\n");
+ return NULL;
+ }
+
+ spi_result = SPI_connect();
+ if (spi_result != SPI_OK_CONNECT) {
+ pfree(sql);
+ elog(ERROR, "rtpg_getSR: Could not connect to database using SPI\n");
+ return NULL;
+ }
+
+ /* execute query */
+ snprintf(sql, len, "SELECT CASE WHEN (upper(auth_name) = 'EPSG' OR upper(auth_name) = 'EPSGA') AND length(COALESCE(auth_srid::text, '')) > 0 THEN upper(auth_name) || ':' || auth_srid WHEN length(COALESCE(auth_name, '') || COALESCE(auth_srid::text, '')) > 0 THEN COALESCE(auth_name, '') || COALESCE(auth_srid::text, '') ELSE '' END, proj4text, srtext FROM spatial_ref_sys WHERE srid = %d LIMIT 1", srid);
+ POSTGIS_RT_DEBUGF(4, "SRS query: %s", sql);
+ spi_result = SPI_execute(sql, TRUE, 0);
+ SPI_pfree(sql);
+ if (spi_result != SPI_OK_SELECT || SPI_tuptable == NULL || SPI_processed != 1) {
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_finish();
+ elog(ERROR, "rtpg_getSR: Cannot find SRID (%d) in spatial_ref_sys", srid);
+ return NULL;
+ }
+
+ tupdesc = SPI_tuptable->tupdesc;
+ tuptable = SPI_tuptable;
+ tuple = tuptable->vals[0];
+
+ /* which column to use? */
+ for (i = 1; i < 4; i++) {
+ tmp = SPI_getvalue(tuple, tupdesc, i);
+
+ /* value AND GDAL supports this SR */
+ if (
+ SPI_result != SPI_ERROR_NOATTRIBUTE &&
+ SPI_result != SPI_ERROR_NOOUTFUNC &&
+ tmp != NULL &&
+ strlen(tmp) &&
+ rt_util_gdal_supported_sr(tmp)
+ ) {
+ POSTGIS_RT_DEBUGF(4, "Value for column %d is %s", i, tmp);
+
+ len = strlen(tmp) + 1;
+ srs = SPI_palloc(sizeof(char) * len);
+ if (NULL == srs) {
+ pfree(tmp);
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_finish();
+ elog(ERROR, "rtpg_getSR: Could not allocate memory for spatial reference text\n");
+ return NULL;
+ }
+ strncpy(srs, tmp, len);
+ pfree(tmp);
+
+ break;
+ }
+
+ if (tmp != NULL)
+ pfree(tmp);
+ continue;
+ }
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_finish();
+
+ /* unable to get SR info */
+ if (srs == NULL) {
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_finish();
+ elog(ERROR, "rtpg_getSR: Could not find a viable spatial reference for SRID (%d)", srid);
+ return NULL;
+ }
+
+ return srs;
+}
--- /dev/null
+/*
+ * $Id$
+ *
+ * WKTRaster - Raster Types for PostGIS
+ * http://www.postgis.org/support/wiki/index.php?WKTRasterHomePage
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef RTPG_INTERNAL_H_INCLUDED
+#define RTPG_INTERNAL_H_INCLUDED
+
+#include "rtpostgis.h"
+
+/***************************************************************
+ * Internal functions must be prefixed with rtpg_. This is
+ * keeping inline with the use of pgis_ for ./postgis C utility
+ * functions.
+ ****************************************************************/
+
+char
+*rtpg_strreplace(
+ const char *str,
+ const char *oldstr, const char *newstr,
+ int *count
+);
+
+char *
+rtpg_strtoupper(char *str);
+
+char *
+rtpg_chartrim(const char* input, char *remove);
+
+char **
+rtpg_strsplit(const char *str, const char *delimiter, int *n);
+
+char *
+rtpg_removespaces(char *str);
+
+char *
+rtpg_trim(const char* input);
+
+char *
+rtpg_getSR(int srid);
+
+#endif /* RTPG_INTERNAL_H_INCLUDED */
--- /dev/null
+/*
+ * $Id$
+ *
+ * WKTRaster - Raster Types for PostGIS
+ * http://www.postgis.org/support/wiki/index.php?WKTRasterHomePage
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <assert.h>
+
+#include <postgres.h> /* for palloc */
+#include <fmgr.h>
+#include <executor/spi.h>
+#include <utils/lsyscache.h> /* for get_typlenbyvalalign */
+#include <utils/array.h> /* for ArrayType */
+#include <utils/builtins.h>
+#include <catalog/pg_type.h> /* for INT2OID, INT4OID, FLOAT4OID, FLOAT8OID and TEXTOID */
+#include <executor/executor.h> /* for GetAttributeByName */
+
+#include "../../postgis_config.h"
+#include "lwgeom_pg.h"
+
+#include "rtpostgis.h"
+#include "rtpg_internal.h"
+
+/* n-raster MapAlgebra */
+Datum RASTER_nMapAlgebra(PG_FUNCTION_ARGS);
+Datum RASTER_nMapAlgebraExpr(PG_FUNCTION_ARGS);
+
+/* raster union aggregate */
+Datum RASTER_union_transfn(PG_FUNCTION_ARGS);
+Datum RASTER_union_finalfn(PG_FUNCTION_ARGS);
+
+/* raster clip */
+Datum RASTER_clip(PG_FUNCTION_ARGS);
+
+/* reclassify specified bands of a raster */
+Datum RASTER_reclass(PG_FUNCTION_ARGS);
+
+/* apply colormap to specified band of a raster */
+Datum RASTER_colorMap(PG_FUNCTION_ARGS);
+
+/* one-raster MapAlgebra */
+Datum RASTER_mapAlgebraExpr(PG_FUNCTION_ARGS);
+Datum RASTER_mapAlgebraFct(PG_FUNCTION_ARGS);
+
+/* one-raster neighborhood MapAlgebra */
+Datum RASTER_mapAlgebraFctNgb(PG_FUNCTION_ARGS);
+
+/* two-raster MapAlgebra */
+Datum RASTER_mapAlgebra2(PG_FUNCTION_ARGS);
+
+/* ---------------------------------------------------------------- */
+/* n-raster MapAlgebra */
+/* ---------------------------------------------------------------- */
+
+typedef struct {
+ Oid ufc_noid;
+ FmgrInfo ufl_info;
+ FunctionCallInfoData ufc_info;
+ int ufc_nullcount;
+} rtpg_nmapalgebra_callback_arg;
+
+typedef struct rtpg_nmapalgebra_arg_t *rtpg_nmapalgebra_arg;
+struct rtpg_nmapalgebra_arg_t {
+ int numraster;
+ rt_pgraster **pgraster;
+ rt_raster *raster;
+ uint8_t *isempty; /* flag indicating if raster is empty */
+ uint8_t *ownsdata; /* is the raster self owned or just a pointer to another raster */
+ int *nband; /* source raster's band index, 0-based */
+ uint8_t *hasband; /* does source raster have band at index nband? */
+
+ rt_pixtype pixtype; /* output raster band's pixel type */
+ int hasnodata; /* NODATA flag */
+ double nodataval; /* NODATA value */
+
+ int distance[2]; /* distance in X and Y axis */
+
+ rt_extenttype extenttype; /* ouput raster's extent type */
+ rt_pgraster *pgcextent; /* custom extent of type rt_pgraster */
+ rt_raster cextent; /* custom extent of type rt_raster */
+
+ rtpg_nmapalgebra_callback_arg callback;
+};
+
+static rtpg_nmapalgebra_arg rtpg_nmapalgebra_arg_init() {
+ rtpg_nmapalgebra_arg arg = NULL;
+
+ arg = palloc(sizeof(struct rtpg_nmapalgebra_arg_t));
+ if (arg == NULL) {
+ elog(ERROR, "rtpg_nmapalgebra_arg_init: Could not allocate memory for arguments");
+ return NULL;
+ }
+
+ arg->numraster = 0;
+ arg->pgraster = NULL;
+ arg->raster = NULL;
+ arg->isempty = NULL;
+ arg->ownsdata = NULL;
+ arg->nband = NULL;
+ arg->hasband = NULL;
+
+ arg->pixtype = PT_END;
+ arg->hasnodata = 1;
+ arg->nodataval = 0;
+
+ arg->distance[0] = 0;
+ arg->distance[1] = 0;
+
+ arg->extenttype = ET_INTERSECTION;
+ arg->pgcextent = NULL;
+ arg->cextent = NULL;
+
+ arg->callback.ufc_noid = InvalidOid;
+ arg->callback.ufc_nullcount = 0;
+
+ return arg;
+}
+
+static void rtpg_nmapalgebra_arg_destroy(rtpg_nmapalgebra_arg arg) {
+ int i = 0;
+
+ if (arg->raster != NULL) {
+ for (i = 0; i < arg->numraster; i++) {
+ if (arg->raster[i] == NULL || !arg->ownsdata[i])
+ continue;
+
+ rt_raster_destroy(arg->raster[i]);
+ }
+
+ pfree(arg->raster);
+ pfree(arg->pgraster);
+ pfree(arg->isempty);
+ pfree(arg->ownsdata);
+ pfree(arg->nband);
+ }
+
+ if (arg->cextent != NULL)
+ rt_raster_destroy(arg->cextent);
+
+ pfree(arg);
+}
+
+static int rtpg_nmapalgebra_rastbandarg_process(rtpg_nmapalgebra_arg arg, ArrayType *array, int *allnull, int *allempty, int *noband) {
+ Oid etype;
+ Datum *e;
+ bool *nulls;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ int n = 0;
+
+ HeapTupleHeader tup;
+ bool isnull;
+ Datum tupv;
+
+ int i;
+ int j;
+ int nband;
+
+ if (arg == NULL || array == NULL) {
+ elog(ERROR, "rtpg_nmapalgebra_rastbandarg_process: NULL values not permitted for parameters");
+ return 0;
+ }
+
+ etype = ARR_ELEMTYPE(array);
+ get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
+
+ deconstruct_array(
+ array,
+ etype,
+ typlen, typbyval, typalign,
+ &e, &nulls, &n
+ );
+
+ if (!n) {
+ elog(ERROR, "rtpg_nmapalgebra_rastbandarg_process: Invalid argument for rastbandarg");
+ return 0;
+ }
+
+ /* prep arg */
+ arg->numraster = n;
+ arg->pgraster = palloc(sizeof(rt_pgraster *) * arg->numraster);
+ arg->raster = palloc(sizeof(rt_raster) * arg->numraster);
+ arg->isempty = palloc(sizeof(uint8_t) * arg->numraster);
+ arg->ownsdata = palloc(sizeof(uint8_t) * arg->numraster);
+ arg->nband = palloc(sizeof(int) * arg->numraster);
+ arg->hasband = palloc(sizeof(uint8_t) * arg->numraster);
+ if (
+ arg->pgraster == NULL ||
+ arg->raster == NULL ||
+ arg->isempty == NULL ||
+ arg->ownsdata == NULL ||
+ arg->nband == NULL ||
+ arg->hasband == NULL
+ ) {
+ elog(ERROR, "rtpg_nmapalgebra_rastbandarg_process: Could not allocate memory for processing rastbandarg");
+ return 0;
+ }
+
+ *allnull = 0;
+ *allempty = 0;
+ *noband = 0;
+
+ /* process each element */
+ for (i = 0; i < n; i++) {
+ if (nulls[i]) {
+ arg->numraster--;
+ continue;
+ }
+
+ POSTGIS_RT_DEBUGF(4, "Processing rastbandarg at index %d", i);
+
+ arg->raster[i] = NULL;
+ arg->isempty[i] = 0;
+ arg->ownsdata[i] = 1;
+ arg->nband[i] = 0;
+ arg->hasband[i] = 0;
+
+ /* each element is a tuple */
+ tup = (HeapTupleHeader) DatumGetPointer(e[i]);
+ if (NULL == tup) {
+ elog(ERROR, "rtpg_nmapalgebra_rastbandarg_process: Invalid argument for rastbandarg at index %d", i);
+ return 0;
+ }
+
+ /* first element, raster */
+ POSTGIS_RT_DEBUG(4, "Processing first element (raster)");
+ tupv = GetAttributeByName(tup, "rast", &isnull);
+ if (isnull) {
+ elog(NOTICE, "First argument (nband) of rastbandarg at index %d is NULL. Assuming NULL raster", i);
+ arg->isempty[i] = 1;
+ arg->ownsdata[i] = 0;
+
+ (*allnull)++;
+ (*allempty)++;
+ (*noband)++;
+
+ continue;
+ }
+
+ arg->pgraster[i] = (rt_pgraster *) PG_DETOAST_DATUM(tupv);
+
+ /* see if this is a copy of an existing pgraster */
+ for (j = 0; j < i; j++) {
+ if (!arg->isempty[j] && (arg->pgraster[i] == arg->pgraster[j])) {
+ POSTGIS_RT_DEBUG(4, "raster matching existing same raster found");
+ arg->raster[i] = arg->raster[j];
+ arg->ownsdata[i] = 0;
+ break;
+ }
+ }
+
+ if (arg->ownsdata[i]) {
+ POSTGIS_RT_DEBUG(4, "deserializing raster");
+ arg->raster[i] = rt_raster_deserialize(arg->pgraster[i], FALSE);
+ if (arg->raster[i] == NULL) {
+ elog(ERROR, "rtpg_nmapalgebra_rastbandarg_process: Could not deserialize raster at index %d", i);
+ return 0;
+ }
+ }
+
+ /* is raster empty? */
+ arg->isempty[i] = rt_raster_is_empty(arg->raster[i]);
+ if (arg->isempty[i]) {
+ (*allempty)++;
+ (*noband)++;
+
+ continue;
+ }
+
+ /* second element, nband */
+ POSTGIS_RT_DEBUG(4, "Processing second element (nband)");
+ tupv = GetAttributeByName(tup, "nband", &isnull);
+ if (isnull) {
+ nband = 1;
+ elog(NOTICE, "First argument (nband) of rastbandarg at index %d is NULL. Assuming nband = %d", i, nband);
+ }
+ else
+ nband = DatumGetInt32(tupv);
+
+ if (nband < 1) {
+ elog(ERROR, "rtpg_nmapalgebra_rastbandarg_process: Band number provided for rastbandarg at index %d must be greater than zero (1-based)", i);
+ return 0;
+ }
+
+ arg->nband[i] = nband - 1;
+ arg->hasband[i] = rt_raster_has_band(arg->raster[i], arg->nband[i]);
+ if (!arg->hasband[i]) {
+ (*noband)++;
+ POSTGIS_RT_DEBUGF(4, "Band at index %d not found in raster", nband);
+ }
+ }
+
+ if (arg->numraster < n) {
+ arg->pgraster = repalloc(arg->pgraster, sizeof(rt_pgraster *) * arg->numraster);
+ arg->raster = repalloc(arg->raster, sizeof(rt_raster) * arg->numraster);
+ arg->isempty = repalloc(arg->isempty, sizeof(uint8_t) * arg->numraster);
+ arg->ownsdata = repalloc(arg->ownsdata, sizeof(uint8_t) * arg->numraster);
+ arg->nband = repalloc(arg->nband, sizeof(int) * arg->numraster);
+ arg->hasband = repalloc(arg->hasband, sizeof(uint8_t) * arg->numraster);
+ if (
+ arg->pgraster == NULL ||
+ arg->raster == NULL ||
+ arg->isempty == NULL ||
+ arg->ownsdata == NULL ||
+ arg->nband == NULL ||
+ arg->hasband == NULL
+ ) {
+ elog(ERROR, "rtpg_nmapalgebra_rastbandarg_process: Could not reallocate memory for processed rastbandarg");
+ return 0;
+ }
+ }
+
+ POSTGIS_RT_DEBUGF(4, "arg->numraster = %d", arg->numraster);
+
+ return 1;
+}
+
+/*
+ Callback for RASTER_nMapAlgebra
+*/
+static int rtpg_nmapalgebra_callback(
+ rt_iterator_arg arg, void *userarg,
+ double *value, int *nodata
+) {
+ rtpg_nmapalgebra_callback_arg *callback = (rtpg_nmapalgebra_callback_arg *) userarg;
+
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+
+ ArrayType *mdValues = NULL;
+ Datum *_values = NULL;
+ bool *_nodata = NULL;
+
+ ArrayType *mdPos = NULL;
+ Datum *_pos = NULL;
+ bool *_null = NULL;
+
+ int i = 0;
+ int x = 0;
+ int y = 0;
+ int z = 0;
+ int dim[3] = {0};
+ int lbound[3] = {1, 1, 1};
+ Datum datum = (Datum) NULL;
+
+ if (arg == NULL)
+ return 0;
+
+ *value = 0;
+ *nodata = 0;
+
+ dim[0] = arg->rasters;
+ dim[1] = arg->rows;
+ dim[2] = arg->columns;
+
+ _values = palloc(sizeof(Datum) * arg->rasters * arg->rows * arg->columns);
+ _nodata = palloc(sizeof(bool) * arg->rasters * arg->rows * arg->columns);
+ if (_values == NULL || _nodata == NULL) {
+ elog(ERROR, "rtpg_nmapalgebra_callback: Could not allocate memory for values array");
+ return 0;
+ }
+
+ /* build mdValues */
+ i = 0;
+ /* raster */
+ for (z = 0; z < arg->rasters; z++) {
+ /* Y axis */
+ for (y = 0; y < arg->rows; y++) {
+ /* X axis */
+ for (x = 0; x < arg->columns; x++) {
+ POSTGIS_RT_DEBUGF(4, "(z, y ,x) = (%d, %d, %d)", z, y, x);
+ POSTGIS_RT_DEBUGF(4, "(value, nodata) = (%f, %d)", arg->values[z][y][x], arg->nodata[z][y][x]);
+
+ _nodata[i] = (bool) arg->nodata[z][y][x];
+ if (!_nodata[i])
+ _values[i] = Float8GetDatum(arg->values[z][y][x]);
+ else
+ _values[i] = (Datum) NULL;
+
+ i++;
+ }
+ }
+ }
+
+ /* info about the type of item in the multi-dimensional array (float8). */
+ get_typlenbyvalalign(FLOAT8OID, &typlen, &typbyval, &typalign);
+
+ /* construct mdValues */
+ mdValues = construct_md_array(
+ _values, _nodata,
+ 3, dim, lbound,
+ FLOAT8OID,
+ typlen, typbyval, typalign
+ );
+ pfree(_nodata);
+ pfree(_values);
+
+ _pos = palloc(sizeof(Datum) * (arg->rasters + 1) * 2);
+ _null = palloc(sizeof(bool) * (arg->rasters + 1) * 2);
+ if (_pos == NULL || _null == NULL) {
+ pfree(mdValues);
+ elog(ERROR, "rtpg_nmapalgebra_callback: Could not allocate memory for position array");
+ return 0;
+ }
+ memset(_null, 0, sizeof(bool) * (arg->rasters + 1) * 2);
+
+ /* build mdPos */
+ i = 0;
+ _pos[i] = arg->dst_pixel[0] + 1;
+ i++;
+ _pos[i] = arg->dst_pixel[1] + 1;
+ i++;
+
+ for (z = 0; z < arg->rasters; z++) {
+ _pos[i] = arg->src_pixel[z][0] + 1;
+ i++;
+
+ _pos[i] = arg->src_pixel[z][1] + 1;
+ i++;
+ }
+
+ /* info about the type of item in the multi-dimensional array (int4). */
+ get_typlenbyvalalign(INT4OID, &typlen, &typbyval, &typalign);
+
+ /* reuse dim and lbound, just tweak to what we need */
+ dim[0] = arg->rasters + 1;
+ dim[1] = 2;
+ lbound[0] = 0;
+
+ /* construct mdPos */
+ mdPos = construct_md_array(
+ _pos, _null,
+ 2, dim, lbound,
+ INT4OID,
+ typlen, typbyval, typalign
+ );
+ pfree(_pos);
+ pfree(_null);
+
+ callback->ufc_info.arg[0] = PointerGetDatum(mdValues);
+ callback->ufc_info.arg[1] = PointerGetDatum(mdPos);
+
+ /* function is strict and null parameter is passed */
+ /* http://archives.postgresql.org/pgsql-general/2011-11/msg00424.php */
+ if (callback->ufl_info.fn_strict && callback->ufc_nullcount) {
+ *nodata = 1;
+
+ pfree(mdValues);
+ pfree(mdPos);
+
+ return 1;
+ }
+
+ /* call user callback function */
+ datum = FunctionCallInvoke(&(callback->ufc_info));
+ pfree(mdValues);
+ pfree(mdPos);
+
+ /* result is not null*/
+ if (!callback->ufc_info.isnull)
+ *value = DatumGetFloat8(datum);
+ else
+ *nodata = 1;
+
+ return 1;
+}
+
+/*
+ ST_MapAlgebra for n rasters
+*/
+PG_FUNCTION_INFO_V1(RASTER_nMapAlgebra);
+Datum RASTER_nMapAlgebra(PG_FUNCTION_ARGS)
+{
+ rtpg_nmapalgebra_arg arg = NULL;
+ rt_iterator itrset;
+ int i = 0;
+ int noerr = 0;
+ int allnull = 0;
+ int allempty = 0;
+ int noband = 0;
+
+ rt_raster raster = NULL;
+ rt_band band = NULL;
+ rt_pgraster *pgraster = NULL;
+
+ POSTGIS_RT_DEBUG(3, "Starting...");
+
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+
+ /* init argument struct */
+ arg = rtpg_nmapalgebra_arg_init();
+ if (arg == NULL) {
+ elog(ERROR, "RASTER_nMapAlgebra: Could not initialize argument structure");
+ PG_RETURN_NULL();
+ }
+
+ /* let helper function process rastbandarg (0) */
+ if (!rtpg_nmapalgebra_rastbandarg_process(arg, PG_GETARG_ARRAYTYPE_P(0), &allnull, &allempty, &noband)) {
+ rtpg_nmapalgebra_arg_destroy(arg);
+ elog(ERROR, "RASTER_nMapAlgebra: Could not process rastbandarg");
+ PG_RETURN_NULL();
+ }
+
+ POSTGIS_RT_DEBUGF(4, "allnull, allempty, noband = %d, %d, %d", allnull, allempty, noband);
+
+ /* all rasters are NULL, return NULL */
+ if (allnull == arg->numraster) {
+ elog(NOTICE, "All input rasters are NULL. Returning NULL");
+ rtpg_nmapalgebra_arg_destroy(arg);
+ PG_RETURN_NULL();
+ }
+
+ /* pixel type (2) */
+ if (!PG_ARGISNULL(2)) {
+ char *pixtypename = text_to_cstring(PG_GETARG_TEXT_P(2));
+
+ /* Get the pixel type index */
+ arg->pixtype = rt_pixtype_index_from_name(pixtypename);
+ if (arg->pixtype == PT_END) {
+ rtpg_nmapalgebra_arg_destroy(arg);
+ elog(ERROR, "RASTER_nMapAlgebra: Invalid pixel type: %s", pixtypename);
+ PG_RETURN_NULL();
+ }
+ }
+
+ /* distancex (3) */
+ if (!PG_ARGISNULL(3))
+ arg->distance[0] = PG_GETARG_INT32(3);
+ /* distancey (4) */
+ if (!PG_ARGISNULL(4))
+ arg->distance[1] = PG_GETARG_INT32(4);
+
+ if (arg->distance[0] < 0 || arg->distance[1] < 0) {
+ rtpg_nmapalgebra_arg_destroy(arg);
+ elog(ERROR, "RASTER_nMapAlgebra: Distance for X and Y axis must be greater than or equal to zero");
+ PG_RETURN_NULL();
+ }
+
+ /* extent type (5) */
+ if (!PG_ARGISNULL(5)) {
+ char *extenttypename = rtpg_strtoupper(rtpg_trim(text_to_cstring(PG_GETARG_TEXT_P(5))));
+ arg->extenttype = rt_util_extent_type(extenttypename);
+ }
+ POSTGIS_RT_DEBUGF(4, "extenttype: %d", arg->extenttype);
+
+ /* custom extent (6) */
+ if (arg->extenttype == ET_CUSTOM) {
+ if (PG_ARGISNULL(6)) {
+ elog(NOTICE, "Custom extent is NULL. Returning NULL");
+ rtpg_nmapalgebra_arg_destroy(arg);
+ PG_RETURN_NULL();
+ }
+
+ arg->pgcextent = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(6));
+
+ /* only need the raster header */
+ arg->cextent = rt_raster_deserialize(arg->pgcextent, TRUE);
+ if (arg->cextent == NULL) {
+ rtpg_nmapalgebra_arg_destroy(arg);
+ elog(ERROR, "RASTER_nMapAlgebra: Could not deserialize custom extent");
+ PG_RETURN_NULL();
+ }
+ else if (rt_raster_is_empty(arg->cextent)) {
+ elog(NOTICE, "Custom extent is an empty raster. Returning empty raster");
+ rtpg_nmapalgebra_arg_destroy(arg);
+
+ raster = rt_raster_new(0, 0);
+ if (raster == NULL) {
+ elog(ERROR, "RASTER_nMapAlgebra: Could not create empty raster");
+ PG_RETURN_NULL();
+ }
+
+ pgraster = rt_raster_serialize(raster);
+ rt_raster_destroy(raster);
+ if (!pgraster) PG_RETURN_NULL();
+
+ SET_VARSIZE(pgraster, pgraster->size);
+ PG_RETURN_POINTER(pgraster);
+ }
+ }
+
+ noerr = 1;
+ /* all rasters are empty, return empty raster */
+ if (allempty == arg->numraster) {
+ elog(NOTICE, "All input rasters are empty. Returning empty raster");
+ noerr = 0;
+ }
+ /* all rasters don't have indicated band, return empty raster */
+ else if (noband == arg->numraster) {
+ elog(NOTICE, "All input rasters do not have bands at indicated indexes. Returning empty raster");
+ noerr = 0;
+ }
+ if (!noerr) {
+ rtpg_nmapalgebra_arg_destroy(arg);
+
+ raster = rt_raster_new(0, 0);
+ if (raster == NULL) {
+ elog(ERROR, "RASTER_nMapAlgebra: Could not create empty raster");
+ PG_RETURN_NULL();
+ }
+
+ pgraster = rt_raster_serialize(raster);
+ rt_raster_destroy(raster);
+ if (!pgraster) PG_RETURN_NULL();
+
+ SET_VARSIZE(pgraster, pgraster->size);
+ PG_RETURN_POINTER(pgraster);
+ }
+
+ /* do regprocedure last (1) */
+ if (!PG_ARGISNULL(1) || get_fn_expr_argtype(fcinfo->flinfo, 1) == REGPROCEDUREOID) {
+ POSTGIS_RT_DEBUG(4, "processing callbackfunc");
+ arg->callback.ufc_noid = PG_GETARG_OID(1);
+
+ /* get function info */
+ fmgr_info(arg->callback.ufc_noid, &(arg->callback.ufl_info));
+
+ /* function cannot return set */
+ noerr = 0;
+ if (arg->callback.ufl_info.fn_retset) {
+ noerr = 1;
+ }
+ /* function should have correct # of args */
+ else if (arg->callback.ufl_info.fn_nargs != 3) {
+ noerr = 2;
+ }
+
+ /*
+ TODO: consider adding checks of the userfunction parameters
+ should be able to use get_fn_expr_argtype() of fmgr.c
+ */
+
+ if (noerr > 0) {
+ rtpg_nmapalgebra_arg_destroy(arg);
+ if (noerr > 1)
+ elog(ERROR, "RASTER_nMapAlgebra: Function provided must have three input parameters");
+ else
+ elog(ERROR, "RASTER_nMapAlgebra: Function provided must return double precision, not resultset");
+ PG_RETURN_NULL();
+ }
+
+ if (func_volatile(arg->callback.ufc_noid) == 'v')
+ elog(NOTICE, "Function provided is VOLATILE. Unless required and for best performance, function should be IMMUTABLE or STABLE");
+
+ /* prep function call data */
+#if POSTGIS_PGSQL_VERSION > 90
+ InitFunctionCallInfoData(arg->callback.ufc_info, &(arg->callback.ufl_info), arg->callback.ufl_info.fn_nargs, InvalidOid, NULL, NULL);
+#else
+ InitFunctionCallInfoData(arg->callback.ufc_info, &(arg->callback.ufl_info), arg->callback.ufl_info.fn_nargs, NULL, NULL);
+#endif
+ memset(arg->callback.ufc_info.argnull, FALSE, sizeof(bool) * arg->callback.ufl_info.fn_nargs);
+
+ /* userargs (7) */
+ if (!PG_ARGISNULL(7))
+ arg->callback.ufc_info.arg[2] = PG_GETARG_DATUM(7);
+ else {
+ arg->callback.ufc_info.arg[2] = (Datum) NULL;
+ arg->callback.ufc_info.argnull[2] = TRUE;
+ arg->callback.ufc_nullcount++;
+ }
+ }
+ else {
+ rtpg_nmapalgebra_arg_destroy(arg);
+ elog(ERROR, "RASTER_nMapAlgebra: callbackfunc must be provided");
+ PG_RETURN_NULL();
+ }
+
+ /* determine nodataval and possibly pixtype */
+ /* band to check */
+ switch (arg->extenttype) {
+ case ET_LAST:
+ i = arg->numraster - 1;
+ break;
+ case ET_SECOND:
+ if (arg->numraster > 1) {
+ i = 1;
+ break;
+ }
+ default:
+ i = 0;
+ break;
+ }
+ /* find first viable band */
+ if (!arg->hasband[i]) {
+ for (i = 0; i < arg->numraster; i++) {
+ if (arg->hasband[i])
+ break;
+ }
+ if (i >= arg->numraster)
+ i = arg->numraster - 1;
+ }
+ band = rt_raster_get_band(arg->raster[i], arg->nband[i]);
+
+ /* set pixel type if PT_END */
+ if (arg->pixtype == PT_END)
+ arg->pixtype = rt_band_get_pixtype(band);
+
+ /* set hasnodata and nodataval */
+ arg->hasnodata = 1;
+ if (rt_band_get_hasnodata_flag(band))
+ rt_band_get_nodata(band, &(arg->nodataval));
+ else
+ arg->nodataval = rt_band_get_min_value(band);
+
+ POSTGIS_RT_DEBUGF(4, "pixtype, hasnodata, nodataval: %s, %d, %f", rt_pixtype_name(arg->pixtype), arg->hasnodata, arg->nodataval);
+
+ /* init itrset */
+ itrset = palloc(sizeof(struct rt_iterator_t) * arg->numraster);
+ if (itrset == NULL) {
+ rtpg_nmapalgebra_arg_destroy(arg);
+ elog(ERROR, "RASTER_nMapAlgebra: Could not allocate memory for iterator arguments");
+ PG_RETURN_NULL();
+ }
+
+ /* set itrset */
+ for (i = 0; i < arg->numraster; i++) {
+ itrset[i].raster = arg->raster[i];
+ itrset[i].nband = arg->nband[i];
+ itrset[i].nbnodata = 1;
+ }
+
+ /* pass everything to iterator */
+ noerr = rt_raster_iterator(
+ itrset, arg->numraster,
+ arg->extenttype, arg->cextent,
+ arg->pixtype,
+ arg->hasnodata, arg->nodataval,
+ arg->distance[0], arg->distance[1],
+ &(arg->callback),
+ rtpg_nmapalgebra_callback,
+ &raster
+ );
+
+ /* cleanup */
+ pfree(itrset);
+ rtpg_nmapalgebra_arg_destroy(arg);
+
+ if (noerr != ES_NONE) {
+ elog(ERROR, "RASTER_nMapAlgebra: Could not run raster iterator function");
+ PG_RETURN_NULL();
+ }
+ else if (raster == NULL)
+ PG_RETURN_NULL();
+
+ pgraster = rt_raster_serialize(raster);
+ rt_raster_destroy(raster);
+
+ POSTGIS_RT_DEBUG(3, "Finished");
+
+ if (!pgraster)
+ PG_RETURN_NULL();
+
+ SET_VARSIZE(pgraster, pgraster->size);
+ PG_RETURN_POINTER(pgraster);
+}
+
+/* ---------------------------------------------------------------- */
+/* expression ST_MapAlgebra for n rasters */
+/* ---------------------------------------------------------------- */
+
+typedef struct {
+ int exprcount;
+
+ struct {
+ SPIPlanPtr spi_plan;
+ uint32_t spi_argcount;
+ uint8_t *spi_argpos;
+
+ int hasval;
+ double val;
+ } expr[3];
+
+ struct {
+ int hasval;
+ double val;
+ } nodatanodata;
+
+ struct {
+ int count;
+ char **val;
+ } kw;
+
+} rtpg_nmapalgebraexpr_callback_arg;
+
+typedef struct rtpg_nmapalgebraexpr_arg_t *rtpg_nmapalgebraexpr_arg;
+struct rtpg_nmapalgebraexpr_arg_t {
+ rtpg_nmapalgebra_arg bandarg;
+
+ rtpg_nmapalgebraexpr_callback_arg callback;
+};
+
+static rtpg_nmapalgebraexpr_arg rtpg_nmapalgebraexpr_arg_init(int cnt, char **kw) {
+ rtpg_nmapalgebraexpr_arg arg = NULL;
+ int i = 0;
+
+ arg = palloc(sizeof(struct rtpg_nmapalgebraexpr_arg_t));
+ if (arg == NULL) {
+ elog(ERROR, "rtpg_nmapalgebraexpr_arg_init: Could not allocate memory for arguments");
+ return NULL;
+ }
+
+ arg->bandarg = rtpg_nmapalgebra_arg_init();
+ if (arg->bandarg == NULL) {
+ elog(ERROR, "rtpg_nmapalgebraexpr_arg_init: Could not allocate memory for arg->bandarg");
+ return NULL;
+ }
+
+ arg->callback.kw.count = cnt;
+ arg->callback.kw.val = kw;
+
+ arg->callback.exprcount = 3;
+ for (i = 0; i < arg->callback.exprcount; i++) {
+ arg->callback.expr[i].spi_plan = NULL;
+ arg->callback.expr[i].spi_argcount = 0;
+ arg->callback.expr[i].spi_argpos = palloc(cnt * sizeof(uint8_t));
+ if (arg->callback.expr[i].spi_argpos == NULL) {
+ elog(ERROR, "rtpg_nmapalgebraexpr_arg_init: Could not allocate memory for spi_argpos");
+ return NULL;
+ }
+ memset(arg->callback.expr[i].spi_argpos, 0, sizeof(uint8_t) * cnt);
+ arg->callback.expr[i].hasval = 0;
+ arg->callback.expr[i].val = 0;
+ }
+
+ arg->callback.nodatanodata.hasval = 0;
+ arg->callback.nodatanodata.val = 0;
+
+ return arg;
+}
+
+static void rtpg_nmapalgebraexpr_arg_destroy(rtpg_nmapalgebraexpr_arg arg) {
+ int i = 0;
+
+ rtpg_nmapalgebra_arg_destroy(arg->bandarg);
+
+ for (i = 0; i < arg->callback.exprcount; i++) {
+ if (arg->callback.expr[i].spi_plan)
+ SPI_freeplan(arg->callback.expr[i].spi_plan);
+ if (arg->callback.kw.count)
+ pfree(arg->callback.expr[i].spi_argpos);
+ }
+
+ pfree(arg);
+}
+
+static int rtpg_nmapalgebraexpr_callback(
+ rt_iterator_arg arg, void *userarg,
+ double *value, int *nodata
+) {
+ rtpg_nmapalgebraexpr_callback_arg *callback = (rtpg_nmapalgebraexpr_callback_arg *) userarg;
+ SPIPlanPtr plan = NULL;
+ int i = 0;
+ int id = -1;
+
+ if (arg == NULL)
+ return 0;
+
+ *value = 0;
+ *nodata = 0;
+
+ /* 2 raster */
+ if (arg->rasters > 1) {
+ /* nodata1 = 1 AND nodata2 = 1, nodatanodataval */
+ if (arg->nodata[0][0][0] && arg->nodata[1][0][0]) {
+ if (callback->nodatanodata.hasval)
+ *value = callback->nodatanodata.val;
+ else
+ *nodata = 1;
+ }
+ /* nodata1 = 1 AND nodata2 != 1, nodata1expr */
+ else if (arg->nodata[0][0][0] && !arg->nodata[1][0][0]) {
+ id = 1;
+ if (callback->expr[id].hasval)
+ *value = callback->expr[id].val;
+ else if (callback->expr[id].spi_plan)
+ plan = callback->expr[id].spi_plan;
+ else
+ *nodata = 1;
+ }
+ /* nodata1 != 1 AND nodata2 = 1, nodata2expr */
+ else if (!arg->nodata[0][0][0] && arg->nodata[1][0][0]) {
+ id = 2;
+ if (callback->expr[id].hasval)
+ *value = callback->expr[id].val;
+ else if (callback->expr[id].spi_plan)
+ plan = callback->expr[id].spi_plan;
+ else
+ *nodata = 1;
+ }
+ /* expression */
+ else {
+ id = 0;
+ if (callback->expr[id].hasval)
+ *value = callback->expr[id].val;
+ else if (callback->expr[id].spi_plan)
+ plan = callback->expr[id].spi_plan;
+ else {
+ if (callback->nodatanodata.hasval)
+ *value = callback->nodatanodata.val;
+ else
+ *nodata = 1;
+ }
+ }
+ }
+ /* 1 raster */
+ else {
+ /* nodata = 1, nodata1expr */
+ if (arg->nodata[0][0][0]) {
+ id = 1;
+ if (callback->expr[id].hasval)
+ *value = callback->expr[id].val;
+ else if (callback->expr[id].spi_plan)
+ plan = callback->expr[id].spi_plan;
+ else
+ *nodata = 1;
+ }
+ /* expression */
+ else {
+ id = 0;
+ if (callback->expr[id].hasval)
+ *value = callback->expr[id].val;
+ else if (callback->expr[id].spi_plan)
+ plan = callback->expr[id].spi_plan;
+ else {
+ /* see if nodata1expr is available */
+ id = 1;
+ if (callback->expr[id].hasval)
+ *value = callback->expr[id].val;
+ else if (callback->expr[id].spi_plan)
+ plan = callback->expr[id].spi_plan;
+ else
+ *nodata = 1;
+ }
+ }
+ }
+
+ /* run prepared plan */
+ if (plan != NULL) {
+ Datum values[12];
+ bool nulls[12];
+ int err = 0;
+
+ TupleDesc tupdesc;
+ SPITupleTable *tuptable = NULL;
+ HeapTuple tuple;
+ Datum datum;
+ bool isnull = FALSE;
+
+ POSTGIS_RT_DEBUGF(4, "Running plan %d", id);
+
+ /* init values and nulls */
+ memset(values, (Datum) NULL, sizeof(Datum) * callback->kw.count);
+ memset(nulls, FALSE, sizeof(bool) * callback->kw.count);
+
+ if (callback->expr[id].spi_argcount) {
+ int idx = 0;
+
+ for (i = 0; i < callback->kw.count; i++) {
+ idx = callback->expr[id].spi_argpos[i];
+ if (idx < 1) continue;
+ idx--; /* 1-based now 0-based */
+
+ switch (i) {
+ /* [rast.x] */
+ case 0:
+ values[idx] = Int32GetDatum(arg->src_pixel[0][0] + 1);
+ break;
+ /* [rast.y] */
+ case 1:
+ values[idx] = Int32GetDatum(arg->src_pixel[0][1] + 1);
+ break;
+ /* [rast.val] */
+ case 2:
+ /* [rast] */
+ case 3:
+ if (!arg->nodata[0][0][0])
+ values[idx] = Float8GetDatum(arg->values[0][0][0]);
+ else
+ nulls[idx] = TRUE;
+ break;
+
+ /* [rast1.x] */
+ case 4:
+ values[idx] = Int32GetDatum(arg->src_pixel[0][0] + 1);
+ break;
+ /* [rast1.y] */
+ case 5:
+ values[idx] = Int32GetDatum(arg->src_pixel[0][1] + 1);
+ break;
+ /* [rast1.val] */
+ case 6:
+ /* [rast1] */
+ case 7:
+ if (!arg->nodata[0][0][0])
+ values[idx] = Float8GetDatum(arg->values[0][0][0]);
+ else
+ nulls[idx] = TRUE;
+ break;
+
+ /* [rast2.x] */
+ case 8:
+ values[idx] = Int32GetDatum(arg->src_pixel[1][0] + 1);
+ break;
+ /* [rast2.y] */
+ case 9:
+ values[idx] = Int32GetDatum(arg->src_pixel[1][1] + 1);
+ break;
+ /* [rast2.val] */
+ case 10:
+ /* [rast2] */
+ case 11:
+ if (!arg->nodata[1][0][0])
+ values[idx] = Float8GetDatum(arg->values[1][0][0]);
+ else
+ nulls[idx] = TRUE;
+ break;
+ }
+
+ }
+ }
+
+ /* run prepared plan */
+ err = SPI_execute_plan(plan, values, nulls, TRUE, 1);
+ if (err != SPI_OK_SELECT || SPI_tuptable == NULL || SPI_processed != 1) {
+ elog(ERROR, "rtpg_nmapalgebraexpr_callback: Unexpected error when running prepared statement %d", id);
+ return 0;
+ }
+
+ /* 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) {
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ elog(ERROR, "rtpg_nmapalgebraexpr_callback: Could not get result of prepared statement %d", id);
+ return 0;
+ }
+
+ if (!isnull) {
+ *value = DatumGetFloat8(datum);
+ POSTGIS_RT_DEBUG(4, "Getting value from Datum");
+ }
+ else {
+ /* 2 raster, check nodatanodataval */
+ if (arg->rasters > 1) {
+ if (callback->nodatanodata.hasval)
+ *value = callback->nodatanodata.val;
+ else
+ *nodata = 1;
+ }
+ /* 1 raster, check nodataval */
+ else {
+ if (callback->expr[1].hasval)
+ *value = callback->expr[1].val;
+ else
+ *nodata = 1;
+ }
+ }
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ }
+
+ POSTGIS_RT_DEBUGF(4, "(value, nodata) = (%f, %d)", *value, *nodata);
+ return 1;
+}
+
+PG_FUNCTION_INFO_V1(RASTER_nMapAlgebraExpr);
+Datum RASTER_nMapAlgebraExpr(PG_FUNCTION_ARGS)
+{
+ MemoryContext mainMemCtx = CurrentMemoryContext;
+ rtpg_nmapalgebraexpr_arg arg = NULL;
+ rt_iterator itrset;
+ uint16_t exprpos[3] = {1, 4, 5};
+
+ int i = 0;
+ int j = 0;
+ int k = 0;
+
+ int numraster = 0;
+ int err = 0;
+ int allnull = 0;
+ int allempty = 0;
+ int noband = 0;
+ int len = 0;
+
+ TupleDesc tupdesc;
+ SPITupleTable *tuptable = NULL;
+ HeapTuple tuple;
+ Datum datum;
+ bool isnull = FALSE;
+
+ rt_raster raster = NULL;
+ rt_band band = NULL;
+ rt_pgraster *pgraster = NULL;
+
+ const int argkwcount = 12;
+ char *argkw[] = {
+ "[rast.x]",
+ "[rast.y]",
+ "[rast.val]",
+ "[rast]",
+ "[rast1.x]",
+ "[rast1.y]",
+ "[rast1.val]",
+ "[rast1]",
+ "[rast2.x]",
+ "[rast2.y]",
+ "[rast2.val]",
+ "[rast2]"
+ };
+
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+
+ /* init argument struct */
+ arg = rtpg_nmapalgebraexpr_arg_init(argkwcount, argkw);
+ if (arg == NULL) {
+ elog(ERROR, "RASTER_nMapAlgebraExpr: Could not initialize argument structure");
+ PG_RETURN_NULL();
+ }
+
+ /* let helper function process rastbandarg (0) */
+ if (!rtpg_nmapalgebra_rastbandarg_process(arg->bandarg, PG_GETARG_ARRAYTYPE_P(0), &allnull, &allempty, &noband)) {
+ rtpg_nmapalgebraexpr_arg_destroy(arg);
+ elog(ERROR, "RASTER_nMapAlgebra: Could not process rastbandarg");
+ PG_RETURN_NULL();
+ }
+
+ POSTGIS_RT_DEBUGF(4, "allnull, allempty, noband = %d, %d, %d", allnull, allempty, noband);
+
+ /* all rasters are NULL, return NULL */
+ if (allnull == arg->bandarg->numraster) {
+ elog(NOTICE, "All input rasters are NULL. Returning NULL");
+ rtpg_nmapalgebraexpr_arg_destroy(arg);
+ PG_RETURN_NULL();
+ }
+
+ /* only work on one or two rasters */
+ if (arg->bandarg->numraster > 1)
+ numraster = 2;
+ else
+ numraster = 1;
+
+ /* pixel type (2) */
+ if (!PG_ARGISNULL(2)) {
+ char *pixtypename = text_to_cstring(PG_GETARG_TEXT_P(2));
+
+ /* Get the pixel type index */
+ arg->bandarg->pixtype = rt_pixtype_index_from_name(pixtypename);
+ if (arg->bandarg->pixtype == PT_END) {
+ rtpg_nmapalgebraexpr_arg_destroy(arg);
+ elog(ERROR, "RASTER_nMapAlgebraExpr: Invalid pixel type: %s", pixtypename);
+ PG_RETURN_NULL();
+ }
+ }
+ POSTGIS_RT_DEBUGF(4, "pixeltype: %d", arg->bandarg->pixtype);
+
+ /* extent type (3) */
+ if (!PG_ARGISNULL(3)) {
+ char *extenttypename = rtpg_strtoupper(rtpg_trim(text_to_cstring(PG_GETARG_TEXT_P(3))));
+ arg->bandarg->extenttype = rt_util_extent_type(extenttypename);
+ }
+
+ if (arg->bandarg->extenttype == ET_CUSTOM) {
+ if (numraster < 2) {
+ elog(NOTICE, "CUSTOM extent type not supported. Defaulting to FIRST");
+ arg->bandarg->extenttype = ET_FIRST;
+ }
+ else {
+ elog(NOTICE, "CUSTOM extent type not supported. Defaulting to INTERSECTION");
+ arg->bandarg->extenttype = ET_INTERSECTION;
+ }
+ }
+ else if (numraster < 2)
+ arg->bandarg->extenttype = ET_FIRST;
+
+ POSTGIS_RT_DEBUGF(4, "extenttype: %d", arg->bandarg->extenttype);
+
+ /* nodatanodataval (6) */
+ if (!PG_ARGISNULL(6)) {
+ arg->callback.nodatanodata.hasval = 1;
+ arg->callback.nodatanodata.val = PG_GETARG_FLOAT8(6);
+ }
+
+ err = 0;
+ /* all rasters are empty, return empty raster */
+ if (allempty == arg->bandarg->numraster) {
+ elog(NOTICE, "All input rasters are empty. Returning empty raster");
+ err = 1;
+ }
+ /* all rasters don't have indicated band, return empty raster */
+ else if (noband == arg->bandarg->numraster) {
+ elog(NOTICE, "All input rasters do not have bands at indicated indexes. Returning empty raster");
+ err = 1;
+ }
+ if (err) {
+ rtpg_nmapalgebraexpr_arg_destroy(arg);
+
+ raster = rt_raster_new(0, 0);
+ if (raster == NULL) {
+ elog(ERROR, "RASTER_nMapAlgebraExpr: Could not create empty raster");
+ PG_RETURN_NULL();
+ }
+
+ pgraster = rt_raster_serialize(raster);
+ rt_raster_destroy(raster);
+ if (!pgraster) PG_RETURN_NULL();
+
+ SET_VARSIZE(pgraster, pgraster->size);
+ PG_RETURN_POINTER(pgraster);
+ }
+
+ /* connect SPI */
+ if (SPI_connect() != SPI_OK_CONNECT) {
+ rtpg_nmapalgebraexpr_arg_destroy(arg);
+ elog(ERROR, "RASTER_nMapAlgebraExpr: Could not connect to the SPI manager");
+ PG_RETURN_NULL();
+ }
+
+ /*
+ process expressions
+
+ exprpos elements are:
+ 1 - expression => spi_plan[0]
+ 4 - nodata1expr => spi_plan[1]
+ 5 - nodata2expr => spi_plan[2]
+ */
+ for (i = 0; i < arg->callback.exprcount; i++) {
+ char *expr = NULL;
+ char *tmp = NULL;
+ char *sql = NULL;
+ char place[5] = "$1";
+
+ if (PG_ARGISNULL(exprpos[i]))
+ continue;
+
+ expr = text_to_cstring(PG_GETARG_TEXT_P(exprpos[i]));
+ POSTGIS_RT_DEBUGF(3, "raw expr of argument #%d: %s", exprpos[i], expr);
+
+ for (j = 0, k = 1; j < argkwcount; j++) {
+ /* attempt to replace keyword with placeholder */
+ len = 0;
+ tmp = rtpg_strreplace(expr, argkw[j], place, &len);
+ pfree(expr);
+ expr = tmp;
+
+ if (len) {
+ POSTGIS_RT_DEBUGF(4, "kw #%d (%s) at pos $%d", j, argkw[j], k);
+ arg->callback.expr[i].spi_argcount++;
+ arg->callback.expr[i].spi_argpos[j] = k++;
+
+ sprintf(place, "$%d", k);
+ }
+ else
+ arg->callback.expr[i].spi_argpos[j] = 0;
+ }
+
+ len = strlen("SELECT (") + strlen(expr) + strlen(")::double precision");
+ sql = (char *) palloc(len + 1);
+ if (sql == NULL) {
+ rtpg_nmapalgebraexpr_arg_destroy(arg);
+ SPI_finish();
+ elog(ERROR, "RASTER_nMapAlgebraExpr: Could not allocate memory for expression parameter %d", exprpos[i]);
+ 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';
+
+ POSTGIS_RT_DEBUGF(3, "sql #%d: %s", exprpos[i], sql);
+
+ /* prepared plan */
+ if (arg->callback.expr[i].spi_argcount) {
+ Oid *argtype = (Oid *) palloc(arg->callback.expr[i].spi_argcount * sizeof(Oid));
+ POSTGIS_RT_DEBUGF(3, "expression parameter %d is a prepared plan", exprpos[i]);
+ if (argtype == NULL) {
+ pfree(sql);
+ rtpg_nmapalgebraexpr_arg_destroy(arg);
+ SPI_finish();
+ elog(ERROR, "RASTER_nMapAlgebraExpr: Could not allocate memory for prepared plan argtypes of expression parameter %d", exprpos[i]);
+ PG_RETURN_NULL();
+ }
+
+ /* specify datatypes of parameters */
+ for (j = 0, k = 0; j < argkwcount; j++) {
+ if (arg->callback.expr[i].spi_argpos[j] < 1) continue;
+
+ /* positions are INT4 */
+ if (
+ (strstr(argkw[j], "[rast.x]") != NULL) ||
+ (strstr(argkw[j], "[rast.y]") != NULL) ||
+ (strstr(argkw[j], "[rast1.x]") != NULL) ||
+ (strstr(argkw[j], "[rast1.y]") != NULL) ||
+ (strstr(argkw[j], "[rast2.x]") != NULL) ||
+ (strstr(argkw[j], "[rast2.y]") != NULL)
+ )
+ argtype[k] = INT4OID;
+ /* everything else is FLOAT8 */
+ else
+ argtype[k] = FLOAT8OID;
+
+ k++;
+ }
+
+ arg->callback.expr[i].spi_plan = SPI_prepare(sql, arg->callback.expr[i].spi_argcount, argtype);
+ pfree(argtype);
+ pfree(sql);
+
+ if (arg->callback.expr[i].spi_plan == NULL) {
+ rtpg_nmapalgebraexpr_arg_destroy(arg);
+ SPI_finish();
+ elog(ERROR, "RASTER_nMapAlgebraExpr: Could not create prepared plan of expression parameter %d", exprpos[i]);
+ PG_RETURN_NULL();
+ }
+ }
+ /* no args, just execute query */
+ else {
+ POSTGIS_RT_DEBUGF(3, "expression parameter %d has no args, simply executing", exprpos[i]);
+ err = SPI_execute(sql, TRUE, 0);
+ pfree(sql);
+
+ if (err != SPI_OK_SELECT || SPI_tuptable == NULL || SPI_processed != 1) {
+ rtpg_nmapalgebraexpr_arg_destroy(arg);
+ SPI_finish();
+ elog(ERROR, "RASTER_nMapAlgebraExpr: Could not evaluate expression parameter %d", exprpos[i]);
+ 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) {
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ rtpg_nmapalgebraexpr_arg_destroy(arg);
+ SPI_finish();
+ elog(ERROR, "RASTER_nMapAlgebraExpr: Could not get result of expression parameter %d", exprpos[i]);
+ PG_RETURN_NULL();
+ }
+
+ if (!isnull) {
+ arg->callback.expr[i].hasval = 1;
+ arg->callback.expr[i].val = DatumGetFloat8(datum);
+ }
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ }
+ }
+
+ /* determine nodataval and possibly pixtype */
+ /* band to check */
+ switch (arg->bandarg->extenttype) {
+ case ET_LAST:
+ case ET_SECOND:
+ if (numraster > 1)
+ i = 1;
+ else
+ i = 0;
+ break;
+ default:
+ i = 0;
+ break;
+ }
+ /* find first viable band */
+ if (!arg->bandarg->hasband[i]) {
+ for (i = 0; i < numraster; i++) {
+ if (arg->bandarg->hasband[i])
+ break;
+ }
+ if (i >= numraster)
+ i = numraster - 1;
+ }
+ band = rt_raster_get_band(arg->bandarg->raster[i], arg->bandarg->nband[i]);
+
+ /* set pixel type if PT_END */
+ if (arg->bandarg->pixtype == PT_END)
+ arg->bandarg->pixtype = rt_band_get_pixtype(band);
+
+ /* set hasnodata and nodataval */
+ arg->bandarg->hasnodata = 1;
+ if (rt_band_get_hasnodata_flag(band))
+ rt_band_get_nodata(band, &(arg->bandarg->nodataval));
+ else
+ arg->bandarg->nodataval = rt_band_get_min_value(band);
+
+ POSTGIS_RT_DEBUGF(4, "pixtype, hasnodata, nodataval: %s, %d, %f", rt_pixtype_name(arg->bandarg->pixtype), arg->bandarg->hasnodata, arg->bandarg->nodataval);
+
+ /* init itrset */
+ itrset = palloc(sizeof(struct rt_iterator_t) * numraster);
+ if (itrset == NULL) {
+ rtpg_nmapalgebraexpr_arg_destroy(arg);
+ SPI_finish();
+ elog(ERROR, "RASTER_nMapAlgebra: Could not allocate memory for iterator arguments");
+ PG_RETURN_NULL();
+ }
+
+ /* set itrset */
+ for (i = 0; i < numraster; i++) {
+ itrset[i].raster = arg->bandarg->raster[i];
+ itrset[i].nband = arg->bandarg->nband[i];
+ itrset[i].nbnodata = 1;
+ }
+
+ /* pass everything to iterator */
+ err = rt_raster_iterator(
+ itrset, numraster,
+ arg->bandarg->extenttype, arg->bandarg->cextent,
+ arg->bandarg->pixtype,
+ arg->bandarg->hasnodata, arg->bandarg->nodataval,
+ 0, 0,
+ &(arg->callback),
+ rtpg_nmapalgebraexpr_callback,
+ &raster
+ );
+
+ pfree(itrset);
+ rtpg_nmapalgebraexpr_arg_destroy(arg);
+
+ if (err != ES_NONE) {
+ SPI_finish();
+ elog(ERROR, "RASTER_nMapAlgebraExpr: Could not run raster iterator function");
+ PG_RETURN_NULL();
+ }
+ else if (raster == NULL) {
+ SPI_finish();
+ PG_RETURN_NULL();
+ }
+
+ /* switch to prior memory context to ensure memory allocated in correct context */
+ MemoryContextSwitchTo(mainMemCtx);
+
+ pgraster = rt_raster_serialize(raster);
+ rt_raster_destroy(raster);
+
+ /* finish SPI */
+ SPI_finish();
+
+ if (!pgraster)
+ PG_RETURN_NULL();
+
+ SET_VARSIZE(pgraster, pgraster->size);
+ PG_RETURN_POINTER(pgraster);
+}
+
+/* ---------------------------------------------------------------- */
+/* ST_Union aggregate functions */
+/* ---------------------------------------------------------------- */
+
+typedef enum {
+ UT_LAST = 0,
+ UT_FIRST,
+ UT_MIN,
+ UT_MAX,
+ UT_COUNT,
+ UT_SUM,
+ UT_MEAN,
+ UT_RANGE
+} rtpg_union_type;
+
+/* internal function translating text of UNION type to enum */
+static rtpg_union_type rtpg_uniontype_index_from_name(const char *cutype) {
+ assert(cutype && strlen(cutype) > 0);
+
+ if (strcmp(cutype, "LAST") == 0)
+ return UT_LAST;
+ else if (strcmp(cutype, "FIRST") == 0)
+ return UT_FIRST;
+ else if (strcmp(cutype, "MIN") == 0)
+ return UT_MIN;
+ else if (strcmp(cutype, "MAX") == 0)
+ return UT_MAX;
+ else if (strcmp(cutype, "COUNT") == 0)
+ return UT_COUNT;
+ else if (strcmp(cutype, "SUM") == 0)
+ return UT_SUM;
+ else if (strcmp(cutype, "MEAN") == 0)
+ return UT_MEAN;
+ else if (strcmp(cutype, "RANGE") == 0)
+ return UT_RANGE;
+
+ return UT_LAST;
+}
+
+typedef struct rtpg_union_band_arg_t *rtpg_union_band_arg;
+struct rtpg_union_band_arg_t {
+ int nband; /* source raster's band index, 0-based */
+ rtpg_union_type uniontype;
+
+ int numraster;
+ rt_raster *raster;
+};
+
+typedef struct rtpg_union_arg_t *rtpg_union_arg;
+struct rtpg_union_arg_t {
+ int numband; /* number of bandargs */
+ rtpg_union_band_arg bandarg;
+};
+
+static void rtpg_union_arg_destroy(rtpg_union_arg arg) {
+ int i = 0;
+ int j = 0;
+ int k = 0;
+
+ if (arg->bandarg != NULL) {
+ for (i = 0; i < arg->numband; i++) {
+ if (!arg->bandarg[i].numraster)
+ continue;
+
+ for (j = 0; j < arg->bandarg[i].numraster; j++) {
+ if (arg->bandarg[i].raster[j] == NULL)
+ continue;
+
+ for (k = rt_raster_get_num_bands(arg->bandarg[i].raster[j]) - 1; k >= 0; k--)
+ rt_band_destroy(rt_raster_get_band(arg->bandarg[i].raster[j], k));
+ rt_raster_destroy(arg->bandarg[i].raster[j]);
+ }
+
+ pfree(arg->bandarg[i].raster);
+ }
+
+ pfree(arg->bandarg);
+ }
+
+ pfree(arg);
+}
+
+static int rtpg_union_callback(
+ rt_iterator_arg arg, void *userarg,
+ double *value, int *nodata
+) {
+ rtpg_union_type *utype = (rtpg_union_type *) userarg;
+
+ if (arg == NULL)
+ return 0;
+
+ if (
+ arg->rasters != 2 ||
+ arg->rows != 1 ||
+ arg->columns != 1
+ ) {
+ elog(ERROR, "rtpg_union_callback: Invalid arguments passed to callback");
+ return 0;
+ }
+
+ *value = 0;
+ *nodata = 0;
+
+ /* handle NODATA situations except for COUNT, which is a special case */
+ if (*utype != UT_COUNT) {
+ /* both NODATA */
+ if (arg->nodata[0][0][0] && arg->nodata[1][0][0]) {
+ *nodata = 1;
+ POSTGIS_RT_DEBUGF(4, "value, nodata = %f, %d", *value, *nodata);
+ return 1;
+ }
+ /* second NODATA */
+ else if (!arg->nodata[0][0][0] && arg->nodata[1][0][0]) {
+ *value = arg->values[0][0][0];
+ POSTGIS_RT_DEBUGF(4, "value, nodata = %f, %d", *value, *nodata);
+ return 1;
+ }
+ /* first NODATA */
+ else if (arg->nodata[0][0][0] && !arg->nodata[1][0][0]) {
+ *value = arg->values[1][0][0];
+ POSTGIS_RT_DEBUGF(4, "value, nodata = %f, %d", *value, *nodata);
+ return 1;
+ }
+ }
+
+ switch (*utype) {
+ case UT_FIRST:
+ *value = arg->values[0][0][0];
+ break;
+ case UT_MIN:
+ if (arg->values[0][0][0] < arg->values[1][0][0])
+ *value = arg->values[0][0][0];
+ else
+ *value = arg->values[1][0][0];
+ break;
+ case UT_MAX:
+ if (arg->values[0][0][0] > arg->values[1][0][0])
+ *value = arg->values[0][0][0];
+ else
+ *value = arg->values[1][0][0];
+ break;
+ case UT_COUNT:
+ /* both NODATA */
+ if (arg->nodata[0][0][0] && arg->nodata[1][0][0])
+ *value = 0;
+ /* second NODATA */
+ else if (!arg->nodata[0][0][0] && arg->nodata[1][0][0])
+ *value = arg->values[0][0][0];
+ /* first NODATA */
+ else if (arg->nodata[0][0][0] && !arg->nodata[1][0][0])
+ *value = 1;
+ /* has value, increment */
+ else
+ *value = arg->values[0][0][0] + 1;
+ break;
+ case UT_SUM:
+ *value = arg->values[0][0][0] + arg->values[1][0][0];
+ break;
+ case UT_MEAN:
+ case UT_RANGE:
+ break;
+ case UT_LAST:
+ default:
+ *value = arg->values[1][0][0];
+ break;
+ }
+
+ POSTGIS_RT_DEBUGF(4, "value, nodata = %f, %d", *value, *nodata);
+
+
+ return 1;
+}
+
+static int rtpg_union_mean_callback(
+ rt_iterator_arg arg, void *userarg,
+ double *value, int *nodata
+) {
+ if (arg == NULL)
+ return 0;
+
+ if (
+ arg->rasters != 2 ||
+ arg->rows != 1 ||
+ arg->columns != 1
+ ) {
+ elog(ERROR, "rtpg_union_mean_callback: Invalid arguments passed to callback");
+ return 0;
+ }
+
+ *value = 0;
+ *nodata = 1;
+
+ POSTGIS_RT_DEBUGF(4, "rast0: %f %d", arg->values[0][0][0], arg->nodata[0][0][0]);
+ POSTGIS_RT_DEBUGF(4, "rast1: %f %d", arg->values[1][0][0], arg->nodata[1][0][0]);
+
+ if (
+ !arg->nodata[0][0][0] &&
+ FLT_NEQ(arg->values[0][0][0], 0) &&
+ !arg->nodata[1][0][0]
+ ) {
+ *value = arg->values[1][0][0] / arg->values[0][0][0];
+ *nodata = 0;
+ }
+
+ POSTGIS_RT_DEBUGF(4, "value, nodata = (%f, %d)", *value, *nodata);
+
+ return 1;
+}
+
+static int rtpg_union_range_callback(
+ rt_iterator_arg arg, void *userarg,
+ double *value, int *nodata
+) {
+ if (arg == NULL)
+ return 0;
+
+ if (
+ arg->rasters != 2 ||
+ arg->rows != 1 ||
+ arg->columns != 1
+ ) {
+ elog(ERROR, "rtpg_union_range_callback: Invalid arguments passed to callback");
+ return 0;
+ }
+
+ *value = 0;
+ *nodata = 1;
+
+ POSTGIS_RT_DEBUGF(4, "rast0: %f %d", arg->values[0][0][0], arg->nodata[0][0][0]);
+ POSTGIS_RT_DEBUGF(4, "rast1: %f %d", arg->values[1][0][0], arg->nodata[1][0][0]);
+
+ if (
+ !arg->nodata[0][0][0] &&
+ !arg->nodata[1][0][0]
+ ) {
+ *value = arg->values[1][0][0] - arg->values[0][0][0];
+ *nodata = 0;
+ }
+
+ POSTGIS_RT_DEBUGF(4, "value, nodata = (%f, %d)", *value, *nodata);
+
+ return 1;
+}
+
+/* called for ST_Union(raster, unionarg[]) */
+static int rtpg_union_unionarg_process(rtpg_union_arg arg, ArrayType *array) {
+ Oid etype;
+ Datum *e;
+ bool *nulls;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ int n = 0;
+
+ HeapTupleHeader tup;
+ bool isnull;
+ Datum tupv;
+
+ int i;
+ int nband = 1;
+ char *utypename = NULL;
+ rtpg_union_type utype = UT_LAST;
+
+ etype = ARR_ELEMTYPE(array);
+ get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
+
+ deconstruct_array(
+ array,
+ etype,
+ typlen, typbyval, typalign,
+ &e, &nulls, &n
+ );
+
+ if (!n) {
+ elog(ERROR, "rtpg_union_unionarg_process: Invalid argument for unionarg");
+ return 0;
+ }
+
+ /* prep arg */
+ arg->numband = n;
+ arg->bandarg = palloc(sizeof(struct rtpg_union_band_arg_t) * arg->numband);
+ if (arg->bandarg == NULL) {
+ elog(ERROR, "rtpg_union_unionarg_process: Could not allocate memory for band information");
+ return 0;
+ }
+
+ /* process each element */
+ for (i = 0; i < n; i++) {
+ if (nulls[i]) {
+ arg->numband--;
+ continue;
+ }
+
+ POSTGIS_RT_DEBUGF(4, "Processing unionarg at index %d", i);
+
+ /* each element is a tuple */
+ tup = (HeapTupleHeader) DatumGetPointer(e[i]);
+ if (NULL == tup) {
+ elog(ERROR, "rtpg_union_unionarg_process: Invalid argument for unionarg");
+ return 0;
+ }
+
+ /* first element, bandnum */
+ tupv = GetAttributeByName(tup, "nband", &isnull);
+ if (isnull) {
+ nband = i + 1;
+ elog(NOTICE, "First argument (nband) of unionarg is NULL. Assuming nband = %d", nband);
+ }
+ else
+ nband = DatumGetInt32(tupv);
+
+ if (nband < 1) {
+ elog(ERROR, "rtpg_union_unionarg_process: Band number must be greater than zero (1-based)");
+ return 0;
+ }
+
+ /* second element, uniontype */
+ tupv = GetAttributeByName(tup, "uniontype", &isnull);
+ if (isnull) {
+ elog(NOTICE, "Second argument (uniontype) of unionarg is NULL. Assuming uniontype = LAST");
+ utype = UT_LAST;
+ }
+ else {
+ utypename = text_to_cstring((text *) DatumGetPointer(tupv));
+ utype = rtpg_uniontype_index_from_name(rtpg_strtoupper(utypename));
+ }
+
+ arg->bandarg[i].uniontype = utype;
+ arg->bandarg[i].nband = nband - 1;
+ arg->bandarg[i].raster = NULL;
+
+ if (
+ utype != UT_MEAN &&
+ utype != UT_RANGE
+ ) {
+ arg->bandarg[i].numraster = 1;
+ }
+ else
+ arg->bandarg[i].numraster = 2;
+ }
+
+ if (arg->numband < n) {
+ arg->bandarg = repalloc(arg->bandarg, sizeof(struct rtpg_union_band_arg_t) * arg->numband);
+ if (arg->bandarg == NULL) {
+ elog(ERROR, "rtpg_union_unionarg_process: Could not reallocate memory for band information");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* called for ST_Union(raster) */
+static int rtpg_union_noarg(rtpg_union_arg arg, rt_raster raster) {
+ int numbands;
+ int i;
+
+ if (rt_raster_is_empty(raster))
+ return 1;
+
+ numbands = rt_raster_get_num_bands(raster);
+ if (numbands <= arg->numband)
+ return 1;
+
+ /* more bands to process */
+ POSTGIS_RT_DEBUG(4, "input raster has more bands, adding more bandargs");
+ if (arg->numband)
+ arg->bandarg = repalloc(arg->bandarg, sizeof(struct rtpg_union_band_arg_t) * numbands);
+ else
+ arg->bandarg = palloc(sizeof(struct rtpg_union_band_arg_t) * numbands);
+ if (arg->bandarg == NULL) {
+ elog(ERROR, "rtpg_union_noarg: Could not reallocate memory for band information");
+ return 0;
+ }
+
+ i = arg->numband;
+ arg->numband = numbands;
+ for (; i < arg->numband; i++) {
+ POSTGIS_RT_DEBUGF(4, "Adding bandarg for band at index %d", i);
+ arg->bandarg[i].uniontype = UT_LAST;
+ arg->bandarg[i].nband = i;
+ arg->bandarg[i].numraster = 1;
+
+ arg->bandarg[i].raster = (rt_raster *) palloc(sizeof(rt_raster) * arg->bandarg[i].numraster);
+ if (arg->bandarg[i].raster == NULL) {
+ elog(ERROR, "rtpg_union_noarg: Could not allocate memory for working rasters");
+ return 0;
+ }
+ memset(arg->bandarg[i].raster, 0, sizeof(rt_raster) * arg->bandarg[i].numraster);
+
+ /* add new working rt_raster but only if working raster already exists */
+ if (!rt_raster_is_empty(arg->bandarg[0].raster[0])) {
+ arg->bandarg[i].raster[0] = rt_raster_clone(arg->bandarg[0].raster[0], 0); /* shallow clone */
+ if (arg->bandarg[i].raster[0] == NULL) {
+ elog(ERROR, "rtpg_union_noarg: Could not create working raster");
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+/* UNION aggregate transition function */
+PG_FUNCTION_INFO_V1(RASTER_union_transfn);
+Datum RASTER_union_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggcontext;
+ MemoryContext oldcontext;
+ rtpg_union_arg iwr = NULL;
+ int skiparg = 0;
+
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ rt_raster _raster = NULL;
+ rt_band _band = NULL;
+ int nband = 1;
+ int noerr = 1;
+ int isempty[2] = {0};
+ int hasband[2] = {0};
+ int nargs = 0;
+ double _offset[4] = {0.};
+ int nbnodata = 0; /* 1 if adding bands */
+
+ int i = 0;
+ int j = 0;
+ int k = 0;
+
+ rt_iterator itrset;
+ char *utypename = NULL;
+ rtpg_union_type utype = UT_LAST;
+ rt_pixtype pixtype = PT_END;
+ int hasnodata = 1;
+ double nodataval = 0;
+
+ rt_raster iraster = NULL;
+ rt_band iband = NULL;
+ int reuserast = 0;
+ int y = 0;
+ uint16_t _dim[2] = {0};
+ void *vals = NULL;
+ uint16_t nvals = 0;
+
+ POSTGIS_RT_DEBUG(3, "Starting...");
+
+ /* cannot be called directly as this is exclusive aggregate function */
+ if (!AggCheckCallContext(fcinfo, &aggcontext)) {
+ elog(ERROR, "RASTER_union_transfn: Cannot be called in a non-aggregate context");
+ PG_RETURN_NULL();
+ }
+
+ /* switch to aggcontext */
+ oldcontext = MemoryContextSwitchTo(aggcontext);
+
+ if (PG_ARGISNULL(0)) {
+ POSTGIS_RT_DEBUG(3, "Creating state variable");
+ /* allocate container in aggcontext */
+ iwr = palloc(sizeof(struct rtpg_union_arg_t));
+ if (iwr == NULL) {
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_union_transfn: Could not allocate memory for state variable");
+ PG_RETURN_NULL();
+ }
+
+ iwr->numband = 0;
+ iwr->bandarg = NULL;
+
+ skiparg = 0;
+ }
+ else {
+ POSTGIS_RT_DEBUG(3, "State variable already exists");
+ iwr = (rtpg_union_arg) PG_GETARG_POINTER(0);
+ skiparg = 1;
+ }
+
+ /* raster arg is NOT NULL */
+ if (!PG_ARGISNULL(1)) {
+ /* deserialize raster */
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+ /* Get raster object */
+ raster = rt_raster_deserialize(pgraster, FALSE);
+ if (raster == NULL) {
+
+ rtpg_union_arg_destroy(iwr);
+ PG_FREE_IF_COPY(pgraster, 1);
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_union_transfn: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+ }
+
+ /* process additional args if needed */
+ nargs = PG_NARGS();
+ POSTGIS_RT_DEBUGF(4, "nargs = %d", nargs);
+ if (nargs > 2) {
+ POSTGIS_RT_DEBUG(4, "processing additional arguments");
+
+ /* if more than 2 arguments, determine the type of argument 3 */
+ /* band number, UNION type or unionarg */
+ if (!PG_ARGISNULL(2)) {
+ Oid calltype = get_fn_expr_argtype(fcinfo->flinfo, 2);
+
+ switch (calltype) {
+ /* UNION type */
+ case TEXTOID: {
+ int idx = 0;
+ int numband = 0;
+
+ POSTGIS_RT_DEBUG(4, "Processing arg 3 as UNION type");
+ nbnodata = 1;
+
+ utypename = text_to_cstring(PG_GETARG_TEXT_P(2));
+ utype = rtpg_uniontype_index_from_name(rtpg_strtoupper(utypename));
+ POSTGIS_RT_DEBUGF(4, "Union type: %s", utypename);
+
+ POSTGIS_RT_DEBUGF(4, "iwr->numband: %d", iwr->numband);
+ /* see if we need to append new bands */
+ if (raster) {
+ idx = iwr->numband;
+ numband = rt_raster_get_num_bands(raster);
+ POSTGIS_RT_DEBUGF(4, "numband: %d", numband);
+
+ /* only worry about appended bands */
+ if (numband > iwr->numband)
+ iwr->numband = numband;
+ }
+
+ if (!iwr->numband)
+ iwr->numband = 1;
+ POSTGIS_RT_DEBUGF(4, "iwr->numband: %d", iwr->numband);
+ POSTGIS_RT_DEBUGF(4, "numband, idx: %d, %d", numband, idx);
+
+ /* bandarg set. only possible after the first call to function */
+ if (iwr->bandarg) {
+ /* only reallocate if new bands need to be added */
+ if (numband > idx) {
+ POSTGIS_RT_DEBUG(4, "Reallocating iwr->bandarg");
+ iwr->bandarg = repalloc(iwr->bandarg, sizeof(struct rtpg_union_band_arg_t) * iwr->numband);
+ }
+ /* prevent initial values step happening below */
+ else
+ idx = iwr->numband;
+ }
+ /* bandarg not set, first call to function */
+ else {
+ POSTGIS_RT_DEBUG(4, "Allocating iwr->bandarg");
+ iwr->bandarg = palloc(sizeof(struct rtpg_union_band_arg_t) * iwr->numband);
+ }
+ if (iwr->bandarg == NULL) {
+
+ rtpg_union_arg_destroy(iwr);
+ if (raster != NULL) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 1);
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_union_transfn: Could not allocate memory for band information");
+ PG_RETURN_NULL();
+ }
+
+ /* set initial values for bands that are "new" */
+ for (i = idx; i < iwr->numband; i++) {
+ iwr->bandarg[i].uniontype = utype;
+ iwr->bandarg[i].nband = i;
+
+ if (
+ utype == UT_MEAN ||
+ utype == UT_RANGE
+ ) {
+ iwr->bandarg[i].numraster = 2;
+ }
+ else
+ iwr->bandarg[i].numraster = 1;
+ iwr->bandarg[i].raster = NULL;
+ }
+
+ break;
+ }
+ /* band number */
+ case INT2OID:
+ case INT4OID:
+ if (skiparg)
+ break;
+
+ POSTGIS_RT_DEBUG(4, "Processing arg 3 as band number");
+ nband = PG_GETARG_INT32(2);
+ if (nband < 1) {
+
+ rtpg_union_arg_destroy(iwr);
+ if (raster != NULL) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 1);
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_union_transfn: Band number must be greater than zero (1-based)");
+ PG_RETURN_NULL();
+ }
+
+ iwr->numband = 1;
+ iwr->bandarg = palloc(sizeof(struct rtpg_union_band_arg_t) * iwr->numband);
+ if (iwr->bandarg == NULL) {
+
+ rtpg_union_arg_destroy(iwr);
+ if (raster != NULL) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 1);
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_union_transfn: Could not allocate memory for band information");
+ PG_RETURN_NULL();
+ }
+
+ iwr->bandarg[0].uniontype = UT_LAST;
+ iwr->bandarg[0].nband = nband - 1;
+
+ iwr->bandarg[0].numraster = 1;
+ iwr->bandarg[0].raster = NULL;
+ break;
+ /* only other type allowed is unionarg */
+ default:
+ if (skiparg)
+ break;
+
+ POSTGIS_RT_DEBUG(4, "Processing arg 3 as unionarg");
+ if (!rtpg_union_unionarg_process(iwr, PG_GETARG_ARRAYTYPE_P(2))) {
+
+ rtpg_union_arg_destroy(iwr);
+ if (raster != NULL) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 1);
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_union_transfn: Could not process unionarg");
+ PG_RETURN_NULL();
+ }
+
+ break;
+ }
+ }
+
+ /* UNION type */
+ if (nargs > 3 && !PG_ARGISNULL(3)) {
+ utypename = text_to_cstring(PG_GETARG_TEXT_P(3));
+ utype = rtpg_uniontype_index_from_name(rtpg_strtoupper(utypename));
+ iwr->bandarg[0].uniontype = utype;
+ POSTGIS_RT_DEBUGF(4, "Union type: %s", utypename);
+
+ if (
+ utype == UT_MEAN ||
+ utype == UT_RANGE
+ ) {
+ iwr->bandarg[0].numraster = 2;
+ }
+ }
+
+ /* allocate space for pointers to rt_raster */
+ for (i = 0; i < iwr->numband; i++) {
+ POSTGIS_RT_DEBUGF(4, "iwr->bandarg[%d].raster @ %p", i, iwr->bandarg[i].raster);
+
+ /* no need to allocate */
+ if (iwr->bandarg[i].raster != NULL)
+ continue;
+
+ POSTGIS_RT_DEBUGF(4, "Allocating space for working rasters of band %d", i);
+
+ iwr->bandarg[i].raster = (rt_raster *) palloc(sizeof(rt_raster) * iwr->bandarg[i].numraster);
+ if (iwr->bandarg[i].raster == NULL) {
+
+ rtpg_union_arg_destroy(iwr);
+ if (raster != NULL) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 1);
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_union_transfn: Could not allocate memory for working raster(s)");
+ PG_RETURN_NULL();
+ }
+
+ memset(iwr->bandarg[i].raster, 0, sizeof(rt_raster) * iwr->bandarg[i].numraster);
+
+ /* add new working rt_raster but only if working raster already exists */
+ if (i > 0 && !rt_raster_is_empty(iwr->bandarg[0].raster[0])) {
+ for (j = 0; j < iwr->bandarg[i].numraster; j++) {
+ iwr->bandarg[i].raster[j] = rt_raster_clone(iwr->bandarg[0].raster[0], 0); /* shallow clone */
+ if (iwr->bandarg[i].raster[j] == NULL) {
+
+ rtpg_union_arg_destroy(iwr);
+ if (raster != NULL) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 1);
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_union_transfn: Could not create working raster");
+ PG_RETURN_NULL();
+ }
+ }
+ }
+ }
+ }
+ /* only raster, no additional args */
+ /* only do this if raster isn't empty */
+ else {
+ POSTGIS_RT_DEBUG(4, "no additional args, checking input raster");
+ nbnodata = 1;
+ if (!rtpg_union_noarg(iwr, raster)) {
+
+ rtpg_union_arg_destroy(iwr);
+ if (raster != NULL) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 1);
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_union_transfn: Could not check and balance number of bands");
+ PG_RETURN_NULL();
+ }
+ }
+
+ /* init itrset */
+ itrset = palloc(sizeof(struct rt_iterator_t) * 2);
+ if (itrset == NULL) {
+
+ rtpg_union_arg_destroy(iwr);
+ if (raster != NULL) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 1);
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_union_transfn: Could not allocate memory for iterator arguments");
+ PG_RETURN_NULL();
+ }
+
+ /* by band to UNION */
+ for (i = 0; i < iwr->numband; i++) {
+
+ /* by raster */
+ for (j = 0; j < iwr->bandarg[i].numraster; j++) {
+ reuserast = 0;
+
+ /* type of union */
+ utype = iwr->bandarg[i].uniontype;
+
+ /* raster flags */
+ isempty[0] = rt_raster_is_empty(iwr->bandarg[i].raster[j]);
+ isempty[1] = rt_raster_is_empty(raster);
+
+ if (!isempty[0])
+ hasband[0] = rt_raster_has_band(iwr->bandarg[i].raster[j], 0);
+ if (!isempty[1])
+ hasband[1] = rt_raster_has_band(raster, iwr->bandarg[i].nband);
+
+ /* determine pixtype, hasnodata and nodataval */
+ _band = NULL;
+ if (!isempty[0] && hasband[0])
+ _band = rt_raster_get_band(iwr->bandarg[i].raster[j], 0);
+ else if (!isempty[1] && hasband[1])
+ _band = rt_raster_get_band(raster, iwr->bandarg[i].nband);
+ else {
+ pixtype = PT_64BF;
+ hasnodata = 1;
+ nodataval = rt_pixtype_get_min_value(pixtype);
+ }
+ if (_band != NULL) {
+ pixtype = rt_band_get_pixtype(_band);
+ hasnodata = 1;
+ if (rt_band_get_hasnodata_flag(_band))
+ rt_band_get_nodata(_band, &nodataval);
+ else
+ nodataval = rt_band_get_min_value(_band);
+ }
+
+ /* UT_MEAN and UT_RANGE require two passes */
+ /* UT_MEAN: first for UT_COUNT and second for UT_SUM */
+ if (iwr->bandarg[i].uniontype == UT_MEAN) {
+ /* first pass, UT_COUNT */
+ if (j < 1)
+ utype = UT_COUNT;
+ else
+ utype = UT_SUM;
+ }
+ /* UT_RANGE: first for UT_MIN and second for UT_MAX */
+ else if (iwr->bandarg[i].uniontype == UT_RANGE) {
+ /* first pass, UT_MIN */
+ if (j < 1)
+ utype = UT_MIN;
+ else
+ utype = UT_MAX;
+ }
+
+ /* force band settings for UT_COUNT */
+ if (utype == UT_COUNT) {
+ pixtype = PT_32BUI;
+ hasnodata = 0;
+ nodataval = 0;
+ }
+
+ POSTGIS_RT_DEBUGF(4, "(pixtype, hasnodata, nodataval) = (%s, %d, %f)", rt_pixtype_name(pixtype), hasnodata, nodataval);
+
+ /* set itrset */
+ itrset[0].raster = iwr->bandarg[i].raster[j];
+ itrset[0].nband = 0;
+ itrset[1].raster = raster;
+ itrset[1].nband = iwr->bandarg[i].nband;
+
+ /* allow use NODATA to replace missing bands */
+ if (nbnodata) {
+ itrset[0].nbnodata = 1;
+ itrset[1].nbnodata = 1;
+ }
+ /* missing bands are not ignored */
+ else {
+ itrset[0].nbnodata = 0;
+ itrset[1].nbnodata = 0;
+ }
+
+ /* if rasters AND bands are present, use copy approach */
+ if (!isempty[0] && !isempty[1] && hasband[0] && hasband[1]) {
+ POSTGIS_RT_DEBUG(3, "using line method");
+
+ /* generate empty out raster */
+ if (rt_raster_from_two_rasters(
+ iwr->bandarg[i].raster[j], raster,
+ ET_UNION,
+ &iraster, _offset
+ ) != ES_NONE) {
+
+ pfree(itrset);
+ rtpg_union_arg_destroy(iwr);
+ if (raster != NULL) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 1);
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_union_transfn: Could not create internal raster");
+ PG_RETURN_NULL();
+ }
+ POSTGIS_RT_DEBUGF(4, "_offset = %f, %f, %f, %f",
+ _offset[0], _offset[1], _offset[2], _offset[3]);
+
+ /* rasters are spatially the same? */
+ if (
+ rt_raster_get_width(iwr->bandarg[i].raster[j]) == rt_raster_get_width(iraster) &&
+ rt_raster_get_height(iwr->bandarg[i].raster[j]) == rt_raster_get_height(iraster)
+ ) {
+ double igt[6] = {0};
+ double gt[6] = {0};
+
+ rt_raster_get_geotransform_matrix(iwr->bandarg[i].raster[j], gt);
+ rt_raster_get_geotransform_matrix(iraster, igt);
+
+ reuserast = rt_util_same_geotransform_matrix(gt, igt);
+ }
+
+ /* use internal raster */
+ if (!reuserast) {
+ /* create band of same type */
+ if (rt_raster_generate_new_band(
+ iraster,
+ pixtype,
+ nodataval,
+ hasnodata, nodataval,
+ 0
+ ) == -1) {
+
+ pfree(itrset);
+ rtpg_union_arg_destroy(iwr);
+ rt_raster_destroy(iraster);
+ if (raster != NULL) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 1);
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_union_transfn: Could not add new band to internal raster");
+ PG_RETURN_NULL();
+ }
+ iband = rt_raster_get_band(iraster, 0);
+
+ /* copy working raster to output raster */
+ _dim[0] = rt_raster_get_width(iwr->bandarg[i].raster[j]);
+ _dim[1] = rt_raster_get_height(iwr->bandarg[i].raster[j]);
+ for (y = 0; y < _dim[1]; y++) {
+ POSTGIS_RT_DEBUGF(4, "Getting pixel line of working raster at (x, y, length) = (0, %d, %d)", y, _dim[0]);
+ if (rt_band_get_pixel_line(
+ _band,
+ 0, y,
+ _dim[0],
+ &vals, &nvals
+ ) != ES_NONE) {
+
+ pfree(itrset);
+ rtpg_union_arg_destroy(iwr);
+ rt_band_destroy(iband);
+ rt_raster_destroy(iraster);
+ if (raster != NULL) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 1);
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_union_transfn: Could not get pixel line from band of working raster");
+ PG_RETURN_NULL();
+ }
+
+ POSTGIS_RT_DEBUGF(4, "Setting pixel line at (x, y, length) = (%d, %d, %d)", (int) _offset[0], (int) _offset[1] + y, nvals);
+ if (rt_band_set_pixel_line(
+ iband,
+ (int) _offset[0], (int) _offset[1] + y,
+ vals, nvals
+ ) != ES_NONE) {
+
+ pfree(itrset);
+ rtpg_union_arg_destroy(iwr);
+ rt_band_destroy(iband);
+ rt_raster_destroy(iraster);
+ if (raster != NULL) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 1);
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_union_transfn: Could not set pixel line to band of internal raster");
+ PG_RETURN_NULL();
+ }
+ }
+ }
+ else {
+ rt_raster_destroy(iraster);
+ iraster = iwr->bandarg[i].raster[j];
+ iband = rt_raster_get_band(iraster, 0);
+ }
+
+ /* run iterator for extent of input raster */
+ noerr = rt_raster_iterator(
+ itrset, 2,
+ ET_LAST, NULL,
+ pixtype,
+ hasnodata, nodataval,
+ 0, 0,
+ &utype,
+ rtpg_union_callback,
+ &_raster
+ );
+ if (noerr != ES_NONE) {
+
+ pfree(itrset);
+ rtpg_union_arg_destroy(iwr);
+ if (!reuserast) {
+ rt_band_destroy(iband);
+ rt_raster_destroy(iraster);
+ }
+ if (raster != NULL) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 1);
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_union_transfn: Could not run raster iterator function");
+ PG_RETURN_NULL();
+ }
+
+ /* with iterator raster, copy data to output raster */
+ _band = rt_raster_get_band(_raster, 0);
+ _dim[0] = rt_raster_get_width(_raster);
+ _dim[1] = rt_raster_get_height(_raster);
+ for (y = 0; y < _dim[1]; y++) {
+ POSTGIS_RT_DEBUGF(4, "Getting pixel line of iterator raster at (x, y, length) = (0, %d, %d)", y, _dim[0]);
+ if (rt_band_get_pixel_line(
+ _band,
+ 0, y,
+ _dim[0],
+ &vals, &nvals
+ ) != ES_NONE) {
+
+ pfree(itrset);
+ rtpg_union_arg_destroy(iwr);
+ if (!reuserast) {
+ rt_band_destroy(iband);
+ rt_raster_destroy(iraster);
+ }
+ if (raster != NULL) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 1);
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_union_transfn: Could not get pixel line from band of working raster");
+ PG_RETURN_NULL();
+ }
+
+ POSTGIS_RT_DEBUGF(4, "Setting pixel line at (x, y, length) = (%d, %d, %d)", (int) _offset[2], (int) _offset[3] + y, nvals);
+ if (rt_band_set_pixel_line(
+ iband,
+ (int) _offset[2], (int) _offset[3] + y,
+ vals, nvals
+ ) != ES_NONE) {
+
+ pfree(itrset);
+ rtpg_union_arg_destroy(iwr);
+ if (!reuserast) {
+ rt_band_destroy(iband);
+ rt_raster_destroy(iraster);
+ }
+ if (raster != NULL) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 1);
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_union_transfn: Could not set pixel line to band of internal raster");
+ PG_RETURN_NULL();
+ }
+ }
+
+ /* free _raster */
+ rt_band_destroy(_band);
+ rt_raster_destroy(_raster);
+
+ /* replace working raster with output raster */
+ _raster = iraster;
+ }
+ else {
+ POSTGIS_RT_DEBUG(3, "using pixel method");
+
+ /* pass everything to iterator */
+ noerr = rt_raster_iterator(
+ itrset, 2,
+ ET_UNION, NULL,
+ pixtype,
+ hasnodata, nodataval,
+ 0, 0,
+ &utype,
+ rtpg_union_callback,
+ &_raster
+ );
+
+ if (noerr != ES_NONE) {
+
+ pfree(itrset);
+ rtpg_union_arg_destroy(iwr);
+ if (raster != NULL) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 1);
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_union_transfn: Could not run raster iterator function");
+ PG_RETURN_NULL();
+ }
+ }
+
+ /* replace working raster */
+ if (iwr->bandarg[i].raster[j] != NULL && !reuserast) {
+ for (k = rt_raster_get_num_bands(iwr->bandarg[i].raster[j]) - 1; k >= 0; k--)
+ rt_band_destroy(rt_raster_get_band(iwr->bandarg[i].raster[j], k));
+ rt_raster_destroy(iwr->bandarg[i].raster[j]);
+ }
+ iwr->bandarg[i].raster[j] = _raster;
+ }
+
+ }
+
+ pfree(itrset);
+ if (raster != NULL) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 1);
+ }
+
+ /* switch back to local context */
+ MemoryContextSwitchTo(oldcontext);
+
+ POSTGIS_RT_DEBUG(3, "Finished");
+
+ PG_RETURN_POINTER(iwr);
+}
+
+/* UNION aggregate final function */
+PG_FUNCTION_INFO_V1(RASTER_union_finalfn);
+Datum RASTER_union_finalfn(PG_FUNCTION_ARGS)
+{
+ rtpg_union_arg iwr;
+ rt_raster _rtn = NULL;
+ rt_raster _raster = NULL;
+ rt_pgraster *pgraster = NULL;
+
+ int i = 0;
+ int j = 0;
+ rt_iterator itrset = NULL;
+ rt_band _band = NULL;
+ int noerr = 1;
+ int status = 0;
+ rt_pixtype pixtype = PT_END;
+ int hasnodata = 0;
+ double nodataval = 0;
+
+ POSTGIS_RT_DEBUG(3, "Starting...");
+
+ /* cannot be called directly as this is exclusive aggregate function */
+ if (!AggCheckCallContext(fcinfo, NULL)) {
+ elog(ERROR, "RASTER_union_finalfn: Cannot be called in a non-aggregate context");
+ PG_RETURN_NULL();
+ }
+
+ /* NULL, return null */
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+
+ iwr = (rtpg_union_arg) PG_GETARG_POINTER(0);
+
+ /* init itrset */
+ itrset = palloc(sizeof(struct rt_iterator_t) * 2);
+ if (itrset == NULL) {
+ rtpg_union_arg_destroy(iwr);
+ elog(ERROR, "RASTER_union_finalfn: Could not allocate memory for iterator arguments");
+ PG_RETURN_NULL();
+ }
+
+ for (i = 0; i < iwr->numband; i++) {
+ if (
+ iwr->bandarg[i].uniontype == UT_MEAN ||
+ iwr->bandarg[i].uniontype == UT_RANGE
+ ) {
+ /* raster containing the SUM or MAX is at index 1 */
+ _band = rt_raster_get_band(iwr->bandarg[i].raster[1], 0);
+
+ pixtype = rt_band_get_pixtype(_band);
+ hasnodata = rt_band_get_hasnodata_flag(_band);
+ if (hasnodata)
+ rt_band_get_nodata(_band, &nodataval);
+ POSTGIS_RT_DEBUGF(4, "(pixtype, hasnodata, nodataval) = (%s, %d, %f)", rt_pixtype_name(pixtype), hasnodata, nodataval);
+
+ itrset[0].raster = iwr->bandarg[i].raster[0];
+ itrset[0].nband = 0;
+ itrset[1].raster = iwr->bandarg[i].raster[1];
+ itrset[1].nband = 0;
+
+ /* pass everything to iterator */
+ if (iwr->bandarg[i].uniontype == UT_MEAN) {
+ noerr = rt_raster_iterator(
+ itrset, 2,
+ ET_UNION, NULL,
+ pixtype,
+ hasnodata, nodataval,
+ 0, 0,
+ NULL,
+ rtpg_union_mean_callback,
+ &_raster
+ );
+ }
+ else if (iwr->bandarg[i].uniontype == UT_RANGE) {
+ noerr = rt_raster_iterator(
+ itrset, 2,
+ ET_UNION, NULL,
+ pixtype,
+ hasnodata, nodataval,
+ 0, 0,
+ NULL,
+ rtpg_union_range_callback,
+ &_raster
+ );
+ }
+
+ if (noerr != ES_NONE) {
+ pfree(itrset);
+ rtpg_union_arg_destroy(iwr);
+ if (_rtn != NULL)
+ rt_raster_destroy(_rtn);
+ elog(ERROR, "RASTER_union_finalfn: Could not run raster iterator function");
+ PG_RETURN_NULL();
+ }
+ }
+ else
+ _raster = iwr->bandarg[i].raster[0];
+
+ /* first band, _rtn doesn't exist */
+ if (i < 1) {
+ uint32_t bandNums[1] = {0};
+ _rtn = rt_raster_from_band(_raster, bandNums, 1);
+ status = (_rtn == NULL) ? -1 : 0;
+ }
+ else
+ status = rt_raster_copy_band(_rtn, _raster, 0, i);
+
+ POSTGIS_RT_DEBUG(4, "destroying source rasters");
+
+ /* destroy source rasters */
+ if (
+ iwr->bandarg[i].uniontype == UT_MEAN ||
+ iwr->bandarg[i].uniontype == UT_RANGE
+ ) {
+ rt_raster_destroy(_raster);
+ }
+
+ for (j = 0; j < iwr->bandarg[i].numraster; j++) {
+ if (iwr->bandarg[i].raster[j] == NULL)
+ continue;
+ rt_raster_destroy(iwr->bandarg[i].raster[j]);
+ iwr->bandarg[i].raster[j] = NULL;
+ }
+
+ if (status < 0) {
+ rtpg_union_arg_destroy(iwr);
+ rt_raster_destroy(_rtn);
+ elog(ERROR, "RASTER_union_finalfn: Could not add band to final raster");
+ PG_RETURN_NULL();
+ }
+ }
+
+ /* cleanup */
+ pfree(itrset);
+ rtpg_union_arg_destroy(iwr);
+
+ pgraster = rt_raster_serialize(_rtn);
+ rt_raster_destroy(_rtn);
+
+ POSTGIS_RT_DEBUG(3, "Finished");
+
+ if (!pgraster)
+ PG_RETURN_NULL();
+
+ SET_VARSIZE(pgraster, pgraster->size);
+ PG_RETURN_POINTER(pgraster);
+}
+
+/* ---------------------------------------------------------------- */
+/* Clip raster with geometry */
+/* ---------------------------------------------------------------- */
+
+typedef struct rtpg_clip_band_t *rtpg_clip_band;
+struct rtpg_clip_band_t {
+ int nband; /* band index */
+ int hasnodata; /* is there a user-specified NODATA? */
+ double nodataval; /* user-specified NODATA */
+};
+
+typedef struct rtpg_clip_arg_t *rtpg_clip_arg;
+struct rtpg_clip_arg_t {
+ rt_extenttype extenttype;
+ rt_raster raster;
+ rt_raster mask;
+
+ int numbands; /* number of bandargs */
+ rtpg_clip_band band;
+};
+
+static rtpg_clip_arg rtpg_clip_arg_init() {
+ rtpg_clip_arg arg = NULL;
+
+ arg = palloc(sizeof(struct rtpg_clip_arg_t));
+ if (arg == NULL) {
+ elog(ERROR, "rtpg_clip_arg_init: Could not allocate memory for function arguments");
+ return NULL;
+ }
+
+ arg->extenttype = ET_INTERSECTION;
+ arg->raster = NULL;
+ arg->mask = NULL;
+ arg->numbands = 0;
+ arg->band = NULL;
+
+ return arg;
+}
+
+static void rtpg_clip_arg_destroy(rtpg_clip_arg arg) {
+ if (arg->band != NULL)
+ pfree(arg->band);
+
+ if (arg->raster != NULL)
+ rt_raster_destroy(arg->raster);
+ if (arg->mask != NULL)
+ rt_raster_destroy(arg->mask);
+
+ pfree(arg);
+}
+
+static int rtpg_clip_callback(
+ rt_iterator_arg arg, void *userarg,
+ double *value, int *nodata
+) {
+ *value = 0;
+ *nodata = 0;
+
+ /* either is NODATA, output is NODATA */
+ if (arg->nodata[0][0][0] || arg->nodata[1][0][0])
+ *nodata = 1;
+ /* set to value */
+ else
+ *value = arg->values[0][0][0];
+
+ return 1;
+}
+
+PG_FUNCTION_INFO_V1(RASTER_clip);
+Datum RASTER_clip(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ LWGEOM *rastgeom = NULL;
+ double gt[6] = {0};
+ int srid = SRID_UNKNOWN;
+
+ rt_pgraster *pgrtn = NULL;
+ rt_raster rtn = NULL;
+
+ GSERIALIZED *gser = NULL;
+ LWGEOM *geom = NULL;
+ unsigned char *wkb = NULL;
+ size_t wkb_len;
+
+ ArrayType *array;
+ Oid etype;
+ Datum *e;
+ bool *nulls;
+
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+
+ int i = 0;
+ int j = 0;
+ int k = 0;
+ rtpg_clip_arg arg = NULL;
+ LWGEOM *tmpgeom = NULL;
+ rt_iterator itrset;
+
+ rt_raster _raster = NULL;
+ rt_band band = NULL;
+ rt_pixtype pixtype;
+ int hasnodata;
+ double nodataval;
+ int noerr = 0;
+
+ POSTGIS_RT_DEBUG(3, "Starting...");
+
+ /* raster or geometry is NULL, return NULL */
+ if (PG_ARGISNULL(0) || PG_ARGISNULL(2))
+ PG_RETURN_NULL();
+
+ /* init arg */
+ arg = rtpg_clip_arg_init();
+ if (arg == NULL) {
+ elog(ERROR, "RASTER_clip: Could not initialize argument structure");
+ PG_RETURN_NULL();
+ }
+
+ /* raster (0) */
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+ /* Get raster object */
+ arg->raster = rt_raster_deserialize(pgraster, FALSE);
+ if (arg->raster == NULL) {
+ rtpg_clip_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_clip: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ /* raster is empty, return empty raster */
+ if (rt_raster_is_empty(arg->raster)) {
+ elog(NOTICE, "Input raster is empty. Returning empty raster");
+
+ rtpg_clip_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ rtn = rt_raster_new(0, 0);
+ if (rtn == NULL) {
+ elog(ERROR, "RASTER_clip: Could not create empty raster");
+ PG_RETURN_NULL();
+ }
+
+ pgrtn = rt_raster_serialize(rtn);
+ rt_raster_destroy(rtn);
+ if (NULL == pgrtn)
+ PG_RETURN_NULL();
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+ /* metadata */
+ rt_raster_get_geotransform_matrix(arg->raster, gt);
+ srid = clamp_srid(rt_raster_get_srid(arg->raster));
+
+ /* geometry (2) */
+ gser = (GSERIALIZED *) PG_DETOAST_DATUM(PG_GETARG_DATUM(2));
+ geom = lwgeom_from_gserialized(gser);
+
+ /* Get a 2D version of the geometry if necessary */
+ if (lwgeom_ndims(geom) > 2) {
+ LWGEOM *geom2d = lwgeom_force_2d(geom);
+ lwgeom_free(geom);
+ geom = geom2d;
+ }
+
+ /* check that SRIDs match */
+ if (srid != clamp_srid(gserialized_get_srid(gser))) {
+ elog(NOTICE, "Geometry provided does not have the same SRID as the raster. Returning NULL");
+
+ rtpg_clip_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ lwgeom_free(geom);
+ PG_FREE_IF_COPY(gser, 2);
+
+ PG_RETURN_NULL();
+ }
+
+ /* crop (4) */
+ if (!PG_ARGISNULL(4) && !PG_GETARG_BOOL(4))
+ arg->extenttype = ET_FIRST;
+
+ /* get intersection geometry of input raster and input geometry */
+ if (rt_raster_get_convex_hull(arg->raster, &rastgeom) != ES_NONE) {
+
+ rtpg_clip_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ lwgeom_free(geom);
+ PG_FREE_IF_COPY(gser, 2);
+
+ elog(ERROR, "RASTER_clip: Could not get convex hull of raster");
+ PG_RETURN_NULL();
+ }
+
+ tmpgeom = lwgeom_intersection(rastgeom, geom);
+ lwgeom_free(rastgeom);
+ lwgeom_free(geom);
+ PG_FREE_IF_COPY(gser, 2);
+ geom = tmpgeom;
+
+ /* intersection is empty AND extent type is INTERSECTION, return empty */
+ if (lwgeom_is_empty(geom) && arg->extenttype == ET_INTERSECTION) {
+ elog(NOTICE, "The input raster and input geometry do not intersect. Returning empty raster");
+
+ rtpg_clip_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ lwgeom_free(geom);
+
+ rtn = rt_raster_new(0, 0);
+ if (rtn == NULL) {
+ elog(ERROR, "RASTER_clip: Could not create empty raster");
+ PG_RETURN_NULL();
+ }
+
+ pgrtn = rt_raster_serialize(rtn);
+ rt_raster_destroy(rtn);
+ if (NULL == pgrtn)
+ PG_RETURN_NULL();
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+ /* nband (1) */
+ 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_clip_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ lwgeom_free(geom);
+ elog(ERROR, "RASTER_clip: Invalid data type for band indexes");
+ PG_RETURN_NULL();
+ break;
+ }
+
+ deconstruct_array(
+ array, etype,
+ typlen, typbyval, typalign,
+ &e, &nulls, &(arg->numbands)
+ );
+
+ arg->band = palloc(sizeof(struct rtpg_clip_band_t) * arg->numbands);
+ if (arg->band == NULL) {
+ rtpg_clip_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ lwgeom_free(geom);
+ elog(ERROR, "RASTER_clip: Could not allocate memory for band arguments");
+ PG_RETURN_NULL();
+ }
+
+ for (i = 0, j = 0; i < arg->numbands; i++) {
+ if (nulls[i]) continue;
+
+ switch (etype) {
+ case INT2OID:
+ arg->band[j].nband = DatumGetInt16(e[i]) - 1;
+ break;
+ case INT4OID:
+ arg->band[j].nband = DatumGetInt32(e[i]) - 1;
+ break;
+ }
+
+ j++;
+ }
+
+ if (j < arg->numbands) {
+ arg->band = repalloc(arg->band, sizeof(struct rtpg_clip_band_t) * j);
+ if (arg->band == NULL) {
+ rtpg_clip_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ lwgeom_free(geom);
+ elog(ERROR, "RASTER_clip: Could not reallocate memory for band arguments");
+ PG_RETURN_NULL();
+ }
+
+ arg->numbands = j;
+ }
+
+ /* validate band */
+ for (i = 0; i < arg->numbands; i++) {
+ if (!rt_raster_has_band(arg->raster, arg->band[i].nband)) {
+ elog(NOTICE, "Band at index %d not found in raster", arg->band[i].nband + 1);
+ rtpg_clip_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ lwgeom_free(geom);
+ PG_RETURN_NULL();
+ }
+
+ arg->band[i].hasnodata = 0;
+ arg->band[i].nodataval = 0;
+ }
+ }
+ else {
+ arg->numbands = rt_raster_get_num_bands(arg->raster);
+
+ /* raster may have no bands */
+ if (arg->numbands) {
+ arg->band = palloc(sizeof(struct rtpg_clip_band_t) * arg->numbands);
+ if (arg->band == NULL) {
+
+ rtpg_clip_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ lwgeom_free(geom);
+
+ elog(ERROR, "RASTER_clip: Could not allocate memory for band arguments");
+ PG_RETURN_NULL();
+ }
+
+ for (i = 0; i < arg->numbands; i++) {
+ arg->band[i].nband = i;
+ arg->band[i].hasnodata = 0;
+ arg->band[i].nodataval = 0;
+ }
+ }
+ }
+
+ /* nodataval (3) */
+ if (!PG_ARGISNULL(3)) {
+ array = PG_GETARG_ARRAYTYPE_P(3);
+ etype = ARR_ELEMTYPE(array);
+ get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
+
+ switch (etype) {
+ case FLOAT4OID:
+ case FLOAT8OID:
+ break;
+ default:
+ rtpg_clip_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ lwgeom_free(geom);
+ elog(ERROR, "RASTER_clip: Invalid data type for NODATA values");
+ PG_RETURN_NULL();
+ break;
+ }
+
+ deconstruct_array(
+ array, etype,
+ typlen, typbyval, typalign,
+ &e, &nulls, &k
+ );
+
+ /* it doesn't matter if there are more nodataval */
+ for (i = 0, j = 0; i < arg->numbands; i++, j++) {
+ /* cap j to the last nodataval element */
+ if (j >= k)
+ j = k - 1;
+
+ if (nulls[j])
+ continue;
+
+ arg->band[i].hasnodata = 1;
+ switch (etype) {
+ case FLOAT4OID:
+ arg->band[i].nodataval = DatumGetFloat4(e[j]);
+ break;
+ case FLOAT8OID:
+ arg->band[i].nodataval = DatumGetFloat8(e[j]);
+ break;
+ }
+ }
+ }
+
+ /* get wkb of geometry */
+ POSTGIS_RT_DEBUG(3, "getting wkb of geometry");
+ wkb = lwgeom_to_wkb(geom, WKB_SFSQL, &wkb_len);
+ lwgeom_free(geom);
+
+ /* rasterize geometry */
+ arg->mask = rt_raster_gdal_rasterize(
+ wkb, wkb_len,
+ 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 (arg->mask == NULL) {
+ rtpg_clip_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_clip: Could not rasterize intersection geometry");
+ PG_RETURN_NULL();
+ }
+
+ /* set SRID */
+ rt_raster_set_srid(arg->mask, srid);
+
+ /* run iterator */
+
+ /* init itrset */
+ itrset = palloc(sizeof(struct rt_iterator_t) * 2);
+ if (itrset == NULL) {
+ rtpg_clip_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_clip: Could not allocate memory for iterator arguments");
+ PG_RETURN_NULL();
+ }
+
+ /* one band at a time */
+ for (i = 0; i < arg->numbands; i++) {
+ POSTGIS_RT_DEBUGF(4, "band arg %d (nband, hasnodata, nodataval) = (%d, %d, %f)",
+ i, arg->band[i].nband, arg->band[i].hasnodata, arg->band[i].nodataval);
+
+ band = rt_raster_get_band(arg->raster, arg->band[i].nband);
+
+ /* band metadata */
+ pixtype = rt_band_get_pixtype(band);
+
+ if (arg->band[i].hasnodata) {
+ hasnodata = 1;
+ nodataval = arg->band[i].nodataval;
+ }
+ else if (rt_band_get_hasnodata_flag(band)) {
+ hasnodata = 1;
+ rt_band_get_nodata(band, &nodataval);
+ }
+ else {
+ hasnodata = 0;
+ nodataval = rt_band_get_min_value(band);
+ }
+
+ /* band is NODATA, create NODATA band and continue */
+ if (rt_band_get_isnodata_flag(band)) {
+ /* create raster */
+ if (rtn == NULL) {
+ noerr = rt_raster_from_two_rasters(arg->raster, arg->mask, arg->extenttype, &rtn, NULL);
+ if (noerr != ES_NONE) {
+ rtpg_clip_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_clip: Could not create output raster");
+ PG_RETURN_NULL();
+ }
+ }
+
+ /* create NODATA band */
+ if (rt_raster_generate_new_band(rtn, pixtype, nodataval, hasnodata, nodataval, i) < 0) {
+ rt_raster_destroy(rtn);
+ rtpg_clip_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_clip: Could not add NODATA band to output raster");
+ PG_RETURN_NULL();
+ }
+
+ continue;
+ }
+
+ /* raster */
+ itrset[0].raster = arg->raster;
+ itrset[0].nband = arg->band[i].nband;
+ itrset[0].nbnodata = 1;
+
+ /* mask */
+ itrset[1].raster = arg->mask;
+ itrset[1].nband = 0;
+ itrset[1].nbnodata = 1;
+
+ /* pass to iterator */
+ noerr = rt_raster_iterator(
+ itrset, 2,
+ arg->extenttype, NULL,
+ pixtype,
+ hasnodata, nodataval,
+ 0, 0,
+ NULL,
+ rtpg_clip_callback,
+ &_raster
+ );
+
+ if (noerr != ES_NONE) {
+ pfree(itrset);
+ rtpg_clip_arg_destroy(arg);
+ if (rtn != NULL) rt_raster_destroy(rtn);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_clip: Could not run raster iterator function");
+ PG_RETURN_NULL();
+ }
+
+ /* new raster */
+ if (rtn == NULL)
+ rtn = _raster;
+ /* copy band */
+ else {
+ band = rt_raster_get_band(_raster, 0);
+ if (band == NULL) {
+ pfree(itrset);
+ rtpg_clip_arg_destroy(arg);
+ rt_raster_destroy(_raster);
+ rt_raster_destroy(rtn);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_clip: Could not get band from working raster");
+ PG_RETURN_NULL();
+ }
+
+ if (rt_raster_add_band(rtn, band, i) < 0) {
+ pfree(itrset);
+ rtpg_clip_arg_destroy(arg);
+ rt_raster_destroy(_raster);
+ rt_raster_destroy(rtn);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_clip: Could not add new band to output raster");
+ PG_RETURN_NULL();
+ }
+
+ rt_raster_destroy(_raster);
+ }
+ }
+
+ pfree(itrset);
+ rtpg_clip_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ pgrtn = rt_raster_serialize(rtn);
+ rt_raster_destroy(rtn);
+
+ POSTGIS_RT_DEBUG(3, "Finished");
+
+ if (!pgrtn)
+ PG_RETURN_NULL();
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+}
+
+/**
+ * Reclassify the specified bands of the raster
+ */
+PG_FUNCTION_INFO_V1(RASTER_reclass);
+Datum RASTER_reclass(PG_FUNCTION_ARGS) {
+ rt_pgraster *pgraster = NULL;
+ rt_pgraster *pgrtn = NULL;
+ rt_raster raster = NULL;
+ rt_band band = NULL;
+ rt_band newband = NULL;
+ uint32_t numBands = 0;
+
+ ArrayType *array;
+ Oid etype;
+ Datum *e;
+ bool *nulls;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ int n = 0;
+
+ int i = 0;
+ int j = 0;
+ int k = 0;
+
+ int a = 0;
+ int b = 0;
+ int c = 0;
+
+ rt_reclassexpr *exprset = NULL;
+ HeapTupleHeader tup;
+ bool isnull;
+ Datum tupv;
+ uint32_t nband = 0;
+ char *expr = NULL;
+ text *exprtext = NULL;
+ double val = 0;
+ char *junk = NULL;
+ int inc_val = 0;
+ int exc_val = 0;
+ char *pixeltype = NULL;
+ text *pixeltypetext = NULL;
+ rt_pixtype pixtype = PT_END;
+ double nodataval = 0;
+ bool hasnodata = FALSE;
+
+ char **comma_set = NULL;
+ int comma_n = 0;
+ char **colon_set = NULL;
+ int colon_n = 0;
+ char **dash_set = NULL;
+ int dash_n = 0;
+
+ POSTGIS_RT_DEBUG(3, "RASTER_reclass: Starting");
+
+ /* pgraster is null, return null */
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+ /* raster */
+ raster = rt_raster_deserialize(pgraster, FALSE);
+ if (!raster) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_reclass: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+ numBands = rt_raster_get_num_bands(raster);
+ POSTGIS_RT_DEBUGF(3, "RASTER_reclass: %d possible bands to be reclassified", n);
+
+ /* process set of reclassarg */
+ POSTGIS_RT_DEBUG(3, "RASTER_reclass: Processing Arg 1 (reclassargset)");
+ array = PG_GETARG_ARRAYTYPE_P(1);
+ etype = ARR_ELEMTYPE(array);
+ get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
+
+ deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
+ &nulls, &n);
+
+ if (!n) {
+ elog(NOTICE, "Invalid argument for reclassargset. Returning original 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);
+ }
+
+ /*
+ process each element of reclassarg
+ each element is the index of the band to process, the set
+ of reclass ranges and the output band's pixeltype
+ */
+ for (i = 0; i < n; i++) {
+ if (nulls[i]) continue;
+
+ /* each element is a tuple */
+ tup = (HeapTupleHeader) DatumGetPointer(e[i]);
+ if (NULL == tup) {
+ elog(NOTICE, "Invalid argument for reclassargset. Returning original 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);
+ }
+
+ /* band index (1-based) */
+ tupv = GetAttributeByName(tup, "nband", &isnull);
+ if (isnull) {
+ elog(NOTICE, "Invalid argument for reclassargset. Missing value of nband for reclassarg of index %d . Returning original raster", i);
+
+ 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);
+ }
+ nband = DatumGetInt32(tupv);
+ POSTGIS_RT_DEBUGF(3, "RASTER_reclass: expression for band %d", nband);
+
+ /* valid band index? */
+ if (nband < 1 || nband > numBands) {
+ elog(NOTICE, "Invalid argument for reclassargset. Invalid band index (must use 1-based) for reclassarg of index %d . Returning original raster", i);
+
+ 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);
+ }
+
+ /* reclass expr */
+ tupv = GetAttributeByName(tup, "reclassexpr", &isnull);
+ if (isnull) {
+ elog(NOTICE, "Invalid argument for reclassargset. Missing value of reclassexpr for reclassarg of index %d . Returning original raster", i);
+
+ 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);
+ }
+ exprtext = (text *) DatumGetPointer(tupv);
+ if (NULL == exprtext) {
+ elog(NOTICE, "Invalid argument for reclassargset. Missing value of reclassexpr for reclassarg of index %d . Returning original raster", i);
+
+ 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);
+ }
+ expr = text_to_cstring(exprtext);
+ POSTGIS_RT_DEBUGF(5, "RASTER_reclass: expr (raw) %s", expr);
+ expr = rtpg_removespaces(expr);
+ POSTGIS_RT_DEBUGF(5, "RASTER_reclass: expr (clean) %s", expr);
+
+ /* split string to its components */
+ /* comma (,) separating rangesets */
+ comma_set = rtpg_strsplit(expr, ",", &comma_n);
+ if (comma_n < 1) {
+ elog(NOTICE, "Invalid argument for reclassargset. Invalid expression of reclassexpr for reclassarg of index %d . Returning original raster", i);
+
+ 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 of reclass expressions */
+ POSTGIS_RT_DEBUGF(5, "RASTER_reclass: %d possible expressions", comma_n);
+ exprset = palloc(comma_n * sizeof(rt_reclassexpr));
+
+ for (a = 0, j = 0; a < comma_n; a++) {
+ POSTGIS_RT_DEBUGF(5, "RASTER_reclass: map %s", comma_set[a]);
+
+ /* colon (:) separating range "src" and "dst" */
+ colon_set = rtpg_strsplit(comma_set[a], ":", &colon_n);
+ if (colon_n != 2) {
+ elog(NOTICE, "Invalid argument for reclassargset. Invalid expression of reclassexpr for reclassarg of index %d . Returning original raster", i);
+ for (k = 0; k < j; k++) pfree(exprset[k]);
+ pfree(exprset);
+
+ 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);
+ }
+
+ /* allocate mem for reclass expression */
+ exprset[j] = palloc(sizeof(struct rt_reclassexpr_t));
+
+ for (b = 0; b < colon_n; b++) {
+ POSTGIS_RT_DEBUGF(5, "RASTER_reclass: range %s", colon_set[b]);
+
+ /* dash (-) separating "min" and "max" */
+ dash_set = rtpg_strsplit(colon_set[b], "-", &dash_n);
+ if (dash_n < 1 || dash_n > 3) {
+ elog(NOTICE, "Invalid argument for reclassargset. Invalid expression of reclassexpr for reclassarg of index %d . Returning original raster", i);
+ for (k = 0; k < j; k++) pfree(exprset[k]);
+ pfree(exprset);
+
+ 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);
+ }
+
+ for (c = 0; c < dash_n; c++) {
+ /* need to handle: (-9999-100 -> "(", "9999", "100" */
+ if (
+ c < 1 &&
+ strlen(dash_set[c]) == 1 && (
+ strchr(dash_set[c], '(') != NULL ||
+ strchr(dash_set[c], '[') != NULL ||
+ strchr(dash_set[c], ')') != NULL ||
+ strchr(dash_set[c], ']') != NULL
+ )
+ ) {
+ junk = palloc(sizeof(char) * (strlen(dash_set[c + 1]) + 2));
+ if (NULL == junk) {
+ for (k = 0; k <= j; k++) pfree(exprset[k]);
+ pfree(exprset);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ elog(ERROR, "RASTER_reclass: Could not allocate memory");
+ PG_RETURN_NULL();
+ }
+
+ sprintf(junk, "%s%s", dash_set[c], dash_set[c + 1]);
+ c++;
+ dash_set[c] = repalloc(dash_set[c], sizeof(char) * (strlen(junk) + 1));
+ strcpy(dash_set[c], junk);
+ pfree(junk);
+
+ /* rebuild dash_set */
+ for (k = 1; k < dash_n; k++) {
+ dash_set[k - 1] = repalloc(dash_set[k - 1], (strlen(dash_set[k]) + 1) * sizeof(char));
+ strcpy(dash_set[k - 1], dash_set[k]);
+ }
+ dash_n--;
+ c--;
+ pfree(dash_set[dash_n]);
+ dash_set = repalloc(dash_set, sizeof(char *) * dash_n);
+ }
+
+ /* there shouldn't be more than two in dash_n */
+ if (c < 1 && dash_n > 2) {
+ elog(NOTICE, "Invalid argument for reclassargset. Invalid expression of reclassexpr for reclassarg of index %d . Returning original raster", i);
+ for (k = 0; k < j; k++) pfree(exprset[k]);
+ pfree(exprset);
+
+ 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);
+ }
+
+ /* check interval flags */
+ exc_val = 0;
+ inc_val = 1;
+ /* range */
+ if (dash_n != 1) {
+ /* min */
+ if (c < 1) {
+ if (
+ strchr(dash_set[c], ')') != NULL ||
+ strchr(dash_set[c], ']') != NULL
+ ) {
+ exc_val = 1;
+ inc_val = 1;
+ }
+ else if (strchr(dash_set[c], '(') != NULL){
+ inc_val = 0;
+ }
+ else {
+ inc_val = 1;
+ }
+ }
+ /* max */
+ else {
+ if (
+ strrchr(dash_set[c], '(') != NULL ||
+ strrchr(dash_set[c], '[') != NULL
+ ) {
+ exc_val = 1;
+ inc_val = 0;
+ }
+ else if (strrchr(dash_set[c], ']') != NULL) {
+ inc_val = 1;
+ }
+ else {
+ inc_val = 0;
+ }
+ }
+ }
+ POSTGIS_RT_DEBUGF(5, "RASTER_reclass: exc_val %d inc_val %d", exc_val, inc_val);
+
+ /* remove interval flags */
+ dash_set[c] = rtpg_chartrim(dash_set[c], "()[]");
+ POSTGIS_RT_DEBUGF(5, "RASTER_reclass: min/max (char) %s", dash_set[c]);
+
+ /* value from string to double */
+ errno = 0;
+ val = strtod(dash_set[c], &junk);
+ if (errno != 0 || dash_set[c] == junk) {
+ elog(NOTICE, "Invalid argument for reclassargset. Invalid expression of reclassexpr for reclassarg of index %d . Returning original raster", i);
+ for (k = 0; k < j; k++) pfree(exprset[k]);
+ pfree(exprset);
+
+ 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);
+ }
+ POSTGIS_RT_DEBUGF(5, "RASTER_reclass: min/max (double) %f", val);
+
+ /* strsplit removes dash (a.k.a. negative sign), compare now to restore */
+ junk = strstr(colon_set[b], dash_set[c]);
+ /* not beginning of string */
+ if (junk != colon_set[b]) {
+ /* prior is a dash */
+ if (*(junk - 1) == '-') {
+ /* prior is beginning of string or prior - 1 char is dash, negative number */
+ if (
+ ((junk - 1) == colon_set[b]) ||
+ (*(junk - 2) == '-') ||
+ (*(junk - 2) == '[') ||
+ (*(junk - 2) == '(')
+ ) {
+ val *= -1.;
+ }
+ }
+ }
+ POSTGIS_RT_DEBUGF(5, "RASTER_reclass: min/max (double) %f", val);
+
+ /* src */
+ if (b < 1) {
+ /* singular value */
+ if (dash_n == 1) {
+ exprset[j]->src.exc_min = exprset[j]->src.exc_max = exc_val;
+ exprset[j]->src.inc_min = exprset[j]->src.inc_max = inc_val;
+ exprset[j]->src.min = exprset[j]->src.max = val;
+ }
+ /* min */
+ else if (c < 1) {
+ exprset[j]->src.exc_min = exc_val;
+ exprset[j]->src.inc_min = inc_val;
+ exprset[j]->src.min = val;
+ }
+ /* max */
+ else {
+ exprset[j]->src.exc_max = exc_val;
+ exprset[j]->src.inc_max = inc_val;
+ exprset[j]->src.max = val;
+ }
+ }
+ /* dst */
+ else {
+ /* singular value */
+ if (dash_n == 1)
+ exprset[j]->dst.min = exprset[j]->dst.max = val;
+ /* min */
+ else if (c < 1)
+ exprset[j]->dst.min = val;
+ /* max */
+ else
+ exprset[j]->dst.max = val;
+ }
+ }
+ pfree(dash_set);
+ }
+ pfree(colon_set);
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_reclass: or: %f - %f nr: %f - %f"
+ , exprset[j]->src.min
+ , exprset[j]->src.max
+ , exprset[j]->dst.min
+ , exprset[j]->dst.max
+ );
+ j++;
+ }
+ pfree(comma_set);
+
+ /* pixel type */
+ tupv = GetAttributeByName(tup, "pixeltype", &isnull);
+ if (isnull) {
+ elog(NOTICE, "Invalid argument for reclassargset. Missing value of pixeltype for reclassarg of index %d . Returning original raster", i);
+
+ 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);
+ }
+ pixeltypetext = (text *) DatumGetPointer(tupv);
+ if (NULL == pixeltypetext) {
+ elog(NOTICE, "Invalid argument for reclassargset. Missing value of pixeltype for reclassarg of index %d . Returning original raster", i);
+
+ 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);
+ }
+ pixeltype = text_to_cstring(pixeltypetext);
+ POSTGIS_RT_DEBUGF(3, "RASTER_reclass: pixeltype %s", pixeltype);
+ pixtype = rt_pixtype_index_from_name(pixeltype);
+
+ /* nodata */
+ tupv = GetAttributeByName(tup, "nodataval", &isnull);
+ if (isnull) {
+ nodataval = 0;
+ hasnodata = FALSE;
+ }
+ else {
+ nodataval = DatumGetFloat8(tupv);
+ hasnodata = TRUE;
+ }
+ POSTGIS_RT_DEBUGF(3, "RASTER_reclass: nodataval %f", nodataval);
+ POSTGIS_RT_DEBUGF(3, "RASTER_reclass: hasnodata %d", hasnodata);
+
+ /* do reclass */
+ band = rt_raster_get_band(raster, nband - 1);
+ if (!band) {
+ elog(NOTICE, "Could not find raster band of index %d. Returning original raster", nband);
+ for (k = 0; k < j; k++) pfree(exprset[k]);
+ pfree(exprset);
+
+ 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);
+ }
+ newband = rt_band_reclass(band, pixtype, hasnodata, nodataval, exprset, j);
+ if (!newband) {
+ for (k = 0; k < j; k++) pfree(exprset[k]);
+ pfree(exprset);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ elog(ERROR, "RASTER_reclass: Could not reclassify raster band of index %d", nband);
+ PG_RETURN_NULL();
+ }
+
+ /* replace old band with new band */
+ if (rt_raster_replace_band(raster, newband, nband - 1) == NULL) {
+ for (k = 0; k < j; k++) pfree(exprset[k]);
+ pfree(exprset);
+
+ rt_band_destroy(newband);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ elog(ERROR, "RASTER_reclass: Could not replace raster band of index %d with reclassified band", nband);
+ PG_RETURN_NULL();
+ }
+
+ /* old band is in the variable band */
+ rt_band_destroy(band);
+
+ /* free exprset */
+ for (k = 0; k < j; k++) pfree(exprset[k]);
+ pfree(exprset);
+ }
+
+ pgrtn = rt_raster_serialize(raster);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ if (!pgrtn)
+ PG_RETURN_NULL();
+
+ POSTGIS_RT_DEBUG(3, "RASTER_reclass: Finished");
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+}
+
+/* ---------------------------------------------------------------- */
+/* apply colormap to specified band of a raster */
+/* ---------------------------------------------------------------- */
+
+typedef struct rtpg_colormap_arg_t *rtpg_colormap_arg;
+struct rtpg_colormap_arg_t {
+ rt_raster raster;
+ int nband; /* 1-based */
+ rt_band band;
+ rt_bandstats bandstats;
+
+ rt_colormap colormap;
+ int nodataentry;
+
+ char **entry;
+ int nentry;
+ char **element;
+ int nelement;
+};
+
+static rtpg_colormap_arg
+rtpg_colormap_arg_init() {
+ rtpg_colormap_arg arg = NULL;
+
+ arg = palloc(sizeof(struct rtpg_colormap_arg_t));
+ if (arg == NULL) {
+ elog(ERROR, "rtpg_colormap_arg: Could not allocate memory for function arguments");
+ return NULL;
+ }
+
+ arg->raster = NULL;
+ arg->nband = 1;
+ arg->band = NULL;
+ arg->bandstats = NULL;
+
+ arg->colormap = palloc(sizeof(struct rt_colormap_t));
+ if (arg->colormap == NULL) {
+ elog(ERROR, "rtpg_colormap_arg: Could not allocate memory for function arguments");
+ return NULL;
+ }
+ arg->colormap->nentry = 0;
+ arg->colormap->entry = NULL;
+ arg->colormap->ncolor = 4; /* assume RGBA */
+ arg->colormap->method = CM_INTERPOLATE;
+ arg->nodataentry = -1;
+
+ arg->entry = NULL;
+ arg->nentry = 0;
+ arg->element = NULL;
+ arg->nelement = 0;
+
+ return arg;
+}
+
+static void
+rtpg_colormap_arg_destroy(rtpg_colormap_arg arg) {
+ int i = 0;
+ if (arg->raster != NULL)
+ rt_raster_destroy(arg->raster);
+
+ if (arg->bandstats != NULL)
+ pfree(arg->bandstats);
+
+ if (arg->colormap != NULL) {
+ if (arg->colormap->entry != NULL)
+ pfree(arg->colormap->entry);
+ pfree(arg->colormap);
+ }
+
+ if (arg->nentry) {
+ for (i = 0; i < arg->nentry; i++) {
+ if (arg->entry[i] != NULL)
+ pfree(arg->entry[i]);
+ }
+ pfree(arg->entry);
+ }
+
+ if (arg->nelement) {
+ for (i = 0; i < arg->nelement; i++)
+ pfree(arg->element[i]);
+ pfree(arg->element);
+ }
+
+ pfree(arg);
+ arg = NULL;
+}
+
+PG_FUNCTION_INFO_V1(RASTER_colorMap);
+Datum RASTER_colorMap(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rtpg_colormap_arg arg = NULL;
+ char *junk = NULL;
+ rt_raster raster = NULL;
+
+ POSTGIS_RT_DEBUG(3, "RASTER_colorMap: Starting");
+
+ /* pgraster is NULL, return NULL */
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+
+ /* init arg */
+ arg = rtpg_colormap_arg_init();
+ if (arg == NULL) {
+ elog(ERROR, "RASTER_colorMap: Could not initialize argument structure");
+ PG_RETURN_NULL();
+ }
+
+ /* raster (0) */
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+ /* raster object */
+ arg->raster = rt_raster_deserialize(pgraster, FALSE);
+ if (!arg->raster) {
+ rtpg_colormap_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_colorMap: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ /* nband (1) */
+ if (!PG_ARGISNULL(1))
+ arg->nband = PG_GETARG_INT32(1);
+ POSTGIS_RT_DEBUGF(4, "nband = %d", arg->nband);
+
+ /* check that band works */
+ if (!rt_raster_has_band(arg->raster, arg->nband - 1)) {
+ elog(NOTICE, "Raster does not have band at index %d. Returning empty raster", arg->nband);
+
+ raster = rt_raster_clone(arg->raster, 0);
+ if (raster == NULL) {
+ rtpg_colormap_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_colorMap: Could not create empty raster");
+ PG_RETURN_NULL();
+ }
+
+ rtpg_colormap_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ pgraster = rt_raster_serialize(raster);
+ rt_raster_destroy(raster);
+ if (pgraster == NULL)
+ PG_RETURN_NULL();
+
+ SET_VARSIZE(pgraster, ((rt_pgraster*) pgraster)->size);
+ PG_RETURN_POINTER(pgraster);
+ }
+
+ /* get band */
+ arg->band = rt_raster_get_band(arg->raster, arg->nband - 1);
+ if (arg->band == NULL) {
+ int nband = arg->nband;
+ rtpg_colormap_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_colorMap: Could not get band at index %d", nband);
+ PG_RETURN_NULL();
+ }
+
+ /* method (3) */
+ if (!PG_ARGISNULL(3)) {
+ char *method = NULL;
+ char *tmp = text_to_cstring(PG_GETARG_TEXT_P(3));
+ POSTGIS_RT_DEBUGF(4, "raw method = %s", tmp);
+
+ method = rtpg_trim(tmp);
+ pfree(tmp);
+ method = rtpg_strtoupper(method);
+
+ if (strcmp(method, "INTERPOLATE") == 0)
+ arg->colormap->method = CM_INTERPOLATE;
+ else if (strcmp(method, "EXACT") == 0)
+ arg->colormap->method = CM_EXACT;
+ else if (strcmp(method, "NEAREST") == 0)
+ arg->colormap->method = CM_NEAREST;
+ else {
+ elog(NOTICE, "Unknown value provided for method. Defaulting to INTERPOLATE");
+ arg->colormap->method = CM_INTERPOLATE;
+ }
+ }
+ /* default to INTERPOLATE */
+ else
+ arg->colormap->method = CM_INTERPOLATE;
+ POSTGIS_RT_DEBUGF(4, "method = %d", arg->colormap->method);
+
+ /* colormap (2) */
+ if (PG_ARGISNULL(2)) {
+ rtpg_colormap_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_colorMap: Value must be provided for colormap");
+ PG_RETURN_NULL();
+ }
+ else {
+ char *tmp = NULL;
+ char *colormap = text_to_cstring(PG_GETARG_TEXT_P(2));
+ char *_entry;
+ char *_element;
+ int i = 0;
+ int j = 0;
+
+ POSTGIS_RT_DEBUGF(4, "colormap = %s", colormap);
+
+ /* empty string */
+ if (!strlen(colormap)) {
+ rtpg_colormap_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_colorMap: Value must be provided for colormap");
+ PG_RETURN_NULL();
+ }
+
+ arg->entry = rtpg_strsplit(colormap, "\n", &(arg->nentry));
+ pfree(colormap);
+ if (arg->nentry < 1) {
+ rtpg_colormap_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_colorMap: Could not process the value provided for colormap");
+ PG_RETURN_NULL();
+ }
+
+ /* allocate the max # of colormap entries */
+ arg->colormap->entry = palloc(sizeof(struct rt_colormap_entry_t) * arg->nentry);
+ if (arg->colormap->entry == NULL) {
+ rtpg_colormap_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_colorMap: Could not allocate memory for colormap entries");
+ PG_RETURN_NULL();
+ }
+ memset(arg->colormap->entry, 0, sizeof(struct rt_colormap_entry_t) * arg->nentry);
+
+ /* each entry */
+ for (i = 0; i < arg->nentry; i++) {
+ /* substitute space for other delimiters */
+ tmp = rtpg_strreplace(arg->entry[i], ":", " ", NULL);
+ _entry = rtpg_strreplace(tmp, ",", " ", NULL);
+ pfree(tmp);
+ tmp = rtpg_strreplace(_entry, "\t", " ", NULL);
+ pfree(_entry);
+ _entry = rtpg_trim(tmp);
+ pfree(tmp);
+
+ POSTGIS_RT_DEBUGF(4, "Processing entry[%d] = %s", i, arg->entry[i]);
+ POSTGIS_RT_DEBUGF(4, "Cleaned entry[%d] = %s", i, _entry);
+
+ /* empty entry, continue */
+ if (!strlen(_entry)) {
+ POSTGIS_RT_DEBUGF(3, "Skipping empty entry[%d]", i);
+ pfree(_entry);
+ continue;
+ }
+
+ arg->element = rtpg_strsplit(_entry, " ", &(arg->nelement));
+ pfree(_entry);
+ if (arg->nelement < 2) {
+ rtpg_colormap_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_colorMap: Could not process colormap entry %d", i + 1);
+ PG_RETURN_NULL();
+ }
+ else if (arg->nelement > 5) {
+ elog(NOTICE, "More than five elements in colormap entry %d. Using at most five elements", i + 1);
+ arg->nelement = 5;
+ }
+
+ /* smallest # of colors */
+ if ((arg->nelement - 1) < arg->colormap->ncolor)
+ arg->colormap->ncolor = arg->nelement - 1;
+
+ /* each element of entry */
+ for (j = 0; j < arg->nelement; j++) {
+
+ _element = rtpg_trim(arg->element[j]);
+ _element = rtpg_strtoupper(_element);
+ POSTGIS_RT_DEBUGF(4, "Processing entry[%d][%d] = %s", i, j, arg->element[j]);
+ POSTGIS_RT_DEBUGF(4, "Cleaned entry[%d][%d] = %s", i, j, _element);
+
+ /* first element is ALWAYS a band value, percentage OR "nv" string */
+ if (j == 0) {
+ char *percent = NULL;
+
+ /* NODATA */
+ if (
+ strcmp(_element, "NV") == 0 ||
+ strcmp(_element, "NULL") == 0 ||
+ strcmp(_element, "NODATA") == 0
+ ) {
+ POSTGIS_RT_DEBUG(4, "Processing NODATA string");
+
+ if (arg->nodataentry > -1) {
+ elog(NOTICE, "More than one NODATA entry found. Using only the first one");
+ }
+ else {
+ arg->colormap->entry[arg->colormap->nentry].isnodata = 1;
+ /* no need to set value as value comes from band's NODATA */
+ arg->colormap->entry[arg->colormap->nentry].value = 0;
+ }
+ }
+ /* percent value */
+ else if ((percent = strchr(_element, '%')) != NULL) {
+ double value;
+ POSTGIS_RT_DEBUG(4, "Processing percent string");
+
+ /* get the band stats */
+ if (arg->bandstats == NULL) {
+ POSTGIS_RT_DEBUG(4, "Getting band stats");
+
+ arg->bandstats = rt_band_get_summary_stats(arg->band, 1, 1, 0, NULL, NULL, NULL);
+ if (arg->bandstats == NULL) {
+ pfree(_element);
+ rtpg_colormap_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_colorMap: Could not get band's summary stats to process percentages");
+ PG_RETURN_NULL();
+ }
+ }
+
+ /* get the string before the percent char */
+ tmp = palloc(sizeof(char) * (percent - _element + 1));
+ if (tmp == NULL) {
+ pfree(_element);
+ rtpg_colormap_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_colorMap: Could not allocate memory for value of percentage");
+ PG_RETURN_NULL();
+ }
+
+ memcpy(tmp, _element, percent - _element);
+ tmp[percent - _element] = '\0';
+ POSTGIS_RT_DEBUGF(4, "Percent value = %s", tmp);
+
+ /* get percentage value */
+ errno = 0;
+ value = strtod(tmp, NULL);
+ pfree(tmp);
+ if (errno != 0 || _element == junk) {
+ pfree(_element);
+ rtpg_colormap_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_colorMap: Could not process percent string to value");
+ PG_RETURN_NULL();
+ }
+
+ /* check percentage */
+ if (value < 0.) {
+ elog(NOTICE, "Percentage values cannot be less than zero. Defaulting to zero");
+ value = 0.;
+ }
+ else if (value > 100.) {
+ elog(NOTICE, "Percentage values cannot be greater than 100. Defaulting to 100");
+ value = 100.;
+ }
+
+ /* get the true pixel value */
+ /* TODO: should the percentage be quantile based? */
+ arg->colormap->entry[arg->colormap->nentry].value = ((value / 100.) * (arg->bandstats->max - arg->bandstats->min)) + arg->bandstats->min;
+ }
+ /* straight value */
+ else {
+ errno = 0;
+ arg->colormap->entry[arg->colormap->nentry].value = strtod(_element, &junk);
+ if (errno != 0 || _element == junk) {
+ pfree(_element);
+ rtpg_colormap_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_colorMap: Could not process string to value");
+ PG_RETURN_NULL();
+ }
+ }
+
+ }
+ /* RGB values (0 - 255) */
+ else {
+ int value = 0;
+
+ errno = 0;
+ value = (int) strtod(_element, &junk);
+ if (errno != 0 || _element == junk) {
+ pfree(_element);
+ rtpg_colormap_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_colorMap: Could not process string to value");
+ PG_RETURN_NULL();
+ }
+
+ if (value > 255) {
+ elog(NOTICE, "RGBA value cannot be greater than 255. Defaulting to 255");
+ value = 255;
+ }
+ else if (value < 0) {
+ elog(NOTICE, "RGBA value cannot be less than zero. Defaulting to zero");
+ value = 0;
+ }
+ arg->colormap->entry[arg->colormap->nentry].color[j - 1] = value;
+ }
+
+ pfree(_element);
+ }
+
+ POSTGIS_RT_DEBUGF(4, "colormap->entry[%d] (isnodata, value, R, G, B, A) = (%d, %f, %d, %d, %d, %d)",
+ arg->colormap->nentry,
+ arg->colormap->entry[arg->colormap->nentry].isnodata,
+ arg->colormap->entry[arg->colormap->nentry].value,
+ arg->colormap->entry[arg->colormap->nentry].color[0],
+ arg->colormap->entry[arg->colormap->nentry].color[1],
+ arg->colormap->entry[arg->colormap->nentry].color[2],
+ arg->colormap->entry[arg->colormap->nentry].color[3]
+ );
+
+ arg->colormap->nentry++;
+ }
+
+ POSTGIS_RT_DEBUGF(4, "colormap->nentry = %d", arg->colormap->nentry);
+ POSTGIS_RT_DEBUGF(4, "colormap->ncolor = %d", arg->colormap->ncolor);
+ }
+
+ /* call colormap */
+ raster = rt_raster_colormap(arg->raster, arg->nband - 1, arg->colormap);
+ if (raster == NULL) {
+ rtpg_colormap_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_colorMap: Could not create new raster with applied colormap");
+ PG_RETURN_NULL();
+ }
+
+ rtpg_colormap_arg_destroy(arg);
+ PG_FREE_IF_COPY(pgraster, 0);
+ pgraster = rt_raster_serialize(raster);
+ rt_raster_destroy(raster);
+
+ POSTGIS_RT_DEBUG(3, "RASTER_colorMap: Done");
+
+ if (pgraster == NULL)
+ PG_RETURN_NULL();
+
+ SET_VARSIZE(pgraster, ((rt_pgraster*) pgraster)->size);
+ PG_RETURN_POINTER(pgraster);
+}
+
+PG_FUNCTION_INFO_V1(RASTER_mapAlgebraExpr);
+Datum RASTER_mapAlgebraExpr(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_pgraster *pgrtn = NULL;
+ rt_raster raster = NULL;
+ rt_raster newrast = NULL;
+ rt_band band = NULL;
+ rt_band newband = NULL;
+ int x, y, nband, width, height;
+ double r;
+ double newnodatavalue = 0.0;
+ double newinitialvalue = 0.0;
+ double newval = 0.0;
+ char *newexpr = NULL;
+ char *initexpr = NULL;
+ char *expression = NULL;
+ int hasnodataval = 0;
+ double nodataval = 0.;
+ rt_pixtype newpixeltype;
+ int skipcomputation = 0;
+ int len = 0;
+ const int argkwcount = 3;
+ enum KEYWORDS { kVAL=0, kX=1, kY=2 };
+ char *argkw[] = {"[rast]", "[rast.x]", "[rast.y]"};
+ Oid argkwtypes[] = { FLOAT8OID, INT4OID, INT4OID };
+ int argcount = 0;
+ Oid argtype[] = { FLOAT8OID, INT4OID, INT4OID };
+ uint8_t argpos[3] = {0};
+ char place[5];
+ int idx = 0;
+ int ret = -1;
+ TupleDesc tupdesc;
+ SPIPlanPtr spi_plan = NULL;
+ SPITupleTable * tuptable = NULL;
+ HeapTuple tuple;
+ char * strFromText = NULL;
+ Datum *values = NULL;
+ Datum datum = (Datum)NULL;
+ char *nulls = NULL;
+ bool isnull = FALSE;
+ int i = 0;
+ int j = 0;
+
+ POSTGIS_RT_DEBUG(2, "RASTER_mapAlgebraExpr: Starting...");
+
+ /* Check raster */
+ if (PG_ARGISNULL(0)) {
+ elog(NOTICE, "Raster is NULL. Returning NULL");
+ PG_RETURN_NULL();
+ }
+
+
+ /* Deserialize raster */
+ pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+ raster = rt_raster_deserialize(pgraster, FALSE);
+ if (NULL == raster) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_mapAlgebraExpr: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraExpr: Getting arguments...");
+
+ if (PG_ARGISNULL(1))
+ nband = 1;
+ else
+ nband = PG_GETARG_INT32(1);
+
+ if (nband < 1)
+ nband = 1;
+
+
+ POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraExpr: Creating new empty raster...");
+
+ /**
+ * Create a new empty raster with having the same georeference as the
+ * provided raster
+ **/
+ width = rt_raster_get_width(raster);
+ height = rt_raster_get_height(raster);
+
+ newrast = rt_raster_new(width, height);
+
+ if ( NULL == newrast ) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_mapAlgebraExpr: Could not create a new raster");
+ PG_RETURN_NULL();
+ }
+
+ rt_raster_set_scale(newrast,
+ rt_raster_get_x_scale(raster),
+ rt_raster_get_y_scale(raster));
+
+ rt_raster_set_offsets(newrast,
+ rt_raster_get_x_offset(raster),
+ rt_raster_get_y_offset(raster));
+
+ rt_raster_set_skews(newrast,
+ rt_raster_get_x_skew(raster),
+ rt_raster_get_y_skew(raster));
+
+ rt_raster_set_srid(newrast, rt_raster_get_srid(raster));
+
+
+ /**
+ * If this new raster is empty (width = 0 OR height = 0) then there is
+ * nothing to compute and we return it right now
+ **/
+ if (rt_raster_is_empty(newrast))
+ {
+ elog(NOTICE, "Raster is empty. Returning an empty raster");
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ pgrtn = rt_raster_serialize(newrast);
+ rt_raster_destroy(newrast);
+ if (NULL == pgrtn) {
+
+ elog(ERROR, "RASTER_mapAlgebraExpr: Could not serialize raster");
+ PG_RETURN_NULL();
+ }
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraExpr: Getting raster band %d...", nband);
+
+ /**
+ * Check if the raster has the required band. Otherwise, return a raster
+ * without band
+ **/
+ if (!rt_raster_has_band(raster, nband - 1)) {
+ elog(NOTICE, "Raster does not have the required band. Returning a raster "
+ "without a band");
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ pgrtn = rt_raster_serialize(newrast);
+ rt_raster_destroy(newrast);
+ if (NULL == pgrtn) {
+ elog(ERROR, "RASTER_mapAlgebraExpr: Could not serialize raster");
+ PG_RETURN_NULL();
+ }
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+ /* Get the raster band */
+ band = rt_raster_get_band(raster, nband - 1);
+ if ( NULL == band ) {
+ elog(NOTICE, "Could not get the required band. Returning a raster "
+ "without a band");
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ pgrtn = rt_raster_serialize(newrast);
+ rt_raster_destroy(newrast);
+ if (NULL == pgrtn) {
+ elog(ERROR, "RASTER_mapAlgebraExpr: Could not serialize raster");
+ PG_RETURN_NULL();
+ }
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+ /*
+ * Get NODATA value
+ */
+ POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraExpr: Getting NODATA value for band...");
+
+ if (rt_band_get_hasnodata_flag(band)) {
+ rt_band_get_nodata(band, &newnodatavalue);
+ }
+
+ else {
+ newnodatavalue = rt_band_get_min_value(band);
+ }
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraExpr: NODATA value for band: = %f",
+ newnodatavalue);
+
+ /**
+ * We set the initial value of the future band to nodata value. If nodata
+ * value is null, then the raster will be initialized to
+ * rt_band_get_min_value but all the values should be recomputed anyway
+ **/
+ newinitialvalue = newnodatavalue;
+
+ /**
+ * Set the new pixeltype
+ **/
+ POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraExpr: Setting pixeltype...");
+
+ if (PG_ARGISNULL(2)) {
+ newpixeltype = rt_band_get_pixtype(band);
+ }
+
+ else {
+ strFromText = text_to_cstring(PG_GETARG_TEXT_P(2));
+ newpixeltype = rt_pixtype_index_from_name(strFromText);
+ pfree(strFromText);
+ if (newpixeltype == PT_END)
+ newpixeltype = rt_band_get_pixtype(band);
+ }
+
+ if (newpixeltype == PT_END) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_mapAlgebraExpr: Invalid pixeltype");
+ PG_RETURN_NULL();
+ }
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraExpr: Pixeltype set to %s",
+ rt_pixtype_name(newpixeltype));
+
+
+ /* Construct expression for raster values */
+ if (!PG_ARGISNULL(3)) {
+ expression = text_to_cstring(PG_GETARG_TEXT_P(3));
+ len = strlen("SELECT (") + strlen(expression) + strlen(")::double precision");
+ initexpr = (char *)palloc(len + 1);
+
+ strncpy(initexpr, "SELECT (", strlen("SELECT ("));
+ strncpy(initexpr + strlen("SELECT ("), expression, strlen(expression));
+ strncpy(initexpr + strlen("SELECT (") + strlen(expression), ")::double precision", strlen(")::double precision"));
+ initexpr[len] = '\0';
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraExpr: Expression is %s", initexpr);
+
+ /* We don't need this memory */
+ /*
+ pfree(expression);
+ expression = NULL;
+ */
+ }
+
+
+
+ /**
+ * Optimization: If a nodataval is provided, use it for newinitialvalue.
+ * Then, we can initialize the raster with this value and skip the
+ * computation of nodata values one by one in the main computing loop
+ **/
+ if (!PG_ARGISNULL(4)) {
+ hasnodataval = 1;
+ nodataval = PG_GETARG_FLOAT8(4);
+ newinitialvalue = nodataval;
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraExpr: new initial value = %f",
+ newinitialvalue);
+ }
+ else
+ hasnodataval = 0;
+
+
+
+ /**
+ * Optimization: If the raster is only filled with nodata values return
+ * right now a raster filled with the newinitialvalue
+ * TODO: Call rt_band_check_isnodata instead?
+ **/
+ if (rt_band_get_isnodata_flag(band)) {
+
+ POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraExpr: Band is a nodata band, returning "
+ "a raster filled with nodata");
+
+ ret = rt_raster_generate_new_band(newrast, newpixeltype,
+ newinitialvalue, TRUE, newnodatavalue, 0);
+
+ /* Free memory */
+ if (initexpr)
+ pfree(initexpr);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ /* Serialize created raster */
+ pgrtn = rt_raster_serialize(newrast);
+ rt_raster_destroy(newrast);
+ if (NULL == pgrtn) {
+ elog(ERROR, "RASTER_mapAlgebraExpr: Could not serialize raster");
+ PG_RETURN_NULL();
+ }
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+
+ /**
+ * Optimization: If expression resume to 'RAST' and hasnodataval is zero,
+ * we can just return the band from the original raster
+ **/
+ if (initexpr != NULL && ( !strcmp(initexpr, "SELECT [rast]") || !strcmp(initexpr, "SELECT [rast.val]") ) && !hasnodataval) {
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraExpr: Expression resumes to RAST. "
+ "Returning raster with band %d from original raster", nband);
+
+ POSTGIS_RT_DEBUGF(4, "RASTER_mapAlgebraExpr: New raster has %d bands",
+ rt_raster_get_num_bands(newrast));
+
+ rt_raster_copy_band(newrast, raster, nband - 1, 0);
+
+ POSTGIS_RT_DEBUGF(4, "RASTER_mapAlgebraExpr: New raster now has %d bands",
+ rt_raster_get_num_bands(newrast));
+
+ if (initexpr)
+ pfree(initexpr);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ /* Serialize created raster */
+ pgrtn = rt_raster_serialize(newrast);
+ rt_raster_destroy(newrast);
+ if (NULL == pgrtn) {
+ elog(ERROR, "RASTER_mapAlgebraExpr: Could not serialize raster");
+ PG_RETURN_NULL();
+ }
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+ /**
+ * Optimization: If expression resume to a constant (it does not contain
+ * [rast)
+ **/
+ if (initexpr != NULL && strstr(initexpr, "[rast") == NULL) {
+ ret = SPI_connect();
+ if (ret != SPI_OK_CONNECT) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_mapAlgebraExpr: Could not connect to the SPI manager");
+ PG_RETURN_NULL();
+ };
+
+ /* Execute the expresion into newval */
+ ret = SPI_execute(initexpr, FALSE, 0);
+
+ if (ret != SPI_OK_SELECT || SPI_tuptable == NULL || SPI_processed != 1) {
+
+ /* Free memory allocated out of the current context */
+ if (SPI_tuptable)
+ SPI_freetuptable(tuptable);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ SPI_finish();
+ elog(ERROR, "RASTER_mapAlgebraExpr: Invalid construction for expression");
+ PG_RETURN_NULL();
+ }
+
+ tupdesc = SPI_tuptable->tupdesc;
+ tuptable = SPI_tuptable;
+
+ tuple = tuptable->vals[0];
+ newexpr = SPI_getvalue(tuple, tupdesc, 1);
+ if ( ! newexpr ) {
+ POSTGIS_RT_DEBUG(3, "Constant expression evaluated to NULL, keeping initvalue");
+ newval = newinitialvalue;
+ } else {
+ newval = atof(newexpr);
+ }
+
+ SPI_freetuptable(tuptable);
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraExpr: New raster value = %f",
+ newval);
+
+ SPI_finish();
+
+ skipcomputation = 1;
+
+ /**
+ * Compute the new value, set it and we will return after creating the
+ * new raster
+ **/
+ if (!hasnodataval) {
+ newinitialvalue = newval;
+ skipcomputation = 2;
+ }
+
+ /* Return the new raster as it will be before computing pixel by pixel */
+ else if (FLT_NEQ(newval, newinitialvalue)) {
+ skipcomputation = 2;
+ }
+ }
+
+ /**
+ * Create the raster receiving all the computed values. Initialize it to the
+ * new initial value
+ **/
+ ret = rt_raster_generate_new_band(newrast, newpixeltype,
+ newinitialvalue, TRUE, newnodatavalue, 0);
+
+ /**
+ * Optimization: If expression is NULL, or all the pixels could be set in
+ * one step, return the initialized raster now
+ **/
+ /*if (initexpr == NULL || skipcomputation == 2) {*/
+ if (expression == NULL || skipcomputation == 2) {
+
+ /* Free memory */
+ if (initexpr)
+ pfree(initexpr);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ /* Serialize created raster */
+ pgrtn = rt_raster_serialize(newrast);
+ rt_raster_destroy(newrast);
+ if (NULL == pgrtn) {
+ elog(ERROR, "RASTER_mapAlgebraExpr: Could not serialize raster");
+ PG_RETURN_NULL();
+ }
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+ RASTER_DEBUG(3, "RASTER_mapAlgebraExpr: Creating new raster band...");
+
+ /* Get the new raster band */
+ newband = rt_raster_get_band(newrast, 0);
+ if ( NULL == newband ) {
+ elog(NOTICE, "Could not modify band for new raster. Returning new "
+ "raster with the original band");
+
+ if (initexpr)
+ pfree(initexpr);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ /* Serialize created raster */
+ pgrtn = rt_raster_serialize(newrast);
+ rt_raster_destroy(newrast);
+ if (NULL == pgrtn) {
+ elog(ERROR, "RASTER_mapAlgebraExpr: Could not serialize raster");
+ PG_RETURN_NULL();
+ }
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraExpr: Main computing loop (%d x %d)",
+ width, height);
+
+ if (initexpr != NULL) {
+ /* Convert [rast.val] to [rast] */
+ newexpr = rtpg_strreplace(initexpr, "[rast.val]", "[rast]", NULL);
+ pfree(initexpr); initexpr=newexpr;
+
+ sprintf(place,"$1");
+ for (i = 0, j = 1; i < argkwcount; i++) {
+ len = 0;
+ newexpr = rtpg_strreplace(initexpr, argkw[i], place, &len);
+ pfree(initexpr); initexpr=newexpr;
+ if (len > 0) {
+ argtype[argcount] = argkwtypes[i];
+ argcount++;
+ argpos[i] = j++;
+
+ sprintf(place, "$%d", j);
+ }
+ else {
+ argpos[i] = 0;
+ }
+ }
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraExpr: initexpr = %s", initexpr);
+
+ /* define values */
+ values = (Datum *) palloc(sizeof(Datum) * argcount);
+ if (values == NULL) {
+
+ SPI_finish();
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ rt_raster_destroy(newrast);
+
+ elog(ERROR, "RASTER_mapAlgebraExpr: Could not allocate memory for value parameters of prepared statement");
+ PG_RETURN_NULL();
+ }
+
+ /* define nulls */
+ nulls = (char *)palloc(argcount);
+ if (nulls == NULL) {
+
+ SPI_finish();
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ rt_raster_destroy(newrast);
+
+ elog(ERROR, "RASTER_mapAlgebraExpr: Could not allocate memory for null parameters of prepared statement");
+ PG_RETURN_NULL();
+ }
+
+ /* Connect to SPI and prepare the expression */
+ ret = SPI_connect();
+ if (ret != SPI_OK_CONNECT) {
+
+ if (initexpr)
+ pfree(initexpr);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ rt_raster_destroy(newrast);
+
+ elog(ERROR, "RASTER_mapAlgebraExpr: Could not connect to the SPI manager");
+ PG_RETURN_NULL();
+ };
+
+ /* Type of all arguments is FLOAT8OID */
+ spi_plan = SPI_prepare(initexpr, argcount, argtype);
+
+ if (spi_plan == NULL) {
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ rt_raster_destroy(newrast);
+
+ SPI_finish();
+
+ pfree(initexpr);
+
+ elog(ERROR, "RASTER_mapAlgebraExpr: Could not prepare expression");
+ PG_RETURN_NULL();
+ }
+ }
+
+ for (x = 0; x < width; x++) {
+ for(y = 0; y < height; y++) {
+ ret = rt_band_get_pixel(band, x, y, &r, NULL);
+
+ /**
+ * We compute a value only for the withdata value pixel since the
+ * nodata value has already been set by the first optimization
+ **/
+ if (ret == ES_NONE && FLT_NEQ(r, newnodatavalue)) {
+ if (skipcomputation == 0) {
+ if (initexpr != NULL) {
+ /* Reset the null arg flags. */
+ memset(nulls, 'n', argcount);
+
+ for (i = 0; i < argkwcount; i++) {
+ idx = argpos[i];
+ if (idx < 1) continue;
+ idx--;
+
+ if (i == kX ) {
+ /* x is 0 based index, but SQL expects 1 based index */
+ values[idx] = Int32GetDatum(x+1);
+ nulls[idx] = ' ';
+ }
+ else if (i == kY) {
+ /* y is 0 based index, but SQL expects 1 based index */
+ values[idx] = Int32GetDatum(y+1);
+ nulls[idx] = ' ';
+ }
+ else if (i == kVAL ) {
+ values[idx] = Float8GetDatum(r);
+ nulls[idx] = ' ';
+ }
+
+ }
+
+ ret = SPI_execute_plan(spi_plan, values, nulls, FALSE, 0);
+ if (ret != SPI_OK_SELECT || SPI_tuptable == NULL ||
+ SPI_processed != 1) {
+ if (SPI_tuptable)
+ SPI_freetuptable(tuptable);
+
+ SPI_freeplan(spi_plan);
+ SPI_finish();
+
+ pfree(values);
+ pfree(nulls);
+ pfree(initexpr);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ rt_raster_destroy(newrast);
+
+ elog(ERROR, "RASTER_mapAlgebraExpr: Error executing prepared plan");
+
+ PG_RETURN_NULL();
+ }
+
+ tupdesc = SPI_tuptable->tupdesc;
+ tuptable = SPI_tuptable;
+
+ tuple = tuptable->vals[0];
+ datum = SPI_getbinval(tuple, tupdesc, 1, &isnull);
+ if ( SPI_result == SPI_ERROR_NOATTRIBUTE ) {
+ POSTGIS_RT_DEBUGF(3, "Expression for pixel %d,%d (value %g) errored, skip setting", x+1,y+1,r);
+ newval = newinitialvalue;
+ }
+ else if ( isnull ) {
+ POSTGIS_RT_DEBUGF(3, "Expression for pixel %d,%d (value %g) evaluated to NULL, skip setting", x+1,y+1,r);
+ newval = newinitialvalue;
+ } else {
+ newval = DatumGetFloat8(datum);
+ }
+
+ SPI_freetuptable(tuptable);
+ }
+
+ else
+ newval = newinitialvalue;
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraExpr: new value = %f",
+ newval);
+ }
+
+
+ rt_band_set_pixel(newband, x, y, newval, NULL);
+ }
+
+ }
+ }
+
+ if (initexpr != NULL) {
+ SPI_freeplan(spi_plan);
+ SPI_finish();
+
+ pfree(values);
+ pfree(nulls);
+ pfree(initexpr);
+ }
+ else {
+ POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraExpr: no SPI cleanup");
+ }
+
+
+ /* The newrast band has been modified */
+
+ POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraExpr: raster modified, serializing it.");
+ /* Serialize created raster */
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ pgrtn = rt_raster_serialize(newrast);
+ rt_raster_destroy(newrast);
+ if (NULL == pgrtn)
+ PG_RETURN_NULL();
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+
+ POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraExpr: raster serialized");
+
+
+ POSTGIS_RT_DEBUG(4, "RASTER_mapAlgebraExpr: returning raster");
+
+
+ PG_RETURN_POINTER(pgrtn);
+}
+
+/*
+ * One raster user function MapAlgebra.
+ */
+PG_FUNCTION_INFO_V1(RASTER_mapAlgebraFct);
+Datum RASTER_mapAlgebraFct(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_pgraster *pgrtn = NULL;
+ rt_raster raster = NULL;
+ rt_raster newrast = NULL;
+ rt_band band = NULL;
+ rt_band newband = NULL;
+ int x, y, nband, width, height;
+ double r;
+ double newnodatavalue = 0.0;
+ double newinitialvalue = 0.0;
+ double newval = 0.0;
+ rt_pixtype newpixeltype;
+ int ret = -1;
+ Oid oid;
+ FmgrInfo cbinfo;
+ FunctionCallInfoData cbdata;
+ Datum tmpnewval;
+ char * strFromText = NULL;
+ int k = 0;
+
+ POSTGIS_RT_DEBUG(2, "RASTER_mapAlgebraFct: STARTING...");
+
+ /* Check raster */
+ if (PG_ARGISNULL(0)) {
+ elog(WARNING, "Raster is NULL. Returning NULL");
+ PG_RETURN_NULL();
+ }
+
+
+ /* Deserialize raster */
+ pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+ raster = rt_raster_deserialize(pgraster, FALSE);
+ if (NULL == raster) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_mapAlgebraFct: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFct: Getting arguments...");
+
+ /* Get the rest of the arguments */
+
+ if (PG_ARGISNULL(1))
+ nband = 1;
+ else
+ nband = PG_GETARG_INT32(1);
+
+ if (nband < 1)
+ nband = 1;
+
+ POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFct: Creating new empty raster...");
+
+ /**
+ * Create a new empty raster with having the same georeference as the
+ * provided raster
+ **/
+ width = rt_raster_get_width(raster);
+ height = rt_raster_get_height(raster);
+
+ newrast = rt_raster_new(width, height);
+
+ if ( NULL == newrast ) {
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ elog(ERROR, "RASTER_mapAlgebraFct: Could not create a new raster");
+ PG_RETURN_NULL();
+ }
+
+ rt_raster_set_scale(newrast,
+ rt_raster_get_x_scale(raster),
+ rt_raster_get_y_scale(raster));
+
+ rt_raster_set_offsets(newrast,
+ rt_raster_get_x_offset(raster),
+ rt_raster_get_y_offset(raster));
+
+ rt_raster_set_skews(newrast,
+ rt_raster_get_x_skew(raster),
+ rt_raster_get_y_skew(raster));
+
+ rt_raster_set_srid(newrast, rt_raster_get_srid(raster));
+
+
+ /**
+ * If this new raster is empty (width = 0 OR height = 0) then there is
+ * nothing to compute and we return it right now
+ **/
+ if (rt_raster_is_empty(newrast))
+ {
+ elog(NOTICE, "Raster is empty. Returning an empty raster");
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ pgrtn = rt_raster_serialize(newrast);
+ rt_raster_destroy(newrast);
+ if (NULL == pgrtn) {
+ elog(ERROR, "RASTER_mapAlgebraFct: Could not serialize raster");
+ PG_RETURN_NULL();
+ }
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFct: Getting raster band %d...", nband);
+
+ /**
+ * Check if the raster has the required band. Otherwise, return a raster
+ * without band
+ **/
+ if (!rt_raster_has_band(raster, nband - 1)) {
+ elog(NOTICE, "Raster does not have the required band. Returning a raster "
+ "without a band");
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ pgrtn = rt_raster_serialize(newrast);
+ rt_raster_destroy(newrast);
+ if (NULL == pgrtn) {
+ elog(ERROR, "RASTER_mapAlgebraFct: Could not serialize raster");
+ PG_RETURN_NULL();
+ }
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+ /* Get the raster band */
+ band = rt_raster_get_band(raster, nband - 1);
+ if ( NULL == band ) {
+ elog(NOTICE, "Could not get the required band. Returning a raster "
+ "without a band");
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ pgrtn = rt_raster_serialize(newrast);
+ rt_raster_destroy(newrast);
+ if (NULL == pgrtn) {
+ elog(ERROR, "RASTER_mapAlgebraFct: Could not serialize raster");
+ PG_RETURN_NULL();
+ }
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+ /*
+ * Get NODATA value
+ */
+ POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFct: Getting NODATA value for band...");
+
+ if (rt_band_get_hasnodata_flag(band)) {
+ rt_band_get_nodata(band, &newnodatavalue);
+ }
+
+ else {
+ newnodatavalue = rt_band_get_min_value(band);
+ }
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFct: NODATA value for band: %f",
+ newnodatavalue);
+ /**
+ * We set the initial value of the future band to nodata value. If nodata
+ * value is null, then the raster will be initialized to
+ * rt_band_get_min_value but all the values should be recomputed anyway
+ **/
+ newinitialvalue = newnodatavalue;
+
+ /**
+ * Set the new pixeltype
+ **/
+ POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFct: Setting pixeltype...");
+
+ if (PG_ARGISNULL(2)) {
+ newpixeltype = rt_band_get_pixtype(band);
+ }
+
+ else {
+ strFromText = text_to_cstring(PG_GETARG_TEXT_P(2));
+ newpixeltype = rt_pixtype_index_from_name(strFromText);
+ pfree(strFromText);
+ if (newpixeltype == PT_END)
+ newpixeltype = rt_band_get_pixtype(band);
+ }
+
+ if (newpixeltype == PT_END) {
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ rt_raster_destroy(newrast);
+
+ elog(ERROR, "RASTER_mapAlgebraFct: Invalid pixeltype");
+ PG_RETURN_NULL();
+ }
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFct: Pixeltype set to %s",
+ rt_pixtype_name(newpixeltype));
+
+ /* Get the name of the callback user function for raster values */
+ if (PG_ARGISNULL(3)) {
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ rt_raster_destroy(newrast);
+
+ elog(ERROR, "RASTER_mapAlgebraFct: Required function is missing. Returning NULL");
+ PG_RETURN_NULL();
+ }
+
+ oid = PG_GETARG_OID(3);
+ if (oid == InvalidOid) {
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ rt_raster_destroy(newrast);
+
+ elog(ERROR, "RASTER_mapAlgebraFct: Got invalid function object id. Returning NULL");
+ PG_RETURN_NULL();
+ }
+
+ fmgr_info(oid, &cbinfo);
+
+ /* function cannot return set */
+ if (cbinfo.fn_retset) {
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ rt_raster_destroy(newrast);
+
+ elog(ERROR, "RASTER_mapAlgebraFct: Function provided must return double precision not resultset");
+ PG_RETURN_NULL();
+ }
+ /* function should have correct # of args */
+ else if (cbinfo.fn_nargs < 2 || cbinfo.fn_nargs > 3) {
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ rt_raster_destroy(newrast);
+
+ elog(ERROR, "RASTER_mapAlgebraFct: Function does not have two or three input parameters");
+ PG_RETURN_NULL();
+ }
+
+ if (cbinfo.fn_nargs == 2)
+ k = 1;
+ else
+ k = 2;
+
+ if (func_volatile(oid) == 'v') {
+ elog(NOTICE, "Function provided is VOLATILE. Unless required and for best performance, function should be IMMUTABLE or STABLE");
+ }
+
+ /* prep function call data */
+#if POSTGIS_PGSQL_VERSION <= 90
+ InitFunctionCallInfoData(cbdata, &cbinfo, 2, InvalidOid, NULL);
+#else
+ InitFunctionCallInfoData(cbdata, &cbinfo, 2, InvalidOid, NULL, NULL);
+#endif
+ memset(cbdata.argnull, FALSE, sizeof(bool) * cbinfo.fn_nargs);
+
+ /* check that the function isn't strict if the args are null. */
+ if (PG_ARGISNULL(4)) {
+ if (cbinfo.fn_strict) {
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ rt_raster_destroy(newrast);
+
+ elog(ERROR, "RASTER_mapAlgebraFct: Strict callback functions cannot have null parameters");
+ PG_RETURN_NULL();
+ }
+
+ cbdata.arg[k] = (Datum)NULL;
+ cbdata.argnull[k] = TRUE;
+ }
+ else {
+ cbdata.arg[k] = PG_GETARG_DATUM(4);
+ }
+
+ /**
+ * Optimization: If the raster is only filled with nodata values return
+ * right now a raster filled with the nodatavalueexpr
+ * TODO: Call rt_band_check_isnodata instead?
+ **/
+ if (rt_band_get_isnodata_flag(band)) {
+
+ POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFct: Band is a nodata band, returning "
+ "a raster filled with nodata");
+
+ ret = rt_raster_generate_new_band(newrast, newpixeltype,
+ newinitialvalue, TRUE, newnodatavalue, 0);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ /* Serialize created raster */
+ pgrtn = rt_raster_serialize(newrast);
+ rt_raster_destroy(newrast);
+ if (NULL == pgrtn) {
+ elog(ERROR, "RASTER_mapAlgebraFct: Could not serialize raster");
+ PG_RETURN_NULL();
+ }
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+
+ /**
+ * Create the raster receiving all the computed values. Initialize it to the
+ * new initial value
+ **/
+ ret = rt_raster_generate_new_band(newrast, newpixeltype,
+ newinitialvalue, TRUE, newnodatavalue, 0);
+
+ /* Get the new raster band */
+ newband = rt_raster_get_band(newrast, 0);
+ if ( NULL == newband ) {
+ elog(NOTICE, "Could not modify band for new raster. Returning new "
+ "raster with the original band");
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ /* Serialize created raster */
+ pgrtn = rt_raster_serialize(newrast);
+ rt_raster_destroy(newrast);
+ if (NULL == pgrtn) {
+ elog(ERROR, "RASTER_mapAlgebraFct: Could not serialize raster");
+ PG_RETURN_NULL();
+ }
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFct: Main computing loop (%d x %d)",
+ width, height);
+
+ for (x = 0; x < width; x++) {
+ for(y = 0; y < height; y++) {
+ ret = rt_band_get_pixel(band, x, y, &r, NULL);
+
+ /**
+ * We compute a value only for the withdata value pixel since the
+ * nodata value has already been set by the first optimization
+ **/
+ if (ret == ES_NONE) {
+ if (FLT_EQ(r, newnodatavalue)) {
+ if (cbinfo.fn_strict) {
+ POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFct: Strict callbacks cannot accept NULL arguments, skipping NODATA cell.");
+ continue;
+ }
+ cbdata.argnull[0] = TRUE;
+ cbdata.arg[0] = (Datum)NULL;
+ }
+ else {
+ cbdata.argnull[0] = FALSE;
+ cbdata.arg[0] = Float8GetDatum(r);
+ }
+
+ /* Add pixel positions if callback has proper # of args */
+ if (cbinfo.fn_nargs == 3) {
+ Datum d[2];
+ ArrayType *a;
+
+ d[0] = Int32GetDatum(x+1);
+ d[1] = Int32GetDatum(y+1);
+
+ a = construct_array(d, 2, INT4OID, sizeof(int32), true, 'i');
+
+ cbdata.argnull[1] = FALSE;
+ cbdata.arg[1] = PointerGetDatum(a);
+ }
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFct: (%dx%d), r = %f",
+ x, y, r);
+
+ tmpnewval = FunctionCallInvoke(&cbdata);
+
+ if (cbdata.isnull) {
+ newval = newnodatavalue;
+ }
+ else {
+ newval = DatumGetFloat8(tmpnewval);
+ }
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFct: new value = %f",
+ newval);
+
+ rt_band_set_pixel(newband, x, y, newval, NULL);
+ }
+
+ }
+ }
+
+ /* The newrast band has been modified */
+
+ POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFct: raster modified, serializing it.");
+ /* Serialize created raster */
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ pgrtn = rt_raster_serialize(newrast);
+ rt_raster_destroy(newrast);
+ if (NULL == pgrtn)
+ PG_RETURN_NULL();
+
+ POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFct: raster serialized");
+
+ POSTGIS_RT_DEBUG(4, "RASTER_mapAlgebraFct: returning raster");
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+}
+
+/**
+ * One raster neighborhood MapAlgebra
+ */
+PG_FUNCTION_INFO_V1(RASTER_mapAlgebraFctNgb);
+Datum RASTER_mapAlgebraFctNgb(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_pgraster *pgrtn = NULL;
+ rt_raster raster = NULL;
+ rt_raster newrast = NULL;
+ rt_band band = NULL;
+ rt_band newband = NULL;
+ int x, y, nband, width, height, ngbwidth, ngbheight, winwidth, winheight, u, v, nIndex, nNullItems;
+ double r, rpix;
+ double newnodatavalue = 0.0;
+ double newinitialvalue = 0.0;
+ double newval = 0.0;
+ rt_pixtype newpixeltype;
+ int ret = -1;
+ Oid oid;
+ FmgrInfo cbinfo;
+ FunctionCallInfoData cbdata;
+ Datum tmpnewval;
+ ArrayType * neighborDatum;
+ char * strFromText = NULL;
+ text * txtNodataMode = NULL;
+ text * txtCallbackParam = NULL;
+ int intReplace = 0;
+ float fltReplace = 0;
+ bool valuereplace = false, pixelreplace, nNodataOnly = true, nNullSkip = false;
+ Datum * neighborData = NULL;
+ bool * neighborNulls = NULL;
+ int neighborDims[2];
+ int neighborLbs[2];
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+
+ POSTGIS_RT_DEBUG(2, "RASTER_mapAlgebraFctNgb: STARTING...");
+
+ /* Check raster */
+ if (PG_ARGISNULL(0)) {
+ elog(WARNING, "Raster is NULL. Returning NULL");
+ PG_RETURN_NULL();
+ }
+
+
+ /* Deserialize raster */
+ pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+ raster = rt_raster_deserialize(pgraster, FALSE);
+ if (NULL == raster)
+ {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_mapAlgebraFctNgb: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFctNgb: Getting arguments...");
+
+ /* Get the rest of the arguments */
+
+ if (PG_ARGISNULL(1))
+ nband = 1;
+ else
+ nband = PG_GETARG_INT32(1);
+
+ if (nband < 1)
+ nband = 1;
+
+ POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFctNgb: Creating new empty raster...");
+
+ /**
+ * Create a new empty raster with having the same georeference as the
+ * provided raster
+ **/
+ width = rt_raster_get_width(raster);
+ height = rt_raster_get_height(raster);
+
+ newrast = rt_raster_new(width, height);
+
+ if ( NULL == newrast ) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_mapAlgebraFctNgb: Could not create a new raster");
+ PG_RETURN_NULL();
+ }
+
+ rt_raster_set_scale(newrast,
+ rt_raster_get_x_scale(raster),
+ rt_raster_get_y_scale(raster));
+
+ rt_raster_set_offsets(newrast,
+ rt_raster_get_x_offset(raster),
+ rt_raster_get_y_offset(raster));
+
+ rt_raster_set_skews(newrast,
+ rt_raster_get_x_skew(raster),
+ rt_raster_get_y_skew(raster));
+
+ rt_raster_set_srid(newrast, rt_raster_get_srid(raster));
+
+
+ /**
+ * If this new raster is empty (width = 0 OR height = 0) then there is
+ * nothing to compute and we return it right now
+ **/
+ if (rt_raster_is_empty(newrast))
+ {
+ elog(NOTICE, "Raster is empty. Returning an empty raster");
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ pgrtn = rt_raster_serialize(newrast);
+ rt_raster_destroy(newrast);
+ if (NULL == pgrtn) {
+ elog(ERROR, "RASTER_mapAlgebraFctNgb: Could not serialize raster");
+ PG_RETURN_NULL();
+ }
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFctNgb: Getting raster band %d...", nband);
+
+ /**
+ * Check if the raster has the required band. Otherwise, return a raster
+ * without band
+ **/
+ if (!rt_raster_has_band(raster, nband - 1)) {
+ elog(NOTICE, "Raster does not have the required band. Returning a raster "
+ "without a band");
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ pgrtn = rt_raster_serialize(newrast);
+ rt_raster_destroy(newrast);
+ if (NULL == pgrtn) {
+ elog(ERROR, "RASTER_mapAlgebraFctNgb: Could not serialize raster");
+ PG_RETURN_NULL();
+ }
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+ /* Get the raster band */
+ band = rt_raster_get_band(raster, nband - 1);
+ if ( NULL == band ) {
+ elog(NOTICE, "Could not get the required band. Returning a raster "
+ "without a band");
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ pgrtn = rt_raster_serialize(newrast);
+ rt_raster_destroy(newrast);
+ if (NULL == pgrtn) {
+ elog(ERROR, "RASTER_mapAlgebraFctNgb: Could not serialize raster");
+ PG_RETURN_NULL();
+ }
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+ /*
+ * Get NODATA value
+ */
+ POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFctNgb: Getting NODATA value for band...");
+
+ if (rt_band_get_hasnodata_flag(band)) {
+ rt_band_get_nodata(band, &newnodatavalue);
+ }
+
+ else {
+ newnodatavalue = rt_band_get_min_value(band);
+ }
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFctNgb: NODATA value for band: %f",
+ newnodatavalue);
+ /**
+ * We set the initial value of the future band to nodata value. If nodata
+ * value is null, then the raster will be initialized to
+ * rt_band_get_min_value but all the values should be recomputed anyway
+ **/
+ newinitialvalue = newnodatavalue;
+
+ /**
+ * Set the new pixeltype
+ **/
+ POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFctNgb: Setting pixeltype...");
+
+ if (PG_ARGISNULL(2)) {
+ newpixeltype = rt_band_get_pixtype(band);
+ }
+
+ else {
+ strFromText = text_to_cstring(PG_GETARG_TEXT_P(2));
+ POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFctNgb: Pixeltype parameter: %s", strFromText);
+ newpixeltype = rt_pixtype_index_from_name(strFromText);
+ pfree(strFromText);
+ if (newpixeltype == PT_END)
+ newpixeltype = rt_band_get_pixtype(band);
+ }
+
+ if (newpixeltype == PT_END) {
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ rt_raster_destroy(newrast);
+
+ elog(ERROR, "RASTER_mapAlgebraFctNgb: Invalid pixeltype");
+ PG_RETURN_NULL();
+ }
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFctNgb: Pixeltype set to %s (%d)",
+ rt_pixtype_name(newpixeltype), newpixeltype);
+
+ /* Get the name of the callback userfunction */
+ if (PG_ARGISNULL(5)) {
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ rt_raster_destroy(newrast);
+
+ elog(ERROR, "RASTER_mapAlgebraFctNgb: Required function is missing");
+ PG_RETURN_NULL();
+ }
+
+ oid = PG_GETARG_OID(5);
+ if (oid == InvalidOid) {
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ rt_raster_destroy(newrast);
+
+ elog(ERROR, "RASTER_mapAlgebraFctNgb: Got invalid function object id");
+ PG_RETURN_NULL();
+ }
+
+ fmgr_info(oid, &cbinfo);
+
+ /* function cannot return set */
+ if (cbinfo.fn_retset) {
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ rt_raster_destroy(newrast);
+
+ elog(ERROR, "RASTER_mapAlgebraFctNgb: Function provided must return double precision not resultset");
+ PG_RETURN_NULL();
+ }
+ /* function should have correct # of args */
+ else if (cbinfo.fn_nargs != 3) {
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ rt_raster_destroy(newrast);
+
+ elog(ERROR, "RASTER_mapAlgebraFctNgb: Function does not have three input parameters");
+ PG_RETURN_NULL();
+ }
+
+ if (func_volatile(oid) == 'v') {
+ elog(NOTICE, "Function provided is VOLATILE. Unless required and for best performance, function should be IMMUTABLE or STABLE");
+ }
+
+ /* prep function call data */
+#if POSTGIS_PGSQL_VERSION <= 90
+ InitFunctionCallInfoData(cbdata, &cbinfo, 3, InvalidOid, NULL);
+#else
+ InitFunctionCallInfoData(cbdata, &cbinfo, 3, InvalidOid, NULL, NULL);
+#endif
+ memset(cbdata.argnull, FALSE, sizeof(bool) * 3);
+
+ /* check that the function isn't strict if the args are null. */
+ if (PG_ARGISNULL(7)) {
+ if (cbinfo.fn_strict) {
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ rt_raster_destroy(newrast);
+
+ elog(ERROR, "RASTER_mapAlgebraFctNgb: Strict callback functions cannot have NULL parameters");
+ PG_RETURN_NULL();
+ }
+
+ cbdata.arg[2] = (Datum)NULL;
+ cbdata.argnull[2] = TRUE;
+ }
+ else {
+ cbdata.arg[2] = PG_GETARG_DATUM(7);
+ }
+
+ /**
+ * Optimization: If the raster is only filled with nodata values return
+ * right now a raster filled with the nodatavalueexpr
+ * TODO: Call rt_band_check_isnodata instead?
+ **/
+ if (rt_band_get_isnodata_flag(band)) {
+
+ POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFctNgb: Band is a nodata band, returning "
+ "a raster filled with nodata");
+
+ rt_raster_generate_new_band(newrast, newpixeltype,
+ newinitialvalue, TRUE, newnodatavalue, 0);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ /* Serialize created raster */
+ pgrtn = rt_raster_serialize(newrast);
+ rt_raster_destroy(newrast);
+ if (NULL == pgrtn) {
+ elog(ERROR, "RASTER_mapAlgebraFctNgb: Could not serialize raster");
+ PG_RETURN_NULL();
+ }
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+
+ /**
+ * Create the raster receiving all the computed values. Initialize it to the
+ * new initial value
+ **/
+ rt_raster_generate_new_band(newrast, newpixeltype,
+ newinitialvalue, TRUE, newnodatavalue, 0);
+
+ /* Get the new raster band */
+ newband = rt_raster_get_band(newrast, 0);
+ if ( NULL == newband ) {
+ elog(NOTICE, "Could not modify band for new raster. Returning new "
+ "raster with the original band");
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ /* Serialize created raster */
+ pgrtn = rt_raster_serialize(newrast);
+ rt_raster_destroy(newrast);
+ if (NULL == pgrtn) {
+ elog(ERROR, "RASTER_mapAlgebraFctNgb: Could not serialize raster");
+ PG_RETURN_NULL();
+ }
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+ /* Get the width of the neighborhood */
+ if (PG_ARGISNULL(3) || PG_GETARG_INT32(3) <= 0) {
+ elog(NOTICE, "Neighborhood width is NULL or <= 0. Returning new "
+ "raster with the original band");
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ /* Serialize created raster */
+ pgrtn = rt_raster_serialize(newrast);
+ rt_raster_destroy(newrast);
+ if (NULL == pgrtn) {
+ elog(ERROR, "RASTER_mapAlgebraFctNgb: Could not serialize raster");
+ PG_RETURN_NULL();
+ }
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+ ngbwidth = PG_GETARG_INT32(3);
+ winwidth = ngbwidth * 2 + 1;
+
+ /* Get the height of the neighborhood */
+ if (PG_ARGISNULL(4) || PG_GETARG_INT32(4) <= 0) {
+ elog(NOTICE, "Neighborhood height is NULL or <= 0. Returning new "
+ "raster with the original band");
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ /* Serialize created raster */
+ pgrtn = rt_raster_serialize(newrast);
+ rt_raster_destroy(newrast);
+ if (NULL == pgrtn) {
+ elog(ERROR, "RASTER_mapAlgebraFctNgb: Could not serialize raster");
+ PG_RETURN_NULL();
+ }
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+ ngbheight = PG_GETARG_INT32(4);
+ winheight = ngbheight * 2 + 1;
+
+ /* Get the type of NODATA behavior for the neighborhoods. */
+ if (PG_ARGISNULL(6)) {
+ elog(NOTICE, "Neighborhood NODATA behavior defaulting to 'ignore'");
+ txtNodataMode = cstring_to_text("ignore");
+ }
+ else {
+ txtNodataMode = PG_GETARG_TEXT_P(6);
+ }
+
+ txtCallbackParam = (text*)palloc(VARSIZE(txtNodataMode));
+ SET_VARSIZE(txtCallbackParam, VARSIZE(txtNodataMode));
+ memcpy((void *)VARDATA(txtCallbackParam), (void *)VARDATA(txtNodataMode), VARSIZE(txtNodataMode) - VARHDRSZ);
+
+ /* pass the nodata mode into the user function */
+ cbdata.arg[1] = CStringGetDatum(txtCallbackParam);
+
+ strFromText = text_to_cstring(txtNodataMode);
+ strFromText = rtpg_strtoupper(strFromText);
+
+ if (strcmp(strFromText, "VALUE") == 0)
+ valuereplace = true;
+ else if (strcmp(strFromText, "IGNORE") != 0 && strcmp(strFromText, "NULL") != 0) {
+ /* if the text is not "IGNORE" or "NULL", it may be a numerical value */
+ if (sscanf(strFromText, "%d", &intReplace) <= 0 && sscanf(strFromText, "%f", &fltReplace) <= 0) {
+ /* the value is NOT an integer NOR a floating point */
+ elog(NOTICE, "Neighborhood NODATA mode is not recognized. Must be one of 'value', 'ignore', "
+ "'NULL', or a numeric value. Returning new raster with the original band");
+
+ /* clean up the nodatamode string */
+ pfree(txtCallbackParam);
+ pfree(strFromText);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ /* Serialize created raster */
+ pgrtn = rt_raster_serialize(newrast);
+ rt_raster_destroy(newrast);
+ if (NULL == pgrtn) {
+ elog(ERROR, "RASTER_mapAlgebraFctNgb: Could not serialize raster");
+ PG_RETURN_NULL();
+ }
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+ }
+ else if (strcmp(strFromText, "NULL") == 0) {
+ /* this setting means that the neighborhood should be skipped if any of the values are null */
+ nNullSkip = true;
+ }
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFctNgb: Main computing loop (%d x %d)",
+ width, height);
+
+ /* Allocate room for the neighborhood. */
+ neighborData = (Datum *)palloc(winwidth * winheight * sizeof(Datum));
+ neighborNulls = (bool *)palloc(winwidth * winheight * sizeof(bool));
+
+ /* The dimensions of the neighborhood array, for creating a multi-dimensional array. */
+ neighborDims[0] = winwidth;
+ neighborDims[1] = winheight;
+
+ /* The lower bounds for the new multi-dimensional array. */
+ neighborLbs[0] = 1;
+ neighborLbs[1] = 1;
+
+ /* Get information about the type of item in the multi-dimensional array (float8). */
+ get_typlenbyvalalign(FLOAT8OID, &typlen, &typbyval, &typalign);
+
+ for (x = 0 + ngbwidth; x < width - ngbwidth; x++) {
+ for(y = 0 + ngbheight; y < height - ngbheight; y++) {
+ /* populate an array with the pixel values in the neighborhood */
+ nIndex = 0;
+ nNullItems = 0;
+ nNodataOnly = true;
+ pixelreplace = false;
+ if (valuereplace) {
+ ret = rt_band_get_pixel(band, x, y, &rpix, NULL);
+ if (ret == ES_NONE && FLT_NEQ(rpix, newnodatavalue)) {
+ pixelreplace = true;
+ }
+ }
+ for (u = x - ngbwidth; u <= x + ngbwidth; u++) {
+ for (v = y - ngbheight; v <= y + ngbheight; v++) {
+ ret = rt_band_get_pixel(band, u, v, &r, NULL);
+ if (ret == ES_NONE) {
+ if (FLT_NEQ(r, newnodatavalue)) {
+ /* If the pixel value for this neighbor cell is not NODATA */
+ neighborData[nIndex] = Float8GetDatum((double)r);
+ neighborNulls[nIndex] = false;
+ nNodataOnly = false;
+ }
+ else {
+ /* If the pixel value for this neighbor cell is NODATA */
+ if (valuereplace && pixelreplace) {
+ /* Replace the NODATA value with the currently processing pixel. */
+ neighborData[nIndex] = Float8GetDatum((double)rpix);
+ neighborNulls[nIndex] = false;
+ /* do not increment nNullItems, since the user requested that the */
+ /* neighborhood replace NODATA values with the central pixel value */
+ }
+ else {
+ neighborData[nIndex] = PointerGetDatum(NULL);
+ neighborNulls[nIndex] = true;
+ nNullItems++;
+ }
+ }
+ }
+ else {
+ /* Fill this will NULL if we can't read the raster pixel. */
+ neighborData[nIndex] = PointerGetDatum(NULL);
+ neighborNulls[nIndex] = true;
+ nNullItems++;
+ }
+ /* Next neighbor position */
+ nIndex++;
+ }
+ }
+
+ /**
+ * We compute a value only for the withdata value neighborhood since the
+ * nodata value has already been set by the first optimization
+ **/
+ if (!(nNodataOnly || /* neighborhood only contains NODATA -- OR -- */
+ (nNullSkip && nNullItems > 0) || /* neighborhood should skip any NODATA cells, and a NODATA cell was detected -- OR -- */
+ (valuereplace && nNullItems > 0))) { /* neighborhood should replace NODATA cells with the central pixel value, and a NODATA cell was detected */
+ POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFctNgb: (%dx%d), %dx%d neighborhood",
+ x, y, winwidth, winheight);
+
+ neighborDatum = construct_md_array((void *)neighborData, neighborNulls, 2, neighborDims, neighborLbs,
+ FLOAT8OID, typlen, typbyval, typalign);
+
+ /* Assign the neighbor matrix as the first argument to the user function */
+ cbdata.arg[0] = PointerGetDatum(neighborDatum);
+
+ /* Invoke the user function */
+ tmpnewval = FunctionCallInvoke(&cbdata);
+
+ /* Get the return value of the user function */
+ if (cbdata.isnull) {
+ newval = newnodatavalue;
+ }
+ else {
+ newval = DatumGetFloat8(tmpnewval);
+ }
+
+ POSTGIS_RT_DEBUGF(3, "RASTER_mapAlgebraFctNgb: new value = %f",
+ newval);
+
+ rt_band_set_pixel(newband, x, y, newval, NULL);
+ }
+
+ /* reset the number of null items in the neighborhood */
+ nNullItems = 0;
+ }
+ }
+
+
+ /* clean up */
+ pfree(neighborNulls);
+ pfree(neighborData);
+ pfree(strFromText);
+ pfree(txtCallbackParam);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ /* The newrast band has been modified */
+
+ POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFctNgb: raster modified, serializing it.");
+ /* Serialize created raster */
+
+ pgrtn = rt_raster_serialize(newrast);
+ rt_raster_destroy(newrast);
+ if (NULL == pgrtn)
+ PG_RETURN_NULL();
+
+ POSTGIS_RT_DEBUG(3, "RASTER_mapAlgebraFctNgb: raster serialized");
+ POSTGIS_RT_DEBUG(4, "RASTER_mapAlgebraFctNgb: returning raster");
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+}
+
+/**
+ * Two raster MapAlgebra
+ */
+PG_FUNCTION_INFO_V1(RASTER_mapAlgebra2);
+Datum RASTER_mapAlgebra2(PG_FUNCTION_ARGS)
+{
+ const int set_count = 2;
+ rt_pgraster *pgrast[2];
+ int pgrastpos[2] = {-1, -1};
+ rt_pgraster *pgrtn;
+ rt_raster rast[2] = {NULL};
+ int _isempty[2] = {0};
+ 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};
+ int _pos[2][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;
+ double gt[6] = {0.};
+
+ Oid calltype = InvalidOid;
+
+ const int spi_count = 3;
+ uint16_t spi_exprpos[3] = {4, 7, 8};
+ uint32_t spi_argcount[3] = {0};
+ char *expr = NULL;
+ char *sql = NULL;
+ SPIPlanPtr spi_plan[3] = {NULL};
+ uint16_t spi_empty = 0;
+ Oid *argtype = NULL;
+ const int argkwcount = 8;
+ uint8_t argpos[3][8] = {{0}};
+ char *argkw[] = {"[rast1.x]", "[rast1.y]", "[rast1.val]", "[rast1]", "[rast2.x]", "[rast2.y]", "[rast2.val]", "[rast2]"};
+ Datum values[argkwcount];
+ bool nulls[argkwcount];
+ TupleDesc tupdesc;
+ SPITupleTable *tuptable = NULL;
+ HeapTuple tuple;
+ Datum datum;
+ bool isnull = FALSE;
+ int hasargval[3] = {0};
+ double argval[3] = {0.};
+ int hasnodatanodataval = 0;
+ double nodatanodataval = 0;
+ int isnodata = 0;
+
+ Oid ufc_noid = InvalidOid;
+ FmgrInfo ufl_info;
+ FunctionCallInfoData ufc_info;
+ int ufc_nullcount = 0;
+
+ int idx = 0;
+ uint32_t i = 0;
+ uint32_t j = 0;
+ uint32_t k = 0;
+ uint32_t x = 0;
+ uint32_t y = 0;
+ int _x = 0;
+ int _y = 0;
+ int err;
+ int aligned = 0;
+ int len = 0;
+
+ POSTGIS_RT_DEBUG(3, "Starting RASTER_mapAlgebra2");
+
+ for (i = 0, j = 0; i < set_count; i++) {
+ if (!PG_ARGISNULL(j)) {
+ pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(j));
+ pgrastpos[i] = j;
+ j++;
+
+ /* raster */
+ rast[i] = rt_raster_deserialize(pgrast[i], FALSE);
+ if (!rast[i]) {
+ for (k = 0; k <= i; k++) {
+ if (k < i && rast[k] != NULL)
+ rt_raster_destroy(rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "RASTER_mapAlgebra2: Could not deserialize the %s raster", i < 1 ? "first" : "second");
+ PG_RETURN_NULL();
+ }
+
+ /* empty */
+ _isempty[i] = rt_raster_is_empty(rast[i]);
+
+ /* band index */
+ if (!PG_ARGISNULL(j)) {
+ bandindex[i] = PG_GETARG_INT32(j);
+ }
+ j++;
+ }
+ else {
+ _isempty[i] = 1;
+ j += 2;
+ }
+
+ POSTGIS_RT_DEBUGF(3, "_isempty[%d] = %d", i, _isempty[i]);
+ }
+
+ /* both rasters are NULL */
+ if (rast[0] == NULL && rast[1] == NULL) {
+ elog(NOTICE, "The two rasters provided are NULL. Returning NULL");
+ for (k = 0; k < set_count; k++) {
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ /* both rasters are empty */
+ if (_isempty[0] && _isempty[1]) {
+ elog(NOTICE, "The two rasters provided are empty. Returning empty raster");
+
+ raster = rt_raster_new(0, 0);
+ if (raster == NULL) {
+ for (k = 0; k < set_count; k++) {
+ if (rast[k] != NULL)
+ rt_raster_destroy(rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "RASTER_mapAlgebra2: Could not create empty raster");
+ PG_RETURN_NULL();
+ }
+ rt_raster_set_scale(raster, 0, 0);
+
+ pgrtn = rt_raster_serialize(raster);
+ rt_raster_destroy(raster);
+ if (!pgrtn)
+ PG_RETURN_NULL();
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+ /* replace the empty or NULL raster with one matching the other */
+ if (
+ (rast[0] == NULL || _isempty[0]) ||
+ (rast[1] == NULL || _isempty[1])
+ ) {
+ /* first raster is empty */
+ if (rast[0] == NULL || _isempty[0]) {
+ i = 0;
+ j = 1;
+ }
+ /* second raster is empty */
+ else {
+ i = 1;
+ j = 0;
+ }
+
+ _rast[j] = rast[j];
+
+ /* raster is empty, destroy it */
+ if (_rast[i] != NULL)
+ rt_raster_destroy(_rast[i]);
+
+ _dim[i][0] = rt_raster_get_width(_rast[j]);
+ _dim[i][1] = rt_raster_get_height(_rast[j]);
+ _dim[j][0] = rt_raster_get_width(_rast[j]);
+ _dim[j][1] = rt_raster_get_height(_rast[j]);
+
+ _rast[i] = rt_raster_new(
+ _dim[j][0],
+ _dim[j][1]
+ );
+ if (_rast[i] == NULL) {
+ rt_raster_destroy(_rast[j]);
+ for (k = 0; k < set_count; k++) {
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "RASTER_mapAlgebra2: Could not create NODATA raster");
+ PG_RETURN_NULL();
+ }
+ rt_raster_set_srid(_rast[i], rt_raster_get_srid(_rast[j]));
+
+ rt_raster_get_geotransform_matrix(_rast[j], gt);
+ rt_raster_set_geotransform_matrix(_rast[i], gt);
+ }
+ else {
+ _rast[0] = rast[0];
+ _dim[0][0] = rt_raster_get_width(_rast[0]);
+ _dim[0][1] = rt_raster_get_height(_rast[0]);
+
+ _rast[1] = rast[1];
+ _dim[1][0] = rt_raster_get_width(_rast[1]);
+ _dim[1][1] = rt_raster_get_height(_rast[1]);
+ }
+
+ /* SRID must match */
+ /*
+ if (rt_raster_get_srid(_rast[0]) != rt_raster_get_srid(_rast[1])) {
+ elog(NOTICE, "The two rasters provided have different SRIDs. Returning NULL");
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+ */
+
+ /* same alignment */
+ if (rt_raster_same_alignment(_rast[0], _rast[1], &aligned, NULL) != ES_NONE) {
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "RASTER_mapAlgebra2: Could not test for alignment on the two rasters");
+ PG_RETURN_NULL();
+ }
+ if (!aligned) {
+ elog(NOTICE, "The two rasters provided do not have the same alignment. Returning NULL");
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ /* pixel type */
+ if (!PG_ARGISNULL(5)) {
+ pixtypename = text_to_cstring(PG_GETARG_TEXT_P(5));
+ /* Get the pixel type index */
+ pixtype = rt_pixtype_index_from_name(pixtypename);
+ if (pixtype == PT_END ) {
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "RASTER_mapAlgebra2: Invalid pixel type: %s", pixtypename);
+ PG_RETURN_NULL();
+ }
+ }
+
+ /* extent type */
+ if (!PG_ARGISNULL(6)) {
+ extenttypename = rtpg_strtoupper(rtpg_trim(text_to_cstring(PG_GETARG_TEXT_P(6))));
+ extenttype = rt_util_extent_type(extenttypename);
+ }
+ POSTGIS_RT_DEBUGF(3, "extenttype: %d %s", extenttype, extenttypename);
+
+ /* computed raster from extent type */
+ err = rt_raster_from_two_rasters(
+ _rast[0], _rast[1],
+ extenttype,
+ &raster, _offset
+ );
+ if (err != ES_NONE) {
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "RASTER_mapAlgebra2: Could not get output raster of correct extent");
+ PG_RETURN_NULL();
+ }
+
+ /* copy offsets */
+ _rastoffset[0][0] = _offset[0];
+ _rastoffset[0][1] = _offset[1];
+ _rastoffset[1][0] = _offset[2];
+ _rastoffset[1][1] = _offset[3];
+
+ /* get output raster dimensions */
+ dim[0] = rt_raster_get_width(raster);
+ dim[1] = rt_raster_get_height(raster);
+
+ i = 2;
+ /* handle special cases for extent */
+ switch (extenttype) {
+ case ET_FIRST:
+ i = 0;
+ case ET_SECOND:
+ if (i > 1)
+ i = 1;
+
+ if (
+ _isempty[i] && (
+ (extenttype == ET_FIRST && i == 0) ||
+ (extenttype == ET_SECOND && i == 1)
+ )
+ ) {
+ elog(NOTICE, "The %s raster is NULL. Returning NULL", (i != 1 ? "FIRST" : "SECOND"));
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ rt_raster_destroy(raster);
+ PG_RETURN_NULL();
+ }
+
+ /* specified band not found */
+ if (!rt_raster_has_band(_rast[i], bandindex[i] - 1)) {
+ elog(NOTICE, "The %s raster does not have the band at index %d. Returning no band raster of correct extent",
+ (i != 1 ? "FIRST" : "SECOND"), bandindex[i]
+ );
+
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+
+ pgrtn = rt_raster_serialize(raster);
+ rt_raster_destroy(raster);
+ if (!pgrtn) PG_RETURN_NULL();
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+ break;
+ case ET_UNION:
+ break;
+ case ET_INTERSECTION:
+ /* no intersection */
+ if (
+ _isempty[0] || _isempty[1] ||
+ !dim[0] || !dim[1]
+ ) {
+ elog(NOTICE, "The two rasters provided have no intersection. Returning no band raster");
+
+ /* raster has dimension, replace with no band raster */
+ if (dim[0] || dim[1]) {
+ rt_raster_destroy(raster);
+
+ raster = rt_raster_new(0, 0);
+ if (raster == NULL) {
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "RASTER_mapAlgebra2: Could not create no band raster");
+ PG_RETURN_NULL();
+ }
+
+ rt_raster_set_scale(raster, 0, 0);
+ rt_raster_set_srid(raster, rt_raster_get_srid(_rast[0]));
+ }
+
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+
+ pgrtn = rt_raster_serialize(raster);
+ rt_raster_destroy(raster);
+ if (!pgrtn) PG_RETURN_NULL();
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+ break;
+ case ET_LAST:
+ case ET_CUSTOM:
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "RASTER_mapAlgebra2: ET_LAST and ET_CUSTOM are not implemented");
+ PG_RETURN_NULL();
+ break;
+ }
+
+ /* both rasters do not have specified bands */
+ if (
+ (!_isempty[0] && !rt_raster_has_band(_rast[0], bandindex[0] - 1)) &&
+ (!_isempty[1] && !rt_raster_has_band(_rast[1], bandindex[1] - 1))
+ ) {
+ elog(NOTICE, "The two rasters provided do not have the respectively specified band indices. Returning no band raster of correct extent");
+
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+
+ pgrtn = rt_raster_serialize(raster);
+ rt_raster_destroy(raster);
+ if (!pgrtn) PG_RETURN_NULL();
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+ }
+
+ /* get bands */
+ for (i = 0; i < set_count; i++) {
+ if (_isempty[i] || !rt_raster_has_band(_rast[i], bandindex[i] - 1)) {
+ _hasnodata[i] = 1;
+ _nodataval[i] = 0;
+
+ continue;
+ }
+
+ _band[i] = rt_raster_get_band(_rast[i], bandindex[i] - 1);
+ if (_band[i] == NULL) {
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ rt_raster_destroy(raster);
+ elog(ERROR, "RASTER_mapAlgebra2: Could not get band %d of the %s raster",
+ bandindex[i],
+ (i < 1 ? "FIRST" : "SECOND")
+ );
+ PG_RETURN_NULL();
+ }
+
+ _hasnodata[i] = rt_band_get_hasnodata_flag(_band[i]);
+ if (_hasnodata[i])
+ rt_band_get_nodata(_band[i], &(_nodataval[i]));
+ }
+
+ /* pixtype is PT_END, get pixtype based upon extent */
+ if (pixtype == PT_END) {
+ if ((extenttype == ET_SECOND && !_isempty[1]) || _isempty[0])
+ pixtype = rt_band_get_pixtype(_band[1]);
+ else
+ pixtype = rt_band_get_pixtype(_band[0]);
+ }
+
+ /* nodata value for new band */
+ if (extenttype == ET_SECOND && !_isempty[1] && _hasnodata[1]) {
+ nodataval = _nodataval[1];
+ }
+ else if (!_isempty[0] && _hasnodata[0]) {
+ nodataval = _nodataval[0];
+ }
+ else if (!_isempty[1] && _hasnodata[1]) {
+ nodataval = _nodataval[1];
+ }
+ else {
+ elog(NOTICE, "Neither raster provided has a NODATA value for the specified band indices. NODATA value set to minimum possible for %s", rt_pixtype_name(pixtype));
+ nodataval = rt_pixtype_get_min_value(pixtype);
+ }
+
+ /* add band to output raster */
+ if (rt_raster_generate_new_band(
+ raster,
+ pixtype,
+ nodataval,
+ 1, nodataval,
+ 0
+ ) < 0) {
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ rt_raster_destroy(raster);
+ elog(ERROR, "RASTER_mapAlgebra2: Could not add new band to output raster");
+ PG_RETURN_NULL();
+ }
+
+ /* get output band */
+ band = rt_raster_get_band(raster, 0);
+ if (band == NULL) {
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ rt_raster_destroy(raster);
+ elog(ERROR, "RASTER_mapAlgebra2: Could not get newly added band of output raster");
+ PG_RETURN_NULL();
+ }
+
+ POSTGIS_RT_DEBUGF(4, "offsets = (%d, %d, %d, %d)",
+ (int) _rastoffset[0][0],
+ (int) _rastoffset[0][1],
+ (int) _rastoffset[1][0],
+ (int) _rastoffset[1][1]
+ );
+
+ POSTGIS_RT_DEBUGF(4, "metadata = (%f, %f, %d, %d, %f, %f, %f, %f, %d)",
+ rt_raster_get_x_offset(raster),
+ rt_raster_get_y_offset(raster),
+ rt_raster_get_width(raster),
+ rt_raster_get_height(raster),
+ rt_raster_get_x_scale(raster),
+ rt_raster_get_y_scale(raster),
+ rt_raster_get_x_skew(raster),
+ rt_raster_get_y_skew(raster),
+ rt_raster_get_srid(raster)
+ );
+
+ /*
+ determine who called this function
+ Arg 4 will either be text or regprocedure
+ */
+ 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\"!");
+
+ /* connect SPI */
+ if (SPI_connect() != SPI_OK_CONNECT) {
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ rt_raster_destroy(raster);
+ elog(ERROR, "RASTER_mapAlgebra2: Could not connect to the SPI manager");
+ PG_RETURN_NULL();
+ }
+
+ /* reset hasargval */
+ memset(hasargval, 0, sizeof(int) * spi_count);
+
+ /*
+ process expressions
+
+ spi_exprpos elements are:
+ 4 - expression => spi_plan[0]
+ 7 - nodata1expr => spi_plan[1]
+ 8 - nodata2expr => spi_plan[2]
+ */
+ for (i = 0; i < spi_count; i++) {
+ if (!PG_ARGISNULL(spi_exprpos[i])) {
+ char *tmp = NULL;
+ char place[5] = "$1";
+ expr = text_to_cstring(PG_GETARG_TEXT_P(spi_exprpos[i]));
+ POSTGIS_RT_DEBUGF(3, "raw expr #%d: %s", i, expr);
+
+ for (j = 0, k = 1; j < argkwcount; j++) {
+ /* attempt to replace keyword with placeholder */
+ len = 0;
+ tmp = rtpg_strreplace(expr, argkw[j], place, &len);
+ pfree(expr);
+ expr = tmp;
+
+ if (len) {
+ spi_argcount[i]++;
+ argpos[i][j] = k++;
+
+ sprintf(place, "$%d", k);
+ }
+ else
+ argpos[i][j] = 0;
+ }
+
+ len = strlen("SELECT (") + strlen(expr) + strlen(")::double precision");
+ sql = (char *) palloc(len + 1);
+ if (sql == NULL) {
+
+ for (k = 0; k < spi_count; k++) SPI_freeplan(spi_plan[k]);
+ SPI_finish();
+
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ rt_raster_destroy(raster);
+
+ elog(ERROR, "RASTER_mapAlgebra2: Could not allocate memory for expression parameter %d", spi_exprpos[i]);
+ 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';
+
+ POSTGIS_RT_DEBUGF(3, "sql #%d: %s", i, sql);
+
+ /* create prepared plan */
+ if (spi_argcount[i]) {
+ argtype = (Oid *) palloc(spi_argcount[i] * sizeof(Oid));
+ if (argtype == NULL) {
+
+ pfree(sql);
+ for (k = 0; k < spi_count; k++) SPI_freeplan(spi_plan[k]);
+ SPI_finish();
+
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ rt_raster_destroy(raster);
+
+ elog(ERROR, "RASTER_mapAlgebra2: Could not allocate memory for prepared plan argtypes of expression parameter %d", spi_exprpos[i]);
+ PG_RETURN_NULL();
+ }
+
+ /* specify datatypes of parameters */
+ for (j = 0, k = 0; j < argkwcount; j++) {
+ if (argpos[i][j] < 1) continue;
+
+ /* positions are INT4 */
+ if (
+ (strstr(argkw[j], "[rast1.x]") != NULL) ||
+ (strstr(argkw[j], "[rast1.y]") != NULL) ||
+ (strstr(argkw[j], "[rast2.x]") != NULL) ||
+ (strstr(argkw[j], "[rast2.y]") != NULL)
+ ) {
+ argtype[k] = INT4OID;
+ }
+ /* everything else is FLOAT8 */
+ else {
+ argtype[k] = FLOAT8OID;
+ }
+
+ k++;
+ }
+
+ spi_plan[i] = SPI_prepare(sql, spi_argcount[i], argtype);
+ pfree(argtype);
+
+ if (spi_plan[i] == NULL) {
+
+ pfree(sql);
+ for (k = 0; k < spi_count; k++) SPI_freeplan(spi_plan[k]);
+ SPI_finish();
+
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ rt_raster_destroy(raster);
+
+ elog(ERROR, "RASTER_mapAlgebra2: Could not create prepared plan of expression parameter %d", spi_exprpos[i]);
+ PG_RETURN_NULL();
+ }
+ }
+ /* no args, just execute query */
+ else {
+ err = SPI_execute(sql, TRUE, 0);
+ if (err != SPI_OK_SELECT || SPI_tuptable == NULL || SPI_processed != 1) {
+
+ pfree(sql);
+ for (k = 0; k < spi_count; k++) SPI_freeplan(spi_plan[k]);
+ SPI_finish();
+
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ rt_raster_destroy(raster);
+
+ elog(ERROR, "RASTER_mapAlgebra2: Could not evaluate expression parameter %d", spi_exprpos[i]);
+ 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) {
+
+ pfree(sql);
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ for (k = 0; k < spi_count; k++) SPI_freeplan(spi_plan[k]);
+ SPI_finish();
+
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ rt_raster_destroy(raster);
+
+ elog(ERROR, "RASTER_mapAlgebra2: Could not get result of expression parameter %d", spi_exprpos[i]);
+ PG_RETURN_NULL();
+ }
+
+ if (!isnull) {
+ hasargval[i] = 1;
+ argval[i] = DatumGetFloat8(datum);
+ }
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ }
+
+ pfree(sql);
+ }
+ else
+ spi_empty++;
+ }
+
+ /* 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)) {
+
+ ufc_nullcount = 0;
+ ufc_noid = PG_GETARG_OID(4);
+
+ /* get function info */
+ fmgr_info(ufc_noid, &ufl_info);
+
+ /* function cannot return set */
+ err = 0;
+ if (ufl_info.fn_retset) {
+ err = 1;
+ }
+ /* function should have correct # of args */
+ else if (ufl_info.fn_nargs < 3 || ufl_info.fn_nargs > 4) {
+ err = 2;
+ }
+
+ /*
+ TODO: consider adding checks of the userfunction parameters
+ should be able to use get_fn_expr_argtype() of fmgr.c
+ */
+
+ if (err > 0) {
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ rt_raster_destroy(raster);
+
+ if (err > 1)
+ elog(ERROR, "RASTER_mapAlgebra2: Function provided must have three or four input parameters");
+ else
+ elog(ERROR, "RASTER_mapAlgebra2: Function provided must return double precision not resultset");
+ PG_RETURN_NULL();
+ }
+
+ if (func_volatile(ufc_noid) == 'v') {
+ elog(NOTICE, "Function provided is VOLATILE. Unless required and for best performance, function should be IMMUTABLE or STABLE");
+ }
+
+ /* prep function call data */
+#if POSTGIS_PGSQL_VERSION <= 90
+ InitFunctionCallInfoData(ufc_info, &ufl_info, ufl_info.fn_nargs, InvalidOid, NULL);
+#else
+ InitFunctionCallInfoData(ufc_info, &ufl_info, ufl_info.fn_nargs, InvalidOid, NULL, NULL);
+#endif
+ memset(ufc_info.argnull, FALSE, sizeof(bool) * ufl_info.fn_nargs);
+
+ if (ufl_info.fn_nargs != 4)
+ k = 2;
+ else
+ k = 3;
+ if (!PG_ARGISNULL(7)) {
+ ufc_info.arg[k] = PG_GETARG_DATUM(7);
+ }
+ else {
+ ufc_info.arg[k] = (Datum) NULL;
+ ufc_info.argnull[k] = TRUE;
+ ufc_nullcount++;
+ }
+ }
+ break;
+ }
+ default:
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ rt_raster_destroy(raster);
+ elog(ERROR, "RASTER_mapAlgebra2: Invalid data type for expression or userfunction");
+ PG_RETURN_NULL();
+ break;
+ }
+
+ /* loop over pixels */
+ /* if any expression present, run */
+ if ((
+ (calltype == TEXTOID) && (
+ (spi_empty != spi_count) || hasnodatanodataval
+ )
+ ) || (
+ (calltype == REGPROCEDUREOID) && (ufc_noid != InvalidOid)
+ )) {
+ for (x = 0; x < dim[0]; x++) {
+ for (y = 0; y < dim[1]; y++) {
+
+ /* get pixel from each raster */
+ for (i = 0; i < set_count; i++) {
+ _haspixel[i] = 0;
+ _pixel[i] = 0;
+
+ /* row/column */
+ _x = x - (int) _rastoffset[i][0];
+ _y = y - (int) _rastoffset[i][1];
+
+ /* store _x and _y in 1-based */
+ _pos[i][0] = _x + 1;
+ _pos[i][1] = _y + 1;
+
+ /* get pixel value */
+ if (_band[i] == NULL) {
+ if (!_hasnodata[i]) {
+ _haspixel[i] = 1;
+ _pixel[i] = _nodataval[i];
+ }
+ }
+ else if (
+ !_isempty[i] &&
+ (_x >= 0 && _x < _dim[i][0]) &&
+ (_y >= 0 && _y < _dim[i][1])
+ ) {
+ err = rt_band_get_pixel(_band[i], _x, _y, &(_pixel[i]), &isnodata);
+ if (err != ES_NONE) {
+
+ if (calltype == TEXTOID) {
+ for (k = 0; k < spi_count; k++) SPI_freeplan(spi_plan[k]);
+ SPI_finish();
+ }
+
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ rt_raster_destroy(raster);
+
+ elog(ERROR, "RASTER_mapAlgebra2: Could not get pixel of %s raster", (i < 1 ? "FIRST" : "SECOND"));
+ PG_RETURN_NULL();
+ }
+
+ if (!_hasnodata[i] || !isnodata)
+ _haspixel[i] = 1;
+ }
+
+ POSTGIS_RT_DEBUGF(5, "pixel r%d(%d, %d) = %d, %f",
+ i,
+ _x, _y,
+ _haspixel[i],
+ _pixel[i]
+ );
+ }
+
+ haspixel = 0;
+
+ switch (calltype) {
+ case TEXTOID: {
+ /* which prepared plan to use? */
+ /* !pixel0 && !pixel1 */
+ /* use nodatanodataval */
+ if (!_haspixel[0] && !_haspixel[1])
+ i = 3;
+ /* pixel0 && !pixel1 */
+ /* run spi_plan[2] (nodata2expr) */
+ else if (_haspixel[0] && !_haspixel[1])
+ i = 2;
+ /* !pixel0 && pixel1 */
+ /* run spi_plan[1] (nodata1expr) */
+ else if (!_haspixel[0] && _haspixel[1])
+ i = 1;
+ /* pixel0 && pixel1 */
+ /* run spi_plan[0] (expression) */
+ else
+ i = 0;
+
+ /* 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 (spi_plan[i] != NULL) {
+ POSTGIS_RT_DEBUGF(4, "Using prepared plan: %d", i);
+
+ /* expression has argument(s) */
+ if (spi_argcount[i]) {
+ /* reset values to (Datum) NULL */
+ memset(values, (Datum) NULL, sizeof(Datum) * argkwcount);
+ /* reset nulls to FALSE */
+ memset(nulls, FALSE, sizeof(bool) * argkwcount);
+
+ /* set values and nulls */
+ for (j = 0; j < argkwcount; j++) {
+ idx = argpos[i][j];
+ if (idx < 1) continue;
+ idx--; /* 1-based becomes 0-based */
+
+ if (strstr(argkw[j], "[rast1.x]") != NULL) {
+ values[idx] = _pos[0][0];
+ }
+ else if (strstr(argkw[j], "[rast1.y]") != NULL) {
+ values[idx] = _pos[0][1];
+ }
+ else if (
+ (strstr(argkw[j], "[rast1.val]") != NULL) ||
+ (strstr(argkw[j], "[rast1]") != NULL)
+ ) {
+ if (_isempty[0] || !_haspixel[0])
+ nulls[idx] = TRUE;
+ else
+ values[idx] = Float8GetDatum(_pixel[0]);
+ }
+ else if (strstr(argkw[j], "[rast2.x]") != NULL) {
+ values[idx] = _pos[1][0];
+ }
+ else if (strstr(argkw[j], "[rast2.y]") != NULL) {
+ values[idx] = _pos[1][1];
+ }
+ else if (
+ (strstr(argkw[j], "[rast2.val]") != NULL) ||
+ (strstr(argkw[j], "[rast2]") != NULL)
+ ) {
+ if (_isempty[1] || !_haspixel[1])
+ nulls[idx] = TRUE;
+ else
+ values[idx] = Float8GetDatum(_pixel[1]);
+ }
+ }
+ }
+
+ /* run prepared plan */
+ err = SPI_execute_plan(spi_plan[i], values, nulls, TRUE, 1);
+ if (err != SPI_OK_SELECT || SPI_tuptable == NULL || SPI_processed != 1) {
+
+ for (k = 0; k < spi_count; k++) SPI_freeplan(spi_plan[k]);
+ SPI_finish();
+
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ rt_raster_destroy(raster);
+
+ elog(ERROR, "RASTER_mapAlgebra2: Unexpected error when running prepared statement %d", i);
+ 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) {
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ for (k = 0; k < spi_count; k++) SPI_freeplan(spi_plan[k]);
+ SPI_finish();
+
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ rt_raster_destroy(raster);
+
+ elog(ERROR, "RASTER_mapAlgebra2: Could not get result of prepared statement %d", i);
+ PG_RETURN_NULL();
+ }
+
+ if (!isnull) {
+ haspixel = 1;
+ pixel = DatumGetFloat8(datum);
+ }
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ }
+ } break;
+ case REGPROCEDUREOID: {
+ Datum d[4];
+ ArrayType *a;
+
+ /* build fcnarg */
+ for (i = 0; i < set_count; i++) {
+ ufc_info.arg[i] = Float8GetDatum(_pixel[i]);
+
+ if (_haspixel[i]) {
+ ufc_info.argnull[i] = FALSE;
+ ufc_nullcount--;
+ }
+ else {
+ ufc_info.argnull[i] = TRUE;
+ ufc_nullcount++;
+ }
+ }
+
+ /* function is strict and null parameter is passed */
+ /* http://archives.postgresql.org/pgsql-general/2011-11/msg00424.php */
+ if (ufl_info.fn_strict && ufc_nullcount)
+ break;
+
+ /* 4 parameters, add position */
+ if (ufl_info.fn_nargs == 4) {
+ /* Datum of 4 element array */
+ /* array is (x1, y1, x2, y2) */
+ for (i = 0; i < set_count; i++) {
+ if (i < 1) {
+ d[0] = Int32GetDatum(_pos[i][0]);
+ d[1] = Int32GetDatum(_pos[i][1]);
+ }
+ else {
+ d[2] = Int32GetDatum(_pos[i][0]);
+ d[3] = Int32GetDatum(_pos[i][1]);
+ }
+ }
+
+ a = construct_array(d, 4, INT4OID, sizeof(int32), true, 'i');
+ ufc_info.arg[2] = PointerGetDatum(a);
+ ufc_info.argnull[2] = FALSE;
+ }
+
+ datum = FunctionCallInvoke(&ufc_info);
+
+ /* result is not null*/
+ if (!ufc_info.isnull) {
+ haspixel = 1;
+ pixel = DatumGetFloat8(datum);
+ }
+ } break;
+ }
+
+ /* burn pixel if haspixel != 0 */
+ if (haspixel) {
+ if (rt_band_set_pixel(band, x, y, pixel, NULL) != ES_NONE) {
+
+ if (calltype == TEXTOID) {
+ for (k = 0; k < spi_count; k++) SPI_freeplan(spi_plan[k]);
+ SPI_finish();
+ }
+
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ rt_raster_destroy(raster);
+
+ elog(ERROR, "RASTER_mapAlgebra2: Could not set pixel value of output raster");
+ PG_RETURN_NULL();
+ }
+ }
+
+ POSTGIS_RT_DEBUGF(5, "(x, y, val) = (%d, %d, %f)", x, y, haspixel ? pixel : nodataval);
+
+ } /* y: height */
+ } /* x: width */
+ }
+
+ /* CLEANUP */
+ if (calltype == TEXTOID) {
+ for (i = 0; i < spi_count; i++) {
+ if (spi_plan[i] != NULL) SPI_freeplan(spi_plan[i]);
+ }
+ SPI_finish();
+ }
+
+ for (k = 0; k < set_count; k++) {
+ if (_rast[k] != NULL)
+ rt_raster_destroy(_rast[k]);
+ if (pgrastpos[k] != -1)
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+
+ pgrtn = rt_raster_serialize(raster);
+ rt_raster_destroy(raster);
+ if (!pgrtn) PG_RETURN_NULL();
+
+ POSTGIS_RT_DEBUG(3, "Finished RASTER_mapAlgebra2");
+
+ SET_VARSIZE(pgrtn, pgrtn->size);
+ PG_RETURN_POINTER(pgrtn);
+}
--- /dev/null
+/*
+ * $Id$
+ *
+ * WKTRaster - Raster Types for PostGIS
+ * http://www.postgis.org/support/wiki/index.php?WKTRasterHomePage
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, 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 "catalog/pg_type.h" /* for INT2OID, INT4OID, FLOAT4OID, FLOAT8OID and TEXTOID */
+
+#include "rtpostgis.h"
+
+/* Get pixel value */
+Datum RASTER_getPixelValue(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);
+
+/* 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);
+}
+
+/* ---------------------------------------------------------------- */
+/* 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) {
+ if (arg->nbands != NULL)
+ pfree(arg->nbands);
+
+ 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]);
+ }
+
+ if (arg->values != NULL)
+ pfree(arg->values);
+ if (arg->nodata != NULL)
+ pfree(arg->nodata);
+ }
+
+ pfree(arg);
+}
+
+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);
+ }
+ }
+
+ }
+ 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++) {
+ 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) {
+ int values_length = 2;
+ Datum values[values_length];
+ bool nulls[values_length];
+ HeapTuple tuple;
+ Datum result;
+ ArrayType *mdValues = NULL;
+ 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);
+
+ /* assemble 3-dimension array of values */
+ mdValues = construct_md_array(
+ arg2->values[call_cntr], arg2->nodata[call_cntr],
+ 2, 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;
+ int 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;
+ unsigned char *wkb = NULL;
+ size_t wkb_len;
+
+ int i = 0;
+ int 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(arg->gv[arg->ngv].geom, WKB_SFSQL, &wkb_len);
+
+ /* rasterize geometry */
+ arg->gv[arg->ngv].mask = rt_raster_gdal_rasterize(
+ wkb, wkb_len,
+ 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,
+ 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);
+}
+
+/**
+ * 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) {
+ int values_length = 3;
+ 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 = (GSERIALIZED *) PG_DETOAST_DATUM(PG_GETARG_DATUM(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,
+ _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);
+}
+
--- /dev/null
+/*
+ * $Id$
+ *
+ * WKTRaster - Raster Types for PostGIS
+ * http://www.postgis.org/support/wiki/index.php?WKTRasterHomePage
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <postgres.h>
+#include <fmgr.h>
+#include <funcapi.h>
+
+#if POSTGIS_PGSQL_VERSION > 92
+#include "access/htup_details.h" /* for heap_form_tuple() */
+#endif
+
+#include "rtpostgis.h"
+
+/* Get all the properties of a raster */
+Datum RASTER_getSRID(PG_FUNCTION_ARGS);
+Datum RASTER_getWidth(PG_FUNCTION_ARGS);
+Datum RASTER_getHeight(PG_FUNCTION_ARGS);
+Datum RASTER_getNumBands(PG_FUNCTION_ARGS);
+Datum RASTER_getXScale(PG_FUNCTION_ARGS);
+Datum RASTER_getYScale(PG_FUNCTION_ARGS);
+Datum RASTER_getXSkew(PG_FUNCTION_ARGS);
+Datum RASTER_getYSkew(PG_FUNCTION_ARGS);
+Datum RASTER_getXUpperLeft(PG_FUNCTION_ARGS);
+Datum RASTER_getYUpperLeft(PG_FUNCTION_ARGS);
+Datum RASTER_getPixelWidth(PG_FUNCTION_ARGS);
+Datum RASTER_getPixelHeight(PG_FUNCTION_ARGS);
+Datum RASTER_getGeotransform(PG_FUNCTION_ARGS);
+Datum RASTER_isEmpty(PG_FUNCTION_ARGS);
+Datum RASTER_hasNoBand(PG_FUNCTION_ARGS);
+
+/* get raster's meta data */
+Datum RASTER_metadata(PG_FUNCTION_ARGS);
+
+/* convert pixel/line to spatial coordinates */
+Datum RASTER_rasterToWorldCoord(PG_FUNCTION_ARGS);
+
+/* convert spatial coordinates to pixel/line*/
+Datum RASTER_worldToRasterCoord(PG_FUNCTION_ARGS);
+
+/* Set all the properties of a raster */
+Datum RASTER_setSRID(PG_FUNCTION_ARGS);
+Datum RASTER_setScale(PG_FUNCTION_ARGS);
+Datum RASTER_setScaleXY(PG_FUNCTION_ARGS);
+Datum RASTER_setSkew(PG_FUNCTION_ARGS);
+Datum RASTER_setSkewXY(PG_FUNCTION_ARGS);
+Datum RASTER_setUpperLeftXY(PG_FUNCTION_ARGS);
+Datum RASTER_setRotation(PG_FUNCTION_ARGS);
+Datum RASTER_setGeotransform(PG_FUNCTION_ARGS);
+
+/**
+ * Return the SRID associated with the raster.
+ */
+PG_FUNCTION_INFO_V1(RASTER_getSRID);
+Datum RASTER_getSRID(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster;
+ rt_raster raster;
+ int32_t srid;
+
+ if (PG_ARGISNULL(0)) PG_RETURN_NULL();
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
+
+ raster = rt_raster_deserialize(pgraster, TRUE);
+ if ( ! raster ) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_getSRID: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ srid = rt_raster_get_srid(raster);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ PG_RETURN_INT32(srid);
+}
+
+/**
+ * Return the width of the raster.
+ */
+PG_FUNCTION_INFO_V1(RASTER_getWidth);
+Datum RASTER_getWidth(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster;
+ rt_raster raster;
+ uint16_t width;
+
+ if (PG_ARGISNULL(0)) PG_RETURN_NULL();
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
+
+ raster = rt_raster_deserialize(pgraster, TRUE);
+ if ( ! raster ) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_getWidth: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ width = rt_raster_get_width(raster);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ PG_RETURN_INT32(width);
+}
+
+/**
+ * Return the height of the raster.
+ */
+PG_FUNCTION_INFO_V1(RASTER_getHeight);
+Datum RASTER_getHeight(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster;
+ rt_raster raster;
+ uint16_t height;
+
+ if (PG_ARGISNULL(0)) PG_RETURN_NULL();
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
+
+ raster = rt_raster_deserialize(pgraster, TRUE);
+ if ( ! raster ) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_getHeight: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ height = rt_raster_get_height(raster);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ PG_RETURN_INT32(height);
+}
+
+/**
+ * Return the number of bands included in the raster.
+ */
+PG_FUNCTION_INFO_V1(RASTER_getNumBands);
+Datum RASTER_getNumBands(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster;
+ rt_raster raster;
+ int32_t num_bands;
+
+ if (PG_ARGISNULL(0)) PG_RETURN_NULL();
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
+
+ raster = rt_raster_deserialize(pgraster, TRUE);
+ if ( ! raster ) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_getNumBands: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ num_bands = rt_raster_get_num_bands(raster);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ PG_RETURN_INT32(num_bands);
+}
+
+/**
+ * Return X scale from georeference of the raster.
+ */
+PG_FUNCTION_INFO_V1(RASTER_getXScale);
+Datum RASTER_getXScale(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster;
+ rt_raster raster;
+ double xsize;
+
+ if (PG_ARGISNULL(0)) PG_RETURN_NULL();
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
+
+ raster = rt_raster_deserialize(pgraster, TRUE);
+ if ( ! raster ) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_getXScale: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ xsize = rt_raster_get_x_scale(raster);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ PG_RETURN_FLOAT8(xsize);
+}
+
+/**
+ * Return Y scale from georeference of the raster.
+ */
+PG_FUNCTION_INFO_V1(RASTER_getYScale);
+Datum RASTER_getYScale(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster;
+ rt_raster raster;
+ double ysize;
+
+ if (PG_ARGISNULL(0)) PG_RETURN_NULL();
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
+
+ raster = rt_raster_deserialize(pgraster, TRUE);
+ if ( ! raster ) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_getYScale: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ ysize = rt_raster_get_y_scale(raster);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ PG_RETURN_FLOAT8(ysize);
+}
+
+/**
+ * Return value of the raster skew about the X axis.
+ */
+PG_FUNCTION_INFO_V1(RASTER_getXSkew);
+Datum RASTER_getXSkew(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster;
+ rt_raster raster;
+ double xskew;
+
+ if (PG_ARGISNULL(0)) PG_RETURN_NULL();
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
+
+ raster = rt_raster_deserialize(pgraster, TRUE);
+ if ( ! raster ) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_getXSkew: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ xskew = rt_raster_get_x_skew(raster);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ PG_RETURN_FLOAT8(xskew);
+}
+
+/**
+ * Return value of the raster skew about the Y axis.
+ */
+PG_FUNCTION_INFO_V1(RASTER_getYSkew);
+Datum RASTER_getYSkew(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster;
+ rt_raster raster;
+ double yskew;
+
+ if (PG_ARGISNULL(0)) PG_RETURN_NULL();
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
+
+ raster = rt_raster_deserialize(pgraster, TRUE);
+ if ( ! raster ) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_getYSkew: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ yskew = rt_raster_get_y_skew(raster);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ PG_RETURN_FLOAT8(yskew);
+}
+
+/**
+ * Return value of the raster offset in the X dimension.
+ */
+PG_FUNCTION_INFO_V1(RASTER_getXUpperLeft);
+Datum RASTER_getXUpperLeft(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster;
+ rt_raster raster;
+ double xul;
+
+ if (PG_ARGISNULL(0)) PG_RETURN_NULL();
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
+
+ raster = rt_raster_deserialize(pgraster, TRUE);
+ if ( ! raster ) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_getXUpperLeft: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ xul = rt_raster_get_x_offset(raster);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ PG_RETURN_FLOAT8(xul);
+}
+
+/**
+ * Return value of the raster offset in the Y dimension.
+ */
+PG_FUNCTION_INFO_V1(RASTER_getYUpperLeft);
+Datum RASTER_getYUpperLeft(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster;
+ rt_raster raster;
+ double yul;
+
+ if (PG_ARGISNULL(0)) PG_RETURN_NULL();
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
+
+ raster = rt_raster_deserialize(pgraster, TRUE);
+ if ( ! raster ) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_getYUpperLeft: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ yul = rt_raster_get_y_offset(raster);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ PG_RETURN_FLOAT8(yul);
+}
+
+/**
+ * Return the pixel width of the raster. The pixel width is
+ * a read-only, dynamically computed value derived from the
+ * X Scale and the Y Skew.
+ *
+ * Pixel Width = sqrt( X Scale * X Scale + Y Skew * Y Skew )
+ */
+PG_FUNCTION_INFO_V1(RASTER_getPixelWidth);
+Datum RASTER_getPixelWidth(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster;
+ rt_raster raster;
+ double xscale;
+ double yskew;
+ double pwidth;
+
+ if (PG_ARGISNULL(0)) PG_RETURN_NULL();
+ pgraster = (rt_pgraster *)PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
+
+ raster = rt_raster_deserialize(pgraster, TRUE);
+ if (!raster) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_getPixelWidth: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ xscale = rt_raster_get_x_scale(raster);
+ yskew = rt_raster_get_y_skew(raster);
+ pwidth = sqrt(xscale*xscale + yskew*yskew);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ PG_RETURN_FLOAT8(pwidth);
+}
+
+/**
+ * Return the pixel height of the raster. The pixel height is
+ * a read-only, dynamically computed value derived from the
+ * Y Scale and the X Skew.
+ *
+ * Pixel Height = sqrt( Y Scale * Y Scale + X Skew * X Skew )
+ */
+PG_FUNCTION_INFO_V1(RASTER_getPixelHeight);
+Datum RASTER_getPixelHeight(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster;
+ rt_raster raster;
+ double yscale;
+ double xskew;
+ double pheight;
+
+ if (PG_ARGISNULL(0)) PG_RETURN_NULL();
+ pgraster = (rt_pgraster *)PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
+
+ raster = rt_raster_deserialize(pgraster, TRUE);
+ if (!raster) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_getPixelHeight: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ yscale = rt_raster_get_y_scale(raster);
+ xskew = rt_raster_get_x_skew(raster);
+ pheight = sqrt(yscale*yscale + xskew*xskew);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ PG_RETURN_FLOAT8(pheight);
+}
+
+/**
+ * Calculates the physically relevant parameters of the supplied raster's
+ * geotransform. Returns them as a set.
+ */
+PG_FUNCTION_INFO_V1(RASTER_getGeotransform);
+Datum RASTER_getGeotransform(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+
+ double imag;
+ double jmag;
+ double theta_i;
+ double theta_ij;
+ /*
+ double xoffset;
+ double yoffset;
+ */
+
+ TupleDesc result_tuple; /* for returning a composite */
+ int values_length = 6;
+ Datum values[values_length];
+ bool nulls[values_length];
+ HeapTuple heap_tuple ; /* instance of the tuple to return */
+ Datum result;
+
+ POSTGIS_RT_DEBUG(3, "RASTER_getGeotransform: Starting");
+
+ /* get argument */
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+ pgraster = (rt_pgraster *)PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
+
+ /* raster */
+ raster = rt_raster_deserialize(pgraster, TRUE);
+ if (!raster) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_getGeotransform: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ /* do the calculation */
+ rt_raster_calc_phys_params(
+ rt_raster_get_x_scale(raster),
+ rt_raster_get_x_skew(raster),
+ rt_raster_get_y_skew(raster),
+ rt_raster_get_y_scale(raster),
+ &imag, &jmag, &theta_i, &theta_ij) ;
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ /* setup the return value infrastructure */
+ if (get_call_result_type(fcinfo, NULL, &result_tuple) != TYPEFUNC_COMPOSITE) {
+ ereport(ERROR, (
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("RASTER_getGeotransform(): function returning record called in context that cannot accept type record"
+ )
+ ));
+ PG_RETURN_NULL();
+ }
+
+ BlessTupleDesc(result_tuple);
+
+ /* get argument */
+ /* prep the composite return value */
+ /* construct datum array */
+ values[0] = Float8GetDatum(imag);
+ values[1] = Float8GetDatum(jmag);
+ values[2] = Float8GetDatum(theta_i);
+ values[3] = Float8GetDatum(theta_ij);
+ values[4] = Float8GetDatum(rt_raster_get_x_offset(raster));
+ values[5] = Float8GetDatum(rt_raster_get_y_offset(raster));
+
+ memset(nulls, FALSE, sizeof(bool) * values_length);
+
+ /* stick em on the heap */
+ heap_tuple = heap_form_tuple(result_tuple, values, nulls);
+
+ /* make the tuple into a datum */
+ result = HeapTupleGetDatum(heap_tuple);
+
+ PG_RETURN_DATUM(result);
+}
+
+/**
+ * Check if raster is empty or not
+ */
+PG_FUNCTION_INFO_V1(RASTER_isEmpty);
+Datum RASTER_isEmpty(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ bool isempty = FALSE;
+
+ /* Deserialize raster */
+ if (PG_ARGISNULL(0)) PG_RETURN_NULL();
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
+
+ raster = rt_raster_deserialize(pgraster, TRUE);
+ if ( ! raster )
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("RASTER_isEmpty: Could not deserialize raster")));
+ PG_FREE_IF_COPY(pgraster, 0);
+ PG_RETURN_NULL();
+ }
+
+ isempty = rt_raster_is_empty(raster);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ PG_RETURN_BOOL(isempty);
+}
+
+/**
+ * Check if the raster has a given band or not
+ */
+PG_FUNCTION_INFO_V1(RASTER_hasNoBand);
+Datum RASTER_hasNoBand(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ int bandindex = 0;
+ bool hasnoband = FALSE;
+
+ /* Deserialize raster */
+ if (PG_ARGISNULL(0)) PG_RETURN_NULL();
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
+
+ raster = rt_raster_deserialize(pgraster, TRUE);
+ if ( ! raster )
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("RASTER_hasNoBand: Could not deserialize raster")));
+ PG_FREE_IF_COPY(pgraster, 0);
+ PG_RETURN_NULL();
+ }
+
+ /* Get band number */
+ bandindex = PG_GETARG_INT32(1);
+ hasnoband = !rt_raster_has_band(raster, bandindex - 1);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ PG_RETURN_BOOL(hasnoband);
+}
+
+/**
+ * Get raster's meta data
+ */
+PG_FUNCTION_INFO_V1(RASTER_metadata);
+Datum RASTER_metadata(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+
+ uint32_t numBands;
+ double scaleX;
+ double scaleY;
+ double ipX;
+ double ipY;
+ double skewX;
+ double skewY;
+ int32_t srid;
+ uint32_t width;
+ uint32_t height;
+
+ TupleDesc tupdesc;
+ int values_length = 10;
+ Datum values[values_length];
+ bool nulls[values_length];
+ HeapTuple tuple;
+ Datum result;
+
+ POSTGIS_RT_DEBUG(3, "RASTER_metadata: Starting");
+
+ /* pgraster is null, return null */
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
+
+ /* raster */
+ raster = rt_raster_deserialize(pgraster, TRUE);
+ if (!raster) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_metadata; Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ /* upper left x, y */
+ ipX = rt_raster_get_x_offset(raster);
+ ipY = rt_raster_get_y_offset(raster);
+
+ /* width, height */
+ width = rt_raster_get_width(raster);
+ height = rt_raster_get_height(raster);
+
+ /* scale x, y */
+ scaleX = rt_raster_get_x_scale(raster);
+ scaleY = rt_raster_get_y_scale(raster);
+
+ /* skew x, y */
+ skewX = rt_raster_get_x_skew(raster);
+ skewY = rt_raster_get_y_skew(raster);
+
+ /* srid */
+ srid = rt_raster_get_srid(raster);
+
+ /* numbands */
+ numBands = rt_raster_get_num_bands(raster);
+
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ /* 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);
+
+ values[0] = Float8GetDatum(ipX);
+ values[1] = Float8GetDatum(ipY);
+ values[2] = UInt32GetDatum(width);
+ values[3] = UInt32GetDatum(height);
+ values[4] = Float8GetDatum(scaleX);
+ values[5] = Float8GetDatum(scaleY);
+ values[6] = Float8GetDatum(skewX);
+ values[7] = Float8GetDatum(skewY);
+ values[8] = Int32GetDatum(srid);
+ values[9] = UInt32GetDatum(numBands);
+
+ memset(nulls, FALSE, sizeof(bool) * values_length);
+
+ /* build a tuple */
+ tuple = heap_form_tuple(tupdesc, values, nulls);
+
+ /* make the tuple into a datum */
+ result = HeapTupleGetDatum(tuple);
+
+ PG_RETURN_DATUM(result);
+}
+
+PG_FUNCTION_INFO_V1(RASTER_rasterToWorldCoord);
+Datum RASTER_rasterToWorldCoord(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ int i = 0;
+ int cr[2] = {0};
+ bool skewed[2] = {false, false};
+ double cw[2] = {0};
+
+ TupleDesc tupdesc;
+ int values_length = 2;
+ Datum values[values_length];
+ bool nulls[values_length];
+ HeapTuple tuple;
+ Datum result;
+
+ POSTGIS_RT_DEBUG(3, "RASTER_rasterToWorldCoord: Starting");
+
+ /* pgraster is null, return null */
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
+
+ /* raster */
+ raster = rt_raster_deserialize(pgraster, TRUE);
+ if (!raster) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_rasterToWorldCoord: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ /* raster skewed? */
+ skewed[0] = FLT_NEQ(rt_raster_get_x_skew(raster), 0) ? true : false;
+ skewed[1] = FLT_NEQ(rt_raster_get_y_skew(raster), 0) ? true : false;
+
+ /* column and row */
+ for (i = 1; i <= 2; i++) {
+ if (PG_ARGISNULL(i)) {
+ /* if skewed on same axis, parameter is required */
+ if (skewed[i - 1]) {
+ elog(NOTICE, "Pixel row and column required for computing longitude and latitude of a rotated raster");
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ PG_RETURN_NULL();
+ }
+
+ continue;
+ }
+
+ cr[i - 1] = PG_GETARG_INT32(i);
+ }
+
+ /* user-provided value is 1-based but needs to be 0-based */
+ if (rt_raster_cell_to_geopoint(
+ raster,
+ (double) cr[0] - 1, (double) cr[1] - 1,
+ &(cw[0]), &(cw[1]),
+ NULL
+ ) != ES_NONE) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_rasterToWorldCoord: Could not compute longitude and latitude from pixel row and column");
+ PG_RETURN_NULL();
+ }
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ /* 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);
+
+ values[0] = Float8GetDatum(cw[0]);
+ values[1] = Float8GetDatum(cw[1]);
+
+ memset(nulls, FALSE, sizeof(bool) * values_length);
+
+ /* build a tuple */
+ tuple = heap_form_tuple(tupdesc, values, nulls);
+
+ /* make the tuple into a datum */
+ result = HeapTupleGetDatum(tuple);
+
+ PG_RETURN_DATUM(result);
+}
+
+PG_FUNCTION_INFO_V1(RASTER_worldToRasterCoord);
+Datum RASTER_worldToRasterCoord(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ int i = 0;
+ double cw[2] = {0};
+ double _cr[2] = {0};
+ int cr[2] = {0};
+ bool skewed = false;
+
+ TupleDesc tupdesc;
+ int values_length = 2;
+ Datum values[values_length];
+ bool nulls[values_length];
+ HeapTuple tuple;
+ Datum result;
+
+ POSTGIS_RT_DEBUG(3, "RASTER_worldToRasterCoord: Starting");
+
+ /* pgraster is null, return null */
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0, sizeof(struct rt_raster_serialized_t));
+
+ /* raster */
+ raster = rt_raster_deserialize(pgraster, TRUE);
+ if (!raster) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_worldToRasterCoord: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ /* raster skewed? */
+ skewed = FLT_NEQ(rt_raster_get_x_skew(raster), 0) ? true : false;
+ if (!skewed)
+ skewed = FLT_NEQ(rt_raster_get_y_skew(raster), 0) ? true : false;
+
+ /* longitude and latitude */
+ for (i = 1; i <= 2; i++) {
+ if (PG_ARGISNULL(i)) {
+ /* if skewed, parameter is required */
+ if (skewed) {
+ elog(NOTICE, "Latitude and longitude required for computing pixel row and column of a rotated raster");
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ PG_RETURN_NULL();
+ }
+
+ continue;
+ }
+
+ cw[i - 1] = PG_GETARG_FLOAT8(i);
+ }
+
+ /* return pixel row and column values are 0-based */
+ if (rt_raster_geopoint_to_cell(
+ raster,
+ cw[0], cw[1],
+ &(_cr[0]), &(_cr[1]),
+ NULL
+ ) != ES_NONE) {
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_worldToRasterCoord: Could not compute pixel row and column from longitude and latitude");
+ PG_RETURN_NULL();
+ }
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+
+ /* force to integer and add one to make 1-based */
+ cr[0] = ((int) _cr[0]) + 1;
+ cr[1] = ((int) _cr[1]) + 1;
+
+ /* 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);
+
+ values[0] = Int32GetDatum(cr[0]);
+ values[1] = Int32GetDatum(cr[1]);
+
+ memset(nulls, FALSE, sizeof(bool) * values_length);
+
+ /* build a tuple */
+ tuple = heap_form_tuple(tupdesc, values, nulls);
+
+ /* make the tuple into a datum */
+ result = HeapTupleGetDatum(tuple);
+
+ PG_RETURN_DATUM(result);
+}
+
+/**
+ * Set the SRID associated with the raster.
+ */
+PG_FUNCTION_INFO_V1(RASTER_setSRID);
+Datum RASTER_setSRID(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_pgraster *pgrtn = NULL;
+ rt_raster raster;
+ int32_t newSRID = PG_GETARG_INT32(1);
+
+ 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_setSRID: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ rt_raster_set_srid(raster, newSRID);
+
+ 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 the scale of the raster.
+ */
+PG_FUNCTION_INFO_V1(RASTER_setScale);
+Datum RASTER_setScale(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_pgraster *pgrtn = NULL;
+ rt_raster raster;
+ double size = PG_GETARG_FLOAT8(1);
+
+ 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_setScale: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ rt_raster_set_scale(raster, size, size);
+
+ 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 the pixel size of the raster.
+ */
+PG_FUNCTION_INFO_V1(RASTER_setScaleXY);
+Datum RASTER_setScaleXY(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_pgraster *pgrtn = NULL;
+ rt_raster raster;
+ double xscale = PG_GETARG_FLOAT8(1);
+ double yscale = PG_GETARG_FLOAT8(2);
+
+ 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_setScaleXY: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ rt_raster_set_scale(raster, xscale, yscale);
+ 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 the skew of the raster.
+ */
+PG_FUNCTION_INFO_V1(RASTER_setSkew);
+Datum RASTER_setSkew(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_pgraster *pgrtn = NULL;
+ rt_raster raster;
+ double skew = PG_GETARG_FLOAT8(1);
+
+ 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_setSkew: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ rt_raster_set_skews(raster, skew, skew);
+
+ 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 the skew of the raster.
+ */
+PG_FUNCTION_INFO_V1(RASTER_setSkewXY);
+Datum RASTER_setSkewXY(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_pgraster *pgrtn = NULL;
+ rt_raster raster;
+ double xskew = PG_GETARG_FLOAT8(1);
+ double yskew = PG_GETARG_FLOAT8(2);
+
+ 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_setSkewXY: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ rt_raster_set_skews(raster, xskew, yskew);
+
+ 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 the raster offset in the X and Y dimension.
+ */
+PG_FUNCTION_INFO_V1(RASTER_setUpperLeftXY);
+Datum RASTER_setUpperLeftXY(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_pgraster *pgrtn = NULL;
+ rt_raster raster;
+ double xoffset = PG_GETARG_FLOAT8(1);
+ double yoffset = PG_GETARG_FLOAT8(2);
+
+ 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_setUpperLeftXY: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ rt_raster_set_offsets(raster, xoffset, yoffset);
+
+ 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 the geotransform of the supplied raster. Returns the raster.
+ */
+PG_FUNCTION_INFO_V1(RASTER_setGeotransform);
+Datum RASTER_setGeotransform(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_pgraster *pgrtn = NULL;
+ rt_raster raster;
+ float8 imag, jmag, theta_i, theta_ij, xoffset, yoffset;
+
+ if (
+ PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2) ||
+ PG_ARGISNULL(3) || PG_ARGISNULL(4) ||
+ PG_ARGISNULL(5) || PG_ARGISNULL(6)
+ ) {
+ PG_RETURN_NULL();
+ }
+
+ /* get the inputs */
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+ imag = PG_GETARG_FLOAT8(1);
+ jmag = PG_GETARG_FLOAT8(2);
+ theta_i = PG_GETARG_FLOAT8(3);
+ theta_ij = PG_GETARG_FLOAT8(4);
+ xoffset = PG_GETARG_FLOAT8(5);
+ yoffset = PG_GETARG_FLOAT8(6);
+
+ raster = rt_raster_deserialize(pgraster, TRUE);
+ if (!raster) {
+ PG_FREE_IF_COPY(pgraster, 0);
+ elog(ERROR, "RASTER_setGeotransform: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ /* store the new geotransform */
+ rt_raster_set_phys_params(raster, imag,jmag,theta_i,theta_ij);
+ rt_raster_set_offsets(raster, xoffset, yoffset);
+
+ /* prep the return value */
+ 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 the rotation of the raster. This method will change the X Scale,
+ * Y Scale, X Skew and Y Skew properties all at once to keep the rotations
+ * about the X and Y axis uniform.
+ *
+ * This method will set the rotation about the X axis and Y axis based on
+ * the pixel size. This pixel size may not be uniform if rasters have different
+ * skew values (the raster cells are diamond-shaped). If a raster has different
+ * skew values has a rotation set upon it, this method will remove the
+ * diamond distortions of the cells, as each axis will have the same rotation.
+ */
+PG_FUNCTION_INFO_V1(RASTER_setRotation);
+Datum RASTER_setRotation(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_pgraster *pgrtn = NULL;
+ rt_raster raster;
+ double rotation = PG_GETARG_FLOAT8(1);
+ double imag, jmag, theta_i, theta_ij;
+
+ 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_setRotation: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ /* preserve all defining characteristics of the grid except for rotation */
+ rt_raster_get_phys_params(raster, &imag, &jmag, &theta_i, &theta_ij);
+ rt_raster_set_phys_params(raster, imag, jmag, rotation, theta_ij);
+
+ 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);
+}
--- /dev/null
+/*
+ * $Id$
+ *
+ * WKTRaster - Raster Types for PostGIS
+ * http://www.postgis.org/support/wiki/index.php?WKTRasterHomePage
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <postgres.h> /* for palloc */
+#include <fmgr.h>
+
+#include "../../postgis_config.h"
+
+#include "lwgeom_pg.h"
+#include "rtpostgis.h"
+
+/* determine if two rasters intersect */
+Datum RASTER_intersects(PG_FUNCTION_ARGS);
+
+/* determine if two rasters overlap */
+Datum RASTER_overlaps(PG_FUNCTION_ARGS);
+
+/* determine if two rasters touch */
+Datum RASTER_touches(PG_FUNCTION_ARGS);
+
+/* determine if the first raster contains the second raster */
+Datum RASTER_contains(PG_FUNCTION_ARGS);
+
+/* determine if the first raster contains properly the second raster */
+Datum RASTER_containsProperly(PG_FUNCTION_ARGS);
+
+/* determine if the first raster covers the second raster */
+Datum RASTER_covers(PG_FUNCTION_ARGS);
+
+/* determine if the first raster is covered by the second raster */
+Datum RASTER_coveredby(PG_FUNCTION_ARGS);
+
+/* determine if the two rasters are within the specified distance of each other */
+Datum RASTER_dwithin(PG_FUNCTION_ARGS);
+
+/* determine if the two rasters are fully within the specified distance of each other */
+Datum RASTER_dfullywithin(PG_FUNCTION_ARGS);
+
+/* determine if two rasters are aligned */
+Datum RASTER_sameAlignment(PG_FUNCTION_ARGS);
+Datum RASTER_notSameAlignmentReason(PG_FUNCTION_ARGS);
+
+/**
+ * See if two rasters intersect
+ */
+PG_FUNCTION_INFO_V1(RASTER_intersects);
+Datum RASTER_intersects(PG_FUNCTION_ARGS)
+{
+ const int set_count = 2;
+ rt_pgraster *pgrast[2];
+ int pgrastpos[2] = {-1, -1};
+ rt_raster rast[2] = {NULL};
+ uint32_t bandindex[2] = {0};
+ uint32_t hasbandindex[2] = {0};
+
+ uint32_t i;
+ uint32_t j;
+ uint32_t k;
+ uint32_t numBands;
+ int rtn;
+ int result;
+
+ for (i = 0, j = 0; i < set_count; i++) {
+ /* pgrast is null, return null */
+ if (PG_ARGISNULL(j)) {
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+ pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(j));
+ pgrastpos[i] = j;
+ j++;
+
+ /* raster */
+ rast[i] = rt_raster_deserialize(pgrast[i], FALSE);
+ if (!rast[i]) {
+ for (k = 0; k <= i; k++) {
+ if (k < i)
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "RASTER_intersects: Could not deserialize the %s raster", i < 1 ? "first" : "second");
+ PG_RETURN_NULL();
+ }
+
+ /* numbands */
+ numBands = rt_raster_get_num_bands(rast[i]);
+ if (numBands < 1) {
+ elog(NOTICE, "The %s raster provided has no bands", i < 1 ? "first" : "second");
+ if (i > 0) i++;
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ /* band index */
+ if (!PG_ARGISNULL(j)) {
+ bandindex[i] = PG_GETARG_INT32(j);
+ if (bandindex[i] < 1 || bandindex[i] > numBands) {
+ elog(NOTICE, "Invalid band index (must use 1-based) for the %s raster. Returning NULL", i < 1 ? "first" : "second");
+ if (i > 0) i++;
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+ hasbandindex[i] = 1;
+ }
+ else
+ hasbandindex[i] = 0;
+ POSTGIS_RT_DEBUGF(4, "hasbandindex[%d] = %d", i, hasbandindex[i]);
+ POSTGIS_RT_DEBUGF(4, "bandindex[%d] = %d", i, bandindex[i]);
+ j++;
+ }
+
+ /* hasbandindex must be balanced */
+ if (
+ (hasbandindex[0] && !hasbandindex[1]) ||
+ (!hasbandindex[0] && hasbandindex[1])
+ ) {
+ elog(NOTICE, "Missing band index. Band indices must be provided for both rasters if any one is provided");
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ /* SRID must match */
+ if (rt_raster_get_srid(rast[0]) != rt_raster_get_srid(rast[1])) {
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "The two rasters provided have different SRIDs");
+ PG_RETURN_NULL();
+ }
+
+ rtn = rt_raster_intersects(
+ rast[0], (hasbandindex[0] ? bandindex[0] - 1 : -1),
+ rast[1], (hasbandindex[1] ? bandindex[1] - 1 : -1),
+ &result
+ );
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+
+ if (rtn != ES_NONE) {
+ elog(ERROR, "RASTER_intersects: Could not test for intersection on the two rasters");
+ PG_RETURN_NULL();
+ }
+
+ PG_RETURN_BOOL(result);
+}
+
+/**
+ * See if two rasters overlap
+ */
+PG_FUNCTION_INFO_V1(RASTER_overlaps);
+Datum RASTER_overlaps(PG_FUNCTION_ARGS)
+{
+ const int set_count = 2;
+ rt_pgraster *pgrast[2];
+ int pgrastpos[2] = {-1, -1};
+ rt_raster rast[2] = {NULL};
+ uint32_t bandindex[2] = {0};
+ uint32_t hasbandindex[2] = {0};
+
+ uint32_t i;
+ uint32_t j;
+ uint32_t k;
+ uint32_t numBands;
+ int rtn;
+ int result;
+
+ for (i = 0, j = 0; i < set_count; i++) {
+ /* pgrast is null, return null */
+ if (PG_ARGISNULL(j)) {
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+ pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(j));
+ pgrastpos[i] = j;
+ j++;
+
+ /* raster */
+ rast[i] = rt_raster_deserialize(pgrast[i], FALSE);
+ if (!rast[i]) {
+ for (k = 0; k <= i; k++) {
+ if (k < i)
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "RASTER_overlaps: Could not deserialize the %s raster", i < 1 ? "first" : "second");
+ PG_RETURN_NULL();
+ }
+
+ /* numbands */
+ numBands = rt_raster_get_num_bands(rast[i]);
+ if (numBands < 1) {
+ elog(NOTICE, "The %s raster provided has no bands", i < 1 ? "first" : "second");
+ if (i > 0) i++;
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ /* band index */
+ if (!PG_ARGISNULL(j)) {
+ bandindex[i] = PG_GETARG_INT32(j);
+ if (bandindex[i] < 1 || bandindex[i] > numBands) {
+ elog(NOTICE, "Invalid band index (must use 1-based) for the %s raster. Returning NULL", i < 1 ? "first" : "second");
+ if (i > 0) i++;
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+ hasbandindex[i] = 1;
+ }
+ else
+ hasbandindex[i] = 0;
+ POSTGIS_RT_DEBUGF(4, "hasbandindex[%d] = %d", i, hasbandindex[i]);
+ POSTGIS_RT_DEBUGF(4, "bandindex[%d] = %d", i, bandindex[i]);
+ j++;
+ }
+
+ /* hasbandindex must be balanced */
+ if (
+ (hasbandindex[0] && !hasbandindex[1]) ||
+ (!hasbandindex[0] && hasbandindex[1])
+ ) {
+ elog(NOTICE, "Missing band index. Band indices must be provided for both rasters if any one is provided");
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ /* SRID must match */
+ if (rt_raster_get_srid(rast[0]) != rt_raster_get_srid(rast[1])) {
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "The two rasters provided have different SRIDs");
+ PG_RETURN_NULL();
+ }
+
+ rtn = rt_raster_overlaps(
+ rast[0], (hasbandindex[0] ? bandindex[0] - 1 : -1),
+ rast[1], (hasbandindex[1] ? bandindex[1] - 1 : -1),
+ &result
+ );
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+
+ if (rtn != ES_NONE) {
+ elog(ERROR, "RASTER_overlaps: Could not test for overlap on the two rasters");
+ PG_RETURN_NULL();
+ }
+
+ PG_RETURN_BOOL(result);
+}
+
+/**
+ * See if two rasters touch
+ */
+PG_FUNCTION_INFO_V1(RASTER_touches);
+Datum RASTER_touches(PG_FUNCTION_ARGS)
+{
+ const int set_count = 2;
+ rt_pgraster *pgrast[2];
+ int pgrastpos[2] = {-1, -1};
+ rt_raster rast[2] = {NULL};
+ uint32_t bandindex[2] = {0};
+ uint32_t hasbandindex[2] = {0};
+
+ uint32_t i;
+ uint32_t j;
+ uint32_t k;
+ uint32_t numBands;
+ int rtn;
+ int result;
+
+ for (i = 0, j = 0; i < set_count; i++) {
+ /* pgrast is null, return null */
+ if (PG_ARGISNULL(j)) {
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+ pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(j));
+ pgrastpos[i] = j;
+ j++;
+
+ /* raster */
+ rast[i] = rt_raster_deserialize(pgrast[i], FALSE);
+ if (!rast[i]) {
+ for (k = 0; k <= i; k++) {
+ if (k < i)
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "RASTER_touches: Could not deserialize the %s raster", i < 1 ? "first" : "second");
+ PG_RETURN_NULL();
+ }
+
+ /* numbands */
+ numBands = rt_raster_get_num_bands(rast[i]);
+ if (numBands < 1) {
+ elog(NOTICE, "The %s raster provided has no bands", i < 1 ? "first" : "second");
+ if (i > 0) i++;
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ /* band index */
+ if (!PG_ARGISNULL(j)) {
+ bandindex[i] = PG_GETARG_INT32(j);
+ if (bandindex[i] < 1 || bandindex[i] > numBands) {
+ elog(NOTICE, "Invalid band index (must use 1-based) for the %s raster. Returning NULL", i < 1 ? "first" : "second");
+ if (i > 0) i++;
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+ hasbandindex[i] = 1;
+ }
+ else
+ hasbandindex[i] = 0;
+ POSTGIS_RT_DEBUGF(4, "hasbandindex[%d] = %d", i, hasbandindex[i]);
+ POSTGIS_RT_DEBUGF(4, "bandindex[%d] = %d", i, bandindex[i]);
+ j++;
+ }
+
+ /* hasbandindex must be balanced */
+ if (
+ (hasbandindex[0] && !hasbandindex[1]) ||
+ (!hasbandindex[0] && hasbandindex[1])
+ ) {
+ elog(NOTICE, "Missing band index. Band indices must be provided for both rasters if any one is provided");
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ /* SRID must match */
+ if (rt_raster_get_srid(rast[0]) != rt_raster_get_srid(rast[1])) {
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "The two rasters provided have different SRIDs");
+ PG_RETURN_NULL();
+ }
+
+ rtn = rt_raster_touches(
+ rast[0], (hasbandindex[0] ? bandindex[0] - 1 : -1),
+ rast[1], (hasbandindex[1] ? bandindex[1] - 1 : -1),
+ &result
+ );
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+
+ if (rtn != ES_NONE) {
+ elog(ERROR, "RASTER_touches: Could not test for touch on the two rasters");
+ PG_RETURN_NULL();
+ }
+
+ PG_RETURN_BOOL(result);
+}
+
+/**
+ * See if the first raster contains the second raster
+ */
+PG_FUNCTION_INFO_V1(RASTER_contains);
+Datum RASTER_contains(PG_FUNCTION_ARGS)
+{
+ const int set_count = 2;
+ rt_pgraster *pgrast[2];
+ int pgrastpos[2] = {-1, -1};
+ rt_raster rast[2] = {NULL};
+ uint32_t bandindex[2] = {0};
+ uint32_t hasbandindex[2] = {0};
+
+ uint32_t i;
+ uint32_t j;
+ uint32_t k;
+ uint32_t numBands;
+ int rtn;
+ int result;
+
+ for (i = 0, j = 0; i < set_count; i++) {
+ /* pgrast is null, return null */
+ if (PG_ARGISNULL(j)) {
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+ pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(j));
+ pgrastpos[i] = j;
+ j++;
+
+ /* raster */
+ rast[i] = rt_raster_deserialize(pgrast[i], FALSE);
+ if (!rast[i]) {
+ for (k = 0; k <= i; k++) {
+ if (k < i)
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "RASTER_contains: Could not deserialize the %s raster", i < 1 ? "first" : "second");
+ PG_RETURN_NULL();
+ }
+
+ /* numbands */
+ numBands = rt_raster_get_num_bands(rast[i]);
+ if (numBands < 1) {
+ elog(NOTICE, "The %s raster provided has no bands", i < 1 ? "first" : "second");
+ if (i > 0) i++;
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ /* band index */
+ if (!PG_ARGISNULL(j)) {
+ bandindex[i] = PG_GETARG_INT32(j);
+ if (bandindex[i] < 1 || bandindex[i] > numBands) {
+ elog(NOTICE, "Invalid band index (must use 1-based) for the %s raster. Returning NULL", i < 1 ? "first" : "second");
+ if (i > 0) i++;
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+ hasbandindex[i] = 1;
+ }
+ else
+ hasbandindex[i] = 0;
+ POSTGIS_RT_DEBUGF(4, "hasbandindex[%d] = %d", i, hasbandindex[i]);
+ POSTGIS_RT_DEBUGF(4, "bandindex[%d] = %d", i, bandindex[i]);
+ j++;
+ }
+
+ /* hasbandindex must be balanced */
+ if (
+ (hasbandindex[0] && !hasbandindex[1]) ||
+ (!hasbandindex[0] && hasbandindex[1])
+ ) {
+ elog(NOTICE, "Missing band index. Band indices must be provided for both rasters if any one is provided");
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ /* SRID must match */
+ if (rt_raster_get_srid(rast[0]) != rt_raster_get_srid(rast[1])) {
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "The two rasters provided have different SRIDs");
+ PG_RETURN_NULL();
+ }
+
+ rtn = rt_raster_contains(
+ rast[0], (hasbandindex[0] ? bandindex[0] - 1 : -1),
+ rast[1], (hasbandindex[1] ? bandindex[1] - 1 : -1),
+ &result
+ );
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+
+ if (rtn != ES_NONE) {
+ elog(ERROR, "RASTER_contains: Could not test that the first raster contains the second raster");
+ PG_RETURN_NULL();
+ }
+
+ PG_RETURN_BOOL(result);
+}
+
+/**
+ * See if the first raster contains properly the second raster
+ */
+PG_FUNCTION_INFO_V1(RASTER_containsProperly);
+Datum RASTER_containsProperly(PG_FUNCTION_ARGS)
+{
+ const int set_count = 2;
+ rt_pgraster *pgrast[2];
+ int pgrastpos[2] = {-1, -1};
+ rt_raster rast[2] = {NULL};
+ uint32_t bandindex[2] = {0};
+ uint32_t hasbandindex[2] = {0};
+
+ uint32_t i;
+ uint32_t j;
+ uint32_t k;
+ uint32_t numBands;
+ int rtn;
+ int result;
+
+ for (i = 0, j = 0; i < set_count; i++) {
+ /* pgrast is null, return null */
+ if (PG_ARGISNULL(j)) {
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+ pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(j));
+ pgrastpos[i] = j;
+ j++;
+
+ /* raster */
+ rast[i] = rt_raster_deserialize(pgrast[i], FALSE);
+ if (!rast[i]) {
+ for (k = 0; k <= i; k++) {
+ if (k < i)
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "RASTER_containsProperly: Could not deserialize the %s raster", i < 1 ? "first" : "second");
+ PG_RETURN_NULL();
+ }
+
+ /* numbands */
+ numBands = rt_raster_get_num_bands(rast[i]);
+ if (numBands < 1) {
+ elog(NOTICE, "The %s raster provided has no bands", i < 1 ? "first" : "second");
+ if (i > 0) i++;
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ /* band index */
+ if (!PG_ARGISNULL(j)) {
+ bandindex[i] = PG_GETARG_INT32(j);
+ if (bandindex[i] < 1 || bandindex[i] > numBands) {
+ elog(NOTICE, "Invalid band index (must use 1-based) for the %s raster. Returning NULL", i < 1 ? "first" : "second");
+ if (i > 0) i++;
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+ hasbandindex[i] = 1;
+ }
+ else
+ hasbandindex[i] = 0;
+ POSTGIS_RT_DEBUGF(4, "hasbandindex[%d] = %d", i, hasbandindex[i]);
+ POSTGIS_RT_DEBUGF(4, "bandindex[%d] = %d", i, bandindex[i]);
+ j++;
+ }
+
+ /* hasbandindex must be balanced */
+ if (
+ (hasbandindex[0] && !hasbandindex[1]) ||
+ (!hasbandindex[0] && hasbandindex[1])
+ ) {
+ elog(NOTICE, "Missing band index. Band indices must be provided for both rasters if any one is provided");
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ /* SRID must match */
+ if (rt_raster_get_srid(rast[0]) != rt_raster_get_srid(rast[1])) {
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "The two rasters provided have different SRIDs");
+ PG_RETURN_NULL();
+ }
+
+ rtn = rt_raster_contains_properly(
+ rast[0], (hasbandindex[0] ? bandindex[0] - 1 : -1),
+ rast[1], (hasbandindex[1] ? bandindex[1] - 1 : -1),
+ &result
+ );
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+
+ if (rtn != ES_NONE) {
+ elog(ERROR, "RASTER_containsProperly: Could not test that the first raster contains properly the second raster");
+ PG_RETURN_NULL();
+ }
+
+ PG_RETURN_BOOL(result);
+}
+
+/**
+ * See if the first raster covers the second raster
+ */
+PG_FUNCTION_INFO_V1(RASTER_covers);
+Datum RASTER_covers(PG_FUNCTION_ARGS)
+{
+ const int set_count = 2;
+ rt_pgraster *pgrast[2];
+ int pgrastpos[2] = {-1, -1};
+ rt_raster rast[2] = {NULL};
+ uint32_t bandindex[2] = {0};
+ uint32_t hasbandindex[2] = {0};
+
+ uint32_t i;
+ uint32_t j;
+ uint32_t k;
+ uint32_t numBands;
+ int rtn;
+ int result;
+
+ for (i = 0, j = 0; i < set_count; i++) {
+ /* pgrast is null, return null */
+ if (PG_ARGISNULL(j)) {
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+ pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(j));
+ pgrastpos[i] = j;
+ j++;
+
+ /* raster */
+ rast[i] = rt_raster_deserialize(pgrast[i], FALSE);
+ if (!rast[i]) {
+ for (k = 0; k <= i; k++) {
+ if (k < i)
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "RASTER_covers: Could not deserialize the %s raster", i < 1 ? "first" : "second");
+ PG_RETURN_NULL();
+ }
+
+ /* numbands */
+ numBands = rt_raster_get_num_bands(rast[i]);
+ if (numBands < 1) {
+ elog(NOTICE, "The %s raster provided has no bands", i < 1 ? "first" : "second");
+ if (i > 0) i++;
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ /* band index */
+ if (!PG_ARGISNULL(j)) {
+ bandindex[i] = PG_GETARG_INT32(j);
+ if (bandindex[i] < 1 || bandindex[i] > numBands) {
+ elog(NOTICE, "Invalid band index (must use 1-based) for the %s raster. Returning NULL", i < 1 ? "first" : "second");
+ if (i > 0) i++;
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+ hasbandindex[i] = 1;
+ }
+ else
+ hasbandindex[i] = 0;
+ POSTGIS_RT_DEBUGF(4, "hasbandindex[%d] = %d", i, hasbandindex[i]);
+ POSTGIS_RT_DEBUGF(4, "bandindex[%d] = %d", i, bandindex[i]);
+ j++;
+ }
+
+ /* hasbandindex must be balanced */
+ if (
+ (hasbandindex[0] && !hasbandindex[1]) ||
+ (!hasbandindex[0] && hasbandindex[1])
+ ) {
+ elog(NOTICE, "Missing band index. Band indices must be provided for both rasters if any one is provided");
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ /* SRID must match */
+ if (rt_raster_get_srid(rast[0]) != rt_raster_get_srid(rast[1])) {
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "The two rasters provided have different SRIDs");
+ PG_RETURN_NULL();
+ }
+
+ rtn = rt_raster_covers(
+ rast[0], (hasbandindex[0] ? bandindex[0] - 1 : -1),
+ rast[1], (hasbandindex[1] ? bandindex[1] - 1 : -1),
+ &result
+ );
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+
+ if (rtn != ES_NONE) {
+ elog(ERROR, "RASTER_covers: Could not test that the first raster covers the second raster");
+ PG_RETURN_NULL();
+ }
+
+ PG_RETURN_BOOL(result);
+}
+
+/**
+ * See if the first raster is covered by the second raster
+ */
+PG_FUNCTION_INFO_V1(RASTER_coveredby);
+Datum RASTER_coveredby(PG_FUNCTION_ARGS)
+{
+ const int set_count = 2;
+ rt_pgraster *pgrast[2];
+ int pgrastpos[2] = {-1, -1};
+ rt_raster rast[2] = {NULL};
+ uint32_t bandindex[2] = {0};
+ uint32_t hasbandindex[2] = {0};
+
+ uint32_t i;
+ uint32_t j;
+ uint32_t k;
+ uint32_t numBands;
+ int rtn;
+ int result;
+
+ for (i = 0, j = 0; i < set_count; i++) {
+ /* pgrast is null, return null */
+ if (PG_ARGISNULL(j)) {
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+ pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(j));
+ pgrastpos[i] = j;
+ j++;
+
+ /* raster */
+ rast[i] = rt_raster_deserialize(pgrast[i], FALSE);
+ if (!rast[i]) {
+ for (k = 0; k <= i; k++) {
+ if (k < i)
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "RASTER_coveredby: Could not deserialize the %s raster", i < 1 ? "first" : "second");
+ PG_RETURN_NULL();
+ }
+
+ /* numbands */
+ numBands = rt_raster_get_num_bands(rast[i]);
+ if (numBands < 1) {
+ elog(NOTICE, "The %s raster provided has no bands", i < 1 ? "first" : "second");
+ if (i > 0) i++;
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ /* band index */
+ if (!PG_ARGISNULL(j)) {
+ bandindex[i] = PG_GETARG_INT32(j);
+ if (bandindex[i] < 1 || bandindex[i] > numBands) {
+ elog(NOTICE, "Invalid band index (must use 1-based) for the %s raster. Returning NULL", i < 1 ? "first" : "second");
+ if (i > 0) i++;
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+ hasbandindex[i] = 1;
+ }
+ else
+ hasbandindex[i] = 0;
+ POSTGIS_RT_DEBUGF(4, "hasbandindex[%d] = %d", i, hasbandindex[i]);
+ POSTGIS_RT_DEBUGF(4, "bandindex[%d] = %d", i, bandindex[i]);
+ j++;
+ }
+
+ /* hasbandindex must be balanced */
+ if (
+ (hasbandindex[0] && !hasbandindex[1]) ||
+ (!hasbandindex[0] && hasbandindex[1])
+ ) {
+ elog(NOTICE, "Missing band index. Band indices must be provided for both rasters if any one is provided");
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ /* SRID must match */
+ if (rt_raster_get_srid(rast[0]) != rt_raster_get_srid(rast[1])) {
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "The two rasters provided have different SRIDs");
+ PG_RETURN_NULL();
+ }
+
+ rtn = rt_raster_coveredby(
+ rast[0], (hasbandindex[0] ? bandindex[0] - 1 : -1),
+ rast[1], (hasbandindex[1] ? bandindex[1] - 1 : -1),
+ &result
+ );
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+
+ if (rtn != ES_NONE) {
+ elog(ERROR, "RASTER_coveredby: Could not test that the first raster is covered by the second raster");
+ PG_RETURN_NULL();
+ }
+
+ PG_RETURN_BOOL(result);
+}
+
+/**
+ * See if the two rasters are within the specified distance of each other
+ */
+PG_FUNCTION_INFO_V1(RASTER_dwithin);
+Datum RASTER_dwithin(PG_FUNCTION_ARGS)
+{
+ const int set_count = 2;
+ rt_pgraster *pgrast[2];
+ int pgrastpos[2] = {-1, -1};
+ rt_raster rast[2] = {NULL};
+ uint32_t bandindex[2] = {0};
+ uint32_t hasbandindex[2] = {0};
+ double distance = 0;
+
+ uint32_t i;
+ uint32_t j;
+ uint32_t k;
+ uint32_t numBands;
+ int rtn;
+ int result;
+
+ for (i = 0, j = 0; i < set_count; i++) {
+ /* pgrast is null, return null */
+ if (PG_ARGISNULL(j)) {
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+ pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(j));
+ pgrastpos[i] = j;
+ j++;
+
+ /* raster */
+ rast[i] = rt_raster_deserialize(pgrast[i], FALSE);
+ if (!rast[i]) {
+ for (k = 0; k <= i; k++) {
+ if (k < i)
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "RASTER_dwithin: Could not deserialize the %s raster", i < 1 ? "first" : "second");
+ PG_RETURN_NULL();
+ }
+
+ /* numbands */
+ numBands = rt_raster_get_num_bands(rast[i]);
+ if (numBands < 1) {
+ elog(NOTICE, "The %s raster provided has no bands", i < 1 ? "first" : "second");
+ if (i > 0) i++;
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ /* band index */
+ if (!PG_ARGISNULL(j)) {
+ bandindex[i] = PG_GETARG_INT32(j);
+ if (bandindex[i] < 1 || bandindex[i] > numBands) {
+ elog(NOTICE, "Invalid band index (must use 1-based) for the %s raster. Returning NULL", i < 1 ? "first" : "second");
+ if (i > 0) i++;
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+ hasbandindex[i] = 1;
+ }
+ else
+ hasbandindex[i] = 0;
+ POSTGIS_RT_DEBUGF(4, "hasbandindex[%d] = %d", i, hasbandindex[i]);
+ POSTGIS_RT_DEBUGF(4, "bandindex[%d] = %d", i, bandindex[i]);
+ j++;
+ }
+
+ /* distance */
+ if (PG_ARGISNULL(4)) {
+ elog(NOTICE, "Distance cannot be NULL. Returning NULL");
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ distance = PG_GETARG_FLOAT8(4);
+ if (distance < 0) {
+ elog(NOTICE, "Distance cannot be less than zero. Returning NULL");
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ /* hasbandindex must be balanced */
+ if (
+ (hasbandindex[0] && !hasbandindex[1]) ||
+ (!hasbandindex[0] && hasbandindex[1])
+ ) {
+ elog(NOTICE, "Missing band index. Band indices must be provided for both rasters if any one is provided");
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ /* SRID must match */
+ if (rt_raster_get_srid(rast[0]) != rt_raster_get_srid(rast[1])) {
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "The two rasters provided have different SRIDs");
+ PG_RETURN_NULL();
+ }
+
+ rtn = rt_raster_within_distance(
+ rast[0], (hasbandindex[0] ? bandindex[0] - 1 : -1),
+ rast[1], (hasbandindex[1] ? bandindex[1] - 1 : -1),
+ distance,
+ &result
+ );
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+
+ if (rtn != ES_NONE) {
+ elog(ERROR, "RASTER_dwithin: Could not test that the two rasters are within the specified distance of each other");
+ PG_RETURN_NULL();
+ }
+
+ PG_RETURN_BOOL(result);
+}
+
+/**
+ * See if the two rasters are fully within the specified distance of each other
+ */
+PG_FUNCTION_INFO_V1(RASTER_dfullywithin);
+Datum RASTER_dfullywithin(PG_FUNCTION_ARGS)
+{
+ const int set_count = 2;
+ rt_pgraster *pgrast[2];
+ int pgrastpos[2] = {-1, -1};
+ rt_raster rast[2] = {NULL};
+ uint32_t bandindex[2] = {0};
+ uint32_t hasbandindex[2] = {0};
+ double distance = 0;
+
+ uint32_t i;
+ uint32_t j;
+ uint32_t k;
+ uint32_t numBands;
+ int rtn;
+ int result;
+
+ for (i = 0, j = 0; i < set_count; i++) {
+ /* pgrast is null, return null */
+ if (PG_ARGISNULL(j)) {
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+ pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(j));
+ pgrastpos[i] = j;
+ j++;
+
+ /* raster */
+ rast[i] = rt_raster_deserialize(pgrast[i], FALSE);
+ if (!rast[i]) {
+ for (k = 0; k <= i; k++) {
+ if (k < i)
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "RASTER_dfullywithin: Could not deserialize the %s raster", i < 1 ? "first" : "second");
+ PG_RETURN_NULL();
+ }
+
+ /* numbands */
+ numBands = rt_raster_get_num_bands(rast[i]);
+ if (numBands < 1) {
+ elog(NOTICE, "The %s raster provided has no bands", i < 1 ? "first" : "second");
+ if (i > 0) i++;
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ /* band index */
+ if (!PG_ARGISNULL(j)) {
+ bandindex[i] = PG_GETARG_INT32(j);
+ if (bandindex[i] < 1 || bandindex[i] > numBands) {
+ elog(NOTICE, "Invalid band index (must use 1-based) for the %s raster. Returning NULL", i < 1 ? "first" : "second");
+ if (i > 0) i++;
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+ hasbandindex[i] = 1;
+ }
+ else
+ hasbandindex[i] = 0;
+ POSTGIS_RT_DEBUGF(4, "hasbandindex[%d] = %d", i, hasbandindex[i]);
+ POSTGIS_RT_DEBUGF(4, "bandindex[%d] = %d", i, bandindex[i]);
+ j++;
+ }
+
+ /* distance */
+ if (PG_ARGISNULL(4)) {
+ elog(NOTICE, "Distance cannot be NULL. Returning NULL");
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ distance = PG_GETARG_FLOAT8(4);
+ if (distance < 0) {
+ elog(NOTICE, "Distance cannot be less than zero. Returning NULL");
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ /* hasbandindex must be balanced */
+ if (
+ (hasbandindex[0] && !hasbandindex[1]) ||
+ (!hasbandindex[0] && hasbandindex[1])
+ ) {
+ elog(NOTICE, "Missing band index. Band indices must be provided for both rasters if any one is provided");
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+
+ /* SRID must match */
+ if (rt_raster_get_srid(rast[0]) != rt_raster_get_srid(rast[1])) {
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "The two rasters provided have different SRIDs");
+ PG_RETURN_NULL();
+ }
+
+ rtn = rt_raster_fully_within_distance(
+ rast[0], (hasbandindex[0] ? bandindex[0] - 1 : -1),
+ rast[1], (hasbandindex[1] ? bandindex[1] - 1 : -1),
+ distance,
+ &result
+ );
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+
+ if (rtn != ES_NONE) {
+ elog(ERROR, "RASTER_dfullywithin: Could not test that the two rasters are fully within the specified distance of each other");
+ PG_RETURN_NULL();
+ }
+
+ PG_RETURN_BOOL(result);
+}
+
+/**
+ * See if two rasters are aligned
+ */
+PG_FUNCTION_INFO_V1(RASTER_sameAlignment);
+Datum RASTER_sameAlignment(PG_FUNCTION_ARGS)
+{
+ const int set_count = 2;
+ rt_pgraster *pgrast[2];
+ int pgrastpos[2] = {-1, -1};
+ rt_raster rast[2] = {NULL};
+
+ uint32_t i;
+ uint32_t j;
+ uint32_t k;
+ int rtn;
+ int aligned = 0;
+ char *reason = NULL;
+
+ for (i = 0, j = 0; i < set_count; i++) {
+ /* pgrast is null, return null */
+ if (PG_ARGISNULL(j)) {
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+ pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(j), 0, sizeof(struct rt_raster_serialized_t));
+ pgrastpos[i] = j;
+ j++;
+
+ /* raster */
+ rast[i] = rt_raster_deserialize(pgrast[i], TRUE);
+ if (!rast[i]) {
+ for (k = 0; k <= i; k++) {
+ if (k < i)
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "RASTER_sameAlignment: Could not deserialize the %s raster", i < 1 ? "first" : "second");
+ PG_RETURN_NULL();
+ }
+ }
+
+ rtn = rt_raster_same_alignment(
+ rast[0],
+ rast[1],
+ &aligned,
+ &reason
+ );
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+
+ if (rtn != ES_NONE) {
+ elog(ERROR, "RASTER_sameAlignment: Could not test for alignment on the two rasters");
+ PG_RETURN_NULL();
+ }
+
+ /* only output reason if not aligned */
+ if (reason != NULL && !aligned)
+ elog(NOTICE, "%s", reason);
+
+ PG_RETURN_BOOL(aligned);
+}
+
+/**
+ * Return a reason why two rasters are not aligned
+ */
+PG_FUNCTION_INFO_V1(RASTER_notSameAlignmentReason);
+Datum RASTER_notSameAlignmentReason(PG_FUNCTION_ARGS)
+{
+ const int set_count = 2;
+ rt_pgraster *pgrast[2];
+ int pgrastpos[2] = {-1, -1};
+ rt_raster rast[2] = {NULL};
+
+ uint32_t i;
+ uint32_t j;
+ uint32_t k;
+ int rtn;
+ int aligned = 0;
+ char *reason = NULL;
+ text *result = NULL;
+
+ for (i = 0, j = 0; i < set_count; i++) {
+ /* pgrast is null, return null */
+ if (PG_ARGISNULL(j)) {
+ for (k = 0; k < i; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ PG_RETURN_NULL();
+ }
+ pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(j), 0, sizeof(struct rt_raster_serialized_t));
+ pgrastpos[i] = j;
+ j++;
+
+ /* raster */
+ rast[i] = rt_raster_deserialize(pgrast[i], TRUE);
+ if (!rast[i]) {
+ for (k = 0; k <= i; k++) {
+ if (k < i)
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+ elog(ERROR, "RASTER_notSameAlignmentReason: Could not deserialize the %s raster", i < 1 ? "first" : "second");
+ PG_RETURN_NULL();
+ }
+ }
+
+ rtn = rt_raster_same_alignment(
+ rast[0],
+ rast[1],
+ &aligned,
+ &reason
+ );
+ for (k = 0; k < set_count; k++) {
+ rt_raster_destroy(rast[k]);
+ PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]);
+ }
+
+ if (rtn != ES_NONE) {
+ elog(ERROR, "RASTER_notSameAlignmentReason: Could not test for alignment on the two rasters");
+ PG_RETURN_NULL();
+ }
+
+ result = cstring2text(reason);
+ PG_RETURN_TEXT_P(result);
+}
--- /dev/null
+/*
+ * $Id$
+ *
+ * WKTRaster - Raster Types for PostGIS
+ * http://www.postgis.org/support/wiki/index.php?WKTRasterHomePage
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <postgres.h>
+#include <fmgr.h>
+#include <utils/builtins.h> /* for text_to_cstring() */
+#include "utils/lsyscache.h" /* for get_typlenbyvalalign */
+#include "utils/array.h" /* for ArrayType */
+#include "catalog/pg_type.h" /* for INT2OID, INT4OID, FLOAT4OID, FLOAT8OID and TEXTOID */
+#include <executor/spi.h>
+#include <funcapi.h> /* for SRF */
+
+#include "rtpostgis.h"
+
+/* Get summary stats */
+Datum RASTER_summaryStats(PG_FUNCTION_ARGS);
+Datum RASTER_summaryStatsCoverage(PG_FUNCTION_ARGS);
+
+/* get histogram */
+Datum RASTER_histogram(PG_FUNCTION_ARGS);
+Datum RASTER_histogramCoverage(PG_FUNCTION_ARGS);
+
+/* get quantiles */
+Datum RASTER_quantile(PG_FUNCTION_ARGS);
+Datum RASTER_quantileCoverage(PG_FUNCTION_ARGS);
+
+/* get counts of values */
+Datum RASTER_valueCount(PG_FUNCTION_ARGS);
+Datum RASTER_valueCountCoverage(PG_FUNCTION_ARGS);
+
+/**
+ * Get summary stats of a band
+ */
+PG_FUNCTION_INFO_V1(RASTER_summaryStats);
+Datum RASTER_summaryStats(PG_FUNCTION_ARGS)
+{
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ rt_band band = NULL;
+ int32_t bandindex = 1;
+ bool exclude_nodata_value = TRUE;
+ int num_bands = 0;
+ double sample = 0;
+ rt_bandstats stats = NULL;
+
+ TupleDesc tupdesc;
+ int values_length = 6;
+ Datum values[values_length];
+ bool nulls[values_length];
+ HeapTuple tuple;
+ Datum result;
+
+ /* pgraster is null, return null */
+ 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_summaryStats: 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();
+ }
+
+ /* exclude_nodata_value flag */
+ if (!PG_ARGISNULL(2))
+ exclude_nodata_value = PG_GETARG_BOOL(2);
+
+ /* sample % */
+ if (!PG_ARGISNULL(3)) {
+ sample = PG_GETARG_FLOAT8(3);
+ if (sample < 0 || sample > 1) {
+ elog(NOTICE, "Invalid sample percentage (must be between 0 and 1). Returning NULL");
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ PG_RETURN_NULL();
+ }
+ else if (FLT_EQ(sample, 0.0))
+ sample = 1;
+ }
+ else
+ sample = 1;
+
+ /* 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();
+ }
+
+ /* we don't need the raw values, hence the zero parameter */
+ stats = rt_band_get_summary_stats(band, (int) exclude_nodata_value, sample, 0, NULL, NULL, NULL);
+ rt_band_destroy(band);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ if (NULL == stats) {
+ elog(NOTICE, "Could not compute summary statistics for band at index %d. Returning NULL", bandindex);
+ PG_RETURN_NULL();
+ }
+
+ /* 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);
+
+ memset(nulls, FALSE, sizeof(bool) * values_length);
+
+ values[0] = Int64GetDatum(stats->count);
+ if (stats->count > 0) {
+ values[1] = Float8GetDatum(stats->sum);
+ values[2] = Float8GetDatum(stats->mean);
+ values[3] = Float8GetDatum(stats->stddev);
+ values[4] = Float8GetDatum(stats->min);
+ values[5] = Float8GetDatum(stats->max);
+ }
+ else {
+ nulls[1] = TRUE;
+ nulls[2] = TRUE;
+ nulls[3] = TRUE;
+ nulls[4] = TRUE;
+ nulls[5] = TRUE;
+ }
+
+ /* build a tuple */
+ tuple = heap_form_tuple(tupdesc, values, nulls);
+
+ /* make the tuple into a datum */
+ result = HeapTupleGetDatum(tuple);
+
+ /* clean up */
+ pfree(stats);
+
+ PG_RETURN_DATUM(result);
+}
+
+/**
+ * Get summary stats of a coverage for a specific band
+ */
+PG_FUNCTION_INFO_V1(RASTER_summaryStatsCoverage);
+Datum RASTER_summaryStatsCoverage(PG_FUNCTION_ARGS)
+{
+ text *tablenametext = NULL;
+ char *tablename = NULL;
+ text *colnametext = NULL;
+ char *colname = NULL;
+ int32_t bandindex = 1;
+ bool exclude_nodata_value = TRUE;
+ double sample = 0;
+
+ int len = 0;
+ char *sql = NULL;
+ int spi_result;
+ Portal portal;
+ TupleDesc tupdesc;
+ SPITupleTable *tuptable = NULL;
+ HeapTuple tuple;
+ Datum datum;
+ bool isNull = FALSE;
+
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ rt_band band = NULL;
+ int num_bands = 0;
+ uint64_t cK = 0;
+ double cM = 0;
+ double cQ = 0;
+ rt_bandstats stats = NULL;
+ rt_bandstats rtn = NULL;
+
+ int values_length = 6;
+ Datum values[values_length];
+ bool nulls[values_length];
+ Datum result;
+
+ /* tablename is null, return null */
+ if (PG_ARGISNULL(0)) {
+ elog(NOTICE, "Table name must be provided");
+ PG_RETURN_NULL();
+ }
+ tablenametext = PG_GETARG_TEXT_P(0);
+ tablename = text_to_cstring(tablenametext);
+ if (!strlen(tablename)) {
+ elog(NOTICE, "Table name must be provided");
+ PG_RETURN_NULL();
+ }
+
+ /* column name is null, return null */
+ if (PG_ARGISNULL(1)) {
+ elog(NOTICE, "Column name must be provided");
+ PG_RETURN_NULL();
+ }
+ colnametext = PG_GETARG_TEXT_P(1);
+ colname = text_to_cstring(colnametext);
+ if (!strlen(colname)) {
+ elog(NOTICE, "Column name must be provided");
+ PG_RETURN_NULL();
+ }
+
+ /* band index is 1-based */
+ if (!PG_ARGISNULL(2))
+ bandindex = PG_GETARG_INT32(2);
+
+ /* exclude_nodata_value flag */
+ if (!PG_ARGISNULL(3))
+ exclude_nodata_value = PG_GETARG_BOOL(3);
+
+ /* sample % */
+ if (!PG_ARGISNULL(4)) {
+ sample = PG_GETARG_FLOAT8(4);
+ if (sample < 0 || sample > 1) {
+ elog(NOTICE, "Invalid sample percentage (must be between 0 and 1). Returning NULL");
+ rt_raster_destroy(raster);
+ PG_RETURN_NULL();
+ }
+ else if (FLT_EQ(sample, 0.0))
+ sample = 1;
+ }
+ else
+ sample = 1;
+
+ /* iterate through rasters of coverage */
+ /* connect to database */
+ spi_result = SPI_connect();
+ if (spi_result != SPI_OK_CONNECT) {
+ pfree(sql);
+ elog(ERROR, "RASTER_summaryStatsCoverage: Could not connect to database using SPI");
+ PG_RETURN_NULL();
+ }
+
+ /* create sql */
+ len = sizeof(char) * (strlen("SELECT \"\" FROM \"\" WHERE \"\" IS NOT NULL") + (strlen(colname) * 2) + strlen(tablename) + 1);
+ sql = (char *) palloc(len);
+ if (NULL == sql) {
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_finish();
+ elog(ERROR, "RASTER_summaryStatsCoverage: Could not allocate memory for sql");
+ PG_RETURN_NULL();
+ }
+
+ /* get cursor */
+ snprintf(sql, len, "SELECT \"%s\" FROM \"%s\" WHERE \"%s\" IS NOT NULL", colname, tablename, colname);
+ portal = SPI_cursor_open_with_args(
+ "coverage",
+ sql,
+ 0, NULL,
+ NULL, NULL,
+ TRUE, 0
+ );
+ pfree(sql);
+
+ /* process resultset */
+ SPI_cursor_fetch(portal, TRUE, 1);
+ while (SPI_processed == 1 && SPI_tuptable != NULL) {
+ tupdesc = SPI_tuptable->tupdesc;
+ tuptable = SPI_tuptable;
+ tuple = tuptable->vals[0];
+
+ datum = SPI_getbinval(tuple, tupdesc, 1, &isNull);
+ if (SPI_result == SPI_ERROR_NOATTRIBUTE) {
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ if (NULL != rtn) pfree(rtn);
+ elog(ERROR, "RASTER_summaryStatsCoverage: Could not get raster of coverage");
+ PG_RETURN_NULL();
+ }
+ else if (isNull) {
+ SPI_cursor_fetch(portal, TRUE, 1);
+ continue;
+ }
+
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM(datum);
+
+ raster = rt_raster_deserialize(pgraster, FALSE);
+ if (!raster) {
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ if (NULL != rtn) pfree(rtn);
+ elog(ERROR, "RASTER_summaryStatsCoverage: Could not deserialize raster");
+ PG_RETURN_NULL();
+ }
+
+ /* inspect number of bands */
+ 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);
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ if (NULL != rtn) pfree(rtn);
+ 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);
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ if (NULL != rtn) pfree(rtn);
+ PG_RETURN_NULL();
+ }
+
+ /* we don't need the raw values, hence the zero parameter */
+ stats = rt_band_get_summary_stats(band, (int) exclude_nodata_value, sample, 0, &cK, &cM, &cQ);
+
+ rt_band_destroy(band);
+ rt_raster_destroy(raster);
+
+ if (NULL == stats) {
+ elog(NOTICE, "Could not compute summary statistics for band at index %d. Returning NULL", bandindex);
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ if (NULL != rtn) pfree(rtn);
+ PG_RETURN_NULL();
+ }
+
+ /* initialize rtn */
+ if (stats->count > 0) {
+ if (NULL == rtn) {
+ rtn = (rt_bandstats) SPI_palloc(sizeof(struct rt_bandstats_t));
+ if (NULL == rtn) {
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ elog(ERROR, "RASTER_summaryStatsCoverage: Could not allocate memory for summary stats of coverage");
+ PG_RETURN_NULL();
+ }
+
+ rtn->sample = stats->sample;
+ rtn->count = stats->count;
+ rtn->min = stats->min;
+ rtn->max = stats->max;
+ rtn->sum = stats->sum;
+ rtn->mean = stats->mean;
+ rtn->stddev = -1;
+
+ rtn->values = NULL;
+ rtn->sorted = 0;
+ }
+ else {
+ rtn->count += stats->count;
+ rtn->sum += stats->sum;
+
+ if (stats->min < rtn->min)
+ rtn->min = stats->min;
+ if (stats->max > rtn->max)
+ rtn->max = stats->max;
+ }
+ }
+
+ pfree(stats);
+
+ /* next record */
+ SPI_cursor_fetch(portal, TRUE, 1);
+ }
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ if (NULL == rtn) {
+ elog(ERROR, "RASTER_summaryStatsCoverage: Could not compute coverage summary stats");
+ PG_RETURN_NULL();
+ }
+
+ /* coverage mean and deviation */
+ rtn->mean = rtn->sum / rtn->count;
+ /* sample deviation */
+ if (rtn->sample > 0 && rtn->sample < 1)
+ rtn->stddev = sqrt(cQ / (rtn->count - 1));
+ /* standard deviation */
+ else
+ rtn->stddev = sqrt(cQ / rtn->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);
+
+ memset(nulls, FALSE, sizeof(bool) * values_length);
+
+ values[0] = Int64GetDatum(rtn->count);
+ if (rtn->count > 0) {
+ values[1] = Float8GetDatum(rtn->sum);
+ values[2] = Float8GetDatum(rtn->mean);
+ values[3] = Float8GetDatum(rtn->stddev);
+ values[4] = Float8GetDatum(rtn->min);
+ values[5] = Float8GetDatum(rtn->max);
+ }
+ else {
+ nulls[1] = TRUE;
+ nulls[2] = TRUE;
+ nulls[3] = TRUE;
+ nulls[4] = TRUE;
+ nulls[5] = TRUE;
+ }
+
+ /* build a tuple */
+ tuple = heap_form_tuple(tupdesc, values, nulls);
+
+ /* make the tuple into a datum */
+ result = HeapTupleGetDatum(tuple);
+
+ /* clean up */
+ pfree(rtn);
+
+ PG_RETURN_DATUM(result);
+}
+
+/**
+ * Returns histogram for a band
+ */
+PG_FUNCTION_INFO_V1(RASTER_histogram);
+Datum RASTER_histogram(PG_FUNCTION_ARGS)
+{
+ FuncCallContext *funcctx;
+ TupleDesc tupdesc;
+
+ int i;
+ rt_histogram hist;
+ rt_histogram hist2;
+ int call_cntr;
+ int max_calls;
+
+ /* first call of function */
+ if (SRF_IS_FIRSTCALL()) {
+ MemoryContext oldcontext;
+
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ rt_band band = NULL;
+ int32_t bandindex = 1;
+ int num_bands = 0;
+ bool exclude_nodata_value = TRUE;
+ double sample = 0;
+ uint32_t bin_count = 0;
+ double *bin_width = NULL;
+ uint32_t bin_width_count = 0;
+ double width = 0;
+ bool right = FALSE;
+ double min = 0;
+ double max = 0;
+ rt_bandstats stats = NULL;
+ uint32_t count;
+
+ int j;
+ int n;
+
+ ArrayType *array;
+ Oid etype;
+ Datum *e;
+ bool *nulls;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+
+ POSTGIS_RT_DEBUG(3, "RASTER_histogram: Starting");
+
+ /* 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);
+
+ /* pgraster is null, return nothing */
+ 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_histogram: Could not deserialize raster");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* 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);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* exclude_nodata_value flag */
+ if (!PG_ARGISNULL(2))
+ exclude_nodata_value = PG_GETARG_BOOL(2);
+
+ /* sample % */
+ if (!PG_ARGISNULL(3)) {
+ sample = PG_GETARG_FLOAT8(3);
+ if (sample < 0 || sample > 1) {
+ elog(NOTICE, "Invalid sample percentage (must be between 0 and 1). Returning NULL");
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ else if (FLT_EQ(sample, 0.0))
+ sample = 1;
+ }
+ else
+ sample = 1;
+
+ /* bin_count */
+ if (!PG_ARGISNULL(4)) {
+ bin_count = PG_GETARG_INT32(4);
+ if (bin_count < 1) bin_count = 0;
+ }
+
+ /* bin_width */
+ if (!PG_ARGISNULL(5)) {
+ array = PG_GETARG_ARRAYTYPE_P(5);
+ 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_histogram: Invalid data type for width");
+ SRF_RETURN_DONE(funcctx);
+ break;
+ }
+
+ deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
+ &nulls, &n);
+
+ bin_width = palloc(sizeof(double) * n);
+ for (i = 0, j = 0; i < n; i++) {
+ if (nulls[i]) continue;
+
+ switch (etype) {
+ case FLOAT4OID:
+ width = (double) DatumGetFloat4(e[i]);
+ break;
+ case FLOAT8OID:
+ width = (double) DatumGetFloat8(e[i]);
+ break;
+ }
+
+ if (width < 0 || FLT_EQ(width, 0.0)) {
+ elog(NOTICE, "Invalid value for width (must be greater than 0). Returning NULL");
+ pfree(bin_width);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ bin_width[j] = width;
+ POSTGIS_RT_DEBUGF(5, "bin_width[%d] = %f", j, bin_width[j]);
+ j++;
+ }
+ bin_width_count = j;
+
+ if (j < 1) {
+ pfree(bin_width);
+ bin_width = NULL;
+ }
+ }
+
+ /* right */
+ if (!PG_ARGISNULL(6))
+ right = PG_GETARG_BOOL(6);
+
+ /* min */
+ if (!PG_ARGISNULL(7)) min = PG_GETARG_FLOAT8(7);
+
+ /* max */
+ if (!PG_ARGISNULL(8)) max = PG_GETARG_FLOAT8(8);
+
+ /* 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);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* get stats */
+ stats = rt_band_get_summary_stats(band, (int) exclude_nodata_value, sample, 1, NULL, NULL, NULL);
+ rt_band_destroy(band);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ if (NULL == stats || NULL == stats->values) {
+ elog(NOTICE, "Could not compute summary statistics for band at index %d", bandindex);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ else if (stats->count < 1) {
+ elog(NOTICE, "Could not compute histogram for band at index %d as the band has no values", bandindex);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* get histogram */
+ hist = rt_band_get_histogram(stats, bin_count, bin_width, bin_width_count, right, min, max, &count);
+ if (bin_width_count) pfree(bin_width);
+ pfree(stats);
+ if (NULL == hist || !count) {
+ elog(NOTICE, "Could not compute histogram for band at index %d", bandindex);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ POSTGIS_RT_DEBUGF(3, "%d bins returned", count);
+
+ /* Store needed information */
+ funcctx->user_fctx = hist;
+
+ /* 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;
+ hist2 = funcctx->user_fctx;
+
+ /* do when there is more left to send */
+ if (call_cntr < max_calls) {
+ int values_length = 4;
+ Datum values[values_length];
+ bool nulls[values_length];
+ HeapTuple tuple;
+ Datum result;
+
+ POSTGIS_RT_DEBUGF(3, "Result %d", call_cntr);
+
+ memset(nulls, FALSE, sizeof(bool) * values_length);
+
+ values[0] = Float8GetDatum(hist2[call_cntr].min);
+ values[1] = Float8GetDatum(hist2[call_cntr].max);
+ values[2] = Int64GetDatum(hist2[call_cntr].count);
+ values[3] = Float8GetDatum(hist2[call_cntr].percent);
+
+ /* build a tuple */
+ tuple = heap_form_tuple(tupdesc, values, nulls);
+
+ /* make the tuple into a datum */
+ result = HeapTupleGetDatum(tuple);
+
+ SRF_RETURN_NEXT(funcctx, result);
+ }
+ /* do when there is no more left */
+ else {
+ pfree(hist2);
+ SRF_RETURN_DONE(funcctx);
+ }
+}
+
+/**
+ * Returns histogram of a coverage for a specified band
+ */
+PG_FUNCTION_INFO_V1(RASTER_histogramCoverage);
+Datum RASTER_histogramCoverage(PG_FUNCTION_ARGS)
+{
+ FuncCallContext *funcctx;
+ TupleDesc tupdesc;
+
+ int i;
+ rt_histogram covhist = NULL;
+ rt_histogram covhist2;
+ int call_cntr;
+ int max_calls;
+
+ POSTGIS_RT_DEBUG(3, "RASTER_histogramCoverage: Starting");
+
+ /* first call of function */
+ if (SRF_IS_FIRSTCALL()) {
+ MemoryContext oldcontext;
+
+ text *tablenametext = NULL;
+ char *tablename = NULL;
+ text *colnametext = NULL;
+ char *colname = NULL;
+ int32_t bandindex = 1;
+ bool exclude_nodata_value = TRUE;
+ double sample = 0;
+ uint32_t bin_count = 0;
+ double *bin_width = NULL;
+ uint32_t bin_width_count = 0;
+ double width = 0;
+ bool right = FALSE;
+ uint32_t count;
+
+ int len = 0;
+ char *sql = NULL;
+ char *tmp = NULL;
+ double min = 0;
+ double max = 0;
+ int spi_result;
+ Portal portal;
+ SPITupleTable *tuptable = NULL;
+ HeapTuple tuple;
+ Datum datum;
+ bool isNull = FALSE;
+
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ rt_band band = NULL;
+ int num_bands = 0;
+ rt_bandstats stats = NULL;
+ rt_histogram hist;
+ uint64_t sum = 0;
+
+ int j;
+ int n;
+
+ ArrayType *array;
+ Oid etype;
+ Datum *e;
+ bool *nulls;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+
+ POSTGIS_RT_DEBUG(3, "RASTER_histogramCoverage: first call of function");
+
+ /* 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);
+
+ /* tablename is null, return null */
+ if (PG_ARGISNULL(0)) {
+ elog(NOTICE, "Table name must be provided");
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ tablenametext = PG_GETARG_TEXT_P(0);
+ tablename = text_to_cstring(tablenametext);
+ if (!strlen(tablename)) {
+ elog(NOTICE, "Table name must be provided");
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ POSTGIS_RT_DEBUGF(3, "RASTER_histogramCoverage: tablename = %s", tablename);
+
+ /* column name is null, return null */
+ if (PG_ARGISNULL(1)) {
+ elog(NOTICE, "Column name must be provided");
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ colnametext = PG_GETARG_TEXT_P(1);
+ colname = text_to_cstring(colnametext);
+ if (!strlen(colname)) {
+ elog(NOTICE, "Column name must be provided");
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ POSTGIS_RT_DEBUGF(3, "RASTER_histogramCoverage: colname = %s", colname);
+
+ /* band index is 1-based */
+ if (!PG_ARGISNULL(2))
+ bandindex = PG_GETARG_INT32(2);
+
+ /* exclude_nodata_value flag */
+ if (!PG_ARGISNULL(3))
+ exclude_nodata_value = PG_GETARG_BOOL(3);
+
+ /* sample % */
+ if (!PG_ARGISNULL(4)) {
+ sample = PG_GETARG_FLOAT8(4);
+ if (sample < 0 || sample > 1) {
+ elog(NOTICE, "Invalid sample percentage (must be between 0 and 1). Returning NULL");
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ else if (FLT_EQ(sample, 0.0))
+ sample = 1;
+ }
+ else
+ sample = 1;
+
+ /* bin_count */
+ if (!PG_ARGISNULL(5)) {
+ bin_count = PG_GETARG_INT32(5);
+ if (bin_count < 1) bin_count = 0;
+ }
+
+ /* bin_width */
+ if (!PG_ARGISNULL(6)) {
+ array = PG_GETARG_ARRAYTYPE_P(6);
+ etype = ARR_ELEMTYPE(array);
+ get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
+
+ switch (etype) {
+ case FLOAT4OID:
+ case FLOAT8OID:
+ break;
+ default:
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_histogramCoverage: Invalid data type for width");
+ SRF_RETURN_DONE(funcctx);
+ break;
+ }
+
+ deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
+ &nulls, &n);
+
+ bin_width = palloc(sizeof(double) * n);
+ for (i = 0, j = 0; i < n; i++) {
+ if (nulls[i]) continue;
+
+ switch (etype) {
+ case FLOAT4OID:
+ width = (double) DatumGetFloat4(e[i]);
+ break;
+ case FLOAT8OID:
+ width = (double) DatumGetFloat8(e[i]);
+ break;
+ }
+
+ if (width < 0 || FLT_EQ(width, 0.0)) {
+ elog(NOTICE, "Invalid value for width (must be greater than 0). Returning NULL");
+ pfree(bin_width);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ bin_width[j] = width;
+ POSTGIS_RT_DEBUGF(5, "bin_width[%d] = %f", j, bin_width[j]);
+ j++;
+ }
+ bin_width_count = j;
+
+ if (j < 1) {
+ pfree(bin_width);
+ bin_width = NULL;
+ }
+ }
+
+ /* right */
+ if (!PG_ARGISNULL(7))
+ right = PG_GETARG_BOOL(7);
+
+ /* connect to database */
+ spi_result = SPI_connect();
+ if (spi_result != SPI_OK_CONNECT) {
+
+ if (bin_width_count) pfree(bin_width);
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_histogramCoverage: Could not connect to database using SPI");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* coverage stats */
+ len = sizeof(char) * (strlen("SELECT min, max FROM _st_summarystats('','',,::boolean,)") + strlen(tablename) + strlen(colname) + (MAX_INT_CHARLEN * 2) + MAX_DBL_CHARLEN + 1);
+ sql = (char *) palloc(len);
+ if (NULL == sql) {
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_finish();
+
+ if (bin_width_count) pfree(bin_width);
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_histogramCoverage: Could not allocate memory for sql");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* get stats */
+ snprintf(sql, len, "SELECT min, max FROM _st_summarystats('%s','%s',%d,%d::boolean,%f)", tablename, colname, bandindex, (exclude_nodata_value ? 1 : 0), sample);
+ POSTGIS_RT_DEBUGF(3, "RASTER_histogramCoverage: %s", sql);
+ spi_result = SPI_execute(sql, TRUE, 0);
+ pfree(sql);
+ if (spi_result != SPI_OK_SELECT || SPI_tuptable == NULL || SPI_processed != 1) {
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_finish();
+
+ if (bin_width_count) pfree(bin_width);
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_histogramCoverage: Could not get summary stats of coverage");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ tupdesc = SPI_tuptable->tupdesc;
+ tuptable = SPI_tuptable;
+ tuple = tuptable->vals[0];
+
+ tmp = SPI_getvalue(tuple, tupdesc, 1);
+ if (NULL == tmp || !strlen(tmp)) {
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_finish();
+
+ if (bin_width_count) pfree(bin_width);
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_histogramCoverage: Could not get summary stats of coverage");
+ SRF_RETURN_DONE(funcctx);
+ }
+ min = strtod(tmp, NULL);
+ POSTGIS_RT_DEBUGF(3, "RASTER_histogramCoverage: min = %f", min);
+ pfree(tmp);
+
+ tmp = SPI_getvalue(tuple, tupdesc, 2);
+ if (NULL == tmp || !strlen(tmp)) {
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_finish();
+
+ if (bin_width_count) pfree(bin_width);
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_histogramCoverage: Could not get summary stats of coverage");
+ SRF_RETURN_DONE(funcctx);
+ }
+ max = strtod(tmp, NULL);
+ POSTGIS_RT_DEBUGF(3, "RASTER_histogramCoverage: max = %f", max);
+ pfree(tmp);
+
+ /* iterate through rasters of coverage */
+ /* create sql */
+ len = sizeof(char) * (strlen("SELECT \"\" FROM \"\" WHERE \"\" IS NOT NULL") + (strlen(colname) * 2) + strlen(tablename) + 1);
+ sql = (char *) palloc(len);
+ if (NULL == sql) {
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_finish();
+
+ if (bin_width_count) pfree(bin_width);
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_histogramCoverage: Could not allocate memory for sql");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* get cursor */
+ snprintf(sql, len, "SELECT \"%s\" FROM \"%s\" WHERE \"%s\" IS NOT NULL", colname, tablename, colname);
+ POSTGIS_RT_DEBUGF(3, "RASTER_histogramCoverage: %s", sql);
+ portal = SPI_cursor_open_with_args(
+ "coverage",
+ sql,
+ 0, NULL,
+ NULL, NULL,
+ TRUE, 0
+ );
+ pfree(sql);
+
+ /* process resultset */
+ SPI_cursor_fetch(portal, TRUE, 1);
+ while (SPI_processed == 1 && SPI_tuptable != NULL) {
+ tupdesc = SPI_tuptable->tupdesc;
+ tuptable = SPI_tuptable;
+ tuple = tuptable->vals[0];
+
+ datum = SPI_getbinval(tuple, tupdesc, 1, &isNull);
+ if (SPI_result == SPI_ERROR_NOATTRIBUTE) {
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ if (NULL != covhist) pfree(covhist);
+ if (bin_width_count) pfree(bin_width);
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_histogramCoverage: Could not get raster of coverage");
+ SRF_RETURN_DONE(funcctx);
+ }
+ else if (isNull) {
+ SPI_cursor_fetch(portal, TRUE, 1);
+ continue;
+ }
+
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM(datum);
+
+ raster = rt_raster_deserialize(pgraster, FALSE);
+ if (!raster) {
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ if (NULL != covhist) pfree(covhist);
+ if (bin_width_count) pfree(bin_width);
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_histogramCoverage: Could not deserialize raster");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* inspect number of bands*/
+ 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);
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ if (NULL != covhist) pfree(covhist);
+ if (bin_width_count) pfree(bin_width);
+
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* 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);
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ if (NULL != covhist) pfree(covhist);
+ if (bin_width_count) pfree(bin_width);
+
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* we need the raw values, hence the non-zero parameter */
+ stats = rt_band_get_summary_stats(band, (int) exclude_nodata_value, sample, 1, NULL, NULL, NULL);
+
+ rt_band_destroy(band);
+ rt_raster_destroy(raster);
+
+ if (NULL == stats) {
+ elog(NOTICE, "Could not compute summary statistics for band at index %d. Returning NULL", bandindex);
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ if (NULL != covhist) pfree(covhist);
+ if (bin_width_count) pfree(bin_width);
+
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* get histogram */
+ if (stats->count > 0) {
+ hist = rt_band_get_histogram(stats, bin_count, bin_width, bin_width_count, right, min, max, &count);
+ pfree(stats);
+ if (NULL == hist || !count) {
+ elog(NOTICE, "Could not compute histogram for band at index %d", bandindex);
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ if (NULL != covhist) pfree(covhist);
+ if (bin_width_count) pfree(bin_width);
+
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ POSTGIS_RT_DEBUGF(3, "%d bins returned", count);
+
+ /* coverage histogram */
+ if (NULL == covhist) {
+ covhist = (rt_histogram) SPI_palloc(sizeof(struct rt_histogram_t) * count);
+ if (NULL == covhist) {
+
+ pfree(hist);
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ if (bin_width_count) pfree(bin_width);
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_histogramCoverage: Could not allocate memory for histogram of coverage");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ for (i = 0; i < count; i++) {
+ sum += hist[i].count;
+ covhist[i].count = hist[i].count;
+ covhist[i].percent = 0;
+ covhist[i].min = hist[i].min;
+ covhist[i].max = hist[i].max;
+ }
+ }
+ else {
+ for (i = 0; i < count; i++) {
+ sum += hist[i].count;
+ covhist[i].count += hist[i].count;
+ }
+ }
+
+ pfree(hist);
+
+ /* assuming bin_count wasn't set, force consistency */
+ if (bin_count <= 0) bin_count = count;
+ }
+
+ /* next record */
+ SPI_cursor_fetch(portal, TRUE, 1);
+ }
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ if (bin_width_count) pfree(bin_width);
+
+ /* finish percent of histogram */
+ if (sum > 0) {
+ for (i = 0; i < count; i++)
+ covhist[i].percent = covhist[i].count / (double) sum;
+ }
+
+ /* Store needed information */
+ funcctx->user_fctx = covhist;
+
+ /* 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;
+ covhist2 = funcctx->user_fctx;
+
+ /* do when there is more left to send */
+ if (call_cntr < max_calls) {
+ int values_length = 4;
+ Datum values[values_length];
+ bool nulls[values_length];
+ HeapTuple tuple;
+ Datum result;
+
+ POSTGIS_RT_DEBUGF(3, "Result %d", call_cntr);
+
+ memset(nulls, FALSE, sizeof(bool) * values_length);
+
+ values[0] = Float8GetDatum(covhist2[call_cntr].min);
+ values[1] = Float8GetDatum(covhist2[call_cntr].max);
+ values[2] = Int64GetDatum(covhist2[call_cntr].count);
+ values[3] = Float8GetDatum(covhist2[call_cntr].percent);
+
+ /* build a tuple */
+ tuple = heap_form_tuple(tupdesc, values, nulls);
+
+ /* make the tuple into a datum */
+ result = HeapTupleGetDatum(tuple);
+
+ SRF_RETURN_NEXT(funcctx, result);
+ }
+ /* do when there is no more left */
+ else {
+ pfree(covhist2);
+ SRF_RETURN_DONE(funcctx);
+ }
+}
+
+/**
+ * Returns quantiles for a band
+ */
+PG_FUNCTION_INFO_V1(RASTER_quantile);
+Datum RASTER_quantile(PG_FUNCTION_ARGS)
+{
+ FuncCallContext *funcctx;
+ TupleDesc tupdesc;
+
+ int i;
+ rt_quantile quant;
+ rt_quantile quant2;
+ int call_cntr;
+ int max_calls;
+
+ /* first call of function */
+ if (SRF_IS_FIRSTCALL()) {
+ MemoryContext oldcontext;
+
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ rt_band band = NULL;
+ int32_t bandindex = 0;
+ int num_bands = 0;
+ bool exclude_nodata_value = TRUE;
+ double sample = 0;
+ double *quantiles = NULL;
+ uint32_t quantiles_count = 0;
+ double quantile = 0;
+ rt_bandstats stats = NULL;
+ uint32_t count;
+
+ int j;
+ int n;
+
+ 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);
+
+ /* pgraster is null, return nothing */
+ 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_quantile: Could not deserialize raster");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* band index is 1-based */
+ 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);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* exclude_nodata_value flag */
+ if (!PG_ARGISNULL(2))
+ exclude_nodata_value = PG_GETARG_BOOL(2);
+
+ /* sample % */
+ if (!PG_ARGISNULL(3)) {
+ sample = PG_GETARG_FLOAT8(3);
+ if (sample < 0 || sample > 1) {
+ elog(NOTICE, "Invalid sample percentage (must be between 0 and 1). Returning NULL");
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ else if (FLT_EQ(sample, 0.0))
+ sample = 1;
+ }
+ else
+ sample = 1;
+
+ /* quantiles */
+ if (!PG_ARGISNULL(4)) {
+ 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);
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_quantile: Invalid data type for quantiles");
+ SRF_RETURN_DONE(funcctx);
+ break;
+ }
+
+ deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
+ &nulls, &n);
+
+ quantiles = palloc(sizeof(double) * n);
+ for (i = 0, j = 0; i < n; i++) {
+ if (nulls[i]) continue;
+
+ switch (etype) {
+ case FLOAT4OID:
+ quantile = (double) DatumGetFloat4(e[i]);
+ break;
+ case FLOAT8OID:
+ quantile = (double) DatumGetFloat8(e[i]);
+ break;
+ }
+
+ if (quantile < 0 || quantile > 1) {
+ elog(NOTICE, "Invalid value for quantile (must be between 0 and 1). Returning NULL");
+ pfree(quantiles);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ quantiles[j] = quantile;
+ POSTGIS_RT_DEBUGF(5, "quantiles[%d] = %f", j, quantiles[j]);
+ j++;
+ }
+ quantiles_count = j;
+
+ if (j < 1) {
+ pfree(quantiles);
+ quantiles = 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);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* get stats */
+ stats = rt_band_get_summary_stats(band, (int) exclude_nodata_value, sample, 1, NULL, NULL, NULL);
+ rt_band_destroy(band);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ if (NULL == stats || NULL == stats->values) {
+ elog(NOTICE, "Could not retrieve summary statistics for band at index %d", bandindex);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ else if (stats->count < 1) {
+ elog(NOTICE, "Could not compute quantiles for band at index %d as the band has no values", bandindex);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* get quantiles */
+ quant = rt_band_get_quantiles(stats, quantiles, quantiles_count, &count);
+ if (quantiles_count) pfree(quantiles);
+ pfree(stats);
+ if (NULL == quant || !count) {
+ elog(NOTICE, "Could not compute quantiles for band at index %d", bandindex);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ POSTGIS_RT_DEBUGF(3, "%d quantiles returned", count);
+
+ /* Store needed information */
+ funcctx->user_fctx = quant;
+
+ /* 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;
+ quant2 = funcctx->user_fctx;
+
+ /* do when there is more left to send */
+ if (call_cntr < max_calls) {
+ int values_length = 2;
+ Datum values[values_length];
+ bool nulls[values_length];
+ HeapTuple tuple;
+ Datum result;
+
+ POSTGIS_RT_DEBUGF(3, "Result %d", call_cntr);
+
+ memset(nulls, FALSE, sizeof(bool) * values_length);
+
+ values[0] = Float8GetDatum(quant2[call_cntr].quantile);
+ values[1] = Float8GetDatum(quant2[call_cntr].value);
+
+ /* build a tuple */
+ tuple = heap_form_tuple(tupdesc, values, nulls);
+
+ /* make the tuple into a datum */
+ result = HeapTupleGetDatum(tuple);
+
+ SRF_RETURN_NEXT(funcctx, result);
+ }
+ /* do when there is no more left */
+ else {
+ pfree(quant2);
+ SRF_RETURN_DONE(funcctx);
+ }
+}
+
+/**
+ * Returns selected quantiles of a coverage for a specified band
+ */
+PG_FUNCTION_INFO_V1(RASTER_quantileCoverage);
+Datum RASTER_quantileCoverage(PG_FUNCTION_ARGS)
+{
+ FuncCallContext *funcctx;
+ TupleDesc tupdesc;
+
+ int i;
+ rt_quantile covquant = NULL;
+ rt_quantile covquant2;
+ int call_cntr;
+ int max_calls;
+
+ POSTGIS_RT_DEBUG(3, "RASTER_quantileCoverage: Starting");
+
+ /* first call of function */
+ if (SRF_IS_FIRSTCALL()) {
+ MemoryContext oldcontext;
+
+ text *tablenametext = NULL;
+ char *tablename = NULL;
+ text *colnametext = NULL;
+ char *colname = NULL;
+ int32_t bandindex = 1;
+ bool exclude_nodata_value = TRUE;
+ double sample = 0;
+ double *quantiles = NULL;
+ uint32_t quantiles_count = 0;
+ double quantile = 0;
+ uint32_t count;
+
+ int len = 0;
+ char *sql = NULL;
+ char *tmp = NULL;
+ uint64_t cov_count = 0;
+ int spi_result;
+ Portal portal;
+ SPITupleTable *tuptable = NULL;
+ HeapTuple tuple;
+ Datum datum;
+ bool isNull = FALSE;
+
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ rt_band band = NULL;
+ int num_bands = 0;
+ struct quantile_llist *qlls = NULL;
+ uint32_t qlls_count;
+
+ int j;
+ int n;
+
+ ArrayType *array;
+ Oid etype;
+ Datum *e;
+ bool *nulls;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+
+ POSTGIS_RT_DEBUG(3, "RASTER_quantileCoverage: first call of function");
+
+ /* 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);
+
+ /* tablename is null, return null */
+ if (PG_ARGISNULL(0)) {
+ elog(NOTICE, "Table name must be provided");
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ tablenametext = PG_GETARG_TEXT_P(0);
+ tablename = text_to_cstring(tablenametext);
+ if (!strlen(tablename)) {
+ elog(NOTICE, "Table name must be provided");
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ POSTGIS_RT_DEBUGF(3, "RASTER_quantileCoverage: tablename = %s", tablename);
+
+ /* column name is null, return null */
+ if (PG_ARGISNULL(1)) {
+ elog(NOTICE, "Column name must be provided");
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ colnametext = PG_GETARG_TEXT_P(1);
+ colname = text_to_cstring(colnametext);
+ if (!strlen(colname)) {
+ elog(NOTICE, "Column name must be provided");
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ POSTGIS_RT_DEBUGF(3, "RASTER_quantileCoverage: colname = %s", colname);
+
+ /* band index is 1-based */
+ if (!PG_ARGISNULL(2))
+ bandindex = PG_GETARG_INT32(2);
+
+ /* exclude_nodata_value flag */
+ if (!PG_ARGISNULL(3))
+ exclude_nodata_value = PG_GETARG_BOOL(3);
+
+ /* sample % */
+ if (!PG_ARGISNULL(4)) {
+ sample = PG_GETARG_FLOAT8(4);
+ if (sample < 0 || sample > 1) {
+ elog(NOTICE, "Invalid sample percentage (must be between 0 and 1). Returning NULL");
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ else if (FLT_EQ(sample, 0.0))
+ sample = 1;
+ }
+ else
+ sample = 1;
+
+ /* quantiles */
+ if (!PG_ARGISNULL(5)) {
+ array = PG_GETARG_ARRAYTYPE_P(5);
+ etype = ARR_ELEMTYPE(array);
+ get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
+
+ switch (etype) {
+ case FLOAT4OID:
+ case FLOAT8OID:
+ break;
+ default:
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_quantileCoverage: Invalid data type for quantiles");
+ SRF_RETURN_DONE(funcctx);
+ break;
+ }
+
+ deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
+ &nulls, &n);
+
+ quantiles = palloc(sizeof(double) * n);
+ for (i = 0, j = 0; i < n; i++) {
+ if (nulls[i]) continue;
+
+ switch (etype) {
+ case FLOAT4OID:
+ quantile = (double) DatumGetFloat4(e[i]);
+ break;
+ case FLOAT8OID:
+ quantile = (double) DatumGetFloat8(e[i]);
+ break;
+ }
+
+ if (quantile < 0 || quantile > 1) {
+ elog(NOTICE, "Invalid value for quantile (must be between 0 and 1). Returning NULL");
+ pfree(quantiles);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ quantiles[j] = quantile;
+ POSTGIS_RT_DEBUGF(5, "quantiles[%d] = %f", j, quantiles[j]);
+ j++;
+ }
+ quantiles_count = j;
+
+ if (j < 1) {
+ pfree(quantiles);
+ quantiles = NULL;
+ }
+ }
+
+ /* coverage stats */
+ /* connect to database */
+ spi_result = SPI_connect();
+ if (spi_result != SPI_OK_CONNECT) {
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_quantileCoverage: Could not connect to database using SPI");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ len = sizeof(char) * (strlen("SELECT count FROM _st_summarystats('','',,::boolean,)") + strlen(tablename) + strlen(colname) + (MAX_INT_CHARLEN * 2) + MAX_DBL_CHARLEN + 1);
+ sql = (char *) palloc(len);
+ if (NULL == sql) {
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_finish();
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_quantileCoverage: Could not allocate memory for sql");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* get stats */
+ snprintf(sql, len, "SELECT count FROM _st_summarystats('%s','%s',%d,%d::boolean,%f)", tablename, colname, bandindex, (exclude_nodata_value ? 1 : 0), sample);
+ POSTGIS_RT_DEBUGF(3, "stats sql: %s", sql);
+ spi_result = SPI_execute(sql, TRUE, 0);
+ pfree(sql);
+ if (spi_result != SPI_OK_SELECT || SPI_tuptable == NULL || SPI_processed != 1) {
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_finish();
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_quantileCoverage: Could not get summary stats of coverage");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ tupdesc = SPI_tuptable->tupdesc;
+ tuptable = SPI_tuptable;
+ tuple = tuptable->vals[0];
+
+ tmp = SPI_getvalue(tuple, tupdesc, 1);
+ if (NULL == tmp || !strlen(tmp)) {
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_finish();
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_quantileCoverage: Could not get summary stats of coverage");
+ SRF_RETURN_DONE(funcctx);
+ }
+ cov_count = strtol(tmp, NULL, 10);
+ POSTGIS_RT_DEBUGF(3, "covcount = %d", (int) cov_count);
+ pfree(tmp);
+
+ /* iterate through rasters of coverage */
+ /* create sql */
+ len = sizeof(char) * (strlen("SELECT \"\" FROM \"\" WHERE \"\" IS NOT NULL") + (strlen(colname) * 2) + strlen(tablename) + 1);
+ sql = (char *) palloc(len);
+ if (NULL == sql) {
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_finish();
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_quantileCoverage: Could not allocate memory for sql");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* get cursor */
+ snprintf(sql, len, "SELECT \"%s\" FROM \"%s\" WHERE \"%s\" IS NOT NULL", colname, tablename, colname);
+ POSTGIS_RT_DEBUGF(3, "coverage sql: %s", sql);
+ portal = SPI_cursor_open_with_args(
+ "coverage",
+ sql,
+ 0, NULL,
+ NULL, NULL,
+ TRUE, 0
+ );
+ pfree(sql);
+
+ /* process resultset */
+ SPI_cursor_fetch(portal, TRUE, 1);
+ while (SPI_processed == 1 && SPI_tuptable != NULL) {
+ if (NULL != covquant) pfree(covquant);
+
+ tupdesc = SPI_tuptable->tupdesc;
+ tuptable = SPI_tuptable;
+ tuple = tuptable->vals[0];
+
+ datum = SPI_getbinval(tuple, tupdesc, 1, &isNull);
+ if (SPI_result == SPI_ERROR_NOATTRIBUTE) {
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_quantileCoverage: Could not get raster of coverage");
+ SRF_RETURN_DONE(funcctx);
+ }
+ else if (isNull) {
+ SPI_cursor_fetch(portal, TRUE, 1);
+ continue;
+ }
+
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM(datum);
+
+ raster = rt_raster_deserialize(pgraster, FALSE);
+ if (!raster) {
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_quantileCoverage: Could not deserialize raster");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* inspect number of bands*/
+ 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);
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* get band */
+ band = rt_raster_get_band(raster, bandindex - 1);
+ if (!band) {
+ elog(NOTICE, "Could not find raster band of index %d. Returning NULL", bandindex);
+
+ rt_raster_destroy(raster);
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ covquant = rt_band_get_quantiles_stream(
+ band,
+ exclude_nodata_value, sample, cov_count,
+ &qlls, &qlls_count,
+ quantiles, quantiles_count,
+ &count
+ );
+
+ rt_band_destroy(band);
+ rt_raster_destroy(raster);
+
+ if (NULL == covquant || !count) {
+ elog(NOTICE, "Could not compute quantiles for band at index %d", bandindex);
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* next record */
+ SPI_cursor_fetch(portal, TRUE, 1);
+ }
+
+ covquant2 = SPI_palloc(sizeof(struct rt_quantile_t) * count);
+ for (i = 0; i < count; i++) {
+ covquant2[i].quantile = covquant[i].quantile;
+ covquant2[i].has_value = covquant[i].has_value;
+ if (covquant2[i].has_value)
+ covquant2[i].value = covquant[i].value;
+ }
+
+ if (NULL != covquant) pfree(covquant);
+ quantile_llist_destroy(&qlls, qlls_count);
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ if (quantiles_count) pfree(quantiles);
+
+ POSTGIS_RT_DEBUGF(3, "%d quantiles returned", count);
+
+ /* Store needed information */
+ funcctx->user_fctx = covquant2;
+
+ /* 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;
+ covquant2 = funcctx->user_fctx;
+
+ /* do when there is more left to send */
+ if (call_cntr < max_calls) {
+ int values_length = 2;
+ Datum values[values_length];
+ bool nulls[values_length];
+ HeapTuple tuple;
+ Datum result;
+
+ POSTGIS_RT_DEBUGF(3, "Result %d", call_cntr);
+
+ memset(nulls, FALSE, sizeof(bool) * values_length);
+
+ values[0] = Float8GetDatum(covquant2[call_cntr].quantile);
+ if (covquant2[call_cntr].has_value)
+ values[1] = Float8GetDatum(covquant2[call_cntr].value);
+ else
+ nulls[1] = TRUE;
+
+ /* build a tuple */
+ tuple = heap_form_tuple(tupdesc, values, nulls);
+
+ /* make the tuple into a datum */
+ result = HeapTupleGetDatum(tuple);
+
+ SRF_RETURN_NEXT(funcctx, result);
+ }
+ /* do when there is no more left */
+ else {
+ POSTGIS_RT_DEBUG(3, "done");
+ pfree(covquant2);
+ SRF_RETURN_DONE(funcctx);
+ }
+}
+
+/* get counts of values */
+PG_FUNCTION_INFO_V1(RASTER_valueCount);
+Datum RASTER_valueCount(PG_FUNCTION_ARGS) {
+ FuncCallContext *funcctx;
+ TupleDesc tupdesc;
+
+ int i;
+ rt_valuecount vcnts;
+ rt_valuecount vcnts2;
+ int call_cntr;
+ int max_calls;
+
+ /* first call of function */
+ if (SRF_IS_FIRSTCALL()) {
+ MemoryContext oldcontext;
+
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ rt_band band = NULL;
+ int32_t bandindex = 0;
+ int num_bands = 0;
+ bool exclude_nodata_value = TRUE;
+ double *search_values = NULL;
+ uint32_t search_values_count = 0;
+ double roundto = 0;
+ uint32_t count;
+
+ int j;
+ int n;
+
+ 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);
+
+ /* pgraster is null, return nothing */
+ 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_valueCount: Could not deserialize raster");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* band index is 1-based */
+ 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);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* exclude_nodata_value flag */
+ if (!PG_ARGISNULL(2))
+ exclude_nodata_value = PG_GETARG_BOOL(2);
+
+ /* search values */
+ if (!PG_ARGISNULL(3)) {
+ array = PG_GETARG_ARRAYTYPE_P(3);
+ 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_valueCount: Invalid data type for values");
+ SRF_RETURN_DONE(funcctx);
+ break;
+ }
+
+ deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
+ &nulls, &n);
+
+ search_values = palloc(sizeof(double) * n);
+ for (i = 0, j = 0; i < n; i++) {
+ if (nulls[i]) continue;
+
+ switch (etype) {
+ case FLOAT4OID:
+ search_values[j] = (double) DatumGetFloat4(e[i]);
+ break;
+ case FLOAT8OID:
+ search_values[j] = (double) DatumGetFloat8(e[i]);
+ break;
+ }
+
+ POSTGIS_RT_DEBUGF(5, "search_values[%d] = %f", j, search_values[j]);
+ j++;
+ }
+ search_values_count = j;
+
+ if (j < 1) {
+ pfree(search_values);
+ search_values = NULL;
+ }
+ }
+
+ /* roundto */
+ if (!PG_ARGISNULL(4)) {
+ roundto = PG_GETARG_FLOAT8(4);
+ if (roundto < 0.) roundto = 0;
+ }
+
+ /* 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);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* get counts of values */
+ vcnts = rt_band_get_value_count(band, (int) exclude_nodata_value, search_values, search_values_count, roundto, NULL, &count);
+ rt_band_destroy(band);
+ rt_raster_destroy(raster);
+ PG_FREE_IF_COPY(pgraster, 0);
+ if (NULL == vcnts || !count) {
+ elog(NOTICE, "Could not count the values for band at index %d", bandindex);
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ POSTGIS_RT_DEBUGF(3, "%d value counts returned", count);
+
+ /* Store needed information */
+ funcctx->user_fctx = vcnts;
+
+ /* 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;
+ vcnts2 = funcctx->user_fctx;
+
+ /* do when there is more left to send */
+ if (call_cntr < max_calls) {
+ int values_length = 3;
+ Datum values[values_length];
+ bool nulls[values_length];
+ HeapTuple tuple;
+ Datum result;
+
+ POSTGIS_RT_DEBUGF(3, "Result %d", call_cntr);
+
+ memset(nulls, FALSE, sizeof(bool) * values_length);
+
+ values[0] = Float8GetDatum(vcnts2[call_cntr].value);
+ values[1] = UInt32GetDatum(vcnts2[call_cntr].count);
+ values[2] = Float8GetDatum(vcnts2[call_cntr].percent);
+
+ /* build a tuple */
+ tuple = heap_form_tuple(tupdesc, values, nulls);
+
+ /* make the tuple into a datum */
+ result = HeapTupleGetDatum(tuple);
+
+ SRF_RETURN_NEXT(funcctx, result);
+ }
+ /* do when there is no more left */
+ else {
+ pfree(vcnts2);
+ SRF_RETURN_DONE(funcctx);
+ }
+}
+
+/* get counts of values for a coverage */
+PG_FUNCTION_INFO_V1(RASTER_valueCountCoverage);
+Datum RASTER_valueCountCoverage(PG_FUNCTION_ARGS) {
+ FuncCallContext *funcctx;
+ TupleDesc tupdesc;
+
+ int i;
+ uint64_t covcount = 0;
+ uint64_t covtotal = 0;
+ rt_valuecount covvcnts = NULL;
+ rt_valuecount covvcnts2;
+ int call_cntr;
+ int max_calls;
+
+ POSTGIS_RT_DEBUG(3, "RASTER_valueCountCoverage: Starting");
+
+ /* first call of function */
+ if (SRF_IS_FIRSTCALL()) {
+ MemoryContext oldcontext;
+
+ text *tablenametext = NULL;
+ char *tablename = NULL;
+ text *colnametext = NULL;
+ char *colname = NULL;
+ int32_t bandindex = 1;
+ bool exclude_nodata_value = TRUE;
+ double *search_values = NULL;
+ uint32_t search_values_count = 0;
+ double roundto = 0;
+
+ int len = 0;
+ char *sql = NULL;
+ int spi_result;
+ Portal portal;
+ SPITupleTable *tuptable = NULL;
+ HeapTuple tuple;
+ Datum datum;
+ bool isNull = FALSE;
+ rt_pgraster *pgraster = NULL;
+ rt_raster raster = NULL;
+ rt_band band = NULL;
+ int num_bands = 0;
+ uint32_t count;
+ uint32_t total;
+ rt_valuecount vcnts = NULL;
+ int exists = 0;
+
+ int j;
+ int n;
+
+ 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);
+
+ /* tablename is null, return null */
+ if (PG_ARGISNULL(0)) {
+ elog(NOTICE, "Table name must be provided");
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ tablenametext = PG_GETARG_TEXT_P(0);
+ tablename = text_to_cstring(tablenametext);
+ if (!strlen(tablename)) {
+ elog(NOTICE, "Table name must be provided");
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ POSTGIS_RT_DEBUGF(3, "tablename = %s", tablename);
+
+ /* column name is null, return null */
+ if (PG_ARGISNULL(1)) {
+ elog(NOTICE, "Column name must be provided");
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ colnametext = PG_GETARG_TEXT_P(1);
+ colname = text_to_cstring(colnametext);
+ if (!strlen(colname)) {
+ elog(NOTICE, "Column name must be provided");
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+ POSTGIS_RT_DEBUGF(3, "colname = %s", colname);
+
+ /* band index is 1-based */
+ if (!PG_ARGISNULL(2))
+ bandindex = PG_GETARG_INT32(2);
+
+ /* exclude_nodata_value flag */
+ if (!PG_ARGISNULL(3))
+ exclude_nodata_value = PG_GETARG_BOOL(3);
+
+ /* search values */
+ if (!PG_ARGISNULL(4)) {
+ array = PG_GETARG_ARRAYTYPE_P(4);
+ etype = ARR_ELEMTYPE(array);
+ get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign);
+
+ switch (etype) {
+ case FLOAT4OID:
+ case FLOAT8OID:
+ break;
+ default:
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_valueCountCoverage: Invalid data type for values");
+ SRF_RETURN_DONE(funcctx);
+ break;
+ }
+
+ deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
+ &nulls, &n);
+
+ search_values = palloc(sizeof(double) * n);
+ for (i = 0, j = 0; i < n; i++) {
+ if (nulls[i]) continue;
+
+ switch (etype) {
+ case FLOAT4OID:
+ search_values[j] = (double) DatumGetFloat4(e[i]);
+ break;
+ case FLOAT8OID:
+ search_values[j] = (double) DatumGetFloat8(e[i]);
+ break;
+ }
+
+ POSTGIS_RT_DEBUGF(5, "search_values[%d] = %f", j, search_values[j]);
+ j++;
+ }
+ search_values_count = j;
+
+ if (j < 1) {
+ pfree(search_values);
+ search_values = NULL;
+ }
+ }
+
+ /* roundto */
+ if (!PG_ARGISNULL(5)) {
+ roundto = PG_GETARG_FLOAT8(5);
+ if (roundto < 0.) roundto = 0;
+ }
+
+ /* iterate through rasters of coverage */
+ /* connect to database */
+ spi_result = SPI_connect();
+ if (spi_result != SPI_OK_CONNECT) {
+
+ if (search_values_count) pfree(search_values);
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_valueCountCoverage: Could not connect to database using SPI");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* create sql */
+ len = sizeof(char) * (strlen("SELECT \"\" FROM \"\" WHERE \"\" IS NOT NULL") + (strlen(colname) * 2) + strlen(tablename) + 1);
+ sql = (char *) palloc(len);
+ if (NULL == sql) {
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_finish();
+
+ if (search_values_count) pfree(search_values);
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_valueCountCoverage: Could not allocate memory for sql");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* get cursor */
+ snprintf(sql, len, "SELECT \"%s\" FROM \"%s\" WHERE \"%s\" IS NOT NULL", colname, tablename, colname);
+ POSTGIS_RT_DEBUGF(3, "RASTER_valueCountCoverage: %s", sql);
+ portal = SPI_cursor_open_with_args(
+ "coverage",
+ sql,
+ 0, NULL,
+ NULL, NULL,
+ TRUE, 0
+ );
+ pfree(sql);
+
+ /* process resultset */
+ SPI_cursor_fetch(portal, TRUE, 1);
+ while (SPI_processed == 1 && SPI_tuptable != NULL) {
+ tupdesc = SPI_tuptable->tupdesc;
+ tuptable = SPI_tuptable;
+ tuple = tuptable->vals[0];
+
+ datum = SPI_getbinval(tuple, tupdesc, 1, &isNull);
+ if (SPI_result == SPI_ERROR_NOATTRIBUTE) {
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ if (NULL != covvcnts) pfree(covvcnts);
+ if (search_values_count) pfree(search_values);
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_valueCountCoverage: Could not get raster of coverage");
+ SRF_RETURN_DONE(funcctx);
+ }
+ else if (isNull) {
+ SPI_cursor_fetch(portal, TRUE, 1);
+ continue;
+ }
+
+ pgraster = (rt_pgraster *) PG_DETOAST_DATUM(datum);
+
+ raster = rt_raster_deserialize(pgraster, FALSE);
+ if (!raster) {
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ if (NULL != covvcnts) pfree(covvcnts);
+ if (search_values_count) pfree(search_values);
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_valueCountCoverage: Could not deserialize raster");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* inspect number of bands*/
+ 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);
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ if (NULL != covvcnts) pfree(covvcnts);
+ if (search_values_count) pfree(search_values);
+
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* 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);
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ if (NULL != covvcnts) pfree(covvcnts);
+ if (search_values_count) pfree(search_values);
+
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ /* get counts of values */
+ vcnts = rt_band_get_value_count(band, (int) exclude_nodata_value, search_values, search_values_count, roundto, &total, &count);
+ rt_band_destroy(band);
+ rt_raster_destroy(raster);
+ if (NULL == vcnts || !count) {
+ elog(NOTICE, "Could not count the values for band at index %d", bandindex);
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ if (NULL != covvcnts) free(covvcnts);
+ if (search_values_count) pfree(search_values);
+
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ POSTGIS_RT_DEBUGF(3, "%d value counts returned", count);
+
+ if (NULL == covvcnts) {
+ covvcnts = (rt_valuecount) SPI_palloc(sizeof(struct rt_valuecount_t) * count);
+ if (NULL == covvcnts) {
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ if (search_values_count) pfree(search_values);
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_valueCountCoverage: Could not allocate memory for value counts of coverage");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ for (i = 0; i < count; i++) {
+ covvcnts[i].value = vcnts[i].value;
+ covvcnts[i].count = vcnts[i].count;
+ covvcnts[i].percent = -1;
+ }
+
+ covcount = count;
+ }
+ else {
+ for (i = 0; i < count; i++) {
+ exists = 0;
+
+ for (j = 0; j < covcount; j++) {
+ if (FLT_EQ(vcnts[i].value, covvcnts[j].value)) {
+ exists = 1;
+ break;
+ }
+ }
+
+ if (exists) {
+ covvcnts[j].count += vcnts[i].count;
+ }
+ else {
+ covcount++;
+ covvcnts = SPI_repalloc(covvcnts, sizeof(struct rt_valuecount_t) * covcount);
+ if (NULL == covvcnts) {
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ if (search_values_count) pfree(search_values);
+ if (NULL != covvcnts) free(covvcnts);
+
+ MemoryContextSwitchTo(oldcontext);
+ elog(ERROR, "RASTER_valueCountCoverage: Could not change allocated memory for value counts of coverage");
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ covvcnts[covcount - 1].value = vcnts[i].value;
+ covvcnts[covcount - 1].count = vcnts[i].count;
+ covvcnts[covcount - 1].percent = -1;
+ }
+ }
+ }
+
+ covtotal += total;
+
+ pfree(vcnts);
+
+ /* next record */
+ SPI_cursor_fetch(portal, TRUE, 1);
+ }
+
+ if (SPI_tuptable) SPI_freetuptable(tuptable);
+ SPI_cursor_close(portal);
+ SPI_finish();
+
+ if (search_values_count) pfree(search_values);
+
+ /* compute percentages */
+ for (i = 0; i < covcount; i++) {
+ covvcnts[i].percent = (double) covvcnts[i].count / covtotal;
+ }
+
+ /* Store needed information */
+ funcctx->user_fctx = covvcnts;
+
+ /* total number of tuples to be returned */
+ funcctx->max_calls = covcount;
+
+ /* 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;
+ covvcnts2 = funcctx->user_fctx;
+
+ /* do when there is more left to send */
+ if (call_cntr < max_calls) {
+ int values_length = 3;
+ Datum values[values_length];
+ bool nulls[values_length];
+ HeapTuple tuple;
+ Datum result;
+
+ POSTGIS_RT_DEBUGF(3, "Result %d", call_cntr);
+
+ memset(nulls, FALSE, sizeof(bool) * values_length);
+
+ values[0] = Float8GetDatum(covvcnts2[call_cntr].value);
+ values[1] = UInt32GetDatum(covvcnts2[call_cntr].count);
+ values[2] = Float8GetDatum(covvcnts2[call_cntr].percent);
+
+ /* build a tuple */
+ tuple = heap_form_tuple(tupdesc, values, nulls);
+
+ /* make the tuple into a datum */
+ result = HeapTupleGetDatum(tuple);
+
+ SRF_RETURN_NEXT(funcctx, result);
+ }
+ /* do when there is no more left */
+ else {
+ pfree(covvcnts2);
+ SRF_RETURN_DONE(funcctx);
+ }
+}
+
--- /dev/null
+/*
+ * $Id$
+ *
+ * WKTRaster - Raster Types for PostGIS
+ * http://www.postgis.org/support/wiki/index.php?WKTRasterHomePage
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <postgres.h> /* for palloc */
+#include <fmgr.h>
+#include <utils/builtins.h>
+
+#include "../../postgis_config.h"
+#include "lwgeom_pg.h"
+
+#include "rtpostgis.h"
+
+Datum RASTER_lib_version(PG_FUNCTION_ARGS);
+Datum RASTER_lib_build_date(PG_FUNCTION_ARGS);
+Datum RASTER_gdal_version(PG_FUNCTION_ARGS);
+Datum RASTER_minPossibleValue(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(RASTER_lib_version);
+Datum RASTER_lib_version(PG_FUNCTION_ARGS)
+{
+ char ver[64];
+ text *result;
+
+ snprintf(ver, 64, "%s r%d", POSTGIS_LIB_VERSION, POSTGIS_SVN_REVISION);
+ ver[63] = '\0';
+
+ result = cstring2text(ver);
+ PG_RETURN_TEXT_P(result);
+}
+
+PG_FUNCTION_INFO_V1(RASTER_lib_build_date);
+Datum RASTER_lib_build_date(PG_FUNCTION_ARGS)
+{
+ char *ver = POSTGIS_BUILD_DATE;
+ text *result;
+ result = palloc(VARHDRSZ + strlen(ver));
+ SET_VARSIZE(result, VARHDRSZ + strlen(ver));
+ memcpy(VARDATA(result), ver, strlen(ver));
+ PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(RASTER_gdal_version);
+Datum RASTER_gdal_version(PG_FUNCTION_ARGS)
+{
+ const char *ver = rt_util_gdal_version("--version");
+ text *result;
+
+ /* add indicator if GDAL isn't configured right */
+ if (!rt_util_gdal_configured()) {
+ char *rtn = NULL;
+ rtn = palloc(strlen(ver) + strlen(" GDAL_DATA not found") + 1);
+ if (!rtn)
+ result = cstring2text(ver);
+ else {
+ sprintf(rtn, "%s GDAL_DATA not found", ver);
+ result = cstring2text(rtn);
+ pfree(rtn);
+ }
+ }
+ else
+ result = cstring2text(ver);
+
+ PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(RASTER_minPossibleValue);
+Datum RASTER_minPossibleValue(PG_FUNCTION_ARGS)
+{
+ text *pixeltypetext = NULL;
+ char *pixeltypechar = NULL;
+ rt_pixtype pixtype = PT_END;
+ double pixsize = 0;
+
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+
+ pixeltypetext = PG_GETARG_TEXT_P(0);
+ pixeltypechar = text_to_cstring(pixeltypetext);
+
+ pixtype = rt_pixtype_index_from_name(pixeltypechar);
+ if (pixtype == PT_END) {
+ elog(ERROR, "RASTER_minPossibleValue: Invalid pixel type: %s", pixeltypechar);
+ PG_RETURN_NULL();
+ }
+
+ pixsize = rt_pixtype_get_min_value(pixtype);
+
+ /*
+ correct pixsize of unsigned pixel types
+ example: for PT_8BUI, the value is CHAR_MIN but if char is signed,
+ the value returned is -127 instead of 0.
+ */
+ switch (pixtype) {
+ case PT_1BB:
+ case PT_2BUI:
+ case PT_4BUI:
+ case PT_8BUI:
+ case PT_16BUI:
+ case PT_32BUI:
+ pixsize = 0;
+ break;
+ default:
+ break;
+ }
+
+ PG_RETURN_FLOAT8(pixsize);
+}
+
--- /dev/null
+/*
+ * $Id$
+ *
+ * WKTRaster - Raster Types for PostGIS
+ * http://www.postgis.org/support/wiki/index.php?WKTRasterHomePage
+ *
+ * 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@keybit.net>
+ *
+ * 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 3 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+/***************************************************************
+ * Some rules for returning NOTICE or ERROR...
+ *
+ * Send an ERROR like:
+ *
+ * elog(ERROR, "RASTER_out: Could not deserialize raster");
+ *
+ * only when:
+ *
+ * -something wrong happen with memory,
+ * -a function got an invalid argument ('3BUI' as pixel type) so that no row can
+ * be processed
+ *
+ * *** IMPORTANT: elog(ERROR, ...) does NOT return to calling function ***
+ *
+ * Send a NOTICE like:
+ *
+ * elog(NOTICE, "Invalid band index (must use 1-based). Returning NULL");
+ *
+ * when arguments (e.g. x, y, band) are NULL or out of range so that some or
+ * most rows can be processed anyway
+ *
+ * in this case,
+ * for SET functions or function normally returning a modified raster, return
+ * the original raster
+ * for GET functions, return NULL
+ * try to deduce a valid parameter value if it makes sence (e.g. out of range
+ * index for addBand)
+ *
+ * Do not put the name of the faulty function for NOTICEs, only with ERRORs.
+ *
+ ****************************************************************/
+
+/******************************************************************************
+ * Some notes on memory management...
+ *
+ * Every time a SQL function is called, PostgreSQL creates a new memory context.
+ * So, all the memory allocated with palloc/repalloc in that context is
+ * automatically free'd at the end of the function. If you want some data to
+ * live between function calls, you have 2 options:
+ *
+ * - Use fcinfo->flinfo->fn_mcxt contex to store the data (by pointing the
+ * data you want to keep with fcinfo->flinfo->fn_extra)
+ * - Use SRF funcapi, and storing the data at multi_call_memory_ctx (by pointing
+ * the data you want to keep with funcctx->user_fctx. funcctx is created by
+ * funcctx = SPI_FIRSTCALL_INIT()). Recommended way in functions returning rows,
+ * like RASTER_dumpAsPolygons (see section 34.9.9 at
+ * http://www.postgresql.org/docs/8.4/static/xfunc-c.html).
+ *
+ * But raster code follows the same philosophy than the rest of PostGIS: keep
+ * memory as clean as possible. So, we free all allocated memory.
+ *
+ * TODO: In case of functions returning NULL, we should free the memory too.
+ *****************************************************************************/
+
+/******************************************************************************
+ * Notes for use of PG_DETOAST_DATUM(), PG_DETOAST_DATUM_SLICE()
+ * and PG_DETOAST_DATUM_COPY()
+ *
+ * When ONLY getting raster (not band) metadata, use PG_DETOAST_DATUM_SLICE()
+ * as it is generally quicker to get only the chunk of memory that contains
+ * the raster metadata.
+ *
+ * Example: PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(0), 0,
+ * sizeof(struct rt_raster_serialized_t))
+ *
+ * When ONLY setting raster or band(s) metadata OR reading band data, use
+ * PG_DETOAST_DATUM() as rt_raster_deserialize() allocates local memory
+ * for the raster and band(s) metadata.
+ *
+ * Example: PG_DETOAST_DATUM(PG_GETARG_DATUM(0))
+ *
+ * When SETTING band pixel values, use PG_DETOAST_DATUM_COPY(). This is
+ * because band data (not metadata) is just a pointer to the correct
+ * memory location in the detoasted datum. What is returned from
+ * PG_DETOAST_DATUM() may or may not be a copy of the input datum.
+ *
+ * From the comments in postgresql/src/include/fmgr.h...
+ *
+ * pg_detoast_datum() gives you either the input datum (if not toasted)
+ * or a detoasted copy allocated with palloc().
+ *
+ * From the mouth of Tom Lane...
+ * http://archives.postgresql.org/pgsql-hackers/2002-01/msg01289.php
+ *
+ * PG_DETOAST_DATUM_COPY guarantees to give you a copy, even if the
+ * original wasn't toasted. This allows you to scribble on the input,
+ * in case that happens to be a useful way of forming your result.
+ * Without a forced copy, a routine for a pass-by-ref datatype must
+ * NEVER, EVER scribble on its input ... because very possibly it'd
+ * be scribbling on a valid tuple in a disk buffer, or a valid entry
+ * in the syscache.
+ *
+ * The key detail above is that the raster datatype is a varlena, a
+ * passed by reference datatype.
+ *
+ * Example: PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0))
+ *
+ * If in doubt, use PG_DETOAST_DATUM_COPY() as that guarantees that the input
+ * datum is copied for use.
+ *****************************************************************************/
+
+#include <postgres.h> /* for palloc */
+#include <fmgr.h> /* for PG_MODULE_MAGIC */
+#include "utils/guc.h"
+
+#include "../../postgis_config.h"
+#include "lwgeom_pg.h"
+
+#include "rtpostgis.h"
+
+/*
+ * This is required for builds against pgsql
+ */
+PG_MODULE_MAGIC;
+
+/* Module load callback */
+void _PG_init(void);
+
+static void
+rtpg_assignHookGDALDataPath(const char *newpath, void *extra) {
+ POSTGIS_RT_DEBUGF(4, "newpath = %s", newpath);
+
+ /* clear finder cache */
+ CPLFinderClean();
+
+ /* clear cached OSR */
+ OSRCleanup();
+
+ /* set GDAL_DATA */
+ CPLSetConfigOption("GDAL_DATA", newpath);
+ POSTGIS_RT_DEBUGF(4, "GDAL_DATA = %s", CPLGetConfigOption("GDAL_DATA", NULL));
+}
+
+static char *gdaldatapath;
+
+/* Module load callback */
+void
+_PG_init(void) {
+ /* Install liblwgeom handlers */
+ pg_install_lwgeom_handlers();
+
+ /* TODO: Install raster callbacks (see rt_init_allocators)??? */
+
+ /* Define custom GUC variables. */
+ DefineCustomStringVariable(
+ "postgis.gdal.datapath", /* name */
+ "Path to GDAL data files.", /* short_desc */
+ "Physical path to directory containing GDAL data files (sets the GDAL_DATA config option).", /* long_desc */
+ &gdaldatapath, /* valueAddr */
+ NULL, /* bootValue */
+ PGC_SUSET, /* GucContext context */
+ 0, /* int flags */
+#if POSTGIS_PGSQL_VERSION >= 91
+ NULL, /* GucStringCheckHook check_hook */
+#endif
+ rtpg_assignHookGDALDataPath, /* GucStringAssignHook assign_hook */
+ NULL /* GucShowHook show_hook */
+ );
+}
+
+/* ---------------------------------------------------------------- */
+/* Memory allocation / error reporting hooks */
+/* TODO: reuse the ones in libpgcommon ? */
+/* ---------------------------------------------------------------- */
+
+static void *
+rt_pg_alloc(size_t size)
+{
+ void * result;
+
+ POSTGIS_RT_DEBUGF(5, "rt_pgalloc(%ld) called", (long int) size);
+
+ result = palloc(size);
+
+ return result;
+}
+
+static void *
+rt_pg_realloc(void *mem, size_t size)
+{
+ void * result;
+
+ POSTGIS_RT_DEBUGF(5, "rt_pg_realloc(%ld) called", (long int) size);
+
+ if (mem)
+ result = repalloc(mem, size);
+
+ else
+ result = palloc(size);
+
+ return result;
+}
+
+static void
+rt_pg_free(void *ptr)
+{
+ POSTGIS_RT_DEBUG(5, "rt_pfree called");
+ pfree(ptr);
+}
+
+static void
+rt_pg_error(const char *fmt, va_list ap)
+{
+#define ERRMSG_MAXLEN 256
+
+ char errmsg[ERRMSG_MAXLEN+1];
+
+ vsnprintf (errmsg, ERRMSG_MAXLEN, fmt, ap);
+
+ errmsg[ERRMSG_MAXLEN]='\0';
+ ereport(ERROR, (errmsg_internal("%s", errmsg)));
+}
+
+static void
+rt_pg_notice(const char *fmt, va_list ap)
+{
+ char *msg;
+
+ /*
+ * This is a GNU extension.
+ * Dunno how to handle errors here.
+ */
+ if (!lw_vasprintf (&msg, fmt, ap))
+ {
+ va_end (ap);
+ return;
+ }
+ ereport(NOTICE, (errmsg_internal("%s", msg)));
+ free(msg);
+}
+
+
+void
+rt_init_allocators(void)
+{
+ /* raster callback - install raster handlers */
+ rt_set_handlers(rt_pg_alloc, rt_pg_realloc, rt_pg_free, rt_pg_error,
+ rt_pg_notice, rt_pg_notice);
+}
*
*/
-#ifndef RT_PG_H_INCLUDED
-#define RT_PG_H_INCLUDED
+#ifndef RTPOSTGIS_H_INCLUDED
+#define RTPOSTGIS_H_INCLUDED
-#include <stdint.h> /* for int16_t and friends */
-
-#include "rt_api.h"
-#include "../../postgis_config.h"
-#include "../raster_config.h"
+#include "librtcore.h"
/* Debugging macros */
#if POSTGIS_DEBUG_LEVEL > 0
#endif
-
-typedef struct rt_pgband8_t {
- uint8_t pixtype;
- uint8_t data[1];
-} rt_pgband8;
-
-typedef struct rt_pgband16_t {
- uint8_t pixtype;
- uint8_t pad;
- uint8_t data[1];
-} rt_pgband16;
-
-typedef struct rt_pgband32_t {
- uint8_t pixtype;
- uint8_t pad0;
- uint8_t pad1;
- uint8_t pad2;
- uint8_t data[1];
-} rt_pgband32;
-
-typedef struct rt_pgband64_t {
- uint8_t pixtype;
- uint8_t pad[7];
- uint8_t data[1];
-} rt_pgband64;
-
-typedef struct rt_pgband_t {
- uint8_t pixtype;
- uint8_t data[1];
-} rt_pgband;
-
/* Header of PostgreSQL-stored RASTER value,
* and binary representation of it */
typedef struct rt_raster_serialized_t rt_pgraster;
-#endif /* RT_PG_H_INCLUDED */
+/* maximum char length required to hold any double or long long value */
+#define MAX_DBL_CHARLEN (3 + DBL_MANT_DIG - DBL_MIN_EXP)
+#define MAX_INT_CHARLEN 32
+
+#endif /* RTPOSTGIS_H_INCLUDED */
*
**********************************************************************/
-#include "rt_api.h"
+#include "librtcore.h"
+
+#include "../../../postgis_config.h"
+#include "../../raster_config.h"
#define PG_TEST(test_func) { #test_func, test_func }
#define MAX_CUNIT_MSG_LENGTH 512