1 /**********************************************************************
2 *
3 * PostGIS - Spatial Types for PostgreSQL
4 * http://postgis.net
5 *
6 * PostGIS 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 * PostGIS 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 PostGIS. If not, see <http://www.gnu.org/licenses/>.
18 *
19 **********************************************************************
20 *
21 * Copyright (C) 2009 Paul Ramsey <pramsey@cleverelephant.ca>
22 *
23 **********************************************************************/
24
25
26 #include "liblwgeom_internal.h"
27 #include "lwgeom_log.h"
28 #include "stringbuffer.h"
29
30 static void lwgeom_to_wkt_sb(const LWGEOM *geom, stringbuffer_t *sb, int precision, uint8_t variant);
31
32 #define buffer_size 128
33
34 /*
35 * ISO format uses both Z and M qualifiers.
36 * Extended format only uses an M qualifier for 3DM variants, where it is not
37 * clear what the third dimension represents.
38 * SFSQL format never has more than two dimensions, so no qualifiers.
39 */
dimension_qualifiers_to_wkt_sb(const LWGEOM * geom,stringbuffer_t * sb,uint8_t variant)40 static void dimension_qualifiers_to_wkt_sb(const LWGEOM *geom, stringbuffer_t *sb, uint8_t variant)
41 {
42
43 /* Extended WKT: POINTM(0 0 0) */
44 #if 0
45 if ( (variant & WKT_EXTENDED) && ! (variant & WKT_IS_CHILD) && FLAGS_GET_M(geom->flags) && (!FLAGS_GET_Z(geom->flags)) )
46 #else
47 if ( (variant & WKT_EXTENDED) && FLAGS_GET_M(geom->flags) && (!FLAGS_GET_Z(geom->flags)) )
48 #endif
49 {
50 stringbuffer_append_len(sb, "M", 1); /* "M" */
51 return;
52 }
53
54 /* ISO WKT: POINT ZM (0 0 0 0) */
55 if ( (variant & WKT_ISO) && (FLAGS_NDIMS(geom->flags) > 2) )
56 {
57 stringbuffer_append_len(sb, " ", 1);
58 if ( FLAGS_GET_Z(geom->flags) )
59 stringbuffer_append_len(sb, "Z", 1);
60 if ( FLAGS_GET_M(geom->flags) )
61 stringbuffer_append_len(sb, "M", 1);
62 stringbuffer_append_len(sb, " ", 1);
63 }
64 }
65
66 /*
67 * Write an empty token out, padding with a space if
68 * necessary.
69 */
empty_to_wkt_sb(stringbuffer_t * sb)70 static void empty_to_wkt_sb(stringbuffer_t *sb)
71 {
72 if ( ! strchr(" ,(", stringbuffer_lastchar(sb)) ) /* "EMPTY" */
73 {
74 stringbuffer_append_len(sb, " ", 1);
75 }
76 stringbuffer_append_len(sb, "EMPTY", 5);
77 }
78
79 inline static void
coordinate_to_wkt_sb(double * coords,stringbuffer_t * sb,uint32_t dimensions,int precision)80 coordinate_to_wkt_sb(double *coords, stringbuffer_t *sb, uint32_t dimensions, int precision)
81 {
82 uint32_t d = 0;
83 stringbuffer_append_double(sb, coords[d], precision);
84
85 for (d = 1; d < dimensions; d++)
86 {
87 stringbuffer_append_len(sb, " ", 1);
88 stringbuffer_append_double(sb, coords[d], precision);
89 }
90 }
91
92 /*
93 * Point array is a list of coordinates. Depending on output mode,
94 * we may suppress some dimensions. ISO and Extended formats include
95 * all dimensions. Standard OGC output only includes X/Y coordinates.
96 */
ptarray_to_wkt_sb(const POINTARRAY * ptarray,stringbuffer_t * sb,int precision,uint8_t variant)97 static void ptarray_to_wkt_sb(const POINTARRAY *ptarray, stringbuffer_t *sb, int precision, uint8_t variant)
98 {
99 /* OGC only includes X/Y */
100 uint32_t dimensions = 2;
101
102 /* ISO and extended formats include all dimensions */
103 if ( variant & ( WKT_ISO | WKT_EXTENDED ) )
104 dimensions = FLAGS_NDIMS(ptarray->flags);
105
106 stringbuffer_makeroom(sb, 2 + ((OUT_MAX_BYTES_DOUBLE + 1) * dimensions * ptarray->npoints));
107 /* Opening paren? */
108 if ( ! (variant & WKT_NO_PARENS) )
109 stringbuffer_append_len(sb, "(", 1);
110
111 /* Digits and commas */
112 if (ptarray->npoints)
113 {
114 uint32_t i = 0;
115
116 double *dbl_ptr = (double *)getPoint_internal(ptarray, i);
117 coordinate_to_wkt_sb(dbl_ptr, sb, dimensions, precision);
118
119 for (i = 1; i < ptarray->npoints; i++)
120 {
121 stringbuffer_append_len(sb, ",", 1);
122 dbl_ptr = (double *)getPoint_internal(ptarray, i);
123 coordinate_to_wkt_sb(dbl_ptr, sb, dimensions, precision);
124 }
125 }
126
127 /* Closing paren? */
128 if ( ! (variant & WKT_NO_PARENS) )
129 stringbuffer_append_len(sb, ")", 1);
130 }
131
132 /*
133 * A four-dimensional point will have different outputs depending on variant.
134 * ISO: POINT ZM (0 0 0 0)
135 * Extended: POINT(0 0 0 0)
136 * OGC: POINT(0 0)
137 * A three-dimensional m-point will have different outputs too.
138 * ISO: POINT M (0 0 0)
139 * Extended: POINTM(0 0 0)
140 * OGC: POINT(0 0)
141 */
lwpoint_to_wkt_sb(const LWPOINT * pt,stringbuffer_t * sb,int precision,uint8_t variant)142 static void lwpoint_to_wkt_sb(const LWPOINT *pt, stringbuffer_t *sb, int precision, uint8_t variant)
143 {
144 if ( ! (variant & WKT_NO_TYPE) )
145 {
146 stringbuffer_append_len(sb, "POINT", 5); /* "POINT" */
147 dimension_qualifiers_to_wkt_sb((LWGEOM*)pt, sb, variant);
148 }
149
150 if ( lwpoint_is_empty(pt) )
151 {
152 empty_to_wkt_sb(sb);
153 return;
154 }
155
156 ptarray_to_wkt_sb(pt->point, sb, precision, variant);
157 }
158
159 /*
160 * LINESTRING(0 0 0, 1 1 1)
161 */
lwline_to_wkt_sb(const LWLINE * line,stringbuffer_t * sb,int precision,uint8_t variant)162 static void lwline_to_wkt_sb(const LWLINE *line, stringbuffer_t *sb, int precision, uint8_t variant)
163 {
164 if ( ! (variant & WKT_NO_TYPE) )
165 {
166 stringbuffer_append_len(sb, "LINESTRING", 10); /* "LINESTRING" */
167 dimension_qualifiers_to_wkt_sb((LWGEOM*)line, sb, variant);
168 }
169 if ( lwline_is_empty(line) )
170 {
171 empty_to_wkt_sb(sb);
172 return;
173 }
174
175 ptarray_to_wkt_sb(line->points, sb, precision, variant);
176 }
177
178 /*
179 * POLYGON(0 0 1, 1 0 1, 1 1 1, 0 1 1, 0 0 1)
180 */
lwpoly_to_wkt_sb(const LWPOLY * poly,stringbuffer_t * sb,int precision,uint8_t variant)181 static void lwpoly_to_wkt_sb(const LWPOLY *poly, stringbuffer_t *sb, int precision, uint8_t variant)
182 {
183 uint32_t i = 0;
184 if ( ! (variant & WKT_NO_TYPE) )
185 {
186 stringbuffer_append_len(sb, "POLYGON", 7); /* "POLYGON" */
187 dimension_qualifiers_to_wkt_sb((LWGEOM*)poly, sb, variant);
188 }
189 if ( lwpoly_is_empty(poly) )
190 {
191 empty_to_wkt_sb(sb);
192 return;
193 }
194
195 stringbuffer_append_len(sb, "(", 1);
196 for ( i = 0; i < poly->nrings; i++ )
197 {
198 if ( i > 0 )
199 stringbuffer_append_len(sb, ",", 1);
200 ptarray_to_wkt_sb(poly->rings[i], sb, precision, variant);
201 }
202 stringbuffer_append_len(sb, ")", 1);
203 }
204
205 /*
206 * CIRCULARSTRING
207 */
lwcircstring_to_wkt_sb(const LWCIRCSTRING * circ,stringbuffer_t * sb,int precision,uint8_t variant)208 static void lwcircstring_to_wkt_sb(const LWCIRCSTRING *circ, stringbuffer_t *sb, int precision, uint8_t variant)
209 {
210 if ( ! (variant & WKT_NO_TYPE) )
211 {
212 stringbuffer_append_len(sb, "CIRCULARSTRING", 14); /* "CIRCULARSTRING" */
213 dimension_qualifiers_to_wkt_sb((LWGEOM*)circ, sb, variant);
214 }
215 if ( lwcircstring_is_empty(circ) )
216 {
217 empty_to_wkt_sb(sb);
218 return;
219 }
220 ptarray_to_wkt_sb(circ->points, sb, precision, variant);
221 }
222
223
224 /*
225 * Multi-points do not wrap their sub-members in parens, unlike other multi-geometries.
226 * MULTPOINT(0 0, 1 1) instead of MULTIPOINT((0 0),(1 1))
227 */
lwmpoint_to_wkt_sb(const LWMPOINT * mpoint,stringbuffer_t * sb,int precision,uint8_t variant)228 static void lwmpoint_to_wkt_sb(const LWMPOINT *mpoint, stringbuffer_t *sb, int precision, uint8_t variant)
229 {
230 uint32_t i = 0;
231 if ( ! (variant & WKT_NO_TYPE) )
232 {
233 stringbuffer_append_len(sb, "MULTIPOINT", 10); /* "MULTIPOINT" */
234 dimension_qualifiers_to_wkt_sb((LWGEOM*)mpoint, sb, variant);
235 }
236 if ( mpoint->ngeoms < 1 )
237 {
238 empty_to_wkt_sb(sb);
239 return;
240 }
241 stringbuffer_append_len(sb, "(", 1);
242 variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */
243 for ( i = 0; i < mpoint->ngeoms; i++ )
244 {
245 if ( i > 0 )
246 stringbuffer_append_len(sb, ",", 1);
247 /* We don't want type strings or parens on our subgeoms */
248 lwpoint_to_wkt_sb(mpoint->geoms[i], sb, precision, variant | WKT_NO_PARENS | WKT_NO_TYPE );
249 }
250 stringbuffer_append_len(sb, ")", 1);
251 }
252
253 /*
254 * MULTILINESTRING
255 */
lwmline_to_wkt_sb(const LWMLINE * mline,stringbuffer_t * sb,int precision,uint8_t variant)256 static void lwmline_to_wkt_sb(const LWMLINE *mline, stringbuffer_t *sb, int precision, uint8_t variant)
257 {
258 uint32_t i = 0;
259
260 if ( ! (variant & WKT_NO_TYPE) )
261 {
262 stringbuffer_append_len(sb, "MULTILINESTRING", 15); /* "MULTILINESTRING" */
263 dimension_qualifiers_to_wkt_sb((LWGEOM*)mline, sb, variant);
264 }
265 if ( mline->ngeoms < 1 )
266 {
267 empty_to_wkt_sb(sb);
268 return;
269 }
270
271 stringbuffer_append_len(sb, "(", 1);
272 variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */
273 for ( i = 0; i < mline->ngeoms; i++ )
274 {
275 if ( i > 0 )
276 stringbuffer_append_len(sb, ",", 1);
277 /* We don't want type strings on our subgeoms */
278 lwline_to_wkt_sb(mline->geoms[i], sb, precision, variant | WKT_NO_TYPE );
279 }
280 stringbuffer_append_len(sb, ")", 1);
281 }
282
283 /*
284 * MULTIPOLYGON
285 */
lwmpoly_to_wkt_sb(const LWMPOLY * mpoly,stringbuffer_t * sb,int precision,uint8_t variant)286 static void lwmpoly_to_wkt_sb(const LWMPOLY *mpoly, stringbuffer_t *sb, int precision, uint8_t variant)
287 {
288 uint32_t i = 0;
289
290 if ( ! (variant & WKT_NO_TYPE) )
291 {
292 stringbuffer_append_len(sb, "MULTIPOLYGON", 12); /* "MULTIPOLYGON" */
293 dimension_qualifiers_to_wkt_sb((LWGEOM*)mpoly, sb, variant);
294 }
295 if ( mpoly->ngeoms < 1 )
296 {
297 empty_to_wkt_sb(sb);
298 return;
299 }
300
301 stringbuffer_append_len(sb, "(", 1);
302 variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */
303 for ( i = 0; i < mpoly->ngeoms; i++ )
304 {
305 if ( i > 0 )
306 stringbuffer_append_len(sb, ",", 1);
307 /* We don't want type strings on our subgeoms */
308 lwpoly_to_wkt_sb(mpoly->geoms[i], sb, precision, variant | WKT_NO_TYPE );
309 }
310 stringbuffer_append_len(sb, ")", 1);
311 }
312
313 /*
314 * Compound curves provide type information for their curved sub-geometries
315 * but not their linestring sub-geometries.
316 * COMPOUNDCURVE((0 0, 1 1), CURVESTRING(1 1, 2 2, 3 3))
317 */
lwcompound_to_wkt_sb(const LWCOMPOUND * comp,stringbuffer_t * sb,int precision,uint8_t variant)318 static void lwcompound_to_wkt_sb(const LWCOMPOUND *comp, stringbuffer_t *sb, int precision, uint8_t variant)
319 {
320 uint32_t i = 0;
321
322 if ( ! (variant & WKT_NO_TYPE) )
323 {
324 stringbuffer_append_len(sb, "COMPOUNDCURVE", 13); /* "COMPOUNDCURVE" */
325 dimension_qualifiers_to_wkt_sb((LWGEOM*)comp, sb, variant);
326 }
327 if ( comp->ngeoms < 1 )
328 {
329 empty_to_wkt_sb(sb);
330 return;
331 }
332
333 stringbuffer_append_len(sb, "(", 1);
334 variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */
335 for ( i = 0; i < comp->ngeoms; i++ )
336 {
337 int type = comp->geoms[i]->type;
338 if ( i > 0 )
339 stringbuffer_append_len(sb, ",", 1);
340 /* Linestring subgeoms don't get type identifiers */
341 if ( type == LINETYPE )
342 {
343 lwline_to_wkt_sb((LWLINE*)comp->geoms[i], sb, precision, variant | WKT_NO_TYPE );
344 }
345 /* But circstring subgeoms *do* get type identifiers */
346 else if ( type == CIRCSTRINGTYPE )
347 {
348 lwcircstring_to_wkt_sb((LWCIRCSTRING*)comp->geoms[i], sb, precision, variant );
349 }
350 else
351 {
352 lwerror("lwcompound_to_wkt_sb: Unknown type received %d - %s", type, lwtype_name(type));
353 }
354 }
355 stringbuffer_append_len(sb, ")", 1);
356 }
357
358 /*
359 * Curve polygons provide type information for their curved rings
360 * but not their linestring rings.
361 * CURVEPOLYGON((0 0, 1 1, 0 1, 0 0), CURVESTRING(0 0, 1 1, 0 1, 0.5 1, 0 0))
362 */
lwcurvepoly_to_wkt_sb(const LWCURVEPOLY * cpoly,stringbuffer_t * sb,int precision,uint8_t variant)363 static void lwcurvepoly_to_wkt_sb(const LWCURVEPOLY *cpoly, stringbuffer_t *sb, int precision, uint8_t variant)
364 {
365 uint32_t i = 0;
366
367 if ( ! (variant & WKT_NO_TYPE) )
368 {
369 stringbuffer_append_len(sb, "CURVEPOLYGON", 12); /* "CURVEPOLYGON" */
370 dimension_qualifiers_to_wkt_sb((LWGEOM*)cpoly, sb, variant);
371 }
372 if ( cpoly->nrings < 1 )
373 {
374 empty_to_wkt_sb(sb);
375 return;
376 }
377 stringbuffer_append_len(sb, "(", 1);
378 variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */
379 for ( i = 0; i < cpoly->nrings; i++ )
380 {
381 int type = cpoly->rings[i]->type;
382 if ( i > 0 )
383 stringbuffer_append_len(sb, ",", 1);
384 switch (type)
385 {
386 case LINETYPE:
387 /* Linestring subgeoms don't get type identifiers */
388 lwline_to_wkt_sb((LWLINE*)cpoly->rings[i], sb, precision, variant | WKT_NO_TYPE );
389 break;
390 case CIRCSTRINGTYPE:
391 /* But circstring subgeoms *do* get type identifiers */
392 lwcircstring_to_wkt_sb((LWCIRCSTRING*)cpoly->rings[i], sb, precision, variant );
393 break;
394 case COMPOUNDTYPE:
395 /* And compoundcurve subgeoms *do* get type identifiers */
396 lwcompound_to_wkt_sb((LWCOMPOUND*)cpoly->rings[i], sb, precision, variant );
397 break;
398 default:
399 lwerror("lwcurvepoly_to_wkt_sb: Unknown type received %d - %s", type, lwtype_name(type));
400 }
401 }
402 stringbuffer_append_len(sb, ")", 1);
403 }
404
405
406 /*
407 * Multi-curves provide type information for their curved sub-geometries
408 * but not their linear sub-geometries.
409 * MULTICURVE((0 0, 1 1), CURVESTRING(0 0, 1 1, 2 2))
410 */
lwmcurve_to_wkt_sb(const LWMCURVE * mcurv,stringbuffer_t * sb,int precision,uint8_t variant)411 static void lwmcurve_to_wkt_sb(const LWMCURVE *mcurv, stringbuffer_t *sb, int precision, uint8_t variant)
412 {
413 uint32_t i = 0;
414
415 if ( ! (variant & WKT_NO_TYPE) )
416 {
417 stringbuffer_append_len(sb, "MULTICURVE", 10); /* "MULTICURVE" */
418 dimension_qualifiers_to_wkt_sb((LWGEOM*)mcurv, sb, variant);
419 }
420 if ( mcurv->ngeoms < 1 )
421 {
422 empty_to_wkt_sb(sb);
423 return;
424 }
425 stringbuffer_append_len(sb, "(", 1);
426 variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */
427 for ( i = 0; i < mcurv->ngeoms; i++ )
428 {
429 int type = mcurv->geoms[i]->type;
430 if ( i > 0 )
431 stringbuffer_append_len(sb, ",", 1);
432 switch (type)
433 {
434 case LINETYPE:
435 /* Linestring subgeoms don't get type identifiers */
436 lwline_to_wkt_sb((LWLINE*)mcurv->geoms[i], sb, precision, variant | WKT_NO_TYPE );
437 break;
438 case CIRCSTRINGTYPE:
439 /* But circstring subgeoms *do* get type identifiers */
440 lwcircstring_to_wkt_sb((LWCIRCSTRING*)mcurv->geoms[i], sb, precision, variant );
441 break;
442 case COMPOUNDTYPE:
443 /* And compoundcurve subgeoms *do* get type identifiers */
444 lwcompound_to_wkt_sb((LWCOMPOUND*)mcurv->geoms[i], sb, precision, variant );
445 break;
446 default:
447 lwerror("lwmcurve_to_wkt_sb: Unknown type received %d - %s", type, lwtype_name(type));
448 }
449 }
450 stringbuffer_append_len(sb, ")", 1);
451 }
452
453
454 /*
455 * Multi-surfaces provide type information for their curved sub-geometries
456 * but not their linear sub-geometries.
457 * MULTISURFACE(((0 0, 1 1, 1 0, 0 0)), CURVEPOLYGON(CURVESTRING(0 0, 1 1, 2 2, 0 1, 0 0)))
458 */
lwmsurface_to_wkt_sb(const LWMSURFACE * msurf,stringbuffer_t * sb,int precision,uint8_t variant)459 static void lwmsurface_to_wkt_sb(const LWMSURFACE *msurf, stringbuffer_t *sb, int precision, uint8_t variant)
460 {
461 uint32_t i = 0;
462
463 if ( ! (variant & WKT_NO_TYPE) )
464 {
465 stringbuffer_append_len(sb, "MULTISURFACE", 12); /* "MULTISURFACE" */
466 dimension_qualifiers_to_wkt_sb((LWGEOM*)msurf, sb, variant);
467 }
468 if ( msurf->ngeoms < 1 )
469 {
470 empty_to_wkt_sb(sb);
471 return;
472 }
473 stringbuffer_append_len(sb, "(", 1);
474 variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */
475 for ( i = 0; i < msurf->ngeoms; i++ )
476 {
477 int type = msurf->geoms[i]->type;
478 if ( i > 0 )
479 stringbuffer_append_len(sb, ",", 1);
480 switch (type)
481 {
482 case POLYGONTYPE:
483 /* Linestring subgeoms don't get type identifiers */
484 lwpoly_to_wkt_sb((LWPOLY*)msurf->geoms[i], sb, precision, variant | WKT_NO_TYPE );
485 break;
486 case CURVEPOLYTYPE:
487 /* But circstring subgeoms *do* get type identifiers */
488 lwcurvepoly_to_wkt_sb((LWCURVEPOLY*)msurf->geoms[i], sb, precision, variant);
489 break;
490 default:
491 lwerror("lwmsurface_to_wkt_sb: Unknown type received %d - %s", type, lwtype_name(type));
492 }
493 }
494 stringbuffer_append_len(sb, ")", 1);
495 }
496
497 /*
498 * Geometry collections provide type information for all their curved sub-geometries
499 * but not their linear sub-geometries.
500 * GEOMETRYCOLLECTION(POLYGON((0 0, 1 1, 1 0, 0 0)), CURVEPOLYGON(CURVESTRING(0 0, 1 1, 2 2, 0 1, 0 0)))
501 */
lwcollection_to_wkt_sb(const LWCOLLECTION * collection,stringbuffer_t * sb,int precision,uint8_t variant)502 static void lwcollection_to_wkt_sb(const LWCOLLECTION *collection, stringbuffer_t *sb, int precision, uint8_t variant)
503 {
504 uint32_t i = 0;
505
506 if ( ! (variant & WKT_NO_TYPE) )
507 {
508 stringbuffer_append_len(sb, "GEOMETRYCOLLECTION", 18); /* "GEOMETRYCOLLECTION" */
509 dimension_qualifiers_to_wkt_sb((LWGEOM*)collection, sb, variant);
510 }
511 if ( collection->ngeoms < 1 )
512 {
513 empty_to_wkt_sb(sb);
514 return;
515 }
516 stringbuffer_append_len(sb, "(", 1);
517 variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are children */
518 for ( i = 0; i < collection->ngeoms; i++ )
519 {
520 if ( i > 0 )
521 stringbuffer_append_len(sb, ",", 1);
522 lwgeom_to_wkt_sb((LWGEOM*)collection->geoms[i], sb, precision, variant );
523 }
524 stringbuffer_append_len(sb, ")", 1);
525 }
526
527 /*
528 * TRIANGLE
529 */
lwtriangle_to_wkt_sb(const LWTRIANGLE * tri,stringbuffer_t * sb,int precision,uint8_t variant)530 static void lwtriangle_to_wkt_sb(const LWTRIANGLE *tri, stringbuffer_t *sb, int precision, uint8_t variant)
531 {
532 if ( ! (variant & WKT_NO_TYPE) )
533 {
534 stringbuffer_append_len(sb, "TRIANGLE", 8); /* "TRIANGLE" */
535 dimension_qualifiers_to_wkt_sb((LWGEOM*)tri, sb, variant);
536 }
537 if ( lwtriangle_is_empty(tri) )
538 {
539 empty_to_wkt_sb(sb);
540 return;
541 }
542
543 stringbuffer_append_len(sb, "(", 1); /* Triangles have extraneous brackets */
544 ptarray_to_wkt_sb(tri->points, sb, precision, variant);
545 stringbuffer_append_len(sb, ")", 1);
546 }
547
548 /*
549 * TIN
550 */
lwtin_to_wkt_sb(const LWTIN * tin,stringbuffer_t * sb,int precision,uint8_t variant)551 static void lwtin_to_wkt_sb(const LWTIN *tin, stringbuffer_t *sb, int precision, uint8_t variant)
552 {
553 uint32_t i = 0;
554
555 if ( ! (variant & WKT_NO_TYPE) )
556 {
557 stringbuffer_append_len(sb, "TIN", 3); /* "TIN" */
558 dimension_qualifiers_to_wkt_sb((LWGEOM*)tin, sb, variant);
559 }
560 if ( tin->ngeoms < 1 )
561 {
562 empty_to_wkt_sb(sb);
563 return;
564 }
565
566 stringbuffer_append_len(sb, "(", 1);
567 for ( i = 0; i < tin->ngeoms; i++ )
568 {
569 if ( i > 0 )
570 stringbuffer_append_len(sb, ",", 1);
571 /* We don't want type strings on our subgeoms */
572 lwtriangle_to_wkt_sb(tin->geoms[i], sb, precision, variant | WKT_NO_TYPE );
573 }
574 stringbuffer_append_len(sb, ")", 1);
575 }
576
577 /*
578 * POLYHEDRALSURFACE
579 */
lwpsurface_to_wkt_sb(const LWPSURFACE * psurf,stringbuffer_t * sb,int precision,uint8_t variant)580 static void lwpsurface_to_wkt_sb(const LWPSURFACE *psurf, stringbuffer_t *sb, int precision, uint8_t variant)
581 {
582 uint32_t i = 0;
583
584 if ( ! (variant & WKT_NO_TYPE) )
585 {
586 stringbuffer_append_len(sb, "POLYHEDRALSURFACE", 17); /* "POLYHEDRALSURFACE" */
587 dimension_qualifiers_to_wkt_sb((LWGEOM*)psurf, sb, variant);
588 }
589 if ( psurf->ngeoms < 1 )
590 {
591 empty_to_wkt_sb(sb);
592 return;
593 }
594
595 variant = variant | WKT_IS_CHILD; /* Inform the sub-geometries they are childre */
596
597 stringbuffer_append_len(sb, "(", 1);
598 for ( i = 0; i < psurf->ngeoms; i++ )
599 {
600 if ( i > 0 )
601 stringbuffer_append_len(sb, ",", 1);
602 /* We don't want type strings on our subgeoms */
603 lwpoly_to_wkt_sb(psurf->geoms[i], sb, precision, variant | WKT_NO_TYPE );
604 }
605 stringbuffer_append_len(sb, ")", 1);
606 }
607
608
609 /*
610 * Generic GEOMETRY
611 */
lwgeom_to_wkt_sb(const LWGEOM * geom,stringbuffer_t * sb,int precision,uint8_t variant)612 static void lwgeom_to_wkt_sb(const LWGEOM *geom, stringbuffer_t *sb, int precision, uint8_t variant)
613 {
614 LWDEBUGF(4, "lwgeom_to_wkt_sb: type %s, hasz %d, hasm %d",
615 lwtype_name(geom->type), (geom->type),
616 FLAGS_GET_Z(geom->flags)?1:0, FLAGS_GET_M(geom->flags)?1:0);
617
618 switch (geom->type)
619 {
620 case POINTTYPE:
621 lwpoint_to_wkt_sb((LWPOINT*)geom, sb, precision, variant);
622 break;
623 case LINETYPE:
624 lwline_to_wkt_sb((LWLINE*)geom, sb, precision, variant);
625 break;
626 case POLYGONTYPE:
627 lwpoly_to_wkt_sb((LWPOLY*)geom, sb, precision, variant);
628 break;
629 case MULTIPOINTTYPE:
630 lwmpoint_to_wkt_sb((LWMPOINT*)geom, sb, precision, variant);
631 break;
632 case MULTILINETYPE:
633 lwmline_to_wkt_sb((LWMLINE*)geom, sb, precision, variant);
634 break;
635 case MULTIPOLYGONTYPE:
636 lwmpoly_to_wkt_sb((LWMPOLY*)geom, sb, precision, variant);
637 break;
638 case COLLECTIONTYPE:
639 lwcollection_to_wkt_sb((LWCOLLECTION*)geom, sb, precision, variant);
640 break;
641 case CIRCSTRINGTYPE:
642 lwcircstring_to_wkt_sb((LWCIRCSTRING*)geom, sb, precision, variant);
643 break;
644 case COMPOUNDTYPE:
645 lwcompound_to_wkt_sb((LWCOMPOUND*)geom, sb, precision, variant);
646 break;
647 case CURVEPOLYTYPE:
648 lwcurvepoly_to_wkt_sb((LWCURVEPOLY*)geom, sb, precision, variant);
649 break;
650 case MULTICURVETYPE:
651 lwmcurve_to_wkt_sb((LWMCURVE*)geom, sb, precision, variant);
652 break;
653 case MULTISURFACETYPE:
654 lwmsurface_to_wkt_sb((LWMSURFACE*)geom, sb, precision, variant);
655 break;
656 case TRIANGLETYPE:
657 lwtriangle_to_wkt_sb((LWTRIANGLE*)geom, sb, precision, variant);
658 break;
659 case TINTYPE:
660 lwtin_to_wkt_sb((LWTIN*)geom, sb, precision, variant);
661 break;
662 case POLYHEDRALSURFACETYPE:
663 lwpsurface_to_wkt_sb((LWPSURFACE*)geom, sb, precision, variant);
664 break;
665 default:
666 lwerror("lwgeom_to_wkt_sb: Type %d - %s unsupported.",
667 geom->type, lwtype_name(geom->type));
668 }
669 }
670
671 static stringbuffer_t *
lwgeom_to_wkt_internal(const LWGEOM * geom,uint8_t variant,int precision)672 lwgeom_to_wkt_internal(const LWGEOM *geom, uint8_t variant, int precision)
673 {
674 stringbuffer_t *sb;
675 if ( geom == NULL )
676 return NULL;
677 sb = stringbuffer_create();
678 /* Extended mode starts with an "SRID=" section for geoms that have one */
679 if ( (variant & WKT_EXTENDED) && lwgeom_has_srid(geom) )
680 {
681 stringbuffer_aprintf(sb, "SRID=%d;", geom->srid);
682 }
683 lwgeom_to_wkt_sb(geom, sb, precision, variant);
684 if ( stringbuffer_getstring(sb) == NULL )
685 {
686 lwerror("Uh oh");
687 return NULL;
688 }
689 return sb;
690 }
691
692 /**
693 * WKT emitter function. Allocates a new *char and fills it with the WKT
694 * representation. If size_out is not NULL, it will be set to the size of the
695 * allocated *char.
696 *
697 * @param variant Bitmasked value, accepts one of WKT_ISO, WKT_SFSQL,
698 * WKT_EXTENDED.
699 * @param precision Maximal number of digits after comma in the output doubles.
700 * @param size_out If supplied, will return the size of the returned string,
701 * including the null terminator.
702 */
703 char *
lwgeom_to_wkt(const LWGEOM * geom,uint8_t variant,int precision,size_t * size_out)704 lwgeom_to_wkt(const LWGEOM *geom, uint8_t variant, int precision, size_t *size_out)
705 {
706 stringbuffer_t *sb = lwgeom_to_wkt_internal(geom, variant, precision);
707 if (!sb)
708 return NULL;
709 char *str = stringbuffer_getstringcopy(sb);
710 if ( size_out )
711 *size_out = stringbuffer_getlength(sb) + 1;
712 stringbuffer_destroy(sb);
713 return str;
714 }
715
716 lwvarlena_t *
lwgeom_to_wkt_varlena(const LWGEOM * geom,uint8_t variant,int precision)717 lwgeom_to_wkt_varlena(const LWGEOM *geom, uint8_t variant, int precision)
718 {
719 stringbuffer_t *sb = lwgeom_to_wkt_internal(geom, variant, precision);
720 if (!sb)
721 return NULL;
722 lwvarlena_t *output = stringbuffer_getvarlenacopy(sb);
723 stringbuffer_destroy(sb);
724 return output;
725 }
726