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