Adding ST_CollectTWKB
authorNicklas Avén <nicklas.aven@jordogskog.no>
Mon, 29 Jun 2015 22:57:13 +0000 (22:57 +0000)
committerNicklas Avén <nicklas.aven@jordogskog.no>
Mon, 29 Jun 2015 22:57:13 +0000 (22:57 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@13753 b70326c6-7e19-0410-871a-916f4a2858ee

liblwgeom/Makefile.in
liblwgeom/liblwgeom.h.in
liblwgeom/lwout_twkb.c
liblwgeom/lwout_twkb.h
liblwgeom/twkb_tools.c [new file with mode: 0644]
postgis/lwgeom_inout.c
postgis/postgis.sql.in

index 4b3db6686baad88021ec16e001ec311713aa91aa..8ec7e0898651c70fdb0ca4a462ce9ec4102b8d7c 100644 (file)
@@ -91,7 +91,8 @@ SA_OBJS = \
        lwgeom_transform.o \
        lwunionfind.o \
        effectivearea.o \
-       varint.o
+       varint.o \
+       twkb_tools.o
 
 NM_OBJS = \
        lwspheroid.o
index c56107ef3dd0dbdb88a60039374c8e83578fe3a9..d1c370ac9c36ac11ba718e0a6609c6e1e7a3c9c3 100644 (file)
@@ -2002,6 +2002,8 @@ extern uint8_t* lwgeom_to_twkb(const LWGEOM *geom, uint8_t variant, int8_t preci
 
 extern uint8_t* lwgeom_to_twkb_with_idlist(const LWGEOM *geom, int64_t *idlist, uint8_t variant, int8_t precision_xy, int8_t precision_z, int8_t precision_m, size_t *twkb_size);
 
+extern uint8_t* twkb_to_twkbcoll(uint8_t **twkb, size_t *sizes,size_t *out_size, int64_t *idlist, int n);
+
 /*******************************************************************************
  * SQLMM internal functions - TODO: Move into separate header files
  ******************************************************************************/
index 04d817432b5a3ea298e36925986a0f0c7f0874b9..5340794a94f236032d04bc1c16223faadb6eab12 100644 (file)
@@ -55,7 +55,7 @@ static uint8_t lwgeom_twkb_type(const LWGEOM *geom)
 * Calculates the size of the bbox in varints in the form:
 * xmin, xdelta, ymin, ydelta
 */
-static size_t sizeof_bbox(TWKB_STATE *ts, int ndims)
+size_t sizeof_bbox(TWKB_STATE *ts, int ndims)
 {
        int i;
        uint8_t buf[16];
@@ -72,7 +72,7 @@ static size_t sizeof_bbox(TWKB_STATE *ts, int ndims)
 * Writes the bbox in varints in the form:
 * xmin, xdelta, ymin, ydelta
 */
-static void write_bbox(TWKB_STATE *ts, int ndims)
+void write_bbox(TWKB_STATE *ts, int ndims)
 {
        int i;
        LWDEBUGF(2, "Entered %s", __func__);
@@ -214,7 +214,7 @@ static int ptarray_to_twkb_buf(const POINTARRAY *pa, TWKB_GLOBALS *globals, TWKB
 * POINTS
 *******************************************************************/
 
-static int lwpoint_to_twkb_buf(const LWPOINT *pt, TWKB_GLOBALS *globals, TWKB_STATE *ts)
+int lwpoint_to_twkb_buf(const LWPOINT *pt, TWKB_GLOBALS *globals, TWKB_STATE *ts)
 {
        LWDEBUGF(2, "Entered %s", __func__);
 
@@ -227,7 +227,7 @@ static int lwpoint_to_twkb_buf(const LWPOINT *pt, TWKB_GLOBALS *globals, TWKB_ST
 * LINESTRINGS
 *******************************************************************/
 
-static int lwline_to_twkb_buf(const LWLINE *line, TWKB_GLOBALS *globals, TWKB_STATE *ts)
+int lwline_to_twkb_buf(const LWLINE *line, TWKB_GLOBALS *globals, TWKB_STATE *ts)
 {
        LWDEBUGF(2, "Entered %s", __func__);
 
@@ -240,7 +240,7 @@ static int lwline_to_twkb_buf(const LWLINE *line, TWKB_GLOBALS *globals, TWKB_ST
 * POLYGONS
 *******************************************************************/
 
-static int lwpoly_to_twkb_buf(const LWPOLY *poly, TWKB_GLOBALS *globals, TWKB_STATE *ts)
+int lwpoly_to_twkb_buf(const LWPOLY *poly, TWKB_GLOBALS *globals, TWKB_STATE *ts)
 {
        int i;
 
@@ -262,7 +262,7 @@ static int lwpoly_to_twkb_buf(const LWPOLY *poly, TWKB_GLOBALS *globals, TWKB_ST
 * MULTI-GEOMETRYS (MultiPoint, MultiLinestring, MultiPolygon)
 *******************************************************************/
 
-static int lwmulti_to_twkb_buf(const LWCOLLECTION *col, TWKB_GLOBALS *globals, TWKB_STATE *ts)
+int lwmulti_to_twkb_buf(const LWCOLLECTION *col, TWKB_GLOBALS *globals, TWKB_STATE *ts)
 {
        int i;
        int nempty = 0;
@@ -312,7 +312,7 @@ static int lwmulti_to_twkb_buf(const LWCOLLECTION *col, TWKB_GLOBALS *globals, T
 * GEOMETRYCOLLECTIONS
 *******************************************************************/
 
-static int lwcollection_to_twkb_buf(const LWCOLLECTION *col, TWKB_GLOBALS *globals, TWKB_STATE *ts)
+int lwcollection_to_twkb_buf(const LWCOLLECTION *col, TWKB_GLOBALS *globals, TWKB_STATE *ts)
 {
        int i;
 
@@ -345,7 +345,7 @@ static int lwcollection_to_twkb_buf(const LWCOLLECTION *col, TWKB_GLOBALS *globa
 * Handle whole TWKB
 *******************************************************************/
 
-static int lwgeom_to_twkb_buf(const LWGEOM *geom, TWKB_GLOBALS *globals, TWKB_STATE *ts)
+int lwgeom_to_twkb_buf(const LWGEOM *geom, TWKB_GLOBALS *globals, TWKB_STATE *ts)
 {
        LWDEBUGF(2, "Entered %s", __func__);
 
@@ -390,7 +390,7 @@ static int lwgeom_to_twkb_buf(const LWGEOM *geom, TWKB_GLOBALS *globals, TWKB_ST
 }
 
 
-static int lwgeom_write_to_buffer(const LWGEOM *geom, TWKB_GLOBALS *globals, TWKB_STATE *parent_state)
+int lwgeom_write_to_buffer(const LWGEOM *geom, TWKB_GLOBALS *globals, TWKB_STATE *parent_state)
 {
        int i, is_empty, has_z, has_m, ndims;
        size_t bbox_size = 0, optional_precision_byte = 0;
index a1a7024e6828362ae7b5c2c4ebde5ea8177f0264..5b8ff97263e01c3ad6777591fcccad299c83e424 100644 (file)
@@ -71,11 +71,14 @@ typedef struct
        int64_t accum_rels[MAX_N_DIMS]; /*Holds the acculmulated relative values*/
 } TWKB_STATE;
 
-static int lwgeom_to_twkb_buf(const LWGEOM *geom, TWKB_GLOBALS *global_values, TWKB_STATE *ts);
+int lwgeom_to_twkb_buf(const LWGEOM *geom, TWKB_GLOBALS *global_values, TWKB_STATE *ts);
 
-static int lwpoint_to_twkb_buf(const LWPOINT *line, TWKB_GLOBALS *global_values, TWKB_STATE *ts);
-static int lwline_to_twkb_buf(const LWLINE *line, TWKB_GLOBALS *global_values, TWKB_STATE *ts);
-static int lwpoly_to_twkb_buf(const LWPOLY *poly, TWKB_GLOBALS *global_values, TWKB_STATE *ts);
-static int lwcollection_to_twkb_buf(const LWCOLLECTION *col, TWKB_GLOBALS *global_values, TWKB_STATE *ts);
-static int lwgeom_write_to_buffer(const LWGEOM *geom, TWKB_GLOBALS *global_values, TWKB_STATE *parent_state);
+int lwpoint_to_twkb_buf(const LWPOINT *line, TWKB_GLOBALS *global_values, TWKB_STATE *ts);
+int lwline_to_twkb_buf(const LWLINE *line, TWKB_GLOBALS *global_values, TWKB_STATE *ts);
+int lwpoly_to_twkb_buf(const LWPOLY *poly, TWKB_GLOBALS *global_values, TWKB_STATE *ts);
+int lwmulti_to_twkb_buf(const LWCOLLECTION *col, TWKB_GLOBALS *globals, TWKB_STATE *ts);
+int lwcollection_to_twkb_buf(const LWCOLLECTION *col, TWKB_GLOBALS *global_values, TWKB_STATE *ts);
+int lwgeom_write_to_buffer(const LWGEOM *geom, TWKB_GLOBALS *global_values, TWKB_STATE *parent_state);
 
+size_t sizeof_bbox(TWKB_STATE *ts, int ndims);
+void write_bbox(TWKB_STATE *ts, int ndims);
\ No newline at end of file
diff --git a/liblwgeom/twkb_tools.c b/liblwgeom/twkb_tools.c
new file mode 100644 (file)
index 0000000..33fa5fe
--- /dev/null
@@ -0,0 +1,172 @@
+/**********************************************************************
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ *
+ * Copyright (C) 2015 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 <math.h>
+#include "liblwgeom_internal.h"
+#include "lwgeom_log.h"
+#include "varint.h"
+#include "lwout_twkb.h"
+
+/**********************************************************************/
+
+/**
+* A function to collect many twkb-buffers into one collection
+*/
+uint8_t* twkb_to_twkbcoll(uint8_t **twkb, size_t *sizes,size_t *out_size, int64_t *idlist, int n)
+{
+       LWDEBUG(2,"Entering twkb_to_twkbcoll");
+       TWKB_STATE ts;
+       int i,b;
+       uint8_t *buf;
+       uint8_t metabyte;
+       int has_bbox   =  0;
+       int has_size   =  0;
+       int extended_dims = 0;
+       int is_empty   = 0;
+       size_t size;    
+       int cursor;
+       int64_t max, min;
+       int n_dims=0;   
+       uint8_t type_prec = 0;
+       uint8_t flag = 0;
+       size_t size_to_register=0;
+       uint8_t *twkb_out;
+       size_t buf_size;
+       int has_z, has_m;
+       
+       for ( i = 0; i < MAX_N_DIMS; i++ )
+       {
+               /* Reset bbox calculation */
+               ts.bbox_max[i] = INT64_MIN;
+               ts.bbox_min[i] = INT64_MAX;
+       }       
+       
+       /*For each buffer, collect their bounding box*/
+       for (i=0;i<n;i++)
+       {               
+               LWDEBUGF(2,"Read header of buffer %d",i);
+               cursor = 0;             
+               buf = twkb[i];
+               buf_size=sizes[i];
+               size_to_register+=buf_size;
+               
+               /*jump over type byte*/
+               cursor++;               
+               metabyte = *(buf+cursor);
+               cursor++;
+
+               has_bbox   =  metabyte & 0x01;
+               has_size   = (metabyte & 0x02) >> 1;
+               extended_dims = (metabyte & 0x08) >> 3;
+               is_empty   = (metabyte & 0x10) >> 4;
+               
+               if ( extended_dims )
+               {
+                       extended_dims = *(buf+cursor);
+                       cursor++;
+
+                       has_z    = (extended_dims & 0x01);
+                       has_m    = (extended_dims & 0x02) >> 1;
+               }
+               else
+               {
+                       has_z = 0;
+                       has_m = 0;
+               }
+               
+               n_dims=2+has_z+has_m;   
+               if(has_size)
+               {
+                       /*jump over size_value*/
+                       cursor+=varint_size(buf+cursor, buf+buf_size);
+               }
+
+               if(!has_bbox)
+                       lwerror("We can only collect twkb with included bbox");
+               
+               /*read bbox and expand to all*/
+               for(b=0;b<n_dims;b++)
+               {
+                       min = varint_s64_decode(buf+cursor, buf+buf_size, &size);
+                       cursor+=size;
+                       if(min<ts.bbox_min[b])
+                               ts.bbox_min[b]=min;
+                       max = min + varint_s64_decode(buf+cursor, buf+buf_size, &size);
+                       cursor+=size;                                   
+                       if(max>ts.bbox_max[b])
+                               ts.bbox_max[b]=max;                     
+               }                       
+       }
+       
+       /*Ok, we have all bounding boxes, then it's time to write*/
+       ts.header_buf = bytebuffer_create_with_size(16);        
+       ts.geom_buf = bytebuffer_create_with_size(32);  
+       
+       /* The type will always be COLLECTIONTYPE */
+       TYPE_PREC_SET_TYPE(type_prec,COLLECTIONTYPE);
+       /* Precision has no meaning here. All subgeoemtries holds their own precision */
+       TYPE_PREC_SET_PREC(type_prec,0);
+       /* Write the type and precision byte */
+       bytebuffer_append_byte(ts.header_buf, type_prec);
+
+       /* METADATA BYTE */
+       /* Always bboxes */
+       FIRST_BYTE_SET_BBOXES(flag, 1);
+       /* Always sizes */
+       FIRST_BYTE_SET_SIZES(flag,1);
+       /* Is there idlist? */
+       FIRST_BYTE_SET_IDLIST(flag, idlist && ! is_empty);
+       /* No extended byte */
+       FIRST_BYTE_SET_EXTENDED(flag, 0);
+       /* Write the header byte */
+       bytebuffer_append_byte(ts.header_buf, flag);    
+       
+       
+       /*Write number of geometries*/
+       bytebuffer_append_uvarint(ts.geom_buf, n);
+       
+       /* We've been handed an idlist, so write it in */
+       if ( idlist )
+       {
+               for ( i = 0; i < n; i++ )
+               {
+                       bytebuffer_append_varint(ts.geom_buf, idlist[i]);
+               }
+       }
+       
+       /*Write the size of our new collection from size-info to the end*/
+       size_to_register += sizeof_bbox(&ts,n_dims) + bytebuffer_getlength(ts.geom_buf);        
+       bytebuffer_append_uvarint(ts.header_buf, size_to_register);     
+       
+       /*Write the bbox to the buffer*/
+       write_bbox(&ts, n_dims);        
+       
+       /*Put the id-list after the header*/
+       bytebuffer_append_bytebuffer(ts.header_buf,ts.geom_buf);        
+       
+       /*Copy all ingoing buffers "as is" to the new buffer*/
+       for ( i = 0; i < n; i++ )
+       {
+               bytebuffer_append_bulk(ts.header_buf, twkb[i], sizes[i]);               
+       }
+       
+       
+       bytebuffer_destroy(ts.geom_buf);
+       
+       if ( out_size )
+               *out_size = bytebuffer_getlength(ts.header_buf);        
+       
+       twkb_out = ts.header_buf->buf_start;
+
+       lwfree(ts.header_buf);
+       
+       return twkb_out;
+}
index 5e2a7c60e891b03d83d5eeff1e25764459b355ac..44a37e4dda81ee6abc9f25a47e1c07d643f1cfb6 100644 (file)
@@ -659,6 +659,114 @@ Datum TWKBFromLWGEOMArray(PG_FUNCTION_ARGS)
 }
 
 
+PG_FUNCTION_INFO_V1(TWKBFromTWKBArray);
+Datum TWKBFromTWKBArray(PG_FUNCTION_ARGS)
+{
+       ArrayType *arr_twkbs = NULL;
+       ArrayType *arr_ids = NULL;
+       int num_twkbs, num_ids, i = 0;
+
+       ArrayIterator iter_twkbs, iter_ids;
+       bool null_twkb, null_id;
+       Datum val_twkb, val_id;
+
+       uint8_t **col = NULL;
+       size_t *sizes = NULL;
+       int64_t *idlist = NULL;
+
+       uint8_t *twkb;
+       size_t twkb_size;
+       size_t out_size;
+       bytea *result;
+       bytea *bytea_twkb;
+       /* The first two arguments are required */
+       if ( PG_NARGS() < 2 || PG_ARGISNULL(0) || PG_ARGISNULL(1) ) 
+               PG_RETURN_NULL();
+
+       arr_twkbs = PG_GETARG_ARRAYTYPE_P(0);
+       arr_ids = PG_GETARG_ARRAYTYPE_P(1);
+
+       num_twkbs = ArrayGetNItems(ARR_NDIM(arr_twkbs), ARR_DIMS(arr_twkbs));
+       num_ids = ArrayGetNItems(ARR_NDIM(arr_ids), ARR_DIMS(arr_ids));
+       
+       if ( num_twkbs != num_ids )
+       {
+               elog(ERROR, "size of geometry[] and integer[] arrays must match");
+               PG_RETURN_NULL();
+       }
+       
+       /* Loop through array and build a collection of geometry and */
+       /* a simple array of ids. If either side is NULL, skip it */
+
+#if POSTGIS_PGSQL_VERSION >= 95        
+       iter_twkbs = array_create_iterator(arr_twkbs, 0, NULL);
+       iter_ids = array_create_iterator(arr_ids, 0, NULL);
+#else
+       iter_twkbs = array_create_iterator(arr_twkbs, 0);
+       iter_ids = array_create_iterator(arr_ids, 0);
+#endif
+       /* Construct collection/idlist first time through */
+               col = palloc0(num_twkbs * sizeof(void*));
+               sizes = palloc0(num_twkbs * sizeof(size_t));
+               idlist = palloc0(num_twkbs * sizeof(int64_t));
+       
+       while( array_iterate(iter_twkbs, &val_twkb, &null_twkb) && 
+              array_iterate(iter_ids, &val_id, &null_id) )
+       {
+               int32_t uid;
+
+               if ( null_twkb || null_id )
+               {
+                       elog(NOTICE, "ST_CollectTWKB skipping NULL entry at position %d", i);
+                       continue;
+               }
+               
+               bytea_twkb =(bytea*) DatumGetPointer(val_twkb);
+               uid = DatumGetInt64(val_id);
+               
+
+
+               twkb_size=VARSIZE_ANY_EXHDR(bytea_twkb);
+
+               /* Store the values */
+               col[i] = (uint8_t*)VARDATA(bytea_twkb);
+               sizes[i] = twkb_size;
+               idlist[i] = uid;
+               i++;
+               
+       }
+       
+       array_free_iterator(iter_twkbs);
+       array_free_iterator(iter_ids);
+       
+       
+       if(i==0)
+       {
+               elog(NOTICE, "No valid geometry - id pairs found");
+               PG_FREE_IF_COPY(arr_twkbs, 0);
+               PG_FREE_IF_COPY(arr_ids, 1);
+               PG_RETURN_NULL();               
+       }
+
+       /* Write out the TWKB */
+        twkb = twkb_to_twkbcoll(col, sizes,&out_size, idlist, i);
+                                         
+       /* Convert to a bytea return type */
+       result = palloc(out_size + VARHDRSZ);
+       memcpy(VARDATA(result), twkb,out_size);
+       SET_VARSIZE(result, out_size + VARHDRSZ);
+       
+       /* Clean up */
+       //~ pfree(twkb);
+       pfree(idlist);
+       PG_FREE_IF_COPY(arr_twkbs, 0);
+       PG_FREE_IF_COPY(arr_ids, 1);
+       
+       PG_RETURN_BYTEA_P(result);
+       //~ PG_RETURN_NULL();
+}
+
+
 /* puts a bbox inside the geometry */
 PG_FUNCTION_INFO_V1(LWGEOM_addBBOX);
 Datum LWGEOM_addBBOX(PG_FUNCTION_ARGS)
index d4dc7d6ad00636f1b2eb799662573c03167b149e..dacd1368125722efcec731e05717bcf583c630b3 100644 (file)
@@ -3560,6 +3560,12 @@ CREATE OR REPLACE FUNCTION ST_Collect(geometry[])
        AS 'MODULE_PATHNAME', 'LWGEOM_collect_garray'
        LANGUAGE 'c' IMMUTABLE STRICT;
 
+-- Availability: 2.2.0
+CREATE OR REPLACE FUNCTION ST_CollectTWKB(twkb bytea[], ids bigint[])
+       RETURNS bytea
+       AS 'MODULE_PATHNAME','TWKBFromTWKBArray'
+       LANGUAGE 'c' IMMUTABLE;
+
 -- Availability: 1.2.2
 CREATE AGGREGATE ST_MemUnion (
        basetype = geometry,