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