]> granicus.if.org Git - postgis/commitdiff
Split rt_core/rt_api.c and rt_pg/rt_pg.c into smaller files.
authorBborie Park <bkpark at ucdavis.edu>
Fri, 2 Aug 2013 19:51:09 +0000 (19:51 +0000)
committerBborie Park <bkpark at ucdavis.edu>
Fri, 2 Aug 2013 19:51:09 +0000 (19:51 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@11719 b70326c6-7e19-0410-871a-916f4a2858ee

37 files changed:
NEWS
raster/loader/raster2pgsql.h
raster/rt_core/Makefile.in
raster/rt_core/librtcore.h [moved from raster/rt_core/rt_api.h with 99% similarity]
raster/rt_core/librtcore_internal.h [new file with mode: 0644]
raster/rt_core/rt_api.c [deleted file]
raster/rt_core/rt_band.c [new file with mode: 0644]
raster/rt_core/rt_context.c [new file with mode: 0644]
raster/rt_core/rt_geometry.c [new file with mode: 0644]
raster/rt_core/rt_mapalgebra.c [new file with mode: 0644]
raster/rt_core/rt_pixel.c [new file with mode: 0644]
raster/rt_core/rt_raster.c [new file with mode: 0644]
raster/rt_core/rt_serialize.c [new file with mode: 0644]
raster/rt_core/rt_serialize.h [new file with mode: 0644]
raster/rt_core/rt_spatial_relationship.c [new file with mode: 0644]
raster/rt_core/rt_statistics.c [new file with mode: 0644]
raster/rt_core/rt_util.c [new file with mode: 0644]
raster/rt_core/rt_warp.c [new file with mode: 0644]
raster/rt_core/rt_wkb.c [new file with mode: 0644]
raster/rt_pg/Makefile.in
raster/rt_pg/rt_pg.c [deleted file]
raster/rt_pg/rtpg_band_properties.c [new file with mode: 0644]
raster/rt_pg/rtpg_create.c [new file with mode: 0644]
raster/rt_pg/rtpg_gdal.c [new file with mode: 0644]
raster/rt_pg/rtpg_geometry.c [new file with mode: 0644]
raster/rt_pg/rtpg_inout.c [new file with mode: 0644]
raster/rt_pg/rtpg_internal.c [new file with mode: 0644]
raster/rt_pg/rtpg_internal.h [new file with mode: 0644]
raster/rt_pg/rtpg_mapalgebra.c [new file with mode: 0644]
raster/rt_pg/rtpg_pixel.c [new file with mode: 0644]
raster/rt_pg/rtpg_raster_properties.c [new file with mode: 0644]
raster/rt_pg/rtpg_spatial_relationship.c [new file with mode: 0644]
raster/rt_pg/rtpg_statistics.c [new file with mode: 0644]
raster/rt_pg/rtpg_utility.c [new file with mode: 0644]
raster/rt_pg/rtpostgis.c [new file with mode: 0644]
raster/rt_pg/rtpostgis.h [moved from raster/rt_pg/rt_pg.h with 75% similarity]
raster/test/cunit/cu_tester.h

diff --git a/NEWS b/NEWS
index 5d9ea5bb65f3bf2c5ae2925ee73eb56008677332..cd08fd79a91d2da3a631eb9ac195841b8b0d5f94 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,9 @@ PostGIS 2.2.0
 
  * 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 *
index 4ba6186254be685bfdcc2a71ae66f68853801c52..ad51f8b233243ac95ae93c7507d111832a6832c6 100644 (file)
 #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)
 
index 9413432262971fd3de7460bf99246b1ce2f6c84c..f0f7206d699b5307294d478d3bf8cb11cd0da772 100644 (file)
@@ -8,22 +8,38 @@
 #
 #############################################################################
 
-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)
 
similarity index 99%
rename from raster/rt_core/rt_api.h
rename to raster/rt_core/librtcore.h
index 1e937e18f06995dfad56324b8d2a16b49feb6bb1..9dd4204b1faf835713ca060344ccbc5ca19889c5 100644 (file)
  *
  */
 
-#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
@@ -2414,4 +2422,4 @@ struct rt_colormap_t {
        rt_colormap_entry entry;
 };
 
-#endif /* RT_API_H_INCLUDED */
+#endif /* LIBRTCORE_H_INCLUDED */
diff --git a/raster/rt_core/librtcore_internal.h b/raster/rt_core/librtcore_internal.h
new file mode 100644 (file)
index 0000000..e7a2f08
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * $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 */
diff --git a/raster/rt_core/rt_api.c b/raster/rt_core/rt_api.c
deleted file mode 100644 (file)
index 80fe4cf..0000000
+++ /dev/null
@@ -1,15248 +0,0 @@
-/*
- * $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;
-}
diff --git a/raster/rt_core/rt_band.c b/raster/rt_core/rt_band.c
new file mode 100644 (file)
index 0000000..0765597
--- /dev/null
@@ -0,0 +1,1773 @@
+/*
+ * $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;
+}
+
diff --git a/raster/rt_core/rt_context.c b/raster/rt_core/rt_context.c
new file mode 100644 (file)
index 0000000..11862aa
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * $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);
+}
diff --git a/raster/rt_core/rt_geometry.c b/raster/rt_core/rt_geometry.c
new file mode 100644 (file)
index 0000000..4991a35
--- /dev/null
@@ -0,0 +1,1183 @@
+/*
+ * $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;
+}
+
diff --git a/raster/rt_core/rt_mapalgebra.c b/raster/rt_core/rt_mapalgebra.c
new file mode 100644 (file)
index 0000000..818f2bb
--- /dev/null
@@ -0,0 +1,1850 @@
+/*
+ * $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;
+}
diff --git a/raster/rt_core/rt_pixel.c b/raster/rt_core/rt_pixel.c
new file mode 100644 (file)
index 0000000..35d880d
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ * $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;
+}
diff --git a/raster/rt_core/rt_raster.c b/raster/rt_core/rt_raster.c
new file mode 100644 (file)
index 0000000..869137b
--- /dev/null
@@ -0,0 +1,3751 @@
+/*
+ * $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;
+}
diff --git a/raster/rt_core/rt_serialize.c b/raster/rt_core/rt_serialize.c
new file mode 100644 (file)
index 0000000..b4740af
--- /dev/null
@@ -0,0 +1,904 @@
+/*
+ * $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;
+}
diff --git a/raster/rt_core/rt_serialize.h b/raster/rt_core/rt_serialize.h
new file mode 100644 (file)
index 0000000..849c344
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * $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 */
diff --git a/raster/rt_core/rt_spatial_relationship.c b/raster/rt_core/rt_spatial_relationship.c
new file mode 100644 (file)
index 0000000..a07223b
--- /dev/null
@@ -0,0 +1,1301 @@
+/*
+ * $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;
+}
+
diff --git a/raster/rt_core/rt_statistics.c b/raster/rt_core/rt_statistics.c
new file mode 100644 (file)
index 0000000..2459672
--- /dev/null
@@ -0,0 +1,1853 @@
+/*
+ * $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;
+}
diff --git a/raster/rt_core/rt_util.c b/raster/rt_core/rt_util.c
new file mode 100644 (file)
index 0000000..a1c43ad
--- /dev/null
@@ -0,0 +1,680 @@
+/*
+ * $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;
+}
+
diff --git a/raster/rt_core/rt_warp.c b/raster/rt_core/rt_warp.c
new file mode 100644 (file)
index 0000000..f583c0d
--- /dev/null
@@ -0,0 +1,974 @@
+/*
+ * $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;
+}
+
diff --git a/raster/rt_core/rt_wkb.c b/raster/rt_core/rt_wkb.c
new file mode 100644 (file)
index 0000000..cf77c6d
--- /dev/null
@@ -0,0 +1,702 @@
+/*
+ * $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;
+}
index fcc77930bfae7bb4f3aa20408b2cb790f9c7c4df..4c6291fe2c01373dc1b689f72bf89c542f6c2deb 100644 (file)
@@ -24,7 +24,20 @@ SQLPP = @SQLPP@
 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)
 #
diff --git a/raster/rt_pg/rt_pg.c b/raster/rt_pg/rt_pg.c
deleted file mode 100644 (file)
index e885b0a..0000000
+++ /dev/null
@@ -1,19220 +0,0 @@
-/*
- * $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);
-}
-
diff --git a/raster/rt_pg/rtpg_band_properties.c b/raster/rt_pg/rtpg_band_properties.c
new file mode 100644 (file)
index 0000000..9a282c4
--- /dev/null
@@ -0,0 +1,726 @@
+/*
+ * $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);
+}
+
diff --git a/raster/rt_pg/rtpg_create.c b/raster/rt_pg/rtpg_create.c
new file mode 100644 (file)
index 0000000..b033b92
--- /dev/null
@@ -0,0 +1,1610 @@
+/*
+ * $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);
+}
diff --git a/raster/rt_pg/rtpg_gdal.c b/raster/rt_pg/rtpg_gdal.c
new file mode 100644 (file)
index 0000000..f964d50
--- /dev/null
@@ -0,0 +1,689 @@
+/*
+ * $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);
+}
+
diff --git a/raster/rt_pg/rtpg_geometry.c b/raster/rt_pg/rtpg_geometry.c
new file mode 100644 (file)
index 0000000..a7a6d61
--- /dev/null
@@ -0,0 +1,1239 @@
+/*
+ * $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);
+}
+
diff --git a/raster/rt_pg/rtpg_inout.c b/raster/rt_pg/rtpg_inout.c
new file mode 100644 (file)
index 0000000..ea25b13
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * $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);
+}
+
diff --git a/raster/rt_pg/rtpg_internal.c b/raster/rt_pg/rtpg_internal.c
new file mode 100644 (file)
index 0000000..17c7a5d
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ * $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;
+}
diff --git a/raster/rt_pg/rtpg_internal.h b/raster/rt_pg/rtpg_internal.h
new file mode 100644 (file)
index 0000000..5e08600
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * $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 */
diff --git a/raster/rt_pg/rtpg_mapalgebra.c b/raster/rt_pg/rtpg_mapalgebra.c
new file mode 100644 (file)
index 0000000..17b4a47
--- /dev/null
@@ -0,0 +1,7021 @@
+/*
+ * $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);
+}
diff --git a/raster/rt_pg/rtpg_pixel.c b/raster/rt_pg/rtpg_pixel.c
new file mode 100644 (file)
index 0000000..7b93ba2
--- /dev/null
@@ -0,0 +1,2278 @@
+/*
+ * $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);
+}
+
diff --git a/raster/rt_pg/rtpg_raster_properties.c b/raster/rt_pg/rtpg_raster_properties.c
new file mode 100644 (file)
index 0000000..368b040
--- /dev/null
@@ -0,0 +1,1172 @@
+/*
+ * $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);
+}
diff --git a/raster/rt_pg/rtpg_spatial_relationship.c b/raster/rt_pg/rtpg_spatial_relationship.c
new file mode 100644 (file)
index 0000000..85053a0
--- /dev/null
@@ -0,0 +1,1315 @@
+/*
+ * $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);
+}
diff --git a/raster/rt_pg/rtpg_statistics.c b/raster/rt_pg/rtpg_statistics.c
new file mode 100644 (file)
index 0000000..453c26a
--- /dev/null
@@ -0,0 +1,2617 @@
+/*
+ * $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);
+       }
+}
+
diff --git a/raster/rt_pg/rtpg_utility.c b/raster/rt_pg/rtpg_utility.c
new file mode 100644 (file)
index 0000000..4f4b7c9
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * $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);
+}
+
diff --git a/raster/rt_pg/rtpostgis.c b/raster/rt_pg/rtpostgis.c
new file mode 100644 (file)
index 0000000..3631859
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * $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);
+}
similarity index 75%
rename from raster/rt_pg/rt_pg.h
rename to raster/rt_pg/rtpostgis.h
index 3137513b65d582baed712ae7efa89e2441e65270..2c885de537a9c7701739fa17e45220f8f5976733 100644 (file)
  *
  */
 
-#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 */
index 26d57242153cbc81ca277e11018dd75d0fae0d02..ab4b4ec9ed3b54c08cb14cddd620cbf03b25d8d4 100644 (file)
@@ -9,7 +9,10 @@
  *
  **********************************************************************/
 
-#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