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