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