]> granicus.if.org Git - postgis/commitdiff
Add ST_AsGDALRaster function and helper functions ST_GDALDrivers and ST_srtext
authorBborie Park <bkpark at ucdavis.edu>
Mon, 16 May 2011 19:48:20 +0000 (19:48 +0000)
committerBborie Park <bkpark at ucdavis.edu>
Mon, 16 May 2011 19:48:20 +0000 (19:48 +0000)
- added rt_raster_to_gdal, rt_raster_gdal_drivers and rt_raster_to_gdal_mem functions to rt_core/rt_api.c and rt_api.h
- added test cases to test/core/testapi.c
- added RASTER_asGDALRaster and RASTER_getGDALDrivers to rt_pg/rt_pg.c
- added SQL functions
- added regression tests

Associated ticket is #901

git-svn-id: http://svn.osgeo.org/postgis/trunk@7155 b70326c6-7e19-0410-871a-916f4a2858ee

raster/rt_core/rt_api.c
raster/rt_core/rt_api.h
raster/rt_pg/rt_pg.c
raster/rt_pg/rtpostgis.sql.in.c
raster/scripts/python/rtgdalraster.py [new file with mode: 0644]
raster/test/core/testapi.c
raster/test/regress/Makefile.in
raster/test/regress/rt_asgdalraster.sql [new file with mode: 0644]
raster/test/regress/rt_asgdalraster_expected [new file with mode: 0644]

index 1c7776a28ab36dc1157d2d61c4a5dc8d8296cb4a..cd3f9c9a06d0b5daebee5ea024d12588b3e2caec 100644 (file)
@@ -29,7 +29,7 @@
 #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 */
+#include <string.h> /* for memcpy and strlen */
 #include <assert.h>
 #include <float.h> /* for FLT_EPSILON and float type limits */
 #include <limits.h> /* for integer type limits */
@@ -4948,3 +4948,389 @@ rt_raster_replace_band(rt_raster raster, rt_band band, int index) {
        return 1;
 }
 
+/**
+ * Return formatted GDAL raster from raster
+ *
+ * @param raster : the raster to convert
+ * @param srs : the raster's coordinate system in OGC WKT or PROJ.4
+ * @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
+ */
+uint8_t*
+rt_raster_to_gdal(rt_raster raster, 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);
+
+       /* any supported format is possible */
+       GDALAllRegister();
+       RASTER_DEBUG(3, "rt_raster_to_gdal: loaded all supported GDAL formats");
+
+       /* output format not specified */
+       if (format == NULL || !strlen(format))
+               format = "GTiff";
+       RASTER_DEBUGF(3, "rt_raster_to_gdal: output format is %s", format);
+
+       /* load raster into a GDAL MEM raster */
+       src_ds = rt_raster_to_gdal_mem(raster, srs, &src_drv);
+       if (NULL == src_ds) {
+               rterror("rt_raster_to_gdal: Unable to convert raster to GDAL MEM format\n");
+               if (NULL != src_drv) {
+                       GDALDeregisterDriver(src_drv);
+                       GDALDestroyDriver(src_drv);
+               }
+               return 0;
+       }
+
+       /* load driver */
+       rtn_drv = GDALGetDriverByName(format);
+       if (NULL == rtn_drv) {
+               rterror("rt_raster_to_gdal: Unable to load the output GDAL driver\n");
+               GDALClose(src_ds);
+               GDALDeregisterDriver(src_drv);
+               GDALDestroyDriver(src_drv);
+               return 0;
+       }
+       RASTER_DEBUG(3, "rt_raster_to_gdal: Output driver loaded");
+
+       /* convert GDAL MEM raster to output format */
+       RASTER_DEBUG(3, "rt_raster_to_gdal: 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: Unable to create the output GDAL dataset\n");
+               GDALClose(src_ds);
+               GDALDeregisterDriver(rtn_drv);
+               GDALDestroyDriver(rtn_drv);
+               GDALDeregisterDriver(src_drv);
+               GDALDestroyDriver(src_drv);
+               return 0;
+       }
+
+       /* close source dataset */
+       GDALClose(src_ds);
+       GDALDeregisterDriver(src_drv);
+       GDALDestroyDriver(src_drv);
+       RASTER_DEBUG(3, "rt_raster_to_gdal: Closed GDAL MEM raster");
+
+       /* close dataset, this also flushes any pending writes */
+       GDALClose(rtn_ds);
+       GDALDeregisterDriver(rtn_drv);
+       GDALDestroyDriver(rtn_drv);
+       RASTER_DEBUG(3, "rt_raster_to_gdal: Closed GDAL output raster");
+
+       RASTER_DEBUG(3, "rt_raster_to_gdal: Done copying GDAL MEM raster to memory file in output format");
+
+       /* from memory file to buffer */
+       RASTER_DEBUG(3, "rt_raster_to_gdal: Copying GDAL memory file to buffer");
+       rtn = VSIGetMemFileBuffer("/vsimem/out.dat", &rtn_lenvsi, TRUE);
+       RASTER_DEBUG(3, "rt_raster_to_gdal: Done copying GDAL memory file to buffer");
+       if (NULL == rtn) {
+               rterror("rt_raster_to_gdal: Unable to create the output GDAL raster\n");
+               return 0;
+       }
+
+       rtn_len = (uint64_t) rtn_lenvsi;
+       *gdalsize = rtn_len;
+
+       return rtn;
+}
+
+struct rt_gdaldriver_t {
+    int idx;
+    char *short_name;
+    char *long_name;
+               char *create_options;
+};
+
+/**
+ * Returns a set of available GDAL drivers
+ *
+ * @param drv_count : number of GDAL drivers available
+ *
+ * @return set of "gdaldriver" values of available GDAL drivers
+ */
+rt_gdaldriver
+rt_raster_gdal_drivers(uint32_t *drv_count) {
+       const char *state;
+       const char *txt;
+       int txt_len;
+       GDALDriverH *drv = NULL;
+       rt_gdaldriver rtn = NULL;
+       int count;
+       int i;
+       uint32_t j;
+
+       GDALAllRegister();
+       count = GDALGetDriverCount();
+       rtn = (rt_gdaldriver) rtalloc(count * sizeof(struct rt_gdaldriver_t));
+       if (NULL == rtn) {
+               rterror("rt_raster_gdal_drivers: Unable to allocate memory for gdaldriver structure");
+               return 0;
+       }
+
+       for (i = 0, j = 0; i < count; i++) {
+               drv = GDALGetDriver(i);
+
+               /* 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);
+
+               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 */
+       rtrealloc(rtn, j * sizeof(struct rt_gdaldriver_t));
+       *drv_count = j;
+
+       return rtn;
+}
+
+/**
+ * Return GDAL datasource using GDAL MEM driver from raster
+ *
+ * @param raster : raster to convert to GDAL MEM
+ * @param srs : the raster's coordinate system in OGC WKT or PROJ.4
+ * @param rtn_drv : is set to the GDAL driver object
+ *
+ * @return GDAL datasource using GDAL MEM driver
+ */
+GDALDatasetH
+rt_raster_to_gdal_mem(rt_raster raster, char *srs,
+       GDALDriverH *rtn_drv) {
+       GDALDriverH drv = NULL;
+       int drv_gen = 0;
+       GDALDatasetH ds = NULL;
+       double gt[] = {0.0, 1.0, 0.0, 0.0, 0.0, 1.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 i;
+       int numBands;
+       rt_band rtband = NULL;
+       rt_pixtype pt = PT_END;
+
+       assert(NULL != raster);
+
+       /* store raster in GDAL MEM raster */
+       GDALRegister_MEM();
+       if (NULL == *rtn_drv) {
+               drv = GDALGetDriverByName("MEM");
+               *rtn_drv = drv;
+               drv_gen = 1;
+               if (NULL == drv) {
+                       rterror("rt_raster_to_gdal_mem: Unable to load the MEM GDAL driver\n");
+                       return 0;
+               }
+       }
+
+       ds = GDALCreate(drv, "", rt_raster_get_width(raster),
+               rt_raster_get_height(raster), 0, GDT_Byte, NULL);
+       if (NULL == ds) {
+               rterror("rt_raster_to_gdal_mem: Couldn't create a GDALDataset to convert it\n");
+               if (drv_gen) {
+                       GDALDeregisterDriver(drv);
+                       GDALDestroyDriver(drv);
+               }
+               return 0;
+       }
+
+       /* add geotransform */
+       gt[0] = rt_raster_get_x_offset(raster);
+       gt[1] = rt_raster_get_x_scale(raster);
+       gt[2] = rt_raster_get_x_skew(raster);
+       gt[3] = rt_raster_get_y_offset(raster);
+       gt[4] = rt_raster_get_y_skew(raster);
+       gt[5] = rt_raster_get_y_scale(raster);
+       cplerr = GDALSetGeoTransform(ds, gt);
+       if (cplerr != CE_None) {
+               rterror("rt_raster_to_gdal_mem: Unable to set geotransformation\n");
+               GDALClose(ds);
+               if (drv_gen) {
+                       GDALDeregisterDriver(drv);
+                       GDALDestroyDriver(drv);
+               }
+               return 0;
+       }
+
+       /* set spatial reference */
+       if (NULL != srs && strlen(srs)) {
+               cplerr = GDALSetProjection(ds, srs);
+               if (cplerr != CE_None) {
+                       rterror("rt_raster_to_gdal_mem: Unable to set projection\n");
+                       GDALClose(ds);
+                       if (drv_gen) {
+                               GDALDeregisterDriver(drv);
+                               GDALDestroyDriver(drv);
+                       }
+                       return 0;
+               }
+       }
+       
+       /* add band(s) */
+       numBands = rt_raster_get_num_bands(raster);
+       for (i = 0; i < numBands; i++) {
+               rtband = rt_raster_get_band(raster, i);
+               if (0 == rtband) {
+                       rterror("rt_raster_to_gdal_mem: Unable to get requested band\n");
+                       GDALClose(ds);
+                       if (drv_gen) {
+                               GDALDeregisterDriver(drv);
+                               GDALDestroyDriver(drv);
+                       }
+                       return 0;
+               }
+
+               pt = rt_band_get_pixtype(rtband);
+               switch (pt) {
+                       case PT_1BB: case PT_2BUI: case PT_4BUI: case PT_8BSI: case PT_8BUI:
+                               gdal_pt = GDT_Byte;
+                               break;
+                       case PT_16BSI: case PT_16BUI:
+                               if (pt == PT_16BSI)
+                                       gdal_pt = GDT_Int16;
+                               else
+                                       gdal_pt = GDT_UInt16;
+                               break;
+                       case PT_32BSI: case PT_32BUI: case PT_32BF:
+                               if (pt == PT_32BSI)
+                                       gdal_pt = GDT_Int32;
+                               else if (pt == PT_32BUI)
+                                       gdal_pt = GDT_UInt32;
+                               else
+                                       gdal_pt = GDT_Float32;
+                               break;
+                       case PT_64BF:
+                               gdal_pt = GDT_Float64;
+                               break;
+                       default:
+                               rtwarn("rt_raster_to_gdal_mem: Unknown pixel type for band\n");
+                               gdal_pt = GDT_Unknown;
+                               break;
+               }
+
+               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);
+
+               if (GDALAddBand(ds, gdal_pt, apszOptions) == CE_Failure) {
+                       rterror("rt_raster_to_gdal_mem: Couldn't add GDALRasterBand\n");
+                       GDALClose(ds);
+                       if (drv_gen) {
+                               GDALDeregisterDriver(drv);
+                               GDALDestroyDriver(drv);
+                       }
+                       return 0;
+               }
+
+               /* check band count */
+               if (GDALGetRasterCount(ds) != i + 1) {
+                       rterror("rt_raster_to_gdal_mem: Error creating GDAL MEM raster band\n");
+                       GDALClose(ds);
+                       if (drv_gen) {
+                               GDALDeregisterDriver(drv);
+                               GDALDestroyDriver(drv);
+                       }
+                       return 0;
+               }
+
+               /* get band to write data to */
+               band = NULL;
+               band = GDALGetRasterBand(ds, i + 1);
+               if (NULL == band) {
+                       rterror("rt_raster_to_gdal_mem: Couldn't get GDAL band for additional processing\n");
+                       GDALClose(ds);
+                       if (drv_gen) {
+                               GDALDeregisterDriver(drv);
+                               GDALDestroyDriver(drv);
+                       }
+                       return 0;
+               }
+
+               /* Add nodata value for band */
+               if (rt_band_get_hasnodata_flag(rtband) != FALSE) {
+                       nodata = rt_band_get_nodata(rtband);
+                       if (GDALSetRasterNoDataValue(band, nodata) != CE_None)
+                               rtwarn("rt_raster_to_gdal_mem: Couldn't set nodata value for band\n");
+               }
+       }
+
+       return ds;
+}
index bd87d6b3b4a4499a752bbc8ec70e98e8472438af..967e6c7ae30150a7c980ef45d46df9f472371f16 100644 (file)
@@ -91,6 +91,8 @@
 #include "gdal_frmts.h"
 #include "gdal.h"
 #include "ogr_api.h"
+#include "ogr_srs_api.h"
+#include "cpl_vsi.h"
 #include "../../postgis_config.h"
 
 /**
@@ -845,6 +847,41 @@ rt_raster rt_raster_from_band(rt_raster raster, uint32_t *bandNums,
 int rt_raster_replace_band(rt_raster raster, rt_band band,
        int index);
 
+/**
+ * Return formatted GDAL raster from raster
+ *
+ * @param raster : the raster to convert
+ * @param srs : the raster's coordinate system in OGC WKT or PROJ.4
+ * @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
+ */
+uint8_t *rt_raster_to_gdal(rt_raster raster, char *srs,
+       char *format, char **options, uint64_t *gdalsize);
+
+/**
+ * Returns a set of available GDAL drivers
+ *
+ * @param drv_count : number of GDAL drivers available
+ *
+ * @return set of "gdaldriver" values of available GDAL drivers
+ */
+typedef struct rt_gdaldriver_t* rt_gdaldriver;
+rt_gdaldriver rt_raster_gdal_drivers(uint32_t *drv_count);
+
+/**
+ * Return GDAL datasource using GDAL MEM driver from raster
+ *
+ * @param raster : raster to convert to GDAL MEM
+ * @param srs : the raster's coordinate system in OGC WKT or PROJ.4
+ * @param rtn_drv : is set to the GDAL driver object
+ *
+ * @return GDAL datasource using GDAL MEM driver
+ */
+GDALDatasetH rt_raster_to_gdal_mem(rt_raster raster, char *srs,
+       GDALDriverH *rtn_drv);
 
 /*- utilities -------------------------------------------------------*/
 
index 22008948b13f185e82b0691c7e74bd8082729dd9..df016bf025a01d5d6d73712b4e134f6ce972c6a4 100644 (file)
@@ -72,6 +72,7 @@ static char *strtoupper(char *str);
 static char    *chartrim(char* input, char *remove); /* for RASTER_reclass */
 static char **strsplit(const char *str, const char *delimiter, int *n); /* for RASTER_reclass */
 static char *removespaces(char *str); /* for RASTER_reclass */
+static char *trim(char* input); /* for RASTER_asGDALRaster */
 
 /***************************************************************
  * Some rules for returning NOTICE or ERROR...
@@ -205,6 +206,9 @@ Datum RASTER_quantile(PG_FUNCTION_ARGS);
 /* reclassify specified bands of a raster */
 Datum RASTER_reclass(PG_FUNCTION_ARGS);
 
+/* convert raster to GDAL raster */
+Datum RASTER_asGDALRaster(PG_FUNCTION_ARGS);
+Datum RASTER_getGDALDrivers(PG_FUNCTION_ARGS);
 
 /* Replace function taken from
  * http://ubuntuforums.org/showthread.php?s=aa6f015109fd7e4c7e30d2fd8b717497&t=141670&page=3
@@ -387,6 +391,38 @@ removespaces(char *str) {
        return rtn;
 }
 
+static char*
+trim(char *input) {
+       char *start;
+       char *ptr;
+
+       if (!input) {
+               return NULL;
+       }
+       else if (!*input) {
+               return input;
+       }
+
+       start = (char *) palloc(sizeof(char) * strlen(input) + 1);
+       if (NULL == start) {
+               fprintf(stderr, "Not enough memory\n");
+               return NULL;
+       }
+       strcpy(start, input);
+
+       /* trim left */
+       while (isspace(*start)) {
+               start++;
+       }
+
+       /* trim right */
+       ptr = start + strlen(start);
+       while (isspace(*--ptr));
+       *(++ptr) = '\0';
+
+       return start;
+}
+
 PG_FUNCTION_INFO_V1(RASTER_lib_version);
 Datum RASTER_lib_version(PG_FUNCTION_ARGS)
 {
@@ -4117,6 +4153,329 @@ Datum RASTER_reclass(PG_FUNCTION_ARGS) {
        PG_RETURN_POINTER(pgrast);
 }
 
+/**
+ * Returns formatted GDAL raster as bytea object of raster
+ */
+PG_FUNCTION_INFO_V1(RASTER_asGDALRaster);
+Datum RASTER_asGDALRaster(PG_FUNCTION_ARGS)
+{
+       rt_pgraster *pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       rt_raster raster;
+
+       text *formattext = NULL;
+       char *format = NULL;
+       char **options = NULL;
+       text *optiontext = NULL;
+       char *option = NULL;
+       text *srstext = NULL;
+       char *srs = NULL;
+
+       ArrayType *array;
+       Oid etype;
+       Datum *e;
+       bool *nulls;
+       int16 typlen;
+       bool typbyval;
+       char typalign;
+       int ndims = 1;
+       int *dims;
+       int *lbs;
+       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");
+
+       raster = rt_raster_deserialize(pgraster);
+       if (!raster) {
+               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_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:
+                               elog(ERROR, "RASTER_asGDALRaster: Invalid data type for options");
+                               PG_RETURN_NULL();
+                               break;
+               }
+
+               ndims = ARR_NDIM(array);
+               dims = ARR_DIMS(array);
+               lbs = ARR_LBOUND(array);
+
+               deconstruct_array(array, etype, typlen, typbyval, typalign, &e,
+                       &nulls, &n);
+
+               if (n) {
+                       options = (char **) palloc(sizeof(char *) * (n + 1));
+                       /* 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 = 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) {
+                               /* add NULL to end */
+                               options[j] = (char *) palloc(sizeof(char));
+                               options[j] = '\0';
+
+                               /* trim allocation */
+                               options = repalloc(options, j * sizeof(char *));
+                       }
+                       else {
+                               pfree(options);
+                               options = NULL;
+                       }
+               }
+       }
+
+       /* process srs */
+       if (!PG_ARGISNULL(3)) {
+               srstext = PG_GETARG_TEXT_P(3);
+               srs = text_to_cstring(srstext);
+               POSTGIS_RT_DEBUGF(3, "RASTER_asGDALRaster: Arg 3 (srs) is %s", srs);
+       }
+
+       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);
+       }
+       rt_raster_destroy(raster);
+
+       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_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);
+       */
+
+       POSTGIS_RT_DEBUG(3, "RASTER_asGDALRaster: Returning pointer to GDAL raster");
+       PG_RETURN_POINTER(result);
+}
+
+/**
+ * Needed for sizeof
+ */
+struct rt_gdaldriver_t {
+    int idx;
+    char *short_name;
+    char *long_name;
+               char *create_options;
+};
+
+/**
+ * Returns available GDAL drivers
+ */
+PG_FUNCTION_INFO_V1(RASTER_getGDALDrivers);
+Datum RASTER_getGDALDrivers(PG_FUNCTION_ARGS)
+{
+       FuncCallContext *funcctx;
+       TupleDesc tupdesc;
+       AttInMetadata *attinmeta;
+
+       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);
+               if (NULL == drv_set || !drv_count) {
+                       elog(NOTICE, "No GDAL drivers found");
+                       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"
+                               )
+                       ));
+               }
+
+               /*
+                * generate attribute metadata needed later to produce tuples from raw
+                * C strings
+                */
+               attinmeta = TupleDescGetAttInMetadata(tupdesc);
+               funcctx->attinmeta = attinmeta;
+               MemoryContextSwitchTo(oldcontext);
+       }
+
+  /* stuff done on every call of the function */
+  funcctx = SRF_PERCALL_SETUP();
+
+       call_cntr = funcctx->call_cntr;
+       max_calls = funcctx->max_calls;
+       attinmeta = funcctx->attinmeta;
+       drv_set2 = funcctx->user_fctx;
+
+       /* do when there is more left to send */
+       if (call_cntr < max_calls) {
+               char **values;
+               HeapTuple tuple;
+               Datum result;
+
+               POSTGIS_RT_DEBUGF(3, "Result %d", call_cntr);
+
+               /*
+                * Prepare a values array for building the returned tuple.
+                * This should be an array of C strings which will
+                * be processed later by the type input functions.
+                */
+               values = (char **) palloc(4 * sizeof(char *));
+
+               values[0] = (char *) palloc(
+                       (snprintf(NULL, 0, "%d", drv_set2[call_cntr].idx) + 1) * sizeof(char)
+               );
+               values[1] = (char *) palloc(
+                       (strlen(drv_set2[call_cntr].short_name) + 1) * sizeof(char)
+               );
+               values[2] = (char *) palloc(
+                       (strlen(drv_set2[call_cntr].long_name) + 1) * sizeof(char)
+               );
+               values[3] = (char *) palloc(
+                       (strlen(drv_set2[call_cntr].create_options) + 1) * sizeof(char)
+               );
+
+               snprintf(
+                       values[0],
+                       (snprintf(NULL, 0, "%d", drv_set2[call_cntr].idx) + 1) * sizeof(char),
+                       "%d",
+                       drv_set2[call_cntr].idx
+               );
+               snprintf(
+                       values[1],
+                       (strlen(drv_set2[call_cntr].short_name) + 1) * sizeof(char),
+                       "%s",
+                       drv_set2[call_cntr].short_name
+               );
+               snprintf(
+                       values[2],
+                       (strlen(drv_set2[call_cntr].long_name) + 1) * sizeof(char),
+                       "%s",
+                       drv_set2[call_cntr].long_name
+               );
+               snprintf(
+                       values[3],
+                       (strlen(drv_set2[call_cntr].create_options) + 1) * sizeof(char),
+                       "%s",
+                       drv_set2[call_cntr].create_options
+               );
+
+               POSTGIS_RT_DEBUGF(4, "Result %d, Index %s", call_cntr, values[0]);
+               POSTGIS_RT_DEBUGF(4, "Result %d, Short Name %s", call_cntr, values[1]);
+               POSTGIS_RT_DEBUGF(4, "Result %d, Full Name %s", call_cntr, values[2]);
+               POSTGIS_RT_DEBUGF(5, "Result %d, Create Options %s", call_cntr, values[3]);
+
+               /* build a tuple */
+               tuple = BuildTupleFromCStrings(attinmeta, values);
+
+               /* make the tuple into a datum */
+               result = HeapTupleGetDatum(tuple);
+
+               /* clean up (this is not really necessary) */
+               pfree(values[3]);
+               pfree(values[2]);
+               pfree(values[1]);
+               pfree(values[0]);
+               pfree(values);
+
+               SRF_RETURN_NEXT(funcctx, result);
+       }
+       /* do when there is no more left */
+       else {
+               pfree(drv_set2);
+               SRF_RETURN_DONE(funcctx);
+       }
+}
+
 /* ---------------------------------------------------------------- */
 /*  Memory allocation / error reporting hooks                       */
 /* ---------------------------------------------------------------- */
index e7123308548d07b97f6005b217f567b46f98123a..35cf6373de772ca21c11db17b781f80023f9c1e4 100644 (file)
@@ -1022,6 +1022,65 @@ CREATE OR REPLACE FUNCTION st_reclass(rast raster, reclassexpr text, pixeltype t
        AS $$ SELECT st_reclass($1, ROW(1, $2, $3, NULL)) $$
        LANGUAGE 'SQL' IMMUTABLE STRICT;
 
+-----------------------------------------------------------------------
+-- ST_AsGDALRaster and supporting functions
+-----------------------------------------------------------------------
+-- returns set of available and usable GDAL drivers
+CREATE OR REPLACE FUNCTION st_gdaldrivers(OUT idx int, OUT short_name text, OUT long_name text, OUT create_options text)
+  RETURNS SETOF record
+       AS 'MODULE_PATHNAME', 'RASTER_getGDALDrivers'
+       LANGUAGE 'C' IMMUTABLE STRICT;
+
+-- return the srtext or proj4text of an srid
+CREATE OR REPLACE FUNCTION st_srtext(q raster)
+       RETURNS text
+       AS $$
+       DECLARE
+               q_srid int;
+               q_srs text;
+       BEGIN
+               q_srid := st_srid($1);
+
+               IF q_srid != -1 THEN
+                       SELECT
+                               CASE
+                                       WHEN length(srtext) > 0
+                                               THEN srtext
+                                       WHEN length(proj4text) > 0
+                                               THEN proj4text
+                                       ELSE NULL
+                               END
+                       INTO q_srs
+                       FROM spatial_ref_sys
+                       WHERE srid = q_srid;
+
+                       IF NOT FOUND THEN
+                               q_srs := NULL;
+                       END IF;
+               ELSE
+                       q_srs := NULL;
+               END IF;
+
+               RETURN q_srs;
+       END;
+       $$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+-- Cannot be strict as "options" and "srs" can be NULL
+CREATE OR REPLACE FUNCTION st_asgdalraster(rast raster, format text, options text[], srs text)
+       RETURNS bytea
+       AS 'MODULE_PATHNAME', 'RASTER_asGDALRaster'
+       LANGUAGE 'C' IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION st_asgdalraster(rast raster, format text, options text[])
+       RETURNS bytea
+       AS $$ SELECT st_asgdalraster($1, $2, $3, st_srtext($1)) $$
+       LANGUAGE 'SQL' IMMUTABLE STRICT;
+
+CREATE OR REPLACE FUNCTION st_asgdalraster(rast raster, format text)
+       RETURNS bytea
+       AS $$ SELECT st_asgdalraster($1, $2, NULL, st_srtext($1)) $$
+       LANGUAGE 'SQL' IMMUTABLE STRICT;
+
 -----------------------------------------------------------------------
 -- MapAlgebra
 -----------------------------------------------------------------------
diff --git a/raster/scripts/python/rtgdalraster.py b/raster/scripts/python/rtgdalraster.py
new file mode 100644 (file)
index 0000000..6b4552a
--- /dev/null
@@ -0,0 +1,98 @@
+#! /usr/bin/env python
+#
+# $Id$
+#
+# A simple utility for passing parameters to ST_AsGDALRaster
+# for a GDAL raster output
+# This utility is handy for debugging purposes.
+#
+# Example:
+#   rtgdalraster.py -d "dbname=postgres" -r "(SELECT rast FROM pele LIMIT 1)" -f JPEG -o pele.jpg
+#
+#
+###############################################################################
+# Copyright (C) 2009 Mateusz Loskot <mateusz@loskot.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
+#
+###############################################################################
+from optparse import OptionParser
+import sys
+import os
+import psycopg2
+
+def logit(msg):
+       if VERBOSE is True:
+               sys.stderr.write("LOG - " + str(msg) + "\n")
+
+###############################################################################
+try:
+       prs = OptionParser(version="%prog",
+               usage="%prog -d <DB> -r <RASTER> -o <FILE> [-f <FORMAT>] [-c <OPTIONS>]",
+               description="Output raster in a GDAL raster format")
+       prs.add_option("-d", "--db", dest="db", action="store", default=None,
+               help="PostgreSQL database connection string, required")
+       prs.add_option("-r", "--raster", dest="raster", action="store", default=None,
+               help="sql expression to create or access a existing raster, required")
+       prs.add_option("-o", "--output", dest="output", action="store", default=None,
+               help="file to put the output of ST_AsGDALRaster if successful, required")
+       prs.add_option("-f", "--format", dest="format", action="store", default="GTiff",
+               help="format of GDAL raster output, optional, default=GTiff")
+       prs.add_option("-c", "--config", dest="config", action="store", default=None,
+               help="comma separated list of GDAL raster creation options, optional")
+       prs.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False,
+               help="be excessively verbose and useful for debugging")
+
+       (opts, args) = prs.parse_args()
+
+       if opts.db is None:
+               prs.error("use -d option to specify database connection string")
+       if opts.raster is None:
+               prs.error("use -r option to specify a sql expression for a raster")
+       if opts.output is None:
+               prs.error("use -o option to specify raster output file")
+
+       if opts.config is not None:
+               opts.cfg = opts.config.split(',')
+       else:
+               opts.cfg = None
+
+       global VERBOSE
+       VERBOSE = opts.verbose
+
+       conn = psycopg2.connect(opts.db)
+       logit("Connected to %s" % opts.db)
+       logit("Raster expression is %s" % opts.raster)
+
+       cur = conn.cursor()
+       sql = "SELECT ST_AsGDALRaster(%s, %%s, %%s::text[], NULL::text)" % (opts.raster)
+       cur.execute(sql, (opts.format, opts.cfg))
+       logit("Number of rows: %i" % cur.rowcount)
+       rec = cur.fetchone()[0];
+       logit("size of raster (bytes): %i" % len(rec))
+
+       fh = open(opts.output, 'wb', -1)
+       fh.write(rec);
+       fh.flush();
+       fh.close();
+       logit("size of %s (bytes): %i" % (opts.output, os.stat(opts.output)[6]));
+
+       cur.close();
+       conn.close();
+
+       print "raster outputted to %s" % opts.output;
+
+except Exception, e:
+    print "ERROR: ", e
index a1cc396a78fb1fac448273fb5bafed34aa6a2e69..ef4e912b15434120530f47598aaef805a1d5a247 100644 (file)
@@ -1261,6 +1261,51 @@ static void testBandReclass() {
        rt_raster_destroy(raster);
 }
 
+struct rt_gdaldriver_t {
+       int idx;
+       char *short_name;
+       char *long_name;
+       char *create_options;
+};
+static void testGDALDrivers() {
+       int i;
+       uint32_t size;
+       rt_gdaldriver drv;
+
+       drv = (rt_gdaldriver) rt_raster_gdal_drivers(&size);
+       /*printf("size: %d\n", size);*/
+       CHECK(size);
+
+       for (i = 0; i < size; i++) {
+               /*printf("gdal_driver: %s\n", drv[i].short_name);*/
+               CHECK(drv[i].short_name);
+       }
+}
+
+static void testRasterToGDAL(rt_raster raster) {
+       uint32_t bandNums[] = {1};
+       int lenBandNums = 1;
+       uint64_t gdalSize;
+       rt_raster rast;
+       uint8_t *rtn = NULL;
+
+       rast = rt_raster_from_band(raster, bandNums, lenBandNums);
+       assert(rast);
+
+       rtn = rt_raster_to_gdal(rast, NULL, "GTiff", NULL, &gdalSize);
+       /*printf("gdalSize: %d\n", (int) gdalSize);*/
+       CHECK(gdalSize);
+
+       /*
+       FILE *fh = NULL;
+       fh = fopen("/tmp/out.tif", "w");
+       fwrite(rtn, sizeof(uint8_t), gdalSize, fh);
+       fclose(fh);
+       */
+
+       rt_raster_destroy(rast);
+}
+
 int
 main()
 {
@@ -1583,6 +1628,13 @@ main()
                testBandReclass();
                printf("Successfully tested rt_band_reclass\n");
 
+               printf("Testing rt_raster_to_gdal\n");
+               testRasterToGDAL(raster);
+               printf("Successfully tested rt_raster_to_gdal\n");
+
+               printf("Testing rt_raster_gdal_drivers\n");
+               testGDALDrivers();
+               printf("Successfully tested rt_raster_gdal_drivers\n");
 
     deepRelease(raster);
 
index 9f594651fbd8c3d916b1ebeadf3fe19a3ca2cabd..dcfd840c5a21bd63933704182d5256471c558487 100644 (file)
@@ -42,6 +42,7 @@ TEST_FUNC = \
        rt_box2d.sql \
        rt_addband.sql \
        rt_band.sql \
+       rt_asgdalraster.sql \
        $(NULL)
 
 TEST_PROPS = \
diff --git a/raster/test/regress/rt_asgdalraster.sql b/raster/test/regress/rt_asgdalraster.sql
new file mode 100644 (file)
index 0000000..46eef9d
--- /dev/null
@@ -0,0 +1,147 @@
+SELECT md5(
+       ST_AsGDALRaster(
+               ST_AddBand(ST_MakeEmptyRaster(200, 200, 10, 10, 2, 2, 0, 0, -1), 1, '64BF', 123.4567, NULL),
+               'GTiff'
+       )
+);
+SELECT md5(
+       ST_AsGDALRaster(
+               ST_AddBand(
+                       ST_AddBand(
+                               ST_AddBand(
+                                       ST_MakeEmptyRaster(200, 200, 10, 10, 2, 2, 0, 0,-1)
+                                       , 1, '64BF', 1234.5678, NULL
+                               )
+                               , '64BF', 987.654321, NULL
+                       )
+                       , '64BF', 9876.54321, NULL
+               ),
+               'GTiff'
+       )
+);
+SELECT md5(
+       ST_AsGDALRaster(
+               ST_AddBand(
+                       ST_AddBand(
+                               ST_AddBand(
+                                       ST_MakeEmptyRaster(200, 200, 10, 10, 2, 2, 0, 0, -1)
+                                       , 1, '64BF', 1234.5678, -9999
+                               )
+                               , '64BF', 987.654321, -9999
+                       )
+                       , '64BF', 9876.54321, -9999
+               ),
+               'GTiff'
+       )
+);
+SELECT md5(
+       ST_AsGDALRaster(
+               ST_AddBand(ST_MakeEmptyRaster(200, 200, 10, 10, 2, 2, 0, 0, -1), 1, '8BSI', 123, NULL),
+               'PNG'
+       )
+);
+SELECT md5(
+       ST_AsGDALRaster(
+               ST_AddBand(ST_MakeEmptyRaster(200, 200, 10, 10, 2, 2, 0, 0, -1), 1, '8BUI', 123, NULL),
+               'PNG'
+       )
+);
+SELECT md5(
+       ST_AsGDALRaster(
+               ST_AddBand(ST_MakeEmptyRaster(200, 200, 10, 10, 2, 2, 0, 0, -1), 1, '8BSI', -123, NULL),
+               'PNG'
+       )
+);
+SELECT md5(
+       ST_AsGDALRaster(
+               ST_AddBand(ST_MakeEmptyRaster(200, 200, 10, 10, 2, 2, 0, 0, -1), 1, '8BUI', 254, NULL),
+               'PNG'
+       )
+);
+SELECT md5(
+       ST_AsGDALRaster(
+               ST_AddBand(
+                       ST_AddBand(
+                               ST_AddBand(
+                                       ST_MakeEmptyRaster(200, 200, 10, 10, 2, 2, 0, 0, -1)
+                                       , 1, '8BSI', 1, -1
+                               )
+                               , 2, '8BSI', 11, -1
+                       )
+                       , 3, '8BSI', 111, -1
+               ),
+               'PNG',
+               ARRAY['ZLEVEL=1']
+       )
+);
+SELECT md5(
+       ST_AsGDALRaster(
+               ST_AddBand(
+                       ST_AddBand(
+                               ST_AddBand(
+                                       ST_MakeEmptyRaster(200, 200, 10, 10, 2, 2, 0, 0, -1)
+                                       , 1, '8BSI', 1, -1
+                               )
+                               , 2, '8BSI', 11, -1
+                       )
+                       , 3, '8BSI', 111, -1
+               ),
+               'PNG',
+               ARRAY['ZLEVEL=9']
+       )
+);
+SELECT md5(
+       ST_AsGDALRaster(
+               ST_AddBand(ST_MakeEmptyRaster(200, 200, 10, 10, 2, 2, 0, 0, -1), 1, '8BSI', 123, NULL),
+               'JPEG'
+       )
+);
+SELECT md5(
+       ST_AsGDALRaster(
+               ST_AddBand(ST_MakeEmptyRaster(200, 200, 10, 10, 2, 2, 0, 0, -1), 1, '8BUI', 123, NULL),
+               'JPEG'
+       )
+);
+SELECT md5(
+       ST_AsGDALRaster(
+               ST_AddBand(ST_MakeEmptyRaster(200, 200, 10, 10, 2, 2, 0, 0, -1), 1, '8BSI', -123, NULL),
+               'JPEG'
+       )
+);
+SELECT md5(
+       ST_AsGDALRaster(
+               ST_AddBand(ST_MakeEmptyRaster(200, 200, 10, 10, 2, 2, 0, 0, -1), 1, '8BUI', 254, NULL),
+               'JPEG'
+       )
+);
+SELECT md5(
+       ST_AsGDALRaster(
+               ST_AddBand(
+                       ST_AddBand(
+                               ST_AddBand(
+                                       ST_MakeEmptyRaster(200, 200, 10, 10, 2, 2, 0, 0, -1)
+                                       , 1, '8BSI', 1, -1
+                               )
+                               , 2, '8BSI', 11, -1
+                       )
+                       , 3, '8BSI', 111, -1
+               ),
+               'JPEG'
+       )
+);
+SELECT md5(
+       ST_AsGDALRaster(
+               ST_AddBand(
+                       ST_AddBand(
+                               ST_AddBand(
+                                       ST_MakeEmptyRaster(200, 200, 10, 10, 2, 2, 0, 0, -1)
+                                       , 1, '8BSI', 1, -1
+                               )
+                               , 2, '8BSI', 11, -1
+                       )
+                       , 3, '8BSI', 111, -1
+               ),
+               'JPEG',
+               ARRAY['QUALITY=90','PROGRESSIVE=ON']
+       )
+);
diff --git a/raster/test/regress/rt_asgdalraster_expected b/raster/test/regress/rt_asgdalraster_expected
new file mode 100644 (file)
index 0000000..f6bc732
--- /dev/null
@@ -0,0 +1,15 @@
+2fd5de5f94416f2999fc24fbc3974a4b
+4af1ca04adf045690fb60764cc93de75
+81de10108dd8eede75ef47c8f6f09bc5
+55279950e29968bcf36b2c11ce8bf88b
+55279950e29968bcf36b2c11ce8bf88b
+4ae84e77caa1a35aa4da233e83feb746
+24188762b5745acda4aa7b92776e7280
+da3c4e827b617f9d7cd83d5759f1d781
+0d6d88030359474c2e86280a65e5e90a
+f0de16a21de438249ec0bbd28eafe63c
+f0de16a21de438249ec0bbd28eafe63c
+ed4e4d60d2ccc82c4597e80c1f97272f
+83b6012757444ff7786b5e8473e6cea3
+b699be9f7045656edbb14ecec1c75b5a
+96a96f9a6d31544dfc5ba5cd25f40934