*
* PostGIS - Spatial Types for PostgreSQL
*
+ * Copyright (C) 2015 Paul Ramsey
* Copyright (C) 2014 Nicklas Avén
*
* This is free software; you can redistribute and/or modify it under
*
**********************************************************************/
-#include <math.h>
-#include "liblwgeom_internal.h"
-#include "lwgeom_log.h"
-#include "varint.h"
-
-#define TWKB_IN_MAXCOORDS 4
-
-/**
-* Used for passing the parse state between the parsing functions.
-*/
-typedef struct
-{
- /* Pointers to the bytes */
- uint8_t *twkb; /* Points to start of TWKB */
- uint8_t *twkb_end; /* Points to end of TWKB */
- uint8_t *pos; /* Current read position */
-
- uint32_t check; /* Simple validity checks on geometries */
- uint32_t lwtype; /* Current type we are handling */
-
- uint8_t has_bbox;
- uint8_t has_size;
- uint8_t has_idlist;
- uint8_t has_z;
- uint8_t has_m;
- uint8_t is_empty;
-
- /* Precision factors to convert ints to double */
- double factor;
- double factor_z;
- double factor_m;
-
- uint64_t size;
-
- /* Info about current geometry */
- uint8_t magic_byte; /* the magic byte contain info about if twkb contain id, size info, bboxes and precision */
-
- int ndims; /* Number of dimensions */
-
- int64_t *coords; /* An array to keep delta values from 4 dimensions */
-
-} twkb_parse_state;
+#include "lwin_twkb.h"
/**
return val;
}
-static inline double twkb_parse_state_double(twkb_parse_state *s, double factor)
+inline double twkb_parse_state_double(twkb_parse_state *s, double factor)
{
size_t size;
int64_t val = varint_s64_decode(s->pos, s->twkb_end, &size);
}
-static void header_from_twkb_state(twkb_parse_state *s)
+void header_from_twkb_state(twkb_parse_state *s)
{
LWDEBUG(2,"Entering magicbyte_from_twkb_state");
#include "lwgeom_log.h"
#include "varint.h"
#include "lwout_twkb.h"
+#include "lwin_twkb.h"
/**********************************************************************/
+typedef struct
+{
+ int64_t bbox_min[MAX_N_DIMS];
+ int64_t bbox_max[MAX_N_DIMS];
+ double min_factor;
+ double min_factor_z;
+ double min_factor_m;
+ int has_z;
+ int has_m;
+ int is_empty;
+} twkb_collection_data;
+
+
/**
* 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)
+static twkb_collection_data twkb_get_bboxes(uint8_t **twkb, size_t *sizes, 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;
- }
+ LWDEBUG(2,"Entering box_maxmin");
+ twkb_parse_state s;
+ double min, max;
+ int i;
+ twkb_collection_data res;
+ GBOX bbox;
+ res.has_z = 0;
+ res.has_m = 0;
+ res.min_factor = res.min_factor_z = res.min_factor_m = 100000000;
+ res.is_empty = 1;
+ bbox.xmin = bbox.ymin = bbox.zmin = bbox.mmin = FLT_MAX;
+ bbox.xmax = bbox.ymax = bbox.zmax = bbox.mmax = FLT_MIN;
/*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;
+ LWDEBUGF(2,"Read header of buffer %d",i);
+
+ /* Zero out the state */
+ memset(&s, 0, sizeof(twkb_parse_state));
+ /* Initialize the state appropriately */
+ s.twkb = s.pos = twkb[i];
+ s.twkb_end = twkb[i] + sizes[i];
+ header_from_twkb_state(&s);
- /*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(!s.is_empty)
+ res.is_empty = 0;
+ if(!s.has_bbox)
+ lwerror("We can only collect twkb with included bbox");
- 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;
- }
+ /*read bbox and expand to all*/
- n_dims=2+has_z+has_m;
- if(has_size)
+ if(s.factor<res.min_factor)
+ res.min_factor=s.factor;
+
+ /* X */
+ min = twkb_parse_state_double(&s, s.factor);
+ if(min < bbox.xmin)
+ bbox.xmin=min;
+ max = min + twkb_parse_state_double(&s, s.factor);
+ if(max > bbox.xmax)
+ bbox.xmax=max;
+ /* Y */
+ min = twkb_parse_state_double(&s, s.factor);
+ if(min < bbox.ymin)
+ bbox.ymin=min;
+ max = min + twkb_parse_state_double(&s, s.factor);
+ if(max > bbox.ymax)
+ bbox.ymax=max;
+ /* Z */
+ if ( s.has_z )
{
- /*jump over size_value*/
- cursor+=varint_size(buf+cursor, buf+buf_size);
+ if(s.factor_z<res.min_factor_z)
+ res.min_factor_z=s.factor_z;
+ res.has_z= 1;
+ min = twkb_parse_state_double(&s, s.factor_z);
+ if(min < bbox.zmin)
+ bbox.zmin=min;
+ max = min + twkb_parse_state_double(&s, s.factor_z);
+ if(max > bbox.zmax)
+ bbox.zmax=max;
}
-
- 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++)
+ /* M */
+ if ( s.has_m )
{
- 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;
- }
+ if(s.factor_m<res.min_factor_m)
+ res.min_factor_m=s.factor_m;
+ res.has_m= 1;
+ min = twkb_parse_state_double(&s, s.factor_m);
+ if(min < bbox.mmin)
+ bbox.mmin=min;
+ max = min + twkb_parse_state_double(&s, s.factor_m);
+ if(max > bbox.mmax)
+ bbox.mmax=max;
+ }
+ }
+
+ res.bbox_min[0] = (int64_t) lround(bbox.xmin*res.min_factor);
+ res.bbox_max[0] = (int64_t) lround(bbox.xmax*res.min_factor);
+ res.bbox_min[1] = (int64_t) lround(bbox.ymin*res.min_factor);
+ res.bbox_max[1] = (int64_t) lround(bbox.ymax*res.min_factor);
+
+ if ( res.has_z )
+ {
+ res.bbox_min[2] = (int64_t) lround(bbox.zmin*res.min_factor);
+ res.bbox_max[2] = (int64_t) lround(bbox.zmax*res.min_factor);
+ }
+ if ( res.has_m )
+ {
+ res.bbox_min[2+res.has_z] = (int64_t) lround(bbox.mmin*res.min_factor);
+ res.bbox_max[2+res.has_z] = (int64_t) lround(bbox.mmax*res.min_factor);
}
+ return res;
+}
+
+static uint8_t* twkb_write_new_buffer(twkb_collection_data ts_data,uint8_t **twkb, size_t *sizes, size_t *out_size, int64_t *idlist, int n)
+{
+ TWKB_STATE ts;
+ int i;
+ uint8_t type_prec = 0;
+ uint8_t flag = 0;
+ size_t size_to_register=0;
+ uint8_t *twkb_out;
+ int n_dims;
+ int has_extended = (int) (ts_data.has_z||ts_data.has_m);
/*Ok, we have all bounding boxes, then it's time to write*/
ts.header_buf = bytebuffer_create_with_size(16);
/* 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);
+ /* Precision here is only for bbox. We use the worst precission we have found */
+ TYPE_PREC_SET_PREC(type_prec, zigzag8((int) log10(ts_data.min_factor)));
/* Write the type and precision byte */
bytebuffer_append_byte(ts.header_buf, type_prec);
/* Always sizes */
FIRST_BYTE_SET_SIZES(flag,1);
/* Is there idlist? */
- FIRST_BYTE_SET_IDLIST(flag, idlist && ! is_empty);
+ FIRST_BYTE_SET_IDLIST(flag, idlist && !ts_data.is_empty);
/* No extended byte */
- FIRST_BYTE_SET_EXTENDED(flag, 0);
+ FIRST_BYTE_SET_EXTENDED(flag, has_extended);
/* Write the header byte */
bytebuffer_append_byte(ts.header_buf, flag);
+ if(has_extended)
+ {
+ uint8_t flag = 0;
+
+ if ( ts_data.has_z && (log10(ts_data.min_factor_z) > 7 || log10(ts_data.min_factor_z) < 0 ) )
+ lwerror("%s: Z precision cannot be negative or greater than 7", __func__);
+
+ if ( ts_data.has_m && ( log10(ts_data.min_factor_m) > 7 || log10(ts_data.min_factor_m)) )
+ lwerror("%s: M precision cannot be negative or greater than 7", __func__);
+
+ HIGHER_DIM_SET_HASZ(flag, ts_data.has_z);
+ HIGHER_DIM_SET_HASM(flag, ts_data.has_m);
+ HIGHER_DIM_SET_PRECZ(flag,(int) log10(ts_data.min_factor_z));
+ HIGHER_DIM_SET_PRECM(flag,(int) log10(ts_data.min_factor_m));
+ bytebuffer_append_byte(ts.header_buf, flag);
+
+ }
/*Write number of geometries*/
bytebuffer_append_uvarint(ts.geom_buf, n);
}
}
+ n_dims = 2 + ts_data.has_z+ts_data.has_m;
+
+ for ( i = 0; i < n_dims; i++ )
+ {
+ ts.bbox_min[i]=ts_data.bbox_min[i];
+ ts.bbox_max[i]=ts_data.bbox_max[i];
+
+ size_to_register += sizes[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);
twkb_out = ts.header_buf->buf_start;
- lwfree(ts.header_buf);
+ lwfree(ts.header_buf);
return twkb_out;
}
+/**
+* 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_collection_data ts_data = twkb_get_bboxes(twkb, sizes, n);
+
+ return twkb_write_new_buffer(ts_data,twkb, sizes, out_size, idlist, n);
+}