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