]> granicus.if.org Git - postgis/commitdiff
Add GeomFromTWKB #2917
authorNicklas Avén <nicklas.aven@jordogskog.no>
Thu, 9 Oct 2014 18:24:39 +0000 (18:24 +0000)
committerNicklas Avén <nicklas.aven@jordogskog.no>
Thu, 9 Oct 2014 18:24:39 +0000 (18:24 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@13053 b70326c6-7e19-0410-871a-916f4a2858ee

13 files changed:
doc/reference_constructor.xml
doc/reference_output.xml
liblwgeom/Makefile.in
liblwgeom/cunit/Makefile.in
liblwgeom/cunit/cu_in_twkb.c [new file with mode: 0644]
liblwgeom/cunit/cu_tester.c
liblwgeom/liblwgeom.h.in
liblwgeom/lwin_twkb.c [new file with mode: 0644]
liblwgeom/varint.c
liblwgeom/varint.h
libpgcommon/lwgeom_pg.h
postgis/lwgeom_inout.c
postgis/postgis.sql.in

index adbaa0c92f889538ad4ac1c289d62dea9c366888..1a775be40deead1bd93919579af919c4969ac22b 100644 (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>
index e1e72cdc382cfd804f5113893a814b324b693518..cf7598724c8c11b7c0c1e065647d1040258911ad 100644 (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>
index 8e485024f9a08731cbef72bf329d2c1f6e81e300..08140d7402a288204ec26d2526cccef1d6034cc9 100644 (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 \
index fc33459347ed26b019761a2fdfd6ecab1150c8d1..8008e0468ca67846011c6ea075d029b7488dd543 100644 (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 \
diff --git a/liblwgeom/cunit/cu_in_twkb.c b/liblwgeom/cunit/cu_in_twkb.c
new file mode 100644 (file)
index 0000000..a093876
--- /dev/null
@@ -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};
index 3629957ed512853ad978e98b66bb4c38a923494f..cf1bd4e4e8767c2da5763ac70ed4bff8562a20b9 100644 (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,
index 5b9ca2a116ea022dad91215da77221061f67d40e..68233070b253e7015e0a5144814ba47a658a2d6e 100644 (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
diff --git a/liblwgeom/lwin_twkb.c b/liblwgeom/lwin_twkb.c
new file mode 100644 (file)
index 0000000..40121fc
--- /dev/null
@@ -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);
+}
index c920ca6bb9254e04f6442d99cb79f69b39a35fba..adaf324a9b024e2c6457148d14bb05472a33e146 100644 (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;
+}
index 36914c8a5f5ba5ddf17ff6baec30a47819f88a73..ed44f7bf404b37da509445bb0c85c7fbaddc8b5f 100644 (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  */
\ No newline at end of file
index 90526be47eb1352b3fabd55735acf6fdb1915d5a..94638c2b3af6be31ec046ff86e5ab30a8595fe01 100644 (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);
 
index 6bf84f9062aed1d813d98b7f47b114fcae5c1d80..fa2904584ad8712c9c2b53e6772f31ce32b379a7 100644 (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
index 9405f0df3b933050a7fc0f96bea09c6179ec228f..5ce971221be06d50e47e86a7aa52e096d90c62ee 100644 (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