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