From: Nicklas Avén Date: Sun, 4 Aug 2013 21:27:27 +0000 (+0000) Subject: Initial commit of TWKB implimentation to trunk X-Git-Tag: 2.2.0rc1~1426 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=fe841913170be949e7ba982be75bd63b4117a3fa;p=postgis Initial commit of TWKB implimentation to trunk git-svn-id: http://svn.osgeo.org/postgis/trunk@11736 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/doc/reference_output.xml b/doc/reference_output.xml index 182344e04..c5dd74c97 100644 --- a/doc/reference_output.xml +++ b/doc/reference_output.xml @@ -1148,4 +1148,103 @@ SELECT (ST_AsLatLonText('POINT (-302.2342342 -792.32498)')); + + + ST_AsTWKB + Returns the geometry as TWKB, Tiny WKB + + + + + + bytea ST_AsTWKB + geometry g1 + integer decimaldigits + integer geometryID + text NDR_or_XDR + + + + + + Description + + Returns the geometry in TWKB format. TWKB is a new compressed binary format. + + + TWKB is still a moving target. The format is described here , and code for building a client can be found here + + + + Availability: 2.2 + + + + + Examples + +SELECT ST_AsTWKB('LINESTRING(1 1,5 5)'::geometry,0,1,'NDR'); + st_astwkb +-------------------------------------------- + \x0142010000000200000001000000010000000404 + + + + + See Also + , , , + + + + + + ST_AsTWKB_agg + Aggregates the geometries and returns as TWKB + + + + + + bytea ST_AsTWKB_agg + geometry g1 + integer decimaldigits + integer geometryID + text NDR_or_XDR + + + + + + Description + + Returns the geometry in TWKB format. TWKB is a new compressed binary format. + This is the aggregate version of ST_AsTWKB. It aggregates and returns the geoemtry in TWKB-format. In the resulting TWKB-geometry each individual ID of the input geometries is stored. + + + TWKB is still a moving target. The format is described here , and code for building a client can be found here + + + + Availability: 2.2 + + + + + Examples + +SELECT ST_AsTWKB_agg(geom,0,id,'NDR') FROM +(SELECT 'LINESTRING(1 1,5 5)'::geometry geom, 1 AS id +UNION ALL +SELECT 'LINESTRING(6 5, 1 7)'::geometry AS geom, 2 AS id; + st_astwkb_agg +---------------------------------------------------------------------------- + \x01560200000001000000020000000100000001000000040402000000020000000100fb02 + + + + + See Also + , , , + + diff --git a/liblwgeom/Makefile.in b/liblwgeom/Makefile.in index 3cc6256fd..6f5602590 100644 --- a/liblwgeom/Makefile.in +++ b/liblwgeom/Makefile.in @@ -60,6 +60,7 @@ SA_OBJS = \ lwin_geojson.o \ lwin_wkb.o \ lwout_wkt.o \ + lwout_twkb.o \ lwin_wkt_parse.o \ lwin_wkt_lex.o \ lwin_wkt.o \ diff --git a/liblwgeom/liblwgeom.h.in b/liblwgeom/liblwgeom.h.in index 3fb60eebe..688e7830c 100644 --- a/liblwgeom/liblwgeom.h.in +++ b/liblwgeom/liblwgeom.h.in @@ -544,6 +544,27 @@ typedef struct } LWTIN; + +typedef struct +{ + int id; //Id, from function parameter + LWGEOM* geom; //the geometry from function parameter +} +lwgeom_id; + +typedef struct +{ + lwgeom_id* points; + int n_points; + lwgeom_id* linestrings; + int n_linestrings; + lwgeom_id* polygons; + int n_polygons; + lwgeom_id* collections; + int n_collections; +} +twkb_geom_arrays; + /* Casts LWGEOM->LW* (return NULL if cast is illegal) */ extern LWMPOLY *lwgeom_as_lwmpoly(const LWGEOM *lwgeom); extern LWMLINE *lwgeom_as_lwmline(const LWGEOM *lwgeom); @@ -1772,6 +1793,9 @@ extern char* lwgeom_to_wkt(const LWGEOM *geom, uint8_t variant, int precision, */ extern uint8_t* lwgeom_to_wkb(const LWGEOM *geom, uint8_t variant, size_t *size_out); +extern uint8_t* lwgeom_to_twkb(const LWGEOM *geom, uint8_t variant, size_t *size_out,int8_t prec, uint32_t id,int method); +extern uint8_t* lwgeom_agg_to_twkb(const twkb_geom_arrays *lwgeom_arrays,uint8_t variant , size_t *size_out,int8_t prec, int method); + /** * @param lwgeom geometry to convert to HEXWKB * @param variant output format to use diff --git a/liblwgeom/lwout_twkb.c b/liblwgeom/lwout_twkb.c new file mode 100644 index 000000000..f10df7600 --- /dev/null +++ b/liblwgeom/lwout_twkb.c @@ -0,0 +1,1170 @@ +/********************************************************************** + * + * PostGIS - Spatial Types for PostgreSQL + * + * Copyright (C) 2013 Nicklas Avén + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU General Public Licence. See the COPYING file. + * + **********************************************************************/ + +#include "lwout_twkb.h" + + +/* +* GeometryType +*/ +static uint8_t lwgeom_twkb_type(const LWGEOM *geom, uint8_t variant) +{ + uint8_t wkb_type = 0; + uint8_t type_flag = 0; + int dims; + + switch ( geom->type ) + { + case POINTTYPE: + wkb_type = WKB_POINT_TYPE; + break; + case LINETYPE: + wkb_type = WKB_LINESTRING_TYPE; + break; + case POLYGONTYPE: + wkb_type = WKB_POLYGON_TYPE; + break; + case MULTIPOINTTYPE: + wkb_type = WKB_MULTIPOINT_TYPE; + break; + case MULTILINETYPE: + wkb_type = WKB_MULTILINESTRING_TYPE; + break; + case MULTIPOLYGONTYPE: + wkb_type = WKB_MULTIPOLYGON_TYPE; + break; + case COLLECTIONTYPE: + wkb_type = WKB_GEOMETRYCOLLECTION_TYPE; + break; + case CIRCSTRINGTYPE: + wkb_type = WKB_CIRCULARSTRING_TYPE; + break; + case COMPOUNDTYPE: + wkb_type = WKB_COMPOUNDCURVE_TYPE; + break; + case CURVEPOLYTYPE: + wkb_type = WKB_CURVEPOLYGON_TYPE; + break; + case MULTICURVETYPE: + wkb_type = WKB_MULTICURVE_TYPE; + break; + case MULTISURFACETYPE: + wkb_type = WKB_MULTISURFACE_TYPE; + break; + case POLYHEDRALSURFACETYPE: + wkb_type = WKB_POLYHEDRALSURFACE_TYPE; + break; + case TINTYPE: + wkb_type = WKB_TIN_TYPE; + break; + case TRIANGLETYPE: + wkb_type = WKB_TRIANGLE_TYPE; + break; + default: + lwerror("Unsupported geometry type: %s [%d]", + lwtype_name(geom->type), geom->type); + } + + /*Set the type*/ + TYPE_DIM_SET_TYPE(type_flag,wkb_type); + LWDEBUGF(4, "Writing type '%d'", wkb_type); + + /*Set number of dimensions*/ + dims = FLAGS_NDIMS(geom->flags); + if (dims>4) + lwerror("TWKB only supports 4 dimensions"); + TYPE_DIM_SET_DIM(type_flag,dims); + LWDEBUGF(4, "Writing ndims '%d'", dims); + + return type_flag; +} + +/* +* SwapBytes? +*/ +static inline int wkb_swap_bytes(uint8_t variant) +{ + /* If requested variant matches machine arch, we don't have to swap! */ + if ( ((variant & WKB_NDR) && (getMachineEndian() == NDR)) || + ((! (variant & WKB_NDR)) && (getMachineEndian() == XDR)) ) + { + return LW_FALSE; + } + return LW_TRUE; +} + +/* +* Integer32 +*/ +static uint8_t* int32_to_twkb_buf(const int ival, uint8_t *buf, uint8_t variant) +{ + char *iptr = (char*)(&ival); + int i = 0; + + if ( sizeof(int) != WKB_INT_SIZE ) + { + lwerror("Machine int size is not %d bytes!", WKB_INT_SIZE); + } + LWDEBUGF(4, "Writing value '%u'", ival); + + /* Machine/request arch mismatch, so flip byte order */ + if ( wkb_swap_bytes(variant) ) + { + for ( i = 0; i < WKB_INT_SIZE; i++ ) + { + buf[i] = iptr[WKB_INT_SIZE - 1 - i]; + } + } + /* If machine arch and requested arch match, don't flip byte order */ + else + { + memcpy(buf, iptr, WKB_INT_SIZE); + } + return buf + WKB_INT_SIZE; +} + +/* +* Byte +*/ +static uint8_t* uint8_to_twkb_buf(const uint8_t ival, uint8_t *buf) +{ + memcpy(buf, &ival, WKB_BYTE_SIZE); + return buf + 1; +} + +/* +* All sizes of integers +* This is for copying the different storaze sizes of the coordinates to the buffer. +*/ +static uint8_t* to_twkb_buf(uint8_t *iptr, uint8_t *buf, uint8_t variant, int the_size) +{ + int i = 0; + + /* Machine/request arch mismatch, so flip byte order */ + if ( wkb_swap_bytes(variant)&&the_size>1 ) + { + for ( i = 0; i < the_size; i++ ) + { + buf[i] = iptr[the_size - 1 - i]; + } + } + /* If machine arch and requested arch match, don't flip byte order */ + else + { + memcpy(buf, iptr, the_size); + } + return buf + the_size; +} + +/* +* Empty +*/ +static size_t empty_to_twkb_size(const LWGEOM *geom, uint8_t variant) +{ + + /* Endian flag/precision + id + type number + npoints*/ + size_t size = WKB_BYTE_SIZE + WKB_INT_SIZE + WKB_BYTE_SIZE + WKB_INT_SIZE; + + return size; +} + +static uint8_t* empty_to_twkb_buf(const LWGEOM *geom, uint8_t *buf, uint8_t variant,int8_t prec, uint32_t id) +{ + uint32_t wkb_type = lwgeom_twkb_type(geom, variant); + if ( geom->type == POINTTYPE ) + { + /* Change POINT to MULTIPOINT */ + wkb_type &= ~WKB_POINT_TYPE; /* clear POINT flag */ + wkb_type |= WKB_MULTIPOINT_TYPE; /* set MULTIPOINT flag */ + } + uint8_t flag=0; + + /* Set the endian flag */ + END_PREC_SET__ENDIANESS(flag, ((variant & WKB_NDR) ? 1 : 0)); + /* Tell what precision to use*/ + END_PREC_SET__PRECISION(flag,prec); + + /*Copy the flag to the buffer*/ + buf = uint8_to_twkb_buf(flag,buf); + + /* Set the geometry id */ + buf = int32_to_twkb_buf(id, buf, variant); + + /* Set the geometry type */ + buf = uint8_to_twkb_buf(wkb_type,buf); + + + /* Set nrings/npoints/ngeoms to zero */ + buf = int32_to_twkb_buf(0, buf, variant); + return buf; +} + +static size_t ptarray_to_twkb_size(const POINTARRAY *pa, uint8_t variant,int prec,int accum_rel[],int method) +{ + switch (method) + { + case 0: + return ptarray_to_twkb_size_m0(pa, variant,prec,accum_rel); + break; + /* Unknown method! */ + default: + lwerror("Unsupported compression method: %d",method ); + } +} + + + + +/* +* POINTARRAY +*/ +static size_t ptarray_to_twkb_size_m0(const POINTARRAY *pa, uint8_t variant,int prec,int accum_rel[]) +{ + LWDEBUGF(2, "ptarray_to_twkb_size entered%d",0); + int dims = FLAGS_NDIMS(pa->flags); + int i, j, r, last_size,factor,test_size,r_test,j_test; + double *dbl_ptr; + size_t size = 0; + last_size=0; + int start=0; + + /*The variable factor is used to "shift" the double float coordinate to keep enough significant digits, + for demanded precision, when cast to integer*/ + factor=pow(10,prec); + + /*This is a multidimmenstional array keeping trac of the three different storage sizes. + It holds number of bytes, max-value and min-value*/ + static int size_array[3][3] = {{1,INT8_MAX,INT8_MIN},{2,INT16_MAX,INT16_MIN},{4,INT32_MAX,INT32_MIN}}; + + /* Include the npoints size if it's not a POINT type) */ + if ( ! ( variant & WKB_NO_NPOINTS ) ) + { + LWDEBUGF(2, "We add space for npoints",0); + size += WKB_INT_SIZE; + } + /*if we don't have a ref-point yet*/ + if(accum_rel[1]==INT32_MIN) + { + LWDEBUGF(2, "We don't have a ref-point yet so we give space for full coordinates",0); + /*Get a pointer to the first point of the point array*/ + dbl_ptr = (double*)getPoint_internal(pa, 0); + + /*Register the size of the first point + it is 4 bytes per dimmension*/ + size += dims*WKB_INT_SIZE; + + LWDEBUGF(2, "Our geom have %d dims",dims); + /*Load the accum_rel aray with the first points dimmension*/ + for ( j = 0; j < dims; j++ ) + { + LWDEBUGF(4, "dim nr %d",j); + r = round(factor*dbl_ptr[j]); + LWDEBUGF(4, "found value %d",r); + accum_rel[j]=r; + + if(fabs(factor*dbl_ptr[j])>size_array[2][1]) + lwerror("The first coordinate exceeds the max_value (%d):%f",size_array[2][1],factor*dbl_ptr[j]); + + } + start=1; + } + + LWDEBUGF(2, "We have %d points to iterate ",pa->npoints); + for ( i = start; i < pa->npoints; i++ ) + { + dbl_ptr = (double*)getPoint_internal(pa, i); + for ( j = 0; j < dims; j++ ) + { + /*To get the relative coordinate we don't get teh distance from the last point + but instead the distance from our accumulated last point + This is important to not build up a accumulated error when rounding the coordinates*/ + r=round(factor*dbl_ptr[j]-accum_rel[j]); + + + /*last used size is too small so we have to increase*/ + if(fabs(r)>size_array[last_size][1]) + { + /*A little ugly, but we sacrify the last possible value fitting into a INT4, just to detect too big values without the need to do the substraction again with a double float instead*/ + if(fabs(r)>=size_array[2][1]) + lwerror("The relative coordinate coordinate exceeds the max_value (%d):%d",size_array[2][1],r); + /*minimum value for last used size, used to flag a change in size*/ + size += size_array[last_size][0]; + + /*Find how much space we actually need */ + while ( fabs(r)>size_array[(++last_size)][1] && last_size<3) {} + + /*register the one byte needed to tell what size we need*/ + size ++; + } + + /*We don't need that much space so let's investigate if we should decrease*/ + else if(last_size>0 && fabs(r)size_array[(test_size)][1] && test_size<=last_size) + { + LWDEBUGF(4, "testing %d bytes for value %d, dim: %d",size_array[test_size][0],r_test,j_test ); + test_size++; + } + } + if(test_sizeflags); + int i, j, r, last_size,factor,test_size,r_test,j_test; + double *dbl_ptr; + factor=pow(10,prec); + last_size=0; + int start=0; + /*This is a multidimmenstional array keeping trac of the three different storage sizes. + It holds number of bytes, max-value and min-value*/ + static int size_array[3][3] = {{1,INT8_MAX,INT8_MIN},{2,INT16_MAX,INT16_MIN},{4,INT32_MAX,INT32_MIN}}; + + /* Set the number of points (if it's not a POINT type) */ + if ( ! ( variant & WKB_NO_NPOINTS ) ) + { + buf = int32_to_twkb_buf(pa->npoints, buf, variant); + LWDEBUGF(4, "Regiter npoints:%d",pa->npoints); + } + + /*if we don't have a ref-point yet*/ + if(accum_rel[0]==INT32_MIN) + { + /*Get a pointer do the first point of the point array*/ + dbl_ptr = (double*)getPoint_internal(pa, 0); + + /*the first coordinate for each dimension is copied to the buffer + and registered in accum_rel array*/ + for ( j = 0; j < dims; j++ ) + { + r = round(factor*dbl_ptr[j]); + accum_rel[j]=r; + LWDEBUGF(4, "Writing dimension #%d (buf = %p)", j, buf); + buf = int32_to_twkb_buf(r, buf, variant); + } + start=1; + } + for ( i = start; i < pa->npoints; i++ ) + { + LWDEBUGF(4, "Writing point #%d", i); + dbl_ptr = (double*)getPoint_internal(pa, i); + for ( j = 0; j < dims; j++ ) + { + /*To get the relative coordinate we don't get the distance from the last point + but instead the distance from our accumulated last point + This is important to not build up a accumulated error when rounding the coordinates*/ + r=round(factor*dbl_ptr[j]-accum_rel[j]); + //accum_rel[j]+=r; + //LWDEBUGF(4, "delta value for dim %d is %d, real coordiinate is %f and accumulated coordinate is%d", j, r,dbl_ptr[j],accum_rel[j] ); + LWDEBUGF(4, "size:%d,dim: %d deltavalue: %d, coordinate: %f accumulated coordinate %d",last_size, j, r,dbl_ptr[j],accum_rel[j] ); + + + + /*last used size is too small so we have to increase*/ + if(fabs(r)>size_array[last_size][1]) + { + + LWDEBUGF(4, "increasing size from %d bytes",size_array[last_size][0]); + /*minimum value for last used size, used to flag a change in size*/ + buf = to_twkb_buf((uint8_t *) &(size_array[last_size][2]),buf, variant, size_array[last_size][0]); + + /*Find how much space we actually need */ + while ( fabs(r)>size_array[(++last_size)][1] && last_size<3) {} + LWDEBUGF(4, "to size %d bytes",size_array[last_size][0]); + + /*register needed space*/ + memcpy( buf, &(size_array[last_size][0]), 1); + buf++; + } + /*We don't need that much space so let's investigate if we should decrease + But if it is just a horizontal or vertical line, one dimmension will have short steps but another will still need bigger steps + So, to avoid size changes up and down for every point we don't decrease size if it is not possible for all dimmensions + We could here look even further forward to find out what is most optimal, but that will cost in computing instead*/ + else if (last_size>0 && fabs(r)size_array[(test_size)][1] && test_size<=last_size) + { + LWDEBUGF(4, "testing %d bytes for value %d, dim: %d",size_array[test_size][0],r_test,j_test ); + test_size++; + } + } + if(test_sizegeom); + size += lwpoint_to_twkb_size(p,variant,prec,refpoint,method); + } + return size; +} +/* +* POINT +*/ +static size_t lwpoint_to_twkb_size(const LWPOINT *pt,uint8_t variant, int8_t prec,int refpoint[],int method) +{ + size_t size = 0; + /* geometry id, if not subgeometry in type 4,5 or 6*/ + if (!(variant & WKB_NO_ID)) + size += WKB_INT_SIZE; + + /* Points */ + size += ptarray_to_twkb_size(pt->point, variant | WKB_NO_NPOINTS, prec,refpoint,method); + return size; +} + + +static uint8_t* lwgeom_agg_to_twkbpoint_buf(lwgeom_id* geom_array,int n, uint8_t *buf, uint8_t variant,int8_t prec, int refpoint[],int method) +{ + + lwgeom_id *li; + uint8_t type_flag = 0; + int i,dims; + /*Set the type*/ + TYPE_DIM_SET_TYPE(type_flag,21); + LWDEBUGF(4, "Writing type '%d'", 21); + + /*Set number of dimensions*/ + dims = FLAGS_NDIMS(((LWGEOM*) (geom_array->geom))->flags); + if (dims>4) + lwerror("TWKB only supports 4 dimensions"); + TYPE_DIM_SET_DIM(type_flag,dims); + LWDEBUGF(4, "Writing ndims '%d'", dims); + buf = uint8_to_twkb_buf(type_flag,buf); + + /* Set number of geometries */ + buf = int32_to_twkb_buf(n, buf, variant); + + + for (i=0;igeom),buf,variant,prec,li->id,refpoint,method); + } + return buf; +} + +static uint8_t* lwpoint_to_twkb_buf(const LWPOINT *pt, uint8_t *buf, uint8_t variant,int8_t prec, uint32_t id,int refpoint[],int method) +{ + + + /* Set the geometry id, if not subgeometry in type 4,5 or 6*/ + if (!(variant & WKB_NO_ID)) + buf = int32_to_twkb_buf(id, buf, variant); + + + /* Set the coordinates */ + buf = ptarray_to_twkb_buf(pt->point, buf, variant | WKB_NO_NPOINTS,prec,refpoint,method); + LWDEBUGF(4, "Pointarray set, buf = %p", buf); + return buf; +} + +static size_t lwgeom_agg_to_twkbline_size(lwgeom_id* geom_array,uint8_t variant,int n,int8_t prec,int refpoint[],int method) +{ + /*One byte for type declaration*/ + size_t size = WKB_BYTE_SIZE; + /*One integer holding number of collections*/ + size += WKB_INT_SIZE; + int i; + for (i=0;igeom),variant,prec,refpoint,method); + } + return size; +} +/* +* LINESTRING, CIRCULARSTRING +*/ +static size_t lwline_to_twkb_size(const LWLINE *line,uint8_t variant, int8_t prec,int refpoint[],int method) +{ + size_t size = 0; + /* geometry id, if not subgeometry in type 4,5 or 6*/ + if (!(variant & WKB_NO_ID)) + size += WKB_INT_SIZE; + + /* Size of point array */ + size += ptarray_to_twkb_size(line->points,variant,prec,refpoint,method); + return size; +} + +static uint8_t* lwgeom_agg_to_twkbline_buf(lwgeom_id* geom_array,int n, uint8_t *buf, uint8_t variant,int8_t prec, int refpoint[],int method) +{ + + lwgeom_id *li; + uint8_t type_flag = 0; + int i,dims; + /*Set the type*/ + TYPE_DIM_SET_TYPE(type_flag,22); + LWDEBUGF(4, "Writing type '%d'",22); + + /*Set number of dimensions*/ + dims = FLAGS_NDIMS(((LWGEOM*) (geom_array->geom))->flags); + if (dims>4) + lwerror("TWKB only supports 4 dimensions"); + TYPE_DIM_SET_DIM(type_flag,dims); + LWDEBUGF(4, "Writing ndims '%d'", dims); + buf = uint8_to_twkb_buf(type_flag,buf); + + /* Set number of geometries */ + buf = int32_to_twkb_buf(n, buf, variant); + + for (i=0;igeom,buf,variant,prec,li->id,refpoint,method); + } + return buf; +} + + +static uint8_t* lwline_to_twkb_buf(const LWLINE *line, uint8_t *buf, uint8_t variant,int8_t prec, uint32_t id,int refpoint[],int method) +{ + + /* Set the geometry id, if not subgeometry in type 4,5 or 6*/ + if (!(variant & WKB_NO_ID)) + buf = int32_to_twkb_buf(id, buf, variant); + + + /* Set the coordinates */ + buf = ptarray_to_twkb_buf(line->points, buf, variant,prec,refpoint,method); + return buf; +} + + +static size_t lwgeom_agg_to_twkbpoly_size(lwgeom_id* geom_array,uint8_t variant,int n,int8_t prec,int refpoint[],int method) +{ + /*One byte for type declaration*/ + size_t size = WKB_BYTE_SIZE; + /*One integer holding number of collections*/ + size += WKB_INT_SIZE; + int i; + for (i=0;igeom),variant,prec,refpoint,method); + } + return size; +} +/* +* POLYGON +*/ +static size_t lwpoly_to_twkb_size(const LWPOLY *poly,uint8_t variant, int8_t prec,int refpoint[],int method) +{ + LWDEBUGF(2, "lwpoly_to_twkb_size entered%d",0); + int i; + + size_t size = 0; + /* geometry id, if not subgeometry in type 4,5 or 6*/ + if (!(variant & WKB_NO_ID)) + size += WKB_INT_SIZE; + + /*nrings*/ + size += WKB_INT_SIZE; + + LWDEBUGF(2, "we have %d rings to iterate",poly->nrings); + for ( i = 0; i < poly->nrings; i++ ) + { + /* Size of ring point array */ + size += ptarray_to_twkb_size(poly->rings[i],variant,prec,refpoint,method); + } + + return size; +} + +static uint8_t* lwgeom_agg_to_twkbpoly_buf(lwgeom_id* geom_array,int n, uint8_t *buf, uint8_t variant,int8_t prec, int refpoint[],int method) +{ + + lwgeom_id *li; + uint8_t type_flag = 0; + int i,dims; + /*Set the type*/ + TYPE_DIM_SET_TYPE(type_flag,23); + LWDEBUGF(4, "Writing type '%d'", 23); + + /*Set number of dimensions*/ + dims = FLAGS_NDIMS(((LWGEOM*) (geom_array->geom))->flags); + if (dims>4) + lwerror("TWKB only supports 4 dimensions"); + TYPE_DIM_SET_DIM(type_flag,dims); + LWDEBUGF(4, "Writing ndims '%d'", dims); + buf = uint8_to_twkb_buf(type_flag,buf); + /* Set number of geometries */ + buf = int32_to_twkb_buf(n, buf, variant); + + for (i=0;igeom),buf,variant,prec,li->id,refpoint,method); + } + return buf; +} + +static uint8_t* lwpoly_to_twkb_buf(const LWPOLY *poly, uint8_t *buf, uint8_t variant,int8_t prec, uint32_t id,int refpoint[],int method) +{ + int i; + + /* Set the geometry id, if not subgeometry in type 4,5 or 6*/ + if (!(variant & WKB_NO_ID)) + buf = int32_to_twkb_buf(id, buf, variant); + + /* Set the number of rings */ + buf = int32_to_twkb_buf(poly->nrings, buf, variant); + + for ( i = 0; i < poly->nrings; i++ ) + { + buf = ptarray_to_twkb_buf(poly->rings[i], buf, variant,prec,refpoint,method); + } + + return buf; +} + + +static size_t lwgeom_agg_to_twkbcollection_size(lwgeom_id* geom_array,uint8_t variant,int n,int8_t prec,int refpoint[],int method) +{ + LWDEBUGF(4, "lwgeom_agg_to_twkbcollection_size entered with %d collections",n); + /*One byte for type declaration*/ + size_t size = WKB_BYTE_SIZE; + /*One integer holding number of collections*/ + size += WKB_INT_SIZE; + int i; + for (i=0;igeom),variant,prec,refpoint,method); + } + return size; +} +/* +* MULTIPOINT, MULTILINESTRING, MULTIPOLYGON, GEOMETRYCOLLECTION +* MULTICURVE, COMPOUNDCURVE, MULTISURFACE, CURVEPOLYGON, TIN, +* POLYHEDRALSURFACE +*/ +static size_t lwcollection_to_twkb_size(const LWCOLLECTION *col,uint8_t variant, int8_t prec,int refpoint[],int method) +{ + LWDEBUGF(2, "lwcollection_to_twkb_size entered, %d",0); + /* id*/ + size_t size = WKB_INT_SIZE; + /* size of geoms */ + size += WKB_INT_SIZE; + int i = 0; + + for ( i = 0; i < col->ngeoms; i++ ) + { + /* size of subgeom */ + size += lwgeom_to_twkb_size((LWGEOM*)col->geoms[i],variant | WKB_NO_ID, prec,refpoint,method); + } + + return size; +} + +static uint8_t* lwgeom_agg_to_twkbcollection_buf(lwgeom_id* geom_array,int n, uint8_t *buf, uint8_t variant,int8_t prec, int refpoint[],int method) +{ + + lwgeom_id *li; + uint8_t type_flag = 0; + int i,dims; + /*Set the type*/ + TYPE_DIM_SET_TYPE(type_flag,24); + LWDEBUGF(4, "Writing type '%d'", 24); + + /*Set number of dimensions*/ + dims = FLAGS_NDIMS(((LWGEOM*) (geom_array->geom))->flags); + if (dims>4) + lwerror("TWKB only supports 4 dimensions"); + TYPE_DIM_SET_DIM(type_flag,dims); + LWDEBUGF(4, "Writing ndims '%d'", dims); + buf = uint8_to_twkb_buf(type_flag,buf); + /* Set number of geometries */ + buf = int32_to_twkb_buf(n, buf, variant); + + for (i=0;igeom,buf,variant,prec,li->id,refpoint,method); + } + return buf; +} + +static uint8_t* lwcollection_to_twkb_buf(const LWCOLLECTION *col, uint8_t *buf, uint8_t variant,int8_t prec, uint32_t id,int refpoint[],int method) +{ + int i; + + + /* Set the geometry id */ + buf = int32_to_twkb_buf(id, buf, variant); + + /* Set the number of rings */ + buf = int32_to_twkb_buf(col->ngeoms, buf, variant); + + /* Write the sub-geometries. Sub-geometries do not get SRIDs, they + inherit from their parents. */ + for ( i = 0; i < col->ngeoms; i++ ) + { + buf = lwgeom_to_twkb_buf(col->geoms[i], buf, variant | WKB_NO_ID,prec,id,refpoint,method); + } + + return buf; +} + +/* +* GEOMETRY +*/ +static size_t lwgeom_to_twkb_size(const LWGEOM *geom,uint8_t variant, int8_t prec,int refpoint[],int method) +{ + LWDEBUGF(2, "lwgeom_to_twkb_size entered %d",0); + size_t size = 0; + if ( geom == NULL ) + return 0; + + /* Short circuit out empty geometries */ + if ( lwgeom_is_empty(geom) ) + { + return empty_to_twkb_size(geom, variant); + } + /*add size of type-declaration*/ + if (!(variant & WKB_NO_TYPE)) + size += WKB_BYTE_SIZE; + switch ( geom->type ) + { + case POINTTYPE: + size += lwpoint_to_twkb_size((LWPOINT*)geom, variant, prec,refpoint,method); + break; + + /* LineString and CircularString both have points elements */ + case CIRCSTRINGTYPE: + case LINETYPE: + size += lwline_to_twkb_size((LWLINE*)geom, variant, prec,refpoint,method); + break; + + /* Polygon has nrings and rings elements */ + case POLYGONTYPE: + size += lwpoly_to_twkb_size((LWPOLY*)geom, variant, prec,refpoint,method); + break; + + /* Triangle has one ring of three points + case TRIANGLETYPE: + size += lwtriangle_to_twkb_size((LWTRIANGLE*)geom, variant); + break;*/ + + /* All these Collection types have ngeoms and geoms elements */ + case MULTIPOINTTYPE: + case MULTILINETYPE: + case MULTIPOLYGONTYPE: + size += lwcollection_to_twkb_size((LWCOLLECTION*)geom, variant | WKB_NO_TYPE, prec,refpoint,method); + break; + case COLLECTIONTYPE: + size += lwcollection_to_twkb_size((LWCOLLECTION*)geom, variant, prec,refpoint,method); + break; + + /* Unknown type! */ + default: + lwerror("Unsupported geometry type: %s [%d]", lwtype_name(geom->type), geom->type); + } + + return size; +} + +/* TODO handle the TRIANGLE type properly */ + +static uint8_t* lwgeom_to_twkb_buf(const LWGEOM *geom, uint8_t *buf, uint8_t variant,int8_t prec, uint32_t id,int refpoint[],int method) +{ + + if ( lwgeom_is_empty(geom) ) + return empty_to_twkb_buf(geom, buf, variant,prec,id); + + switch ( geom->type ) + { + case POINTTYPE: + { + if (!(variant & WKB_NO_TYPE)) + buf = uint8_to_twkb_buf(lwgeom_twkb_type(geom, variant),buf); + return lwpoint_to_twkb_buf((LWPOINT*)geom, buf, variant,prec,id,refpoint,method); + } + /* LineString and CircularString both have 'points' elements */ + case CIRCSTRINGTYPE: + case LINETYPE: + { + if (!(variant & WKB_NO_TYPE)) + buf = uint8_to_twkb_buf(lwgeom_twkb_type(geom, variant),buf); + return lwline_to_twkb_buf((LWLINE*)geom, buf, variant,prec,id,refpoint,method); + } + /* Polygon has 'nrings' and 'rings' elements */ + case POLYGONTYPE: + { + if (!(variant & WKB_NO_TYPE)) + buf = uint8_to_twkb_buf(lwgeom_twkb_type(geom, variant),buf); + return lwpoly_to_twkb_buf((LWPOLY*)geom, buf, variant,prec,id,refpoint,method); + } + /* Triangle has one ring of three points + case TRIANGLETYPE: + return lwtriangle_to_twkb_buf((LWTRIANGLE*)geom, buf, variant); +*/ + /* All these Collection types have 'ngeoms' and 'geoms' elements */ + case MULTIPOINTTYPE: + case MULTILINETYPE: + case MULTIPOLYGONTYPE: + { + buf = uint8_to_twkb_buf(lwgeom_twkb_type(geom, variant),buf); + return lwcollection_to_twkb_buf((LWCOLLECTION*)geom, buf, variant | WKB_NO_TYPE,prec,id,refpoint,method); + } + case COLLECTIONTYPE: + { + buf = uint8_to_twkb_buf(lwgeom_twkb_type(geom, variant),buf); + return lwcollection_to_twkb_buf((LWCOLLECTION*)geom, buf, variant,prec,id,refpoint,method); + } + /* Unknown type! */ + default: + lwerror("Unsupported geometry type: %s [%d]", lwtype_name(geom->type), geom->type); + } + /* Return value to keep compiler happy. */ + return 0; +} + +/** +* Convert LWGEOM to a char* in TWKB format. Caller is responsible for freeing +* the returned array. +*/ +uint8_t* lwgeom_to_twkb(const LWGEOM *geom, uint8_t variant, size_t *size_out,int8_t prec, uint32_t id,int method) +{ + size_t buf_size; + uint8_t *buf = NULL; + uint8_t *wkb_out = NULL; + uint8_t flag=0; + /*an integer array holding the reference point. In most cases the last used point + but in the case of pointcloud it is a user defined refpoint. + INT32_MIN indicates that the ref-point is not set yet*/ + int refpoint[4]= {INT32_MIN,INT32_MIN,INT32_MIN,INT32_MIN}; + int refpoint2[4]= {INT32_MIN,INT32_MIN,INT32_MIN,INT32_MIN}; + + /* Initialize output size */ + if ( size_out ) *size_out = 0; + + if ( geom == NULL ) + { + LWDEBUG(4,"Cannot convert NULL into WKB."); + lwerror("Cannot convert NULL into WKB."); + return NULL; + } + + /* Calculate the required size of the output buffer */ + + /*Adding the size for the first byte*/ + buf_size = 1; + buf_size += lwgeom_to_twkb_size(geom,variant,prec,refpoint,method); + LWDEBUGF(4, "WKB output size: %d", buf_size); + + if ( buf_size == 0 ) + { + LWDEBUG(4,"Error calculating output WKB buffer size."); + lwerror("Error calculating output WKB buffer size."); + return NULL; + } + + /* If neither or both variants are specified, choose the native order */ + if ( ! (variant & WKB_NDR || variant & WKB_XDR) || + (variant & WKB_NDR && variant & WKB_XDR) ) + { + if ( getMachineEndian() == NDR ) + variant = variant | WKB_NDR; + else + variant = variant | WKB_XDR; + } + + /* Allocate the buffer */ + buf = lwalloc(buf_size); + + if ( buf == NULL ) + { + LWDEBUGF(4,"Unable to allocate %d bytes for WKB output buffer.", buf_size); + lwerror("Unable to allocate %d bytes for WKB output buffer.", buf_size); + return NULL; + } + + /* Retain a pointer to the front of the buffer for later */ + wkb_out = buf; + + /* Set the endian flag */ + END_PREC_SET__ENDIANESS(flag, ((variant & WKB_NDR) ? 1 : 0)); + /* Tell what method to use*/ + END_PREC_SET__METHOD(flag, method); + /* Tell what precision to use*/ + END_PREC_SET__PRECISION(flag,prec); + + /*Copy the flag to the buffer*/ + buf = uint8_to_twkb_buf(flag,buf); + + + + /* Write the WKB into the output buffer */ + buf = lwgeom_to_twkb_buf(geom, buf,variant, prec,id,refpoint2,method); + + LWDEBUGF(4,"buf (%p) - wkb_out (%p) = %d", buf, wkb_out, buf - wkb_out); + + /* The buffer pointer should now land at the end of the allocated buffer space. Let's check. */ + if ( buf_size != (buf - wkb_out) ) + { + LWDEBUG(4,"Output WKB is not the same size as the allocated buffer."); + lwerror("Output WKB is not the same size as the allocated buffer."); + lwfree(wkb_out); + return NULL; + } + + /* Report output size */ + if ( size_out ) *size_out = buf_size; + + return wkb_out; +} + +uint8_t* lwgeom_agg_to_twkb(const twkb_geom_arrays *lwgeom_arrays,uint8_t variant , size_t *size_out,int8_t prec,int method) +{ + size_t buf_size; + uint8_t *buf = NULL; + uint8_t flag = 0; + uint8_t *wkb_out = NULL; + int chk_homogenity=0; + uint8_t type_flag = 0; + int dims; + /*an integer array holding the reference point. In most cases the last used point + but in the case of pointcloud it is a user defined refpoint. + INT32_MIN indicates that the ref-point is not set yet*/ + int refpoint[4]= {INT32_MIN,INT32_MIN,INT32_MIN,INT32_MIN}; + int refpoint2[4]= {INT32_MIN,INT32_MIN,INT32_MIN,INT32_MIN}; + + LWDEBUGF(4, "We have collected: %d points, %d linestrings, %d polygons and %d collections",lwgeom_arrays->n_points,lwgeom_arrays->n_linestrings,lwgeom_arrays->n_polygons,lwgeom_arrays->n_collections ); + + + /* Initialize output size */ + if ( size_out ) *size_out = 0; + + if (lwgeom_arrays->n_points > 0) + chk_homogenity++; + if (lwgeom_arrays->n_linestrings > 0) + chk_homogenity++; + if (lwgeom_arrays->n_polygons > 0) + chk_homogenity++ ; + if (lwgeom_arrays->n_collections > 0) + chk_homogenity++; + + + + if(chk_homogenity==0) + return NULL; + if(chk_homogenity>1) + buf_size=6; + else + buf_size=1; + + + if (lwgeom_arrays->n_points > 0) + buf_size += lwgeom_agg_to_twkbpoint_size(lwgeom_arrays->points,variant,lwgeom_arrays->n_points, prec,refpoint,method); + if (lwgeom_arrays->n_linestrings > 0) + buf_size += lwgeom_agg_to_twkbline_size(lwgeom_arrays->linestrings,variant,lwgeom_arrays->n_linestrings, prec,refpoint,method); + if (lwgeom_arrays->n_polygons > 0) + buf_size += lwgeom_agg_to_twkbpoly_size(lwgeom_arrays->polygons,variant,lwgeom_arrays->n_polygons, prec,refpoint,method); + if (lwgeom_arrays->n_collections > 0) + buf_size += lwgeom_agg_to_twkbcollection_size(lwgeom_arrays->collections,variant,lwgeom_arrays->n_collections, prec,refpoint,method); + + + + + + LWDEBUGF(4, "WKB output size: %d", buf_size); + + if ( buf_size == 0 ) + { + LWDEBUG(4,"Error calculating output WKB buffer size."); + lwerror("Error calculating output WKB buffer size."); + return NULL; + } + + /* If neither or both variants are specified, choose the native order */ + if ( ! (variant & WKB_NDR || variant & WKB_XDR) || + (variant & WKB_NDR && variant & WKB_XDR) ) + { + if ( getMachineEndian() == NDR ) + variant = variant | WKB_NDR; + else + variant = variant | WKB_XDR; + } + + /* Allocate the buffer */ + buf = lwalloc(buf_size); + + if ( buf == NULL ) + { + LWDEBUGF(4,"Unable to allocate %d bytes for WKB output buffer.", buf_size); + lwerror("Unable to allocate %d bytes for WKB output buffer.", buf_size); + return NULL; + } + + /* Retain a pointer to the front of the buffer for later */ + wkb_out = buf; + + + /* Set the endian flag */ + END_PREC_SET__ENDIANESS(flag, ((variant & WKB_NDR) ? 1 : 0)); + /* Tell what method to use*/ + END_PREC_SET__METHOD(flag, method); + /* Tell what precision to use*/ + END_PREC_SET__PRECISION(flag,prec); + + /*Copy the flag to the buffer*/ + buf = uint8_to_twkb_buf(flag,buf); + + /*set type and number of geometries for the top level, if more than 1 type og underlying geometries*/ + if(chk_homogenity>1) + { + + /*Set the type*/ + TYPE_DIM_SET_TYPE(type_flag,7); + LWDEBUGF(4, "Writing type '%d'",7); + + /*We just set this to 4 dimmensions. It doesn't matter since all undelying geometries have their own*/ + dims = 4; + TYPE_DIM_SET_DIM(type_flag,dims); + LWDEBUGF(4, "Writing ndims '%d'", dims); + buf = uint8_to_twkb_buf(type_flag,buf); + + /* Set number of geometries */ + buf = int32_to_twkb_buf(chk_homogenity, buf, variant); + } + + /* Write the WKB into the output buffer + buf = lwgeom_to_twkb_buf(geom, buf,variant, prec,id,refpoint2);*/ + + if (lwgeom_arrays->n_points > 0) + buf =lwgeom_agg_to_twkbpoint_buf(lwgeom_arrays->points,lwgeom_arrays->n_points, buf,variant, prec,refpoint2,method); + if (lwgeom_arrays->n_linestrings > 0) + buf =lwgeom_agg_to_twkbline_buf(lwgeom_arrays->linestrings,lwgeom_arrays->n_linestrings, buf,variant, prec,refpoint2,method); + if (lwgeom_arrays->n_polygons > 0) + buf =lwgeom_agg_to_twkbpoly_buf(lwgeom_arrays->polygons,lwgeom_arrays->n_polygons, buf,variant, prec,refpoint2,method); + if (lwgeom_arrays->n_collections > 0) + buf =lwgeom_agg_to_twkbcollection_buf(lwgeom_arrays->collections,lwgeom_arrays->n_collections, buf,variant, prec,refpoint2,method); + + + + + LWDEBUGF(4,"buf (%p) - wkb_out (%p) = %d", buf, wkb_out, buf - wkb_out); + + /* The buffer pointer should now land at the end of the allocated buffer space. Let's check. */ + if ( buf_size != (buf - wkb_out) ) + { + LWDEBUG(4,"Output WKB is not the same size as the allocated buffer."); + lwerror("Output WKB is not the same size as the allocated buffer."); + lwfree(wkb_out); + return NULL; + } + + /* Report output size */ + if ( size_out ) *size_out = buf_size; + + return wkb_out; + +} + + + + + + + + + + + diff --git a/liblwgeom/lwout_twkb.h b/liblwgeom/lwout_twkb.h new file mode 100644 index 000000000..2c5e3d4a6 --- /dev/null +++ b/liblwgeom/lwout_twkb.h @@ -0,0 +1,74 @@ + +/********************************************************************** + * $Id: lwout_twkb.h 4715 2009-11-01 17:58:42Z nicklas $ + * + * PostGIS - Spatial Types for PostgreSQL + * http://postgis.refractions.net + * Copyright 2013 Nicklas Avén + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU General Public Licence. See the COPYING file. + * + **********************************************************************/ + +#include "liblwgeom_internal.h" +#include "lwgeom_log.h" +#include + +/** +* Macros for manipulating the 'endian_precision' int. An int8_t used as follows: +* Endianess 1 bit +* 3 unused bits +* Precision 4 bits +*/ + +#define END_PREC_SET__ENDIANESS(flag, endianess) ((flag) = (flag & 0xFE) | ((endianess & 0x01))) +#define END_PREC_SET__METHOD(flag, method) ((flag) = (flag & 0xF1) | ((prec<<1) & 0x0E)) +#define END_PREC_SET__PRECISION(flag, prec) ((flag) = (flag & 0x0F) | ((prec<<4) & 0xF0)) + + +/** +* Macros for manipulating the 'type_dim' int. An int8_t used as follows: +* Type 5 bits +* NDims 3 bits +*/ + +#define TYPE_DIM_SET_TYPE(flag, type) ((flag) = (flag & 0xE0) | ((type & 0x1F))) +#define TYPE_DIM_SET_DIM(flag, dim) ((flag) = (flag & 0x1F) | ((dim & 0x07)<<5)) + +/*For variant variable that holds options when building the twkb*/ +#define WKB_NO_ID 0x01 /* This position has another meaning when building wkb! */ +#define WKB_NO_TYPE 0x02 /* This position has another meaning when building wkb! */ + +static size_t ptarray_to_twkb_size(const POINTARRAY *pa, uint8_t variant,int prec,int accum_rel[],int method); +static uint8_t* ptarray_to_twkb_buf(const POINTARRAY *pa, uint8_t *buf, uint8_t variant,int8_t prec,int accum_rel[],int method); + +static size_t ptarray_to_twkb_size_m0(const POINTARRAY *pa, uint8_t variant,int prec,int accum_rel[]); +static uint8_t* ptarray_to_twkb_buf_m0(const POINTARRAY *pa, uint8_t *buf, uint8_t variant,int8_t prec,int accum_rel[]); + +static size_t lwgeom_agg_to_twkbpoint_size(lwgeom_id *geom_array, uint8_t variant,int n,int8_t prec,int refpoint[],int method); +static size_t lwpoint_to_twkb_size(const LWPOINT *pt, uint8_t variant, int8_t prec,int refpoint[],int method); +static uint8_t* lwgeom_agg_to_twkbpoint_buf(lwgeom_id* geom_array,int n, uint8_t *buf, uint8_t variant,int8_t prec, int refpoint[],int method); +static uint8_t* lwpoint_to_twkb_buf(const LWPOINT *pt, uint8_t *buf, uint8_t variant,int8_t prec, uint32_t id,int refpoint[],int method); + +static size_t lwgeom_agg_to_twkbline_size(lwgeom_id *geom_array, uint8_t variant,int n,int8_t prec,int refpoint[],int method); +static size_t lwline_to_twkb_size(const LWLINE *line, uint8_t variant, int8_t prec,int refpoint[],int method); +static uint8_t* lwgeom_agg_to_twkbline_buf(lwgeom_id* geom_array,int n, uint8_t *buf, uint8_t variant,int8_t prec, int refpoint[],int method); +static uint8_t* lwline_to_twkb_buf(const LWLINE *line, uint8_t *buf, uint8_t variant,int8_t prec, uint32_t id,int refpoint[],int method); + +static size_t lwgeom_agg_to_twkbpoly_size(lwgeom_id *geom_array, uint8_t variant,int n,int8_t prec,int refpoint[],int method); +static size_t lwpoly_to_twkb_size(const LWPOLY *poly, uint8_t variant,int8_t prec,int refpoint[],int method); +static uint8_t* lwgeom_agg_to_twkbpoly_buf(lwgeom_id* geom_array,int n, uint8_t *buf, uint8_t variant,int8_t prec, int refpoint[],int method); +static uint8_t* lwpoly_to_twkb_buf(const LWPOLY *poly, uint8_t *buf, uint8_t variant,int8_t prec, uint32_t id,int refpoint[],int method); + +static size_t lwgeom_agg_to_twkbcollection_size(lwgeom_id *geom_array, uint8_t variant,int n,int8_t prec,int refpoint[],int method); +static size_t lwcollection_to_twkb_size(const LWCOLLECTION *col, uint8_t variant, int8_t prec,int refpoint[],int method); +static uint8_t* lwgeom_agg_to_twkbcollection_buf(lwgeom_id* geom_array,int n, uint8_t *buf, uint8_t variant,int8_t prec, int refpoint[],int method); +static uint8_t* lwcollection_to_twkb_buf(const LWCOLLECTION *col, uint8_t *buf, uint8_t variant,int8_t prec, uint32_t id,int refpoint[],int method); + +static size_t lwgeom_to_twkb_size(const LWGEOM *geom, uint8_t variant, int8_t prec,int refpoint[],int method); +static uint8_t* lwgeom_to_twkb_buf(const LWGEOM *geom, uint8_t *buf, uint8_t variant,int8_t prec, uint32_t id,int refpoint[],int method); + + +//static size_t lwgeom_to_twkb_size(const LWGEOM *geom, uint8_t variant,int8_t prec); + diff --git a/libpgcommon/lwgeom_pg.h b/libpgcommon/lwgeom_pg.h index 594bff983..3d392e84f 100644 --- a/libpgcommon/lwgeom_pg.h +++ b/libpgcommon/lwgeom_pg.h @@ -153,6 +153,7 @@ Datum LWGEOM_force_multi(PG_FUNCTION_ARGS); Datum LWGEOMFromWKB(PG_FUNCTION_ARGS); Datum WKBFromLWGEOM(PG_FUNCTION_ARGS); +Datum TWKBFromLWGEOM(PG_FUNCTION_ARGS); Datum LWGEOM_getBBOX(PG_FUNCTION_ARGS); Datum LWGEOM_addBBOX(PG_FUNCTION_ARGS); diff --git a/postgis/lwgeom_accum.c b/postgis/lwgeom_accum.c index 6df1e0a24..11043f926 100644 --- a/postgis/lwgeom_accum.c +++ b/postgis/lwgeom_accum.c @@ -28,6 +28,8 @@ Datum pgis_geometry_accum_transfn(PG_FUNCTION_ARGS); Datum pgis_geometry_accum_finalfn(PG_FUNCTION_ARGS); Datum pgis_geometry_union_finalfn(PG_FUNCTION_ARGS); Datum pgis_geometry_collect_finalfn(PG_FUNCTION_ARGS); +Datum pgis_twkb_accum_finalfn(PG_FUNCTION_ARGS); +Datum pgis_twkb_accum_transfn(PG_FUNCTION_ARGS); Datum pgis_geometry_polygonize_finalfn(PG_FUNCTION_ARGS); Datum pgis_geometry_makeline_finalfn(PG_FUNCTION_ARGS); Datum pgis_abs_in(PG_FUNCTION_ARGS); @@ -36,6 +38,7 @@ Datum pgis_abs_out(PG_FUNCTION_ARGS); /* External prototypes */ Datum pgis_union_geometry_array(PG_FUNCTION_ARGS); Datum LWGEOM_collect_garray(PG_FUNCTION_ARGS); +Datum LWGEOM_twkb_garray(PG_FUNCTION_ARGS); Datum polygonize_garray(PG_FUNCTION_ARGS); Datum LWGEOM_makeline_garray(PG_FUNCTION_ARGS); @@ -64,6 +67,26 @@ typedef struct } pgis_abs; +typedef struct +{ + Datum id; //Id, from function parameter + Datum geom; //the geometry from function parameter +} +geom_id; + + +typedef struct +{ + uint8_t variant; + int precision; //number of decimals in coordinates + int n_rows; + int max_rows; + geom_id *geoms; + int method; +} +twkb_state; + + /** ** We're never going to use this type externally so the in/out ** functions are dummies. @@ -138,6 +161,91 @@ pgis_geometry_accum_transfn(PG_FUNCTION_ARGS) PG_RETURN_POINTER(p); } + + +PG_FUNCTION_INFO_V1(pgis_twkb_accum_transfn); +Datum +pgis_twkb_accum_transfn(PG_FUNCTION_ARGS) +{ + MemoryContext aggcontext; + MemoryContext oldcontext; + twkb_state* state; + int32 newlen; + text *endianess; + GSERIALIZED *geom; + int i; + uint8_t variant = 0; + +if (!AggCheckCallContext(fcinfo, &aggcontext)) + { + /* cannot be called directly because of dummy-type argument */ + elog(ERROR, "array_agg_transfn called in non-aggregate context"); + aggcontext = NULL; /* keep compiler quiet */ + } +oldcontext = MemoryContextSwitchTo(aggcontext); + if ( PG_ARGISNULL(0) ) + { + /*state gets palloced and 10 geometry elements too + don't forget to free*/ + + + + state=palloc(sizeof(twkb_state)); + state->geoms = palloc(10*sizeof(geom_id)); + state->max_rows = 10; + state->n_rows = 0; + + /* If user specified precision, respect it */ + state->precision = PG_ARGISNULL(2) ? (int) 0 : PG_GETARG_INT32(2); + + + /* If user specified endianness, respect it */ + //endianess = PG_ARGISNULL(4) ? 0 : PG_GETARG_TEXT_P(4); + + endianess = PG_GETARG_TEXT_P(4); + if ( ! strncmp(VARDATA(endianess), "xdr", 3) || + ! strncmp(VARDATA(endianess), "XDR", 3) ) + { + variant = variant | WKB_XDR; + } + else + { + variant = variant | WKB_NDR; + } + state->variant=variant; + + /* If user specified method, respect it + This will probably be taken away when we can decide which compression method that is best */ + state->method = PG_ARGISNULL(5) ? (int) 0 : PG_GETARG_INT32(5); + + } + else + { + state = PG_GETARG_POINTER(0); + + if(!((state->n_rows)<(state->max_rows))) + { + newlen = (state->max_rows)*2; + /* switch to aggregate memory context */ + + state->geoms = (geom_id*)repalloc((void*)(state->geoms),newlen*sizeof(geom_id)); + + state->max_rows = newlen; + } + + } + + geom = (GSERIALIZED*)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); + + ((state->geoms)+state->n_rows)->geom = PG_ARGISNULL(1) ? (Datum) 0 : PointerGetDatum(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(1))); + ((state->geoms)+state->n_rows)->id = PG_ARGISNULL(3) ? (int) 0 : PG_GETARG_INT32(3); + + (state->n_rows)++; +MemoryContextSwitchTo(oldcontext); + + PG_RETURN_POINTER(state); +} + Datum pgis_accum_finalfn(pgis_abs *p, MemoryContext mctx, FunctionCallInfo fcinfo); /** @@ -236,6 +344,105 @@ pgis_geometry_collect_finalfn(PG_FUNCTION_ARGS) PG_RETURN_DATUM(result); } +/** +* The "twkb" final function passes the geometry[] to a twkb +* conversion before returning the result. +*/ +PG_FUNCTION_INFO_V1(pgis_twkb_accum_finalfn); +Datum +pgis_twkb_accum_finalfn(PG_FUNCTION_ARGS) +{ + int i; + twkb_state *state; + geom_id *geom_array; + twkb_geom_arrays lwgeom_arrays; + GSERIALIZED *geom; + LWGEOM *lwgeom; + size_t twkb_size; + uint8_t *twkb; + bytea *result; + state = PG_GETARG_POINTER(0); + lwgeom_arrays.n_points=lwgeom_arrays.n_linestrings=lwgeom_arrays.n_polygons=lwgeom_arrays.n_collections=0; + geom_array=state->geoms; + + + + + for (i=0;in_rows;i++) + { + geom = (GSERIALIZED*)PG_DETOAST_DATUM((geom_array+i)->geom); + lwgeom = lwgeom_from_gserialized(geom); + switch ( lwgeom->type ) + { + case POINTTYPE: + if (lwgeom_arrays.n_points==0) + lwgeom_arrays.points = palloc(((state->n_rows)-i)*sizeof(geom_id)); + + (lwgeom_arrays.points+lwgeom_arrays.n_points)->geom=lwgeom; + (lwgeom_arrays.points+lwgeom_arrays.n_points)->id=(geom_array+i)->id; + (lwgeom_arrays.n_points)++; + break; + /* LineString and CircularString both have 'points' elements */ + case LINETYPE: + if (lwgeom_arrays.n_linestrings==0) + lwgeom_arrays.linestrings = palloc(((state->n_rows)-i)*sizeof(geom_id)); + + (lwgeom_arrays.linestrings+lwgeom_arrays.n_linestrings)->geom=lwgeom; + (lwgeom_arrays.linestrings+lwgeom_arrays.n_linestrings)->id=(geom_array+i)->id; + (lwgeom_arrays.n_linestrings)++; + break; + + /* Polygon has 'nrings' and 'rings' elements */ + case POLYGONTYPE: + if (lwgeom_arrays.n_polygons==0) + lwgeom_arrays.polygons = palloc(((state->n_rows)-i)*sizeof(geom_id)); + + (lwgeom_arrays.polygons+lwgeom_arrays.n_polygons)->geom=lwgeom; + (lwgeom_arrays.polygons+lwgeom_arrays.n_polygons)->id=(geom_array+i)->id; + (lwgeom_arrays.n_polygons)++; + break; + + /* Triangle has one ring of three points + case TRIANGLETYPE: + return lwtriangle_to_twkb_buf((LWTRIANGLE*)geom, buf, variant); + */ + /* All these Collection types have 'ngeoms' and 'geoms' elements */ + case MULTIPOINTTYPE: + case MULTILINETYPE: + case MULTIPOLYGONTYPE: + case COLLECTIONTYPE: + if (lwgeom_arrays.n_collections==0) + lwgeom_arrays.collections = palloc(((state->n_rows)-i)*sizeof(geom_id)); + + (lwgeom_arrays.collections+lwgeom_arrays.n_collections)->geom=lwgeom; + (lwgeom_arrays.collections+lwgeom_arrays.n_collections)->id=(geom_array+i)->id; + (lwgeom_arrays.n_collections)++; + break; + + /* Unknown type! */ + default: + lwerror("Unsupported geometry type: %s [%d]", lwtype_name(lwgeom->type), lwgeom->type); + } + + } + + twkb = lwgeom_agg_to_twkb(&lwgeom_arrays, state->variant , &twkb_size,(int8_t) state->precision,state->method); + + + /* Clean up and return */ + /* Prepare the PgSQL text return type */ + + result = palloc(twkb_size + VARHDRSZ); + SET_VARSIZE(result, twkb_size+VARHDRSZ); + memcpy(VARDATA(result), twkb, twkb_size); + + /* Clean up and return */ + pfree(twkb); + + PG_RETURN_BYTEA_P(result); +} + + /** * The "polygonize" final function passes the geometry[] to a polygonization * before returning the result. diff --git a/postgis/lwgeom_inout.c b/postgis/lwgeom_inout.c index e0652fb07..a5f463b75 100644 --- a/postgis/lwgeom_inout.c +++ b/postgis/lwgeom_inout.c @@ -403,6 +403,77 @@ Datum WKBFromLWGEOM(PG_FUNCTION_ARGS) } +PG_FUNCTION_INFO_V1(TWKBFromLWGEOM); +Datum TWKBFromLWGEOM(PG_FUNCTION_ARGS) +{ + GSERIALIZED *geom = (GSERIALIZED*)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + LWGEOM *lwgeom; + uint8_t *twkb; + size_t twkb_size; + uint8_t variant = 0; + bytea *result; + text *type; + int id,prec,method; + + /* If user specified precision, respect it */ + if ( (PG_NARGS()>1) && (!PG_ARGISNULL(1)) ) + { + prec = PG_GETARG_INT32(1); + if (fabs(prec)>7) + lwerror("precision cannot be more than 7"); + } + else + prec=0; + + /* If user specified id, respect it */ + if ( (PG_NARGS()>2) && (!PG_ARGISNULL(2)) ) + { + id = PG_GETARG_INT32(2); + } + else + id=0; + + /* If user specified endianness, respect it */ + if ( (PG_NARGS()>3) && (!PG_ARGISNULL(3)) ) + { + type = PG_GETARG_TEXT_P(3); + + if ( ! strncmp(VARDATA(type), "xdr", 3) || + ! strncmp(VARDATA(type), "XDR", 3) ) + { + variant = variant | WKB_XDR; + } + else + { + variant = variant | WKB_NDR; + } + } + /* If user specified method, respect it + This will probably be taken away when we can decide which compression method that is best */ + if ( (PG_NARGS()>4) && (!PG_ARGISNULL(4)) ) + { + method = PG_GETARG_INT32(4); + } + else + method=0; + + /* Create TWKB bin string */ + lwgeom = lwgeom_from_gserialized(geom); + twkb = lwgeom_to_twkb(lwgeom, variant , &twkb_size,(int8_t) prec,(int32_t) id,method); + lwgeom_free(lwgeom); + + /* Prepare the PgSQL text return type */ + result = palloc(twkb_size + VARHDRSZ); + memcpy(VARDATA(result), twkb, twkb_size); + + SET_VARSIZE(result, twkb_size+VARHDRSZ); + + /* Clean up and return */ + pfree(twkb); + PG_FREE_IF_COPY(geom, 0); + PG_RETURN_BYTEA_P(result); +} + /* puts a bbox inside the geometry */ PG_FUNCTION_INFO_V1(LWGEOM_addBBOX); Datum LWGEOM_addBBOX(PG_FUNCTION_ARGS) diff --git a/postgis/postgis.sql.in b/postgis/postgis.sql.in index 06313e3d4..42d871dc6 100644 --- a/postgis/postgis.sql.in +++ b/postgis/postgis.sql.in @@ -1343,6 +1343,37 @@ CREATE OR REPLACE FUNCTION ST_AsEWKT(geometry) AS 'MODULE_PATHNAME','LWGEOM_asEWKT' LANGUAGE 'c' IMMUTABLE STRICT; +-- Availability: 2.2.0 +CREATE OR REPLACE FUNCTION ST_AsTWKB(geometry) + RETURNS bytea + AS 'MODULE_PATHNAME','TWKBFromLWGEOM' + LANGUAGE 'c' IMMUTABLE STRICT; + + +-- Availability: 2.2.0 +CREATE OR REPLACE FUNCTION ST_AsTWKB(geometry,int4) + RETURNS bytea + AS 'MODULE_PATHNAME','TWKBFromLWGEOM' + LANGUAGE 'c' IMMUTABLE STRICT; + +-- Availability: 2.2.0 +CREATE OR REPLACE FUNCTION ST_AsTWKB(geometry,int4,int4) + RETURNS bytea + AS 'MODULE_PATHNAME','TWKBFromLWGEOM' + LANGUAGE 'c' IMMUTABLE STRICT; + +-- Availability: 2.2.0 +CREATE OR REPLACE FUNCTION ST_AsTWKB(geometry,int4,int4,text) + RETURNS bytea + AS 'MODULE_PATHNAME','TWKBFromLWGEOM' + LANGUAGE 'c' IMMUTABLE STRICT; + +-- Availability: 2.2.0 +CREATE OR REPLACE FUNCTION ST_AsTWKB(geometry,int4,int4,text,int) + RETURNS bytea + AS 'MODULE_PATHNAME','TWKBFromLWGEOM' + LANGUAGE 'c' IMMUTABLE STRICT; + -- Availability: 1.2.2 CREATE OR REPLACE FUNCTION ST_AsEWKB(geometry) RETURNS BYTEA @@ -3314,6 +3345,35 @@ CREATE OR REPLACE FUNCTION pgis_geometry_makeline_finalfn(pgis_abs) AS 'MODULE_PATHNAME' LANGUAGE 'c'; +--Availability: 2.2.0 + +CREATE OR REPLACE FUNCTION pgis_twkb_accum_transfn(internal, geometry,int,int,text) + RETURNS internal + AS 'MODULE_PATHNAME' + LANGUAGE c ; + +CREATE OR REPLACE FUNCTION pgis_twkb_accum_transfn(internal, geometry,int,int,text,int) + RETURNS internal + AS 'MODULE_PATHNAME' + LANGUAGE c ; + +CREATE OR REPLACE FUNCTION pgis_twkb_accum_finalfn(internal) + RETURNS bytea + AS 'MODULE_PATHNAME' + LANGUAGE c ; + +CREATE AGGREGATE st_astwkb_agg(geometry,int,int,text) ( + SFUNC=pgis_twkb_accum_transfn, + STYPE=internal, + FINALFUNC=pgis_twkb_accum_finalfn +); + +CREATE AGGREGATE st_astwkb_agg(geometry,int,int,text,int) ( + SFUNC=pgis_twkb_accum_transfn, + STYPE=internal, + FINALFUNC=pgis_twkb_accum_finalfn +); + -- Availability: 1.2.2 CREATE AGGREGATE ST_Accum ( sfunc = pgis_geometry_accum_transfn,