lwgeom_transform.o \
lwunionfind.o \
effectivearea.o \
- varint.o
+ varint.o \
+ twkb_tools.o
NM_OBJS = \
lwspheroid.o
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
******************************************************************************/
* 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];
* 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__);
* 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__);
* 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__);
* 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;
* 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;
* 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;
* 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__);
}
-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;
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
--- /dev/null
+/**********************************************************************
+ *
+ * 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;
+}
}
+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)
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,