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