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 2009 - 2010 Oslandia
22  *
23  **********************************************************************/
24 
25 
26 /**
27 * @file GML input routines.
28 * Ability to parse GML geometry fragment and to return an LWGEOM
29 * or an error message.
30 *
31 * Implement ISO SQL/MM ST_GMLToSQL method
32 * Cf: ISO 13249-3:2009 -> 5.1.50 (p 134)
33 *
34 * GML versions supported:
35 *  - Triangle, Tin and TriangulatedSurface,
36 *    and PolyhedralSurface elements
37 *  - GML 3.2.1 Namespace
38 *  - GML 3.1.1 Simple Features profile SF-2
39 *    (with backward compatibility to GML 3.1.0 and 3.0.0)
40 *  - GML 2.1.2
41 * Cf: <http://www.opengeospatial.org/standards/gml>
42 *
43 * NOTA: this code doesn't (yet ?) support SQL/MM curves
44 *
45 * Written by Olivier Courtin - Oslandia
46 *
47 **********************************************************************/
48 
49 
50 #include <libxml/tree.h>
51 #include <libxml/parser.h>
52 #include <libxml/xpath.h>
53 #include <libxml/xpathInternals.h>
54 
55 #include "postgres.h"
56 #include "executor/spi.h"
57 #include "utils/builtins.h"
58 
59 #include "../postgis_config.h"
60 #include "lwgeom_pg.h"
61 #include "liblwgeom.h"
62 #include "lwgeom_transform.h"
63 
64 
65 Datum geom_from_gml(PG_FUNCTION_ARGS);
66 static LWGEOM *lwgeom_from_gml(const char *wkt, int xml_size);
67 static LWGEOM* parse_gml(xmlNodePtr xnode, bool *hasz, int *root_srid);
68 
69 typedef struct struct_gmlSrs
70 {
71 	int32_t srid;
72 	bool reverse_axis;
73 }
74 gmlSrs;
75 
76 #define XLINK_NS	((char *) "http://www.w3.org/1999/xlink")
77 #define GML_NS		((char *) "http://www.opengis.net/gml")
78 #define GML32_NS	((char *) "http://www.opengis.net/gml/3.2")
79 
80 
81 
gml_lwpgerror(char * msg,int error_code)82 static void gml_lwpgerror(char *msg, __attribute__((__unused__)) int error_code)
83 {
84         POSTGIS_DEBUGF(3, "ST_GeomFromGML ERROR %i", error_code);
85         lwpgerror("%s", msg);
86 }
87 
88 /**
89  * Ability to parse GML geometry fragment and to return an LWGEOM
90  * or an error message.
91  *
92  * ISO SQL/MM define two error messages:
93 *  Cf: ISO 13249-3:2009 -> 5.1.50 (p 134)
94  *  - invalid GML representation
95  *  - unknown spatial reference system
96  */
97 PG_FUNCTION_INFO_V1(geom_from_gml);
geom_from_gml(PG_FUNCTION_ARGS)98 Datum geom_from_gml(PG_FUNCTION_ARGS)
99 {
100 	GSERIALIZED *geom;
101 	text *xml_input;
102 	LWGEOM *lwgeom;
103 	char *xml;
104 	int root_srid=SRID_UNKNOWN;
105 	int xml_size;
106 
107 	/* Get the GML stream */
108 	if (PG_ARGISNULL(0)) PG_RETURN_NULL();
109 	xml_input = PG_GETARG_TEXT_P(0);
110 	xml = text_to_cstring(xml_input);
111 	xml_size = VARSIZE_ANY_EXHDR(xml_input);
112 
113 	/* Zero for undefined */
114 	root_srid = PG_GETARG_INT32(1);
115 
116 #if POSTGIS_PROJ_VERSION < 60
117 	/* Internally lwgeom_from_gml calls gml_reproject_pa which, for PROJ before 6, called GetProj4String.
118 	 * That function requires access to spatial_ref_sys, so in order to have it ready we need to ensure
119 	 * the internal cache is initialized
120 	 */
121 	postgis_initialize_cache(fcinfo);
122 #endif
123 	lwgeom = lwgeom_from_gml(xml, xml_size);
124 	if ( root_srid != SRID_UNKNOWN )
125 		lwgeom->srid = root_srid;
126 
127 	geom = geometry_serialize(lwgeom);
128 	lwgeom_free(lwgeom);
129 
130 	PG_RETURN_POINTER(geom);
131 }
132 
133 
134 /**
135  * Return false if current element namespace is not a GML one
136  * Return true otherwise.
137  */
is_gml_namespace(xmlNodePtr xnode,bool is_strict)138 static bool is_gml_namespace(xmlNodePtr xnode, bool is_strict)
139 {
140 	xmlNsPtr *ns, *p;
141 
142 	ns = xmlGetNsList(xnode->doc, xnode);
143 	/*
144 	 * If no namespace is available we could return true anyway
145 	 * (because we work only on GML fragment, we don't want to
146 	 *  'oblige' to add namespace on the geometry root node)
147 	 */
148 	if (ns == NULL) { return !is_strict; }
149 
150 	/*
151 	 * Handle namespaces:
152 	 *  - http://www.opengis.net/gml      (GML 3.1.1 and priors)
153 	 *  - http://www.opengis.net/gml/3.2  (GML 3.2.1)
154 	 */
155 	for (p=ns ; *p ; p++)
156 	{
157                 if ((*p)->href == NULL || (*p)->prefix == NULL ||
158                      xnode->ns == NULL || xnode->ns->prefix == NULL) continue;
159 
160 		if (!xmlStrcmp(xnode->ns->prefix, (*p)->prefix))
161                 {
162 			if (    !strcmp((char *) (*p)->href, GML_NS)
163                              || !strcmp((char *) (*p)->href, GML32_NS))
164 			{
165 				xmlFree(ns);
166                                 return true;
167 			} else {
168                                 xmlFree(ns);
169                                 return false;
170                         }
171 		}
172 	}
173 
174 	xmlFree(ns);
175 	return !is_strict; /* Same reason here to not return false */
176 }
177 
178 
179 /**
180  * Retrieve a GML property from a node or NULL otherwise
181  * Respect namespaces if presents in the node element
182  */
gmlGetProp(xmlNodePtr xnode,xmlChar * prop)183 static xmlChar *gmlGetProp(xmlNodePtr xnode, xmlChar *prop)
184 {
185 	xmlChar *value;
186 
187 	if (!is_gml_namespace(xnode, true))
188 		return xmlGetProp(xnode, prop);
189 	/*
190 	 * Handle namespaces:
191 	 *  - http://www.opengis.net/gml      (GML 3.1.1 and priors)
192 	 *  - http://www.opengis.net/gml/3.2  (GML 3.2.1)
193 	 */
194 	value = xmlGetNsProp(xnode, prop, (xmlChar *) GML_NS);
195 	if (value == NULL) value = xmlGetNsProp(xnode, prop, (xmlChar *) GML32_NS);
196 
197 	/* In last case try without explicit namespace */
198 	if (value == NULL) value = xmlGetNoNsProp(xnode, prop);
199 
200 	return value;
201 }
202 
203 
204 /**
205  * Return true if current node contains a simple XLink
206  * Return false otherwise.
207  */
is_xlink(xmlNodePtr node)208 static bool is_xlink(xmlNodePtr node)
209 {
210 	xmlChar *prop;
211 
212 	prop = xmlGetNsProp(node, (xmlChar *)"type", (xmlChar *) XLINK_NS);
213 	if (prop == NULL) return false;
214 	if (strcmp((char *) prop, "simple"))
215 	{
216 		xmlFree(prop);
217 		return false;
218 	}
219 
220 	prop = xmlGetNsProp(node, (xmlChar *)"href", (xmlChar *) XLINK_NS);
221 	if (prop == NULL) return false;
222 	if (prop[0] != '#')
223 	{
224 		xmlFree(prop);
225 		return false;
226 	}
227 	xmlFree(prop);
228 
229 	return true;
230 }
231 
232 
233 /**
234  * Return a xmlNodePtr on a node referenced by a XLink or NULL otherwise
235  */
get_xlink_node(xmlNodePtr xnode)236 static xmlNodePtr get_xlink_node(xmlNodePtr xnode)
237 {
238 	char *id;
239 	xmlNsPtr *ns, *n;
240 	xmlXPathContext *ctx;
241 	xmlXPathObject *xpath;
242 	xmlNodePtr node, ret_node;
243 	xmlChar *href, *p, *node_id;
244 
245 	href = xmlGetNsProp(xnode, (xmlChar *)"href", (xmlChar *) XLINK_NS);
246 	id = lwalloc((xmlStrlen(xnode->ns->prefix) * 2 + xmlStrlen(xnode->name)
247 	              + xmlStrlen(href) + sizeof("//:[@:id='']") + 1));
248 	p = href;
249 	p++; /* ignore '#' first char */
250 
251 	/* XPath pattern look like: //gml:point[@gml:id='p1'] */
252 	sprintf(id, "//%s:%s[@%s:id='%s']", 	(char *) xnode->ns->prefix,
253 	        (char *) xnode->name,
254 	        (char *) xnode->ns->prefix,
255 	        (char *) p);
256 
257 	ctx = xmlXPathNewContext(xnode->doc);
258 	if (ctx == NULL)
259 	{
260 		xmlFree(href);
261 		lwfree(id);
262 		return NULL;
263 	}
264 
265 	/* Handle namespaces */
266 	ns = xmlGetNsList(xnode->doc, xnode);
267 	for (n=ns ; *n; n++) xmlXPathRegisterNs(ctx, (*n)->prefix, (*n)->href);
268 	xmlFree(ns);
269 
270 	/* Execute XPath expression */
271 	xpath = xmlXPathEvalExpression((xmlChar *) id, ctx);
272 	lwfree(id);
273 	if (xpath == NULL || xpath->nodesetval == NULL || xpath->nodesetval->nodeNr != 1)
274 	{
275 		xmlFree(href);
276 		xmlXPathFreeObject(xpath);
277 		xmlXPathFreeContext(ctx);
278 		return NULL;
279 	}
280 	ret_node = xpath->nodesetval->nodeTab[0];
281 	xmlXPathFreeObject(xpath);
282 	xmlXPathFreeContext(ctx);
283 
284 	/* Protection against circular calls */
285 	for (node = xnode ; node != NULL ; node = node->parent)
286 	{
287 		if (node->type != XML_ELEMENT_NODE) continue;
288 		node_id = gmlGetProp(node, (xmlChar *) "id");
289 		if (node_id != NULL)
290 		{
291 			if (!xmlStrcmp(node_id, p))
292 				gml_lwpgerror("invalid GML representation", 2);
293 			xmlFree(node_id);
294 		}
295 	}
296 
297 	xmlFree(href);
298 	return ret_node;
299 }
300 
301 
302 
303 /**
304  * Use Proj to reproject a given POINTARRAY
305  */
306 
307 #if POSTGIS_PROJ_VERSION < 60
308 
309 static POINTARRAY *
gml_reproject_pa(POINTARRAY * pa,int32_t srid_in,int32_t srid_out)310 gml_reproject_pa(POINTARRAY *pa, int32_t srid_in, int32_t srid_out)
311 {
312 	PJ pj;
313 	char *text_in, *text_out;
314 
315 	if (srid_in == SRID_UNKNOWN) return pa; /* nothing to do */
316 	if (srid_out == SRID_UNKNOWN) gml_lwpgerror("invalid GML representation", 3);
317 
318 	text_in = GetProj4String(srid_in);
319 	text_out = GetProj4String(srid_out);
320 
321 	pj.pj_from = projpj_from_string(text_in);
322 	pj.pj_to = projpj_from_string(text_out);
323 
324 	lwfree(text_in);
325 	lwfree(text_out);
326 
327 	if ( ptarray_transform(pa, &pj) == LW_FAILURE )
328 	{
329 		elog(ERROR, "gml_reproject_pa: reprojection failed");
330 	}
331 
332 	pj_free(pj.pj_from);
333 	pj_free(pj.pj_to);
334 
335 	return pa;
336 }
337 #else
338 /*
339  * TODO: rework GML projection handling to skip the spatial_ref_sys
340  * lookups, and use the Proj 6+ EPSG catalogue and built-in SRID
341  * lookups directly. Drop this ugly hack.
342  */
343 static POINTARRAY *
gml_reproject_pa(POINTARRAY * pa,int32_t epsg_in,int32_t epsg_out)344 gml_reproject_pa(POINTARRAY *pa, int32_t epsg_in, int32_t epsg_out)
345 {
346 	PJ *pj;
347 	LWPROJ *lwp;
348 	char text_in[16];
349 	char text_out[16];
350 
351 	if (epsg_in == SRID_UNKNOWN)
352 		return pa; /* nothing to do */
353 
354 	if (epsg_out == SRID_UNKNOWN)
355 	{
356 		gml_lwpgerror("invalid GML representation", 3);
357 		return NULL;
358 	}
359 
360 	snprintf(text_in, 16, "EPSG:%d", epsg_in);
361 	snprintf(text_out, 16, "EPSG:%d", epsg_out);
362 	pj = proj_create_crs_to_crs(NULL, text_in, text_out, NULL);
363 
364 	lwp = lwproj_from_PJ(pj, LW_FALSE);
365 	if (!lwp)
366 	{
367 		proj_destroy(pj);
368 		gml_lwpgerror("Could not create LWPROJ*", 57);
369 		return NULL;
370 	}
371 
372 	if (ptarray_transform(pa, lwp) == LW_FAILURE)
373 	{
374 		proj_destroy(pj);
375 		elog(ERROR, "gml_reproject_pa: reprojection failed");
376 		return NULL;
377 	}
378 	proj_destroy(pj);
379 	pfree(lwp);
380 
381 	return pa;
382 }
383 #endif /* POSTGIS_PROJ_VERSION */
384 
385 
386 /**
387  * Return 1 if given srid is planar (0 otherwise, i.e geocentric srid)
388  * Return -1 if srid is not in spatial_ref_sys
389  */
390 static int
gml_is_srid_planar(int32_t srid)391 gml_is_srid_planar(int32_t srid)
392 {
393 	char *result;
394 	char query[256];
395 	int is_planar, err;
396 
397 	if (SPI_OK_CONNECT != SPI_connect ())
398 		lwpgerror("gml_is_srid_planar: could not connect to SPI manager");
399 
400 	/* A way to find if this projection is planar or geocentric */
401 	sprintf(query, "SELECT position('+units=m ' in proj4text) \
402                         FROM spatial_ref_sys WHERE srid='%d'", srid);
403 
404 	err = SPI_exec(query, 1);
405 	if (err < 0) lwpgerror("gml_is_srid_planar: error executing query %d", err);
406 
407 	/* No entry in spatial_ref_sys */
408 	if (SPI_processed <= 0)
409 	{
410 		SPI_finish();
411 		return -1;
412 	}
413 
414 	result = SPI_getvalue(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1);
415 	is_planar = atoi(result);
416 	SPI_finish();
417 
418 	return is_planar;
419 }
420 
421 
422 /**
423  * Parse gml srsName attribute
424  */
parse_gml_srs(xmlNodePtr xnode,gmlSrs * srs)425 static void parse_gml_srs(xmlNodePtr xnode, gmlSrs *srs)
426 {
427 	char *p;
428 	int is_planar;
429 	xmlNodePtr node;
430 	xmlChar *srsname;
431 	bool latlon = false;
432 	char sep = ':';
433 
434 	node = xnode;
435 	srsname = gmlGetProp(node, (xmlChar *) "srsName");
436 	/*printf("srsname %s\n",srsname);*/
437 	if (!srsname)
438 	{
439 		if (node->parent == NULL)
440 		{
441 			srs->srid = SRID_UNKNOWN;
442 			srs->reverse_axis = false;
443 			return;
444 		}
445 		parse_gml_srs(node->parent, srs);
446 	}
447 	else
448 	{
449 		/* Severals	srsName formats are available...
450 		 *  cf WFS 1.1.0 -> 9.2 (p36)
451 		 *  cf ISO 19142:2009 -> 7.9.2.4.4 (p34)
452 		 *  cf RFC 5165 <http://tools.ietf.org/html/rfc5165>
453 		 *  cf CITE WFS-1.1 (GetFeature-tc17.2)
454 		 */
455 
456 		/* SRS pattern like:   	EPSG:4326
457 		  			urn:EPSG:geographicCRS:4326
458 		  		  	urn:ogc:def:crs:EPSG:4326
459 		 			urn:ogc:def:crs:EPSG::4326
460 		  			urn:ogc:def:crs:EPSG:6.6:4326
461 		   			urn:x-ogc:def:crs:EPSG:6.6:4326
462 					http://www.opengis.net/gml/srs/epsg.xml#4326
463 					http://www.epsg.org/6.11.2/4326
464 		*/
465 
466 		if (!strncmp((char *) srsname, "EPSG:", 5))
467 		{
468 			sep = ':';
469 			latlon = false;
470 		}
471 		else if (!strncmp((char *) srsname, "urn:ogc:def:crs:EPSG:", 21)
472 		         || !strncmp((char *) srsname, "urn:x-ogc:def:crs:EPSG:", 23)
473 		         || !strncmp((char *) srsname, "urn:EPSG:geographicCRS:", 23))
474 		{
475 			sep = ':';
476 			latlon = true;
477 		}
478 		else if (!strncmp((char *) srsname,
479 		                  "http://www.opengis.net/gml/srs/epsg.xml#", 40))
480 		{
481 			sep = '#';
482 			latlon = false;
483 		}
484 		else gml_lwpgerror("unknown spatial reference system", 4);
485 
486 		/* retrieve the last ':' or '#' char */
487 		for (p = (char *) srsname ; *p ; p++);
488 		for (--p ; *p != sep ; p--)
489 			if (!isdigit(*p)) gml_lwpgerror("unknown spatial reference system", 5);
490 
491 		srs->srid = atoi(++p);
492 
493 		/* Check into spatial_ref_sys that this SRID really exist */
494 		is_planar = gml_is_srid_planar(srs->srid);
495 		if (srs->srid == SRID_UNKNOWN || is_planar == -1)
496 			gml_lwpgerror("unknown spatial reference system", 6);
497 
498 		/* About lat/lon issue, Cf: http://tinyurl.com/yjpr55z */
499 		srs->reverse_axis = !is_planar && latlon;
500 
501 		xmlFree(srsname);
502 		return;
503 	}
504 }
505 
506 
507 /**
508  * Parse a string supposed to be a double
509  */
parse_gml_double(char * d,bool space_before,bool space_after)510 static double parse_gml_double(char *d, bool space_before, bool space_after)
511 {
512 	char *p;
513 	int st;
514 	enum states
515 	{
516 		INIT     	= 0,
517 		NEED_DIG  	= 1,
518 		DIG	  	= 2,
519 		NEED_DIG_DEC 	= 3,
520 		DIG_DEC 	= 4,
521 		EXP	 	= 5,
522 		NEED_DIG_EXP 	= 6,
523 		DIG_EXP 	= 7,
524 		END 		= 8
525 	};
526 
527 	/*
528 	 * Double pattern
529 	 * [-|\+]?[0-9]+(\.)?([0-9]+)?([Ee](\+|-)?[0-9]+)?
530 	 * We could also meet spaces before and/or after
531 	 * this pattern upon parameters
532 	 */
533 
534 	if (space_before) while (isspace(*d)) d++;
535 	for (st = INIT, p = d ; *p ; p++)
536 	{
537 
538 		if (isdigit(*p))
539 		{
540 			if (st == INIT || st == NEED_DIG) 		st = DIG;
541 			else if (st == NEED_DIG_DEC) 			st = DIG_DEC;
542 			else if (st == NEED_DIG_EXP || st == EXP) 	st = DIG_EXP;
543 			else if (st == DIG || st == DIG_DEC || st == DIG_EXP);
544 			else gml_lwpgerror("invalid GML representation", 7);
545 		}
546 		else if (*p == '.')
547 		{
548 			if      (st == DIG) 				st = NEED_DIG_DEC;
549 			else    gml_lwpgerror("invalid GML representation", 8);
550 		}
551 		else if (*p == '-' || *p == '+')
552 		{
553 			if      (st == INIT) 				st = NEED_DIG;
554 			else if (st == EXP) 				st = NEED_DIG_EXP;
555 			else    gml_lwpgerror("invalid GML representation", 9);
556 		}
557 		else if (*p == 'e' || *p == 'E')
558 		{
559 			if      (st == DIG || st == DIG_DEC) 		st = EXP;
560 			else    gml_lwpgerror("invalid GML representation", 10);
561 		}
562 		else if (isspace(*p))
563 		{
564 			if (!space_after) gml_lwpgerror("invalid GML representation", 11);
565 			if (st == DIG || st == DIG_DEC || st == DIG_EXP)st = END;
566 			else if (st == NEED_DIG_DEC)			st = END;
567 			else if (st == END);
568 			else    gml_lwpgerror("invalid GML representation", 12);
569 		}
570 		else  gml_lwpgerror("invalid GML representation", 13);
571 	}
572 
573 	if (st != DIG && st != NEED_DIG_DEC && st != DIG_DEC && st != DIG_EXP && st != END)
574 		gml_lwpgerror("invalid GML representation", 14);
575 
576 	return atof(d);
577 }
578 
579 
580 /**
581  * Parse gml:coordinates
582  */
parse_gml_coordinates(xmlNodePtr xnode,bool * hasz)583 static POINTARRAY* parse_gml_coordinates(xmlNodePtr xnode, bool *hasz)
584 {
585 	xmlChar *gml_coord, *gml_ts, *gml_cs, *gml_dec;
586 	char cs, ts, dec;
587 	POINTARRAY *dpa;
588 	int gml_dims;
589 	char *p, *q;
590 	bool digit;
591 	POINT4D pt;
592 
593 	/* We begin to retrieve coordinates string */
594 	gml_coord = xmlNodeGetContent(xnode);
595 	p = (char *) gml_coord;
596 
597 	/* Default GML coordinates pattern: 	x1,y1 x2,y2
598 	 * 					x1,y1,z1 x2,y2,z2
599 	 *
600 	 * Cf GML 2.1.2 -> 4.3.1 (p18)
601 	 */
602 
603 	/* Retrieve separator between coordinates tuples */
604 	gml_ts = gmlGetProp(xnode, (xmlChar *) "ts");
605 	if (gml_ts == NULL) ts = ' ';
606 	else
607 	{
608 		if (xmlStrlen(gml_ts) > 1 || isdigit(gml_ts[0]))
609 			gml_lwpgerror("invalid GML representation", 15);
610 		ts = gml_ts[0];
611 		xmlFree(gml_ts);
612 	}
613 
614 	/* Retrieve separator between each coordinate */
615 	gml_cs = gmlGetProp(xnode, (xmlChar *) "cs");
616 	if (gml_cs == NULL) cs = ',';
617 	else
618 	{
619 		if (xmlStrlen(gml_cs) > 1 || isdigit(gml_cs[0]))
620 			gml_lwpgerror("invalid GML representation", 16);
621 		cs = gml_cs[0];
622 		xmlFree(gml_cs);
623 	}
624 
625 	/* Retrieve decimal separator */
626 	gml_dec = gmlGetProp(xnode, (xmlChar *) "decimal");
627 	if (gml_dec == NULL) dec = '.';
628 	else
629 	{
630 		if (xmlStrlen(gml_dec) > 1 || isdigit(gml_dec[0]))
631 			gml_lwpgerror("invalid GML representation", 17);
632 		dec = gml_dec[0];
633 		xmlFree(gml_dec);
634 	}
635 
636 	if (cs == ts || cs == dec || ts == dec)
637 		gml_lwpgerror("invalid GML representation", 18);
638 
639 	/* HasZ, !HasM, 1 Point */
640 	dpa = ptarray_construct_empty(1, 0, 1);
641 
642 	while (isspace(*p)) p++;		/* Eat extra whitespaces if any */
643 	for (q = p, gml_dims=0, digit = false ; *p ; p++)
644 	{
645 
646 		if (isdigit(*p)) digit = true;	/* One state parser */
647 
648 		/* Coordinate Separator */
649 		if (*p == cs)
650 		{
651 			*p = '\0';
652 			gml_dims++;
653 
654 			if (*(p+1) == '\0') gml_lwpgerror("invalid GML representation", 19);
655 
656 			if 	(gml_dims == 1) pt.x = parse_gml_double(q, false, true);
657 			else if (gml_dims == 2) pt.y = parse_gml_double(q, false, true);
658 
659 			q = p+1;
660 
661 			/* Tuple Separator (or end string) */
662 		}
663 		else if (digit && (*p == ts || *(p+1) == '\0'))
664 		{
665 			if (*p == ts) *p = '\0';
666 			gml_dims++;
667 
668 			if (gml_dims < 2 || gml_dims > 3)
669 				gml_lwpgerror("invalid GML representation", 20);
670 
671 			if (gml_dims == 3)
672 				pt.z = parse_gml_double(q, false, true);
673 			else
674 			{
675 				pt.y = parse_gml_double(q, false, true);
676 				*hasz = false;
677 			}
678 
679 			ptarray_append_point(dpa, &pt, LW_TRUE);
680 			digit = false;
681 
682 			q = p+1;
683 			gml_dims = 0;
684 
685 			/* Need to put standard decimal separator to atof handle */
686 		}
687 		else if (*p == dec && dec != '.') *p = '.';
688 	}
689 
690 	xmlFree(gml_coord);
691 
692 	return dpa; /* ptarray_clone_deep(dpa); */
693 }
694 
695 
696 /**
697  * Parse gml:coord
698  */
parse_gml_coord(xmlNodePtr xnode,bool * hasz)699 static POINTARRAY* parse_gml_coord(xmlNodePtr xnode, bool *hasz)
700 {
701 	xmlNodePtr xyz;
702 	POINTARRAY *dpa;
703 	bool x,y,z;
704 	xmlChar *c;
705 	POINT4D p;
706 
707 	/* HasZ?, !HasM, 1 Point */
708 	dpa = ptarray_construct_empty(1, 0, 1);
709 
710 	x = y = z = false;
711 	for (xyz = xnode->children ; xyz != NULL ; xyz = xyz->next)
712 	{
713 		if (xyz->type != XML_ELEMENT_NODE) continue;
714 		if (!is_gml_namespace(xyz, false)) continue;
715 
716 		if (!strcmp((char *) xyz->name, "X"))
717 		{
718 			if (x) gml_lwpgerror("invalid GML representation", 21);
719 			c = xmlNodeGetContent(xyz);
720 			p.x = parse_gml_double((char *) c, true, true);
721 			x = true;
722 			xmlFree(c);
723 		}
724 		else  if (!strcmp((char *) xyz->name, "Y"))
725 		{
726 			if (y) gml_lwpgerror("invalid GML representation", 22);
727 			c = xmlNodeGetContent(xyz);
728 			p.y = parse_gml_double((char *) c, true, true);
729 			y = true;
730 			xmlFree(c);
731 		}
732 		else if (!strcmp((char *) xyz->name, "Z"))
733 		{
734 			if (z) gml_lwpgerror("invalid GML representation", 23);
735 			c = xmlNodeGetContent(xyz);
736 			p.z = parse_gml_double((char *) c, true, true);
737 			z = true;
738 			xmlFree(c);
739 		}
740 	}
741 	/* Check dimension consistancy */
742 	if (!x || !y) gml_lwpgerror("invalid GML representation", 24);
743 	if (!z) *hasz = false;
744 
745 	ptarray_append_point(dpa, &p, LW_FALSE);
746 
747 	return dpa; /* ptarray_clone_deep(dpa); */
748 }
749 
750 
751 /**
752  * Parse gml:pos
753  */
parse_gml_pos(xmlNodePtr xnode,bool * hasz)754 static POINTARRAY* parse_gml_pos(xmlNodePtr xnode, bool *hasz)
755 {
756 	xmlChar *dimension, *gmlpos;
757 	int dim, gml_dim;
758 	POINTARRAY *dpa;
759 	char *pos, *p;
760 	bool digit;
761 	POINT4D pt = {0, 0, 0, 0};
762 
763 	/* HasZ, !HasM, 1 Point */
764 	dpa = ptarray_construct_empty(1, 0, 1);
765 
766     dimension = gmlGetProp(xnode, (xmlChar *) "srsDimension");
767     if (dimension == NULL) /* in GML 3.0.0 it was dimension */
768         dimension = gmlGetProp(xnode, (xmlChar *) "dimension");
769     if (dimension == NULL) dim = 2;	/* We assume that we are in 2D */
770     else
771     {
772         dim = atoi((char *) dimension);
773         xmlFree(dimension);
774         if (dim < 2 || dim > 3)
775             gml_lwpgerror("invalid GML representation", 25);
776     }
777     if (dim == 2) *hasz = false;
778 
779     /* We retrieve gml:pos string */
780     gmlpos = xmlNodeGetContent(xnode);
781     pos = (char *) gmlpos;
782     while (isspace(*pos)) pos++;	/* Eat extra whitespaces if any */
783 
784     /* gml:pos pattern: 	x1 y1
785         * 			x1 y1 z1
786         */
787     for (p=pos, gml_dim=0, digit=false ; *pos ; pos++)
788     {
789         if (isdigit(*pos)) digit = true;
790         if (digit && (*pos == ' ' || *(pos+1) == '\0'))
791         {
792             if (*pos == ' ') *pos = '\0';
793             gml_dim++;
794             if 	(gml_dim == 1)
795                 pt.x = parse_gml_double(p, true, true);
796             else if (gml_dim == 2)
797                 pt.y = parse_gml_double(p, true, true);
798             else if (gml_dim == 3)
799                 pt.z = parse_gml_double(p, true, true);
800 
801             p = pos+1;
802             digit = false;
803         }
804     }
805     xmlFree(gmlpos);
806 
807     /* Test again coherent dimensions on each coord */
808     if (gml_dim == 2) *hasz = false;
809     if (gml_dim < 2 || gml_dim > 3 || gml_dim != dim)
810         gml_lwpgerror("invalid GML representation", 26);
811 
812     ptarray_append_point(dpa, &pt, LW_FALSE);
813 
814 	return dpa; /* ptarray_clone_deep(dpa); */
815 }
816 
817 
818 /**
819  * Parse gml:posList
820  */
parse_gml_poslist(xmlNodePtr xnode,bool * hasz)821 static POINTARRAY* parse_gml_poslist(xmlNodePtr xnode, bool *hasz)
822 {
823 	xmlChar *dimension, *gmlposlist;
824 	char *poslist, *p;
825 	int dim, gml_dim;
826 	POINTARRAY *dpa;
827 	POINT4D pt = {0, 0, 0, 0};
828 	bool digit;
829 
830 	/* Retrieve gml:srsDimension attribute if any */
831 	dimension = gmlGetProp(xnode, (xmlChar *) "srsDimension");
832 	if (dimension == NULL) /* in GML 3.0.0 it was dimension */
833 		dimension = gmlGetProp(xnode, (xmlChar *) "dimension");
834 	if (dimension == NULL) dim = 2;	/* We assume that we are in common 2D */
835 	else
836 	{
837 		dim = atoi((char *) dimension);
838 		xmlFree(dimension);
839 		if (dim < 2 || dim > 3) gml_lwpgerror("invalid GML representation", 27);
840 	}
841 	if (dim == 2) *hasz = false;
842 
843 	/* Retrieve gml:posList string */
844 	gmlposlist = xmlNodeGetContent(xnode);
845 	poslist = (char *) gmlposlist;
846 
847 	/* HasZ?, !HasM, 1 point */
848 	dpa = ptarray_construct_empty(1, 0, 1);
849 
850 	/* gml:posList pattern: 	x1 y1 x2 y2
851 	 * 				x1 y1 z1 x2 y2 z2
852 	 */
853 	while (isspace(*poslist)) poslist++;	/* Eat extra whitespaces if any */
854 	for (p=poslist, gml_dim=0, digit=false ; *poslist ; poslist++)
855 	{
856 		if (isdigit(*poslist)) digit = true;
857 		if (digit && (*poslist == ' ' || *(poslist+1) == '\0'))
858 		{
859 			if (*poslist == ' ') *poslist = '\0';
860 
861 			gml_dim++;
862 			if 	(gml_dim == 1) pt.x = parse_gml_double(p, true, true);
863 			else if (gml_dim == 2) pt.y = parse_gml_double(p, true, true);
864 			else if (gml_dim == 3) pt.z = parse_gml_double(p, true, true);
865 
866 			if (gml_dim == dim)
867 			{
868 				ptarray_append_point(dpa, &pt, LW_FALSE);
869 				pt.x = pt.y = pt.z = pt.m = 0.0;
870 				gml_dim = 0;
871 			}
872 			else if (*(poslist+1) == '\0')
873 				gml_lwpgerror("invalid GML representation", 28);
874 
875 			p = poslist+1;
876 			digit = false;
877 		}
878 	}
879 
880 	xmlFree(gmlposlist);
881 
882 	return dpa; /* ptarray_clone_deep(dpa); */
883 }
884 
885 
886 /**
887  * Parse data coordinates
888  *
889  * There's several ways to encode data coordinates, who could be mixed
890  * inside a single geometrie:
891  *  - gml:pos element
892  *  - gml:posList element
893  *  - gml:pointProperty
894  *  - gml:pointRep 					(deprecated in 3.1.0)
895  *  - gml:coordinate element with tuples string inside 	(deprecated in 3.1.0)
896  *  - gml:coord elements with X,Y(,Z) nested elements 	(deprecated in 3.0.0)
897  */
parse_gml_data(xmlNodePtr xnode,bool * hasz,int * root_srid)898 static POINTARRAY* parse_gml_data(xmlNodePtr xnode, bool *hasz, int *root_srid)
899 {
900 	POINTARRAY *pa = 0, *tmp_pa = 0;
901 	xmlNodePtr xa, xb;
902 	gmlSrs srs;
903 	bool found;
904 
905 	pa = NULL;
906 
907 	for (xa = xnode ; xa != NULL ; xa = xa->next)
908 	{
909 		if (xa->type != XML_ELEMENT_NODE) continue;
910 		if (!is_gml_namespace(xa, false)) continue;
911 		if (xa->name == NULL) continue;
912 
913 		if (!strcmp((char *) xa->name, "pos"))
914 		{
915 			tmp_pa = parse_gml_pos(xa, hasz);
916 			if (pa == NULL) pa = tmp_pa;
917 			else pa = ptarray_merge(pa, tmp_pa);
918 
919 		}
920 		else if (!strcmp((char *) xa->name, "posList"))
921 		{
922 			tmp_pa = parse_gml_poslist(xa, hasz);
923 			if (pa == NULL) pa = tmp_pa;
924 			else pa = ptarray_merge(pa, tmp_pa);
925 
926 		}
927 		else if (!strcmp((char *) xa->name, "coordinates"))
928 		{
929 			tmp_pa = parse_gml_coordinates(xa, hasz);
930 			if (pa == NULL) pa = tmp_pa;
931 			else pa = ptarray_merge(pa, tmp_pa);
932 
933 		}
934 		else if (!strcmp((char *) xa->name, "coord"))
935 		{
936 			tmp_pa = parse_gml_coord(xa, hasz);
937 			if (pa == NULL) pa = tmp_pa;
938 			else pa = ptarray_merge(pa, tmp_pa);
939 
940 		}
941 		else if (!strcmp((char *) xa->name, "pointRep") ||
942 		         !strcmp((char *) xa->name, "pointProperty"))
943 		{
944 
945 			found = false;
946 			for (xb = xa->children ; xb != NULL ; xb = xb->next)
947 			{
948 				if (xb->type != XML_ELEMENT_NODE) continue;
949 				if (!is_gml_namespace(xb, false)) continue;
950 				if (!strcmp((char *) xb->name, "Point"))
951 				{
952 					found = true;
953 					break;
954 				}
955 			}
956 			if (!found || xb == NULL)
957 				gml_lwpgerror("invalid GML representation", 29);
958 
959 			if (is_xlink(xb)) xb = get_xlink_node(xb);
960 			if (xb == NULL || xb->children == NULL)
961 				gml_lwpgerror("invalid GML representation", 30);
962 
963 			tmp_pa = parse_gml_data(xb->children, hasz, root_srid);
964 			if (tmp_pa->npoints != 1)
965 				gml_lwpgerror("invalid GML representation", 31);
966 
967 			parse_gml_srs(xb, &srs);
968 			if (srs.reverse_axis) tmp_pa = ptarray_flip_coordinates(tmp_pa);
969 			if (*root_srid == SRID_UNKNOWN) *root_srid = srs.srid;
970 			else if (srs.srid != *root_srid)
971 				gml_reproject_pa(tmp_pa, srs.srid, *root_srid);
972 			if (pa == NULL) pa = tmp_pa;
973 			else pa = ptarray_merge(pa, tmp_pa);
974 		}
975 	}
976 
977 	if (pa == NULL) gml_lwpgerror("invalid GML representation", 32);
978 
979 	return pa;
980 }
981 
982 
983 /**
984  * Parse GML point (2.1.2, 3.1.1)
985  */
parse_gml_point(xmlNodePtr xnode,bool * hasz,int * root_srid)986 static LWGEOM* parse_gml_point(xmlNodePtr xnode, bool *hasz, int *root_srid)
987 {
988 	gmlSrs srs;
989 	LWGEOM *geom;
990 	POINTARRAY *pa;
991 
992 	if (is_xlink(xnode)) xnode = get_xlink_node(xnode);
993 
994 	if (xnode->children == NULL)
995 		return lwpoint_as_lwgeom(lwpoint_construct_empty(*root_srid, 0, 0));
996 
997 	pa = parse_gml_data(xnode->children, hasz, root_srid);
998 	if (pa->npoints != 1) gml_lwpgerror("invalid GML representation", 34);
999 
1000 	parse_gml_srs(xnode, &srs);
1001 	if (srs.reverse_axis) pa = ptarray_flip_coordinates(pa);
1002 	if (!*root_srid)
1003 	{
1004 		*root_srid = srs.srid;
1005 		geom = (LWGEOM *) lwpoint_construct(*root_srid, NULL, pa);
1006 	}
1007 	else
1008 	{
1009 		if (srs.srid != *root_srid)
1010 			gml_reproject_pa(pa, srs.srid, *root_srid);
1011 		geom = (LWGEOM *) lwpoint_construct(SRID_UNKNOWN, NULL, pa);
1012 	}
1013 
1014 	return geom;
1015 }
1016 
1017 
1018 /**
1019  * Parse GML lineString (2.1.2, 3.1.1)
1020  */
parse_gml_line(xmlNodePtr xnode,bool * hasz,int * root_srid)1021 static LWGEOM* parse_gml_line(xmlNodePtr xnode, bool *hasz, int *root_srid)
1022 {
1023 	gmlSrs srs;
1024 	LWGEOM *geom;
1025 	POINTARRAY *pa;
1026 
1027 	if (is_xlink(xnode)) xnode = get_xlink_node(xnode);
1028 
1029 	if (xnode->children == NULL)
1030 		return lwline_as_lwgeom(lwline_construct_empty(*root_srid, 0, 0));
1031 
1032 	pa = parse_gml_data(xnode->children, hasz, root_srid);
1033 	if (pa->npoints < 2) gml_lwpgerror("invalid GML representation", 36);
1034 
1035 	parse_gml_srs(xnode, &srs);
1036 	if (srs.reverse_axis) pa = ptarray_flip_coordinates(pa);
1037 	if (!*root_srid)
1038 	{
1039 		*root_srid = srs.srid;
1040 		geom = (LWGEOM *) lwline_construct(*root_srid, NULL, pa);
1041 	}
1042 	else
1043 	{
1044 		if (srs.srid != *root_srid)
1045 			gml_reproject_pa(pa, srs.srid, *root_srid);
1046 		geom = (LWGEOM *) lwline_construct(SRID_UNKNOWN, NULL, pa);
1047 	}
1048 
1049 	return geom;
1050 }
1051 
1052 
1053 /**
1054  * Parse GML Curve (3.1.1)
1055  */
parse_gml_curve(xmlNodePtr xnode,bool * hasz,int * root_srid)1056 static LWGEOM* parse_gml_curve(xmlNodePtr xnode, bool *hasz, int *root_srid)
1057 {
1058 	xmlNodePtr xa;
1059 	size_t lss;
1060 	bool found=false;
1061 	gmlSrs srs;
1062 	LWGEOM *geom=NULL;
1063 	POINTARRAY *pa=NULL;
1064 	POINTARRAY **ppa=NULL;
1065 	uint32 npoints=0;
1066 	xmlChar *interpolation=NULL;
1067 
1068 	if (is_xlink(xnode)) xnode = get_xlink_node(xnode);
1069 
1070 	/* Looking for gml:segments */
1071 	for (xa = xnode->children ; xa != NULL ; xa = xa->next)
1072 	{
1073 		if (xa->type != XML_ELEMENT_NODE) continue;
1074 		if (!is_gml_namespace(xa, false)) continue;
1075 		if (!strcmp((char *) xa->name, "segments"))
1076 		{
1077 			found = true;
1078 			break;
1079 		}
1080 	}
1081 	if (!found) gml_lwpgerror("invalid GML representation", 37);
1082 
1083 	ppa = (POINTARRAY**) lwalloc(sizeof(POINTARRAY*));
1084 
1085 	/* Processing each gml:LineStringSegment */
1086 	for (xa = xa->children, lss=0; xa != NULL ; xa = xa->next)
1087 	{
1088 		if (xa->type != XML_ELEMENT_NODE) continue;
1089 		if (!is_gml_namespace(xa, false)) continue;
1090 		if (strcmp((char *) xa->name, "LineStringSegment")) continue;
1091 
1092 		/* GML SF is restricted to linear interpolation  */
1093 		interpolation = gmlGetProp(xa, (xmlChar *) "interpolation");
1094 		if (interpolation != NULL)
1095 		{
1096 			if (strcmp((char *) interpolation, "linear"))
1097 				gml_lwpgerror("invalid GML representation", 38);
1098 			xmlFree(interpolation);
1099 		}
1100 
1101 		if (lss > 0) ppa = (POINTARRAY**) lwrealloc((POINTARRAY *) ppa,
1102 			                   sizeof(POINTARRAY*) * (lss + 1));
1103 
1104 		ppa[lss] = parse_gml_data(xa->children, hasz, root_srid);
1105 		npoints += ppa[lss]->npoints;
1106 		if (ppa[lss]->npoints < 2)
1107 			gml_lwpgerror("invalid GML representation", 39);
1108 		lss++;
1109 	}
1110 	if (lss == 0) gml_lwpgerror("invalid GML representation", 40);
1111 
1112 	/* Most common case, a single segment */
1113 	if (lss == 1) pa = ppa[0];
1114 
1115 	if (lss > 1)
1116 	{
1117 		/*
1118 		 * "The curve segments are connected to one another, with the end point
1119 		 *  of each segment except the last being the start point of the next
1120 		 *  segment"  from  ISO 19107:2003 -> 6.3.16.1 (p43)
1121 		 *
1122 		 * So we must aggregate all the segments into a single one and avoid
1123 		 * to copy the redundant points
1124 		 */
1125 		size_t cp_point_size = sizeof(POINT3D); /* All internals are done with 3D */
1126 		size_t final_point_size = *hasz ? sizeof(POINT3D) : sizeof(POINT2D);
1127 		pa = ptarray_construct(1, 0, npoints - lss + 1);
1128 
1129 		/* Copy the first linestring fully */
1130 		memcpy(getPoint_internal(pa, 0), getPoint_internal(ppa[0], 0), cp_point_size * (ppa[0]->npoints));
1131 		npoints = ppa[0]->npoints;
1132 		lwfree(ppa[0]);
1133 
1134 		/* For the rest of linestrings, ensure the first point matches the
1135 		 * last point of the previous one, and copy all points except the
1136 		 * first one (since it'd be repeated)
1137 		 */
1138 		for (size_t i = 1; i < lss; i++)
1139 		{
1140 			if (memcmp(getPoint_internal(pa, npoints - 1), getPoint_internal(ppa[i], 0), final_point_size))
1141 				gml_lwpgerror("invalid GML representation", 41);
1142 
1143 			memcpy(getPoint_internal(pa, npoints),
1144 			       getPoint_internal(ppa[i], 1),
1145 			       cp_point_size * (ppa[i]->npoints - 1));
1146 
1147 			npoints += ppa[i]->npoints - 1;
1148 			lwfree(ppa[i]);
1149 		}
1150 	}
1151 
1152 	lwfree(ppa);
1153 
1154 	parse_gml_srs(xnode, &srs);
1155 	if (srs.reverse_axis) pa = ptarray_flip_coordinates(pa);
1156 	if (srs.srid != *root_srid && *root_srid != SRID_UNKNOWN)
1157 		gml_reproject_pa(pa, srs.srid, *root_srid);
1158 	geom = (LWGEOM *) lwline_construct(*root_srid, NULL, pa);
1159 
1160 	return geom;
1161 }
1162 
1163 
1164 /**
1165  * Parse GML LinearRing (3.1.1)
1166  */
parse_gml_linearring(xmlNodePtr xnode,bool * hasz,int * root_srid)1167 static LWGEOM* parse_gml_linearring(xmlNodePtr xnode, bool *hasz, int *root_srid)
1168 {
1169 	gmlSrs srs;
1170 	LWGEOM *geom;
1171 	POINTARRAY **ppa = NULL;
1172 
1173 	if (is_xlink(xnode)) xnode = get_xlink_node(xnode);
1174 	parse_gml_srs(xnode, &srs);
1175 
1176 	ppa = (POINTARRAY**) lwalloc(sizeof(POINTARRAY*));
1177 	ppa[0] = parse_gml_data(xnode->children, hasz, root_srid);
1178 
1179 	if (ppa[0]->npoints < 4
1180             || (!*hasz && !ptarray_is_closed_2d(ppa[0]))
1181             ||  (*hasz && !ptarray_is_closed_3d(ppa[0])))
1182 	    gml_lwpgerror("invalid GML representation", 42);
1183 
1184 	if (srs.reverse_axis)
1185 		ppa[0] = ptarray_flip_coordinates(ppa[0]);
1186 
1187 	if (srs.srid != *root_srid && *root_srid != SRID_UNKNOWN)
1188 		gml_reproject_pa(ppa[0], srs.srid, *root_srid);
1189 
1190 	geom = (LWGEOM *) lwpoly_construct(*root_srid, NULL, 1, ppa);
1191 
1192 	return geom;
1193 }
1194 
1195 
1196 /**
1197  * Parse GML Polygon (2.1.2, 3.1.1)
1198  */
parse_gml_polygon(xmlNodePtr xnode,bool * hasz,int * root_srid)1199 static LWGEOM* parse_gml_polygon(xmlNodePtr xnode, bool *hasz, int *root_srid)
1200 {
1201 	gmlSrs srs;
1202 	int i, ring;
1203 	LWGEOM *geom;
1204 	xmlNodePtr xa, xb;
1205 	POINTARRAY **ppa = NULL;
1206 
1207 	if (is_xlink(xnode)) xnode = get_xlink_node(xnode);
1208 
1209 	if (xnode->children == NULL)
1210 		return lwpoly_as_lwgeom(lwpoly_construct_empty(*root_srid, 0, 0));
1211 
1212 	parse_gml_srs(xnode, &srs);
1213 
1214 	for (xa = xnode->children ; xa != NULL ; xa = xa->next)
1215 	{
1216 		/* Polygon/outerBoundaryIs -> GML 2.1.2 */
1217 		/* Polygon/exterior        -> GML 3.1.1 */
1218 		if (xa->type != XML_ELEMENT_NODE) continue;
1219 		if (!is_gml_namespace(xa, false)) continue;
1220 		if  (strcmp((char *) xa->name, "outerBoundaryIs") &&
1221 		        strcmp((char *) xa->name, "exterior")) continue;
1222 
1223 		for (xb = xa->children ; xb != NULL ; xb = xb->next)
1224 		{
1225 			if (xb->type != XML_ELEMENT_NODE) continue;
1226 			if (!is_gml_namespace(xb, false)) continue;
1227 			if (strcmp((char *) xb->name, "LinearRing")) continue;
1228 
1229 			ppa = (POINTARRAY**) lwalloc(sizeof(POINTARRAY*));
1230 			ppa[0] = parse_gml_data(xb->children, hasz, root_srid);
1231 
1232 			if (ppa[0]->npoints < 4
1233 			        || (!*hasz && !ptarray_is_closed_2d(ppa[0]))
1234 			        ||  (*hasz && !ptarray_is_closed_3d(ppa[0])))
1235 				gml_lwpgerror("invalid GML representation", 43);
1236 
1237 			if (srs.reverse_axis) ppa[0] = ptarray_flip_coordinates(ppa[0]);
1238 		}
1239 	}
1240 
1241 	/* Found an <exterior> or <outerBoundaryIs> but no rings?!? We're outa here! */
1242 	if ( ! ppa )
1243 		gml_lwpgerror("invalid GML representation", 43);
1244 
1245 	for (ring=1, xa = xnode->children ; xa != NULL ; xa = xa->next)
1246 	{
1247 		/* Polygon/innerBoundaryIs -> GML 2.1.2 */
1248 		/* Polygon/interior        -> GML 3.1.1 */
1249 		if (xa->type != XML_ELEMENT_NODE) continue;
1250 		if (!is_gml_namespace(xa, false)) continue;
1251 		if  (strcmp((char *) xa->name, "innerBoundaryIs") &&
1252 		        strcmp((char *) xa->name, "interior")) continue;
1253 
1254 		for (xb = xa->children ; xb != NULL ; xb = xb->next)
1255 		{
1256 			if (xb->type != XML_ELEMENT_NODE) continue;
1257 			if (!is_gml_namespace(xb, false)) continue;
1258 			if (strcmp((char *) xb->name, "LinearRing")) continue;
1259 
1260 			ppa = (POINTARRAY**) lwrealloc((POINTARRAY *) ppa,
1261 			                               sizeof(POINTARRAY*) * (ring + 1));
1262 			ppa[ring] = parse_gml_data(xb->children, hasz, root_srid);
1263 
1264 			if (ppa[ring]->npoints < 4
1265 			        || (!*hasz && !ptarray_is_closed_2d(ppa[ring]))
1266 			        ||  (*hasz && !ptarray_is_closed_3d(ppa[ring])))
1267 				gml_lwpgerror("invalid GML representation", 43);
1268 
1269 			if (srs.reverse_axis) ppa[ring] = ptarray_flip_coordinates(ppa[ring]);
1270 			ring++;
1271 		}
1272 	}
1273 
1274 	/* Exterior Ring is mandatory */
1275 	if (ppa == NULL || ppa[0] == NULL) gml_lwpgerror("invalid GML representation", 44);
1276 
1277 	if (srs.srid != *root_srid && *root_srid != SRID_UNKNOWN)
1278 	{
1279 		for (i=0 ; i < ring ; i++)
1280 			gml_reproject_pa(ppa[i], srs.srid, *root_srid);
1281 	}
1282 	geom = (LWGEOM *) lwpoly_construct(*root_srid, NULL, ring, ppa);
1283 
1284 	return geom;
1285 }
1286 
1287 
1288 /**
1289  * Parse GML Triangle (3.1.1)
1290  */
parse_gml_triangle(xmlNodePtr xnode,bool * hasz,int * root_srid)1291 static LWGEOM* parse_gml_triangle(xmlNodePtr xnode, bool *hasz, int *root_srid)
1292 {
1293 	gmlSrs srs;
1294 	LWGEOM *geom;
1295 	xmlNodePtr xa, xb;
1296 	POINTARRAY *pa = NULL;
1297 	xmlChar *interpolation=NULL;
1298 
1299 	if (is_xlink(xnode)) xnode = get_xlink_node(xnode);
1300 
1301 	if (xnode->children == NULL)
1302 		return lwtriangle_as_lwgeom(lwtriangle_construct_empty(*root_srid, 0, 0));
1303 
1304 	/* GML SF is restricted to planar interpolation
1305 	       NOTA: I know Triangle is not part of SF, but
1306 	       we have to be consistent with other surfaces */
1307 	interpolation = gmlGetProp(xnode, (xmlChar *) "interpolation");
1308 	if (interpolation != NULL)
1309 	{
1310 		if (strcmp((char *) interpolation, "planar"))
1311 			gml_lwpgerror("invalid GML representation", 45);
1312 		xmlFree(interpolation);
1313 	}
1314 
1315 	parse_gml_srs(xnode, &srs);
1316 
1317 	for (xa = xnode->children ; xa != NULL ; xa = xa->next)
1318 	{
1319 		/* Triangle/exterior */
1320 		if (xa->type != XML_ELEMENT_NODE) continue;
1321 		if (!is_gml_namespace(xa, false)) continue;
1322 		if (strcmp((char *) xa->name, "exterior")) continue;
1323 
1324 		for (xb = xa->children ; xb != NULL ; xb = xb->next)
1325 		{
1326 			/* Triangle/exterior/LinearRing */
1327 			if (xb->type != XML_ELEMENT_NODE) continue;
1328 			if (!is_gml_namespace(xb, false)) continue;
1329 			if (strcmp((char *) xb->name, "LinearRing")) continue;
1330 
1331 			pa = (POINTARRAY*) lwalloc(sizeof(POINTARRAY));
1332 			pa = parse_gml_data(xb->children, hasz, root_srid);
1333 
1334 			if (pa->npoints != 4
1335 			        || (!*hasz && !ptarray_is_closed_2d(pa))
1336 			        ||  (*hasz && !ptarray_is_closed_3d(pa)))
1337 				gml_lwpgerror("invalid GML representation", 46);
1338 
1339 			if (srs.reverse_axis) pa = ptarray_flip_coordinates(pa);
1340 		}
1341 	}
1342 
1343 	/* Exterior Ring is mandatory */
1344 	if (pa == NULL) gml_lwpgerror("invalid GML representation", 47);
1345 
1346 	if (srs.srid != *root_srid && *root_srid != SRID_UNKNOWN)
1347 		gml_reproject_pa(pa, srs.srid, *root_srid);
1348 
1349 	geom = (LWGEOM *) lwtriangle_construct(*root_srid, NULL, pa);
1350 
1351 	return geom;
1352 }
1353 
1354 
1355 /**
1356  * Parse GML PolygonPatch (3.1.1)
1357  */
parse_gml_patch(xmlNodePtr xnode,bool * hasz,int * root_srid)1358 static LWGEOM* parse_gml_patch(xmlNodePtr xnode, bool *hasz, int *root_srid)
1359 {
1360 	xmlChar *interpolation=NULL;
1361 	POINTARRAY **ppa=NULL;
1362 	LWGEOM *geom=NULL;
1363 	xmlNodePtr xa, xb;
1364 	int i, ring=0;
1365 	gmlSrs srs;
1366 
1367 	/* PolygonPatch */
1368 	if (strcmp((char *) xnode->name, "PolygonPatch"))
1369 		gml_lwpgerror("invalid GML representation", 48);
1370 
1371 	/* GML SF is restricted to planar interpolation  */
1372 	interpolation = gmlGetProp(xnode, (xmlChar *) "interpolation");
1373 	if (interpolation != NULL)
1374 	{
1375 		if (strcmp((char *) interpolation, "planar"))
1376 			gml_lwpgerror("invalid GML representation", 48);
1377 		xmlFree(interpolation);
1378 	}
1379 
1380 	parse_gml_srs(xnode, &srs);
1381 
1382 	/* PolygonPatch/exterior */
1383 	for (xa = xnode->children ; xa != NULL ; xa = xa->next)
1384 	{
1385 		if (!is_gml_namespace(xa, false)) continue;
1386 		if (strcmp((char *) xa->name, "exterior")) continue;
1387 
1388 		/* PolygonPatch/exterior/LinearRing */
1389 		for (xb = xa->children ; xb != NULL ; xb = xb->next)
1390 		{
1391 			if (xb->type != XML_ELEMENT_NODE) continue;
1392 			if (!is_gml_namespace(xb, false)) continue;
1393 			if (strcmp((char *) xb->name, "LinearRing")) continue;
1394 
1395 			ppa = (POINTARRAY**) lwalloc(sizeof(POINTARRAY*));
1396 			ppa[0] = parse_gml_data(xb->children, hasz, root_srid);
1397 
1398 			if (ppa[0]->npoints < 4
1399 			        || (!*hasz && !ptarray_is_closed_2d(ppa[0]))
1400 			        ||  (*hasz && !ptarray_is_closed_3d(ppa[0])))
1401 				gml_lwpgerror("invalid GML representation", 48);
1402 
1403 			if (srs.reverse_axis)
1404 				ppa[0] = ptarray_flip_coordinates(ppa[0]);
1405 		}
1406 	}
1407 
1408 	/* Interior but no Exterior ! */
1409 	if ( ! ppa )
1410 	 	gml_lwpgerror("invalid GML representation", 48);
1411 
1412 	/* PolygonPatch/interior */
1413 	for (ring=1, xa = xnode->children ; xa != NULL ; xa = xa->next)
1414 	{
1415 		if (xa->type != XML_ELEMENT_NODE) continue;
1416 		if (!is_gml_namespace(xa, false)) continue;
1417 		if (strcmp((char *) xa->name, "interior")) continue;
1418 
1419 		/* PolygonPatch/interior/LinearRing */
1420 		for (xb = xa->children ; xb != NULL ; xb = xb->next)
1421 		{
1422 			if (xb->type != XML_ELEMENT_NODE) continue;
1423 			if (strcmp((char *) xb->name, "LinearRing")) continue;
1424 
1425 			ppa = (POINTARRAY**) lwrealloc((POINTARRAY *) ppa,
1426 			                               sizeof(POINTARRAY*) * (ring + 1));
1427 			ppa[ring] = parse_gml_data(xb->children, hasz, root_srid);
1428 
1429 			if (ppa[ring]->npoints < 4
1430 			        || (!*hasz && !ptarray_is_closed_2d(ppa[ring]))
1431 			        || ( *hasz && !ptarray_is_closed_3d(ppa[ring])))
1432 				gml_lwpgerror("invalid GML representation", 49);
1433 
1434 			if (srs.reverse_axis)
1435 				ppa[ring] = ptarray_flip_coordinates(ppa[ring]);
1436 
1437 			ring++;
1438 		}
1439 	}
1440 
1441 	/* Exterior Ring is mandatory */
1442 	if (ppa == NULL || ppa[0] == NULL) gml_lwpgerror("invalid GML representation", 50);
1443 
1444 	if (srs.srid != *root_srid && *root_srid != SRID_UNKNOWN)
1445 	{
1446 		for (i=0 ; i < ring ; i++)
1447 			gml_reproject_pa(ppa[i], srs.srid, *root_srid);
1448 	}
1449 	geom = (LWGEOM *) lwpoly_construct(*root_srid, NULL, ring, ppa);
1450 
1451 	return geom;
1452 }
1453 
1454 
1455 /**
1456  * Parse GML Surface (3.1.1)
1457  */
parse_gml_surface(xmlNodePtr xnode,bool * hasz,int * root_srid)1458 static LWGEOM* parse_gml_surface(xmlNodePtr xnode, bool *hasz, int *root_srid)
1459 {
1460 	xmlNodePtr xa;
1461 	int patch;
1462 	LWGEOM *geom=NULL;
1463 	bool found=false;
1464 
1465 	if (is_xlink(xnode)) xnode = get_xlink_node(xnode);
1466 
1467 	/* Looking for gml:patches */
1468 	for (xa = xnode->children ; xa != NULL ; xa = xa->next)
1469 	{
1470 		if (xa->type != XML_ELEMENT_NODE) continue;
1471 		if (!is_gml_namespace(xa, false)) continue;
1472 		if (!strcmp((char *) xa->name, "patches"))
1473 		{
1474 			found = true;
1475 			break;
1476 		}
1477 	}
1478 	if (!found) gml_lwpgerror("invalid GML representation", 51);
1479 
1480 	/* Processing gml:PolygonPatch */
1481 	for (patch=0, xa = xa->children ; xa != NULL ; xa = xa->next)
1482 	{
1483 		if (xa->type != XML_ELEMENT_NODE) continue;
1484 		if (!is_gml_namespace(xa, false)) continue;
1485 		if (strcmp((char *) xa->name, "PolygonPatch")) continue;
1486 		patch++;
1487 
1488 		/* SQL/MM define ST_CurvePolygon as a single patch only,
1489 		   cf ISO 13249-3:2009 -> 4.2.9 (p27) */
1490 		if (patch > 1) gml_lwpgerror("invalid GML representation", 52);
1491 
1492 		geom = parse_gml_patch(xa, hasz, root_srid);
1493 	}
1494 
1495 	if (!patch) gml_lwpgerror("invalid GML representation", 53);
1496 
1497 	return geom;
1498 }
1499 
1500 
1501 /**
1502  * Parse GML Tin (and TriangulatedSurface) (3.1.1)
1503  *
1504  * TODO handle also Tin attributes:
1505  * - stopLines
1506  * - breakLines
1507  * - maxLength
1508  * - position
1509  */
parse_gml_tin(xmlNodePtr xnode,bool * hasz,int * root_srid)1510 static LWGEOM* parse_gml_tin(xmlNodePtr xnode, bool *hasz, int *root_srid)
1511 {
1512 	gmlSrs srs;
1513 	xmlNodePtr xa;
1514 	LWGEOM *geom=NULL;
1515 	bool found=false;
1516 
1517 	if (is_xlink(xnode)) xnode = get_xlink_node(xnode);
1518 
1519 	parse_gml_srs(xnode, &srs);
1520 	if (*root_srid == SRID_UNKNOWN && srs.srid != SRID_UNKNOWN)
1521 		*root_srid = srs.srid;
1522 
1523 	geom = (LWGEOM *)lwcollection_construct_empty(TINTYPE, *root_srid, 1, 0);
1524 
1525 	if (xnode->children == NULL)
1526 		return geom;
1527 
1528 	/* Looking for gml:patches or gml:trianglePatches */
1529 	for (xa = xnode->children ; xa != NULL ; xa = xa->next)
1530 	{
1531 		if (xa->type != XML_ELEMENT_NODE) continue;
1532 		if (!is_gml_namespace(xa, false)) continue;
1533 		if (!strcmp((char *) xa->name, "patches") ||
1534 		        !strcmp((char *) xa->name, "trianglePatches"))
1535 		{
1536 			found = true;
1537 			break;
1538 		}
1539 	}
1540 	if (!found) return geom; /* empty one */
1541 
1542 	/* Processing each gml:Triangle */
1543 	for (xa = xa->children ; xa != NULL ; xa = xa->next)
1544 	{
1545 		if (xa->type != XML_ELEMENT_NODE) continue;
1546 		if (!is_gml_namespace(xa, false)) continue;
1547 		if (strcmp((char *) xa->name, "Triangle")) continue;
1548 
1549 		if (xa->children != NULL)
1550 			geom = (LWGEOM*) lwtin_add_lwtriangle((LWTIN *) geom,
1551 			       (LWTRIANGLE *) parse_gml_triangle(xa, hasz, root_srid));
1552 	}
1553 
1554 	return geom;
1555 }
1556 
1557 
1558 /**
1559  * Parse gml:MultiPoint (2.1.2, 3.1.1)
1560  */
parse_gml_mpoint(xmlNodePtr xnode,bool * hasz,int * root_srid)1561 static LWGEOM* parse_gml_mpoint(xmlNodePtr xnode, bool *hasz, int *root_srid)
1562 {
1563 	gmlSrs srs;
1564 	xmlNodePtr xa, xb;
1565 	LWGEOM *geom = NULL;
1566 
1567 	if (is_xlink(xnode)) xnode = get_xlink_node(xnode);
1568 
1569 	parse_gml_srs(xnode, &srs);
1570 	if (*root_srid == SRID_UNKNOWN && srs.srid != SRID_UNKNOWN)
1571 		*root_srid = srs.srid;
1572 
1573 	geom = (LWGEOM *)lwcollection_construct_empty(MULTIPOINTTYPE, *root_srid, 1, 0);
1574 
1575 	if (xnode->children == NULL)
1576 		return geom;
1577 
1578 	for (xa = xnode->children ; xa != NULL ; xa = xa->next)
1579 	{
1580 		/* MultiPoint/pointMember */
1581 		if (xa->type != XML_ELEMENT_NODE) continue;
1582 		if (!is_gml_namespace(xa, false)) continue;
1583 		if (!strcmp((char *) xa->name, "pointMembers"))
1584 		{
1585 			for (xb = xa->children ; xb != NULL ; xb = xb->next)
1586 			{
1587 				geom = (LWGEOM*)lwmpoint_add_lwpoint(
1588 				    (LWMPOINT*)geom,
1589 				    (LWPOINT*)parse_gml(xb, hasz, root_srid));
1590 			}
1591 		}
1592 		else if (!strcmp((char *) xa->name, "pointMember"))
1593 		{
1594 			if (xa->children != NULL)
1595 				geom = (LWGEOM*)lwmpoint_add_lwpoint((LWMPOINT*)geom,
1596 			                                         (LWPOINT*)parse_gml(xa->children, hasz, root_srid));
1597 		}
1598 	}
1599 
1600 	return geom;
1601 }
1602 
1603 
1604 /**
1605  * Parse gml:MultiLineString (2.1.2, 3.1.1)
1606  */
parse_gml_mline(xmlNodePtr xnode,bool * hasz,int * root_srid)1607 static LWGEOM* parse_gml_mline(xmlNodePtr xnode, bool *hasz, int *root_srid)
1608 {
1609 	gmlSrs srs;
1610 	xmlNodePtr xa;
1611 	LWGEOM *geom = NULL;
1612 
1613 	if (is_xlink(xnode)) xnode = get_xlink_node(xnode);
1614 
1615 	parse_gml_srs(xnode, &srs);
1616 	if (*root_srid == SRID_UNKNOWN && srs.srid != SRID_UNKNOWN)
1617 		*root_srid = srs.srid;
1618 
1619 	geom = (LWGEOM *)lwcollection_construct_empty(MULTILINETYPE, *root_srid, 1, 0);
1620 
1621 	if (xnode->children == NULL)
1622 		return geom;
1623 
1624 	for (xa = xnode->children ; xa != NULL ; xa = xa->next)
1625 	{
1626 		/* MultiLineString/lineStringMember */
1627 		if (xa->type != XML_ELEMENT_NODE) continue;
1628 		if (!is_gml_namespace(xa, false)) continue;
1629 		if (strcmp((char *) xa->name, "lineStringMember")) continue;
1630 		if (xa->children != NULL)
1631 			geom = (LWGEOM*)lwmline_add_lwline((LWMLINE*)geom,
1632 			                                   (LWLINE*)parse_gml(xa->children, hasz, root_srid));
1633 	}
1634 
1635 	return geom;
1636 }
1637 
1638 
1639 /**
1640  * Parse GML MultiCurve (3.1.1)
1641  */
parse_gml_mcurve(xmlNodePtr xnode,bool * hasz,int * root_srid)1642 static LWGEOM* parse_gml_mcurve(xmlNodePtr xnode, bool *hasz, int *root_srid)
1643 {
1644 	gmlSrs srs;
1645 	xmlNodePtr xa, xb;
1646 	LWGEOM *geom = NULL;
1647 
1648 	if (is_xlink(xnode)) xnode = get_xlink_node(xnode);
1649 
1650 	parse_gml_srs(xnode, &srs);
1651 	if (*root_srid == SRID_UNKNOWN && srs.srid != SRID_UNKNOWN)
1652 		*root_srid = srs.srid;
1653 
1654 	geom = (LWGEOM *)lwcollection_construct_empty(MULTILINETYPE, *root_srid, 1, 0);
1655 
1656 	if (xnode->children == NULL)
1657 		return geom;
1658 
1659 	for (xa = xnode->children ; xa != NULL ; xa = xa->next)
1660 	{
1661 
1662 		/* MultiCurve/curveMember */
1663 		if (xa->type != XML_ELEMENT_NODE) continue;
1664 		if (!is_gml_namespace(xa, false)) continue;
1665 		if (!strcmp((char *) xa->name, "curveMembers"))
1666 		{
1667 			for (xb = xa->children ; xb != NULL ; xb = xb->next)
1668 			{
1669 				if (xb != NULL)
1670 					geom = (LWGEOM*)lwmline_add_lwline((LWMLINE*)geom,
1671 				                                       (LWLINE*)parse_gml(xb, hasz, root_srid));
1672 			}
1673 		}
1674 		else if (!strcmp((char *) xa->name, "curveMember"))
1675 		{
1676 			if (xa->children != NULL)
1677 				geom = (LWGEOM*)lwmline_add_lwline((LWMLINE*)geom,
1678 				                                   (LWLINE*)parse_gml(xa->children, hasz, root_srid));
1679 		}
1680 	}
1681 
1682 	return geom;
1683 }
1684 
1685 
1686 /**
1687  * Parse GML MultiPolygon (2.1.2, 3.1.1)
1688  */
parse_gml_mpoly(xmlNodePtr xnode,bool * hasz,int * root_srid)1689 static LWGEOM* parse_gml_mpoly(xmlNodePtr xnode, bool *hasz, int *root_srid)
1690 {
1691 	gmlSrs srs;
1692 	xmlNodePtr xa;
1693 	LWGEOM *geom = NULL;
1694 
1695 	if (is_xlink(xnode)) xnode = get_xlink_node(xnode);
1696 
1697 	parse_gml_srs(xnode, &srs);
1698 	if (*root_srid == SRID_UNKNOWN && srs.srid != SRID_UNKNOWN)
1699 		*root_srid = srs.srid;
1700 
1701 	geom = (LWGEOM *)lwcollection_construct_empty(MULTIPOLYGONTYPE, *root_srid, 1, 0);
1702 
1703 	if (xnode->children == NULL)
1704 		return geom;
1705 
1706 	for (xa = xnode->children ; xa != NULL ; xa = xa->next)
1707 	{
1708 		/* MultiPolygon/polygonMember */
1709 		if (xa->type != XML_ELEMENT_NODE) continue;
1710 		if (!is_gml_namespace(xa, false)) continue;
1711 		if (strcmp((char *) xa->name, "polygonMember")) continue;
1712 		if (xa->children != NULL)
1713 			geom = (LWGEOM*)lwmpoly_add_lwpoly((LWMPOLY*)geom,
1714 			                                   (LWPOLY*)parse_gml(xa->children, hasz, root_srid));
1715 	}
1716 
1717 	return geom;
1718 }
1719 
1720 
1721 /**
1722  * Parse GML MultiSurface (3.1.1)
1723  */
parse_gml_msurface(xmlNodePtr xnode,bool * hasz,int * root_srid)1724 static LWGEOM* parse_gml_msurface(xmlNodePtr xnode, bool *hasz, int *root_srid)
1725 {
1726 	gmlSrs srs;
1727 	xmlNodePtr xa, xb;
1728 	LWGEOM *geom = NULL;
1729 
1730 	if (is_xlink(xnode)) xnode = get_xlink_node(xnode);
1731 
1732 	parse_gml_srs(xnode, &srs);
1733 	if (*root_srid == SRID_UNKNOWN && srs.srid != SRID_UNKNOWN)
1734 		*root_srid = srs.srid;
1735 
1736 	geom = (LWGEOM *)lwcollection_construct_empty(MULTIPOLYGONTYPE, *root_srid, 1, 0);
1737 
1738 	if (xnode->children == NULL)
1739 		return geom;
1740 
1741 	for (xa = xnode->children ; xa != NULL ; xa = xa->next)
1742 	{
1743 		/* MultiSurface/surfaceMember */
1744 		if (xa->type != XML_ELEMENT_NODE) continue;
1745 		if (!is_gml_namespace(xa, false)) continue;
1746 		if (!strcmp((char *) xa->name, "surfaceMembers"))
1747 		{
1748 			for (xb = xa->children ; xb != NULL ; xb = xb->next)
1749 			{
1750 				if (xb != NULL)
1751 					geom = (LWGEOM*)lwmpoly_add_lwpoly((LWMPOLY*)geom,
1752 				                                       (LWPOLY*)parse_gml(xb, hasz, root_srid));
1753 			}
1754 		}
1755 		else if (!strcmp((char *) xa->name, "surfaceMember"))
1756 		{
1757 			if (xa->children != NULL)
1758 				geom = (LWGEOM*)lwmpoly_add_lwpoly((LWMPOLY*)geom,
1759 				                                   (LWPOLY*)parse_gml(xa->children, hasz, root_srid));
1760 		}
1761 	}
1762 
1763 	return geom;
1764 }
1765 
1766 
1767 /**
1768  * Parse GML PolyhedralSurface (3.1.1)
1769  * Nota: It's not part of SF-2
1770  */
parse_gml_psurface(xmlNodePtr xnode,bool * hasz,int * root_srid)1771 static LWGEOM* parse_gml_psurface(xmlNodePtr xnode, bool *hasz, int *root_srid)
1772 {
1773 	gmlSrs srs;
1774 	xmlNodePtr xa;
1775 	bool found = false;
1776 	LWGEOM *geom = NULL;
1777 
1778 	if (is_xlink(xnode)) xnode = get_xlink_node(xnode);
1779 
1780 	parse_gml_srs(xnode, &srs);
1781 	if (*root_srid == SRID_UNKNOWN && srs.srid != SRID_UNKNOWN)
1782 		*root_srid = srs.srid;
1783 
1784 	geom = (LWGEOM *)lwcollection_construct_empty(POLYHEDRALSURFACETYPE, *root_srid, 1, 0);
1785 
1786 	if (xnode->children == NULL)
1787 		return geom;
1788 
1789 	/* Looking for gml:polygonPatches */
1790 	for (xa = xnode->children ; xa != NULL ; xa = xa->next)
1791 	{
1792 		if (xa->type != XML_ELEMENT_NODE) continue;
1793 		if (!is_gml_namespace(xa, false)) continue;
1794 		if (!strcmp((char *) xa->name, "polygonPatches"))
1795 		{
1796 			found = true;
1797 			break;
1798 		}
1799 	}
1800 	if (!found) return geom;
1801 
1802 	for (xa = xa->children ; xa != NULL ; xa = xa->next)
1803 	{
1804 		/* PolyhedralSurface/polygonPatches/PolygonPatch */
1805 		if (xa->type != XML_ELEMENT_NODE) continue;
1806 		if (!is_gml_namespace(xa, false)) continue;
1807 		if (strcmp((char *) xa->name, "PolygonPatch")) continue;
1808 
1809 		geom = (LWGEOM*)lwpsurface_add_lwpoly((LWPSURFACE*)geom,
1810 		                                      (LWPOLY*)parse_gml_patch(xa, hasz, root_srid));
1811 	}
1812 
1813 	return geom;
1814 }
1815 
1816 
1817 /**
1818  * Parse GML MultiGeometry (2.1.2, 3.1.1)
1819  */
parse_gml_coll(xmlNodePtr xnode,bool * hasz,int * root_srid)1820 static LWGEOM* parse_gml_coll(xmlNodePtr xnode, bool *hasz, int *root_srid)
1821 {
1822 	gmlSrs srs;
1823 	xmlNodePtr xa;
1824 	LWGEOM *geom = NULL;
1825 
1826 	if (is_xlink(xnode)) xnode = get_xlink_node(xnode);
1827 
1828 	parse_gml_srs(xnode, &srs);
1829 	if (*root_srid == SRID_UNKNOWN && srs.srid != SRID_UNKNOWN)
1830 		*root_srid = srs.srid;
1831 
1832 	geom = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, *root_srid, 1, 0);
1833 
1834 	if (xnode->children == NULL)
1835 		return geom;
1836 
1837 	for (xa = xnode->children ; xa != NULL ; xa = xa->next)
1838 	{
1839 		if (xa->type != XML_ELEMENT_NODE) continue;
1840 		if (!is_gml_namespace(xa, false)) continue;
1841 
1842 		/*
1843 		 * In GML 2.1.2 pointMember, lineStringMember and
1844 		 * polygonMember are parts of geometryMember
1845 		 * substitution group
1846 		 */
1847 		if (	   !strcmp((char *) xa->name, "pointMember")
1848 		        || !strcmp((char *) xa->name, "lineStringMember")
1849 		        || !strcmp((char *) xa->name, "polygonMember")
1850 		        || !strcmp((char *) xa->name, "geometryMember"))
1851 		{
1852 			if (xa->children == NULL) break;
1853 			geom = (LWGEOM*)lwcollection_add_lwgeom((LWCOLLECTION *)geom,
1854 			                                        parse_gml(xa->children, hasz, root_srid));
1855 		}
1856 	}
1857 
1858 	return geom;
1859 }
1860 
1861 /**
1862  * Read GML
1863  */
1864 static LWGEOM *
lwgeom_from_gml(const char * xml,int xml_size)1865 lwgeom_from_gml(const char *xml, int xml_size)
1866 {
1867 	xmlDocPtr xmldoc;
1868 	xmlNodePtr xmlroot=NULL;
1869 	LWGEOM *lwgeom = NULL;
1870 	bool hasz=true;
1871 	int root_srid=SRID_UNKNOWN;
1872 
1873 	/* Begin to Parse XML doc */
1874 	xmlInitParser();
1875 
1876 	xmldoc = xmlReadMemory(xml, xml_size, NULL, NULL, XML_PARSE_SAX1);
1877 	if (!xmldoc)
1878 	{
1879 		xmlCleanupParser();
1880 		gml_lwpgerror("invalid GML representation", 1);
1881 		return NULL;
1882 	}
1883 
1884 	xmlroot = xmlDocGetRootElement(xmldoc);
1885 	if (!xmlroot)
1886 	{
1887 		xmlFreeDoc(xmldoc);
1888 		xmlCleanupParser();
1889 		gml_lwpgerror("invalid GML representation", 1);
1890 		return NULL;
1891 	}
1892 
1893 	lwgeom = parse_gml(xmlroot, &hasz, &root_srid);
1894 
1895 	xmlFreeDoc(xmldoc);
1896 	xmlCleanupParser();
1897 	/* shouldn't we be releasing xmldoc too here ? */
1898 
1899 	if ( root_srid != SRID_UNKNOWN )
1900 		lwgeom->srid = root_srid;
1901 
1902 	/* GML geometries could be either 2 or 3D and can be nested mixed.
1903 	 * Missing Z dimension is even tolerated inside some GML coords
1904 	 *
1905 	 * So we deal with 3D in all structures allocation, and flag hasz
1906 	 * to false if we met once a missing Z dimension
1907 	 * In this case, we force recursive 2D.
1908 	 */
1909 	if (!hasz)
1910 	{
1911 		LWGEOM *tmp = lwgeom_force_2d(lwgeom);
1912 		lwgeom_free(lwgeom);
1913 		lwgeom = tmp;
1914 	}
1915 
1916 	return lwgeom;
1917 }
1918 
1919 
1920 /**
1921  * Parse GML
1922  */
parse_gml(xmlNodePtr xnode,bool * hasz,int * root_srid)1923 static LWGEOM* parse_gml(xmlNodePtr xnode, bool *hasz, int *root_srid)
1924 {
1925 	xmlNodePtr xa = xnode;
1926 	gmlSrs srs;
1927 
1928 	while (xa != NULL && (xa->type != XML_ELEMENT_NODE
1929 	                      || !is_gml_namespace(xa, false))) xa = xa->next;
1930 
1931 	if (xa == NULL) gml_lwpgerror("invalid GML representation", 55);
1932 
1933 	parse_gml_srs(xa, &srs);
1934 	if (*root_srid == SRID_UNKNOWN && srs.srid != SRID_UNKNOWN)
1935 	{
1936 		*root_srid = srs.srid;
1937 	}
1938 
1939 	if (!strcmp((char *) xa->name, "Point"))
1940 		return parse_gml_point(xa, hasz, root_srid);
1941 
1942 	if (!strcmp((char *) xa->name, "LineString"))
1943 		return parse_gml_line(xa, hasz, root_srid);
1944 
1945 	if (!strcmp((char *) xa->name, "Curve"))
1946 		return parse_gml_curve(xa, hasz, root_srid);
1947 
1948 	if (!strcmp((char *) xa->name, "LinearRing"))
1949 		return parse_gml_linearring(xa, hasz, root_srid);
1950 
1951 	if (!strcmp((char *) xa->name, "Polygon"))
1952 		return parse_gml_polygon(xa, hasz, root_srid);
1953 
1954 	if (!strcmp((char *) xa->name, "Triangle"))
1955 		return parse_gml_triangle(xa, hasz, root_srid);
1956 
1957 	if (!strcmp((char *) xa->name, "Surface"))
1958 		return parse_gml_surface(xa, hasz, root_srid);
1959 
1960 	if (!strcmp((char *) xa->name, "MultiPoint"))
1961 		return parse_gml_mpoint(xa, hasz, root_srid);
1962 
1963 	if (!strcmp((char *) xa->name, "MultiLineString"))
1964 		return parse_gml_mline(xa, hasz, root_srid);
1965 
1966 	if (!strcmp((char *) xa->name, "MultiCurve"))
1967 		return parse_gml_mcurve(xa, hasz, root_srid);
1968 
1969 	if (!strcmp((char *) xa->name, "MultiPolygon"))
1970 		return parse_gml_mpoly(xa, hasz, root_srid);
1971 
1972 	if (!strcmp((char *) xa->name, "MultiSurface"))
1973 		return parse_gml_msurface(xa, hasz, root_srid);
1974 
1975 	if (!strcmp((char *) xa->name, "PolyhedralSurface"))
1976 		return parse_gml_psurface(xa, hasz, root_srid);
1977 
1978 	if ((!strcmp((char *) xa->name, "Tin")) ||
1979 	        !strcmp((char *) xa->name, "TriangulatedSurface" ))
1980 		return parse_gml_tin(xa, hasz, root_srid);
1981 
1982 	if (!strcmp((char *) xa->name, "MultiGeometry"))
1983 		return parse_gml_coll(xa, hasz, root_srid);
1984 
1985 	gml_lwpgerror("invalid GML representation", 56);
1986 	return NULL; /* Never reach */
1987 }
1988