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