[postgis-commits] svn - r3612 - spike/wktraster/rt_core
postgis-commits at postgis.refractions.net
postgis-commits at postgis.refractions.net
Mon Feb 2 09:16:00 PST 2009
Author: strk
Date: 2009-02-02 09:16:00 -0800 (Mon, 02 Feb 2009)
New Revision: 3612
Modified:
spike/wktraster/rt_core/Makefile.in
spike/wktraster/rt_core/rt_api.c
spike/wktraster/rt_core/rt_api.h
spike/wktraster/rt_core/testwkb.c
Log:
Add deserializer and wkb/hexwkb output, tests for it
Modified: spike/wktraster/rt_core/Makefile.in
===================================================================
--- spike/wktraster/rt_core/Makefile.in 2009-02-02 14:53:36 UTC (rev 3611)
+++ spike/wktraster/rt_core/Makefile.in 2009-02-02 17:16:00 UTC (rev 3612)
@@ -23,7 +23,7 @@
all: librtcore.a
clean:
- rm -f testapi $(RT_OBJS)
+ rm -f testapi testwkb $(RT_OBJS)
rm -f librtcore.a
testapi: testapi.c rt_api.h rt_api.c check.h
Modified: spike/wktraster/rt_core/rt_api.c
===================================================================
--- spike/wktraster/rt_core/rt_api.c 2009-02-02 14:53:36 UTC (rev 3611)
+++ spike/wktraster/rt_core/rt_api.c 2009-02-02 17:16:00 UTC (rev 3612)
@@ -1244,12 +1244,24 @@
return *(*from)++;
}
+static void
+write_uint8(uint8_t** from, uint8_t v)
+{
+ *(*from)++ = v;
+}
+
static int8_t
read_int8(const uint8_t** from)
{
return (int8_t)read_uint8(from);
}
+static void
+write_int8(uint8_t** from, int8_t v)
+{
+ *(*from)++ = v;
+}
+
static uint16_t
read_uint16(const uint8_t** from, uint8_t littleEndian)
{
@@ -1269,12 +1281,44 @@
return ret;
}
+static void
+write_uint16(uint8_t** to, uint8_t littleEndian, uint16_t v)
+{
+ if ( littleEndian )
+ {
+ (*to)[0] = v & 0x00FF;
+ (*to)[1] = v >> 8;
+ }
+ else
+ {
+ (*to)[1] = v & 0x00FF;
+ (*to)[0] = v >> 8;
+ }
+ *to += 2;
+}
+
static int16_t
read_int16(const uint8_t** from, uint8_t littleEndian)
{
return read_uint16(from, littleEndian);
}
+static void
+write_int16(uint8_t** to, uint8_t littleEndian, int16_t v)
+{
+ if ( littleEndian )
+ {
+ (*to)[0] = v & 0x00FF;
+ (*to)[1] = v >> 8;
+ }
+ else
+ {
+ (*to)[1] = v & 0x00FF;
+ (*to)[0] = v >> 8;
+ }
+ *to += 2;
+}
+
static uint32_t
read_uint32(const uint8_t** from, uint8_t littleEndian)
{
@@ -1300,12 +1344,52 @@
return ret;
}
+static void
+write_uint32(uint8_t** to, uint8_t littleEndian, uint32_t v)
+{
+ if ( littleEndian )
+ {
+ (*to)[0] = v & 0x000000FF;
+ (*to)[1] = ( v & 0x0000FF00 ) >> 8;
+ (*to)[2] = ( v & 0x00FF0000 ) >> 16;
+ (*to)[3] = ( v & 0xFF000000 ) >> 24;
+ }
+ else
+ {
+ (*to)[3] = v & 0x000000FF;
+ (*to)[2] = ( v & 0x0000FF00 ) >> 8;
+ (*to)[1] = ( v & 0x00FF0000 ) >> 16;
+ (*to)[0] = ( v & 0xFF000000 ) >> 24;
+ }
+ *to += 4;
+}
+
static int32_t
read_int32(const uint8_t** from, uint8_t littleEndian)
{
return read_uint32(from, littleEndian);
}
+static void
+write_int32(uint8_t** to, uint8_t littleEndian, int32_t v)
+{
+ if ( littleEndian )
+ {
+ (*to)[0] = v & 0x000000FF;
+ (*to)[1] = ( v & 0x0000FF00 ) >> 8;
+ (*to)[2] = ( v & 0x00FF0000 ) >> 16;
+ (*to)[3] = ( v & 0xFF000000 ) >> 24;
+ }
+ else
+ {
+ (*to)[3] = v & 0x000000FF;
+ (*to)[2] = ( v & 0x0000FF00 ) >> 8;
+ (*to)[1] = ( v & 0x00FF0000 ) >> 16;
+ (*to)[0] = ( v & 0xFF000000 ) >> 24;
+ }
+ *to += 4;
+}
+
static float
read_float32(const uint8_t** from, uint8_t littleEndian)
{
@@ -1319,6 +1403,18 @@
return ret.f;
}
+static void
+write_float32(uint8_t** from, uint8_t littleEndian, float f)
+{
+ union {
+ float f;
+ uint32_t i;
+ } u;
+
+ u.f = f;
+ write_uint32(from, littleEndian, u.i);
+}
+
static double
read_float64(const uint8_t** from, uint8_t littleEndian)
{
@@ -1355,6 +1451,41 @@
return ret.d;
}
+static void
+write_float64(uint8_t** to, uint8_t littleEndian, double v)
+{
+ union {
+ double d;
+ uint64_t i;
+ } u;
+
+ u.d = v;
+
+ if ( littleEndian )
+ {
+ (*to)[0] = u.i & 0x00000000000000FF;
+ (*to)[1] = ( u.i & 0x000000000000FF00 ) >> 8;
+ (*to)[2] = ( u.i & 0x0000000000FF0000 ) >> 16;
+ (*to)[3] = ( u.i & 0x00000000FF000000 ) >> 24;
+ (*to)[4] = ( u.i & 0x000000FF00000000 ) >> 32;
+ (*to)[5] = ( u.i & 0x0000FF0000000000 ) >> 40;
+ (*to)[6] = ( u.i & 0x00FF000000000000 ) >> 48;
+ (*to)[7] = ( u.i & 0xFF00000000000000 ) >> 56;
+ }
+ else
+ {
+ (*to)[7] = u.i & 0x00000000000000FF;
+ (*to)[6] = ( u.i & 0x000000000000FF00 ) >> 8;
+ (*to)[5] = ( u.i & 0x0000000000FF0000 ) >> 16;
+ (*to)[4] = ( u.i & 0x00000000FF000000 ) >> 24;
+ (*to)[3] = ( u.i & 0x000000FF00000000 ) >> 32;
+ (*to)[2] = ( u.i & 0x0000FF0000000000 ) >> 40;
+ (*to)[1] = ( u.i & 0x00FF000000000000 ) >> 48;
+ (*to)[0] = ( u.i & 0xFF00000000000000 ) >> 56;
+ }
+ *to += 8;
+}
+
#define BANDTYPE_FLAGS_MASK 0xF0
#define BANDTYPE_PIXTYPE_MASK 0x0F
#define BANDTYPE_FLAG_OFFDB (1<<7)
@@ -1416,7 +1547,6 @@
}
/* Read nodata value */
-
switch (band->pixtype)
{
default:
@@ -1616,6 +1746,11 @@
}
rast = (rt_raster)ctx->alloc(sizeof(struct rt_raster_t));
+ if ( ! rast )
+ {
+ ctx->err("Out of memory allocating raster for wkb input");
+ return 0;
+ }
rast->numBands = read_uint16(&ptr, endian);
rast->scaleX = read_float64(&ptr, endian);
rast->scaleY = read_float64(&ptr, endian);
@@ -1728,12 +1863,242 @@
for (i=0; i<wkbsize; ++i) /* parse full hex */
{
- wkb[i] = parse_hex(&(hexwkb[i*2]));
+ wkb[i] = parse_hex((char*)&(hexwkb[i*2]));
}
- return rt_raster_from_wkb(ctx, wkb, wkbsize);
+ rt_raster r = rt_raster_from_wkb(ctx, wkb, wkbsize);
+
+ //ctx->dealloc(wkb); /* as long as rt_raster_from_wkb copies memory */
+
+ return r;
}
+static uint32_t
+rt_raster_wkb_size(rt_context ctx, rt_raster raster)
+{
+ uint32_t size = RT_WKB_HDR_SZ;
+ uint16_t i;
+
+ for (i=0; i<raster->numBands; ++i)
+ {
+ rt_band band = raster->bands[i];
+ rt_pixtype pixtype = band->pixtype;
+ int pixbytes = rt_pixtype_size(ctx, pixtype);
+
+ if ( pixbytes < 1 ) {
+ // ERROR !
+ ctx->err("Corrupted band: unkonwn pixtype");
+ return 0;
+ }
+
+ /* Add space for band type */
+ size += 1;
+
+ /* Add space for nodata value */
+ size += pixbytes;
+
+ if ( band->offline )
+ {
+ /* Add space for band number */
+ size += 1;
+
+ /* Add space for null-terminated path */
+ size += strlen(band->data.offline.path)+1;
+ }
+ else
+ {
+ /* Add space for actual data */
+ size += pixbytes*raster->width*raster->height;
+ }
+
+ }
+
+ return size;
+}
+
+uint8_t *
+rt_raster_to_wkb(rt_context ctx, rt_raster raster, uint32_t *wkbsize)
+{
+ uint8_t *wkb, *ptr;
+ uint16_t i;
+ uint8_t littleEndian = isMachineLittleEndian();
+
+ *wkbsize = rt_raster_wkb_size(ctx, raster);
+ wkb = (uint8_t*)ctx->alloc(*wkbsize);
+ if ( ! wkb )
+ {
+ ctx->err("Out of memory allocating WKB for raster");
+ return 0;
+ }
+
+ ptr = wkb;
+
+ /* Write endianness */
+ *ptr = littleEndian; ptr+=1;
+
+ /* Write version */
+ write_uint16(&ptr, littleEndian, 0);
+
+ /* Copy header (from numBands up) */
+ memcpy(ptr, &(raster->numBands), sizeof(struct rt_raster_serialized_t)-6);
+ ptr += sizeof(struct rt_raster_serialized_t)-6;
+
+ /* Serialize bands now */
+ for (i=0; i<raster->numBands; ++i)
+ {
+ rt_band band = raster->bands[i];
+ rt_pixtype pixtype = band->pixtype;
+ int pixbytes = rt_pixtype_size(ctx, pixtype);
+
+ ctx->info("Writing WKB for band %d", i);
+
+ if ( pixbytes < 1 ) {
+ // ERROR !
+ ctx->err("Corrupted band: unkonwn pixtype");
+ return 0;
+ }
+
+ /* Add band type */
+ *ptr = band->pixtype;
+ if ( band->offline ) *ptr |= BANDTYPE_FLAG_OFFDB;
+ ptr += 1;
+
+#if 0 // no padding required for WKB
+ /* Add padding (if needed) */
+ if ( pixbytes > 1 ) {
+ memset(ptr, '\0', pixbytes-1);
+ ptr += pixbytes-1;
+ }
+ /* Consistency checking (ptr is pixbytes-aligned) */
+ assert(! (((uint64_t)ptr)%pixbytes) );
+#endif
+
+ /* Add nodata value */
+ switch (pixtype)
+ {
+ default:
+ abort(); /* shoudn't happen */
+ return 0;
+ case PT_1BB:
+ case PT_2BUI:
+ case PT_4BUI:
+ case PT_8BUI:
+ {
+ uint8_t v = band->nodataval;
+ *ptr = v; ptr+=1;
+ break;
+ }
+ case PT_8BSI:
+ {
+ int8_t v = band->nodataval;
+ *ptr = v; ptr+=1;
+ break;
+ }
+
+
+ case PT_16BSI:
+ case PT_16BUI:
+ {
+ uint16_t v = band->nodataval;
+ memcpy(ptr, &v, 2); ptr+=2;
+ break;
+ }
+
+ case PT_32BSI:
+ case PT_32BUI:
+ {
+ uint32_t v = band->nodataval;
+ memcpy(ptr, &v, 4); ptr+=4;
+ break;
+ }
+
+ case PT_16BF:
+ ctx->err("16BF pixel type not supported yet");
+ break;
+
+ case PT_32BF:
+ {
+ float v = band->nodataval;
+ memcpy(ptr, &v, 4); ptr+=4;
+ break;
+ }
+
+ case PT_64BF:
+ {
+ memcpy(ptr, &band->nodataval, 8); ptr+=8;
+ break;
+ }
+ }
+
+#if 0 // no padding for WKB
+ /* Consistency checking (ptr is pixbytes-aligned) */
+ assert(! ((uint64_t)ptr%pixbytes) );
+#endif
+
+ if ( band->offline )
+ {
+ /* Write band number */
+ *ptr = band->data.offline.bandNum; ptr += 1;
+
+ /* Write path */
+ strcpy((char*)ptr, band->data.offline.path);
+ ptr += strlen(band->data.offline.path)+1;
+ }
+ else
+ {
+ /* Write data */
+ {
+ uint32_t datasize = raster->width*raster->height*pixbytes;
+ memcpy(ptr, band->data.mem, datasize);
+ ptr += datasize;
+ }
+ }
+
+#if 0 // no padding for WKB
+ /* Pad up to 8-bytes boundary */
+ while ( (uint64_t)ptr%8 ) {
+ *ptr = 0; ++ptr;
+ }
+
+ /* Consistency checking (ptr is pixbytes-aligned) */
+ assert(! ((uint64_t)ptr%pixbytes) );
+#endif
+ }
+
+ return wkb;
+}
+
+char *
+rt_raster_to_hexwkb(rt_context ctx, rt_raster raster, uint32_t *hexwkbsize)
+{
+ uint8_t *wkb;
+ char* hexwkb;
+ uint32_t i;
+ uint32_t wkbsize;
+
+ wkb = rt_raster_to_wkb(ctx, raster, &wkbsize);
+
+ *hexwkbsize = wkbsize*2; /* hex is 2 times bytes */
+ hexwkb = (char*)ctx->alloc( (*hexwkbsize)+1);
+ if ( ! hexwkb )
+ {
+ ctx->dealloc(wkb);
+ ctx->err("Out of memory hexifying raster WKB");
+ return 0;
+ }
+ hexwkb[*hexwkbsize] = '\0'; /* Null-terminate */
+
+ for (i=0; i<wkbsize; i++)
+ {
+ deparse_hex(wkb[i], &(hexwkb[2*i]));
+ }
+
+ ctx->dealloc(wkb); /* we don't need this anymore */
+
+ return hexwkb;
+}
+
+
/*--------- Serializer/Deserializer --------------------------------------*/
static uint32_t
@@ -1928,3 +2293,171 @@
return ret;
}
+rt_raster
+rt_raster_deserialize(rt_context ctx, void* serialized)
+{
+ rt_raster rast;
+ uint16_t i;
+ const uint8_t *ptr;
+ uint8_t littleEndian = isMachineLittleEndian();
+
+ rast = (rt_raster)ctx->alloc(sizeof(struct rt_raster_t));
+ if ( ! rast )
+ {
+ ctx->err("Out of memory allocating raster for deserialization");
+ return 0;
+ }
+
+ memcpy(rast, serialized, sizeof(struct rt_raster_serialized_t));
+
+ if ( ! rast->numBands )
+ {
+ rast->bands = 0;
+ return rast;
+ }
+
+ rast->bands = ctx->alloc(rast->numBands*sizeof(rt_band));
+
+ /* Deserialize bands now */
+ ptr = (uint8_t*)serialized+sizeof(struct rt_raster_serialized_t);
+ for (i=0; i<rast->numBands; ++i)
+ {
+ rt_band band;
+ uint8_t type;
+ int pixbytes;
+
+ band = ctx->alloc(sizeof(struct rt_band_t));
+ if ( ! band )
+ {
+ ctx->err("Out of memory allocating rt_band during deserialization");
+ return 0;
+ }
+
+ type = *ptr; ptr++;
+ band->pixtype = type&BANDTYPE_PIXTYPE_MASK;
+ band->offline = BANDTYPE_IS_OFFDB(type) ? 1 : 0;
+ band->width = rast->width;
+ band->height = rast->height;
+ band->ownsData = 0;
+
+ /* Advance by data padding */
+ pixbytes = rt_pixtype_size(ctx, band->pixtype);
+ ptr += pixbytes-1;
+
+ /* Read nodata value */
+ switch (band->pixtype)
+ {
+ default:
+ {
+ ctx->err("Unknown pixeltype %d", band->pixtype);
+ ctx->dealloc(band);
+ ctx->dealloc(rast);
+ return 0;
+ }
+
+ case PT_1BB:
+ {
+ band->nodataval = ((int)read_uint8(&ptr)) & 0x01;
+ break;
+ }
+
+ case PT_2BUI:
+ {
+ band->nodataval = ((int)read_uint8(&ptr)) & 0x03;
+ break;
+ }
+
+ case PT_4BUI:
+ {
+ band->nodataval = ((int)read_uint8(&ptr)) & 0x0F;
+ break;
+ }
+
+ case PT_8BSI:
+ {
+ band->nodataval = read_int8(&ptr);
+ break;
+ }
+
+ case PT_8BUI:
+ {
+ band->nodataval = read_uint8(&ptr);
+ break;
+ }
+
+ case PT_16BSI:
+ {
+ band->nodataval = read_int16(&ptr, littleEndian);
+ break;
+ }
+
+ case PT_16BUI:
+ {
+ band->nodataval = read_uint16(&ptr, littleEndian);
+ break;
+ }
+
+ case PT_16BF:
+ {
+ ctx->err("16BF pixel type not supported yet");
+ ctx->dealloc(band);
+ return 0;
+ }
+
+ case PT_32BSI:
+ {
+ band->nodataval = read_int32(&ptr, littleEndian);
+ break;
+ }
+
+ case PT_32BUI:
+ {
+ band->nodataval = read_uint32(&ptr, littleEndian);
+ break;
+ }
+
+ case PT_32BF:
+ {
+ band->nodataval = read_float32(&ptr, littleEndian);
+ break;
+ }
+
+ case PT_64BF:
+ {
+ band->nodataval = read_float64(&ptr, littleEndian);
+ break;
+ }
+ }
+
+ /* Consistency checking (ptr is pixbytes-aligned) */
+ assert(! (((uint64_t)ptr)%pixbytes) );
+
+ if ( band->offline )
+ {
+ /* Read band number */
+ band->data.offline.bandNum = *ptr; ptr += 1;
+
+ /* Register path */
+ band->data.offline.path = (char*)ptr;
+ ptr += strlen(band->data.offline.path)+1;
+ }
+ else
+ {
+ /* Register data */
+ {
+ uint32_t datasize = rast->width*rast->height*pixbytes;
+ band->data.mem = (uint8_t*)ptr;
+ ptr += datasize;
+ }
+ }
+
+ /* Pad up to 8-bytes boundary */
+ ptr += ( 8 - ((uint64_t)ptr%8) );
+
+ /* Consistency checking (ptr is pixbytes-aligned) */
+ assert(! ((uint64_t)ptr%pixbytes) );
+ }
+
+ return rast;
+}
+
Modified: spike/wktraster/rt_core/rt_api.h
===================================================================
--- spike/wktraster/rt_core/rt_api.h 2009-02-02 14:53:36 UTC (rev 3611)
+++ spike/wktraster/rt_core/rt_api.h 2009-02-02 17:16:00 UTC (rev 3612)
@@ -257,6 +257,27 @@
uint32_t hexwkbsize);
/**
+ * Return this raster in WKB form
+ *
+ * @param ctx : context, for thread safety
+ * @param raster : the raster
+ * @param wkbsize : will be set to the size of returned wkb form
+ */
+uint8_t *rt_raster_to_wkb(rt_context ctx, rt_raster raster,
+ uint32_t *wkbsize);
+
+/**
+ * Return this raster in HEXWKB form (null-terminated hex)
+ *
+ * @param ctx : context, for thread safety
+ * @param raster : the raster
+ * @param hexwkbsize : will be set to the size of returned wkb form,
+ * not including the null termination
+ */
+char *rt_raster_to_hexwkb(rt_context ctx, rt_raster raster,
+ uint32_t *hexwkbsize);
+
+/**
* Release memory associated to a raster
*
* Note that this will not release data
@@ -443,4 +464,14 @@
*/
void* rt_raster_serialize(rt_context ctx, rt_raster raster);
+/**
+ * Return a raster from a serialized form.
+ *
+ * Serialized form is documented in doc/RFC1-SerializedFormat.
+ *
+ * NOTE: the raster will contain pointer to the serialized
+ * form, which must be kept alive.
+ */
+rt_raster rt_raster_deserialize(rt_context ctx, void* serialized);
+
#endif /* RT_API_H */
Modified: spike/wktraster/rt_core/testwkb.c
===================================================================
--- spike/wktraster/rt_core/testwkb.c 2009-02-02 14:53:36 UTC (rev 3611)
+++ spike/wktraster/rt_core/testwkb.c 2009-02-02 17:16:00 UTC (rev 3612)
@@ -14,7 +14,8 @@
/* will use default allocators and message handlers */
rt_context ctx;
rt_raster raster;
- const char* hexwkb;
+ const char* hexwkb, *out;
+ uint32_t len;
ctx = rt_context_new(0, 0, 0);
/* ------------------------------------------------------ */
@@ -47,6 +48,18 @@
CHECK_EQUALS(rt_raster_get_width(ctx, raster), 7);
CHECK_EQUALS(rt_raster_get_height(ctx, raster), 8);
+ out = rt_raster_to_hexwkb(ctx, raster, &len);
+/*
+ printf(" in hexwkb len: %d\n", strlen(hexwkb));
+ printf("out hexwkb len: %d\n", len);
+ printf(" in hexwkb: %s\n", hexwkb);
+ printf("out hexwkb: %s\n", out);
+*/
+ CHECK_EQUALS(len, strlen(hexwkb));
+/* would depend on machine endian...
+ CHECK( ! strcmp(hexwkb, out) );
+*/
+
/* ------------------------------------------------------ */
hexwkb =
@@ -77,6 +90,16 @@
CHECK_EQUALS(rt_raster_get_width(ctx, raster), 7);
CHECK_EQUALS(rt_raster_get_height(ctx, raster), 8);
+ out = rt_raster_to_hexwkb(ctx, raster, &len);
+ printf(" in hexwkb len: %d\n", strlen(hexwkb));
+ printf("out hexwkb len: %d\n", len);
+ printf(" in hexwkb: %s\n", hexwkb);
+ printf("out hexwkb: %s\n", out);
+ CHECK_EQUALS(len, strlen(hexwkb));
+/* would depend on machine endian...
+ CHECK( ! strcmp(hexwkb, out) );
+*/
+
/* ------------------------------------------------------ */
hexwkb =
@@ -118,6 +141,14 @@
CHECK_EQUALS(rt_band_get_pixel(ctx, band, 0, 0), 1);
}
+ out = rt_raster_to_hexwkb(ctx, raster, &len);
+ printf(" in hexwkb len: %d\n", strlen(hexwkb));
+ printf("out hexwkb len: %d\n", len);
+ CHECK_EQUALS(len, strlen(hexwkb));
+/* would depend on machine endian...
+ CHECK( ! strcmp(hexwkb, out) );
+*/
+
/* ------------------------------------------------------ */
hexwkb =
@@ -169,6 +200,14 @@
CHECK_EQUALS(rt_band_get_pixel(ctx, band, 2, 1), 2);
}
+ out = rt_raster_to_hexwkb(ctx, raster, &len);
+ printf(" in hexwkb len: %d\n", strlen(hexwkb));
+ printf("out hexwkb len: %d\n", len);
+ CHECK_EQUALS(len, strlen(hexwkb));
+/* would depend on machine endian...
+ CHECK( ! strcmp(hexwkb, out) );
+*/
+
/* ------------------------------------------------------ */
hexwkb =
@@ -220,6 +259,15 @@
CHECK_EQUALS(rt_band_get_pixel(ctx, band, 2, 1), 2);
}
+ out = rt_raster_to_hexwkb(ctx, raster, &len);
+ printf(" in hexwkb len: %d\n", strlen(hexwkb));
+ printf("out hexwkb len: %d\n", len);
+ CHECK_EQUALS(len, strlen(hexwkb));
+/* would depend on machine endian
+ CHECK( ! strcmp(hexwkb, out) );
+*/
+
+
/* ------------------------------------------------------ */
hexwkb =
@@ -271,6 +319,14 @@
CHECK_EQUALS(rt_band_get_pixel(ctx, band, 2, 1), 2);
}
+ out = rt_raster_to_hexwkb(ctx, raster, &len);
+ printf(" in hexwkb len: %d\n", strlen(hexwkb));
+ printf("out hexwkb len: %d\n", len);
+ CHECK_EQUALS(len, strlen(hexwkb));
+/* would depend on machine endian
+ CHECK( ! strcmp(hexwkb, out) );
+*/
+
/* ------------------------------------------------------ */
printf("All tests successful !\n");
More information about the postgis-commits
mailing list