Add GeomFromTWKB #2917

git-svn-id: http://svn.osgeo.org/postgis/trunk@13053 b70326c6-7e19-0410-871a-916f4a2858ee
This commit is contained in:
Nicklas Avén 2014-10-09 18:24:39 +00:00
parent 5b8b6ee9e1
commit eaaf0e6a0e
13 changed files with 883 additions and 0 deletions

View file

@ -982,6 +982,70 @@ SELECT
</refsection>
</refentry>
<refentry id="ST_GeomFromTWKB">
<refnamediv>
<refname>ST_GeomFromTWKB</refname>
<refpurpose>Creates a geometry instance from a Tiny Well-Known Binary geometry
representation (TWKB).</refpurpose>
</refnamediv>
<refsynopsisdiv>
<funcsynopsis>
<funcprototype>
<funcdef>geometry <function>ST_GeomFromTWKB</function></funcdef>
<paramdef><type>bytea </type> <parameter>geom</parameter></paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>
<refsection>
<title>Description</title>
<para>The <varname>ST_GeomFromTWKB</varname> function, takes a tiny well-known
binary representation of a geometry and creates an instance of the appropriate
geometry type.</para>
<para>SRID always defaults to 0 (Unknown).</para>
<note>
<para>TWKB doesn't make any difference of Z and M. So a 3D geoemtry in TWKB will always be a GeometryZ, like PointZ when converted to PostGIS geometry</para>
</note>
</refsection>
<refsection>
<title>Examples</title>
<programlisting>--Although bytea rep contains single \, these need to be escaped when inserting into a table
-- unless standard_conforming_strings is set to on.
SELECT ST_AsEWKT(
ST_GeomFromTWKB(E'\\x304202f7f40dbce4040105')
);
st_asewkt
------------------------------------------------------
LINESTRING(-113.98 39.198,-113.981 39.195)
(1 row)
SELECT
ST_AsText(
ST_GeomFromTWKB(
ST_AsTWKB('POINT(2 5)'::geometry,0)
)
);
st_astext
------------
POINT(2 5)
(1 row)</programlisting>
</refsection>
<!-- Optionally add a "See Also" section -->
<refsection>
<title>See Also</title>
<para><xref linkend="ST_GeomFromWKB" />,<xref linkend="ST_AsTWKB" />,<xref linkend="ST_AsTWKBAgg" />,<xref linkend="ST_WKBToSQL" />, <xref linkend="ST_AsBinary" />, <xref linkend="ST_GeomFromEWKB" /></para>
</refsection>
</refentry>
<refentry id="ST_LineFromEncodedPolyline">
<refnamediv>
<refname>ST_LineFromEncodedPolyline</refname>

View file

@ -1180,6 +1180,9 @@ SELECT (ST_AsLatLonText('POINT (-302.2342342 -792.32498)'));
<note>
<para>TWKB is still a moving target. The format is described <ulink url="https://github.com/nicklasaven/TWKB">https://github.com/nicklasaven/TWKB</ulink> , and code for building a client can be found <ulink url="https://github.com/nicklasaven/twkb_web">https://github.com/nicklasaven/twkb_web"</ulink></para>
</note>
<note>
<para>TWKB doesn't make any difference of Z and M. So PointM and PointZ will give the same twkb representation.</para>
</note>
<para>Availability: 2.2.0</para>
</refsection>

View file

@ -56,6 +56,7 @@ SA_OBJS = \
lwout_wkb.o \
lwin_geojson.o \
lwin_wkb.o \
lwin_twkb.o \
lwout_wkt.o \
lwout_twkb.o \
lwin_wkt_parse.o \

View file

@ -53,6 +53,7 @@ OBJS= \
cu_out_x3d.o \
cu_in_geojson.o \
cu_in_wkb.o \
cu_in_twkb.o \
cu_in_wkt.o \
cu_in_encoded_polyline.o \
cu_varint.o \

View file

@ -0,0 +1,176 @@
/**********************************************************************
* $Id: cu_out_twkb.c 6036 2010-10-03 18:14:35Z pramsey $
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.net
* Copyright 2010 Paul Ramsey <pramsey@cleverelephant.ca>
*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "CUnit/Basic.h"
#include "liblwgeom_internal.h"
#include "cu_tester.h"
/*
** Global variable to hold TWKB strings
*/
char *hex_a;
char *hex_b;
/*
** The suite initialization function.
** Create any re-used objects.
*/
static int init_twkb_in_suite(void)
{
hex_a = NULL;
hex_b = NULL;
return 0;
}
/*
** The suite cleanup function.
** Frees any global objects.
*/
static int clean_twkb_in_suite(void)
{
if (hex_a) free(hex_a);
if (hex_b) free(hex_b);
hex_a = NULL;
hex_b = NULL;
return 0;
}
static void cu_twkb_in(char *wkt)
{
LWGEOM_PARSER_RESULT pr;
LWGEOM *g_a, *g_b;
uint8_t *twkb_a, *twkb_b;
size_t twkb_size_a, twkb_size_b;
/* int i; char *hex; */
if ( hex_a ) free(hex_a);
if ( hex_b ) free(hex_b);
/* Turn WKT into geom */
lwgeom_parse_wkt(&pr, wkt, LW_PARSER_CHECK_NONE);
if ( pr.errcode )
{
printf("ERROR: %s\n", pr.message);
printf("POSITION: %d\n", pr.errlocation);
exit(0);
}
/* Get the geom */
g_a = pr.geom;
/* Turn geom into TWKB */
twkb_a =lwgeom_to_twkb(g_a, 0, &twkb_size_a,0,0);
/* Turn WKB back into geom */
g_b = lwgeom_from_twkb(twkb_a, twkb_size_a, LW_PARSER_CHECK_NONE);
/* Turn geom to WKB again */
twkb_b = lwgeom_to_twkb(g_b, 0, &twkb_size_b,0,0);
/* Turn geoms into WKB for comparisons */
hex_a = hexbytes_from_bytes(twkb_a, twkb_size_a);
hex_b = hexbytes_from_bytes(twkb_b, twkb_size_b);
/* Clean up */
lwfree(twkb_a);
lwfree(twkb_b);
lwgeom_parser_result_free(&pr);
lwgeom_free(g_b);
}
static void test_twkb_in_point(void)
{
cu_twkb_in("POINT(0 0 0 0)");
// printf("old: %s\nnew: %s\n",hex_a, hex_b);
CU_ASSERT_STRING_EQUAL(hex_a, hex_b);
cu_twkb_in("POINT(1 1)");
// printf("old: %s\nnew: %s\n",hex_a, hex_b);
CU_ASSERT_STRING_EQUAL(hex_a, hex_b);
}
static void test_twkb_in_linestring(void)
{
cu_twkb_in("LINESTRING(0 0,1 1)");
CU_ASSERT_STRING_EQUAL(hex_a, hex_b);
cu_twkb_in("LINESTRING(0 0 1,1 1 2,2 2 3)");
CU_ASSERT_STRING_EQUAL(hex_a, hex_b);
}
static void test_twkb_in_polygon(void)
{
cu_twkb_in("POLYGON((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0))");
CU_ASSERT_STRING_EQUAL(hex_a, hex_b);
cu_twkb_in("POLYGON((0 0 0 1,0 1 0 2,1 1 0 3,1 0 0 4,0 0 0 5))");
CU_ASSERT_STRING_EQUAL(hex_a, hex_b);
cu_twkb_in("POLYGON((0 0 0 1,0 1 0 2,1 1 0 3,1 0 0 4,0 0 0 5))");
CU_ASSERT_STRING_EQUAL(hex_a, hex_b);
cu_twkb_in("POLYGON EMPTY");
CU_ASSERT_STRING_EQUAL(hex_a, hex_b);
}
static void test_twkb_in_multipoint(void)
{
cu_twkb_in("MULTIPOINT(0 0 0,0 1 0,1 1 0,1 0 0,0 0 1)");
CU_ASSERT_STRING_EQUAL(hex_a, hex_b);
}
static void test_twkb_in_multilinestring(void) {}
static void test_twkb_in_multipolygon(void)
{
cu_twkb_in("MULTIPOLYGON(((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)),((-1 -1 0,-1 2 0,2 2 0,2 -1 0,-1 -1 0),(0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)))");
CU_ASSERT_STRING_EQUAL(hex_a, hex_b);
//printf("old: %s\nnew: %s\n",hex_a, hex_b);
}
static void test_twkb_in_collection(void)
{
cu_twkb_in("GEOMETRYCOLLECTION(POLYGON((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)),POINT(1 1 1))");
CU_ASSERT_STRING_EQUAL(hex_a, hex_b);
cu_twkb_in("GEOMETRYCOLLECTION EMPTY");
CU_ASSERT_STRING_EQUAL(hex_a, hex_b);
cu_twkb_in("GEOMETRYCOLLECTION(MULTIPOLYGON(((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0))),POLYGON((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)),POINT(1 1 1),LINESTRING(0 0 0, 1 1 1))");
CU_ASSERT_STRING_EQUAL(hex_a, hex_b);
}
/*
** Used by test harness to register the tests in this file.
*/
CU_TestInfo twkb_in_tests[] =
{
PG_TEST(test_twkb_in_point),
PG_TEST(test_twkb_in_linestring),
PG_TEST(test_twkb_in_polygon),
PG_TEST(test_twkb_in_multipoint),
PG_TEST(test_twkb_in_multilinestring),
PG_TEST(test_twkb_in_multipolygon),
PG_TEST(test_twkb_in_collection),
CU_TEST_INFO_NULL
};
CU_SuiteInfo twkb_in_suite = {"TWKB In Suite", init_twkb_in_suite, clean_twkb_in_suite, twkb_in_tests};

View file

@ -34,6 +34,7 @@ extern CU_SuiteInfo node_suite;
extern CU_SuiteInfo wkt_out_suite;
extern CU_SuiteInfo wkt_in_suite;
extern CU_SuiteInfo twkb_out_suite;
extern CU_SuiteInfo twkb_in_suite;
extern CU_SuiteInfo wkb_out_suite;
extern CU_SuiteInfo wkb_in_suite;
extern CU_SuiteInfo libgeom_suite;
@ -79,6 +80,7 @@ int main(int argc, char *argv[])
wkt_out_suite,
wkt_in_suite,
twkb_out_suite,
twkb_in_suite,
wkb_out_suite,
wkb_in_suite,
libgeom_suite,

View file

@ -1846,6 +1846,10 @@ extern char *lwgeom_to_ewkt(const LWGEOM *lwgeom);
* @param check parser check flags, see LW_PARSER_CHECK_* macros
*/
extern LWGEOM* lwgeom_from_wkb(const uint8_t *wkb, const size_t wkb_size, const char check);
/**
* @param check parser check flags, see LW_PARSER_CHECK_* macros
*/
extern LWGEOM* lwgeom_from_twkb(uint8_t *twkb, size_t twkb_size, char check);
/**
* @param check parser check flags, see LW_PARSER_CHECK_* macros

533
liblwgeom/lwin_twkb.c Normal file
View file

@ -0,0 +1,533 @@
/**********************************************************************
*
* PostGIS - Spatial Types for PostgreSQL
*
* Copyright (C) 2014 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"
/**
* Used for passing the parse state between the parsing functions.
*/
typedef struct
{
/*General*/
uint8_t *twkb; /* Points to start of TWKB */
uint8_t *twkb_end; /* Expected end of TWKB */
int check; /* Simple validity checks on geometries */
/*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 dimmensions */
int has_z; /*TODO get rid of this*/
int has_m; /*TODO get rid of this*/
int has_bboxes;
double factor; /*precission factor to get the real numbers from the integers*/
uint32_t lwtype; /* Current type we are handling */
/*Parsing info*/
int read_id; /*This is not telling if the twkb uses id or not. It is just for internal use to tell if id shall be read. Like the points inside multipoint doesn't have id*/
uint8_t *pos; /* Current parse position */
int64_t *coords; /*An array to keep delta values from 4 dimmensions*/
} twkb_parse_state;
/**
* Internal function declarations.
*/
LWGEOM* lwgeom_from_twkb_state(twkb_parse_state *s);
/**********************************************************************/
/**
* Check that we are not about to read off the end of the WKB
* array.
*/
static inline void twkb_parse_state_check(twkb_parse_state *s, size_t next)
{
if( (s->pos + next) > s->twkb_end)
lwerror("TWKB structure does not match expected size!");
}
/**
* Take in an unknown kind of wkb type number and ensure it comes out
* as an extended WKB type number (with Z/M/SRID flags masked onto the
* high bits).
*/
static void lwtype_from_twkb_state(twkb_parse_state *s, uint8_t twkb_type)
{
LWDEBUG(2,"Entering lwtype_from_twkb_state");
switch (twkb_type)
{
case 1:
s->lwtype = POINTTYPE;
break;
case 2:
s->lwtype = LINETYPE;
break;
case 3:
s->lwtype = POLYGONTYPE;
break;
case 4:
s->lwtype = MULTIPOINTTYPE;
break;
case 5:
s->lwtype = MULTILINETYPE;
break;
case 6:
s->lwtype = MULTIPOLYGONTYPE;
break;
case 7:
s->lwtype = COLLECTIONTYPE;
break;
default: /* Error! */
lwerror("Unknown WKB type");
break;
}
LWDEBUGF(4,"Got lwtype %s (%u)", lwtype_name(s->lwtype), s->lwtype);
return;
}
/**
* Byte
* Read a byte and advance the parse state forward.
*/
static uint8_t byte_from_twkb_state(twkb_parse_state *s)
{
LWDEBUG(2,"Entering byte_from_twkb_state");
uint8_t return_value = 0;
twkb_parse_state_check(s, WKB_BYTE_SIZE);
LWDEBUG(4, "Passed state check");
return_value = s->pos[0];
s->pos += WKB_BYTE_SIZE;
return return_value;
}
/**
* POINTARRAY
* Read a dynamically sized point array and advance the parse state forward.
* First read the number of points, then read the points.
*/
static POINTARRAY* ptarray_from_twkb_state(twkb_parse_state *s, uint32_t npoints)
{
LWDEBUG(2,"Entering ptarray_from_twkb_state");
POINTARRAY *pa = NULL;
uint32_t ndims = s->ndims;
int i,j;
LWDEBUGF(4,"Pointarray has %d points", npoints);
/* Empty! */
if( npoints == 0 )
return ptarray_construct(s->has_z, s->has_m, npoints);
double *dlist;
pa = ptarray_construct(s->has_z, s->has_m, npoints);
dlist = (double*)(pa->serialized_pointlist);
for( i = 0; i < npoints; i++ )
{
for (j=0;j<ndims;j++)
{
s->coords[j]+=varint_s64_read(&(s->pos), s->twkb_end);
dlist[(ndims*i)+j] = s->coords[j]/s->factor;
}
}
return pa;
}
/**
* POINT
*/
static LWPOINT* lwpoint_from_twkb_state(twkb_parse_state *s)
{
LWDEBUG(2,"Entering lwpoint_from_twkb_state");
static uint32_t npoints = 1;
/* TODO, make an option to use the id-value and return a set with geometry and id*/
if((s->magic_byte&TWKB_ID) && s->read_id)
varint_64_jump_n(&(s->pos),1, s->twkb_end); //Jump over id value
POINTARRAY *pa = ptarray_from_twkb_state(s,npoints);
return lwpoint_construct(0, NULL, pa);
}
/**
* LINESTRING
*/
static LWLINE* lwline_from_twkb_state(twkb_parse_state *s)
{
LWDEBUG(2,"Entering lwline_from_twkb_state");
uint32_t npoints;
/* TODO, make an option to use the id-value and return a set with geometry and id*/
if((s->magic_byte&TWKB_ID) && s->read_id)
varint_64_jump_n(&(s->pos),1, s->twkb_end); //Jump over id value
/*get number of points*/
npoints = varint_u64_read(&(s->pos), s->twkb_end);
POINTARRAY *pa = ptarray_from_twkb_state(s,npoints);
if( pa == NULL || pa->npoints == 0 )
return lwline_construct_empty(0, s->has_z, s->has_m);
if( s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 2 )
{
lwerror("%s must have at least two points", lwtype_name(s->lwtype));
return NULL;
}
return lwline_construct(0, NULL, pa);
}
/**
* POLYGON
*/
static LWPOLY* lwpoly_from_twkb_state(twkb_parse_state *s)
{
LWDEBUG(2,"Entering lwpoly_from_twkb_state");
uint32_t nrings,npoints;
int i;
/* TODO, make an option to use the id-value and return a set with geometry and id*/
if((s->magic_byte&TWKB_ID) && s->read_id)
varint_64_jump_n(&(s->pos),1, s->twkb_end); //Jump over id value
/*get number of rings*/
nrings= varint_u64_read(&(s->pos), s->twkb_end);
LWPOLY *poly = lwpoly_construct_empty(0, s->has_z, s->has_m);
LWDEBUGF(4,"Polygon has %d rings", nrings);
/* Empty polygon? */
if( nrings == 0 )
return poly;
for( i = 0; i < nrings; i++ )
{
/*get number of points*/
npoints = varint_u64_read(&(s->pos), s->twkb_end);
POINTARRAY *pa = ptarray_from_twkb_state(s,npoints);
if( pa == NULL )
continue;
/* Check for at least four points. */
if( s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 4 )
{
LWDEBUGF(2, "%s must have at least four points in each ring", lwtype_name(s->lwtype));
lwerror("%s must have at least four points in each ring", lwtype_name(s->lwtype));
return NULL;
}
/* Check that first and last points are the same. */
if( s->check & LW_PARSER_CHECK_CLOSURE && ! ptarray_is_closed_2d(pa) )
{
/*TODO copy the first point to the last instead of error*/
LWDEBUGF(2, "%s must have closed rings", lwtype_name(s->lwtype));
lwerror("%s must have closed rings", lwtype_name(s->lwtype));
return NULL;
}
/* Add ring to polygon */
if ( lwpoly_add_ring(poly, pa) == LW_FAILURE )
{
LWDEBUG(2, "Unable to add ring to polygon");
lwerror("Unable to add ring to polygon");
}
}
return poly;
}
/**
* MULTIPOINT
*/
static LWCOLLECTION* lwmultipoint_from_twkb_state(twkb_parse_state *s)
{
LWDEBUG(2,"Entering lwmultipoint_from_twkb_state");
int ngeoms;
LWGEOM *geom = NULL;
/* TODO, make an option to use the id-value and return a set with geometry and id*/
if((s->magic_byte&TWKB_ID) && s->read_id)
varint_64_jump_n(&(s->pos),1, s->twkb_end); //Jump over id value
/*Now we switch off id reading for subgeometries*/
s->read_id=LW_FALSE;
/*get number of geometries*/
ngeoms= varint_u64_read(&(s->pos), s->twkb_end);
LWDEBUGF(4,"Number of geometries %d",ngeoms);
LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype,0, s->has_z, s->has_m);
while (ngeoms>0)
{
geom = (LWGEOM*) lwpoint_from_twkb_state(s);
if ( lwcollection_add_lwgeom(col, geom) == NULL )
{
lwerror("Unable to add geometry (%p) to collection (%p)", geom, col);
return NULL;
}
ngeoms--;
}
/*Better turn it on again because we don't know what will come next*/
s->read_id=LW_FALSE;
return col;
}
/**
* MULTILINESTRING
*/
static LWCOLLECTION* lwmultiline_from_twkb_state(twkb_parse_state *s)
{
LWDEBUG(2,"Entering lwmultilinestring_from_twkb_state");
int ngeoms;
LWGEOM *geom = NULL;
/* TODO, make an option to use the id-value and return a set with geometry and id*/
if((s->magic_byte&TWKB_ID) && s->read_id)
varint_64_jump_n(&(s->pos),1, s->twkb_end); //Jump over id value
/*Now we switch off id reading for subgeometries*/
s->read_id=LW_FALSE;
/*get number of geometries*/
ngeoms= varint_u64_read(&(s->pos), s->twkb_end);
LWDEBUGF(4,"Number of geometries %d",ngeoms);
LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype,0, s->has_z, s->has_m);
while (ngeoms>0)
{
geom = (LWGEOM*) lwline_from_twkb_state(s);
if ( lwcollection_add_lwgeom(col, geom) == NULL )
{
lwerror("Unable to add geometry (%p) to collection (%p)", geom, col);
return NULL;
}
ngeoms--;
}
/*Better turn it on again because we don't know what will come next*/
s->read_id=LW_FALSE;
return col;
}
/**
* MULTIPOLYGON
*/
static LWCOLLECTION* lwmultipoly_from_twkb_state(twkb_parse_state *s)
{
LWDEBUG(2,"Entering lwmultipolygon_from_twkb_state");
int ngeoms;
LWGEOM *geom = NULL;
/* TODO, make an option to use the id-value and return a set with geometry and id*/
if((s->magic_byte&TWKB_ID) && s->read_id)
varint_64_jump_n(&(s->pos),1, s->twkb_end); //Jump over id value
/*Now we switch off id reading for subgeometries*/
s->read_id=LW_FALSE;
/*get number of geometries*/
ngeoms= varint_u64_read(&(s->pos), s->twkb_end);
LWDEBUGF(4,"Number of geometries %d",ngeoms);
LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype,0, s->has_z, s->has_m);
while (ngeoms>0)
{
geom = (LWGEOM*) lwpoly_from_twkb_state(s);
if ( lwcollection_add_lwgeom(col, geom) == NULL )
{
lwerror("Unable to add geometry (%p) to collection (%p)", geom, col);
return NULL;
}
ngeoms--;
}
/*Better turn it on again because we don't know what will come next*/
s->read_id=LW_FALSE;
return col;
}
/**
* COLLECTION, MULTIPOINTTYPE, MULTILINETYPE, MULTIPOLYGONTYPE
**/
static LWCOLLECTION* lwcollection_from_twkb_state(twkb_parse_state *s)
{
LWDEBUG(2,"Entering lwcollection_from_twkb_state");
int ngeoms;
LWGEOM *geom = NULL;
/* TODO, make an option to use the id-value and return a set with geometry and id*/
if((s->magic_byte&TWKB_ID) && s->read_id)
varint_64_jump_n(&(s->pos),1, s->twkb_end); //Jump over id value
/*Now we switch off id reading for subgeometries*/
s->read_id=LW_FALSE;
/*get number of geometries*/
ngeoms= varint_u64_read(&(s->pos), s->twkb_end);
LWDEBUGF(4,"Number of geometries %d",ngeoms);
LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype,0, s->has_z, s->has_m);
while (ngeoms>0)
{
geom = lwgeom_from_twkb_state(s);
if ( lwcollection_add_lwgeom(col, geom) == NULL )
{
lwerror("Unable to add geometry (%p) to collection (%p)", geom, col);
return NULL;
}
ngeoms--;
}
/*We better turn id reading on again because we don't know what will come next*/
s->read_id=LW_FALSE;
return col;
}
static void magicbyte_from_twkb_state(twkb_parse_state *s)
{
LWDEBUG(2,"Entering magicbyte_from_twkb_state");
int precission;
s->has_bboxes=LW_FALSE;
/*Get the first magic byte*/
s->magic_byte = byte_from_twkb_state(s);
/*Read precission from the last 4 bits of the magic_byte*/
precission=(s->magic_byte&0xF0)>>4;
s->factor=pow(10,1.0*precission);
/*If the twkb-geometry has size information we just jump over it*/
if(s->magic_byte&TWKB_SIZES)
varint_64_jump_n(&(s->pos),1, s->twkb_end); //Jump over size information
/*If our dataset has bboxes we just set a flag for that. We cannot do anything about it before we know the number of dimmensions*/
if(s->magic_byte&TWKB_BBOXES)
s->has_bboxes=LW_TRUE;
}
/**
* GEOMETRY
* Generic handling for WKB geometries. The front of every WKB geometry
* (including those embedded in collections) is an endian byte, a type
* number and an optional srid number. We handle all those here, then pass
* to the appropriate handler for the specific type.
*/
LWGEOM* lwgeom_from_twkb_state(twkb_parse_state *s)
{
LWDEBUG(2,"Entering lwgeom_from_twkb_state");
uint8_t type_dim_byte;
uint8_t twkb_type;
/* Read the type and number of dimmensions*/
type_dim_byte = byte_from_twkb_state(s);
twkb_type=type_dim_byte&0x1F;
s->ndims=(type_dim_byte&0xE0)>>5;
s->has_z=LW_FALSE;
s->has_m=LW_FALSE;
if(s->ndims>2) s->has_z=LW_TRUE;
if(s->ndims>3) s->has_m=LW_TRUE;
/*Now we know number of dommensions so we can jump over the bboxes with right number of "jumps"*/
if (s->has_bboxes)
{
varint_64_jump_n(&(s->pos),2*(s->ndims), s->twkb_end); //Jump over bbox
/*We only have bboxes at top level, so once found we forget about it*/
s->has_bboxes=LW_FALSE;
}
lwtype_from_twkb_state(s, twkb_type);
/* Do the right thing */
switch( s->lwtype )
{
case POINTTYPE:
return (LWGEOM*)lwpoint_from_twkb_state(s);
break;
case LINETYPE:
return (LWGEOM*)lwline_from_twkb_state(s);
break;
case POLYGONTYPE:
return (LWGEOM*)lwpoly_from_twkb_state(s);
break;
case MULTIPOINTTYPE:
return (LWGEOM*)lwmultipoint_from_twkb_state(s);
break;
case MULTILINETYPE:
return (LWGEOM*)lwmultiline_from_twkb_state(s);
break;
case MULTIPOLYGONTYPE:
return (LWGEOM*)lwmultipoly_from_twkb_state(s);
break;
case COLLECTIONTYPE:
return (LWGEOM*)lwcollection_from_twkb_state(s);
break;
/* Unknown type! */
default:
lwerror("Unsupported geometry type: %s [%d]", lwtype_name(s->lwtype), s->lwtype);
}
/* Return value to keep compiler happy. */
return NULL;
}
/* TODO add check for SRID consistency */
/**
* WKB inputs *must* have a declared size, to prevent malformed WKB from reading
* off the end of the memory segment (this stops a malevolent user from declaring
* a one-ring polygon to have 10 rings, causing the WKB reader to walk off the
* end of the memory).
*
* Check is a bitmask of: LW_PARSER_CHECK_MINPOINTS, LW_PARSER_CHECK_ODD,
* LW_PARSER_CHECK_CLOSURE, LW_PARSER_CHECK_NONE, LW_PARSER_CHECK_ALL
*/
LWGEOM* lwgeom_from_twkb(uint8_t *twkb, size_t twkb_size, char check)
{
LWDEBUG(2,"Entering lwgeom_from_twkb");
twkb_parse_state s;
/* Initialize the state appropriately */
s.twkb = twkb;
s.twkb_end = twkb+twkb_size;
s.check = check;
s.ndims= 0;
int64_t coords[4] = {0,0,0,0};
s.coords=coords;
s.pos = twkb;
s.read_id=LW_TRUE; /*This is just to tell that IF the twkb uses id it should be read. The functions can switch this off for reading subgeometries for instance*/
LWDEBUGF(4,"twkb_size: %d",(int) twkb_size);
/* Hand the check catch-all values */
if ( check & LW_PARSER_CHECK_NONE )
s.check = 0;
else
s.check = check;
/*read the first byte and put the result in twkb_state*/
magicbyte_from_twkb_state(&s);
return lwgeom_from_twkb_state(&s);
}

View file

@ -155,3 +155,58 @@ varint_u64_encode_buf(uint64_t val, uint8_t **buf)
_varint_u64_encode_buf(val, buf);
return 0;
}
/*Read varint*/
/* Read from unsigned 64bit varint */
uint64_t varint_u64_read(uint8_t **data, uint8_t *the_end)
{
uint64_t nVal = 0;
int nShift = 0;
uint8_t nByte;
while(*data<=the_end)//Check so we don't read beyond the twkb
{
nByte = (uint8_t) **data; //read a byte
if (!(nByte & 0x80)) //If it is the last byte in the varInt ....
{
(*data) ++; //move the "cursor" one step
return nVal | ((uint64_t)nByte << nShift); //Move the last read byte to the most significant place in the result and return the whole result
}
/*We get here when there is more to read in the input varInt*/
nVal |= ((uint64_t)(nByte & 0x7f)) << nShift; //Here we take the least significant 7 bits of the read byte and put it in the most significant place in the result variable.
(*data) ++; //move the "cursor" of the input buffer step (8 bits)
nShift += 7; //move the cursor in the resulting variable (7 bits)
}
lwerror("VarInt value goes beyond TWKB");
return 0;
}
/* Read from signed 64bit varint */
uint64_t varint_s64_read(uint8_t **data, uint8_t *the_end)
{
uint64_t nVal = varint_u64_read(data,the_end);
/* un-zig-zag-ging */
if ((nVal & 1) == 0)
return (((uint64_t)nVal) >> 1);
else
return (uint64_t) (-(nVal >> 1)-1);
}
/* Used to jump over varint values as efficient as possible*/
void varint_64_jump_n(uint8_t **data, int nValues, uint8_t *the_end)
{
uint8_t nByte;
while(nValues>0)//Check so we don't read beyond the twkb
{
if(*data>the_end)
lwerror("VarInt value goes beyond TWKB");
nByte = (uint8_t) **data; //read a byte
if (!(nByte & 0x80)) //If it is the last byte in the varInt ....
{
nValues--;//...We count one more varint
}
(*data) ++; //move the "cursor" of the input buffer step (8 bits)
}
return;
}

View file

@ -20,6 +20,7 @@
#define _LIBLWGEOM_VARINT_H 1
#include <stdint.h>
/*Write varInt to buffer*/
/* Find encoded size for unsigned 32bit integer */
unsigned varint_u32_encoded_size(uint32_t val);
@ -45,4 +46,17 @@ unsigned varint_s64_encoded_size(int64_t val);
/* Encode unsigned 64bit integer */
int varint_s64_encode_buf(int64_t val, uint8_t **buf);
/*Read varint*/
/* Read from unsigned 64bit varint */
uint64_t varint_u64_read(uint8_t **buf, uint8_t *the_end);
/* Read from signed 64bit varint */
uint64_t varint_s64_read(uint8_t **buf, uint8_t *the_end);
/*To jump over values like id, when we don't care about id
should be faster than reading it properly
nValues tells how many varInts to jump over*/
void varint_64_jump_n(uint8_t **data, int nValues, uint8_t *the_end);
#endif /* !defined _LIBLWGEOM_VARINT_H */

View file

@ -153,6 +153,7 @@ Datum LWGEOM_force_multi(PG_FUNCTION_ARGS);
Datum LWGEOM_force_curve(PG_FUNCTION_ARGS);
Datum LWGEOMFromWKB(PG_FUNCTION_ARGS);
Datum LWGEOMFromTWKB(PG_FUNCTION_ARGS);
Datum WKBFromLWGEOM(PG_FUNCTION_ARGS);
Datum TWKBFromLWGEOM(PG_FUNCTION_ARGS);

View file

@ -354,6 +354,29 @@ Datum LWGEOMFromWKB(PG_FUNCTION_ARGS)
PG_FREE_IF_COPY(bytea_wkb, 0);
PG_RETURN_POINTER(geom);
}
/*
* LWGEOMFromTWKB(wkb)
* NOTE: twkb is in *binary* not hex form.
*
*/
PG_FUNCTION_INFO_V1(LWGEOMFromTWKB);
Datum LWGEOMFromTWKB(PG_FUNCTION_ARGS)
{
bytea *bytea_twkb = (bytea*)PG_GETARG_BYTEA_P(0);
GSERIALIZED *geom;
LWGEOM *lwgeom;
uint8_t *twkb = (uint8_t*)VARDATA(bytea_twkb);
lwgeom = lwgeom_from_twkb(twkb, VARSIZE(bytea_twkb)-VARHDRSZ, LW_PARSER_CHECK_ALL);
if ( lwgeom_needs_bbox(lwgeom) )
lwgeom_add_bbox(lwgeom);
geom = geometry_serialize(lwgeom);
lwgeom_free(lwgeom);
PG_FREE_IF_COPY(bytea_twkb, 0);
PG_RETURN_POINTER(geom);
}
/*
* WKBFromLWGEOM(lwgeom) --> wkb

View file

@ -1441,6 +1441,12 @@ CREATE OR REPLACE FUNCTION ST_GeomFromEWKB(bytea)
AS 'MODULE_PATHNAME','LWGEOMFromWKB'
LANGUAGE 'c' IMMUTABLE STRICT;
-- Availability: 2.2
CREATE OR REPLACE FUNCTION ST_GeomFromTWKB(bytea)
RETURNS geometry
AS 'MODULE_PATHNAME','LWGEOMFromTWKB'
LANGUAGE 'c' IMMUTABLE STRICT;
-- Deprecation in 1.2.3
CREATE OR REPLACE FUNCTION GeomFromEWKT(text)
RETURNS geometry