From: Paul Ramsey Date: Wed, 13 May 2015 18:03:16 +0000 (+0000) Subject: #3085 Add aggregate support and enhance TWKB support in general X-Git-Tag: 2.2.0rc1~514 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=17c3f1f085436c558093d4ddb566ba5b1d6fb380;p=postgis #3085 Add aggregate support and enhance TWKB support in general git-svn-id: http://svn.osgeo.org/postgis/trunk@13493 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/doc/reference_constructor.xml b/doc/reference_constructor.xml index 1a775be40..eaf5d15de 100644 --- a/doc/reference_constructor.xml +++ b/doc/reference_constructor.xml @@ -1042,7 +1042,7 @@ SELECT See Also - ,,,, , + ,,, , diff --git a/doc/reference_output.xml b/doc/reference_output.xml index 235d2096d..8aa20d38e 100644 --- a/doc/reference_output.xml +++ b/doc/reference_output.xml @@ -1175,7 +1175,7 @@ SELECT (ST_AsLatLonText('POINT (-302.2342342 -792.32498)')); ST_AsTWKB - Returns the geometry as TWKB, Tiny WKB + Returns the geometry as TWKB, aka "Tiny Well-Known Binary" @@ -1183,24 +1183,32 @@ SELECT (ST_AsLatLonText('POINT (-302.2342342 -792.32498)')); bytea ST_AsTWKB geometry g1 - integer decimaldigits - int8 geometryID=null - boolean include sizes=false - boolean include bounding boxes=false + integer decimaldigits_xy=0 + integer decimaldigits_z=0 + integer decimaldigits_m=0 + boolean include_sizes=false + boolean include_bounding boxes=false + + + bytea ST_AsTWKB + geometry[] geometries + bigint[] unique_ids + integer decimaldigits_xy=0 + integer decimaldigits_z=0 + integer decimaldigits_m=0 + boolean include_sizes=false + boolean include_bounding_boxes=false Description - Returns the geometry in TWKB format. TWKB is a new compressed binary format. - The second parameter is an integer used to define the number of coordinate decimals in the output. - The third parameter is optional. It is used to give each TWKB-geometry an unique ID - The fourth parameter is optional. If it is set to true, the size of the geometry is included + Returns the geometry in TWKB (Tiny Well-Known Binary) format. TWKB is a compressed binary format with a focus on minimizing the size of the output. + The decimal digits parameters control how much precision is stored in the output. By default, values are rounded to the nearest unit before encoding. If you want to transfer more precision, increase the number. For example, a value of 1 implies that the first digit to the right of the decimal point will be preserved. + The sizes and bounding boxes parameters control whether optional information about the encoded length of the object and the bounds of the object are included in the output. By default they are not. Do not turn them on unless your client software has a use for them, as they just use up space (and saving space is the point of TWKB). + The array-input form of the function is used to convert a collection of geometries and unique identifiers into a TWKB collection that preserves the identifiers. This is useful for clients that expect to unpack a collection and then access further information about the objects inside. You can create the arrays using the array_agg function. The other parameters operate the same as for the simple form of the function. - TWKB is still a moving target. The format is described https://github.com/TWKB/Specification , and code for building a client can be found https://github.com/TWKB/twkb.js - - - TWKB doesn't make any difference of Z and M. So PointM and PointZ will give the same twkb representation. + The format specification is available online at https://github.com/TWKB/Specification, and code for building a JavaScript client can be found at https://github.com/TWKB/twkb.js. Availability: 2.2.0 @@ -1210,91 +1218,20 @@ SELECT (ST_AsLatLonText('POINT (-302.2342342 -792.32498)')); Examples -SELECT ST_AsTWKB('LINESTRING(1 1,5 5)'::geometry,0,1); +SELECT ST_AsTWKB('LINESTRING(1 1,5 5)'::geometry); st_astwkb -------------------------------------------- -\x0142020202020808 +\x02000202020808 See Also - , , , , + , , , - - - ST_AsTWKBAgg - Aggregates the geometries and returns as TWKB - - - - - - bytea ST_AsTWKBAgg - geometry set g1 - integer decimaldigits - - - bytea ST_AsTWKBAgg - geometry set g1 - integer decimaldigits - int8 geometryID - - - bytea ST_AsTWKBAgg - geometry set g1 - integer decimaldigits - int8 geometryID - boolean include sizes - - - bytea ST_AsTWKBAgg - geometry set g1 - integer decimaldigits - int8 geometryID - boolean include sizes - boolean include bounding boxes - - - - - Description - - Returns the geometry in TWKB format. TWKB is a new compressed binary format. - This is the aggregate version of ST_AsTWKB. It aggregates and returns the geometry in TWKB-format. In the resulting TWKB-geometry each individual ID of the input geometries is stored. - The second parameter is an integer used to define the number of coordinate decimals in the output. - The third parameter is optional. It is used to give each TWKB-geometry an unique ID - The fourth parameter is optional. If it is set to true, the size of the geometry is included - - TWKB is still a moving target. The format is described https://github.com/nicklasaven/TWKB , and code for building a client can be found https://github.com/nicklasaven/twkb_web - - - - Availability: 2.2.0 - - - - - Examples - -SELECT ST_AsTWKBAgg(geom,0,id) FROM -(SELECT 'LINESTRING(1 1,5 5)'::geometry geom, 1 AS id -UNION ALL -SELECT 'LINESTRING(6 5, 1 7)'::geometry AS geom, 2 AS id) foo; - st_astwkbagg ----------------------------------------------------------------------------- -\x015602020202020808040202000904 - - - - - See Also - , , , , - - diff --git a/liblwgeom/Makefile.in b/liblwgeom/Makefile.in index fc27456c2..58a9921f9 100644 --- a/liblwgeom/Makefile.in +++ b/liblwgeom/Makefile.in @@ -32,6 +32,7 @@ LEX=@LEX@ # Standalone LWGEOM objects SA_OBJS = \ stringbuffer.o \ + bytebuffer.o \ measures.o \ measures3d.o \ box2d.o \ diff --git a/liblwgeom/cunit/Makefile.in b/liblwgeom/cunit/Makefile.in index 040b18927..18c9cd9b9 100644 --- a/liblwgeom/cunit/Makefile.in +++ b/liblwgeom/cunit/Makefile.in @@ -24,6 +24,7 @@ LDFLAGS = @GEOS_LDFLAGS@ -lgeos_c $(CUNIT_LDFLAGS) -lm # ADD YOUR NEW TEST FILE HERE (1/1) OBJS= \ cu_algorithm.o \ + cu_bytebuffer.o \ cu_buildarea.o \ cu_clean.o \ cu_print.o \ @@ -53,8 +54,8 @@ OBJS= \ cu_surface.o \ cu_out_x3d.o \ cu_in_geojson.o \ - cu_in_wkb.o \ cu_in_twkb.o \ + cu_in_wkb.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 index 6c4815dc3..47bcb3cbd 100644 --- a/liblwgeom/cunit/cu_in_twkb.c +++ b/liblwgeom/cunit/cu_in_twkb.c @@ -23,6 +23,8 @@ */ char *hex_a; char *hex_b; +uint8_t precision = 0; +uint8_t variant = 0; /* ** The suite initialization function. @@ -56,34 +58,35 @@ static void cu_twkb_in(char *wkt) 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 ) + 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); + twkb_a = lwgeom_to_twkb(g_a, variant, precision, precision, precision, &twkb_size_a); + + // printf("\n Size: %ld\n", twkb_size_a); - /* Turn WKB back into geom */ + /* Turn TWKB 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); + // printf("\n Org: %s\n 1st: %s\n 2nd: %s\n", wkt, lwgeom_to_ewkt(g_a), lwgeom_to_ewkt(g_b)); + + /* Turn geom to TWKB again */ + twkb_b = lwgeom_to_twkb(g_b, variant, precision, precision, precision, &twkb_size_b); - /* Turn geoms into WKB for comparisons */ + /* Turn TWKB into hex for comparisons */ + if ( hex_a ) free(hex_a); + if ( hex_b ) free(hex_b); hex_a = hexbytes_from_bytes(twkb_a, twkb_size_a); hex_b = hexbytes_from_bytes(twkb_b, twkb_size_b); @@ -101,7 +104,9 @@ static void test_twkb_in_point(void) 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); + + cu_twkb_in("POINT EMPTY"); CU_ASSERT_STRING_EQUAL(hex_a, hex_b); } @@ -112,6 +117,9 @@ static void test_twkb_in_linestring(void) cu_twkb_in("LINESTRING(0 0 1,1 1 2,2 2 3)"); CU_ASSERT_STRING_EQUAL(hex_a, hex_b); + + cu_twkb_in("LINESTRING EMPTY"); + CU_ASSERT_STRING_EQUAL(hex_a, hex_b); } static void test_twkb_in_polygon(void) @@ -129,34 +137,95 @@ static void test_twkb_in_polygon(void) CU_ASSERT_STRING_EQUAL(hex_a, hex_b); } -static void test_twkb_in_multipoint(void) +static void test_twkb_in_multipoint(void) { + cu_twkb_in("MULTIPOINT Z EMPTY"); + CU_ASSERT_STRING_EQUAL(hex_a, hex_b); + + cu_twkb_in("MULTIPOINT(1 2, EMPTY, 5 6)"); + CU_ASSERT_STRING_EQUAL(hex_a, hex_b); + // printf(" 1st: %s\n 2nd: %s\n", hex_a, hex_b); + 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); + + cu_twkb_in("MULTIPOINT(1 2 3, 1 2 3, 4 5 6, -3 -4 -5, -10 -5 -1)"); + CU_ASSERT_STRING_EQUAL(hex_a, hex_b); } -static void test_twkb_in_multilinestring(void) {} +static void test_twkb_in_multilinestring(void) +{ + cu_twkb_in("MULTILINESTRING((0 0,0 1),(1 1, 10 10))"); + CU_ASSERT_STRING_EQUAL(hex_a, hex_b); + + cu_twkb_in("MULTILINESTRING((0 0,0 1),EMPTY,(1 1, 10 10))"); + CU_ASSERT_STRING_EQUAL(hex_a, hex_b); + + cu_twkb_in("MULTILINESTRING((0 0 200000,0 1 10),(1 100000000 23, 10 10 45))"); + CU_ASSERT_STRING_EQUAL(hex_a, hex_b); + // printf(" 1st: %s\n 2nd: %s\n", hex_a, hex_b); + + cu_twkb_in("MULTILINESTRING EMPTY"); + CU_ASSERT_STRING_EQUAL(hex_a, hex_b); +} 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); + + cu_twkb_in("MULTIPOLYGON EMPTY"); + 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(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); + 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_twkb_in("GEOMETRYCOLLECTION(POINT(1 2 3), LINESTRING EMPTY, POINT(4 5 6))"); CU_ASSERT_STRING_EQUAL(hex_a, hex_b); + cu_twkb_in("GEOMETRYCOLLECTION(POINT(1 2 3), POINT EMPTY, POINT(4 5 6))"); + CU_ASSERT_STRING_EQUAL(hex_a, hex_b); } +/* +** PRECISION TESTS HERE: We ALTER THE 'precision' GLOBAL +*/ + +static void test_twkb_in_precision(void) +{ + /* Try these cases at several different precisions */ + for ( precision = 1; precision <= 6; precision++ ) + { + cu_twkb_in("MULTILINESTRING((0 0,0 1),EMPTY,(1 1, 10 10))"); + CU_ASSERT_STRING_EQUAL(hex_a, hex_b); + // printf("old: %s\nnew: %s\n",hex_a, hex_b); + + 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); + + 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("MULTILINESTRING((0 0 200000,0 1 10),(1 100000000 23, 10 10 45))"); + CU_ASSERT_STRING_EQUAL(hex_a, hex_b); + } + + /* Go back to default precision */ + precision = 0; +} + + /* ** Used by test harness to register the tests in this file. @@ -172,4 +241,5 @@ void twkb_in_suite_setup(void) PG_ADD_TEST(suite, test_twkb_in_multilinestring); PG_ADD_TEST(suite, test_twkb_in_multipolygon); PG_ADD_TEST(suite, test_twkb_in_collection); + PG_ADD_TEST(suite, test_twkb_in_precision); } diff --git a/liblwgeom/cunit/cu_out_twkb.c b/liblwgeom/cunit/cu_out_twkb.c index fef698a1f..34a9da381 100644 --- a/liblwgeom/cunit/cu_out_twkb.c +++ b/liblwgeom/cunit/cu_out_twkb.c @@ -18,10 +18,12 @@ #include "liblwgeom_internal.h" #include "cu_tester.h" + /* ** Global variable to hold hex TWKB strings */ char *s; +char *w; /* ** The suite initialization function. @@ -30,6 +32,7 @@ char *s; static int init_twkb_out_suite(void) { s = NULL; + w = NULL; return 0; } @@ -40,69 +43,136 @@ static int init_twkb_out_suite(void) static int clean_twkb_out_suite(void) { if (s) free(s); + if (w) free(w); s = NULL; + w = NULL; return 0; } /* -** Creating an input TWKB from a wkt string +** Creating an input TWKB from a wkt string +*/ +static void cu_twkb(char *wkt, int8_t prec_xy, int8_t prec_z, int8_t prec_m, uint8_t variant) +{ + LWGEOM *g = lwgeom_from_wkt(wkt, LW_PARSER_CHECK_NONE); + size_t twkb_size; + uint8_t *twkb; + if ( ! g ) lwnotice("input wkt is invalid: %s", wkt); + twkb = lwgeom_to_twkb(g, variant, prec_xy, prec_z, prec_m, &twkb_size); + lwgeom_free(g); + if ( s ) free(s); + s = hexbytes_from_bytes(twkb, twkb_size); + free(twkb); +} + + +/* +** Creating an input TWKB from a wkt string */ -static void cu_twkb(char *wkt,int8_t prec, int64_t id) +static void cu_twkb_idlist(char *wkt, uint64_t *idlist, int8_t prec_xy, int8_t prec_z, int8_t prec_m, uint8_t variant) { LWGEOM *g = lwgeom_from_wkt(wkt, LW_PARSER_CHECK_NONE); - size_t size; - uint8_t *twkb = lwgeom_to_twkb(g, TWKB_ID, &size, prec, id); + LWGEOM *g_b; + size_t twkb_size; + uint8_t *twkb; + if ( ! g ) lwnotice("input wkt is invalid: %s", wkt); + twkb = lwgeom_to_twkb_with_idlist(g, idlist, variant, prec_xy, prec_z, prec_m, &twkb_size); lwgeom_free(g); if ( s ) free(s); - s = hexbytes_from_bytes(twkb, size); - free(twkb); + if ( w ) free(w); + s = hexbytes_from_bytes(twkb, twkb_size); + g_b = lwgeom_from_twkb(twkb, twkb_size, LW_PARSER_CHECK_NONE); + w = lwgeom_to_ewkt(g_b); + lwgeom_free(g_b); + free(twkb); } + static void test_twkb_out_point(void) { - cu_twkb("POINT(0 0 0 0)",0,1); - CU_ASSERT_STRING_EQUAL(s,"01810200000000"); -// printf("TWKB: %s\n",s); - cu_twkb("SRID=4;POINTM(1 1 1)",0,1); - CU_ASSERT_STRING_EQUAL(s,"016102020202"); -// printf("TWKB: %s\n",s); + + cu_twkb("POINT EMPTY", 0, 0, 0, 0); + CU_ASSERT_STRING_EQUAL(s,"0110"); + + cu_twkb("POINT(0 0)", 0, 0, 0, 0); + CU_ASSERT_STRING_EQUAL(s,"01000000"); + + cu_twkb("POINT(0 0 0 0)", 0, 0, 0, 0); + CU_ASSERT_STRING_EQUAL(s,"01080300000000"); + + /* Point with bounding box */ + cu_twkb("POINT(0 0)", 0, 0, 0, TWKB_BBOX); + CU_ASSERT_STRING_EQUAL(s,"0101000000000000"); + // printf("TWKB: %s\n",s); + + /* Adding a size paramters to X/Y */ + cu_twkb("POINT(0 0)", 0, 0, 0, TWKB_SIZE); + CU_ASSERT_STRING_EQUAL(s,"0102020000"); + + /* Adding a size paramters to X/Y/M */ + cu_twkb("POINTM(0 0 0)", 0, 0, 0, TWKB_SIZE); + CU_ASSERT_STRING_EQUAL(s,"010A0203000000"); + + /* Adding a size paramters to X/Y/Z/M */ + cu_twkb("POINT(0 0 0 0)", 0, 0, 0, TWKB_SIZE); + CU_ASSERT_STRING_EQUAL(s,"010A030400000000"); + + /* Since the third dimension is Z it shall get a precision of 1 decimal (third argument) */ + cu_twkb("POINTZ(1 1 1)", 0,1,2, 0); + CU_ASSERT_STRING_EQUAL(s,"010845020214"); + + /* Since the third dimension is M it shall get a precision of 2 decimals (fourth argument) */ + cu_twkb("POINTM(1 1 1)", 0,1,2, 0); + // printf("\n%s\n", s); + CU_ASSERT_STRING_EQUAL(s,"0108460202C801"); } static void test_twkb_out_linestring(void) { - cu_twkb("LINESTRING(0 0,1 1)",0,1); - CU_ASSERT_STRING_EQUAL(s,"0142020200000202"); -// printf("TWKB: %s\n",s); - cu_twkb("LINESTRING(0 0 1,1 1 2,2 2 3)",0,1); - CU_ASSERT_STRING_EQUAL(s,"01620203000002020202020202"); - cu_twkb("LINESTRING EMPTY",0,1); - CU_ASSERT_STRING_EQUAL(s,"01024200"); + cu_twkb("LINESTRING(0 0,1 1)", 0, 0, 0, 0); + CU_ASSERT_STRING_EQUAL(s,"02000200000202"); + // printf("TWKB: %s\n",s); + + cu_twkb("LINESTRING(0 0 1,1 1 2,2 2 3)", 0, 0, 0, 0); + CU_ASSERT_STRING_EQUAL(s,"02080103000002020202020202"); + // printf("TWKB: %s\n",s); + + /* Line with bounding box */ + cu_twkb("LINESTRING(0 0,1 1,2 2)", 0, 0, 0, TWKB_BBOX); + CU_ASSERT_STRING_EQUAL(s,"02010004000403000002020202"); + // printf("TWKB: %s\n",s); + + cu_twkb("LINESTRING EMPTY", 0, 0, 0, 0); + CU_ASSERT_STRING_EQUAL(s,"0210"); + // printf("TWKB: %s\n",s); } static void test_twkb_out_polygon(void) { - cu_twkb("SRID=4;POLYGON((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0))",0,1); - CU_ASSERT_STRING_EQUAL(s,"0163020105000000000200020000000100010000"); + cu_twkb("SRID=4;POLYGON((0 0 0, 0 1 0,1 1 0,1 0 0, 0 0 0))", 0, 0, 0, 0); + CU_ASSERT_STRING_EQUAL(s,"0308010105000000000200020000000100010000"); + // printf("TWKB: %s\n",s); - cu_twkb("SRID=14;POLYGON((0 0 0 1,0 1 0 2,1 1 0 3,1 0 0 4,0 0 0 5))",0,1); - CU_ASSERT_STRING_EQUAL(s,"01830201050000000200020002020000020001000201000002"); - - cu_twkb("POLYGON EMPTY",0,1); - CU_ASSERT_STRING_EQUAL(s,"01024300"); + cu_twkb("SRID=14;POLYGON((0 0 0 1, 0 1 0 2,1 1 0 3,1 0 0 4, 0 0 0 5))", 0, 0, 0, 0); + CU_ASSERT_STRING_EQUAL(s,"03080301050000000200020002020000020001000201000002"); + // printf("TWKB: %s\n",s); + cu_twkb("POLYGON EMPTY", 0, 0, 0, 0); + CU_ASSERT_STRING_EQUAL(s,"0310"); + // printf("TWKB: %s\n",s); } -static void test_twkb_out_multipoint(void) +static void test_twkb_out_multipoint(void) { - cu_twkb("SRID=4;MULTIPOINT(0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)",0,1); - CU_ASSERT_STRING_EQUAL(s,"01640205000000000200020000000100010000"); + cu_twkb("MULTIPOINT(0 0 0, 0 1 0,1 1 0,1 0 0, 0 0 0)", 0, 0, 0, 0); + CU_ASSERT_STRING_EQUAL(s,"04080105000000000200020000000100010000"); - cu_twkb("MULTIPOINT(0 0 0, 0.26794919243112270647255365849413 1 3)",7,1); + cu_twkb("MULTIPOINT(0 0 0, 0.26794919243112270647255365849413 1 3)",7 ,7 , 0, 0); //printf("WKB: %s",s); - CU_ASSERT_STRING_EQUAL(s,"71640202000000888BC70280DAC409808ECE1C"); + CU_ASSERT_STRING_EQUAL(s,"E4081D02000000888BC70280DAC409808ECE1C"); // printf("TWKB: %s\n",s); } @@ -110,17 +180,98 @@ static void test_twkb_out_multilinestring(void) {} static void test_twkb_out_multipolygon(void) { - cu_twkb("SRID=14;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)))",0,1); - CU_ASSERT_STRING_EQUAL(s,"016602020105000000000200020000000100010000020501010000060006000000050005000005020200000200020000000100010000"); + cu_twkb("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)))", 0, 0, 0, 0); + CU_ASSERT_STRING_EQUAL(s,"060801020105000000000200020000000100010000020501010000060006000000050005000005020200000200020000000100010000"); } static void test_twkb_out_collection(void) { - cu_twkb("SRID=14;GEOMETRYCOLLECTION(POLYGON((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)),POINT(1 1 1))",0,1); - CU_ASSERT_STRING_EQUAL(s,"0167020263010500000000020002000000010001000061020202"); + cu_twkb("GEOMETRYCOLLECTION(LINESTRING(1 1, 2 2), LINESTRING(3 3, 4 4), LINESTRING(5 5, 6 6))", 0, 0, 0, 0); + // printf("TWKB: %s\n",s); + CU_ASSERT_STRING_EQUAL(s,"07000302000202020202020002060602020200020A0A0202"); + + cu_twkb("GEOMETRYCOLLECTION(POLYGON((0 0 0, 0 1 0,1 1 0,1 0 0, 0 0 0)),POINT(1 1 1))", 0, 0, 0, 0); + // printf("TWKB: %s\n",s); + CU_ASSERT_STRING_EQUAL(s,"070801020308010105000000000200020000000100010000010801020202"); + + cu_twkb("GEOMETRYCOLLECTION EMPTY", 0, 0, 0, 0); + CU_ASSERT_STRING_EQUAL(s,"0710"); +} + +static void test_twkb_out_idlist(void) +{ + uint64_t idlist[2]; + + idlist[0] = 2; + idlist[1] = 4; + cu_twkb_idlist("MULTIPOINT(1 1, 0 0)",idlist, 0, 0, 0, 0); + // printf("TWKB: %s\n",s); + // printf("WKT: %s\n",w); + CU_ASSERT_STRING_EQUAL(s,"040402040802020101"); + CU_ASSERT_STRING_EQUAL(w,"MULTIPOINT(1 1,0 0)"); + + /* + 04 06 multipoint, size/idlist + 07 size 7 bytes + 02 two geometries + 0408 idlist (2, 4) + 0202 first point @ 1,1 + 0101 second point offset -1,-1 + */ + idlist[0] = 2; + idlist[1] = 4; + cu_twkb_idlist("MULTIPOINT(1 1, 0 0)",idlist, 0, 0, 0, TWKB_SIZE); + // printf("TWKB: %s\n",s); + // printf("WKT: %s\n",w); + CU_ASSERT_STRING_EQUAL(s,"04060702040802020101"); + CU_ASSERT_STRING_EQUAL(w,"MULTIPOINT(1 1,0 0)"); + + /* + 04 07 multipoint, bbox/size/idlist + 0B size 11 bytes + 00020002 bbox x(0,1), y(0,1) + 02 two geometries + 0408 idlist (2,4) + 0202 first point @ 1,1 + 0101 seconds point offset -1,-1 + */ + idlist[0] = 2; + idlist[1] = 4; + cu_twkb_idlist("MULTIPOINT(1 1, 0 0)",idlist, 0, 0, 0, TWKB_SIZE | TWKB_BBOX); + // printf("TWKB: %s\n",s); + // printf("WKT: %s\n",w); + CU_ASSERT_STRING_EQUAL(s,"04070B0002000202040802020101"); + CU_ASSERT_STRING_EQUAL(w,"MULTIPOINT(1 1,0 0)"); + + /* + 0704 geometrycollection, idlist + 02 two geometries + 0408 idlist (2,4) + 01000202 first point (type, meta, x, y) + 01000000 second point (type, meta, x, y) + */ + idlist[0] = 2; + idlist[1] = 4; + cu_twkb_idlist("GEOMETRYCOLLECTION(POINT(1 1),POINT(0 0))",idlist, 0, 0, 0, 0); + // printf("TWKB: %s\n",s); + CU_ASSERT_STRING_EQUAL(s,"07040204080100020201000000"); + CU_ASSERT_STRING_EQUAL(w,"GEOMETRYCOLLECTION(POINT(1 1),POINT(0 0))"); + + /* + 0706 geometrycollection, size/idlist + 0D size, 13 bytes + 02 two geometries + 0408 idlist (2,4) + 0102020202 first point (type, meta, size, x, y) + 0102020000 second point (type, meta, size, x, y) + */ + idlist[0] = 2; + idlist[1] = 4; + cu_twkb_idlist("GEOMETRYCOLLECTION(POINT(1 1),POINT(0 0))",idlist, 0, 0, 0, TWKB_SIZE); + // printf("TWKB: %s\n",s); + CU_ASSERT_STRING_EQUAL(s,"07060D02040801020202020102020000"); + CU_ASSERT_STRING_EQUAL(w,"GEOMETRYCOLLECTION(POINT(1 1),POINT(0 0))"); - cu_twkb("GEOMETRYCOLLECTION EMPTY",0,1); - CU_ASSERT_STRING_EQUAL(s,"01024700"); } @@ -138,4 +289,5 @@ void twkb_out_suite_setup(void) PG_ADD_TEST(suite, test_twkb_out_multilinestring); PG_ADD_TEST(suite, test_twkb_out_multipolygon); PG_ADD_TEST(suite, test_twkb_out_collection); + PG_ADD_TEST(suite, test_twkb_out_idlist); } diff --git a/liblwgeom/cunit/cu_tester.c b/liblwgeom/cunit/cu_tester.c index 4e3090cd0..26daf12b3 100644 --- a/liblwgeom/cunit/cu_tester.c +++ b/liblwgeom/cunit/cu_tester.c @@ -76,6 +76,7 @@ PG_SuiteSetup setupfuncs[] = #if HAVE_LIBJSON in_geojson_suite_setup, #endif + twkb_in_suite_setup, libgeom_suite_setup, measures_suite_setup, effectivearea_suite_setup, @@ -97,7 +98,6 @@ PG_SuiteSetup setupfuncs[] = surface_suite_setup, tree_suite_setup, triangulate_suite_setup, - twkb_in_suite_setup, twkb_out_suite_setup, varint_suite_setup, wkb_in_suite_setup, diff --git a/liblwgeom/cunit/cu_varint.c b/liblwgeom/cunit/cu_varint.c index 9741d54e4..9342bd586 100644 --- a/liblwgeom/cunit/cu_varint.c +++ b/liblwgeom/cunit/cu_varint.c @@ -20,92 +20,102 @@ #include "varint.h" #include "cu_tester.h" -static void do_test_u32_varint(uint32_t nr,int expected_size, char* expected_res) + + +// size_t varint_u32_encode_buf(uint32_t val, uint8_t *buf); +// size_t varint_s32_encode_buf(int32_t val, uint8_t *buf); +// size_t varint_u64_encode_buf(uint64_t val, uint8_t *buf); +// size_t varint_s64_encode_buf(int64_t val, uint8_t *buf); +// int64_t varint_s64_decode(const uint8_t *the_start, const uint8_t *the_end, size_t *size); +// uint64_t varint_u64_decode(const uint8_t *the_start, const uint8_t *the_end, size_t *size); +// +// size_t varint_size(const uint8_t *the_start, const uint8_t *the_end); +// + +static void do_test_u32_varint(uint32_t nr, int expected_size, char* expected_res) { - uint8_t *buf, *original; int size; char *hex; + uint8_t buf[16]; - buf = original=lwalloc(8); - size = varint_u32_encoded_size(nr); - if ( size != expected_size ) { - printf("Expected: %d\nObtained: %d\n", expected_size, size); - } - CU_ASSERT_EQUAL(size,expected_size); - varint_u32_encode_buf(nr, &buf); - hex = hexbytes_from_bytes(original,size); - if ( strcmp(hex,expected_res) ) { - printf("Expected: %s\nObtained: %s\n", expected_res, hex); - } + size = varint_u32_encode_buf(nr, buf); + if ( size != expected_size ) + printf("Expected: %d\nObtained: %d\n", expected_size, size); + + CU_ASSERT_EQUAL(size, expected_size); + + hex = hexbytes_from_bytes(buf, size); + if ( strcmp(hex, expected_res) ) + printf("Expected: %s\nObtained: %s\n", expected_res, hex); + CU_ASSERT_STRING_EQUAL(hex, expected_res); -lwfree(original); - lwfree(hex); + lwfree(hex); } static void do_test_s32_varint(int32_t nr,int expected_size, char* expected_res) { - uint8_t *buf, *original; + uint8_t buf[16]; int size; char *hex; - buf = original=lwalloc(8); - size = varint_s32_encoded_size(nr); - if ( size != expected_size ) { - printf("Expected: %d\nObtained: %d\n", expected_size, size); + size = varint_s32_encode_buf(nr, buf); + if ( size != expected_size ) + { + printf("Expected: %d\nObtained: %d\n", expected_size, size); } CU_ASSERT_EQUAL(size,expected_size); - varint_s32_encode_buf(nr, &buf); - hex = hexbytes_from_bytes(original,size); - if ( strcmp(hex,expected_res) ) { - printf("Expected: %s\nObtained: %s\n", expected_res, hex); + + hex = hexbytes_from_bytes(buf, size); + if ( strcmp(hex,expected_res) ) + { + printf("Expected: %s\nObtained: %s\n", expected_res, hex); } CU_ASSERT_STRING_EQUAL(hex, expected_res); -lwfree(original); - lwfree(hex); + lwfree(hex); } static void do_test_u64_varint(uint64_t nr,int expected_size, char* expected_res) { - uint8_t *buf, *original; + uint8_t buf[16]; int size; char *hex; - buf = original=lwalloc(8); - size = varint_u64_encoded_size(nr); - if ( size != expected_size ) { - printf("Expected: %d\nObtained: %d\n", expected_size, size); + size = varint_u64_encode_buf(nr, buf); + if ( size != expected_size ) + { + printf("Expected: %d\nObtained: %d\n", expected_size, size); } CU_ASSERT_EQUAL(size,expected_size); - varint_u64_encode_buf(nr, &buf); - hex = hexbytes_from_bytes(original,size); - if ( strcmp(hex,expected_res) ) { - printf("Expected: %s\nObtained: %s\n", expected_res, hex); + + hex = hexbytes_from_bytes(buf,size); + if ( strcmp(hex,expected_res) ) + { + printf("Expected: %s\nObtained: %s\n", expected_res, hex); } CU_ASSERT_STRING_EQUAL(hex, expected_res); -lwfree(original); - lwfree(hex); + lwfree(hex); } static void do_test_s64_varint(int64_t nr,int expected_size, char* expected_res) { - uint8_t *buf, *original; + uint8_t buf[16]; int size; char *hex; - buf = original=lwalloc(8); - size = varint_s64_encoded_size(nr); - if ( size != expected_size ) { - printf("Expected: %d\nObtained: %d\n", expected_size, size); + size = varint_s64_encode_buf(nr, buf); + if ( size != expected_size ) + { + printf("Expected: %d\nObtained: %d\n", expected_size, size); } CU_ASSERT_EQUAL(size,expected_size); - varint_s64_encode_buf(nr, &buf); - hex = hexbytes_from_bytes(original,size); - if ( strcmp(hex,expected_res) ) { - printf("Expected: %s\nObtained: %s\n", expected_res, hex); + + hex = hexbytes_from_bytes(buf,size); + if ( strcmp(hex,expected_res) ) + { + printf("Expected: %s\nObtained: %s\n", expected_res, hex); } CU_ASSERT_STRING_EQUAL(hex, expected_res); -lwfree(original); - lwfree(hex); + lwfree(hex); } static void test_varint(void) @@ -180,6 +190,59 @@ static void test_varint(void) } +static void do_test_u64_roundtrip(uint64_t i64_in) +{ + uint8_t buffer[16]; + uint64_t i64_out; + size_t size_in, size_out; + size_in = varint_u64_encode_buf(i64_in, buffer); + i64_out = varint_u64_decode(buffer, buffer + size_in, &size_out); + CU_ASSERT_EQUAL(i64_in, i64_out); + CU_ASSERT_EQUAL(size_in, size_out); +} + +static void do_test_s64_roundtrip(int64_t i64_in) +{ + uint8_t buffer[16]; + int64_t i64_out; + size_t size_in, size_out; + size_in = varint_s64_encode_buf(i64_in, buffer); + i64_out = varint_s64_decode(buffer, buffer + size_in, &size_out); + CU_ASSERT_EQUAL(i64_in, i64_out); + CU_ASSERT_EQUAL(size_in, size_out); +} + +static void test_varint_roundtrip(void) +{ + int i; + for ( i = 0; i < 1024; i += 63 ) + { + do_test_u64_roundtrip(i); + do_test_s64_roundtrip(i); + do_test_s64_roundtrip(-1*i); + } +} + +static void test_zigzag(void) +{ + int64_t a; + int32_t b; + int i; + + for ( i = 1; i < 1024; i += 31 ) + { + a = b = i; + CU_ASSERT_EQUAL(a, unzigzag64(zigzag64(a))); + CU_ASSERT_EQUAL(b, unzigzag32(zigzag64(b))); + + a = b = -1 * i; + CU_ASSERT_EQUAL(a, unzigzag64(zigzag64(a))); + CU_ASSERT_EQUAL(b, unzigzag32(zigzag64(b))); + } + +} + + /* ** Used by the test harness to register the tests in this file. */ @@ -187,5 +250,7 @@ void varint_suite_setup(void); void varint_suite_setup(void) { CU_pSuite suite = CU_add_suite("varint", NULL, NULL); + PG_ADD_TEST(suite, test_zigzag); PG_ADD_TEST(suite, test_varint); + PG_ADD_TEST(suite, test_varint_roundtrip); } diff --git a/liblwgeom/liblwgeom.h.in b/liblwgeom/liblwgeom.h.in index 1dcd3fbd6..49aa233a2 100644 --- a/liblwgeom/liblwgeom.h.in +++ b/liblwgeom/liblwgeom.h.in @@ -20,6 +20,7 @@ #include #include #include "proj_api.h" +#include "bytebuffer.h" /** * @file liblwgeom.h @@ -586,29 +587,6 @@ typedef struct } LWTIN; -/** -* TWKB Support Structures -*/ -typedef struct -{ - int64_t id; /* Id, from function parameter */ - LWGEOM* geom; /* the geometry from function parameter */ -} -lwgeom_id; - -typedef struct -{ - lwgeom_id* points; - int n_points; - lwgeom_id* linestrings; - int n_linestrings; - lwgeom_id* polygons; - int n_polygons; - lwgeom_id* collections; - int n_collections; -} -twkb_geom_arrays; - /** * Snap-to-grid Support */ @@ -1924,11 +1902,12 @@ LWGEOM_UNPARSER_RESULT; /* ** Variants available for TWKB */ -#define TWKB_ID 0x01 /*User wants id*/ -#define TWKB_SIZES 0x02 /*User wants sizes*/ -#define TWKB_BBOXES 0x04 /*User wants bboxes*/ -#define TWKB_NO_TYPE 0x08 /*No type because it is a sub geoemtry*/ -#define TWKB_NO_ID 0x10 /*No ID because it is a subgeoemtry*/ +#define TWKB_BBOX 0x01 /* User wants bboxes */ +#define TWKB_SIZE 0x02 /* User wants sizes */ +#define TWKB_ID 0x04 /* User wants id */ +#define TWKB_NO_TYPE 0x10 /* No type because it is a sub geoemtry */ +#define TWKB_NO_ID 0x20 /* No ID because it is a subgeoemtry */ +#define TWKB_DEFAULT_PRECISION 0 /* Aim for 1m (or ft) rounding by default */ /* ** New parsing and unparsing functions. @@ -1947,9 +1926,6 @@ extern char* lwgeom_to_wkt(const LWGEOM *geom, uint8_t variant, int precision, */ extern uint8_t* lwgeom_to_wkb(const LWGEOM *geom, uint8_t variant, size_t *size_out); -extern uint8_t* lwgeom_to_twkb(const LWGEOM *geom, uint8_t variant, size_t *size_out,int8_t prec, int64_t id); -extern uint8_t* lwgeom_agg_to_twkb(const twkb_geom_arrays *lwgeom_arrays,uint8_t variant , size_t *size_out,int8_t prec); - /** * @param lwgeom geometry to convert to HEXWKB * @param variant output format to use @@ -1957,7 +1933,6 @@ extern uint8_t* lwgeom_agg_to_twkb(const twkb_geom_arrays *lwgeom_arrays,uint8_t */ extern char* lwgeom_to_hexwkb(const LWGEOM *geom, uint8_t variant, size_t *size_out); - /** * @param lwgeom geometry to convert to EWKT */ @@ -1965,14 +1940,13 @@ extern char *lwgeom_to_ewkt(const LWGEOM *lwgeom); /** * @param check parser check flags, see LW_PARSER_CHECK_* macros + * @param size length of WKB byte buffer + * @param wkb WKB byte buffer */ 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 wkt WKT string * @param check parser check flags, see LW_PARSER_CHECK_* macros */ extern LWGEOM* lwgeom_from_wkt(const char *wkt, const char check); @@ -2002,6 +1976,25 @@ extern void lwfree(void *mem); /* Utilities */ extern char *lwmessage_truncate(char *str, int startpos, int endpos, int maxlength, int truncdirection); +/* +* TWKB functions +*/ + +/** + * @param check parser check flags, see LW_PARSER_CHECK_* macros + * @param size parser check flags, see LW_PARSER_CHECK_* macros + */ +extern LWGEOM* lwgeom_from_twkb(uint8_t *twkb, size_t twkb_size, char check); + +/** + * @param geom input geometry + * @param variant what variations on TWKB are requested? + * @param twkb_size returns the length of the output TWKB in bytes if set + */ +extern uint8_t* lwgeom_to_twkb(const LWGEOM *geom, uint8_t variant, int8_t precision_xy, int8_t precision_z, int8_t precision_m, size_t *twkb_size); + +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); + /******************************************************************************* * SQLMM internal functions - TODO: Move into separate header files ******************************************************************************/ diff --git a/liblwgeom/lwin_twkb.c b/liblwgeom/lwin_twkb.c index df4bd3cbf..d79e6748a 100644 --- a/liblwgeom/lwin_twkb.c +++ b/liblwgeom/lwin_twkb.c @@ -14,27 +14,42 @@ #include "lwgeom_log.h" #include "varint.h" +#define TWKB_IN_MAXCOORDS 4 + /** * Used for passing the parse state between the parsing functions. */ -typedef struct +typedef struct { - /*General*/ + /* Pointers to the bytes */ 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*/ + 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 */ - /*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*/ + + 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; @@ -47,58 +62,81 @@ LWGEOM* lwgeom_from_twkb_state(twkb_parse_state *s); /**********************************************************************/ /** -* Check that we are not about to read off the end of the WKB +* 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) +static inline void twkb_parse_state_advance(twkb_parse_state *s, size_t next) { - if( (s->pos + next) > s->twkb_end) - lwerror("TWKB structure does not match expected size!"); -} + { + lwerror("%s: TWKB structure does not match expected size!", __func__); + // lwnotice("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) + s->pos += next; +} + +static inline int64_t twkb_parse_state_varint(twkb_parse_state *s) { - LWDEBUG(2,"Entering lwtype_from_twkb_state"); + size_t size; + int64_t val = varint_s64_decode(s->pos, s->twkb_end, &size); + twkb_parse_state_advance(s, size); + return val; +} + +static inline uint64_t twkb_parse_state_uvarint(twkb_parse_state *s) +{ + size_t size; + uint64_t val = varint_u64_decode(s->pos, s->twkb_end, &size); + twkb_parse_state_advance(s, size); + return val; +} + +static 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); + twkb_parse_state_advance(s, size); + return val / factor; +} - +static inline void twkb_parse_state_varint_skip(twkb_parse_state *s) +{ + size_t size = varint_size(s->pos, s->twkb_end); + + if ( ! size ) + lwerror("%s: no varint to skip", __func__); + + twkb_parse_state_advance(s, size); + return; +} + + + +static uint32_t lwtype_from_twkb_type(uint8_t twkb_type) +{ switch (twkb_type) { - case 1: - s->lwtype = POINTTYPE; - break; - case 2: - s->lwtype = LINETYPE; - break; + case 1: + return POINTTYPE; + case 2: + return LINETYPE; case 3: - s->lwtype = POLYGONTYPE; - break; + return POLYGONTYPE; case 4: - s->lwtype = MULTIPOINTTYPE; - break; + return MULTIPOINTTYPE; case 5: - s->lwtype = MULTILINETYPE; - break; + return MULTILINETYPE; case 6: - s->lwtype = MULTIPOLYGONTYPE; - break; - case 7: - s->lwtype = COLLECTIONTYPE; - break; + return MULTIPOLYGONTYPE; + case 7: + return COLLECTIONTYPE; default: /* Error! */ lwerror("Unknown WKB type"); - break; + return 0; } - - LWDEBUGF(4,"Got lwtype %s (%u)", lwtype_name(s->lwtype), s->lwtype); - - return; + return 0; } /** @@ -107,48 +145,56 @@ static void lwtype_from_twkb_state(twkb_parse_state *s, uint8_t twkb_type) */ 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; + uint8_t val = *(s->pos); + twkb_parse_state_advance(s, WKB_BYTE_SIZE); + return val; } /** * 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; + int i; + double *dlist; - + LWDEBUG(2,"Entering ptarray_from_twkb_state"); LWDEBUGF(4,"Pointarray has %d points", npoints); - /* Empty! */ if( npoints == 0 ) - return ptarray_construct(s->has_z, s->has_m, npoints); - - double *dlist; + return ptarray_construct_empty(s->has_z, s->has_m, 0); + pa = ptarray_construct(s->has_z, s->has_m, npoints); dlist = (double*)(pa->serialized_pointlist); for( i = 0; i < npoints; i++ ) { - for (j=0;jcoords[j] += twkb_parse_state_varint(s); + dlist[ndims*i + j] = s->coords[j] / s->factor; + j++; + /* Y */ + s->coords[j] += twkb_parse_state_varint(s); + dlist[ndims*i + j] = s->coords[j] / s->factor; + j++; + /* Z */ + if ( s->has_z ) + { + s->coords[j] += twkb_parse_state_varint(s); + dlist[ndims*i + j] = s->coords[j] / s->factor_z; + j++; + } + /* M */ + if ( s->has_m ) { - s->coords[j]+=varint_s64_read(&(s->pos), s->twkb_end); - dlist[(ndims*i)+j] = s->coords[j]/s->factor; + s->coords[j] += twkb_parse_state_varint(s); + dlist[ndims*i + j] = s->coords[j] / s->factor_m; + j++; } } @@ -160,15 +206,16 @@ static POINTARRAY* ptarray_from_twkb_state(twkb_parse_state *s, uint32_t npoints */ static LWPOINT* lwpoint_from_twkb_state(twkb_parse_state *s) { - LWDEBUG(2,"Entering lwpoint_from_twkb_state"); static uint32_t npoints = 1; + POINTARRAY *pa; + + LWDEBUG(2,"Entering lwpoint_from_twkb_state"); - /* 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); + if ( s->is_empty ) + return lwpoint_construct_empty(SRID_UNKNOWN, s->has_z, s->has_m); + + pa = ptarray_from_twkb_state(s, npoints); + return lwpoint_construct(SRID_UNKNOWN, NULL, pa); } /** @@ -176,19 +223,25 @@ static LWPOINT* lwpoint_from_twkb_state(twkb_parse_state *s) */ static LWLINE* lwline_from_twkb_state(twkb_parse_state *s) { - LWDEBUG(2,"Entering lwline_from_twkb_state"); uint32_t npoints; + POINTARRAY *pa; - /* 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); + LWDEBUG(2,"Entering lwline_from_twkb_state"); + + if ( s->is_empty ) + return lwline_construct_empty(SRID_UNKNOWN, s->has_z, s->has_m); + + /* Read number of points */ + npoints = twkb_parse_state_uvarint(s); + + if ( npoints == 0 ) + return lwline_construct_empty(SRID_UNKNOWN, s->has_z, s->has_m); + + /* Read coordinates */ + pa = ptarray_from_twkb_state(s, npoints); + + if( pa == NULL ) + return lwline_construct_empty(SRID_UNKNOWN, s->has_z, s->has_m); if( s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 2 ) { @@ -196,7 +249,7 @@ static LWLINE* lwline_from_twkb_state(twkb_parse_state *s) return NULL; } - return lwline_construct(0, NULL, pa); + return lwline_construct(SRID_UNKNOWN, NULL, pa); } /** @@ -204,34 +257,45 @@ static LWLINE* lwline_from_twkb_state(twkb_parse_state *s) */ static LWPOLY* lwpoly_from_twkb_state(twkb_parse_state *s) { - LWDEBUG(2,"Entering lwpoly_from_twkb_state"); - uint32_t nrings,npoints; + uint32_t nrings; 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); + LWPOLY *poly; + + LWDEBUG(2,"Entering lwpoly_from_twkb_state"); + + if ( s->is_empty ) + return lwpoly_construct_empty(SRID_UNKNOWN, s->has_z, s->has_m); + + /* Read number of rings */ + nrings = twkb_parse_state_uvarint(s); + + /* Start w/ empty polygon */ + poly = lwpoly_construct_empty(SRID_UNKNOWN, 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); + /* Ret number of points */ + uint32_t npoints = twkb_parse_state_uvarint(s); + POINTARRAY *pa = ptarray_from_twkb_state(s, npoints); + + /* Skip empty rings */ if( pa == NULL ) continue; + /* Force first and last points to be the same. */ + if( ! ptarray_is_closed_2d(pa) ) + { + POINT4D pt; + getPoint4d_p(pa, 0, &pt); + ptarray_append_point(pa, &pt, LW_FALSE); + } + /* Check for at least four points. */ if( s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 4 ) { @@ -240,15 +304,6 @@ static LWPOLY* lwpoly_from_twkb_state(twkb_parse_state *s) 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 ) { @@ -257,7 +312,7 @@ static LWPOLY* lwpoly_from_twkb_state(twkb_parse_state *s) } } - return poly; + return poly; } @@ -266,33 +321,37 @@ static LWPOLY* lwpoly_from_twkb_state(twkb_parse_state *s) */ static LWCOLLECTION* lwmultipoint_from_twkb_state(twkb_parse_state *s) { - LWDEBUG(2,"Entering lwmultipoint_from_twkb_state"); - int ngeoms; + int ngeoms, i; 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) + LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype, SRID_UNKNOWN, s->has_z, s->has_m); + + LWDEBUG(2,"Entering lwmultipoint_from_twkb_state"); + + if ( s->is_empty ) + return col; + + /* Read number of geometries */ + ngeoms = twkb_parse_state_uvarint(s); + LWDEBUGF(4,"Number of geometries %d", ngeoms); + + /* It has an idlist, we need to skip that */ + if ( s->has_idlist ) + { + for ( i = 0; i < ngeoms; i++ ) + twkb_parse_state_varint_skip(s); + } + + for ( i = 0; i < ngeoms; i++ ) { - geom = (LWGEOM*) lwpoint_from_twkb_state(s); + geom = lwpoint_as_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; + + return col; } /** @@ -300,33 +359,38 @@ static LWCOLLECTION* lwmultipoint_from_twkb_state(twkb_parse_state *s) */ static LWCOLLECTION* lwmultiline_from_twkb_state(twkb_parse_state *s) { - LWDEBUG(2,"Entering lwmultilinestring_from_twkb_state"); - int ngeoms; + int ngeoms, i; 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); + LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype, SRID_UNKNOWN, s->has_z, s->has_m); + + LWDEBUG(2,"Entering lwmultilinestring_from_twkb_state"); + + if ( s->is_empty ) + return col; + + /* Read number of geometries */ + ngeoms = twkb_parse_state_uvarint(s); + LWDEBUGF(4,"Number of geometries %d",ngeoms); - LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype,0, s->has_z, s->has_m); - while (ngeoms>0) + + /* It has an idlist, we need to skip that */ + if ( s->has_idlist ) + { + for ( i = 0; i < ngeoms; i++ ) + twkb_parse_state_varint_skip(s); + } + + for ( i = 0; i < ngeoms; i++ ) { - geom = (LWGEOM*) lwline_from_twkb_state(s); + geom = lwline_as_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; + + return col; } /** @@ -334,33 +398,37 @@ static LWCOLLECTION* lwmultiline_from_twkb_state(twkb_parse_state *s) */ static LWCOLLECTION* lwmultipoly_from_twkb_state(twkb_parse_state *s) { - LWDEBUG(2,"Entering lwmultipolygon_from_twkb_state"); - int ngeoms; + int ngeoms, i; 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); + LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype, SRID_UNKNOWN, s->has_z, s->has_m); + + LWDEBUG(2,"Entering lwmultipolygon_from_twkb_state"); + + if ( s->is_empty ) + return col; + + /* Read number of geometries */ + ngeoms = twkb_parse_state_uvarint(s); LWDEBUGF(4,"Number of geometries %d",ngeoms); - LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype,0, s->has_z, s->has_m); - while (ngeoms>0) + + /* It has an idlist, we need to skip that */ + if ( s->has_idlist ) + { + for ( i = 0; i < ngeoms; i++ ) + twkb_parse_state_varint_skip(s); + } + + for ( i = 0; i < ngeoms; i++ ) { - geom = (LWGEOM*) lwpoly_from_twkb_state(s); + geom = lwpoly_as_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; + + return col; } @@ -369,22 +437,28 @@ static LWCOLLECTION* lwmultipoly_from_twkb_state(twkb_parse_state *s) **/ static LWCOLLECTION* lwcollection_from_twkb_state(twkb_parse_state *s) { - LWDEBUG(2,"Entering lwcollection_from_twkb_state"); - int ngeoms; + int ngeoms, i; 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); + LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype, SRID_UNKNOWN, s->has_z, s->has_m); + + LWDEBUG(2,"Entering lwcollection_from_twkb_state"); + + if ( s->is_empty ) + return col; + + /* Read number of geometries */ + ngeoms = twkb_parse_state_uvarint(s); + LWDEBUGF(4,"Number of geometries %d",ngeoms); - LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype,0, s->has_z, s->has_m); - while (ngeoms>0) + + /* It has an idlist, we need to skip that */ + if ( s->has_idlist ) + { + for ( i = 0; i < ngeoms; i++ ) + twkb_parse_state_varint_skip(s); + } + + for ( i = 0; i < ngeoms; i++ ) { geom = lwgeom_from_twkb_state(s); if ( lwcollection_add_lwgeom(col, geom) == NULL ) @@ -392,142 +466,203 @@ static LWCOLLECTION* lwcollection_from_twkb_state(twkb_parse_state *s) 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; + + + return col; } -static void magicbyte_from_twkb_state(twkb_parse_state *s) +static void header_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 info */ - - /*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; + + uint8_t extended_dims; + + /* Read the first two bytes */ + uint8_t type_precision = byte_from_twkb_state(s); + uint8_t metadata = byte_from_twkb_state(s); + + /* Strip type and precision out of first byte */ + uint8_t type = type_precision & 0x0F; + int8_t precision = unzigzag8((type_precision & 0xF0) >> 4); + + /* Convert TWKB type to internal type */ + s->lwtype = lwtype_from_twkb_type(type); + + /* Convert the precision into factor */ + s->factor = pow(10, (double)precision); + + /* Strip metadata flags out of second byte */ + s->has_bbox = metadata & 0x01; + s->has_size = (metadata & 0x02) >> 1; + s->has_idlist = (metadata & 0x04) >> 2; + extended_dims = (metadata & 0x08) >> 3; + s->is_empty = (metadata & 0x10) >> 4; + + /* Flag for higher dims means read a third byte */ + if ( extended_dims ) + { + int8_t precision_z, precision_m; + + extended_dims = byte_from_twkb_state(s); + + /* Strip Z/M presence and precision from ext byte */ + s->has_z = (extended_dims & 0x01); + s->has_m = (extended_dims & 0x02) >> 1; + precision_z = (extended_dims & 0x1C) >> 2; + precision_m = (extended_dims & 0xE0) >> 5; + + /* Convert the precision into factor */ + s->factor_z = pow(10, (double)precision_z); + s->factor_m = pow(10, (double)precision_m); + } + else + { + s->has_z = 0; + s->has_m = 0; + s->factor_z = 0; + s->factor_m = 0; + } + + /* Read the size, if there is one */ + if ( s->has_size ) + { + s->size = twkb_parse_state_uvarint(s); + } + + /* Calculate the number of dimensions */ + s->ndims = 2 + s->has_z + s->has_m; + + return; } + + /** -* 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. +* Generic handling for TWKB geometries. The front of every TWKB geometry +* (including those embedded in collections) is a type byte and metadata byte, +* then optional size, bbox, etc. Read those, then switch to particular type +* handling code. */ 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) + GBOX bbox; + LWGEOM *geom = NULL; + uint32_t has_bbox = LW_FALSE; + int i; + + /* Read the first two bytes, and optional */ + /* extended precision info and optional size info */ + header_from_twkb_state(s); + + /* Just experienced a geometry header, so now we */ + /* need to reset our coordinate deltas */ + for ( i = 0; i < TWKB_IN_MAXCOORDS; i++ ) + { + s->coords[i] = 0.0; + } + + /* Read the bounding box, is there is one */ + if ( s->has_bbox ) { - 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; + /* Initialize */ + has_bbox = s->has_bbox; + memset(&bbox, 0, sizeof(GBOX)); + bbox.flags = gflags(s->has_z, s->has_m, 0); + + /* X */ + bbox.xmin = twkb_parse_state_double(s, s->factor); + bbox.xmax = bbox.xmin + twkb_parse_state_double(s, s->factor); + /* Y */ + bbox.ymin = twkb_parse_state_double(s, s->factor); + bbox.ymax = bbox.ymin + twkb_parse_state_double(s, s->factor); + /* Z */ + if ( s->has_z ) + { + bbox.zmin = twkb_parse_state_double(s, s->factor_z); + bbox.zmax = bbox.zmin + twkb_parse_state_double(s, s->factor_z); + } + /* M */ + if ( s->has_z ) + { + bbox.mmin = twkb_parse_state_double(s, s->factor_m); + bbox.mmax = bbox.mmin + twkb_parse_state_double(s, s->factor_m); + } } - - lwtype_from_twkb_state(s, twkb_type); - - - /* Do the right thing */ + + /* Switch to code for the particular type we're dealing with */ switch( s->lwtype ) { case POINTTYPE: - return (LWGEOM*)lwpoint_from_twkb_state(s); + geom = lwpoint_as_lwgeom(lwpoint_from_twkb_state(s)); break; case LINETYPE: - return (LWGEOM*)lwline_from_twkb_state(s); + geom = lwline_as_lwgeom(lwline_from_twkb_state(s)); break; case POLYGONTYPE: - return (LWGEOM*)lwpoly_from_twkb_state(s); + geom = lwpoly_as_lwgeom(lwpoly_from_twkb_state(s)); break; case MULTIPOINTTYPE: - return (LWGEOM*)lwmultipoint_from_twkb_state(s); + geom = lwcollection_as_lwgeom(lwmultipoint_from_twkb_state(s)); break; case MULTILINETYPE: - return (LWGEOM*)lwmultiline_from_twkb_state(s); + geom = lwcollection_as_lwgeom(lwmultiline_from_twkb_state(s)); break; case MULTIPOLYGONTYPE: - return (LWGEOM*)lwmultipoly_from_twkb_state(s); + geom = lwcollection_as_lwgeom(lwmultipoly_from_twkb_state(s)); break; case COLLECTIONTYPE: - return (LWGEOM*)lwcollection_from_twkb_state(s); + geom = lwcollection_as_lwgeom(lwcollection_from_twkb_state(s)); break; - /* Unknown type! */ default: lwerror("Unsupported geometry type: %s [%d]", lwtype_name(s->lwtype), s->lwtype); + break; + } + + if ( has_bbox ) + { + geom->bbox = gbox_clone(&bbox); } - /* Return value to keep compiler happy. */ - return NULL; - + return geom; } -/* 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 +* 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, +* 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"); + int64_t coords[TWKB_IN_MAXCOORDS] = {0, 0, 0, 0}; twkb_parse_state s; - + + LWDEBUG(2,"Entering lwgeom_from_twkb"); + LWDEBUGF(4,"twkb_size: %d",(int) twkb_size); + + /* Zero out the state */ + memset(&s, 0, sizeof(twkb_parse_state)); + /* Initialize the state appropriately */ - s.twkb = twkb; - s.twkb_end = twkb+twkb_size; + s.twkb = s.pos = 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.coords = coords; + + /* Handle 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); + + + /* Read the rest of the geometry */ return lwgeom_from_twkb_state(&s); } diff --git a/liblwgeom/lwout_twkb.c b/liblwgeom/lwout_twkb.c index 755a45541..b2c7bcb9d 100644 --- a/liblwgeom/lwout_twkb.c +++ b/liblwgeom/lwout_twkb.c @@ -10,1065 +10,504 @@ **********************************************************************/ #include "lwout_twkb.h" -#include "varint.h" /* -* GeometryType +* GeometryType, and dimensions */ static uint8_t lwgeom_twkb_type(const LWGEOM *geom) { + uint8_t twkb_type = 0; + LWDEBUGF(2, "Entered lwgeom_twkb_type",0); - uint8_t wkb_type = 0; - uint8_t type_flag = 0; - int dims; - + switch ( geom->type ) { - case POINTTYPE: - wkb_type = WKB_POINT_TYPE; - break; - case LINETYPE: - wkb_type = WKB_LINESTRING_TYPE; - break; - case POLYGONTYPE: - wkb_type = WKB_POLYGON_TYPE; - break; - case MULTIPOINTTYPE: - wkb_type = WKB_MULTIPOINT_TYPE; - break; - case MULTILINETYPE: - wkb_type = WKB_MULTILINESTRING_TYPE; - break; - case MULTIPOLYGONTYPE: - wkb_type = WKB_MULTIPOLYGON_TYPE; - break; - case COLLECTIONTYPE: - wkb_type = WKB_GEOMETRYCOLLECTION_TYPE; - break; - case CIRCSTRINGTYPE: - wkb_type = WKB_CIRCULARSTRING_TYPE; - break; - case COMPOUNDTYPE: - wkb_type = WKB_COMPOUNDCURVE_TYPE; - break; - case CURVEPOLYTYPE: - wkb_type = WKB_CURVEPOLYGON_TYPE; - break; - case MULTICURVETYPE: - wkb_type = WKB_MULTICURVE_TYPE; - break; - case MULTISURFACETYPE: - wkb_type = WKB_MULTISURFACE_TYPE; - break; - case POLYHEDRALSURFACETYPE: - wkb_type = WKB_POLYHEDRALSURFACE_TYPE; - break; - case TINTYPE: - wkb_type = WKB_TIN_TYPE; - break; - case TRIANGLETYPE: - wkb_type = WKB_TRIANGLE_TYPE; - break; - default: - lwerror("Unsupported geometry type: %s [%d]", - lwtype_name(geom->type), geom->type); + case POINTTYPE: + twkb_type = WKB_POINT_TYPE; + break; + case LINETYPE: + twkb_type = WKB_LINESTRING_TYPE; + break; + case POLYGONTYPE: + twkb_type = WKB_POLYGON_TYPE; + break; + case MULTIPOINTTYPE: + twkb_type = WKB_MULTIPOINT_TYPE; + break; + case MULTILINETYPE: + twkb_type = WKB_MULTILINESTRING_TYPE; + break; + case MULTIPOLYGONTYPE: + twkb_type = WKB_MULTIPOLYGON_TYPE; + break; + case COLLECTIONTYPE: + twkb_type = WKB_GEOMETRYCOLLECTION_TYPE; + break; + default: + lwerror("Unsupported geometry type: %s [%d]", + lwtype_name(geom->type), geom->type); } - - /*Set the type*/ - TYPE_DIM_SET_TYPE(type_flag,wkb_type); - LWDEBUGF(4, "Writing type '%d'", wkb_type); - - /*Set number of dimensions*/ - dims = FLAGS_NDIMS(geom->flags); - if (dims>4) - lwerror("TWKB only supports 4 dimensions"); - TYPE_DIM_SET_DIM(type_flag,dims); - LWDEBUGF(4, "Writing ndims '%d'", dims); - - return type_flag; -} - - -/** -Function for putting a Byte value into the buffer -*/ -static int uint8_to_twkb_buf(const uint8_t ival, uint8_t **buf) -{ - LWDEBUGF(2, "Entered uint8_to_twkb_buf",0); - LWDEBUGF(4, "Writing value %d",ival); - memcpy(*buf, &ival, WKB_BYTE_SIZE); - (*buf)++; - return 0; -} - -/* -* Empty -*/ -static size_t empty_to_twkb_size(const LWGEOM *geom, uint8_t *variant, int64_t id) -{ - LWDEBUGF(2, "Entered empty_to_twkb_size",0); - size_t size = WKB_BYTE_SIZE; - - /*If bboxes is requested we ignore it. - There is no bbox to an empty geometry*/ - *variant = *variant & ~TWKB_BBOXES; - - /*size of ID*/ - if ((*variant & TWKB_ID) && !(*variant & TWKB_NO_ID)) - size += varint_s64_encoded_size((int64_t) id); - /*size of npoints*/ - size += varint_u64_encoded_size((uint64_t) 0); - - return size; + return twkb_type; } -static int empty_to_twkb_buf(const LWGEOM *geom, uint8_t **buf, uint8_t *variant, int64_t id) -{ - LWDEBUGF(2, "Entered empty_to_twkb_buf",0); - uint32_t wkb_type = lwgeom_twkb_type(geom); - if ( geom->type == POINTTYPE ) - { - /* Change POINT to MULTIPOINT */ - wkb_type &= ~WKB_POINT_TYPE; /* clear POINT flag */ - wkb_type |= WKB_MULTIPOINT_TYPE; /* set MULTIPOINT flag */ - } - - /* Set the geometry id */ - if ((*variant & TWKB_ID) && !(*variant & TWKB_NO_ID)) - varint_s64_encode_buf(id, buf); - - /* Set the geometry type */ - uint8_to_twkb_buf(wkb_type,buf); - - /* Set nrings/npoints/ngeoms to zero */ - varint_u64_encode_buf(0, buf); - return 0; -} /** -Calculates the size of the bbox in varInts in the form: -xmin, xmax, deltax, deltay +* Calculates the size of the bbox in varints in the form: +* xmin, xdelta, ymin, ydelta */ -static size_t sizeof_bbox(int dims,int64_t min_values[], int64_t max_values[]) +static size_t sizeof_bbox(TWKB_STATE *ts, int ndims) { int i; + uint8_t buf[16]; size_t size = 0; - for (i=0;ibbox_min[i], buf); + size += varint_s64_encode_buf((ts->bbox_max[i] - ts->bbox_min[i]), buf); + } return size; } /** -Writes the bbox in varInts in the form: -xmin, xmax, deltax, deltay +* Writes the bbox in varints in the form: +* xmin, xdelta, ymin, ydelta */ -static void write_bbox(uint8_t **buf,int dims,int64_t min_values[], int64_t max_values[]) +static void write_bbox(TWKB_STATE *ts, int ndims) { int i; - for (i=0;iheader_buf, ts->bbox_min[i]); + bytebuffer_append_varint(ts->header_buf, (ts->bbox_max[i] - ts->bbox_min[i])); } - } + /** -Calculates the needed space for storing a specific pointarray as varInt-encoded +* Stores a pointarray as varints in the buffer */ -static size_t ptarray_to_twkb_size(const POINTARRAY *pa, uint8_t variant,int factor,int64_t accum_rel[3][4]) +static int ptarray_to_twkb_buf(const POINTARRAY *pa, TWKB_GLOBALS *globals, TWKB_STATE *ts, int register_npoints) { - LWDEBUGF(2, "Entered ptarray_to_twkb_size_m1",0); int64_t r; - int dims = FLAGS_NDIMS(pa->flags); - size_t size = 0; - int j,i; - double *dbl_ptr; - /*The variable factor is used to "shift" the double float coordinate to keep enough significant digits, - for demanded precision, when cast to integer*/ -/* factor=pow(10,prec); */ - /* Include the npoints size if it's not a POINT type) */ - if ( ! ( variant & WKB_NO_NPOINTS ) ) + int ndims = FLAGS_NDIMS(pa->flags); + int i, j; + + LWDEBUGF(2, "Entered %s", __func__); + + /* Set the number of points (if it's not a POINT type) */ + if ( register_npoints ) { - LWDEBUGF(4, "We add space for npoints",0); - size+=varint_u64_encoded_size(pa->npoints); + bytebuffer_append_uvarint(ts->geom_buf, pa->npoints); + LWDEBUGF(4, "Register npoints:%d", pa->npoints); } for ( i = 0; i < pa->npoints; i++ ) { - dbl_ptr = (double*)getPoint_internal(pa, i); - for ( j = 0; j < dims; j++ ) + LWDEBUGF(4, "Writing point #%d", i); + double *dbl_ptr = (double*)getPoint_internal(pa, i); + + /* Write this coordinate to the buffer as a varint */ + for ( j = 0; j < ndims; j++ ) { - LWDEBUGF(4, "dim nr %d, refvalue is %d",j, accum_rel[0][j]); - r=(int64_t) lround(factor*dbl_ptr[j])-accum_rel[0][j]; - accum_rel[0][j]+=r; - if(variant&TWKB_BBOXES) + /* To get the relative coordinate we don't get the distance */ + /* from the last point but instead the distance from our */ + /* last accumulated point. This is important to not build up an */ + /* accumulated error when rounding the coordinates */ + r = (int64_t) lround(globals->factor[j] * dbl_ptr[j]) - ts->accum_rels[j]; + LWDEBUGF(4, "deltavalue: %d, ", r); + ts->accum_rels[j] += r; + bytebuffer_append_varint(ts->geom_buf, r); + } + + /* See if this coordinate expands the bounding box */ + if( globals->variant & TWKB_BBOX ) + { + for ( j = 0; j < ndims; j++ ) { - if(accum_rel[0][j]accum_rel[2][j]) - accum_rel[2][j]=accum_rel[0][j]; + if( ts->accum_rels[j] > ts->bbox_max[j] ) + ts->bbox_max[j] = ts->accum_rels[j]; + + if( ts->accum_rels[j] < ts->bbox_min[j] ) + ts->bbox_min[j] = ts->accum_rels[j]; } - size += varint_s64_encoded_size((int64_t) r); - LWDEBUGF(4, "deltavalue = %d and resulting refvalue is %d",r,accum_rel[0][j]); } - } - return size; -} - -/** -Stores a pointarray as varInts in the buffer -*/ -static int ptarray_to_twkb_buf(const POINTARRAY *pa, uint8_t **buf, uint8_t variant,int64_t factor,int64_t accum_rel[]) -{ - LWDEBUG(2, "entered ptarray_to_twkb_buf\n"); - int64_t r; - int dims = FLAGS_NDIMS(pa->flags); - int i, j; - double *dbl_ptr; - /*factor=pow(10,prec);*/ - - - - /* Set the number of points (if it's not a POINT type) */ - if ( ! ( variant & WKB_NO_NPOINTS ) ) - { - varint_u64_encode_buf(pa->npoints,buf); - LWDEBUGF(4, "Register npoints:%d",pa->npoints); } - for ( i = 0; i < pa->npoints; i++ ) - { - LWDEBUGF(4, "Writing point #%d", i); - dbl_ptr = (double*)getPoint_internal(pa, i); - for ( j = 0; j < dims; j++ ) - { - /*To get the relative coordinate we don't get the distance from the last point - but instead the distance from our accumulated last point - This is important to not build up a accumulated error when rounding the coordinates*/ - r=(int64_t) lround(factor*dbl_ptr[j])-accum_rel[j]; - LWDEBUGF(4, "deltavalue: %d, ",r ); - accum_rel[j]+=r; - varint_s64_encode_buf(r,buf); - } - } - /* LWDEBUGF(4, "Done (buf = %p)", buf); */ return 0; } /****************************************************************** -POINTS +* POINTS *******************************************************************/ -/** -Calculates needed storage size for aggregated points -*/ -static size_t lwgeom_agg_to_twkbpoint_size(lwgeom_id *geom_array,uint8_t *variant,int n,int64_t factor,int64_t refpoint[3][4]) -{ - LWDEBUG(2, "entered lwgeom_agg_to_twkbpoint_size\n"); - lwgeom_id *li; - /*One byte for type declaration*/ - size_t size = WKB_BYTE_SIZE; - /*One integer holding number of geometries*/ - size += varint_u64_encoded_size((uint64_t) n); - int i; - for (i=0;igeom),variant,factor,li->id,refpoint); - } - return size; -} -/** -Calculates needed storage size for a point -*/ -static size_t lwpoint_to_twkb_size(const LWPOINT *pt,uint8_t *variant, int64_t factor, int64_t id,int64_t refpoint[3][4]) +static int lwpoint_to_twkb_buf(const LWPOINT *pt, TWKB_GLOBALS *globals, TWKB_STATE *ts) { - LWDEBUG(2, "entered lwpoint_to_twkb_size\n"); - size_t size = 0; - /* geometry id, if not subgeometry in type 4,5 or 6*/ - if ((*variant & TWKB_ID) && !(*variant & TWKB_NO_ID)) - size += varint_s64_encoded_size((int64_t) id); + LWDEBUGF(2, "Entered %s", __func__); - /* Points */ - size += ptarray_to_twkb_size(pt->point, *variant | WKB_NO_NPOINTS, factor,refpoint); - return size; + /* Set the coordinates (don't write npoints) */ + ptarray_to_twkb_buf(pt->point, globals, ts, 0); + return 0; } +/****************************************************************** +* LINESTRINGS +*******************************************************************/ -/** -Iterates an aggregation of points -*/ -static int lwgeom_agg_to_twkbpoint_buf(lwgeom_id* geom_array,int n, uint8_t **buf, uint8_t *variant,int64_t factor, int64_t refpoint[3][4]) +static int lwline_to_twkb_buf(const LWLINE *line, TWKB_GLOBALS *globals, TWKB_STATE *ts) { - LWDEBUG(2, "entered lwgeom_agg_to_twkbpoint_buf\n"); - lwgeom_id *li; - uint8_t type_flag = 0; - int i,dims; - /*Set the type*/ - TYPE_DIM_SET_TYPE(type_flag,21); - LWDEBUGF(4, "Writing type '%d'", 21); - - /*Set number of dimensions*/ - dims = FLAGS_NDIMS(((LWGEOM*) (geom_array->geom))->flags); - if (dims>4) - lwerror("TWKB only supports 4 dimensions"); - TYPE_DIM_SET_DIM(type_flag,dims); - LWDEBUGF(4, "Writing ndims '%d'", dims); - uint8_to_twkb_buf(type_flag,buf); - - if(*variant&TWKB_BBOXES) - { - write_bbox(buf,dims,refpoint[1],refpoint[2]); - /* So we only write bboxes to highest level */ - *variant = *variant & ~TWKB_BBOXES; - } - - /* Set number of geometries */ - varint_u64_encode_buf(n, buf); - - for (i=0;igeom),buf,variant,factor,li->id,refpoint[0]); - - } - return 0; -} + LWDEBUGF(2, "Entered %s", __func__); -/** -Sends a point to the buffer -*/ -static int lwpoint_to_twkb_buf(const LWPOINT *pt, uint8_t **buf, uint8_t *variant,int64_t factor, int64_t id,int64_t refpoint[]) -{ - LWDEBUG(2, "entered lwpoint_to_twkb_buf\n"); - /* Set the geometry id, if not subgeometry in type 4,5 or 6*/ - if ((*variant & TWKB_ID) && !(*variant & TWKB_NO_ID)) - varint_s64_encode_buf(id, buf); - - - /* Set the coordinates */ - ptarray_to_twkb_buf(pt->point, buf, *variant | WKB_NO_NPOINTS,factor,refpoint); - LWDEBUGF(4, "Pointarray set, buf = %p", buf); + /* Set the coordinates (do write npoints) */ + ptarray_to_twkb_buf(line->points, globals, ts, 1); return 0; } /****************************************************************** -LINESTRINGS +* POLYGONS *******************************************************************/ -/** -Calculates needed storage size for aggregated lines -*/ -static size_t lwgeom_agg_to_twkbline_size(lwgeom_id* geom_array,uint8_t *variant,int n,int64_t factor,int64_t refpoint[3][4]) + +static int lwpoly_to_twkb_buf(const LWPOLY *poly, TWKB_GLOBALS *globals, TWKB_STATE *ts) { - lwgeom_id *li; - /*One byte for type declaration*/ - size_t size = WKB_BYTE_SIZE; - /*One integer holding number of lines*/ - size += varint_u64_encoded_size((uint64_t) n); int i; - for (i=0;igeom),variant,factor,li->id,refpoint); - } - return size; -} -/** -Calculates needed storage size for a line -*/ -static size_t lwline_to_twkb_size(const LWLINE *line,uint8_t *variant, int64_t factor, int64_t id,int64_t refpoint[3][4]) -{ - size_t size = 0; - /* geometry id, if not subgeometry in type 4,5 or 6*/ - if ((*variant & TWKB_ID) && !(*variant & TWKB_NO_ID)) - size += varint_s64_encoded_size((int64_t) id); - /* Size of point array */ - size += ptarray_to_twkb_size(line->points,*variant,factor,refpoint); - return size; -} -/** -Iterates an aggregation of lines -*/ -static int lwgeom_agg_to_twkbline_buf(lwgeom_id* geom_array,int n, uint8_t **buf, uint8_t *variant,int64_t factor, int64_t refpoint[3][4]) -{ + /* Set the number of rings */ + bytebuffer_append_uvarint(ts->geom_buf, (uint64_t) poly->nrings); - lwgeom_id *li; - uint8_t type_flag = 0; - int i,dims; - /*Set the type*/ - TYPE_DIM_SET_TYPE(type_flag,22); - LWDEBUGF(4, "Writing type '%d'",22); - - /*Set number of dimensions*/ - dims = FLAGS_NDIMS(((LWGEOM*) (geom_array->geom))->flags); - if (dims>4) - lwerror("TWKB only supports 4 dimensions"); - TYPE_DIM_SET_DIM(type_flag,dims); - LWDEBUGF(4, "Writing ndims '%d'", dims); - uint8_to_twkb_buf(type_flag,buf); - - if(*variant&TWKB_BBOXES) - { - write_bbox(buf,dims,refpoint[1],refpoint[2]); - /* So we only write bboxes to highest level */ - *variant = *variant & ~TWKB_BBOXES; - } - - /* Set number of geometries */ - varint_u64_encode_buf(n, buf); - for (i=0;inrings; i++ ) { - li=(geom_array+i); - lwline_to_twkb_buf((LWLINE*) li->geom,buf,variant,factor,li->id,refpoint[0]); + /* Set the coordinates (do write npoints) */ + ptarray_to_twkb_buf(poly->rings[i], globals, ts, 1); } + return 0; } -/** -Sends a line to the buffer -*/ -static int lwline_to_twkb_buf(const LWLINE *line, uint8_t **buf, uint8_t *variant,int64_t factor, int64_t id,int64_t refpoint[]) -{ - /* Set the geometry id, if not subgeometry in type 4,5 or 6*/ - if ((*variant & TWKB_ID) && !(*variant & TWKB_NO_ID)) - varint_s64_encode_buf(id, buf); - - /* Set the coordinates */ - ptarray_to_twkb_buf(line->points, buf, *variant,factor,refpoint); - return 0; -} /****************************************************************** -POLYGONS +* MULTI-GEOMETRYS (MultiPoint, MultiLinestring, MultiPolygon) *******************************************************************/ -/** -Calculates needed storage size for aggregated polygon -*/ -static size_t lwgeom_agg_to_twkbpoly_size(lwgeom_id* geom_array,uint8_t *variant,int n,int64_t factor,int64_t refpoint[3][4]) + +static int lwmulti_to_twkb_buf(const LWCOLLECTION *col, TWKB_GLOBALS *globals, TWKB_STATE *ts) { - lwgeom_id *li; - /*One byte for type declaration*/ - size_t size = WKB_BYTE_SIZE; - /*One integer holding number of collections*/ - size +=varint_u64_encoded_size((uint64_t) n); int i; - for (i=0;igeom),variant,factor,li->id,refpoint); - } - return size; -} + int nempty = 0; -/** -Calculates needed storage size for a polygon -*/ -static size_t lwpoly_to_twkb_size(const LWPOLY *poly,uint8_t *variant, int64_t factor, int64_t id,int64_t refpoint[3][4]) -{ - LWDEBUGF(2, "lwpoly_to_twkb_size entered%d",0); - int i; - - size_t size = 0; - /* geometry id, if not subgeometry in type 4,5 or 6*/ - if ((*variant & TWKB_ID) && !(*variant & TWKB_NO_ID)) - size += varint_s64_encoded_size((int64_t) id); - - /*nrings*/ - size += varint_u64_encoded_size((uint64_t) poly->nrings); - - LWDEBUGF(2, "we have %d rings to iterate",poly->nrings); - for ( i = 0; i < poly->nrings; i++ ) + LWDEBUGF(2, "Entered %s", __func__); + LWDEBUGF(4, "Number of geometries in multi is %d", col->ngeoms); + + /* Deal with special case for MULTIPOINT: skip any empty points */ + if ( col->type == MULTIPOINTTYPE ) { - /* Size of ring point array */ - size += ptarray_to_twkb_size(poly->rings[i],*variant,factor,refpoint); + for ( i = 0; i < col->ngeoms; i++ ) + if ( lwgeom_is_empty(col->geoms[i]) ) + nempty++; } - return size; -} - -/** -Iterates an aggregation of polygons -*/ -static int lwgeom_agg_to_twkbpoly_buf(lwgeom_id* geom_array,int n, uint8_t **buf, uint8_t *variant,int64_t factor, int64_t refpoint[3][4]) -{ + /* Set the number of geometries */ + bytebuffer_append_uvarint(ts->geom_buf, (uint64_t) (col->ngeoms - nempty)); - lwgeom_id *li; - uint8_t type_flag = 0; - int i,dims; - /*Set the type*/ - TYPE_DIM_SET_TYPE(type_flag,23); - LWDEBUGF(4, "Writing type '%d'", 23); - - /*Set number of dimensions*/ - dims = FLAGS_NDIMS(((LWGEOM*) (geom_array->geom))->flags); - if (dims>4) - lwerror("TWKB only supports 4 dimensions"); - TYPE_DIM_SET_DIM(type_flag,dims); - LWDEBUGF(4, "Writing ndims '%d'", dims); - uint8_to_twkb_buf(type_flag,buf); - - - if(*variant&TWKB_BBOXES) - { - write_bbox(buf,dims,refpoint[1],refpoint[2]); - /* So we only write bboxes to highest level */ - *variant = *variant & ~TWKB_BBOXES; - } - - - /* Set number of geometries */ - varint_u64_encode_buf(n, buf); - - for (i=0;iidlist ) { - li=(geom_array+i); - lwpoly_to_twkb_buf((LWPOLY*) (li->geom),buf,variant,factor,li->id,refpoint[0]); + for ( i = 0; i < col->ngeoms; i++ ) + { + /* Skip empty points in multipoints, we can't represent them */ + if ( col->type == MULTIPOINTTYPE && lwgeom_is_empty(col->geoms[i]) ) + continue; + + bytebuffer_append_varint(ts->geom_buf, ts->idlist[i]); + } + + /* Empty it out to nobody else uses it now */ + ts->idlist = NULL; } - return 0; -} -/** -Sends a polygon to the buffer -*/ -static int lwpoly_to_twkb_buf(const LWPOLY *poly, uint8_t **buf, uint8_t *variant,int64_t factor, int64_t id,int64_t refpoint[]) -{ - int i; - - /* Set the geometry id, if not subgeometry in type 4,5 or 6*/ - if ((*variant & TWKB_ID) && !(*variant & TWKB_NO_ID)) - varint_s64_encode_buf(id, buf); - - /* Set the number of rings */ - varint_u64_encode_buf(poly->nrings, buf); - - for ( i = 0; i < poly->nrings; i++ ) + for ( i = 0; i < col->ngeoms; i++ ) { - ptarray_to_twkb_buf(poly->rings[i], buf, *variant,factor,refpoint); - } + /* Skip empty points in multipoints, we can't represent them */ + if ( col->type == MULTIPOINTTYPE && lwgeom_is_empty(col->geoms[i]) ) + continue; + lwgeom_to_twkb_buf(col->geoms[i], globals, ts); + } return 0; } /****************************************************************** -COLLECTIONS +* GEOMETRYCOLLECTIONS *******************************************************************/ -/** -Calculates needed storage size for aggregated collection -*/ -static size_t lwgeom_agg_to_twkbcollection_size(lwgeom_id* geom_array,uint8_t *variant,int n,int64_t factor,int64_t refpoint[3][4]) + +static int lwcollection_to_twkb_buf(const LWCOLLECTION *col, TWKB_GLOBALS *globals, TWKB_STATE *ts) { - lwgeom_id *li; - LWDEBUGF(4, "lwgeom_agg_to_twkbcollection_size entered with %d collections",n); - /*One byte for type declaration*/ - size_t size = WKB_BYTE_SIZE; - /*One integer holding number of collections*/ - size += varint_u64_encoded_size((uint64_t) n); int i; - for (i=0;igeom),variant,factor,li->id,refpoint); - } - return size; -} -/** -Calculates needed storage size for a collection -*/ -static size_t lwcollection_to_twkb_size(const LWCOLLECTION *col,uint8_t *variant, int64_t factor, int64_t id,int64_t refpoint[3][4]) -{ - LWDEBUGF(2, "lwcollection_to_twkb_size entered, %d",0); - size_t size = 0; - /* geometry id, if not subgeometry in type 4,5 or 6*/ - if ((*variant & TWKB_ID) && !(*variant & TWKB_NO_ID)) - size += varint_s64_encoded_size((int64_t) id); - /* size of geoms */ - size += varint_u64_encoded_size((uint64_t) col->ngeoms); - int i = 0; - - *variant=*variant | TWKB_NO_ID; /*Switch off id:s*/ - for ( i = 0; i < col->ngeoms; i++ ) - { - size += lwgeom_to_twkb_size((LWGEOM*)col->geoms[i],variant, factor,id,refpoint); - } - *variant=*variant & ~TWKB_NO_ID; /*Switch back*/ - return size; -} + LWDEBUGF(2, "Entered %s", __func__); + LWDEBUGF(4, "Number of geometries in collection is %d", col->ngeoms); -/** -Iterates an aggregation of collections -*/ -static int lwgeom_agg_to_twkbcollection_buf(lwgeom_id* geom_array,int n, uint8_t **buf, uint8_t *variant,int64_t factor, int64_t refpoint[3][4]) -{ + /* Set the number of geometries */ + bytebuffer_append_uvarint(ts->geom_buf, (uint64_t) col->ngeoms); - lwgeom_id *li; - uint8_t type_flag = 0; - int i,dims; - /*Set the type*/ - TYPE_DIM_SET_TYPE(type_flag,24); - LWDEBUGF(4, "Writing type '%d'", 24); - - /*Set number of dimensions*/ - dims = FLAGS_NDIMS(((LWGEOM*) (geom_array->geom))->flags); - if (dims>4) - lwerror("TWKB only supports 4 dimensions"); - TYPE_DIM_SET_DIM(type_flag,dims); - LWDEBUGF(4, "Writing ndims '%d'", dims); - uint8_to_twkb_buf(type_flag,buf); - - - if(*variant&TWKB_BBOXES) - { - write_bbox(buf,dims,refpoint[1],refpoint[2]); - /* So we only write bboxes to highest level */ - *variant = *variant & ~TWKB_BBOXES; - } - - /* Set number of geometries */ - varint_u64_encode_buf(n, buf); - - for (i=0;iidlist ) { - li=(geom_array+i); - lwcollection_to_twkb_buf((LWCOLLECTION*) li->geom,buf,variant,factor,li->id,refpoint); + for ( i = 0; i < col->ngeoms; i++ ) + bytebuffer_append_varint(ts->geom_buf, ts->idlist[i]); + + /* Empty it out to nobody else uses it now */ + ts->idlist = NULL; } - return 0; -} -/** -Iterates a collection -*/ -static int lwcollection_to_twkb_buf(const LWCOLLECTION *col, uint8_t **buf, uint8_t *variant,int64_t factor, int64_t id,int64_t refpoint[3][4]) -{ - LWDEBUG(2, "lwcollection_to_twkb_buf entered"); - int i; - - /* Set the geometry id*/ - if ((*variant & TWKB_ID) && !(*variant & TWKB_NO_ID)) - varint_s64_encode_buf(id, buf); - - /* Set the number of geometries */ - LWDEBUGF(4, "Number of geometries in collection is %d",col->ngeoms); - varint_u64_encode_buf(col->ngeoms, buf); - - /* Write the sub-geometries. */ - *variant=*variant | TWKB_NO_ID; /*Switch off id:s*/ - for ( i = 0; i < col->ngeoms; i++ ) - { - LWDEBUGF(4, "Subgeoemtry nr %d with TWKB_NO_TYPE as %d ",i,(int) *variant&TWKB_NO_TYPE ); - lwgeom_to_twkb_buf(col->geoms[i], buf, variant,factor,id,refpoint); + /* Write in the sub-geometries */ + for ( i = 0; i < col->ngeoms; i++ ) + { + lwgeom_write_to_buffer(col->geoms[i], globals, ts); } - *variant=*variant & ~TWKB_NO_ID; /*Switch back*/ return 0; } /****************************************************************** -Handle whole TWKB +* Handle whole TWKB *******************************************************************/ -/** -Calculates the needed space for a geometry as twkb -*/ -static size_t lwgeom_to_twkb_size(const LWGEOM *geom,uint8_t *variant, int64_t factor, int64_t id,int64_t refpoint[3][4]) +static int lwgeom_to_twkb_buf(const LWGEOM *geom, TWKB_GLOBALS *globals, TWKB_STATE *ts) { - LWDEBUG(2, "lwgeom_to_twkb_size entered"); - size_t size = 0; - if ( geom == NULL ) - return 0; - - /* Short circuit out empty geometries */ - if ( lwgeom_is_empty(geom) ) - { - return empty_to_twkb_size(geom, variant,id); - } - /*add size of type-declaration*/ - if (!(*variant & TWKB_NO_TYPE)) - size += WKB_BYTE_SIZE; - switch ( geom->type ) - { - case POINTTYPE: - size += lwpoint_to_twkb_size((LWPOINT*)geom, variant, factor,id,refpoint); - break; + LWDEBUGF(2, "Entered %s", __func__); - /* LineString and CircularString both have points elements */ - case CIRCSTRINGTYPE: - case LINETYPE: - size += lwline_to_twkb_size((LWLINE*)geom, variant, factor,id,refpoint); - break; - - /* Polygon has nrings and rings elements */ - case POLYGONTYPE: - size += lwpoly_to_twkb_size((LWPOLY*)geom, variant, factor,id,refpoint); - break; - - /* Triangle has one ring of three points - case TRIANGLETYPE: - size += lwtriangle_to_twkb_size((LWTRIANGLE*)geom, variant); - break;*/ - - /* All these Collection types have ngeoms and geoms elements */ - case MULTIPOINTTYPE: - case MULTILINETYPE: - case MULTIPOLYGONTYPE: - *variant =*variant | TWKB_NO_TYPE; - size += lwcollection_to_twkb_size((LWCOLLECTION*)geom,variant, factor,id,refpoint); - /* We need to get back the possibility to write types */ - *variant =*variant & ~TWKB_NO_TYPE; - break; - case COLLECTIONTYPE: - size += lwcollection_to_twkb_size((LWCOLLECTION*)geom, variant, factor,id,refpoint); - break; - - /* Unknown type! */ - default: - lwerror("Unsupported geometry type: %s [%d]", lwtype_name(geom->type), geom->type); - } - - return size; -} - - -static int lwgeom_to_twkb_buf(const LWGEOM *geom, uint8_t **buf, uint8_t *variant,int64_t factor, int64_t id,int64_t refpoint[3][4]) -{ - LWDEBUG(2, "lwgeom_to_twkb_buf entered"); - int res; - if ( lwgeom_is_empty(geom) ) - return empty_to_twkb_buf(geom, buf, variant,id); - - if (!(*variant & TWKB_NO_TYPE)) - { - uint8_to_twkb_buf(lwgeom_twkb_type(geom),buf); - - if(*variant&TWKB_BBOXES) - { - write_bbox(buf,FLAGS_NDIMS(geom->flags),refpoint[1],refpoint[2]); - /* So we only write bboxes to highest level */ - *variant = *variant & ~TWKB_BBOXES; - } - } switch ( geom->type ) { case POINTTYPE: { - LWDEBUGF(4,"Type found is Point, %d",geom->type); - return lwpoint_to_twkb_buf((LWPOINT*)geom, buf, variant,factor,id,refpoint[0]); + LWDEBUGF(4,"Type found is Point, %d", geom->type); + return lwpoint_to_twkb_buf((LWPOINT*) geom, globals, ts); } - /* LineString and CircularString both have 'points' elements */ - case CIRCSTRINGTYPE: case LINETYPE: { - LWDEBUGF(4,"Type found is Linestring, %d",geom->type); - return lwline_to_twkb_buf((LWLINE*)geom, buf, variant,factor,id,refpoint[0]); + LWDEBUGF(4,"Type found is Linestring, %d", geom->type); + return lwline_to_twkb_buf((LWLINE*) geom, globals, ts); } /* Polygon has 'nrings' and 'rings' elements */ case POLYGONTYPE: { - LWDEBUGF(4,"Type found is Polygon, %d",geom->type); - return lwpoly_to_twkb_buf((LWPOLY*)geom, buf, variant,factor,id,refpoint[0]); + LWDEBUGF(4,"Type found is Polygon, %d", geom->type); + return lwpoly_to_twkb_buf((LWPOLY*)geom, globals, ts); } - /* Triangle has one ring of three points - case TRIANGLETYPE: - return lwtriangle_to_twkb_buf((LWTRIANGLE*)geom, buf, variant);*/ + /* All these Collection types have 'ngeoms' and 'geoms' elements */ case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: { - LWDEBUGF(4,"Type found is Multi, %d",geom->type); - /*the NO_TYPE flag tells that the type not shall be repeated for subgeometries*/ - *variant=*variant | TWKB_NO_TYPE; - res= lwcollection_to_twkb_buf((LWCOLLECTION*)geom, buf, variant,factor,id,refpoint); - /* We need to get back the possibility to write types */ - *variant =*variant & ~TWKB_NO_TYPE; - return res; - } + LWDEBUGF(4,"Type found is Multi, %d", geom->type); + return lwmulti_to_twkb_buf((LWCOLLECTION*)geom, globals, ts); + } case COLLECTIONTYPE: { - LWDEBUGF(4,"Type found is collection, %d",geom->type); - return lwcollection_to_twkb_buf((LWCOLLECTION*)geom, buf, variant,factor,id,refpoint); + LWDEBUGF(4,"Type found is collection, %d", geom->type); + return lwcollection_to_twkb_buf((LWCOLLECTION*) geom, globals, ts); } /* Unknown type! */ default: - lwerror("Unsupported geometry type: %s [%d]", lwtype_name(geom->type), geom->type); + lwerror("Unsupported geometry type: %s [%d]", lwtype_name((geom)->type), (geom)->type); } - /* Return value to keep compiler happy. */ + return 0; } -/** -* Convert LWGEOM to a char* in TWKB format. Caller is responsible for freeing -* the returned array. -*/ -uint8_t* lwgeom_to_twkb(const LWGEOM *geom, uint8_t variant, size_t *size_out,int8_t prec, int64_t id) -{ - LWDEBUGF(2, "lwgeom_to_twkb entered with variant value %x",variant); - size_t buf_size=0,size_size=0; - uint8_t *buf = NULL; - uint8_t *wkb_out = NULL; - uint8_t flag=0; - int64_t factor=pow(10,prec); - - /*an integer array - first dimmension holding the reference point, the last used point - second and third is for max and min values to calculate bounding box.*/ - int64_t refpoint[3][MAX_N_DIMS]={{0,0,0,0},{INT64_MAX,INT64_MAX,INT64_MAX,INT64_MAX},{INT64_MIN,INT64_MIN,INT64_MIN,INT64_MIN}}; - - - /* Initialize output size */ - if ( size_out ) *size_out = 0; - if ( geom == NULL ) - { - LWDEBUG(4,"Cannot convert NULL into WKB."); - lwerror("Cannot convert NULL into WKB."); - return NULL; +static 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; + uint8_t flag = 0, type_prec = 0; + + TWKB_STATE child_state; + memset(&child_state, 0, sizeof(TWKB_STATE)); + child_state.header_buf = bytebuffer_create_with_size(16); + child_state.geom_buf = bytebuffer_create_with_size(64); + child_state.idlist = parent_state->idlist; + + /* Read dimensionality from input */ + has_z = lwgeom_has_z(geom); + has_m = lwgeom_has_m(geom); + ndims = lwgeom_ndims(geom); + is_empty = lwgeom_is_empty(geom); + + /* Do we need extended precision? If we have a Z or M we do. */ + optional_precision_byte = (has_z || has_m); + + /* Both X and Y dimension use the same precision */ + globals->factor[0] = pow(10, globals->prec_xy); + globals->factor[1] = globals->factor[0]; + + /* Z and M dimensions have their own precisions */ + if ( has_z ) + globals->factor[2] = pow(10, globals->prec_z); + if ( has_m ) + globals->factor[2 + has_z] = pow(10, globals->prec_m); + + /* Reset stats */ + for ( i = 0; i < MAX_N_DIMS; i++ ) + { + /* Reset bbox calculation */ + child_state.bbox_max[i] = INT64_MIN; + child_state.bbox_min[i] = INT64_MAX; + /* Reset acumulated delta values to get absolute values on next point */ + child_state.accum_rels[i] = 0; + } + + /* TYPE/PRECISION BYTE */ + if ( abs(globals->prec_xy) > 7 ) + lwerror("%s: X/Z precision cannot be greater than 7 or less than -7", __func__); + + /* Read the TWKB type number from the geometry */ + TYPE_PREC_SET_TYPE(type_prec, lwgeom_twkb_type(geom)); + /* Zig-zag the precision value before encoding it since it is a signed value */ + TYPE_PREC_SET_PREC(type_prec, zigzag8(globals->prec_xy)); + /* Write the type and precision byte */ + bytebuffer_append_byte(child_state.header_buf, type_prec); + + /* METADATA BYTE */ + /* Set first bit if we are going to store bboxes */ + FIRST_BYTE_SET_BBOXES(flag, (globals->variant & TWKB_BBOX) && ! is_empty); + /* Set second bit if we are going to store resulting size */ + FIRST_BYTE_SET_SIZES(flag, globals->variant & TWKB_SIZE); + /* There will be no ID-list (for now) */ + FIRST_BYTE_SET_IDLIST(flag, parent_state->idlist && ! is_empty); + /* Are there higher dimensions */ + FIRST_BYTE_SET_EXTENDED(flag, optional_precision_byte); + /* Empty? */ + FIRST_BYTE_SET_EMPTY(flag, is_empty); + /* Write the header byte */ + bytebuffer_append_byte(child_state.header_buf, flag); + + /* EXTENDED PRECISION BYTE (OPTIONAL) */ + /* If needed, write the extended dim byte */ + if( optional_precision_byte ) + { + uint8_t flag = 0; + + if ( has_z && ( globals->prec_z > 7 || globals->prec_z < 0 ) ) + lwerror("%s: Z precision cannot be negative or greater than 7", __func__); + + if ( has_m && ( globals->prec_m > 7 || globals->prec_m < 0 ) ) + lwerror("%s: M precision cannot be negative or greater than 7", __func__); + + HIGHER_DIM_SET_HASZ(flag, has_z); + HIGHER_DIM_SET_HASM(flag, has_m); + HIGHER_DIM_SET_PRECZ(flag, globals->prec_z); + HIGHER_DIM_SET_PRECM(flag, globals->prec_m); + bytebuffer_append_byte(child_state.header_buf, flag); + } + + /* It the geometry is empty, we're almost done */ + if ( is_empty ) + { + /* If this output is sized, write the size of */ + /* all following content, which is zero because */ + /* there is none */ + if ( globals->variant & TWKB_SIZE ) + bytebuffer_append_byte(child_state.header_buf, 0); + + bytebuffer_append_bytebuffer(parent_state->geom_buf, child_state.header_buf); + bytebuffer_destroy(child_state.header_buf); + bytebuffer_destroy(child_state.geom_buf); + return 0; } - /* Calculate the required size of the output buffer */ - - /*Adding the size for the first byte*/ - buf_size += 1; - buf_size += lwgeom_to_twkb_size(geom,&variant,factor,id,refpoint); - LWDEBUGF(4, "WKB output size: %d", buf_size); + /* Write the TWKB into the output buffer */ + lwgeom_to_twkb_buf(geom, globals, &child_state); - - /* add the size of the bbox - * If empty geometry request for bbox is ignored */ - if(variant&TWKB_BBOXES) - { + /* Did we have a box? If so, how big? */ + bbox_size = 0; + if( globals->variant & TWKB_BBOX ) + { LWDEBUG(4,"We want boxes and will calculate required size"); - buf_size += sizeof_bbox(FLAGS_NDIMS(geom->flags),refpoint[1],refpoint[2]); + bbox_size = sizeof_bbox(&child_state, ndims); } - - /* reset refpoints */ - refpoint[0][0]=refpoint[0][1]=refpoint[0][2]=refpoint[0][3]=0; - - - if ( buf_size == 0 ) + /* Write the size if wanted */ + if( globals->variant & TWKB_SIZE ) { - LWDEBUG(4,"Error calculating output TWKB buffer size."); - lwerror("Error calculating output TWKB buffer size."); - return NULL; + /* Here we have to add what we know will be written to header */ + /* buffer after size value is written */ + size_t size_to_register = bytebuffer_getlength(child_state.geom_buf); + size_to_register += bbox_size; + bytebuffer_append_uvarint(child_state.header_buf, size_to_register); } - /*If we want geometry sizes, we need space for that (the size we are going to store is the twkb_size - first byte and size value itself)*/ - if(variant & TWKB_SIZES) - size_size=varint_u64_encoded_size((uint64_t) (buf_size-1)); - - /* Allocate the buffer */ - buf = lwalloc(buf_size+size_size); - if ( buf == NULL ) - { - LWDEBUGF(4,"Unable to allocate %d bytes for TWKB output buffer.", buf_size); - lwerror("Unable to allocate %d bytes for TWKB output buffer.", buf_size); - return NULL; - } - - /* Retain a pointer to the front of the buffer for later */ - wkb_out = buf; - - - - /* set ID bit if ID*/ - FIRST_BYTE_SET_ID(flag, ((variant & TWKB_ID) ? 0xFF : 0)); - /* set second bit if we are going to store resulting size*/ - FIRST_BYTE_SET_SIZES(flag, ((variant & TWKB_SIZES) ? 0xFF : 0)); - /* set third bit if we are going to store bboxes*/ - FIRST_BYTE_SET_BBOXES(flag, ((variant & TWKB_BBOXES) ? 0xFF : 0)); - - /* Tell what precision to use*/ - FIRST_BYTE_SET_PRECISION(flag,prec); - - /*Copy the flag to the buffer*/ - uint8_to_twkb_buf(flag,&buf); - - /*Write the size of the geometry*/ - if(variant & TWKB_SIZES) - varint_u64_encode_buf((uint64_t) (buf_size-1), &buf); - - /* Write the TWKB into the output buffer */ - lwgeom_to_twkb_buf(geom, &buf,&variant, factor,id,refpoint); - LWDEBUGF(4,"buf (%p) - wkb_out (%p) = %d", buf, wkb_out, buf - wkb_out); + if( globals->variant & TWKB_BBOX ) + write_bbox(&child_state, ndims); - /* The buffer pointer should now land at the end of the allocated buffer space. Let's check. */ - if ( (buf_size+size_size) != (buf - wkb_out) ) - { - LWDEBUG(4,"Output TWKB is not the same size as the allocated buffer."); - lwerror("Output TWKB is not the same size as the allocated buffer (precalculated size:%d, allocated size:%d)", buf_size, (buf - wkb_out)); - lwfree(wkb_out); - return NULL; - } - - /* Report output size */ - if ( size_out ) *size_out = (buf_size+size_size); + bytebuffer_append_bytebuffer(parent_state->geom_buf,child_state.header_buf); + bytebuffer_append_bytebuffer(parent_state->geom_buf,child_state.geom_buf); - return wkb_out; + bytebuffer_destroy(child_state.header_buf); + bytebuffer_destroy(child_state.geom_buf); + return 0; } -uint8_t* lwgeom_agg_to_twkb(const twkb_geom_arrays *lwgeom_arrays,uint8_t variant , size_t *size_out,int8_t prec) + +/** +* Convert LWGEOM to a char* in TWKB format. Caller is responsible for freeing +* the returned array. +*/ +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) { - size_t buf_size=0,size_size=0; - uint8_t *buf = NULL; - uint8_t flag = 0; - uint8_t *wkb_out = NULL; - int nDims=0; - int chk_homogenity=0; - uint8_t type_flag = 0; - int dims; - int64_t factor=pow(10,prec); - /*an integer array holding the reference point. In most cases the last used point - but in the case of pointcloud it is a user defined refpoint. - INT32_MIN indicates that the ref-point is not set yet*/ - int64_t refpoint[3][MAX_N_DIMS]={{0,0,0,0},{INT64_MAX,INT64_MAX,INT64_MAX,INT64_MAX},{INT64_MIN,INT64_MIN,INT64_MIN,INT64_MIN}}; - - LWDEBUGF(4, "We have collected: %d points, %d linestrings, %d polygons and %d collections",lwgeom_arrays->n_points,lwgeom_arrays->n_linestrings,lwgeom_arrays->n_polygons,lwgeom_arrays->n_collections ); + LWDEBUGF(2, "Entered %s", __func__); + LWDEBUGF(2, "variant value %x", variant); - - /* Initialize output size */ - if ( size_out ) *size_out = 0; - + TWKB_GLOBALS tg; + TWKB_STATE ts; - if (lwgeom_arrays->n_points > 0) - chk_homogenity++; - if (lwgeom_arrays->n_linestrings > 0) - chk_homogenity++; - if (lwgeom_arrays->n_polygons > 0) - chk_homogenity++ ; - if (lwgeom_arrays->n_collections > 0) - chk_homogenity++; - - - - if(chk_homogenity==0) - return NULL; - if(chk_homogenity>1) - buf_size = 2+varint_u64_encoded_size((uint64_t) chk_homogenity); - else - buf_size=1; - - - if (lwgeom_arrays->n_points > 0) - buf_size += lwgeom_agg_to_twkbpoint_size(lwgeom_arrays->points,&variant,lwgeom_arrays->n_points, factor,refpoint); - if (lwgeom_arrays->n_linestrings > 0) - buf_size += lwgeom_agg_to_twkbline_size(lwgeom_arrays->linestrings,&variant,lwgeom_arrays->n_linestrings, factor,refpoint); - if (lwgeom_arrays->n_polygons > 0) - buf_size += lwgeom_agg_to_twkbpoly_size(lwgeom_arrays->polygons,&variant,lwgeom_arrays->n_polygons, factor,refpoint); - if (lwgeom_arrays->n_collections > 0) - buf_size += lwgeom_agg_to_twkbcollection_size(lwgeom_arrays->collections,&variant,lwgeom_arrays->n_collections, factor,refpoint); - - /* add the size of the bbox - * If empty geometry request for bbox is ignored */ - - if(variant&TWKB_BBOXES) - { - LWDEBUG(4,"We want boxes and will calculate required size"); - /* Check how many dimmensions that have been used for the box */ - while (nDimsINT64_MIN) - nDims++; - buf_size += sizeof_bbox(nDims,refpoint[1],refpoint[2]); - } + uint8_t *twkb; + + memset(&ts, 0, sizeof(TWKB_STATE)); + memset(&tg, 0, sizeof(TWKB_GLOBALS)); - /* reset refpoints */ - refpoint[0][0]=refpoint[0][1]=refpoint[0][2]=refpoint[0][3]=0; - - LWDEBUGF(4, "WKB output size: %d", buf_size); + tg.variant = variant; + tg.prec_xy = precision_xy; + tg.prec_z = precision_z; + tg.prec_m = precision_m; - if ( buf_size == 0 ) + if ( idlist && ! lwgeom_is_collection(geom) ) { - LWDEBUG(4,"Error calculating output TWKB buffer size."); - lwerror("Error calculating output TWKB buffer size."); + lwerror("Only collections can support ID lists"); return NULL; } - - /*If we want geometry sizes, we need space for that (the size we are going to store is the twkb_size - first byte and size value itself)*/ - if(variant & TWKB_SIZES) - size_size=varint_u64_encoded_size((uint64_t) (buf_size-1)); - - /* Allocate the buffer */ - buf = lwalloc(buf_size+size_size); - if ( buf == NULL ) + if ( ! geom ) { - LWDEBUGF(4,"Unable to allocate %d bytes for WKB output buffer.", buf_size); - lwerror("Unable to allocate %d bytes for WKB output buffer.", buf_size); + LWDEBUG(4,"Cannot convert NULL into TWKB."); + lwerror("Cannot convert NULL into TWKB"); return NULL; } - - /* Retain a pointer to the front of the buffer for later */ - wkb_out = buf; - - - /* Set the id flag */ - FIRST_BYTE_SET_ID(flag, ((variant & TWKB_ID) ? 1 : 0)); - /* set second bit if we are going to store resulting size*/ - FIRST_BYTE_SET_SIZES(flag, ((variant & TWKB_SIZES) ? 0xFF : 0)); - /* set third bit if we are going to store bboxes*/ - FIRST_BYTE_SET_BBOXES(flag, ((variant & TWKB_BBOXES) ? 0xFF : 0)); - /* Tell what precision to use*/ - FIRST_BYTE_SET_PRECISION(flag,prec); - /*Copy the flag to the buffer*/ - uint8_to_twkb_buf(flag,&buf); + ts.idlist = idlist; + ts.geom_buf = bytebuffer_create(); + lwgeom_write_to_buffer(geom, &tg, &ts); - /*Write the size of the geometry*/ - if(variant & TWKB_SIZES) - varint_u64_encode_buf((uint64_t) (buf_size-1), &buf); - - /*set type and number of geometries for the top level, if more than 1 type og underlying geometries*/ - if(chk_homogenity>1) - { + if ( twkb_size ) + *twkb_size = bytebuffer_getlength(ts.geom_buf); - /*Set the type*/ - TYPE_DIM_SET_TYPE(type_flag,7); - LWDEBUGF(4, "Writing type '%d'",7); - - /*We just set this to 4 dimmensions. It doesn't matter since all undelying geometries have their own*/ - dims = 4; - TYPE_DIM_SET_DIM(type_flag,dims); - LWDEBUGF(4, "Writing ndims '%d'", dims); - uint8_to_twkb_buf(type_flag,&buf); - - /* Set number of geometries */ - varint_u64_encode_buf(chk_homogenity, &buf); - } - - /* Write the WKB into the output buffer - buf = lwgeom_to_twkb_buf(geom, buf,variant, prec,id,refpoint2);*/ - - if (lwgeom_arrays->n_points > 0) - lwgeom_agg_to_twkbpoint_buf(lwgeom_arrays->points,lwgeom_arrays->n_points, &buf,&variant, factor,refpoint); - if (lwgeom_arrays->n_linestrings > 0) - lwgeom_agg_to_twkbline_buf(lwgeom_arrays->linestrings,lwgeom_arrays->n_linestrings, &buf,&variant, factor,refpoint); - if (lwgeom_arrays->n_polygons > 0) - lwgeom_agg_to_twkbpoly_buf(lwgeom_arrays->polygons,lwgeom_arrays->n_polygons, &buf,&variant, factor,refpoint); - if (lwgeom_arrays->n_collections > 0) - lwgeom_agg_to_twkbcollection_buf(lwgeom_arrays->collections,lwgeom_arrays->n_collections, &buf,&variant, factor,refpoint); - - - + twkb = ts.geom_buf->buf_start; + lwfree(ts.geom_buf); + return twkb; +} - LWDEBUGF(4,"buf (%p) - wkb_out (%p) = %d", buf, wkb_out, buf - wkb_out); - /* The buffer pointer should now land at the end of the allocated buffer space. Let's check. */ - if ( (buf_size+size_size) != (buf - wkb_out) ) - { - LWDEBUG(4,"Output TWKB is not the same size as the allocated buffer."); - lwerror("Output TWKB is not the same size as the allocated buffer (precalculated size:%d, allocated size:%d)", buf_size, (buf - wkb_out)); - lwfree(wkb_out); - return NULL; - } +uint8_t* +lwgeom_to_twkb(const LWGEOM *geom, uint8_t variant, + int8_t precision_xy, int8_t precision_z, int8_t precision_m, + size_t *twkb_size) +{ + return lwgeom_to_twkb_with_idlist(geom, NULL, variant, precision_xy, precision_z, precision_m, twkb_size); +} - /* Report output size */ - if ( size_out ) *size_out = (buf_size+size_size); - return wkb_out; - -} diff --git a/liblwgeom/lwout_twkb.h b/liblwgeom/lwout_twkb.h index 4dccf282a..07df5ad18 100644 --- a/liblwgeom/lwout_twkb.h +++ b/liblwgeom/lwout_twkb.h @@ -14,57 +14,69 @@ #include "liblwgeom_internal.h" #include "lwgeom_log.h" #include +#include "bytebuffer.h" /* Maximum number of geometry dimmensions that internal arrays can hold */ #define MAX_N_DIMS 4 -/** -* If the twkb includes an ID 1 bit -* If the twkb includes its size 1 bit -* If the twkb includes bboxes 1 bit -* Precision 4 bits -*/ - - -#define FIRST_BYTE_SET_ID(flag, id) ((flag) = (flag & 0xFE) | ((id & 0x01))) -#define FIRST_BYTE_SET_SIZES(flag, sizes) ((flag) = (flag & 0xFD) | ((sizes & 0x02))) -#define FIRST_BYTE_SET_BBOXES(flag, bboxes) ((flag) = (flag & 0xFB) | ((bboxes & 0x04))) - -#define FIRST_BYTE_SET_PRECISION(flag, prec) ((flag) = (flag & 0x0F) | ((prec<<4) & 0xF0)) +#define MAX_BBOX_SIZE 64 +#define MAX_SIZE_SIZE 8 /** -* Macros for manipulating the 'type_dim' int. An int8_t used as follows: -* Type 5 bits -* NDims 3 bits +* Header true/false flags */ -#define TYPE_DIM_SET_TYPE(flag, type) ((flag) = (flag & 0xE0) | ((type & 0x1F))) -#define TYPE_DIM_SET_DIM(flag, dim) ((flag) = (flag & 0x1F) | ((dim & 0x07)<<5)) +#define FIRST_BYTE_SET_BBOXES(flag, bool) ((flag) = ((bool) ? (flag) | 0x01 : (flag) & (~0x01))) +#define FIRST_BYTE_SET_SIZES(flag, bool) ((flag) = ((bool) ? (flag) | 0x02 : (flag) & (~0x02))) +#define FIRST_BYTE_SET_IDLIST(flag, bool) ((flag) = ((bool) ? (flag) | 0x04 : (flag) & (~0x04))) +#define FIRST_BYTE_SET_EXTENDED(flag, bool) ((flag) = ((bool) ? (flag) | 0x08 : (flag) & (~0x08))) +#define FIRST_BYTE_SET_EMPTY(flag, bool) ((flag) = ((bool) ? (flag) | 0x10 : (flag) & (~0x10))) -static size_t ptarray_to_twkb_size(const POINTARRAY *pa, uint8_t variant,int prec,int64_t accum_rel[3][4]); -static int ptarray_to_twkb_buf(const POINTARRAY *pa, uint8_t **buf, uint8_t variant,int64_t factor,int64_t accum_rel[]); -static size_t lwgeom_agg_to_twkbpoint_size(lwgeom_id *geom_array, uint8_t *variant,int n,int64_t factor,int64_t refpoint[3][4]); -static size_t lwpoint_to_twkb_size(const LWPOINT *pt, uint8_t *variant, int64_t factor, int64_t id,int64_t refpoint[3][4]); -static int lwgeom_agg_to_twkbpoint_buf(lwgeom_id* geom_array,int n, uint8_t **buf, uint8_t *variant,int64_t factor, int64_t refpoint[3][4]); -static int lwpoint_to_twkb_buf(const LWPOINT *pt, uint8_t **buf, uint8_t *variant,int64_t factor, int64_t id,int64_t refpoint[]); - -static size_t lwgeom_agg_to_twkbline_size(lwgeom_id *geom_array, uint8_t *variant,int n,int64_t factor,int64_t refpoint[3][4]); -static size_t lwline_to_twkb_size(const LWLINE *line, uint8_t *variant, int64_t factor, int64_t id,int64_t refpoint[3][4]); -static int lwgeom_agg_to_twkbline_buf(lwgeom_id* geom_array,int n, uint8_t **buf, uint8_t *variant,int64_t factor, int64_t refpoint[3][4]); -static int lwline_to_twkb_buf(const LWLINE *line, uint8_t **buf, uint8_t *variant,int64_t factor, int64_t id,int64_t refpoint[]); - -static size_t lwgeom_agg_to_twkbpoly_size(lwgeom_id *geom_array, uint8_t *variant,int n,int64_t factor,int64_t refpoint[3][4]); -static size_t lwpoly_to_twkb_size(const LWPOLY *poly, uint8_t *variant,int64_t factor, int64_t id,int64_t refpoint[3][4]); -static int lwgeom_agg_to_twkbpoly_buf(lwgeom_id* geom_array,int n, uint8_t **buf, uint8_t *variant,int64_t factor, int64_t refpoint[3][4]); -static int lwpoly_to_twkb_buf(const LWPOLY *poly, uint8_t **buf, uint8_t *variant,int64_t factor, int64_t id,int64_t refpoint[]); - -static size_t lwgeom_agg_to_twkbcollection_size(lwgeom_id *geom_array, uint8_t *variant,int n,int64_t factor,int64_t refpoint[3][4]); -static size_t lwcollection_to_twkb_size(const LWCOLLECTION *col, uint8_t *variant, int64_t factor, int64_t id,int64_t refpoint[3][4]); -static int lwgeom_agg_to_twkbcollection_buf(lwgeom_id* geom_array,int n, uint8_t **buf, uint8_t *variant,int64_t factor, int64_t refpoint[3][4]); -static int lwcollection_to_twkb_buf(const LWCOLLECTION *col, uint8_t **buf, uint8_t *variant,int64_t factor, int64_t id,int64_t refpoint[3][4]); +/** +* Macros for manipulating the 'type_precision' int. An int8_t used as follows: +* Type 4 bits +* Precision 4 bits +*/ -static size_t lwgeom_to_twkb_size(const LWGEOM *geom, uint8_t *variant, int64_t factor, int64_t id,int64_t refpoint[3][4]); -static int lwgeom_to_twkb_buf(const LWGEOM *geom, uint8_t **buf, uint8_t *variant,int64_t factor, int64_t id,int64_t refpoint[3][4]); +#define TYPE_PREC_SET_TYPE(flag, type) ((flag) = ((flag) & 0xF0) | (((type) & 0x0F))) +#define TYPE_PREC_SET_PREC(flag, prec) ((flag) = ((flag) & 0x0F) | (((prec) & 0x0F) << 4)) + +#define HIGHER_DIM_SET_HASZ(flag, bool) ((flag) = ((bool) ? (flag) | 0x01 : (flag) & (~0x01))) +#define HIGHER_DIM_SET_HASM(flag, bool) ((flag) = ((bool) ? (flag) | 0x02 : (flag) & (~0x02))) + +#define HIGHER_DIM_SET_PRECZ(flag, prec) ((flag) = ((flag) & 0xE3) | (((prec) & 0x07) << 2)) +#define HIGHER_DIM_SET_PRECM(flag, prec) ((flag) = ((flag) & 0x1F) | (((prec) & 0x07) << 5)) + +typedef struct +{ + /* Options defined at start */ + uint8_t variant; + int8_t prec_xy; + int8_t prec_z; + int8_t prec_m; + float factor[4]; /*What factor to multiply the coordiinates with to get the requested precision*/ +} TWKB_GLOBALS; + +typedef struct +{ + uint8_t variant; /*options that change at runtime*/ + bytebuffer_t *header_buf; + bytebuffer_t *geom_buf; + int hasz; + int hasm; + const int64_t *idlist; + int64_t bbox_min[MAX_N_DIMS]; + int64_t bbox_max[MAX_N_DIMS]; + 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); + +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); diff --git a/liblwgeom/ptarray.c b/liblwgeom/ptarray.c index 5da8e6acd..4d3803b28 100644 --- a/liblwgeom/ptarray.c +++ b/liblwgeom/ptarray.c @@ -56,12 +56,11 @@ ptarray_construct(char hasz, char hasm, uint32_t npoints) POINTARRAY* ptarray_construct_empty(char hasz, char hasm, uint32_t maxpoints) { - uint8_t dims = gflags(hasz, hasm, 0); POINTARRAY *pa = lwalloc(sizeof(POINTARRAY)); pa->serialized_pointlist = NULL; /* Set our dimsionality info on the bitmap */ - pa->flags = dims; + pa->flags = gflags(hasz, hasm, 0); /* We will be allocating a bit of room */ pa->npoints = 0; diff --git a/liblwgeom/varint.c b/liblwgeom/varint.c index 30e9e34bd..e2f992a2a 100644 --- a/liblwgeom/varint.c +++ b/liblwgeom/varint.c @@ -20,193 +20,180 @@ #include "lwgeom_log.h" #include "liblwgeom.h" -/** - * Constants to find the range of Varint. - * Since Varint only uses 7 bits instead of 8 in each byte a 8 byte varint - * only uses 56 bits for the number and 8 for telling if next byte is used - */ -const int64_t varint_u64_max = ((int64_t) 1<<56) - 1; -const int64_t varint_s64_min = (int64_t) -1<<55; -const int64_t varint_s64_max = ((int64_t)1<<55) - 1; -const int32_t varint_u32_max = ((int32_t) 1<<28) - 1; -const int32_t varint_s32_min = (int32_t) -1<<27; -const int32_t varint_s32_max = ((int32_t)1<<27) - 1; - -static unsigned -_varint_u64_encoded_size(uint64_t q) -{ - int n=0; - while ((q>>(7*(n+1))) >0) ++n; - return ++n; -} +/* -------------------------------------------------------------------------------- */ -static int -_varint_u64_encode_buf(uint64_t val, uint8_t **buf) +static size_t +_varint_u64_encode_buf(uint64_t val, uint8_t *buf) { uint8_t grp; - uint64_t q=val; + uint64_t q = val; + uint8_t *ptr = buf; while (1) { - grp=127&q; /* We put the 7 least significant bits in grp */ - q=q>>7; /* We rightshift our input value 7 bits which means that the 7 next least significant bits becomes the 7 least significant */ - if(q>0) /* Check if, after our rightshifting, we still have anything to read in our input value. */ + /* We put the 7 least significant bits in grp */ + grp = 0x7f & q; + /* We rightshift our input value 7 bits */ + /* which means that the 7 next least significant bits */ + /* becomes the 7 least significant */ + q = q >> 7; + /* Check if, after our rightshifting, we still have */ + /* anything to read in our input value. */ + if ( q > 0 ) { - /*In the next line quite a lot is happening. - Since there is more to read in our input value we signalize that by setting the most siginicant bit in our byte to 1. - Then we put that byte in our buffer (**buf) and move the cursor to our buffer (*buf) one step*/ - *((*buf)++)=128^grp; + /* In the next line quite a lot is happening. */ + /* Since there is more to read in our input value */ + /* we signal that by setting the most siginicant bit */ + /* in our byte to 1. */ + /* Then we put that byte in our buffer and move the pointer */ + /* forward one step */ + *ptr = 0x80 | grp; + ptr++; } else { - /*The same as above, but since there is nothing more to read in our input value we leave the most significant bit unset*/ - *((*buf)++)=grp; - /* printf("grp1:%d\n",(int) grp); */ - return 0; + /* The same as above, but since there is nothing more */ + /* to read in our input value we leave the most significant bit unset */ + *ptr = grp; + ptr++; + return ptr - buf; } } - return 0; + /* This cannot happen */ + lwerror("%s: Got out of infinite loop. Consciousness achieved.", __func__); + return (size_t)0; } -unsigned -varint_u32_encoded_size(uint32_t val) + +size_t +varint_u64_encode_buf(uint64_t val, uint8_t *buf) { - LWDEBUGF(2, "Entered varint_u32_encoded_size, value %u", val); + return _varint_u64_encode_buf(val, buf); +} - if( val>varint_u32_max ) { - lwerror("Value is out of range for unsigned 32bit varint (0 to %ld)", - varint_u32_max); - } - return _varint_u64_encoded_size(val); /* implicit upcast to 64bit int */ +size_t +varint_u32_encode_buf(uint32_t val, uint8_t *buf) +{ + return _varint_u64_encode_buf((uint64_t)val, buf); } -int -varint_u32_encode_buf(uint32_t val, uint8_t **buf) +size_t +varint_s64_encode_buf(int64_t val, uint8_t *buf) { - LWDEBUGF(2, "Entered varint_u32_encode_buf, value %u", val); - _varint_u64_encode_buf(val, buf); /* implicit upcast to 64bit */ -return 0; + return _varint_u64_encode_buf(zigzag64(val), buf); } -unsigned -varint_s32_encoded_size(int32_t val) +size_t +varint_s32_encode_buf(int32_t val, uint8_t *buf) { - LWDEBUGF(2, "Entered varint_s32_encoded_size, value %d", val); - - if(valvarint_s32_max) { - lwerror("Value is out of range for signed 32bit varint (%d to %d)", - varint_s32_min, varint_s32_max); - } + return _varint_u64_encode_buf((uint64_t)zigzag32(val), buf); +} - uint32_t q = (val << 1) ^ (val >> 31); /* zig-zag encode */ - return _varint_u64_encoded_size(q); /* implicit upcast to 64bit int */ +/* Read from signed 64bit varint */ +int64_t +varint_s64_decode(const uint8_t *the_start, const uint8_t *the_end, size_t *size) +{ + return unzigzag64(varint_u64_decode(the_start, the_end, size)); } -int -varint_s32_encode_buf(int32_t val, uint8_t **buf) +/* Read from unsigned 64bit varint */ +uint64_t +varint_u64_decode(const uint8_t *the_start, const uint8_t *the_end, size_t *size) { - - uint32_t q; - q = (val << 1) ^ (val >> 31);/* zig-zag encode */ - _varint_u64_encode_buf(q, buf);/* implicit upcast to 64bit */ + uint64_t nVal = 0; + int nShift = 0; + uint8_t nByte; + const uint8_t *ptr = the_start; + + /* Check so we don't read beyond the twkb */ + while( ptr < the_end ) + { + nByte = *ptr; + /* Hibit is set, so this isn't the last byte */ + if (nByte & 0x80) + { + /* We get here when there is more to read in the input varInt */ + /* Here we take the least significant 7 bits of the read */ + /* byte and put it in the most significant place in the result variable. */ + nVal |= ((uint64_t)(nByte & 0x7f)) << nShift; + /* move the "cursor" of the input buffer step (8 bits) */ + ptr++; + /* move the cursor in the resulting variable (7 bits) */ + nShift += 7; + } + else + { + /* move the "cursor" one step */ + ptr++; + /* Move the last read byte to the most significant */ + /* place in the result and return the whole result */ + *size = ptr - the_start; + return nVal | ((uint64_t)nByte << nShift); + } + } + lwerror("%s: varint extends past end of buffer", __func__); return 0; } -unsigned -varint_s64_encoded_size(int64_t val) +size_t +varint_size(const uint8_t *the_start, const uint8_t *the_end) { - LWDEBUGF(2, "Entered varint_s64_encoded_size, value %ld", val); - - if(valvarint_s64_max) { - lwerror("Value is out of range for signed 64bit varint (%ld to %ld)", - varint_s64_min, varint_s64_max); - } + const uint8_t *ptr = the_start; - uint64_t q = (val << 1) ^ (val >> 63); /* zig-zag encode */ - return _varint_u64_encoded_size(q); + /* Check so we don't read beyond the twkb */ + while( ptr < the_end ) + { + /* Hibit is set, this isn't the last byte */ + if (*ptr & 0x80) + { + ptr++; + } + else + { + ptr++; + return ptr - the_start; + } + } + return 0; } -int -varint_s64_encode_buf(int64_t val, uint8_t **buf) +uint64_t zigzag64(int64_t val) { - uint64_t q; - q = (val << 1) ^ (val >> 63); - varint_u64_encode_buf(q, buf); - return 0; + return (val << 1) ^ (val >> 63); } -unsigned -varint_u64_encoded_size(uint64_t val) +uint32_t zigzag32(int32_t val) { - LWDEBUGF(2, "Entered varint_u64_encoded_size, value %lu", val); - - if( val>varint_u64_max ) { - lwerror("Value is out of range for unsigned 64bit varint (0 to %ld)", - varint_u64_max); - } - - return _varint_u64_encoded_size(val); + return (val << 1) ^ (val >> 31); } - -int -varint_u64_encode_buf(uint64_t val, uint8_t **buf) + +uint8_t zigzag8(int8_t val) { - LWDEBUGF(2, "Entered varint_u64_encode_buf, value %lu", val); - _varint_u64_encode_buf(val, buf); - return 0; + return (val << 1) ^ (val >> 7); } - -/*Read varint*/ - -/* Read from unsigned 64bit varint */ -uint64_t varint_u64_read(uint8_t **data, uint8_t *the_end) + +int64_t unzigzag64(uint64_t val) { - 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)) /* 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; + if ( val & 0x01 ) + return -1 * (int64_t)((val+1) >> 1); + else + return (int64_t)(val >> 1); } - -/* Read from signed 64bit varint */ -uint64_t varint_s64_read(uint8_t **data, uint8_t *the_end) + +int32_t unzigzag32(uint32_t val) { - 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); + if ( val & 0x01 ) + return -1 * (int32_t)((val+1) >> 1); + else + return (int32_t)(val >> 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) + +int8_t unzigzag8(uint8_t val) { - 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; + if ( val & 0x01 ) + return -1 * (int8_t)((val+1) >> 1); + else + return (int8_t)(val >> 1); } + + diff --git a/liblwgeom/varint.h b/liblwgeom/varint.h index 8fd824726..b01079cee 100644 --- a/liblwgeom/varint.h +++ b/liblwgeom/varint.h @@ -20,44 +20,26 @@ #define _LIBLWGEOM_VARINT_H 1 #include -/*Write varInt to buffer*/ +#include -/* Find encoded size for unsigned 32bit integer */ -unsigned varint_u32_encoded_size(uint32_t val); -/* Encode unsigned 32bit integer */ -int varint_u32_encode_buf(uint32_t val, uint8_t **buf); +/* NEW SIGNATURES */ -/* Find encoded size for signed 32bit integer */ -unsigned varint_s32_encoded_size(int32_t val); +size_t varint_u32_encode_buf(uint32_t val, uint8_t *buf); +size_t varint_s32_encode_buf(int32_t val, uint8_t *buf); +size_t varint_u64_encode_buf(uint64_t val, uint8_t *buf); +size_t varint_s64_encode_buf(int64_t val, uint8_t *buf); +int64_t varint_s64_decode(const uint8_t *the_start, const uint8_t *the_end, size_t *size); +uint64_t varint_u64_decode(const uint8_t *the_start, const uint8_t *the_end, size_t *size); -/* Encode signed 32bit integer */ -int varint_s32_encode_buf(int32_t val, uint8_t **buf); +size_t varint_size(const uint8_t *the_start, const uint8_t *the_end); -/* Find encoded size for unsigned 64bit integer */ -unsigned varint_u64_encoded_size(uint64_t val); - -/* Encode unsigned 64bit integer */ -int varint_u64_encode_buf(uint64_t val, uint8_t **buf); - -/* Find encoded size for signed 64bit integer */ -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); +uint64_t zigzag64(int64_t val); +uint32_t zigzag32(int32_t val); +uint8_t zigzag8(int8_t val); +int64_t unzigzag64(uint64_t val); +int32_t unzigzag32(uint32_t val); +int8_t unzigzag8(uint8_t val); #endif /* !defined _LIBLWGEOM_VARINT_H */ diff --git a/libpgcommon/lwgeom_pg.h b/libpgcommon/lwgeom_pg.h index 46f6ba9da..719a5afb5 100644 --- a/libpgcommon/lwgeom_pg.h +++ b/libpgcommon/lwgeom_pg.h @@ -164,8 +164,6 @@ Datum LWGEOM_force_curve(PG_FUNCTION_ARGS); Datum LWGEOMFromEWKB(PG_FUNCTION_ARGS); Datum LWGEOMFromTWKB(PG_FUNCTION_ARGS); -Datum WKBFromLWGEOM(PG_FUNCTION_ARGS); -Datum TWKBFromLWGEOM(PG_FUNCTION_ARGS); Datum LWGEOM_getBBOX(PG_FUNCTION_ARGS); Datum LWGEOM_addBBOX(PG_FUNCTION_ARGS); diff --git a/libpgcommon/lwgeom_transform.c b/libpgcommon/lwgeom_transform.c index 525c4a242..608fa0aa8 100644 --- a/libpgcommon/lwgeom_transform.c +++ b/libpgcommon/lwgeom_transform.c @@ -765,3 +765,28 @@ void srid_is_latlong(FunctionCallInfo fcinfo, int srid) } +srs_precision srid_axis_precision(FunctionCallInfo fcinfo, int srid, int precision) +{ + projPJ pj1; + projPJ pj2; + + srs_precision sp; + + sp.precision_xy = precision; + sp.precision_z = precision; + sp.precision_m = precision; + + if ( srid == SRID_UNKNOWN ) + return sp; + + if ( GetProjectionsUsingFCInfo(fcinfo, srid, srid, &pj1, &pj2) == LW_FAILURE) + return sp; + + if ( pj_is_latlong(pj1) ) + { + sp.precision_xy += 5; + return sp; + } + + return sp; +} diff --git a/libpgcommon/lwgeom_transform.h b/libpgcommon/lwgeom_transform.h index 827c1131c..b09c1cdf1 100644 --- a/libpgcommon/lwgeom_transform.h +++ b/libpgcommon/lwgeom_transform.h @@ -14,7 +14,12 @@ #include "liblwgeom.h" #include "lwgeom_pg.h" - +typedef struct srs_precision +{ + int precision_xy; + int precision_z; + int precision_m; +} srs_precision; char* GetProj4StringSPI(int srid); void SetPROJ4LibPath(void) ; @@ -34,6 +39,7 @@ projPJ GetProjectionFromPROJ4Cache(Proj4Cache cache, int srid); int GetProjectionsUsingFCInfo(FunctionCallInfo fcinfo, int srid1, int srid2, projPJ *pj1, projPJ *pj2); int spheroid_init_from_srid(FunctionCallInfo fcinfo, int srid, SPHEROID *s); void srid_is_latlong(FunctionCallInfo fcinfo, int srid); +srs_precision srid_axis_precision(FunctionCallInfo fcinfo, int srid, int precision); /** * Builtin SRID values diff --git a/postgis/lwgeom_accum.c b/postgis/lwgeom_accum.c index 6a23d8ab2..c68d631ef 100644 --- a/postgis/lwgeom_accum.c +++ b/postgis/lwgeom_accum.c @@ -21,6 +21,7 @@ #include "liblwgeom.h" #include "lwgeom_pg.h" +#include "lwgeom_transform.h" /* Local prototypes */ Datum PGISDirectFunctionCall1(PGFunction func, Datum arg1); @@ -28,8 +29,6 @@ Datum pgis_geometry_accum_transfn(PG_FUNCTION_ARGS); Datum pgis_geometry_accum_finalfn(PG_FUNCTION_ARGS); Datum pgis_geometry_union_finalfn(PG_FUNCTION_ARGS); Datum pgis_geometry_collect_finalfn(PG_FUNCTION_ARGS); -Datum pgis_twkb_accum_finalfn(PG_FUNCTION_ARGS); -Datum pgis_twkb_accum_transfn(PG_FUNCTION_ARGS); Datum pgis_geometry_polygonize_finalfn(PG_FUNCTION_ARGS); Datum pgis_geometry_makeline_finalfn(PG_FUNCTION_ARGS); Datum pgis_abs_in(PG_FUNCTION_ARGS); @@ -38,7 +37,6 @@ Datum pgis_abs_out(PG_FUNCTION_ARGS); /* External prototypes */ Datum pgis_union_geometry_array(PG_FUNCTION_ARGS); Datum LWGEOM_collect_garray(PG_FUNCTION_ARGS); -Datum LWGEOM_twkb_garray(PG_FUNCTION_ARGS); Datum polygonize_garray(PG_FUNCTION_ARGS); Datum LWGEOM_makeline_garray(PG_FUNCTION_ARGS); @@ -67,23 +65,6 @@ typedef struct } pgis_abs; -typedef struct -{ - int64_t id; //Id, from function parameter - Datum geom; //the geometry from function parameter -} -geom_id; - - -typedef struct -{ - uint8_t variant; - int precision; //number of decimals in coordinates - int n_rows; - int max_rows; - geom_id *geoms; -} -twkb_state; /** @@ -95,7 +76,7 @@ Datum pgis_abs_in(PG_FUNCTION_ARGS) { ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("function pgis_abs_in not implemented"))); + errmsg("function %s not implemented", __func__))); PG_RETURN_POINTER(NULL); } PG_FUNCTION_INFO_V1(pgis_abs_out); @@ -103,7 +84,7 @@ Datum pgis_abs_out(PG_FUNCTION_ARGS) { ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("function pgis_abs_out not implemented"))); + errmsg("function %s not implemented", __func__))); PG_RETURN_POINTER(NULL); } @@ -127,15 +108,10 @@ pgis_geometry_accum_transfn(PG_FUNCTION_ARGS) (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); - if (fcinfo->context && IsA(fcinfo->context, AggState)) - aggcontext = ((AggState *) fcinfo->context)->aggcontext; - else if (fcinfo->context && IsA(fcinfo->context, WindowAggState)) - aggcontext = ((WindowAggState *) fcinfo->context)->aggcontext; - - else + if ( ! AggCheckCallContext(fcinfo, &aggcontext) ) { /* cannot be called directly because of dummy-type argument */ - elog(ERROR, "array_agg_transfn called in non-aggregate context"); + elog(ERROR, "%s called in non-aggregate context", __func__); aggcontext = NULL; /* keep compiler quiet */ } @@ -162,88 +138,6 @@ pgis_geometry_accum_transfn(PG_FUNCTION_ARGS) -PG_FUNCTION_INFO_V1(pgis_twkb_accum_transfn); -Datum -pgis_twkb_accum_transfn(PG_FUNCTION_ARGS) -{ - MemoryContext aggcontext; - MemoryContext oldcontext; - twkb_state* state; - int32 newlen; - uint8_t variant = 0; - - -if (!AggCheckCallContext(fcinfo, &aggcontext)) - { - /* cannot be called directly because of dummy-type argument */ - elog(ERROR, "array_agg_transfn called in non-aggregate context"); - aggcontext = NULL; /* keep compiler quiet */ - } - oldcontext = MemoryContextSwitchTo(aggcontext); - if ( PG_ARGISNULL(0) ) - { - /*state gets palloced and 10 geometry elements too - don't forget to free*/ - - state=palloc(sizeof(twkb_state)); - state->geoms = (geom_id*) palloc(10*sizeof(geom_id)); - state->max_rows = 10; - state->n_rows = 0; - - /* If user specified precision, respect it */ - state->precision = PG_ARGISNULL(2) ? (int) 0 : PG_GETARG_INT32(2); - } - else - { - state = (twkb_state*) PG_GETARG_POINTER(0); - - if(!((state->n_rows)<(state->max_rows))) - { - newlen = (state->max_rows)*2; - - state->geoms = (geom_id*)repalloc((void*)(state->geoms),newlen*sizeof(geom_id)); - - state->max_rows = newlen; - } - - } - - if (!PG_ARGISNULL(1)) - { - ((state->geoms)+state->n_rows)->geom = PG_ARGISNULL(1) ? (Datum) 0 : PointerGetDatum(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(1))); - - - if ((PG_NARGS()>3) && (!PG_ARGISNULL(3))) - { - variant = variant | (TWKB_ID); - ((state->geoms)+state->n_rows)->id = PG_GETARG_INT64(3); - } - else - { - variant = variant & ~TWKB_ID; - ((state->geoms)+state->n_rows)->id = 0; - } - - /* If user wants registered twkb sizes */ - if ( (PG_NARGS()>4) && (!PG_ARGISNULL(4)) && PG_GETARG_BOOL(4) ) - variant = variant | (TWKB_SIZES); - else - variant = variant & ~TWKB_SIZES; - - /* If user wants bounding boxes */ - if ( (PG_NARGS()>5) && (!PG_ARGISNULL(5)) && PG_GETARG_BOOL(5) ) - variant = variant | (TWKB_BBOXES); - else - variant = variant & ~TWKB_BBOXES; - - state->variant=variant; - (state->n_rows)++; - } - MemoryContextSwitchTo(oldcontext); - - PG_RETURN_POINTER(state); -} - Datum pgis_accum_finalfn(pgis_abs *p, MemoryContext mctx, FunctionCallInfo fcinfo); /** @@ -293,7 +187,7 @@ pgis_geometry_accum_finalfn(PG_FUNCTION_ARGS) } /** -* The "accum" final function passes the geometry[] to a union +* The "union" final function passes the geometry[] to a union * conversion before returning the result. */ PG_FUNCTION_INFO_V1(pgis_geometry_union_finalfn); @@ -342,107 +236,6 @@ pgis_geometry_collect_finalfn(PG_FUNCTION_ARGS) PG_RETURN_DATUM(result); } -/** -* The "twkb" final function passes the geometry[] to a twkb -* conversion before returning the result. -*/ -PG_FUNCTION_INFO_V1(pgis_twkb_accum_finalfn); -Datum -pgis_twkb_accum_finalfn(PG_FUNCTION_ARGS) -{ - int i; - twkb_state *state; - geom_id *geom_array; - twkb_geom_arrays lwgeom_arrays; - GSERIALIZED *geom; - LWGEOM *lwgeom; - size_t twkb_size; - uint8_t *twkb; - bytea *result; - state = (twkb_state*) PG_GETARG_POINTER(0); - lwgeom_arrays.n_points=lwgeom_arrays.n_linestrings=lwgeom_arrays.n_polygons=lwgeom_arrays.n_collections=0; - geom_array=state->geoms; - - if (state->n_rows<1) - PG_RETURN_NULL(); - - - for (i=0;in_rows;i++) - { - geom = (GSERIALIZED*)PG_DETOAST_DATUM((geom_array+i)->geom); - lwgeom = lwgeom_from_gserialized(geom); - switch ( lwgeom->type ) - { - case POINTTYPE: - if (lwgeom_arrays.n_points==0) - lwgeom_arrays.points = (lwgeom_id*)palloc(((state->n_rows)-i)*sizeof(lwgeom_id)); - - (lwgeom_arrays.points+lwgeom_arrays.n_points)->geom=lwgeom; - (lwgeom_arrays.points+lwgeom_arrays.n_points)->id=(geom_array+i)->id; - (lwgeom_arrays.n_points)++; - break; - /* LineString and CircularString both have 'points' elements */ - case LINETYPE: - if (lwgeom_arrays.n_linestrings==0) - lwgeom_arrays.linestrings = (lwgeom_id*)palloc(((state->n_rows)-i)*sizeof(lwgeom_id)); - - (lwgeom_arrays.linestrings+lwgeom_arrays.n_linestrings)->geom=lwgeom; - (lwgeom_arrays.linestrings+lwgeom_arrays.n_linestrings)->id=(geom_array+i)->id; - (lwgeom_arrays.n_linestrings)++; - break; - - /* Polygon has 'nrings' and 'rings' elements */ - case POLYGONTYPE: - if (lwgeom_arrays.n_polygons==0) - lwgeom_arrays.polygons = (lwgeom_id*)palloc(((state->n_rows)-i)*sizeof(lwgeom_id)); - - (lwgeom_arrays.polygons+lwgeom_arrays.n_polygons)->geom=lwgeom; - (lwgeom_arrays.polygons+lwgeom_arrays.n_polygons)->id=(geom_array+i)->id; - (lwgeom_arrays.n_polygons)++; - break; - - /* Triangle has one ring of three points - case TRIANGLETYPE: - return lwtriangle_to_twkb_buf((LWTRIANGLE*)geom, buf, variant); - */ - /* All these Collection types have 'ngeoms' and 'geoms' elements */ - case MULTIPOINTTYPE: - case MULTILINETYPE: - case MULTIPOLYGONTYPE: - case COLLECTIONTYPE: - if (lwgeom_arrays.n_collections==0) - lwgeom_arrays.collections = (lwgeom_id*)palloc(((state->n_rows)-i)*sizeof(lwgeom_id)); - - (lwgeom_arrays.collections+lwgeom_arrays.n_collections)->geom=lwgeom; - (lwgeom_arrays.collections+lwgeom_arrays.n_collections)->id=(geom_array+i)->id; - (lwgeom_arrays.n_collections)++; - break; - - /* Unknown type! */ - default: - lwerror("Unsupported geometry type: %s [%d]", lwtype_name(lwgeom->type), lwgeom->type); - } - - } - if (abs(state->precision)>7) - lwerror("precision cannot be more than 7"); - - twkb = lwgeom_agg_to_twkb(&lwgeom_arrays, state->variant , &twkb_size,(int8_t) state->precision); - - - /* Clean up and return */ - /* Prepare the PgSQL text return type */ - - result = palloc(twkb_size + VARHDRSZ); - SET_VARSIZE(result, twkb_size+VARHDRSZ); - memcpy(VARDATA(result), twkb, twkb_size); - - /* Clean up and return */ - pfree(twkb); - - PG_RETURN_BYTEA_P(result); -} - /** * The "polygonize" final function passes the geometry[] to a polygonization diff --git a/postgis/lwgeom_inout.c b/postgis/lwgeom_inout.c index 58520f163..e408a17b9 100644 --- a/postgis/lwgeom_inout.c +++ b/postgis/lwgeom_inout.c @@ -16,10 +16,18 @@ #include "utils/elog.h" #include "mb/pg_wchar.h" # include "lib/stringinfo.h" /* for binary input */ +#include "utils/array.h" +#include "utils/lsyscache.h" +#include "funcapi.h" #include "liblwgeom.h" #include "lwgeom_pg.h" #include "geography.h" /* for lwgeom_valid_typmod */ +#include "lwgeom_transform.h" + +#if POSTGIS_PGSQL_VERSION > 92 +#include "access/htup_details.h" +#endif void elog_ERROR(const char* string); @@ -33,6 +41,9 @@ Datum parse_WKT_lwgeom(PG_FUNCTION_ARGS); Datum LWGEOM_recv(PG_FUNCTION_ARGS); Datum LWGEOM_send(PG_FUNCTION_ARGS); Datum LWGEOM_to_latlon(PG_FUNCTION_ARGS); +Datum WKBFromLWGEOM(PG_FUNCTION_ARGS); +Datum TWKBFromLWGEOM(PG_FUNCTION_ARGS); +Datum TWKBFromLWGEOMArray(PG_FUNCTION_ARGS); /* @@ -392,7 +403,6 @@ Datum WKBFromLWGEOM(PG_FUNCTION_ARGS) uint8_t variant = 0; bytea *result; text *type; - /* If user specified endianness, respect it */ if ( (PG_NARGS()>1) && (!PG_ARGISNULL(1)) ) { @@ -408,9 +418,10 @@ Datum WKBFromLWGEOM(PG_FUNCTION_ARGS) variant = variant | WKB_NDR; } } - + wkb_size= VARSIZE(geom) - VARHDRSZ; /* Create WKB hex string */ lwgeom = lwgeom_from_gserialized(geom); + wkb = lwgeom_to_wkb(lwgeom, variant | WKB_EXTENDED , &wkb_size); lwgeom_free(lwgeom); @@ -425,7 +436,6 @@ Datum WKBFromLWGEOM(PG_FUNCTION_ARGS) PG_RETURN_BYTEA_P(result); } - PG_FUNCTION_INFO_V1(TWKBFromLWGEOM); Datum TWKBFromLWGEOM(PG_FUNCTION_ARGS) { @@ -435,66 +445,226 @@ Datum TWKBFromLWGEOM(PG_FUNCTION_ARGS) size_t twkb_size; uint8_t variant = 0; bytea *result; - int64_t id; - int prec; + int precXY, precZ, precM; + srs_precision sp; /*check for null input since we cannot have the sql-function as strict. That is because we use null as default for optional ID*/ if ( PG_ARGISNULL(0) ) PG_RETURN_NULL(); - geom = PG_GETARG_GSERIALIZED_P(0); - /* If user specified precision, respect it */ - if ( (PG_NARGS()>1) && (!PG_ARGISNULL(1)) ) + geom = PG_GETARG_GSERIALIZED_P(0); + + /* Read sensible precision defaults (about one meter) given the srs */ + sp = srid_axis_precision(fcinfo, gserialized_get_srid(geom), TWKB_DEFAULT_PRECISION); + + /* If user did not specify XY precision, use sensible default */ + if ( PG_NARGS() > 1 && PG_ARGISNULL(1) ) { - prec = PG_GETARG_INT32(1); - if (abs(prec)>7) - lwerror("precision cannot be more than 7"); + precXY = sp.precision_xy; } else - prec=0; - - /* If user specified id, respect it */ - if ( (PG_NARGS()>2) && (!PG_ARGISNULL(2)) ) { - variant = variant | (TWKB_ID); - id = PG_GETARG_INT64(2); + precXY = PG_GETARG_INT32(1); + if ( abs(precXY) > 7 ) + lwerror("precision cannot be more than 7"); + } + + /* If user did not specify Z precision, use sensible default */ + if ( PG_NARGS() > 2 && PG_ARGISNULL(2) ) + { + precZ = sp.precision_z; } else { - variant = variant & ~TWKB_ID; - id=0; - } - - /* If user wants registered twkb sizes */ - if ( (PG_NARGS()>3) && (!PG_ARGISNULL(3)) && PG_GETARG_BOOL(3) ) - variant = variant | (TWKB_SIZES); + precZ = PG_GETARG_INT32(2); + if ( precZ < 0 ) + lwerror("precision for Z cannot be negative"); + if ( precZ > 7 ) + lwerror("precision for Z cannot be more than 7"); + } + + /* If user did not specify M precision, use sensible default */ + if ( PG_NARGS() > 3 && PG_ARGISNULL(3) ) + { + precM = sp.precision_m; + } else - variant = variant & ~TWKB_SIZES; + { + precM = PG_GETARG_INT32(3); + if ( precM < 0 ) + lwerror("precision for Z cannot be negative"); + if ( precM > 7 ) + lwerror("precision for Z cannot be more than 7"); + } + + /* We don't permit ids for single geoemtries */ + variant = variant & ~TWKB_ID; - /* If user wants bounding boxes */ + /* If user wants registered twkb sizes */ if ( (PG_NARGS()>4) && (!PG_ARGISNULL(4)) && PG_GETARG_BOOL(4) ) - variant = variant | (TWKB_BBOXES); + variant |= TWKB_SIZE; else - variant = variant & ~TWKB_BBOXES; + variant &= ~TWKB_SIZE; + /* If user wants bounding boxes */ + if ( (PG_NARGS()>5) && (!PG_ARGISNULL(5)) && PG_GETARG_BOOL(5) ) + variant |= TWKB_BBOX; + else + variant &= ~TWKB_BBOX; - /* Create TWKB bin string */ + /* Create TWKB binary string */ lwgeom = lwgeom_from_gserialized(geom); - twkb = lwgeom_to_twkb(lwgeom, variant , &twkb_size,(int8_t) prec,(int64_t) id); + twkb = lwgeom_to_twkb(lwgeom, variant, precXY, precZ, precM, &twkb_size); lwgeom_free(lwgeom); /* Prepare the PgSQL text return type */ result = palloc(twkb_size + VARHDRSZ); memcpy(VARDATA(result), twkb, twkb_size); - - SET_VARSIZE(result, twkb_size+VARHDRSZ); + SET_VARSIZE(result, twkb_size + VARHDRSZ); - /* Clean up and return */ pfree(twkb); PG_FREE_IF_COPY(geom, 0); PG_RETURN_BYTEA_P(result); } + +PG_FUNCTION_INFO_V1(TWKBFromLWGEOMArray); +Datum TWKBFromLWGEOMArray(PG_FUNCTION_ARGS) +{ + ArrayType *arr_geoms = NULL; + ArrayType *arr_ids = NULL; + int num_geoms, num_ids, i = 0; + + ArrayIterator iter_geoms, iter_ids; + bool null_geom, null_id; + Datum val_geom, val_id; + + int is_homogeneous = true; + int subtype = 0; + LWCOLLECTION *col = NULL; + int64_t *idlist = NULL; + uint8_t variant = 0; + + srs_precision sp; + uint8_t *twkb; + size_t twkb_size; + bytea *result; + + /* The first two arguments are required */ + if ( PG_NARGS() < 2 || PG_ARGISNULL(0) || PG_ARGISNULL(1) ) + PG_RETURN_NULL(); + + arr_geoms = PG_GETARG_ARRAYTYPE_P(0); + arr_ids = PG_GETARG_ARRAYTYPE_P(1); + + num_geoms = ArrayGetNItems(ARR_NDIM(arr_geoms), ARR_DIMS(arr_geoms)); + num_ids = ArrayGetNItems(ARR_NDIM(arr_ids), ARR_DIMS(arr_ids)); + + if ( num_geoms != 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 */ + iter_geoms = array_create_iterator(arr_geoms, 0); + iter_ids = array_create_iterator(arr_ids, 0); + + while( array_iterate(iter_geoms, &val_geom, &null_geom) && + array_iterate(iter_ids, &val_id, &null_id) ) + { + LWGEOM *geom; + int32_t uid; + + if ( null_geom || null_id ) + { + elog(NOTICE, "ST_AsTWKB skipping NULL entry at position %d", i); + continue; + } + + geom = lwgeom_from_gserialized((GSERIALIZED*)DatumGetPointer(val_geom)); + uid = DatumGetInt64(val_id); + + /* Construct collection/idlist first time through */ + if ( ! col ) + col = lwcollection_construct_empty(COLLECTIONTYPE, lwgeom_get_srid(geom), lwgeom_has_z(geom), lwgeom_has_m(geom)); + if ( ! idlist ) + idlist = palloc0(num_geoms * sizeof(int64_t)); + + /* Store the values */ + lwcollection_add_lwgeom(col, geom); + idlist[i++] = uid; + + /* Grab the geometry type and note if all geometries share it */ + /* If so, we can make this a homogeneous collection and save some space */ + if ( lwgeom_get_type(geom) != subtype && subtype ) + { + is_homogeneous = false; + } + else + { + subtype = lwgeom_get_type(geom); + } + + } + + array_free_iterator(iter_geoms); + array_free_iterator(iter_ids); + + if ( is_homogeneous ) + { + col->type = lwtype_get_collectiontype(subtype); + } + + /* Read sensible precision defaults (about one meter) given the srs */ + sp = srid_axis_precision(fcinfo, lwgeom_get_srid(lwcollection_as_lwgeom(col)), TWKB_DEFAULT_PRECISION); + + /* If user did not specify XY precision, use sensible default */ + if ( ! ( PG_NARGS() > 2 && PG_ARGISNULL(2) ) ) + sp.precision_xy = PG_GETARG_INT32(2); + + /* If user did not specify Z precision, use sensible default */ + if ( ! ( PG_NARGS() > 3 && PG_ARGISNULL(3) ) ) + sp.precision_z = PG_GETARG_INT32(3); + + /* If user did not specify M precision, use sensible default */ + if ( ! ( PG_NARGS() > 4 && PG_ARGISNULL(4) ) ) + sp.precision_m = PG_GETARG_INT32(4); + + /* We are building an ID'ed output */ + variant = TWKB_ID; + + /* If user wants registered twkb sizes */ + if ( PG_NARGS() > 5 && !PG_ARGISNULL(5) && PG_GETARG_BOOL(5) ) + variant |= TWKB_SIZE; + + /* If user wants bounding boxes */ + if ( PG_NARGS() > 6 && !PG_ARGISNULL(6) && PG_GETARG_BOOL(6) ) + variant |= TWKB_BBOX; + + /* Write out the TWKB */ + twkb = lwgeom_to_twkb_with_idlist(lwcollection_as_lwgeom(col), + idlist, variant, + sp.precision_xy, sp.precision_z, sp.precision_m, + &twkb_size); + + /* Convert to a bytea return type */ + result = palloc(twkb_size + VARHDRSZ); + memcpy(VARDATA(result), twkb, twkb_size); + SET_VARSIZE(result, twkb_size + VARHDRSZ); + + /* Clean up */ + pfree(twkb); + pfree(idlist); + lwcollection_free(col); + PG_FREE_IF_COPY(arr_geoms, 0); + PG_FREE_IF_COPY(arr_ids, 1); + + PG_RETURN_BYTEA_P(result); +} + + /* puts a bbox inside the geometry */ PG_FUNCTION_INFO_V1(LWGEOM_addBBOX); Datum LWGEOM_addBBOX(PG_FUNCTION_ARGS) diff --git a/postgis/postgis.sql.in b/postgis/postgis.sql.in index 369640703..a66a49924 100644 --- a/postgis/postgis.sql.in +++ b/postgis/postgis.sql.in @@ -1500,11 +1500,17 @@ CREATE OR REPLACE FUNCTION ST_AsEWKT(geometry) LANGUAGE 'c' IMMUTABLE STRICT; -- Availability: 2.2.0 -CREATE OR REPLACE FUNCTION ST_AsTWKB(geometry,int4,int8 default null,boolean default false, boolean default false) +CREATE OR REPLACE FUNCTION ST_AsTWKB(geom geometry, prec int4 default NULL, prec_z int4 default NULL, prec_m int4 default NULL, with_sizes boolean default NULL, with_boxes boolean default NULL) RETURNS bytea AS 'MODULE_PATHNAME','TWKBFromLWGEOM' LANGUAGE 'c' IMMUTABLE; - + +-- Availability: 2.2.0 +CREATE OR REPLACE FUNCTION ST_AsTWKB(geom geometry[], ids bigint[], prec int4 default NULL, prec_z int4 default NULL, prec_m int4 default NULL, with_sizes boolean default NULL, with_boxes boolean default NULL) + RETURNS bytea + AS 'MODULE_PATHNAME','TWKBFromLWGEOMArray' + LANGUAGE 'c' IMMUTABLE; + -- Availability: 1.2.2 CREATE OR REPLACE FUNCTION ST_AsEWKB(geometry) RETURNS BYTEA @@ -3531,64 +3537,6 @@ CREATE OR REPLACE FUNCTION pgis_geometry_makeline_finalfn(pgis_abs) AS 'MODULE_PATHNAME' LANGUAGE 'c'; ---Availability: 2.2.0 -CREATE OR REPLACE FUNCTION pgis_twkb_accum_transfn(internal, geometry,int) - RETURNS internal - AS 'MODULE_PATHNAME' - LANGUAGE c ; - --- Availability: 2.2.0 -CREATE OR REPLACE FUNCTION pgis_twkb_accum_transfn(internal, geometry,int,int8) - RETURNS internal - AS 'MODULE_PATHNAME' - LANGUAGE c ; - --- Availability: 2.2.0 -CREATE OR REPLACE FUNCTION pgis_twkb_accum_transfn(internal, geometry,int,int8,boolean) - RETURNS internal - AS 'MODULE_PATHNAME' - LANGUAGE c ; - --- Availability: 2.2.0 -CREATE OR REPLACE FUNCTION pgis_twkb_accum_transfn(internal, geometry,int,int8,boolean,boolean) - RETURNS internal - AS 'MODULE_PATHNAME' - LANGUAGE c ; - --- Availability: 2.2.0 -CREATE OR REPLACE FUNCTION pgis_twkb_accum_finalfn(internal) - RETURNS bytea - AS 'MODULE_PATHNAME' - LANGUAGE c ; - --- Availability: 2.2.0 -CREATE AGGREGATE st_astwkbagg(geometry,int) ( - SFUNC=pgis_twkb_accum_transfn, - STYPE=internal, - FINALFUNC=pgis_twkb_accum_finalfn -); - --- Availability: 2.2.0 -CREATE AGGREGATE st_astwkbagg(geometry,int,int8) ( - SFUNC=pgis_twkb_accum_transfn, - STYPE=internal, - FINALFUNC=pgis_twkb_accum_finalfn -); - --- Availability: 2.2.0 -CREATE AGGREGATE st_astwkbagg(geometry,int,int8,boolean) ( - SFUNC=pgis_twkb_accum_transfn, - STYPE=internal, - FINALFUNC=pgis_twkb_accum_finalfn -); - --- Availability: 2.2.0 -CREATE AGGREGATE st_astwkbagg(geometry,int,int8,boolean,boolean) ( - SFUNC=pgis_twkb_accum_transfn, - STYPE=internal, - FINALFUNC=pgis_twkb_accum_finalfn -); - -- Availability: 1.2.2 CREATE AGGREGATE ST_Accum ( sfunc = pgis_geometry_accum_transfn, diff --git a/regress/tickets.sql b/regress/tickets.sql index ef0840146..4972d21c2 100644 --- a/regress/tickets.sql +++ b/regress/tickets.sql @@ -859,7 +859,7 @@ INSERT INTO images VALUES (1, 'first_image', 'SRID=4326;POLYGON((-162.211667 88. SELECT '#2556' AS ticket, id, round(ST_Distance(extent, 'SRID=4326;POLYGON((-46.625977 81.634149,-46.625977 81.348076,-48.999023 81.348076,-48.999023 81.634149,-46.625977 81.634149))'::geography)) from images; DROP TABLE images; -SELECT '#2672', ST_AsTWKBAgg(null::geometry, 3); +-- SELECT '#2672', ST_AsTWKBAgg(null::geometry, 3); SELECT '#2704', ST_AsText(ST_GeomFromGML('