1 /**********************************************************************
2  *
3  * rttopo - topology library
4  * http://git.osgeo.org/gitea/rttopo/librttopo
5  *
6  * rttopo is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * rttopo is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with rttopo.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  **********************************************************************
20  *
21  * Copyright (C) 2013 Nicklas Avén
22  *
23  **********************************************************************/
24 
25 
26 
27 #include "rttopo_config.h"
28 #include "rtout_twkb.h"
29 
30 /*
31 * GeometryType, and dimensions
32 */
rtgeom_twkb_type(const RTCTX * ctx,const RTGEOM * geom)33 static uint8_t rtgeom_twkb_type(const RTCTX *ctx, const RTGEOM *geom)
34 {
35   uint8_t twkb_type = 0;
36 
37   RTDEBUGF(ctx, 2, "Entered  rtgeom_twkb_type",0);
38 
39   switch ( geom->type )
40   {
41     case RTPOINTTYPE:
42       twkb_type = RTWKB_POINT_TYPE;
43       break;
44     case RTLINETYPE:
45       twkb_type = RTWKB_LINESTRING_TYPE;
46       break;
47     case RTPOLYGONTYPE:
48       twkb_type = RTWKB_POLYGON_TYPE;
49       break;
50     case RTMULTIPOINTTYPE:
51       twkb_type = RTWKB_MULTIPOINT_TYPE;
52       break;
53     case RTMULTILINETYPE:
54       twkb_type = RTWKB_MULTILINESTRING_TYPE;
55       break;
56     case RTMULTIPOLYGONTYPE:
57       twkb_type = RTWKB_MULTIPOLYGON_TYPE;
58       break;
59     case RTCOLLECTIONTYPE:
60       twkb_type = RTWKB_GEOMETRYCOLLECTION_TYPE;
61       break;
62     default:
63       rterror(ctx, "Unsupported geometry type: %s [%d]",
64         rttype_name(ctx, geom->type), geom->type);
65   }
66   return twkb_type;
67 }
68 
69 
70 /**
71 * Calculates the size of the bbox in varints in the form:
72 * xmin, xdelta, ymin, ydelta
73 */
sizeof_bbox(const RTCTX * ctx,TWKB_STATE * ts,int ndims)74 static size_t sizeof_bbox(const RTCTX *ctx, TWKB_STATE *ts, int ndims)
75 {
76   int i;
77   uint8_t buf[16];
78   size_t size = 0;
79   RTDEBUGF(ctx, 2, "Entered %s", __func__);
80   for ( i = 0; i < ndims; i++ )
81   {
82     size += varint_s64_encode_buf(ctx, ts->bbox_min[i], buf);
83     size += varint_s64_encode_buf(ctx, (ts->bbox_max[i] - ts->bbox_min[i]), buf);
84   }
85   return size;
86 }
87 /**
88 * Writes the bbox in varints in the form:
89 * xmin, xdelta, ymin, ydelta
90 */
write_bbox(const RTCTX * ctx,TWKB_STATE * ts,int ndims)91 static void write_bbox(const RTCTX *ctx, TWKB_STATE *ts, int ndims)
92 {
93   int i;
94   RTDEBUGF(ctx, 2, "Entered %s", __func__);
95   for ( i = 0; i < ndims; i++ )
96   {
97     bytebuffer_append_varint(ctx, ts->header_buf, ts->bbox_min[i]);
98     bytebuffer_append_varint(ctx, ts->header_buf, (ts->bbox_max[i] - ts->bbox_min[i]));
99   }
100 }
101 
102 
103 /**
104 * Stores a pointarray as varints in the buffer
105 * @register_npoints, controls whether an npoints entry is added to the buffer (used to skip npoints for point types)
106 * @dimension, states the dimensionality of object this array is part of (0 = point, 1 = linear, 2 = areal)
107 */
ptarray_to_twkb_buf(const RTCTX * ctx,const RTPOINTARRAY * pa,TWKB_GLOBALS * globals,TWKB_STATE * ts,int register_npoints,int minpoints)108 static int ptarray_to_twkb_buf(const RTCTX *ctx, const RTPOINTARRAY *pa, TWKB_GLOBALS *globals, TWKB_STATE *ts, int register_npoints, int minpoints)
109 {
110   int ndims = RTFLAGS_NDIMS(pa->flags);
111   int i, j;
112   bytebuffer_t b;
113   bytebuffer_t *b_p;
114   int64_t nextdelta[MAX_N_DIMS];
115   int npoints = 0;
116   size_t npoints_offset = 0;
117 
118   RTDEBUGF(ctx, 2, "Entered %s", __func__);
119 
120   /* Dispense with the empty case right away */
121   if ( pa->npoints == 0 && register_npoints )
122   {
123     RTDEBUGF(ctx, 4, "Register npoints:%d", pa->npoints);
124     bytebuffer_append_uvarint(ctx, ts->geom_buf, pa->npoints);
125     return 0;
126   }
127 
128   /* If npoints is more than 127 it is unpredictable how many bytes npoints will need */
129   /* Then we have to store the deltas in a temp buffer to later add them after npoints */
130   /* If noints is below 128 we know 1 byte will be needed */
131   /* Then we can make room for that 1 byte at once and write to */
132   /* ordinary buffer */
133   if( pa->npoints > 127 )
134   {
135     /* Independent buffer to hold the coordinates, so we can put the npoints */
136     /* into the stream once we know how many points we actually have */
137     bytebuffer_init_with_size(ctx, &b, 3 * ndims * pa->npoints);
138     b_p = &b;
139   }
140   else
141   {
142     /* We give an alias to our ordinary buffer */
143     b_p = ts->geom_buf;
144     if ( register_npoints )
145     {
146       /* We do not store a pointer to the place where we want the npoints value */
147       /* Instead we store how far from the beginning of the buffer we want the value */
148       /* That is because we otherwise will get in trouble if the buffer is reallocated */
149       npoints_offset = b_p->writecursor - b_p->buf_start;
150 
151       /* We just move the cursor 1 step to make room for npoints byte */
152       /* We use the function append_byte even if we have no value yet, */
153       /* since that gives us the check for big enough buffer and moves the cursor */
154       bytebuffer_append_byte(ctx, b_p, 0);
155     }
156   }
157 
158   for ( i = 0; i < pa->npoints; i++ )
159   {
160     double *dbl_ptr = (double*)rt_getPoint_internal(ctx, pa, i);
161     int diff = 0;
162 
163     /* Write this coordinate to the buffer as a varint */
164     for ( j = 0; j < ndims; j++ )
165     {
166       /* To get the relative coordinate we don't get the distance */
167       /* from the last point but instead the distance from our */
168       /* last accumulated point. This is important to not build up an */
169       /* accumulated error when rounding the coordinates */
170       nextdelta[j] = (int64_t) llround(globals->factor[j] * dbl_ptr[j]) - ts->accum_rels[j];
171       RTDEBUGF(ctx, 4, "deltavalue: %d, ", nextdelta[j]);
172       diff += llabs(nextdelta[j]);
173     }
174 
175     /* Skipping the first point is not allowed */
176     /* If the sum(abs()) of all the deltas was zero, */
177     /* then this was a duplicate point, so we can ignore it */
178     if ( i > minpoints && diff == 0 )
179       continue;
180 
181     /* We really added a point, so... */
182     npoints++;
183 
184     /* Write this vertex to the temporary buffer as varints */
185     for ( j = 0; j < ndims; j++ )
186     {
187       ts->accum_rels[j] += nextdelta[j];
188       bytebuffer_append_varint(ctx, b_p, nextdelta[j]);
189     }
190 
191     /* See if this coordinate expands the bounding box */
192     if( globals->variant & TWKB_BBOX )
193     {
194       for ( j = 0; j < ndims; j++ )
195       {
196         if( ts->accum_rels[j] > ts->bbox_max[j] )
197           ts->bbox_max[j] = ts->accum_rels[j];
198 
199         if( ts->accum_rels[j] < ts->bbox_min[j] )
200           ts->bbox_min[j] = ts->accum_rels[j];
201       }
202     }
203 
204   }
205 
206   if ( pa->npoints > 127 )
207   {
208     /* Now write the temporary results into the main buffer */
209     /* First the npoints */
210     if ( register_npoints )
211       bytebuffer_append_uvarint(ctx, ts->geom_buf, npoints);
212     /* Now the coordinates */
213     bytebuffer_append_bytebuffer(ctx, ts->geom_buf, b_p);
214 
215     /* Clear our temporary buffer */
216     rtfree(ctx, b.buf_start);
217   }
218   else
219   {
220     /* If we didn't use a temp buffer, we just write that npoints value */
221     /* to where it belongs*/
222     if ( register_npoints )
223       varint_u64_encode_buf(ctx, npoints, b_p->buf_start + npoints_offset);
224   }
225 
226   return 0;
227 }
228 
229 /******************************************************************
230 * POINTS
231 *******************************************************************/
232 
rtpoint_to_twkb_buf(const RTCTX * ctx,const RTPOINT * pt,TWKB_GLOBALS * globals,TWKB_STATE * ts)233 static int rtpoint_to_twkb_buf(const RTCTX *ctx, const RTPOINT *pt, TWKB_GLOBALS *globals, TWKB_STATE *ts)
234 {
235   RTDEBUGF(ctx, 2, "Entered %s", __func__);
236 
237   /* Set the coordinates (don't write npoints) */
238   ptarray_to_twkb_buf(ctx, pt->point, globals, ts, 0, 1);
239   return 0;
240 }
241 
242 /******************************************************************
243 * LINESTRINGS
244 *******************************************************************/
245 
rtline_to_twkb_buf(const RTCTX * ctx,const RTLINE * line,TWKB_GLOBALS * globals,TWKB_STATE * ts)246 static int rtline_to_twkb_buf(const RTCTX *ctx, const RTLINE *line, TWKB_GLOBALS *globals, TWKB_STATE *ts)
247 {
248   RTDEBUGF(ctx, 2, "Entered %s", __func__);
249 
250   /* Set the coordinates (do write npoints) */
251   ptarray_to_twkb_buf(ctx, line->points, globals, ts, 1, 2);
252   return 0;
253 }
254 
255 /******************************************************************
256 * POLYGONS
257 *******************************************************************/
258 
rtpoly_to_twkb_buf(const RTCTX * ctx,const RTPOLY * poly,TWKB_GLOBALS * globals,TWKB_STATE * ts)259 static int rtpoly_to_twkb_buf(const RTCTX *ctx, const RTPOLY *poly, TWKB_GLOBALS *globals, TWKB_STATE *ts)
260 {
261   int i;
262 
263   /* Set the number of rings */
264   bytebuffer_append_uvarint(ctx, ts->geom_buf, (uint64_t) poly->nrings);
265 
266   for ( i = 0; i < poly->nrings; i++ )
267   {
268     /* Set the coordinates (do write npoints) */
269     ptarray_to_twkb_buf(ctx, poly->rings[i], globals, ts, 1, 4);
270   }
271 
272   return 0;
273 }
274 
275 
276 
277 /******************************************************************
278 * MULTI-GEOMETRYS (MultiPoint, MultiLinestring, MultiPolygon)
279 *******************************************************************/
280 
rtmulti_to_twkb_buf(const RTCTX * ctx,const RTCOLLECTION * col,TWKB_GLOBALS * globals,TWKB_STATE * ts)281 static int rtmulti_to_twkb_buf(const RTCTX *ctx, const RTCOLLECTION *col, TWKB_GLOBALS *globals, TWKB_STATE *ts)
282 {
283   int i;
284   int nempty = 0;
285 
286   RTDEBUGF(ctx, 2, "Entered %s", __func__);
287   RTDEBUGF(ctx, 4, "Number of geometries in multi is %d", col->ngeoms);
288 
289   /* Deal with special case for MULTIPOINT: skip any empty points */
290   if ( col->type == RTMULTIPOINTTYPE )
291   {
292     for ( i = 0; i < col->ngeoms; i++ )
293       if ( rtgeom_is_empty(ctx, col->geoms[i]) )
294         nempty++;
295   }
296 
297   /* Set the number of geometries */
298   bytebuffer_append_uvarint(ctx, ts->geom_buf, (uint64_t) (col->ngeoms - nempty));
299 
300   /* We've been handed an idlist, so write it in */
301   if ( ts->idlist )
302   {
303     for ( i = 0; i < col->ngeoms; i++ )
304     {
305       /* Skip empty points in multipoints, we can't represent them */
306       if ( col->type == RTMULTIPOINTTYPE && rtgeom_is_empty(ctx, col->geoms[i]) )
307         continue;
308 
309       bytebuffer_append_varint(ctx, ts->geom_buf, ts->idlist[i]);
310     }
311 
312     /* Empty it out to nobody else uses it now */
313     ts->idlist = NULL;
314   }
315 
316   for ( i = 0; i < col->ngeoms; i++ )
317   {
318     /* Skip empty points in multipoints, we can't represent them */
319     if ( col->type == RTMULTIPOINTTYPE && rtgeom_is_empty(ctx, col->geoms[i]) )
320       continue;
321 
322     rtgeom_to_twkb_buf(ctx, col->geoms[i], globals, ts);
323   }
324   return 0;
325 }
326 
327 /******************************************************************
328 * GEOMETRYCOLLECTIONS
329 *******************************************************************/
330 
rtcollection_to_twkb_buf(const RTCTX * ctx,const RTCOLLECTION * col,TWKB_GLOBALS * globals,TWKB_STATE * ts)331 static int rtcollection_to_twkb_buf(const RTCTX *ctx, const RTCOLLECTION *col, TWKB_GLOBALS *globals, TWKB_STATE *ts)
332 {
333   int i;
334 
335   RTDEBUGF(ctx, 2, "Entered %s", __func__);
336   RTDEBUGF(ctx, 4, "Number of geometries in collection is %d", col->ngeoms);
337 
338   /* Set the number of geometries */
339   bytebuffer_append_uvarint(ctx, ts->geom_buf, (uint64_t) col->ngeoms);
340 
341   /* We've been handed an idlist, so write it in */
342   if ( ts->idlist )
343   {
344     for ( i = 0; i < col->ngeoms; i++ )
345       bytebuffer_append_varint(ctx, ts->geom_buf, ts->idlist[i]);
346 
347     /* Empty it out to nobody else uses it now */
348     ts->idlist = NULL;
349   }
350 
351   /* Write in the sub-geometries */
352   for ( i = 0; i < col->ngeoms; i++ )
353   {
354     rtgeom_write_to_buffer(ctx, col->geoms[i], globals, ts);
355   }
356   return 0;
357 }
358 
359 
360 /******************************************************************
361 * Handle whole TWKB
362 *******************************************************************/
363 
rtgeom_to_twkb_buf(const RTCTX * ctx,const RTGEOM * geom,TWKB_GLOBALS * globals,TWKB_STATE * ts)364 static int rtgeom_to_twkb_buf(const RTCTX *ctx, const RTGEOM *geom, TWKB_GLOBALS *globals, TWKB_STATE *ts)
365 {
366   RTDEBUGF(ctx, 2, "Entered %s", __func__);
367 
368   switch ( geom->type )
369   {
370     case RTPOINTTYPE:
371     {
372       RTDEBUGF(ctx, 4,"Type found is Point, %d", geom->type);
373       return rtpoint_to_twkb_buf(ctx, (RTPOINT*) geom, globals, ts);
374     }
375     case RTLINETYPE:
376     {
377       RTDEBUGF(ctx, 4,"Type found is Linestring, %d", geom->type);
378       return rtline_to_twkb_buf(ctx, (RTLINE*) geom, globals, ts);
379     }
380     /* Polygon has 'nrings' and 'rings' elements */
381     case RTPOLYGONTYPE:
382     {
383       RTDEBUGF(ctx, 4,"Type found is Polygon, %d", geom->type);
384       return rtpoly_to_twkb_buf(ctx, (RTPOLY*)geom, globals, ts);
385     }
386 
387     /* All these Collection types have 'ngeoms' and 'geoms' elements */
388     case RTMULTIPOINTTYPE:
389     case RTMULTILINETYPE:
390     case RTMULTIPOLYGONTYPE:
391     {
392       RTDEBUGF(ctx, 4,"Type found is Multi, %d", geom->type);
393       return rtmulti_to_twkb_buf(ctx, (RTCOLLECTION*)geom, globals, ts);
394     }
395     case RTCOLLECTIONTYPE:
396     {
397       RTDEBUGF(ctx, 4,"Type found is collection, %d", geom->type);
398       return rtcollection_to_twkb_buf(ctx, (RTCOLLECTION*) geom, globals, ts);
399     }
400     /* Unknown type! */
401     default:
402       rterror(ctx, "Unsupported geometry type: %s [%d]", rttype_name(ctx, (geom)->type), (geom)->type);
403   }
404 
405   return 0;
406 }
407 
408 
rtgeom_write_to_buffer(const RTCTX * ctx,const RTGEOM * geom,TWKB_GLOBALS * globals,TWKB_STATE * parent_state)409 static int rtgeom_write_to_buffer(const RTCTX *ctx, const RTGEOM *geom, TWKB_GLOBALS *globals, TWKB_STATE *parent_state)
410 {
411   int i, is_empty, has_z, has_m, ndims;
412   size_t bbox_size = 0, optional_precision_byte = 0;
413   uint8_t flag = 0, type_prec = 0;
414 
415   TWKB_STATE child_state;
416   memset(&child_state, 0, sizeof(TWKB_STATE));
417   child_state.header_buf = bytebuffer_create_with_size(ctx, 16);
418   child_state.geom_buf = bytebuffer_create_with_size(ctx, 64);
419   child_state.idlist = parent_state->idlist;
420 
421   /* Read dimensionality from input */
422   has_z = rtgeom_has_z(ctx, geom);
423   has_m = rtgeom_has_m(ctx, geom);
424   ndims = rtgeom_ndims(ctx, geom);
425   is_empty = rtgeom_is_empty(ctx, geom);
426 
427   /* Do we need extended precision? If we have a Z or M we do. */
428   optional_precision_byte = (has_z || has_m);
429 
430   /* Both X and Y dimension use the same precision */
431   globals->factor[0] = pow(10, globals->prec_xy);
432   globals->factor[1] = globals->factor[0];
433 
434   /* Z and M dimensions have their own precisions */
435   if ( has_z )
436     globals->factor[2] = pow(10, globals->prec_z);
437   if ( has_m )
438     globals->factor[2 + has_z] = pow(10, globals->prec_m);
439 
440   /* Reset stats */
441   for ( i = 0; i < MAX_N_DIMS; i++ )
442   {
443     /* Reset bbox calculation */
444     child_state.bbox_max[i] = INT64_MIN;
445     child_state.bbox_min[i] = INT64_MAX;
446     /* Reset acumulated delta values to get absolute values on next point */
447     child_state.accum_rels[i] = 0;
448   }
449 
450   /* RTTYPE/PRECISION BYTE */
451   if ( abs(globals->prec_xy) > 7 )
452     rterror(ctx, "%s: X/Z precision cannot be greater than 7 or less than -7", __func__);
453 
454   /* Read the TWKB type number from the geometry */
455   RTTYPE_PREC_SET_TYPE(type_prec, rtgeom_twkb_type(ctx, geom));
456   /* Zig-zag the precision value before encoding it since it is a signed value */
457   TYPE_PREC_SET_PREC(type_prec, zigzag8(ctx, globals->prec_xy));
458   /* Write the type and precision byte */
459   bytebuffer_append_byte(ctx, child_state.header_buf, type_prec);
460 
461   /* METADATA BYTE */
462   /* Set first bit if we are going to store bboxes */
463   FIRST_BYTE_SET_BBOXES(flag, (globals->variant & TWKB_BBOX) && ! is_empty);
464   /* Set second bit if we are going to store resulting size */
465   FIRST_BYTE_SET_SIZES(flag, globals->variant & TWKB_SIZE);
466   /* There will be no ID-list (for now) */
467   FIRST_BYTE_SET_IDLIST(flag, parent_state->idlist && ! is_empty);
468   /* Are there higher dimensions */
469   FIRST_BYTE_SET_EXTENDED(flag, optional_precision_byte);
470   /* Empty? */
471   FIRST_BYTE_SET_EMPTY(flag, is_empty);
472   /* Write the header byte */
473   bytebuffer_append_byte(ctx, child_state.header_buf, flag);
474 
475   /* EXTENDED PRECISION BYTE (OPTIONAL) */
476   /* If needed, write the extended dim byte */
477   if( optional_precision_byte )
478   {
479     uint8_t flag = 0;
480 
481     if ( has_z && ( globals->prec_z > 7 || globals->prec_z < 0 ) )
482       rterror(ctx, "%s: Z precision cannot be negative or greater than 7", __func__);
483 
484     if ( has_m && ( globals->prec_m > 7 || globals->prec_m < 0 ) )
485       rterror(ctx, "%s: M precision cannot be negative or greater than 7", __func__);
486 
487     HIGHER_DIM_SET_HASZ(flag, has_z);
488     HIGHER_DIM_SET_HASM(flag, has_m);
489     HIGHER_DIM_SET_PRECZ(flag, globals->prec_z);
490     HIGHER_DIM_SET_PRECM(flag, globals->prec_m);
491     bytebuffer_append_byte(ctx, child_state.header_buf, flag);
492   }
493 
494   /* It the geometry is empty, we're almost done */
495   if ( is_empty )
496   {
497     /* If this output is sized, write the size of */
498     /* all following content, which is zero because */
499     /* there is none */
500     if ( globals->variant & TWKB_SIZE )
501       bytebuffer_append_byte(ctx, child_state.header_buf, 0);
502 
503     bytebuffer_append_bytebuffer(ctx, parent_state->geom_buf, child_state.header_buf);
504     bytebuffer_destroy(ctx, child_state.header_buf);
505     bytebuffer_destroy(ctx, child_state.geom_buf);
506     return 0;
507   }
508 
509   /* Write the TWKB into the output buffer */
510   rtgeom_to_twkb_buf(ctx, geom, globals, &child_state);
511 
512   /*If we have a header_buf, we know that this function is called inside a collection*/
513   /*and then we have to merge the bboxes of the included geometries*/
514   /*and put the result to the parent (the collection)*/
515   if( (globals->variant & TWKB_BBOX) && parent_state->header_buf )
516   {
517     RTDEBUG(ctx, 4,"Merge bboxes");
518     for ( i = 0; i < ndims; i++ )
519     {
520       if(child_state.bbox_min[i]<parent_state->bbox_min[i])
521         parent_state->bbox_min[i] = child_state.bbox_min[i];
522       if(child_state.bbox_max[i]>parent_state->bbox_max[i])
523         parent_state->bbox_max[i] = child_state.bbox_max[i];
524     }
525   }
526 
527   /* Did we have a box? If so, how big? */
528   bbox_size = 0;
529   if( globals->variant & TWKB_BBOX )
530   {
531     RTDEBUG(ctx, 4,"We want boxes and will calculate required size");
532     bbox_size = sizeof_bbox(ctx, &child_state, ndims);
533   }
534 
535   /* Write the size if wanted */
536   if( globals->variant & TWKB_SIZE )
537   {
538     /* Here we have to add what we know will be written to header */
539     /* buffer after size value is written */
540     size_t size_to_register = bytebuffer_getlength(ctx, child_state.geom_buf);
541     size_to_register += bbox_size;
542     bytebuffer_append_uvarint(ctx, child_state.header_buf, size_to_register);
543   }
544 
545   if( globals->variant & TWKB_BBOX )
546     write_bbox(ctx, &child_state, ndims);
547 
548   bytebuffer_append_bytebuffer(ctx, parent_state->geom_buf,child_state.header_buf);
549   bytebuffer_append_bytebuffer(ctx, parent_state->geom_buf,child_state.geom_buf);
550 
551   bytebuffer_destroy(ctx, child_state.header_buf);
552   bytebuffer_destroy(ctx, child_state.geom_buf);
553   return 0;
554 }
555 
556 
557 /**
558 * Convert RTGEOM to a char* in TWKB format. Caller is responsible for freeing
559 * the returned array.
560 */
561 uint8_t*
rtgeom_to_twkb_with_idlist(const RTCTX * ctx,const RTGEOM * geom,int64_t * idlist,uint8_t variant,int8_t precision_xy,int8_t precision_z,int8_t precision_m,size_t * twkb_size)562 rtgeom_to_twkb_with_idlist(const RTCTX *ctx, const RTGEOM *geom, int64_t *idlist, uint8_t variant,
563                int8_t precision_xy, int8_t precision_z, int8_t precision_m,
564                size_t *twkb_size)
565 {
566   RTDEBUGF(ctx, 2, "Entered %s", __func__);
567   RTDEBUGF(ctx, 2, "variant value %x", variant);
568 
569   TWKB_GLOBALS tg;
570   TWKB_STATE ts;
571 
572   uint8_t *twkb;
573 
574   memset(&ts, 0, sizeof(TWKB_STATE));
575   memset(&tg, 0, sizeof(TWKB_GLOBALS));
576 
577   tg.variant = variant;
578   tg.prec_xy = precision_xy;
579   tg.prec_z = precision_z;
580   tg.prec_m = precision_m;
581 
582   if ( idlist && ! rtgeom_is_collection(ctx, geom) )
583   {
584     rterror(ctx, "Only collections can support ID lists");
585     return NULL;
586   }
587 
588   if ( ! geom )
589   {
590     RTDEBUG(ctx, 4,"Cannot convert NULL into TWKB.");
591     rterror(ctx, "Cannot convert NULL into TWKB");
592     return NULL;
593   }
594 
595   ts.idlist = idlist;
596   ts.header_buf = NULL;
597   ts.geom_buf = bytebuffer_create(ctx);
598   rtgeom_write_to_buffer(ctx, geom, &tg, &ts);
599 
600   if ( twkb_size )
601     *twkb_size = bytebuffer_getlength(ctx, ts.geom_buf);
602 
603   twkb = ts.geom_buf->buf_start;
604   rtfree(ctx, ts.geom_buf);
605   return twkb;
606 }
607 
608 
609 uint8_t*
rtgeom_to_twkb(const RTCTX * ctx,const RTGEOM * geom,uint8_t variant,int8_t precision_xy,int8_t precision_z,int8_t precision_m,size_t * twkb_size)610 rtgeom_to_twkb(const RTCTX *ctx, const RTGEOM *geom, uint8_t variant,
611                int8_t precision_xy, int8_t precision_z, int8_t precision_m,
612                size_t *twkb_size)
613 {
614   return rtgeom_to_twkb_with_idlist(ctx, geom, NULL, variant, precision_xy, precision_z, precision_m, twkb_size);
615 }
616 
617 
618