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