root/spike/wktraster/rt_pg/rt_pg.c

Revision 5869, 53.3 KB (checked in by pracine, 21 months ago)

-Better handle of NULL parameters in RASTER_makeEmpty
-Do not send an error when the requested band does not exist, just a NULL and a warning.

  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 *
4 * WKTRaster - Raster Types for PostGIS
5 * http://www.postgis.org/support/wiki/index.php?WKTRasterHomePage
6 *
7 * Copyright (C) 2009-2010  Sandro Santilli <strk@keybit.net>, David Zwarg <dzwarg@avencia.com>,
8 * Pierre Racine <pierre.racine@sbf.ulaval.ca>, Jorge Arevalo <jorge.arevalo@deimos-space.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
23 *
24 */
25
26#include <math.h>
27#include <float.h>
28#include <string.h>
29#include <stdio.h>
30#include <errno.h>
31#include <assert.h>
32
33#include <postgres.h> /* for palloc */
34#include <access/gist.h>
35#include <access/itup.h>
36#include <fmgr.h>
37#include <utils/elog.h>
38#include <funcapi.h>
39
40/*#include "lwgeom_pg.h"*/
41#include "liblwgeom.h"
42#include "rt_pg.h"
43#include "pgsql_compat.h"
44#include "rt_api.h"
45#include "../wktraster_config.h"
46
47/* Define this to debug pgsql RASTER activity */
48/* Alternative, use ./configure --enable-rtpg-debug */
49/*#define POSTGIS_RASTER_PG_DEBUG 1*/
50
51/* Define this to debug pgsql RASTER memory activity */
52/* Alternative, use ./configure --enable-rtpgmem-debug */
53/*#define POSTGIS_RASTER_PG_DEBUG_MEM 1*/
54
55#define POSTGIS_RASTER_WARN_ON_TRUNCATION
56
57/*
58 * This is required for builds against pgsql 8.2
59 */
60#ifdef PG_MODULE_MAGIC
61PG_MODULE_MAGIC;
62#endif
63
64/* Internal funcs */
65static rt_context get_rt_context(FunctionCallInfoData *fcinfo);
66static void *rt_pgalloc(size_t size);
67static void *rt_pgrealloc(void *mem, size_t size);
68static void rt_pgfree(void *mem);
69
70
71/* Prototypes */
72
73/* Utility functions */
74Datum RASTER_lib_version(PG_FUNCTION_ARGS);
75Datum RASTER_lib_build_date(PG_FUNCTION_ARGS);
76Datum RASTER_makeEmpty(PG_FUNCTION_ARGS);
77Datum RASTER_setSRID(PG_FUNCTION_ARGS);
78
79/* Input/output and format conversions */
80Datum RASTER_in(PG_FUNCTION_ARGS);
81Datum RASTER_out(PG_FUNCTION_ARGS);
82
83Datum RASTER_to_bytea(PG_FUNCTION_ARGS);
84Datum RASTER_to_binary(PG_FUNCTION_ARGS);
85
86/* Raster as geometry operations */
87Datum RASTER_convex_hull(PG_FUNCTION_ARGS);
88Datum RASTER_dumpAsWKTPolygons(PG_FUNCTION_ARGS);
89
90/* Get all the properties of a raster */
91Datum RASTER_getSRID(PG_FUNCTION_ARGS);
92Datum RASTER_getWidth(PG_FUNCTION_ARGS);
93Datum RASTER_getHeight(PG_FUNCTION_ARGS);
94Datum RASTER_getNumBands(PG_FUNCTION_ARGS);
95Datum RASTER_getXPixelSize(PG_FUNCTION_ARGS);
96Datum RASTER_getYPixelSize(PG_FUNCTION_ARGS);
97Datum RASTER_setPixelSize(PG_FUNCTION_ARGS);
98Datum RASTER_setPixelSizeXY(PG_FUNCTION_ARGS);
99Datum RASTER_getXSkew(PG_FUNCTION_ARGS);
100Datum RASTER_getYSkew(PG_FUNCTION_ARGS);
101Datum RASTER_setSkew(PG_FUNCTION_ARGS);
102Datum RASTER_setSkewXY(PG_FUNCTION_ARGS);
103Datum RASTER_getXUpperLeft(PG_FUNCTION_ARGS);
104Datum RASTER_getYUpperLeft(PG_FUNCTION_ARGS);
105Datum RASTER_setUpperLeftXY(PG_FUNCTION_ARGS);
106Datum RASTER_getBandPixelType(PG_FUNCTION_ARGS);
107Datum RASTER_getBandPixelTypeName(PG_FUNCTION_ARGS);
108Datum RASTER_getBandHasNoDataValue(PG_FUNCTION_ARGS);
109Datum RASTER_setBandHasNoDataValue(PG_FUNCTION_ARGS);
110Datum RASTER_getBandNoDataValue(PG_FUNCTION_ARGS);
111Datum RASTER_setBandNoDataValue(PG_FUNCTION_ARGS);
112Datum RASTER_getBandPath(PG_FUNCTION_ARGS);
113Datum RASTER_getPixelValue(PG_FUNCTION_ARGS);
114Datum RASTER_setPixelValue(PG_FUNCTION_ARGS);
115Datum RASTER_addband(PG_FUNCTION_ARGS);
116
117
118static void *
119rt_pgalloc(size_t size)
120{
121    void* ret;
122#ifdef POSTGIS_RASTER_PG_DEBUG_MEM
123    elog(NOTICE, "rt_pgalloc(%ld) called", size);
124#endif
125    ret = palloc(size);
126#ifdef POSTGIS_RASTER_PG_DEBUG_MEM
127    elog(NOTICE, "palloc(%ld) returned", size);
128#endif
129    return ret;
130}
131
132static void *
133rt_pgrealloc(void *mem, size_t size)
134{
135    void* ret;
136#ifdef POSTGIS_RASTER_PG_DEBUG_MEM
137    elog(NOTICE, "rt_pgrealloc(%p, %ld) called", mem, size);
138#endif
139    if ( mem )
140    {
141#ifdef POSTGIS_RASTER_PG_DEBUG_MEM
142        elog(NOTICE, "rt_pgrealloc calling repalloc(%p, %ld)", mem, size);
143#endif
144        ret = repalloc(mem, size);
145    }
146    else
147    {
148#ifdef POSTGIS_RASTER_PG_DEBUG_MEM
149        elog(NOTICE, "rt_pgrealloc calling palloc(%ld)", size);
150#endif
151        ret = palloc(size);
152    }
153    return ret;
154}
155
156static void
157rt_pgfree(void *mem)
158{
159#ifdef POSTGIS_RASTER_PG_DEBUG_MEM
160    elog(NOTICE, "rt_pgfree(%p) calling pfree", mem);
161#endif
162    pfree(mem);
163}
164
165static void
166rt_pgerr(const char *fmt, ...)
167{
168    va_list ap;
169    char msg[1024];
170
171    va_start(ap, fmt);
172
173    vsnprintf(msg, 1023, fmt, ap);
174
175    elog(ERROR, "%s", msg);
176
177    va_end(ap);
178}
179
180static void
181rt_pgwarn(const char *fmt, ...)
182{
183    va_list ap;
184    char msg[1024];
185
186    va_start(ap, fmt);
187
188    vsnprintf(msg, 1023, fmt, ap);
189
190    elog(WARNING, "%s", msg);
191
192    va_end(ap);
193}
194
195static void
196rt_pginfo(const char *fmt, ...)
197{
198    va_list ap;
199    char msg[1024];
200
201    va_start(ap, fmt);
202
203    vsnprintf(msg, 1023, fmt, ap);
204
205    elog(INFO, "%s", msg);
206
207    va_end(ap);
208}
209
210
211static rt_context
212get_rt_context(FunctionCallInfoData *fcinfo)
213{
214    rt_context ctx = 0;
215    MemoryContext old_context;
216
217    if ( ctx ) return ctx;
218
219    /* We switch memory context info so the rt_context
220     * is not freed by the end of function call
221     * TODO: check _which_ context we should be using
222     * for a whole-plugin-lifetime persistence */
223    old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
224    ctx = rt_context_new(rt_pgalloc, rt_pgrealloc, rt_pgfree);
225    MemoryContextSwitchTo(old_context);
226
227    rt_context_set_message_handlers(ctx, rt_pgerr, rt_pgwarn, rt_pginfo);
228
229    return ctx;
230}
231
232PG_FUNCTION_INFO_V1(RASTER_lib_version);
233Datum RASTER_lib_version(PG_FUNCTION_ARGS)
234{
235    char *ver = POSTGIS_RASTER_LIB_VERSION;
236    text *result;
237    result = palloc(VARHDRSZ  + strlen(ver));
238    SET_VARSIZE(result, VARHDRSZ + strlen(ver));
239    memcpy(VARDATA(result), ver, strlen(ver));
240    PG_RETURN_POINTER(result);
241}
242
243PG_FUNCTION_INFO_V1(RASTER_lib_build_date);
244Datum RASTER_lib_build_date(PG_FUNCTION_ARGS)
245{
246    char *ver = POSTGIS_RASTER_BUILD_DATE;
247    text *result;
248    result = palloc(VARHDRSZ  + strlen(ver));
249    SET_VARSIZE(result, VARHDRSZ + strlen(ver));
250    memcpy(VARDATA(result), ver, strlen(ver));
251    PG_RETURN_POINTER(result);
252}
253
254/*
255 * Input is a string with hex chars in it.
256 * Convert to binary and put in the result
257 */
258PG_FUNCTION_INFO_V1(RASTER_in);
259Datum RASTER_in(PG_FUNCTION_ARGS)
260{
261    rt_raster raster;
262    char *hexwkb = PG_GETARG_CSTRING(0);
263    void *result = NULL;
264    rt_context ctx = get_rt_context(fcinfo);
265
266    raster = rt_raster_from_hexwkb(ctx, hexwkb, strlen(hexwkb));
267    result = rt_raster_serialize(ctx, raster);
268
269    SET_VARSIZE(result, ((rt_pgraster*)result)->size);
270
271    if ( NULL != result )
272        PG_RETURN_POINTER(result);
273    else
274        PG_RETURN_NULL();
275}
276
277/*given a RASTER structure, convert it to Hex and put it in a string */
278PG_FUNCTION_INFO_V1(RASTER_out);
279Datum RASTER_out(PG_FUNCTION_ARGS)
280{
281    rt_pgraster *pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
282    rt_raster raster = NULL;
283    uint32_t hexwkbsize = 0;
284    char *hexwkb = NULL;
285    rt_context ctx = get_rt_context(fcinfo);
286
287    raster = rt_raster_deserialize(ctx, pgraster);
288    if ( ! raster ) {
289        elog(ERROR, "Could not deserialize raster");
290        PG_RETURN_NULL();
291    }
292
293    /*elog(NOTICE, "RASTER_out: rt_raster_deserialize returned %p", raster);*/
294
295    hexwkb = rt_raster_to_hexwkb(ctx, raster, &hexwkbsize);
296    if ( ! hexwkb )
297    {
298        elog(ERROR, "Could not HEX-WKBize raster");
299        PG_RETURN_NULL();
300    }
301
302    /*elog(NOTICE, "RASTER_out: rt_raster_to_hexwkb returned");*/
303
304    PG_RETURN_CSTRING(hexwkb);
305}
306
307/*
308 * Return bytea object with raster in Well-Known-Binary form.
309 */
310PG_FUNCTION_INFO_V1(RASTER_to_bytea);
311Datum RASTER_to_bytea(PG_FUNCTION_ARGS)
312{
313    rt_pgraster *pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
314    rt_raster raster = NULL;
315    rt_context ctx = get_rt_context(fcinfo);
316    uint8_t *wkb = NULL;
317    uint32_t wkb_size = 0;
318    bytea *result = NULL;
319    int result_size = 0;
320
321    raster = rt_raster_deserialize(ctx, pgraster);
322    if ( ! raster ) {
323        elog(ERROR, "Could not deserialize raster");
324        PG_RETURN_NULL();
325    }
326    /*elog(NOTICE, "RASTER_to_binary: rt_raster_deserialize returned %p", raster);*/
327
328    /* Uses context allocator */
329    wkb = rt_raster_to_wkb(ctx, raster, &wkb_size);
330    if ( ! wkb ) {
331        elog(ERROR, "Could not allocate and generate WKB data");
332        PG_RETURN_NULL();
333    }
334
335    /* TODO: Replace all palloc/pfree, malloc/free, etc. with rt_context_t->{alloc/dealloc}
336     * FIXME: ATM, impossible as rt_context_t is incomplete type for rt_pg layer. */
337    result_size = wkb_size + VARHDRSZ;
338    result = (bytea *)palloc(result_size);
339    SET_VARSIZE(result, result_size);
340    memcpy(VARDATA(result), wkb, VARSIZE(result) - VARHDRSZ);
341    pfree(wkb);
342
343    PG_RETURN_POINTER(result);
344}
345
346/*
347 * Return bytea object with raster requested using ST_AsBinary function.
348 */
349PG_FUNCTION_INFO_V1(RASTER_to_binary);
350Datum RASTER_to_binary(PG_FUNCTION_ARGS)
351{
352    rt_pgraster *pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
353    rt_raster raster = NULL;
354    rt_context ctx = get_rt_context(fcinfo);
355    uint8_t *wkb = NULL;
356    uint32_t wkb_size = 0;
357    char *result = NULL;
358    int result_size = 0;
359
360    raster = rt_raster_deserialize(ctx, pgraster);
361    if ( ! raster ) {
362        elog(ERROR, "Could not deserialize raster");
363        PG_RETURN_NULL();
364    }
365    /*elog(NOTICE, "RASTER_to_binary: rt_raster_deserialize returned %p", raster);*/
366
367    /* Uses context allocator */
368    wkb = rt_raster_to_wkb(ctx, raster, &wkb_size);
369    if ( ! wkb ) {
370        elog(ERROR, "Could not allocate and generate WKB data");
371        PG_RETURN_NULL();
372    }
373
374    /* TODO: Replace all palloc/pfree, malloc/free, etc. with rt_context_t->{alloc/dealloc}
375     * FIXME: ATM, impossible as rt_context_t is incomplete type for rt_pg layer. */
376    result_size = wkb_size + VARHDRSZ;
377    result = (char *)palloc(result_size);
378    SET_VARSIZE(result, result_size);
379    memcpy(VARDATA(result), wkb, VARSIZE(result) - VARHDRSZ);
380    pfree(wkb);
381
382    PG_RETURN_POINTER(result);
383}
384
385/**
386 * Return the convex hull of this raster as a 4-vertices POLYGON.
387 */
388PG_FUNCTION_INFO_V1(RASTER_convex_hull);
389Datum RASTER_convex_hull(PG_FUNCTION_ARGS)
390{
391    rt_pgraster *pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
392    rt_raster raster;
393    rt_context ctx = get_rt_context(fcinfo);
394    LWPOLY* convexhull;
395    uchar* pglwgeom;
396
397    { /* TODO: can be optimized to only detoast the header! */
398        raster = rt_raster_deserialize(ctx, pgraster);
399        if ( ! raster ) {
400            elog(ERROR, "Could not deserialize raster");
401            PG_RETURN_NULL();
402        }
403
404        /*elog(NOTICE, "rt_raster_deserialize returned %p", raster);*/
405
406        convexhull = rt_raster_get_convex_hull(ctx, raster);
407        if ( ! convexhull ) {
408            elog(ERROR, "Could not get raster's convex hull");
409            PG_RETURN_NULL();
410        }
411    }
412
413    {
414        size_t sz = lwpoly_serialize_size(convexhull);
415        pglwgeom = palloc(VARHDRSZ+sz);
416        lwpoly_serialize_buf(convexhull, (uchar*)VARDATA(pglwgeom), &sz);
417        SET_VARSIZE(pglwgeom, VARHDRSZ+sz);
418    }
419
420    /* PG_FREE_IF_COPY(pgraster, 0); */
421    /* lwfree(convexhull) */
422    /* ... more deallocs ... */
423
424
425    PG_RETURN_POINTER(pglwgeom);
426}
427
428
429/**
430
431 * Needed for sizeof
432 */
433struct rt_wktgeomval_t {
434    int srid;
435    double val;
436    char * wktGeom;
437};
438
439
440PG_FUNCTION_INFO_V1(RASTER_dumpAsWKTPolygons);
441Datum RASTER_dumpAsWKTPolygons(PG_FUNCTION_ARGS)
442{   
443    rt_pgraster *pgraster;
444    rt_raster raster;
445    rt_context ctx;
446    FuncCallContext *funcctx;
447    TupleDesc tupdesc;
448    AttInMetadata *attinmeta;
449    int nband;
450    rt_wktgeomval wktGeomval;
451        rt_wktgeomval wktGeomval2;
452    int call_cntr;
453    int max_calls;
454    int nElements;
455
456
457    /* stuff done only on the first call of the function */
458    if (SRF_IS_FIRSTCALL())
459    {
460       
461#ifdef POSTGIS_RASTER_PG_DEBUG_MEM
462        elog(NOTICE, "RASTER_dumpAsWKTPolygons first call");
463#endif 
464        MemoryContext   oldcontext;
465
466        /* create a function context for cross-call persistence */
467        funcctx = SRF_FIRSTCALL_INIT();
468
469        /* switch to memory context appropriate for multiple function calls */
470        oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
471
472        /**
473         * Create a new context. We don't call get_rt_context, because this
474         * function changes the memory context to the one pointed by
475         * fcinfo->flinfo->fn_mcxt, and we want to keep the current one
476         */
477        ctx = rt_context_new(rt_pgalloc, rt_pgrealloc, rt_pgfree);
478        rt_context_set_message_handlers(ctx, rt_pgerr, rt_pgwarn, rt_pginfo);
479
480        /* Get input arguments */
481        pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
482        raster = rt_raster_deserialize(ctx, pgraster);
483        if ( ! raster ) 
484        {
485            ereport(ERROR,
486                    (errcode(ERRCODE_OUT_OF_MEMORY), 
487                    errmsg("Could not deserialize raster")));
488            PG_RETURN_NULL();
489        }
490
491        if (PG_NARGS() == 2)
492            nband = PG_GETARG_UINT32(1);
493        else
494            nband = 1; /* By default, first band */
495                       
496#ifdef POSTGIS_RASTER_PG_DEBUG_MEM
497        elog(NOTICE, "RASTER_dumpAsWKTPolygons: band %d", nband);
498#endif                 
499
500        /* Polygonize raster */
501       
502        /**
503         * Dump raster
504         */
505        wktGeomval = rt_raster_dump_as_wktpolygons(ctx, raster, nband, &nElements);     
506        if (NULL == wktGeomval)
507        {
508            ereport(ERROR,
509                    (errcode(ERRCODE_NO_DATA_FOUND), 
510                    errmsg("Could not polygonize raster")));
511            PG_RETURN_NULL();
512        }
513               
514#ifdef POSTGIS_RASTER_PG_DEBUG_MEM
515        elog(NOTICE, "RASTER_dumpAsWKTPolygons: raster dump, %d elements returned", nElements);
516#endif         
517
518        /**
519         * Not needed to check geomval. It was allocated by the new
520         * rt_context, that uses palloc. It never returns NULL
521         */
522
523        /* Store needed information */
524        funcctx->user_fctx = wktGeomval;
525
526        /* total number of tuples to be returned */
527        funcctx->max_calls = nElements;
528               
529        /* Build a tuple descriptor for our result type */
530        if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
531            ereport(ERROR,
532                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
533                     errmsg("function returning record called in context "
534                            "that cannot accept type record")));
535
536        /*
537         * generate attribute metadata needed later to produce tuples from raw
538         * C strings
539         */
540        attinmeta = TupleDescGetAttInMetadata(tupdesc);
541        funcctx->attinmeta = attinmeta;
542
543        MemoryContextSwitchTo(oldcontext);
544    }
545
546    /* stuff done on every call of the function */
547    funcctx = SRF_PERCALL_SETUP();
548       
549    call_cntr = funcctx->call_cntr;
550    max_calls = funcctx->max_calls;
551    attinmeta = funcctx->attinmeta;
552    wktGeomval2 = funcctx->user_fctx;
553
554    if (call_cntr < max_calls)    /* do when there is more left to send */
555    {
556
557        char       **values;
558        HeapTuple    tuple;
559        Datum        result;
560               
561               
562#ifdef POSTGIS_RASTER_PG_DEBUG_MEM
563    elog(NOTICE, "RASTER_dumpAsWKTPolygons: call number %d", call_cntr);
564#endif
565                       
566        /*
567         * Prepare a values array for building the returned tuple.
568         * This should be an array of C strings which will
569         * be processed later by the type input functions.
570         */
571        values = (char **) palloc(3 * sizeof(char *));           
572
573        values[0] = (char *) palloc(
574                (strlen(wktGeomval2[call_cntr].wktGeom) + 1) * sizeof(char));
575        values[1] = (char *) palloc(18 * sizeof(char));
576        values[2] = (char *) palloc(16 * sizeof(char));
577
578        snprintf(values[0], 
579            (strlen(wktGeomval2[call_cntr].wktGeom) + 1) * sizeof(char), "%s", 
580            wktGeomval2[call_cntr].wktGeom);       
581        snprintf(values[1], 18 * sizeof(char), "%f", wktGeomval2[call_cntr].val);
582        snprintf(values[2], 16 * sizeof(char), "%d", wktGeomval2[call_cntr].srid);
583
584                /**
585                 * Free resources.
586                 * TODO: Do we have to use the same context we used
587                 * for creating them?
588                 */
589                pfree(wktGeomval2[call_cntr].wktGeom);
590
591
592        /* build a tuple */
593        tuple = BuildTupleFromCStrings(attinmeta, values);
594
595        /* make the tuple into a datum */
596        result = HeapTupleGetDatum(tuple);
597
598        /* clean up (this is not really necessary) */
599        pfree(values[0]);
600        pfree(values[1]);
601        pfree(values[2]);
602        pfree(values);
603       
604
605        SRF_RETURN_NEXT(funcctx, result);
606    }
607    else    /* do when there is no more left */
608    {       
609                pfree(wktGeomval2);
610        SRF_RETURN_DONE(funcctx);
611    }
612
613}
614
615
616/**
617 * rt_MakeEmptyRaster( <width>, <height>, <ipx>, <ipy>,
618 *                                        <scalex>, <scaley>,
619 *                                        <skewx>, <skewy>,
620 *                                        <srid>)
621 */
622PG_FUNCTION_INFO_V1(RASTER_makeEmpty);
623Datum RASTER_makeEmpty(PG_FUNCTION_ARGS)
624{
625    uint16 width, height;
626    double ipx, ipy, scalex, scaley, skewx, skewy;
627    int32_t srid;
628    rt_pgraster *pgraster;
629    rt_raster raster;
630    rt_context ctx;
631
632    if ( PG_NARGS() < 9 )
633    {
634        elog(ERROR, "MakeEmptyRaster requires 9 args");
635        PG_RETURN_NULL();
636    }
637
638    if (PG_ARGISNULL(0))
639        width = 0;
640    else
641        width = PG_GETARG_UINT16(0);
642
643    if (PG_ARGISNULL(1))
644        height = 0;
645    else
646        height = PG_GETARG_UINT16(1);
647
648    if (PG_ARGISNULL(2))
649        ipx = 0;
650    else
651        ipx = PG_GETARG_FLOAT8(2);
652
653    if (PG_ARGISNULL(3))
654        ipy = 0;
655    else
656        ipy = PG_GETARG_FLOAT8(3);
657
658    if (PG_ARGISNULL(4))
659        scalex = 0;
660    else
661        scalex = PG_GETARG_FLOAT8(4);
662
663    if (PG_ARGISNULL(5))
664        scaley = 0;
665    else
666        scaley = PG_GETARG_FLOAT8(5);
667
668    if (PG_ARGISNULL(6))
669        skewx = 0;
670    else
671        skewx = PG_GETARG_FLOAT8(6);
672
673    if (PG_ARGISNULL(7))
674        skewy = 0;
675    else
676        skewy = PG_GETARG_FLOAT8(7);
677
678    if (PG_ARGISNULL(8))
679        srid = -1;
680    else
681        srid = PG_GETARG_INT32(8);
682
683#ifdef POSTGIS_RASTER_PG_DEBUG
684    elog(NOTICE, "%dx%d, ip:%g,%g, scale:%g,%g, skew:%g,%g srid:%d",
685                  width, height, ipx, ipy, scalex, scaley,
686                  skewx, skewy, srid);
687#endif
688
689    ctx = get_rt_context(fcinfo);
690
691    raster = rt_raster_new(ctx, width, height);
692    if ( ! raster ) {
693        PG_RETURN_NULL(); /* error was supposedly printed already */
694    }
695
696    rt_raster_set_pixel_sizes(ctx, raster, scalex, scaley);
697    rt_raster_set_offsets(ctx, raster, ipx, ipy);
698    rt_raster_set_skews(ctx, raster, skewx, skewy);
699    rt_raster_set_srid(ctx, raster, srid);
700
701    pgraster = rt_raster_serialize(ctx, raster);
702    if ( ! pgraster ) PG_RETURN_NULL();
703
704    SET_VARSIZE(pgraster, pgraster->size);
705    PG_RETURN_POINTER(pgraster);
706}
707
708/**
709 * Return the SRID associated with the raster.
710 */
711PG_FUNCTION_INFO_V1(RASTER_getSRID);
712Datum RASTER_getSRID(PG_FUNCTION_ARGS)
713{
714    rt_pgraster *pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
715    rt_raster raster;
716    rt_context ctx = get_rt_context(fcinfo);
717    int32_t srid;
718
719    /* TODO: can be optimized to only detoast the header! */
720    raster = rt_raster_deserialize(ctx, pgraster);
721    if ( ! raster ) {
722        elog(ERROR, "Could not deserialize raster");
723        PG_RETURN_NULL();
724    }
725
726    srid = rt_raster_get_srid(ctx, raster);
727    PG_RETURN_INT32(srid);
728}
729
730/**
731 * Set the SRID associated with the raster.
732 */
733PG_FUNCTION_INFO_V1(RASTER_setSRID);
734Datum RASTER_setSRID(PG_FUNCTION_ARGS)
735{
736    rt_pgraster *pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
737    rt_raster raster;
738    rt_context ctx = get_rt_context(fcinfo);
739    int32_t newSRID = PG_GETARG_INT32(1);
740
741    /* TODO: can be optimized to only detoast the header! */
742    raster = rt_raster_deserialize(ctx, pgraster);
743    if ( ! raster ) {
744        elog(ERROR, "Could not deserialize raster");
745        PG_RETURN_NULL();
746    }
747
748    rt_raster_set_srid(ctx, raster, newSRID);
749
750    pgraster = rt_raster_serialize(ctx, raster);
751    if ( ! pgraster ) PG_RETURN_NULL();
752
753    SET_VARSIZE(pgraster, pgraster->size);
754    PG_RETURN_POINTER(pgraster);
755}
756
757/**
758 * Return the width of the raster.
759 */
760PG_FUNCTION_INFO_V1(RASTER_getWidth);
761Datum RASTER_getWidth(PG_FUNCTION_ARGS)
762{
763    rt_pgraster *pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
764    rt_raster raster;
765    rt_context ctx = get_rt_context(fcinfo);
766    uint16_t width;
767
768    /* TODO: can be optimized to only detoast the header! */
769    raster = rt_raster_deserialize(ctx, pgraster);
770    if ( ! raster ) {
771        elog(ERROR, "Could not deserialize raster");
772        PG_RETURN_NULL();
773    }
774
775    width = rt_raster_get_width(ctx, raster);
776    PG_RETURN_INT32(width);
777}
778
779/**
780 * Return the height of the raster.
781 */
782PG_FUNCTION_INFO_V1(RASTER_getHeight);
783Datum RASTER_getHeight(PG_FUNCTION_ARGS)
784{
785    rt_pgraster *pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
786    rt_raster raster;
787    rt_context ctx = get_rt_context(fcinfo);
788    uint16_t height;
789
790    /* TODO: can be optimized to only detoast the header! */
791    raster = rt_raster_deserialize(ctx, pgraster);
792    if ( ! raster ) {
793        elog(ERROR, "Could not deserialize raster");
794        PG_RETURN_NULL();
795    }
796
797    height = rt_raster_get_height(ctx, raster);
798    PG_RETURN_INT32(height);
799}
800
801/**
802 * Return the number of bands included in the raster.
803 */
804PG_FUNCTION_INFO_V1(RASTER_getNumBands);
805Datum RASTER_getNumBands(PG_FUNCTION_ARGS)
806{
807    rt_pgraster *pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
808    rt_raster raster;
809    rt_context ctx = get_rt_context(fcinfo);
810    int32_t num_bands;
811
812    /* TODO: can be optimized to only detoast the header! */
813    raster = rt_raster_deserialize(ctx, pgraster);
814    if ( ! raster ) {
815        elog(ERROR, "Could not deserialize raster");
816        PG_RETURN_NULL();
817    }
818
819    num_bands = rt_raster_get_num_bands(ctx, raster);
820    PG_RETURN_INT32(num_bands);
821}
822
823/**
824 * Return X size of pixel from georeference of the raster.
825 */
826PG_FUNCTION_INFO_V1(RASTER_getXPixelSize);
827Datum RASTER_getXPixelSize(PG_FUNCTION_ARGS)
828{
829    rt_pgraster *pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
830    rt_raster raster;
831    rt_context ctx = get_rt_context(fcinfo);
832    double xsize;
833
834    /* TODO: can be optimized to only detoast the header! */
835    raster = rt_raster_deserialize(ctx, pgraster);
836    if ( ! raster ) {
837        elog(ERROR, "Could not deserialize raster");
838        PG_RETURN_NULL();
839    }
840
841    xsize = rt_raster_get_pixel_width(ctx, raster);
842    PG_RETURN_FLOAT8(xsize);
843}
844
845/**
846 * Return Y size of pixel from georeference of the raster.
847 */
848PG_FUNCTION_INFO_V1(RASTER_getYPixelSize);
849Datum RASTER_getYPixelSize(PG_FUNCTION_ARGS)
850{
851    rt_pgraster *pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
852    rt_raster raster;
853    rt_context ctx = get_rt_context(fcinfo);
854    double ysize;
855
856    /* TODO: can be optimized to only detoast the header! */
857    raster = rt_raster_deserialize(ctx, pgraster);
858    if ( ! raster ) {
859        elog(ERROR, "Could not deserialize raster");
860        PG_RETURN_NULL();
861    }
862
863    ysize = rt_raster_get_pixel_height(ctx, raster);
864    PG_RETURN_FLOAT8(ysize);
865}
866
867/**
868 * Set the pixel size of the raster.
869 */
870PG_FUNCTION_INFO_V1(RASTER_setPixelSize);
871Datum RASTER_setPixelSize(PG_FUNCTION_ARGS)
872{
873    rt_pgraster *pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
874    rt_raster raster;
875    rt_context ctx = get_rt_context(fcinfo);
876    double size = PG_GETARG_FLOAT8(1);
877
878    raster = rt_raster_deserialize(ctx, pgraster);
879    if (! raster ) {
880        elog(ERROR, "Could not deserialize raster");
881        PG_RETURN_NULL();
882    }
883
884    rt_raster_set_pixel_sizes(ctx, raster, size, size);
885
886    pgraster = rt_raster_serialize(ctx, raster);
887    if ( ! pgraster ) PG_RETURN_NULL();
888
889    SET_VARSIZE(pgraster, pgraster->size);
890    PG_RETURN_POINTER(pgraster);
891}
892
893/**
894 * Set the pixel size of the raster.
895 */
896PG_FUNCTION_INFO_V1(RASTER_setPixelSizeXY);
897Datum RASTER_setPixelSizeXY(PG_FUNCTION_ARGS)
898{
899    rt_pgraster *pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
900    rt_raster raster;
901    rt_context ctx = get_rt_context(fcinfo);
902    double xsize = PG_GETARG_FLOAT8(1);
903    double ysize = PG_GETARG_FLOAT8(2);
904
905    raster = rt_raster_deserialize(ctx, pgraster);
906    if (! raster ) {
907        elog(ERROR, "Could not deserialize raster");
908        PG_RETURN_NULL();
909    }
910
911    rt_raster_set_pixel_sizes(ctx, raster, xsize, ysize);
912
913    pgraster = rt_raster_serialize(ctx, raster);
914    if ( ! pgraster ) PG_RETURN_NULL();
915
916    SET_VARSIZE(pgraster, pgraster->size);
917    PG_RETURN_POINTER(pgraster);
918}
919
920/**
921 * Return value of the raster skew about the X axis.
922 */
923PG_FUNCTION_INFO_V1(RASTER_getXSkew);
924Datum RASTER_getXSkew(PG_FUNCTION_ARGS)
925{
926    rt_pgraster *pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
927    rt_raster raster;
928    rt_context ctx = get_rt_context(fcinfo);
929    double xskew;
930
931    /* TODO: can be optimized to only detoast the header! */
932    raster = rt_raster_deserialize(ctx, pgraster);
933    if ( ! raster ) {
934        elog(ERROR, "Could not deserialize raster");
935        PG_RETURN_NULL();
936    }
937
938    xskew = rt_raster_get_x_skew(ctx, raster);
939    PG_RETURN_FLOAT8(xskew);
940}
941
942/**
943 * Return value of the raster skew about the Y axis.
944 */
945PG_FUNCTION_INFO_V1(RASTER_getYSkew);
946Datum RASTER_getYSkew(PG_FUNCTION_ARGS)
947{
948    rt_pgraster *pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
949    rt_raster raster;
950    rt_context ctx = get_rt_context(fcinfo);
951    double yskew;
952
953    /* TODO: can be optimized to only detoast the header! */
954    raster = rt_raster_deserialize(ctx, pgraster);
955    if ( ! raster ) {
956        elog(ERROR, "Could not deserialize raster");
957        PG_RETURN_NULL();
958    }
959
960    yskew = rt_raster_get_y_skew(ctx, raster);
961    PG_RETURN_FLOAT8(yskew);
962}
963
964/**
965 * Set the skew of the raster.
966 */
967PG_FUNCTION_INFO_V1(RASTER_setSkew);
968Datum RASTER_setSkew(PG_FUNCTION_ARGS)
969{
970    rt_pgraster *pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
971    rt_raster raster;
972    rt_context ctx = get_rt_context(fcinfo);
973    double skew = PG_GETARG_FLOAT8(1);
974
975    raster = rt_raster_deserialize(ctx, pgraster);
976    if (! raster ) {
977        elog(ERROR, "Could not deserialize raster");
978        PG_RETURN_NULL();
979    }
980
981    rt_raster_set_skews(ctx, raster, skew, skew);
982
983    pgraster = rt_raster_serialize(ctx, raster);
984    if ( ! pgraster ) PG_RETURN_NULL();
985
986    SET_VARSIZE(pgraster, pgraster->size);
987    PG_RETURN_POINTER(pgraster);
988}
989
990/**
991 * Set the skew of the raster.
992 */
993PG_FUNCTION_INFO_V1(RASTER_setSkewXY);
994Datum RASTER_setSkewXY(PG_FUNCTION_ARGS)
995{
996    rt_pgraster *pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
997    rt_raster raster;
998    rt_context ctx = get_rt_context(fcinfo);
999    double xskew = PG_GETARG_FLOAT8(1);
1000    double yskew = PG_GETARG_FLOAT8(2);
1001
1002    raster = rt_raster_deserialize(ctx, pgraster);
1003    if (! raster ) {
1004        elog(ERROR, "Could not deserialize raster");
1005        PG_RETURN_NULL();
1006    }
1007
1008    rt_raster_set_skews(ctx, raster, xskew, yskew);
1009
1010    pgraster = rt_raster_serialize(ctx, raster);
1011    if ( ! pgraster ) PG_RETURN_NULL();
1012
1013    SET_VARSIZE(pgraster, pgraster->size);
1014    PG_RETURN_POINTER(pgraster);
1015}
1016
1017/**
1018 * Return value of the raster offset in the X dimension.
1019 */
1020PG_FUNCTION_INFO_V1(RASTER_getXUpperLeft);
1021Datum RASTER_getXUpperLeft(PG_FUNCTION_ARGS)
1022{
1023    rt_pgraster *pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
1024    rt_raster raster;
1025    rt_context ctx = get_rt_context(fcinfo);
1026    double xul;
1027
1028    /* TODO: can be optimized to only detoast the header! */
1029    raster = rt_raster_deserialize(ctx, pgraster);
1030    if ( ! raster ) {
1031        elog(ERROR, "Could not deserialize raster");
1032        PG_RETURN_NULL();
1033    }
1034
1035    xul = rt_raster_get_x_offset(ctx, raster);
1036    PG_RETURN_FLOAT8(xul);
1037}
1038
1039/**
1040 * Return value of the raster offset in the Y dimension.
1041 */
1042PG_FUNCTION_INFO_V1(RASTER_getYUpperLeft);
1043Datum RASTER_getYUpperLeft(PG_FUNCTION_ARGS)
1044{
1045    rt_pgraster *pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
1046    rt_raster raster;
1047    rt_context ctx = get_rt_context(fcinfo);
1048    double yul;
1049
1050    /* TODO: can be optimized to only detoast the header! */
1051    raster = rt_raster_deserialize(ctx, pgraster);
1052    if ( ! raster ) {
1053        elog(ERROR, "Could not deserialize raster");
1054        PG_RETURN_NULL();
1055    }
1056
1057    yul = rt_raster_get_y_offset(ctx, raster);
1058    PG_RETURN_FLOAT8(yul);
1059}
1060
1061/**
1062 * Set the raster offset in the X and Y dimension.
1063 */
1064PG_FUNCTION_INFO_V1(RASTER_setUpperLeftXY);
1065Datum RASTER_setUpperLeftXY(PG_FUNCTION_ARGS)
1066{
1067    rt_pgraster *pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
1068    rt_raster raster;
1069    rt_context ctx = get_rt_context(fcinfo);
1070    double xoffset = PG_GETARG_FLOAT8(1);
1071    double yoffset = PG_GETARG_FLOAT8(2);
1072
1073    raster = rt_raster_deserialize(ctx, pgraster);
1074    if (! raster ) {
1075        elog(ERROR, "Could not deserialize raster");
1076        PG_RETURN_NULL();
1077    }
1078
1079    rt_raster_set_offsets(ctx, raster, xoffset, yoffset);
1080
1081    pgraster = rt_raster_serialize(ctx, raster);
1082    if ( ! pgraster ) PG_RETURN_NULL();
1083
1084    SET_VARSIZE(pgraster, pgraster->size);
1085    PG_RETURN_POINTER(pgraster);
1086}
1087
1088/**
1089 * Return pixel type of the specified band of raster.
1090 * Band index is 1-based.
1091 */
1092PG_FUNCTION_INFO_V1(RASTER_getBandPixelType);
1093Datum RASTER_getBandPixelType(PG_FUNCTION_ARGS)
1094{
1095    rt_pgraster *pgraster = NULL;
1096    rt_raster raster = NULL;
1097    rt_band band = NULL;
1098    rt_context ctx = NULL;
1099    rt_pixtype pixtype;
1100    int32_t index;
1101
1102    /* Index is 1-based */
1103    index = PG_GETARG_INT32(1);
1104    if ( index < 1 ) {
1105        elog(ERROR, "Invalid band index (must use 1-based)");
1106        PG_RETURN_NULL();
1107    }
1108    assert(0 <= (index - 1));
1109
1110    /* Deserialize raster */
1111    pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
1112    ctx = get_rt_context(fcinfo);
1113
1114    raster = rt_raster_deserialize(ctx, pgraster);
1115    if ( ! raster ) {
1116        elog(ERROR, "Could not deserialize raster");
1117        PG_RETURN_NULL();
1118    }
1119
1120    /* Fetch requested band and its pixel type */
1121    band = rt_raster_get_band(ctx, raster, index - 1);
1122    if ( ! band ) {
1123        elog(NOTICE, "Could not find raster band of index %d. Returning NULL", index);
1124        PG_RETURN_NULL();
1125    }
1126
1127    pixtype = rt_band_get_pixtype(ctx, band);
1128    PG_RETURN_INT32(pixtype);
1129}
1130
1131/**
1132 * Return name of pixel type of the specified band of raster.
1133 * Band index is 1-based.
1134 * NOTE: This is unofficial utility not included in the spec.
1135 */
1136PG_FUNCTION_INFO_V1(RASTER_getBandPixelTypeName);
1137Datum RASTER_getBandPixelTypeName(PG_FUNCTION_ARGS)
1138{
1139    rt_pgraster *pgraster = NULL;
1140    rt_raster raster = NULL;
1141    rt_band band = NULL;
1142    rt_context ctx = NULL;
1143    rt_pixtype pixtype;
1144    int32_t index;
1145    const size_t name_size = 8; /* size of type name */
1146    size_t size = 0;
1147    char *ptr = NULL;
1148    text *result = NULL;
1149
1150    /* Index is 1-based */
1151    index = PG_GETARG_INT32(1);
1152    if ( index < 1 ) {
1153        elog(ERROR, "Invalid band index (must use 1-based)");
1154        PG_RETURN_NULL();
1155    }
1156    assert(0 <= (index - 1));
1157
1158    /* Deserialize raster */
1159    pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
1160    ctx = get_rt_context(fcinfo);
1161
1162    raster = rt_raster_deserialize(ctx, pgraster);
1163    if ( ! raster ) {
1164        elog(ERROR, "Could not deserialize raster");
1165        PG_RETURN_NULL();
1166    }
1167
1168    /* Fetch requested band and its pixel type */
1169    band = rt_raster_get_band(ctx, raster, index - 1);
1170    if ( ! band ) {
1171        elog(NOTICE, "Could not find raster band of index %d. Returning NULL", index);
1172        PG_RETURN_NULL();
1173    }
1174
1175    pixtype = rt_band_get_pixtype(ctx, band);
1176
1177    result = palloc(VARHDRSZ + name_size);
1178    if ( ! result ) {
1179        elog(ERROR, "Could not allocate memory for output text object");
1180        PG_RETURN_NULL();
1181    }
1182    memset(VARDATA(result), 0, name_size);
1183    ptr = (char *)result + VARHDRSZ;
1184
1185    switch (pixtype)
1186    {
1187        case PT_1BB:  /* 1-bit boolean            */
1188            strcpy(ptr, "1BB");
1189            break;
1190        case PT_2BUI: /* 2-bit unsigned integer   */
1191            strcpy(ptr, "2BUI");
1192            break;
1193        case PT_4BUI: /* 4-bit unsigned integer   */
1194            strcpy(ptr, "4BUI");
1195            break;
1196        case PT_8BSI: /* 8-bit signed integer     */
1197            strcpy(ptr, "8BSI");
1198            break;
1199        case PT_8BUI: /* 8-bit unsigned integer   */
1200            strcpy(ptr, "8BUI");
1201            break;
1202        case PT_16BSI:/* 16-bit signed integer    */
1203            strcpy(ptr, "16BSI");
1204            break;
1205        case PT_16BUI:/* 16-bit unsigned integer  */
1206            strcpy(ptr, "16BUI");
1207            break;
1208        case PT_32BSI:/* 32-bit signed integer    */
1209            strcpy(ptr, "32BSI");
1210            break;
1211        case PT_32BUI:/* 32-bit unsigned integer  */
1212            strcpy(ptr, "32BUI");
1213            break;
1214        case PT_32BF: /* 32-bit float             */
1215            strcpy(ptr, "32BF");
1216            break;
1217        case PT_64BF: /* 64-bit float             */
1218
1219            strcpy(ptr, "64BF");
1220            break;
1221        default:      /* PT_END=13 */
1222            elog(ERROR, "Invalid value of pixel type");
1223            pfree(result);
1224            PG_RETURN_NULL();
1225    }
1226
1227    size = VARHDRSZ + strlen(ptr);
1228    SET_VARSIZE(result, size);
1229
1230    PG_RETURN_TEXT_P(result);
1231}
1232
1233/**
1234 * Return true if the NODATA value of the specified band of raster is a true NODATA value.
1235 */
1236PG_FUNCTION_INFO_V1(RASTER_getBandHasNoDataValue);
1237Datum RASTER_getBandHasNoDataValue(PG_FUNCTION_ARGS)
1238{
1239    rt_pgraster *pgraster = NULL;
1240    rt_raster raster = NULL;
1241    rt_band band = NULL;
1242    rt_context ctx = NULL;
1243    bool hasnodata;
1244    int32_t index;
1245
1246    /* Index is 1-based */
1247    index = PG_GETARG_INT32(1);
1248    if ( index < 1 ) {
1249        elog(ERROR, "Invalid band index (must use 1-based)");
1250        PG_RETURN_NULL();
1251    }
1252    assert(0 <= (index - 1));
1253
1254    /* Deserialize raster */
1255    pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
1256    ctx = get_rt_context(fcinfo);
1257
1258    raster = rt_raster_deserialize(ctx, pgraster);
1259    if ( ! raster ) {
1260        elog(ERROR, "Could not deserialize raster");
1261        PG_RETURN_NULL();
1262    }
1263
1264    /* Fetch requested band and its NODATA value */
1265    band = rt_raster_get_band(ctx, raster, index - 1);
1266    if ( ! band ) {
1267        elog(NOTICE, "Could not find raster band of index %d. Returning NULL", index);
1268        PG_RETURN_NULL();
1269    }
1270
1271        hasnodata = (rt_band_get_hasnodata_flag(ctx, band)) ? TRUE : FALSE;
1272    PG_RETURN_BOOL(hasnodata);
1273}
1274
1275/**
1276 * Set the flag specifying if the NODATA value of the specified band of raster is a true NODATA value..
1277 */
1278PG_FUNCTION_INFO_V1(RASTER_setBandHasNoDataValue);
1279Datum RASTER_setBandHasNoDataValue(PG_FUNCTION_ARGS)
1280{
1281    rt_pgraster *pgraster = NULL;
1282    rt_raster raster = NULL;
1283    rt_band band = NULL;
1284    rt_context ctx = NULL;
1285    bool hasnodata;
1286    int32_t index;
1287
1288    /* Index is 1-based */
1289    index = PG_GETARG_INT32(1);
1290    if ( index < 1 ) {
1291        elog(ERROR, "Invalid band index (must use 1-based)");
1292        PG_RETURN_NULL();
1293    }
1294    assert(0 <= (index - 1));
1295
1296    /* Get the NODATA value */
1297    hasnodata = PG_GETARG_BOOL(2);
1298
1299    /* Deserialize raster */
1300    pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
1301    ctx = get_rt_context(fcinfo);
1302
1303    raster = rt_raster_deserialize(ctx, pgraster);
1304    if ( ! raster ) {
1305        elog(ERROR, "Could not deserialize raster");
1306        PG_RETURN_NULL();
1307    }
1308
1309    /* Fetch requested band */
1310    band = rt_raster_get_band(ctx, raster, index - 1);
1311    if ( ! band ) {
1312        elog(NOTICE, "Could not find raster band of index %d. Returning NULL", index);
1313        PG_RETURN_NULL();
1314    }
1315
1316    /* Set the band's NODATA value */
1317    rt_band_set_hasnodata_flag(ctx, band, hasnodata);
1318
1319    pgraster = rt_raster_serialize(ctx, raster);
1320    if ( ! pgraster ) PG_RETURN_NULL();
1321 
1322    SET_VARSIZE(pgraster, pgraster->size);
1323    PG_RETURN_POINTER(pgraster);
1324}
1325
1326
1327/**
1328 * Return NODATA value of the specified band of raster.
1329 * The value is always returned as FLOAT32 even if the pixel type is INTEGER.
1330 */
1331PG_FUNCTION_INFO_V1(RASTER_getBandNoDataValue);
1332Datum RASTER_getBandNoDataValue(PG_FUNCTION_ARGS)
1333{
1334    rt_pgraster *pgraster = NULL;
1335    rt_raster raster = NULL;
1336    rt_band band = NULL;
1337    rt_context ctx = NULL;
1338    double nodata;
1339    int32_t index;
1340
1341    /* Index is 1-based */
1342    index = PG_GETARG_INT32(1);
1343    if ( index < 1 ) {
1344        elog(ERROR, "Invalid band index (must use 1-based)");
1345        PG_RETURN_NULL();
1346    }
1347    assert(0 <= (index - 1));
1348
1349    /* Deserialize raster */
1350    pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
1351    ctx = get_rt_context(fcinfo);
1352
1353    raster = rt_raster_deserialize(ctx, pgraster);
1354    if ( ! raster ) {
1355        elog(ERROR, "Could not deserialize raster");
1356        PG_RETURN_NULL();
1357    }
1358
1359    /* Fetch requested band and its NODATA value */
1360    band = rt_raster_get_band(ctx, raster, index - 1);
1361    if ( ! band ) {
1362        elog(NOTICE, "Could not find raster band of index %d. Returning NULL", index);
1363        PG_RETURN_NULL();
1364    }
1365
1366    nodata = rt_band_get_nodata(ctx, band);
1367    PG_RETURN_FLOAT4(nodata);
1368}
1369
1370 
1371/**
1372 * Set the NODATA value of the specified band of raster.
1373 */
1374PG_FUNCTION_INFO_V1(RASTER_setBandNoDataValue);
1375Datum RASTER_setBandNoDataValue(PG_FUNCTION_ARGS)
1376{
1377    rt_pgraster *pgraster = NULL;
1378    rt_raster raster = NULL;
1379    rt_band band = NULL;
1380    rt_context ctx = NULL;
1381    double nodata;
1382    int32_t index;
1383
1384    /* Index is 1-based */
1385    index = PG_GETARG_INT32(1);
1386    if ( index < 1 ) {
1387        elog(ERROR, "Invalid band index (must use 1-based)");
1388        PG_RETURN_NULL();
1389    }
1390    assert(0 <= (index - 1));
1391
1392    /* Get the NODATA value */
1393    nodata = PG_GETARG_FLOAT8(2);
1394
1395    /* Deserialize raster */
1396    pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
1397    ctx = get_rt_context(fcinfo);
1398
1399    raster = rt_raster_deserialize(ctx, pgraster);
1400    if ( ! raster ) {
1401        elog(ERROR, "Could not deserialize raster");
1402        PG_RETURN_NULL();
1403    }
1404
1405    /* Fetch requested band */
1406    band = rt_raster_get_band(ctx, raster, index - 1);
1407    if ( ! band ) {
1408        elog(NOTICE, "Could not find raster band of index %d. Returning NULL", index);
1409        PG_RETURN_NULL();
1410    }
1411
1412    /* Set the band's NODATA value */
1413    rt_band_set_nodata(ctx, band, nodata);
1414
1415    pgraster = rt_raster_serialize(ctx, raster);
1416    if ( ! pgraster ) PG_RETURN_NULL();
1417 
1418    SET_VARSIZE(pgraster, pgraster->size);
1419    PG_RETURN_POINTER(pgraster);
1420}
1421
1422/**
1423 * Return the path of the raster for out-db raster.
1424 */
1425PG_FUNCTION_INFO_V1(RASTER_getBandPath);
1426Datum RASTER_getBandPath(PG_FUNCTION_ARGS)
1427{
1428    rt_pgraster *pgraster = NULL;
1429    rt_raster raster = NULL;
1430    rt_band band = NULL;
1431    rt_context ctx = NULL;
1432    int32_t index;
1433    const char *bandpath;
1434        text *result;
1435
1436    /* Index is 1-based */
1437    index = PG_GETARG_INT32(1);
1438    if ( index < 1 ) {
1439        elog(ERROR, "Invalid band index (must use 1-based)");
1440        PG_RETURN_NULL();
1441    }
1442    assert(0 <= (index - 1));
1443
1444    /* Deserialize raster */
1445    pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
1446    ctx = get_rt_context(fcinfo);
1447
1448    raster = rt_raster_deserialize(ctx, pgraster);
1449    if ( ! raster ) {
1450        elog(ERROR, "Could not deserialize raster");
1451        PG_RETURN_NULL();
1452    }
1453
1454    /* Fetch requested band and its NODATA value */
1455    band = rt_raster_get_band(ctx, raster, index - 1);
1456    if ( ! band ) {
1457        elog(NOTICE, "Could not find raster band of index %d. Returning NULL", index);
1458        PG_RETURN_NULL();
1459    }
1460
1461    bandpath = rt_band_get_ext_path(ctx, band);
1462    if ( ! bandpath )
1463    {
1464        PG_RETURN_NULL();
1465    }
1466
1467    result = (text *) palloc(VARHDRSZ + strlen(bandpath) + 1);
1468    if ( ! result ) {
1469        elog(ERROR, "Could not allocate memory for output text object");
1470        PG_RETURN_NULL();
1471    }
1472    SET_VARSIZE(result, VARHDRSZ + strlen(bandpath) + 1);
1473
1474    strcpy((char *) VARDATA(result), bandpath);
1475    PG_RETURN_TEXT_P(result);
1476}
1477
1478
1479/**
1480 * Return value of a single pixel.
1481 * Pixel location is specified by 1-based index of Nth band of raster and
1482 * X,Y coordinates (X <= RT_Width(raster) and Y <= RT_Height(raster)).
1483 *
1484 * TODO: Should we returen NUMERIC instead of FLOAT8 ?
1485 */
1486PG_FUNCTION_INFO_V1(RASTER_getPixelValue);
1487Datum RASTER_getPixelValue(PG_FUNCTION_ARGS)
1488{
1489    rt_pgraster *pgraster = NULL;
1490    rt_raster raster = NULL;
1491    rt_band band = NULL;
1492    rt_context ctx = NULL;
1493    double pixvalue = 0;
1494    int32_t nband = 0;
1495    int32_t x = 0;
1496    int32_t y = 0;
1497    int result = 0;
1498
1499    /* Index is 1-based */
1500    nband = PG_GETARG_INT32(1);
1501    if ( nband < 1 ) {
1502        elog(ERROR, "Invalid band index (must use 1-based)");
1503        PG_RETURN_NULL();
1504    }
1505    assert(0 <= (nband - 1));
1506
1507    /* Validate pixel coordinates are in range */
1508    x = PG_GETARG_INT32(2);
1509    y = PG_GETARG_INT32(3);
1510#ifdef POSTGIS_RASTER_PG_DEBUG
1511    elog(NOTICE, "Pixel coordinates (%d, %d)", x, y);
1512#endif
1513    /* Deserialize raster */
1514    pgraster = (rt_pgraster *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
1515    ctx = get_rt_context(fcinfo);
1516
1517    raster = rt_raster_deserialize(ctx, pgraster);
1518    if ( ! raster ) {
1519        elog(ERROR, "Could not deserialize raster");
1520        PG_RETURN_NULL();
1521    }
1522
1523    /* Fetch Nth band using 0-based internal index */
1524    band = rt_raster_get_band(ctx, raster, nband - 1);
1525    if ( ! band ) {
1526        elog(NOTICE, "Could not find raster band of index %d. Returning NULL", nband);
1527        PG_RETURN_NULL();
1528    }
1529
1530    /* Fetch pixel using 0-based coordiantes */
1531    result = rt_band_get_pixel(ctx, band, x - 1, y - 1, &pixvalue);
1532    if ( result == -1 ) PG_RETURN_NULL();
1533    PG_RETURN_FLOAT8(pixvalue);
1534}
1535
1536/**
1537 * Write value of raster sample on given position and in specified band.
1538 */
1539PG_FUNCTION_INFO_V1(RASTER_setPixelValue);
1540Datum RASTER_setPixelValue(PG_FUNCTION_ARGS)
1541{
1542    rt_pgraster *pgraster = NULL;
1543    rt_raster raster = NULL;
1544    rt_band band = NULL;
1545    rt_context ctx = NULL;
1546    double pixvalue = 0;
1547    int32_t nband = 0;
1548    int32_t x = 0;
1549    int32_t y = 0;
1550
1551    /* nband is 1-based */
1552    nband = PG_GETARG_INT32(1);
1553    if ( nband < 1 ) {
1554        elog(ERROR, "Invalid band index (must use 1-based)");
1555        PG_RETURN_NULL();
1556    }
1557    assert(0 <= (nband - 1));
1558
1559    /* Validate pixel coordinates are in range */
1560    x = PG_GETARG_INT32(2);
1561    y = PG_GETARG_INT32(3);
1562#ifdef POSTGIS_RASTER_PG_DEBUG
1563    elog(NOTICE, "Pixel coordinates (%d, %d)", x, y);
1564#endif
1565
1566    /* Get the pixel value */
1567    pixvalue = PG_GETARG_FLOAT8(4);
1568
1569    /* Deserialize raster */
1570    pgraster = (rt_pgraster *)PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0));
1571    ctx = get_rt_context(fcinfo);
1572
1573    raster = rt_raster_deserialize(ctx, pgraster);
1574    if ( ! raster ) {
1575        elog(ERROR, "Could not deserialize raster");
1576        PG_RETURN_NULL();
1577    }
1578
1579    /* Fetch requested band */
1580    band = rt_raster_get_band(ctx, raster, nband - 1);
1581    if ( ! band ) {
1582        elog(NOTICE, "Could not find raster band of index %d. Returning NULL", nband);
1583        PG_RETURN_NULL();
1584    }
1585
1586    /* Set the band's pixel value */
1587    rt_band_set_pixel(ctx, band, x - 1, y - 1, pixvalue);
1588
1589    pgraster = rt_raster_serialize(ctx, raster);
1590    if ( ! pgraster ) PG_RETURN_NULL();
1591 
1592    SET_VARSIZE(pgraster, pgraster->size);
1593    PG_RETURN_POINTER(pgraster);
1594}
1595
1596/**
1597 * Add a band to the given raster at the given position.
1598 */
1599PG_FUNCTION_INFO_V1(RASTER_addband);
1600Datum RASTER_addband(PG_FUNCTION_ARGS)
1601{
1602    rt_pgraster *pgraster = NULL;
1603    rt_raster raster = NULL;
1604    rt_band band = NULL;
1605    rt_context ctx = NULL;
1606   
1607    int index = 0;
1608    double initialvalue = 0;
1609    double nodatavalue = 0;
1610    bool hasnodata = FALSE;
1611
1612    text *pixeltypename = NULL;
1613    char *new_pixeltypename = NULL;
1614
1615    int pixtype = PT_END;
1616    int width = 0;
1617    int height = 0;
1618    int32_t oldnumbands = 0;
1619    int32_t numbands = 0;
1620   
1621    int datasize = 0;
1622    int numval = 0;
1623    void *mem;
1624    int32_t checkvalint = 0;
1625    uint32_t checkvaluint = 0;
1626    double checkvaldouble = 0;
1627    float checkvalfloat = 0;
1628    int i;
1629   
1630    /* Get the initial pixel value */
1631    if (PG_ARGISNULL(3))
1632        initialvalue = 0;
1633    else
1634        initialvalue = PG_GETARG_FLOAT8(3);
1635   
1636    /* Get the nodata value */
1637    if (PG_ARGISNULL(4))
1638        nodatavalue = 0;
1639    else
1640    {
1641        nodatavalue = PG_GETARG_FLOAT8(4);
1642        hasnodata = TRUE;
1643    }
1644   
1645    /* Deserialize raster */
1646    pgraster = (rt_pgraster *)PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0));
1647    ctx = get_rt_context(fcinfo);
1648
1649    /* Get the pixel type in text form */
1650    if (PG_ARGISNULL(2)) {
1651        elog(ERROR, "Pixel type can not be null");
1652        PG_RETURN_NULL();
1653    }
1654     
1655    pixeltypename = PG_GETARG_TEXT_P(2);
1656    new_pixeltypename = (char *) palloc(VARSIZE(pixeltypename) + 1 - VARHDRSZ);
1657    SET_VARSIZE(new_pixeltypename, VARSIZE(pixeltypename));
1658    memcpy(new_pixeltypename, VARDATA(pixeltypename), VARSIZE(pixeltypename) - VARHDRSZ);
1659    new_pixeltypename[VARSIZE(pixeltypename) - VARHDRSZ] = 0; /* null terminate */
1660
1661    /* Get the pixel type index */
1662    pixtype = rt_pixtype_index_from_name(ctx, new_pixeltypename);
1663    if ( pixtype == PT_END ) {
1664        elog(ERROR, "Invalid pixel type: %s", new_pixeltypename);
1665        PG_RETURN_NULL();
1666    }
1667    assert(-1 < pixtype && pixtype < PT_END);
1668
1669    raster = rt_raster_deserialize(ctx, pgraster);
1670    if ( ! raster ) {
1671        elog(ERROR, "Could not deserialize raster");
1672        PG_RETURN_NULL();
1673    }
1674   
1675    /* Make sure index (1 based) is in a valid range */
1676    oldnumbands = rt_raster_get_num_bands(ctx, raster);
1677    if (PG_ARGISNULL(1))
1678        index = oldnumbands + 1;
1679    else
1680    {
1681        index = PG_GETARG_UINT16(1);
1682        if (index < 1) {
1683            elog(ERROR, "Invalid band index (must be 1-based)");
1684            PG_RETURN_NULL();
1685        }
1686        if (index > oldnumbands + 1) {   
1687            elog(WARNING, "Band index number exceed possible values, truncated to number of band (%u) + 1", oldnumbands);
1688            index = oldnumbands + 1;
1689        }
1690    }
1691   
1692    /* Determine size of memory block to allocate and allocate*/
1693    width = rt_raster_get_width(ctx, raster);
1694    height = rt_raster_get_height(ctx, raster);
1695    numval = width * height;
1696    datasize = rt_pixtype_size(ctx, pixtype) * numval;
1697   
1698   
1699    mem = palloc((int) datasize);
1700    if (!mem)
1701    {
1702        elog(ERROR, "Could not allocate memory for band");
1703        PG_RETURN_NULL();
1704    }
1705       
1706    /* Initialize block of memory to 0 or to the provided initial value */
1707    if (initialvalue == 0)
1708        memset(mem, 0, datasize);
1709    else
1710    {
1711        switch (pixtype)
1712        {
1713            case PT_1BB:
1714            {
1715                uint8_t *ptr = mem;
1716                for (i = 0; i < numval; i++)
1717                    ptr[i] = (uint8_t) initialvalue&0x01;
1718                checkvalint = ptr[0];
1719                break;
1720            }
1721            case PT_2BUI:
1722            {
1723                uint8_t *ptr = mem;
1724                for (i = 0; i < numval; i++)
1725                    ptr[i] = (uint8_t) initialvalue&0x03;
1726                checkvalint = ptr[0];
1727                break;
1728            }
1729            case PT_4BUI:
1730            {
1731                uint8_t *ptr = mem;
1732                for (i = 0; i < numval; i++)
1733                    ptr[i] = (uint8_t) initialvalue&0x0F;
1734                checkvalint = ptr[0];
1735                break;
1736            }
1737            case PT_8BSI:
1738            {
1739                int8_t *ptr = mem;
1740                for (i = 0; i < numval; i++)
1741                    ptr[i] = (int8_t) initialvalue;
1742                checkvalint = ptr[0];
1743                break;
1744            }
1745            case PT_8BUI:
1746            {
1747                uint8_t *ptr = mem;
1748                for (i = 0; i < numval; i++)
1749                    ptr[i] = (uint8_t) initialvalue;
1750                checkvalint = ptr[0];
1751                break;
1752            }
1753            case PT_16BSI:
1754            {
1755                int16_t *ptr = mem;
1756                for (i = 0; i < numval; i++)
1757                    ptr[i] = (int16_t) initialvalue;
1758                checkvalint = ptr[0];
1759                break;
1760            }
1761            case PT_16BUI:
1762            {
1763                uint16_t *ptr = mem;
1764                for (i = 0; i < numval; i++)
1765                    ptr[i] = (uint16_t) initialvalue;
1766                checkvalint = ptr[0];
1767                break;
1768            }
1769            case PT_32BSI:
1770            {
1771                int32_t *ptr = mem;
1772                for (i = 0; i < numval; i++)
1773                    ptr[i] = (int32_t) initialvalue;
1774                checkvalint = ptr[0];
1775                break;
1776            }
1777            case PT_32BUI:
1778            {
1779                uint32_t *ptr = mem;
1780                for (i = 0; i < numval; i++)
1781                    ptr[i] = (uint32_t) initialvalue;
1782                checkvaluint = ptr[0];
1783                break;
1784            }
1785            case PT_32BF:
1786            {
1787                float *ptr = mem;
1788                for (i = 0; i < numval; i++)
1789                    ptr[i] = (float) initialvalue;
1790                checkvalfloat = ptr[0];
1791                break;
1792            }
1793            case PT_64BF:
1794            {
1795                double *ptr = mem;
1796                for (i = 0; i < numval; i++)
1797                    ptr[i] = initialvalue;
1798                checkvaldouble = ptr[0];
1799                break;
1800            }
1801            default:
1802            {
1803                elog(ERROR, "Unknown pixeltype %d", pixtype);
1804                PG_RETURN_NULL();
1805            }
1806        }
1807    }
1808
1809#ifdef POSTGIS_RASTER_WARN_ON_TRUNCATION
1810    /* Overflow checking */
1811    switch (pixtype)
1812    {
1813        case PT_1BB:
1814        case PT_2BUI:
1815        case PT_4BUI:
1816        case PT_8BSI:
1817        case PT_8BUI:
1818        case PT_16BSI:
1819        case PT_16BUI:
1820        case PT_32BSI:
1821        {
1822            if (fabs(checkvalint - initialvalue) > 0.0001)
1823                elog(WARNING, "Initial pixel value for %s band got truncated from %f to %d",
1824                    rt_pixtype_name(ctx, pixtype),
1825                    initialvalue, checkvalint);
1826            break;
1827        }
1828        case PT_32BUI:
1829        {
1830            if (fabs(checkvaluint - initialvalue) > 0.0001)
1831                elog(WARNING, "Initial pixel value for %s band got truncated from %f to %u",
1832                    rt_pixtype_name(ctx, pixtype),
1833                    initialvalue, checkvaluint);
1834            break;
1835        }
1836        case PT_32BF:
1837        {
1838            /* For float, because the initial value is a double,
1839            there is very often a difference between the desired value and the obtained one */
1840            if (checkvalfloat != initialvalue)
1841                elog(WARNING, "Initial pixel value for %s band got truncated from %f to %g",
1842                    rt_pixtype_name(ctx, pixtype),
1843                    initialvalue, checkvalfloat);
1844            break;
1845        }
1846        case PT_64BF:
1847        {
1848            if (checkvaldouble != initialvalue)
1849                elog(WARNING, "Initial pixel value for %s band got truncated from %f to %g",
1850                    rt_pixtype_name(ctx, pixtype),
1851                    initialvalue, checkvaldouble);
1852            break;
1853        }
1854    }
1855#endif /* POSTGIS_RASTER_WARN_ON_TRUNCATION */
1856
1857    band = rt_band_new_inline(ctx, width, height, pixtype, hasnodata, nodatavalue, mem);
1858    if (! band) {
1859        elog(ERROR, "Could not add band to raster. Returning NULL");
1860        PG_RETURN_NULL();
1861    }
1862    index = rt_raster_add_band(ctx, raster, band, index - 1);
1863    numbands = rt_raster_get_num_bands(ctx, raster);
1864    if (numbands == oldnumbands || index == -1) {
1865        elog(ERROR, "Could not add band to raster. Returning NULL");
1866        PG_RETURN_NULL();
1867    }
1868    pgraster = rt_raster_serialize(ctx, raster);
1869    if (!pgraster) PG_RETURN_NULL();
1870
1871    SET_VARSIZE(pgraster, pgraster->size);
1872    PG_RETURN_POINTER(pgraster);
1873}
1874
1875/* ---------------------------------------------------------------- */
1876/*  Memory allocation / error reporting hooks                       */
1877/* ---------------------------------------------------------------- */
1878
1879static void *
1880rt_pg_alloc(size_t size)
1881{
1882        void * result;
1883
1884        result = palloc(size);
1885
1886        if ( ! result )
1887        {
1888                ereport(ERROR, (errmsg_internal("Out of virtual memory")));
1889                return NULL;
1890        }
1891        return result;
1892}
1893
1894static void *
1895rt_pg_realloc(void *mem, size_t size)
1896{
1897        void * result;
1898
1899        result = repalloc(mem, size);
1900
1901        return result;
1902}
1903
1904static void
1905rt_pg_free(void *ptr)
1906{
1907        pfree(ptr);
1908}
1909
1910static void
1911rt_pg_error(const char *fmt, va_list ap)
1912{
1913#define ERRMSG_MAXLEN 256
1914
1915        char errmsg[ERRMSG_MAXLEN+1];
1916
1917        vsnprintf (errmsg, ERRMSG_MAXLEN, fmt, ap);
1918
1919        errmsg[ERRMSG_MAXLEN]='\0';
1920        ereport(ERROR, (errmsg_internal("%s", errmsg)));
1921}
1922
1923static void
1924rt_pg_notice(const char *fmt, va_list ap)
1925{
1926        char *msg;
1927
1928        /*
1929         * This is a GNU extension.
1930         * Dunno how to handle errors here.
1931         */
1932        if (!lw_vasprintf (&msg, fmt, ap))
1933        {
1934                va_end (ap);
1935                return;
1936        }
1937        ereport(NOTICE, (errmsg_internal("%s", msg)));
1938        free(msg);
1939}
1940
1941
1942/* This is needed by liblwgeom */
1943void
1944lwgeom_init_allocators(void)
1945{
1946    /* liblwgeom callback - install PostgreSQL handlers */
1947    lwalloc_var = rt_pg_alloc;
1948    lwrealloc_var = rt_pg_realloc;
1949    lwfree_var = rt_pg_free;
1950
1951    lwerror_var = rt_pg_error;
1952    lwnotice_var = rt_pg_notice;
1953}
1954
1955/* ---------------------------------------------------------------- */
1956/*  Memory allocation / error reporting hooks                       */
1957/* ---------------------------------------------------------------- */
Note: See TracBrowser for help on using the browser.