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