source: trunk/postgis/lwgeom_in_gml.c @ 4744

Last change on this file since 4744 was 4744, checked in by pramsey, 7 years ago

Fix for #273? Some unitialized variables may have been causing problems. Initializing them fixed this problem for me.

  • Property svn:keywords set to Id
File size: 39.6 KB
Line 
1/**********************************************************************
2 * $Id: lwgeom_in_gml.c 4744 2009-11-04 23:51:57Z pramsey $
3 *
4 * PostGIS - Spatial Types for PostgreSQL
5 * http://postgis.refractions.net
6 * Copyright 2009 Oslandia
7 *
8 * This is free software; you can redistribute and/or modify it under
9 * the terms of the GNU General Public Licence. See the COPYING file.
10 *
11 **********************************************************************/
12
13/**
14* @file GML input routines.
15* Ability to parse GML geometry fragment and to return an LWGEOM
16* or an error message.
17*
18* Implement ISO SQL/MM ST_GMLToSQL method
19* Cf: ISO 13249-3 -> 5.1.50 (p 134)
20*
21* GML versions supported:
22*  - GML 3.2.1 Namespace
23*  - GML 3.1.1 Simple Features profile
24*  - GML 3.1.0 and 3.0.0 SF elements and attributes
25*  - GML 2.1.2
26* Cf: <http://www.opengeospatial.org/standards/gml>
27*
28* NOTA: this code doesn't (yet ?) support SQL/MM curves
29*
30* Written by Olivier Courtin - Oslandia
31*
32**********************************************************************/
33
34
35#include "postgres.h"
36#include "lwgeom_pg.h"
37#include "liblwgeom.h"
38#include "lwgeom_transform.h"
39#include "executor/spi.h"
40
41
42#if HAVE_LIBXML2
43#include <libxml/tree.h>
44#include <libxml/parser.h>
45#include <libxml/xpath.h>
46#include <libxml/xpathInternals.h>
47
48
49
50Datum geom_from_gml(PG_FUNCTION_ARGS);
51static LWGEOM* parse_gml(xmlNodePtr xnode, bool *hasz, int *root_srid);
52
53typedef struct struct_gmlSrs
54{
55       int srid;
56       bool reverse_axis;
57}
58gmlSrs;
59
60#define XLINK_NS        ((char *) "http://www.w3.org/1999/xlink")
61#define GML_NS          ((char *) "http://www.opengis.net/gml")
62#define GML32_NS        ((char *) "http://www.opengis.net/gml/3.2")
63
64
65/**
66 * Ability to parse GML geometry fragment and to return an LWGEOM
67 * or an error message.
68 *
69 * ISO SQL/MM define two error messages:
70*  Cf: ISO 13249-3 -> 5.1.50 (p 134)
71 *  - invalid GML representation
72 *  - unknown spatial reference system
73 */
74PG_FUNCTION_INFO_V1(geom_from_gml);
75Datum geom_from_gml(PG_FUNCTION_ARGS)
76{
77        PG_LWGEOM *geom, *geom2d;
78        xmlDocPtr xmldoc;
79        text *xml_input; 
80        LWGEOM *lwgeom;
81        int xml_size;
82        uchar *srl;
83        char *xml;
84        size_t size=0;
85        bool hasz=true;
86        int root_srid=0;
87        xmlNodePtr xmlroot=NULL;
88
89
90        /* Get the GML stream */
91        if (PG_ARGISNULL(0)) PG_RETURN_NULL();
92        xml_input = PG_GETARG_TEXT_P(0);
93
94        xml_size = VARSIZE(xml_input) - VARHDRSZ;       /* actual letters */
95        xml = palloc(xml_size + 1);                     /* +1 for null */
96        memcpy(xml, VARDATA(xml_input), xml_size);
97        xml[xml_size] = 0;                              /* null term */
98
99        /* Begin to Parse XML doc */
100        xmlInitParser();
101        xmldoc = xmlParseMemory(xml, xml_size);
102        if (!xmldoc || (xmlroot = xmlDocGetRootElement(xmldoc)) == NULL) {
103                xmlFreeDoc(xmldoc);
104                xmlCleanupParser();
105                lwerror("invalid GML representation");
106        }
107
108        lwgeom = parse_gml(xmlroot, &hasz, &root_srid);
109        lwgeom->bbox = lwgeom_compute_box2d(lwgeom);
110        geom = pglwgeom_serialize(lwgeom);
111        lwgeom_release(lwgeom);
112
113        xmlFreeDoc(xmldoc);
114        xmlCleanupParser();
115
116        /* GML geometry could be either 2 or 3D and can be nested mixed.
117         * Missing Z dimension is even tolerated inside some GML coords
118         *
119         * So we deal with 3D in all structure allocation, and flag hasz
120         * to false if we met once a missing Z dimension
121         * In this case, we force recursive 2D.
122         */
123        if (!hasz) {
124                srl = lwalloc(VARSIZE(geom));
125                lwgeom_force2d_recursive(SERIALIZED_FORM(geom), srl, &size);
126                geom2d = PG_LWGEOM_construct(srl, pglwgeom_getSRID(geom),
127                                     lwgeom_hasBBOX(geom->type));
128                lwfree(geom);
129                geom = geom2d;
130        }
131
132        PG_RETURN_POINTER(geom);
133}
134                       
135
136/**
137 * Return true if current node contains a simple xlink
138 * Return false otherwise.
139 */
140static bool is_xlink(xmlNodePtr node)
141{
142        xmlChar *prop;
143
144        prop = xmlGetNsProp(node, (xmlChar *)"type", (xmlChar *) XLINK_NS);
145        if (prop == NULL) return false;
146        if (strcmp((char *) prop, "simple")) {
147                xmlFree(prop);
148                return false;
149        }
150
151        prop = xmlGetNsProp(node, (xmlChar *)"href", (xmlChar *) XLINK_NS);
152        if (prop == NULL) return false;
153        if (prop[0] != '#') {
154                xmlFree(prop);
155                return false;
156        }
157        xmlFree(prop);
158
159        return true;
160}
161
162
163/**
164 * Return a xmlNodePtr on a node referenced by a xlink or NULL otherwise
165 */
166static xmlNodePtr get_xlink_node(xmlNodePtr xnode)
167{
168        char *id;
169        xmlNodePtr node;
170        xmlNsPtr *ns, *n;
171        xmlChar *href, *p;
172        xmlXPathContext *ctx;
173        xmlXPathObject *xpath;
174
175        href = xmlGetNsProp(xnode, (xmlChar *)"href", (xmlChar *) XLINK_NS);
176        id = lwalloc((xmlStrlen(xnode->ns->prefix) * 2 + xmlStrlen(xnode->name)
177                                + xmlStrlen(href) + sizeof("//:[@:id='']") + 1));
178        p = href;
179        p++; /* ignore '#' first char */
180
181        /* Xpath pattern look like:             //gml:point[@gml:id='p1']   */
182        sprintf(id, "//%s:%s[@%s:id='%s']",     (char *) xnode->ns->prefix,
183                                                (char *) xnode->name,
184                                                (char *) xnode->ns->prefix,
185                                                (char *) p);
186        xmlFree(href);
187
188        ctx = xmlXPathNewContext(xnode->doc);
189        if (ctx == NULL) {
190                lwfree(id);
191                return NULL;
192        }
193
194        /* Handle namespaces */
195        ns = xmlGetNsList(xnode->doc, xnode);
196        for (n=ns ; *n; n++) xmlXPathRegisterNs(ctx, (*n)->prefix, (*n)->href);
197        xmlFree(ns);
198
199        /* Execute Xpath expression */
200        xpath = xmlXPathEvalExpression((xmlChar *) id, ctx);
201        lwfree(id);
202        if (xpath == NULL || xpath->nodesetval == NULL || xpath->nodesetval->nodeNr != 1) {
203                xmlXPathFreeObject(xpath);
204                xmlXPathFreeContext(ctx);
205                return NULL;
206        }
207        node = xpath->nodesetval->nodeTab[0];
208        xmlXPathFreeObject(xpath);
209        xmlXPathFreeContext(ctx);
210
211        return node;
212}
213
214
215/**
216 * Return false if current element namespace is not a GML one
217 * Return true otherwise.
218 */
219static bool is_gml_namespace(xmlNodePtr xnode, bool is_strict)
220{
221        xmlNsPtr *ns, *p;
222         
223        ns = xmlGetNsList(xnode->doc, xnode);
224        /*
225         * If no namespace is available we could return true anyway
226         * (because we work only on GML fragment, we don't want to
227         *  'oblige' to add namespace on the geometry root node)
228         */
229        if (ns == NULL) return !is_strict;
230
231        /*
232         * Handle namespaces:
233         *  - http://www.opengis.net/gml      (GML 3.1.1 and priors)
234         *  - http://www.opengis.net/gml/3.2  (GML 3.2.1)
235         */
236        for (p=ns ; *p ; p++) {
237                if ((*p)->href == NULL) continue;
238                if (!strcmp((char *) (*p)->href, GML_NS) ||
239                    !strcmp((char *) (*p)->href, GML32_NS)) {
240                        if (    (*p)->prefix == NULL ||
241                                !xmlStrcmp(xnode->ns->prefix, (*p)->prefix)) {
242
243                                xmlFree(ns);
244                                return true;
245                        }
246                }
247        }
248
249        xmlFree(ns);
250        return false;
251}
252
253
254/**
255 * Retrieve a GML propertie from a node
256 * Respect namespaces if presents in the node element
257 */
258static xmlChar *gmlGetProp(xmlNodePtr xnode, xmlChar *prop)
259{
260        xmlChar *value;
261
262        if (!is_gml_namespace(xnode, true))
263                return xmlGetProp(xnode, prop);
264        /*
265         * Handle namespaces:
266         *  - http://www.opengis.net/gml      (GML 3.1.1 and priors)
267         *  - http://www.opengis.net/gml/3.2  (GML 3.2.1)
268         */
269        value = xmlGetNsProp(xnode, prop, (xmlChar *) GML_NS);
270        if (value == NULL) value = xmlGetNsProp(xnode, prop, (xmlChar *) GML32_NS);
271
272        /* In last case try without explicit namespace */
273        if (value == NULL) value = xmlGetNoNsProp(xnode, prop);
274
275        return value;
276}
277
278
279/**
280 * Reverse X and Y axis on a given POINTARRAY
281 */
282static POINTARRAY* gml_reverse_axis_pa(POINTARRAY *pa)
283{
284        int i;
285        double d;
286        POINT4D p;
287
288        for (i=0 ; i < pa->npoints ; i++) {
289                getPoint4d_p(pa, i, &p);
290                d = p.y;
291                p.y = p.x;
292                p.x = d;
293                setPoint4d(pa, i, &p);
294        }
295
296        return pa;
297}
298
299
300/**
301 * Use Proj4 to reproject a given POINTARRAY
302 */
303static POINTARRAY* gml_reproject_pa(POINTARRAY *pa, int srid_in, int srid_out)
304{
305        int i;
306        POINT4D p;
307        projPJ in_pj, out_pj;
308        char *text_in, *text_out;
309
310        if (srid_in == -1 || srid_out == -1)
311                lwerror("invalid GML representation");
312
313        text_in = GetProj4StringSPI(srid_in);
314        text_out = GetProj4StringSPI(srid_out);
315
316        in_pj = make_project(text_in);
317        out_pj = make_project(text_out);
318
319        lwfree(text_in);
320        lwfree(text_out);
321
322        for (i=0 ; i < pa->npoints ; i++) {
323                getPoint4d_p(pa, i, &p);
324                transform_point(&p, in_pj, out_pj);
325                setPoint4d(pa, i, &p);
326        }
327
328        pj_free(in_pj);
329        pj_free(out_pj);
330
331        return pa;
332}
333
334
335/**
336 * Return 1 if given srid is planar (0 otherwise, i.e geocentric srid)
337 * Return -1 if srid is not in spatial_ref_sys
338 */
339static int gml_is_srid_planar(int srid)
340{
341        char *result;
342        char query[256];
343        int is_planar, err;
344
345        if (SPI_OK_CONNECT != SPI_connect ())
346                lwerror("gml_is_srid_lat_lon: could not connect to SPI manager");
347
348        /* A way to find if this projection is planar or geocentric */
349        sprintf(query, "SELECT position('+units=m ' in proj4text) \
350                        FROM spatial_ref_sys WHERE srid='%d'", srid);
351
352        err = SPI_exec(query, 1);
353        if (err < 0) lwerror("gml_is_srid_lat_lon: error executing query %d", err);
354
355        /* No entry in spatial_ref_sys */
356        if (SPI_processed <= 0) {
357                SPI_finish();
358                return -1;
359        }
360
361        result = SPI_getvalue(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1);
362        is_planar = atoi(result);
363        SPI_finish();
364
365        return is_planar;
366}
367           
368
369/**
370 * Parse gml srsName attribute
371 */ 
372static gmlSrs* parse_gml_srs(xmlNodePtr xnode)
373{
374        char *p;
375        gmlSrs *srs;
376        int is_planar;
377        xmlNodePtr node;
378        xmlChar *srsname;
379        bool latlon = false;
380        char sep = ':';
381
382        node = xnode;
383        srsname = gmlGetProp(node, (xmlChar *) "srsName");
384        if (!srsname) {
385                if (node->parent == NULL) {
386                        srs = (gmlSrs*) lwalloc(sizeof(gmlSrs));
387                        srs->srid = -1;
388                        srs->reverse_axis = false;
389                        return srs;
390                }
391                return parse_gml_srs(node->parent);
392        }
393
394        srs = (gmlSrs*) lwalloc(sizeof(gmlSrs));
395
396        /* Severals srsName formats are available...
397         *  cf WFS 1.1.0 -> 9.2 (p36)
398         *  cf ISO 19142 -> 7.9.2.4.4 (p34)
399         *  cf RFC 5165 <http://tools.ietf.org/html/rfc5165>
400         */
401
402        /* SRS pattern like:    EPSG:4326                                       
403                                urn:EPSG:geographicCRS:4326                     
404                                urn:ogc:def:crs:EPSG:4326
405                                urn:ogc:def:crs:EPSG::4326
406                                urn:ogc:def:crs:EPSG:6.6:4326
407                                urn:x-ogc:def:crs:EPSG:6.6:4326
408                                http://www.opengis.net/gml/srs/epsg.xml#4326
409        */
410
411        if (!strncmp((char *) srsname, "EPSG:", 5)) {
412                sep = ':';
413                latlon = false;
414        } else if (!strncmp((char *) srsname, "urn:ogc:def:crs:EPSG:", 21)
415                || !strncmp((char *) srsname, "urn:x-ogc:def:crs:EPSG:", 23)
416                || !strncmp((char *) srsname, "urn:EPSG:geographicCRS:", 23)) {
417                sep = ':';
418                latlon = true;
419        }  else if (!strncmp((char *) srsname, 
420                                "http://www.opengis.net/gml/srs/epsg.xml#", 40)) {
421                sep = '#';
422                latlon = false;
423        } else lwerror("unknown spatial reference system");
424
425        /* retrieve the last ':' or '#' char */
426        for (p = (char *) srsname ; *p ; p++);
427        for (--p ; *p != sep ; p--)
428                if (!isdigit(*p)) lwerror("unknown spatial reference system");
429
430        srs->srid = atoi(++p);
431
432        /* Check into spatial_ref_sys that this SRID really exist */
433        is_planar = gml_is_srid_planar(srs->srid);
434        if (srs->srid == -1 || is_planar == -1) 
435                lwerror("unknown spatial reference system");
436
437        /* About lat/lon issue, Cf: http://tinyurl.com/yjpr55z */
438        srs->reverse_axis = !is_planar && latlon;
439
440        xmlFree(srsname);
441        return srs;
442}
443
444
445/**
446 * Parse a string supposed to be a double
447 */
448static double parse_gml_double(char *d, bool space_before, bool space_after)
449{
450        char *p;
451        int st;
452        enum states {
453                INIT            = 0,
454                NEED_DIG        = 1,
455                DIG             = 2,
456                NEED_DIG_DEC    = 3,
457                DIG_DEC         = 4,
458                EXP             = 5,
459                NEED_DIG_EXP    = 6,
460                DIG_EXP         = 7,
461                END             = 8
462        };
463
464        /*
465         * Double pattern
466         * [-|\+]?[0-9]+(\.)?([0-9]+)?([Ee](\+|-)?[0-9]+)?
467         * We could also meet spaces before or after
468         * this pattern upon parameters
469         */
470
471        if (space_before) while (isspace(*d)) d++;
472        for (st = INIT, p = d ; *p ; p++) {
473
474                if (isdigit(*p)) {
475                                if (st == INIT || st == NEED_DIG)       st = DIG;
476                        else if (st == NEED_DIG_DEC)                    st = DIG_DEC;
477                        else if (st == NEED_DIG_EXP || st == EXP)       st = DIG_EXP;
478                        else if (st == DIG || st == DIG_DEC || st == DIG_EXP);
479                        else lwerror("invalid GML representation"); 
480                } else if (*p == '.') {
481                        if      (st == DIG)                             st = NEED_DIG_DEC;
482                        else    lwerror("invalid GML representation"); 
483                } else if (*p == '-' || *p == '+') {
484                        if      (st == INIT)                            st = NEED_DIG;
485                        else if (st == EXP)                             st = NEED_DIG_EXP;
486                        else    lwerror("invalid GML representation"); 
487                } else if (*p == 'e' || *p == 'E') {
488                        if      (st == DIG || st == DIG_DEC)            st = EXP;
489                        else    lwerror("invalid GML representation"); 
490                } else if (isspace(*p)) {
491                        if (!space_after) lwerror("invalid GML representation"); 
492                        if (st == DIG || st == DIG_DEC || st == DIG_EXP)st = END;
493                        else if (st == NEED_DIG_DEC)                    st = END;
494                        else if (st == END);
495                        else    lwerror("invalid GML representation");
496                } else  lwerror("invalid GML representation");
497        }             
498
499        if (st != DIG && st != NEED_DIG_DEC && st != DIG_DEC && st != DIG_EXP && st != END)
500                lwerror("invalid GML representation");
501
502        return atof(d);
503}
504
505
506/**
507 * Parse gml:coordinates
508 */
509static POINTARRAY* parse_gml_coordinates(xmlNodePtr xnode, bool *hasz)
510{
511        xmlChar *gml_coord, *gml_ts, *gml_cs, *gml_dec;
512        char *coord, *p, *q;
513        char cs, ts, dec;
514        DYNPTARRAY *dpa;
515        POINTARRAY *pa;
516        int gml_dims;
517        bool digit;
518        POINT4D pt;
519        uchar dims=0;
520
521        /* We begin to retrieve coordinates string */
522        gml_coord = xmlNodeGetContent(xnode);
523        p = coord = (char *) gml_coord;
524
525        /* Default GML coordinates pattern:     x1,y1 x2,y2
526         *                                      x1,y1,z1 x2,y2,z2
527         *                             
528         * Cf GML 2.1.2 -> 4.3.1 (p18)
529         */
530
531        /* Retrieve separator between coordinates tuples */
532        gml_ts = gmlGetProp(xnode, (xmlChar *) "ts");
533        if (gml_ts == NULL) ts = ' ';
534        else {
535                if (xmlStrlen(gml_ts) > 1 || isdigit(gml_ts[0]))
536                        lwerror("invalid GML representation");
537                ts = gml_ts[0];
538                xmlFree(gml_ts);
539        }
540
541        /* Retrieve separator between each coordinate */
542        gml_cs = gmlGetProp(xnode, (xmlChar *) "cs");
543        if (gml_cs == NULL) cs = ',';
544        else {
545                if (xmlStrlen(gml_cs) > 1 || isdigit(gml_cs[0]))
546                        lwerror("invalid GML representation");
547                cs = gml_cs[0];
548                xmlFree(gml_cs);
549        }
550
551        /* Retrieve decimal separator */
552        gml_dec = gmlGetProp(xnode, (xmlChar *) "decimal");
553        if (gml_dec == NULL) dec = '.';
554        else {
555                if (xmlStrlen(gml_dec) > 1 || isdigit(gml_dec[0]))
556                        lwerror("invalid GML representation");
557                dec = gml_dec[0];
558                xmlFree(gml_dec);
559        }
560
561        if (cs == ts || cs == dec || ts == dec)
562                lwerror("invalid GML representation");
563
564        /* Now we create PointArray from coordinates values */
565        TYPE_SETZM(dims, 1, 0);
566        dpa = dynptarray_create(1, dims);
567
568        while(isspace(*p)) p++;         /* Eat extra whitespaces if any */
569        for (q = p, gml_dims=0, digit = false ; *p ; p++) {
570
571                if (isdigit(*p)) digit = true;  /* One state parser */
572
573                /* Coordinate Separator */
574                if (*p == cs) {
575                        *p = '\0';
576                        gml_dims++;
577
578                        if (*(p+1) == '\0') lwerror("invalid GML representation");
579
580                        if      (gml_dims == 1) pt.x = parse_gml_double(q, false, true);
581                        else if (gml_dims == 2) pt.y = parse_gml_double(q, false, true);
582
583                        q = p+1;
584
585                /* Tuple Separator (or end string) */
586                } else if (digit && (*p == ts || *(p+1) == '\0')) {
587                        if (*p == ts) *p = '\0';
588                        gml_dims++;
589
590                        if (gml_dims < 2 || gml_dims > 3)
591                                lwerror("invalid GML representation");
592
593                        if (gml_dims == 3)
594                                pt.z = parse_gml_double(q, false, true);
595                        else {
596                                pt.y = parse_gml_double(q, false, true);
597                                *hasz = false;
598                        }
599
600                        dynptarray_addPoint4d(dpa, &pt, 0);
601                        digit = false;
602
603                        q = p+1;
604                        gml_dims = 0;
605
606                /* Need to put standard decimal separator to atof handle */
607                } else if (*p == dec && dec != '.') *p = '.';
608        }
609
610        xmlFree(gml_coord);
611        pa = ptarray_clone(dpa->pa);
612        lwfree(dpa);
613
614        return pa;
615}
616
617
618/**
619 * Parse gml:coord
620 */
621static POINTARRAY* parse_gml_coord(xmlNodePtr xnode, bool *hasz)
622{
623        xmlNodePtr xyz;
624        DYNPTARRAY *dpa;
625        POINTARRAY *pa;
626        bool x,y,z;
627        xmlChar *c;
628        POINT4D p;
629        uchar dims=0;
630
631        TYPE_SETZM(dims, 1, 0);
632        dpa = dynptarray_create(1, dims);
633
634        x = y = z = false;
635        for (xyz = xnode->children ; xyz != NULL ; xyz = xyz->next) {
636
637                if (xyz->type != XML_ELEMENT_NODE) continue;
638
639                if (!strcmp((char *) xyz->name, "X")) {
640                        if (x) lwerror("invalid GML representation");
641                        c = xmlNodeGetContent(xyz);
642                        p.x = parse_gml_double((char *) c, true, true);
643                        x = true;
644                        xmlFree(c);
645                } else  if (!strcmp((char *) xyz->name, "Y")) {
646                        if (y) lwerror("invalid GML representation");
647                        c = xmlNodeGetContent(xyz);
648                        p.y = parse_gml_double((char *) c, true, true);
649                        y = true;
650                        xmlFree(c);
651                } else if (!strcmp((char *) xyz->name, "Z")) {
652                        if (z) lwerror("invalid GML representation");
653                        c = xmlNodeGetContent(xyz);
654                        p.z = parse_gml_double((char *) c, true, true);
655                        z = true;
656                        xmlFree(c);
657                }
658        }
659        /* Check dimension consistancy */
660        if (!x || !y) lwerror("invalid GML representation");
661        if (!z) *hasz = false;
662
663        dynptarray_addPoint4d(dpa, &p, 0);
664        x = y = z = false;
665
666        pa = ptarray_clone(dpa->pa);
667        lwfree(dpa);
668
669        return pa;
670}
671
672
673/**
674 * Parse gml:pos
675 */
676static POINTARRAY* parse_gml_pos(xmlNodePtr xnode, bool *hasz)
677{
678        xmlChar *dimension, *gmlpos;
679        xmlNodePtr posnode;
680        int dim, gml_dim;
681        DYNPTARRAY *dpa;
682        POINTARRAY *pa;
683        char *pos, *p;
684        bool digit;
685        POINT4D pt;
686        uchar dims=0;
687
688        TYPE_SETZM(dims, 1, 0);
689        dpa = dynptarray_create(1, dims);
690
691        for (posnode = xnode ; posnode != NULL ; posnode = posnode->next) {
692
693                /* We only care about pos element */
694                if (posnode->type != XML_ELEMENT_NODE) continue;
695                if (strcmp((char *) posnode->name, "pos")) continue;
696
697                dimension = gmlGetProp(xnode, (xmlChar *) "srsDimension");
698                if (dimension == NULL) /* in GML 3.0.0 it was dimension */
699                        dimension = gmlGetProp(xnode, (xmlChar *) "dimension");
700                if (dimension == NULL) dim = 2; /* We assume that we are in 2D */
701                else {
702                        dim = atoi((char *) dimension);
703                        xmlFree(dimension);
704                        if (dim < 2 || dim > 3)
705                                lwerror("invalid GML representation");
706                }
707                if (dim == 2) *hasz = false;
708       
709                /* We retrieve gml:pos string */
710                gmlpos = xmlNodeGetContent(posnode);
711                pos = (char *) gmlpos;
712                while(isspace(*pos)) pos++;     /* Eat extra whitespaces if any */
713
714                /* gml:pos pattern:     x1 y1
715                *                       x1 y1 z1
716                */
717                for (p=pos, gml_dim=0, digit=false ; *pos ; pos++) {
718
719                        if (isdigit(*pos)) digit = true;
720                        if (digit && (*pos == ' ' || *(pos+1) == '\0')) {
721
722                                if (*pos == ' ') *pos = '\0';
723                                gml_dim++;
724                                if      (gml_dim == 1)
725                                        pt.x = parse_gml_double(p, true, true);
726                                else if (gml_dim == 2)
727                                        pt.y = parse_gml_double(p, true, true);
728                                else if (gml_dim == 3)
729                                        pt.z = parse_gml_double(p, true, true);
730                       
731                                p = pos+1;
732                                digit = false;
733                        }
734                }
735                xmlFree(gmlpos);
736
737                /* Test again coherent dimensions on each coord */
738                if (gml_dim == 2) *hasz = false;
739                if (gml_dim < 2 || gml_dim > 3 || gml_dim != dim)
740                        lwerror("invalid GML representation");
741
742                dynptarray_addPoint4d(dpa, &pt, 0);
743        }
744
745        pa = ptarray_clone(dpa->pa);
746        lwfree(dpa);
747
748        return pa;
749}
750
751
752/**
753 * Parse gml:posList
754 */
755static POINTARRAY* parse_gml_poslist(xmlNodePtr xnode, bool *hasz)
756{
757        xmlChar *dimension, *gmlposlist;
758        char *poslist, *p;
759        int dim, gml_dim;
760        DYNPTARRAY *dpa;
761        POINTARRAY *pa;
762        POINT4D pt;
763        bool digit;
764        uchar dims=0;
765
766        /* Retrieve gml:srsDimension attribute if any */
767        dimension = gmlGetProp(xnode, (xmlChar *) "srsDimension");
768        if (dimension == NULL) /* in GML 3.0.0 it was dimension */
769                dimension = gmlGetProp(xnode, (xmlChar *) "dimension");
770        if (dimension == NULL) dim = 2; /* We assume that we are in common 2D */
771        else {
772                dim = atoi((char *) dimension);
773                xmlFree(dimension);
774                if (dim < 2 || dim > 3) lwerror("invalid GML representation");
775        }
776        if (dim == 2) *hasz = false;
777       
778        /* Retrieve gml:posList string */
779        gmlposlist = xmlNodeGetContent(xnode);
780        poslist = (char *) gmlposlist;
781
782        TYPE_SETZM(dims, 1, 0);
783        dpa = dynptarray_create(1, dims);
784
785        /* gml:posList pattern:         x1 y1 x2 y2
786         *                              x1 y1 z1 x2 y2 z2
787         */
788        while(isspace(*poslist)) poslist++;     /* Eat extra whitespaces if any */
789        for (p=poslist, gml_dim=0, digit=false ; *poslist ; poslist++) {
790
791                if (isdigit(*poslist)) digit = true;
792                if (digit && (*poslist == ' ' || *(poslist+1) == '\0')) {
793
794                        if (*poslist == ' ') *poslist = '\0';
795
796                        gml_dim++;
797                        if      (gml_dim == 1) pt.x = parse_gml_double(p, true, true);
798                        else if (gml_dim == 2) pt.y = parse_gml_double(p, true, true);
799                        else if (gml_dim == 3) pt.z = parse_gml_double(p, true, true);
800
801                        if (gml_dim == dim) {
802                                dynptarray_addPoint4d(dpa, &pt, 0);
803                                gml_dim = 0;
804                        } else if (*(poslist+1) == '\0')
805                                lwerror("invalid GML representation");
806
807                        p = poslist+1;
808                        digit = false;
809                }
810        }
811
812        xmlFree(gmlposlist);
813        pa = ptarray_clone(dpa->pa);
814        lwfree(dpa);
815
816        return pa;
817}
818
819
820/**
821 * Parse data coordinates
822 *
823 * There's several ways to encode data coordinates, who could be mixed
824 * inside a single geometrie:
825 *  - gml:pos element
826 *  - gml:posList element
827 *  - gml:pointProperty
828 *  - gml:pointRep                                      (deprecated in 3.1.0)
829 *  - gml:coordinate element with tuples string inside  (deprecated in 3.1.0)
830 *  - gml:coord elements with X,Y(,Z) nested elements   (deprecated in 3.0.0)
831 */
832static POINTARRAY* parse_gml_data(xmlNodePtr xnode, bool *hasz, int *root_srid)
833{
834        POINTARRAY *pa, *tmp_pa;
835        xmlNodePtr xa, xb;
836        gmlSrs *srs;
837        bool found;
838
839        pa = NULL;
840
841        for (xa = xnode ; xa != NULL ; xa = xa->next) {
842
843                if (xa->type != XML_ELEMENT_NODE) continue;
844                if (!is_gml_namespace(xa, false)) continue;
845
846                if (!strcmp((char *) xa->name, "pos")) {
847                        tmp_pa = parse_gml_pos(xa, hasz);
848                        if (pa == NULL) pa = tmp_pa;
849                        else pa = ptarray_merge(pa, tmp_pa);
850
851                } else if (!strcmp((char *) xa->name, "posList")) {
852                        tmp_pa = parse_gml_poslist(xa, hasz);
853                        if (pa == NULL) pa = tmp_pa;
854                        else pa = ptarray_merge(pa, tmp_pa);
855
856                } else if (!strcmp((char *) xa->name, "coordinates")) {
857                        tmp_pa = parse_gml_coordinates(xa, hasz);
858                        if (pa == NULL) pa = tmp_pa;
859                        else pa = ptarray_merge(pa, tmp_pa);
860
861                } else if (!strcmp((char *) xa->name, "coord")) {
862                        tmp_pa = parse_gml_coord(xa, hasz);
863                        if (pa == NULL) pa = tmp_pa;
864                        else pa = ptarray_merge(pa, tmp_pa);
865
866                } else if (!strcmp((char *) xa->name, "pointRep") ||
867                           !strcmp((char *) xa->name, "pointProperty")) {
868
869                        found = false;
870                        for (xb = xa->children ; xb != NULL ; xb = xb->next) {
871                                if (xb->type != XML_ELEMENT_NODE) continue;
872                                if (!is_gml_namespace(xb, false)) continue;
873                                if (!strcmp((char *) xb->name, "point")) {
874                                        found = true;
875                                        break;
876                                }
877                        }
878
879                        if (!found || xb == NULL) lwerror("invalid GML representation");
880
881                        if (is_xlink(xb)) xb = get_xlink_node(xb);
882                        if (xb == NULL || xb->children == NULL)
883                                lwerror("invalid GML representation");
884
885                        tmp_pa = parse_gml_data(xb->children, hasz, root_srid);
886                        if (tmp_pa->npoints != 1) lwerror("invalid GML representation");
887
888                        srs = parse_gml_srs(xb);
889                        if (srs->reverse_axis) tmp_pa = gml_reverse_axis_pa(tmp_pa);
890                        if (!*root_srid) *root_srid = srs->srid;
891                        else  {
892                                if (srs->srid != *root_srid)
893                                gml_reproject_pa(tmp_pa, srs->srid, *root_srid);
894                        }
895                        if (pa == NULL) pa = tmp_pa;
896                        else pa = ptarray_merge(pa, tmp_pa);
897                        lwfree(srs);
898                }
899        }
900
901        if (pa == NULL) lwerror("invalid GML representation");
902
903        return pa;
904}
905
906
907/**
908 * Parse GML point (2.1.2, 3.1.1)
909 */
910static LWGEOM* parse_gml_point(xmlNodePtr xnode, bool *hasz, int *root_srid)
911{
912        gmlSrs *srs;
913        LWGEOM *geom;
914        POINTARRAY *pa;
915
916        if (xnode->children == NULL) lwerror("invalid GML representation");
917        pa = parse_gml_data(xnode->children, hasz, root_srid);
918        if (pa->npoints != 1) lwerror("invalid GML representation");
919
920        srs = parse_gml_srs(xnode);
921        if (srs->reverse_axis) pa = gml_reverse_axis_pa(pa);
922        if (!*root_srid) {
923                *root_srid = srs->srid;
924                geom = (LWGEOM *) lwpoint_construct(*root_srid, NULL, pa);
925        } else  {
926                if (srs->srid != *root_srid)
927                        gml_reproject_pa(pa, srs->srid, *root_srid);
928                geom = (LWGEOM *) lwpoint_construct(-1, NULL, pa);
929        }
930        lwfree(srs);
931
932        return geom;
933}
934
935
936/**
937 * Parse GML lineString (2.1.2, 3.1.1)
938 */
939static LWGEOM* parse_gml_line(xmlNodePtr xnode, bool *hasz, int *root_srid)
940{
941        gmlSrs *srs;
942        LWGEOM *geom;
943        POINTARRAY *pa;
944
945        if (xnode->children == NULL) lwerror("invalid GML representation");
946        pa = parse_gml_data(xnode->children, hasz, root_srid);
947        if (pa->npoints < 2) lwerror("invalid GML representation");
948
949        srs = parse_gml_srs(xnode);
950        if (srs->reverse_axis) pa = gml_reverse_axis_pa(pa);
951        if (!*root_srid) {
952                *root_srid = srs->srid;
953                geom = (LWGEOM *) lwline_construct(*root_srid, NULL, pa);
954        } else  {
955                if (srs->srid != *root_srid)
956                        gml_reproject_pa(pa, srs->srid, *root_srid);
957                geom = (LWGEOM *) lwline_construct(-1, NULL, pa);
958        }
959        lwfree(srs);
960
961        return geom;
962}
963
964
965/**
966 * Parse GML Curve (3.1.1)
967 */
968static LWGEOM* parse_gml_curve(xmlNodePtr xnode, bool *hasz, int *root_srid)
969{
970        xmlChar *interpolation;
971        int lss, last, i;
972        POINTARRAY **ppa;
973        POINTARRAY *pa;
974        xmlNodePtr xa;
975        LWGEOM *geom;
976        gmlSrs *srs;
977        bool found=false;
978        unsigned int npoints=0;
979
980        /* Looking for gml:segments */
981        for (xa = xnode->children ; xa != NULL ; xa = xa->next) {
982
983                if (xa->type != XML_ELEMENT_NODE) continue;
984                if (!is_gml_namespace(xa, false)) continue;
985                if (!strcmp((char *) xa->name, "segments")) {
986                        found = true;
987                        break;
988                }
989        }
990        if (!found) lwerror("invalid GML representation");
991
992        ppa = (POINTARRAY**) lwalloc(sizeof(POINTARRAY*));
993
994        /* Processing each gml:LineStringSegment */
995        for (xa = xa->children, lss=0; xa != NULL ; xa = xa->next) {
996
997                if (xa->type != XML_ELEMENT_NODE) continue;
998                if (!is_gml_namespace(xa, false)) continue;
999                if (strcmp((char *) xa->name, "LineStringSegment")) continue;
1000
1001                /* GML SF is resticted to linear interpolation  */
1002                interpolation = gmlGetProp(xa, (xmlChar *) "interpolation");
1003                if (interpolation != NULL) {
1004                        if (strcmp((char *) interpolation, "linear"))
1005                                lwerror("invalid GML representation");
1006                        xmlFree(interpolation);
1007                }
1008
1009                if (lss > 0) ppa = (POINTARRAY**) lwrealloc((POINTARRAY *) ppa,
1010                                sizeof(POINTARRAY*) * (lss + 1));
1011
1012                ppa[lss] = parse_gml_data(xa->children, hasz, root_srid);
1013                npoints += ppa[lss]->npoints;
1014                if (ppa[lss]->npoints < 2)
1015                        lwerror("invalid GML representation");
1016                lss++;
1017        }
1018        if (lss == 0) lwerror("invalid GML representation");
1019
1020        /* Most common case, a single segment */
1021        if (lss == 1) pa = ppa[0];
1022
1023
1024        /*
1025         * "The curve segments are connected to one another, with the end point
1026         *  of each segment except the last being the start point of the next
1027         *  segment"  from  ISO 19107 -> 6.3.16.1 (p43)
1028         *
1029         * So we must aggregate all the segments into a single one and avoid
1030         * to copy the redundants points
1031         */
1032        if (lss > 1) {
1033                pa = ptarray_construct(1, 0, npoints - (lss - 1));
1034                for (last = npoints = i = 0; i < lss ; i++) {
1035
1036                        if (i + 1 == lss) last = 1;
1037                        /* Check if segments are not disjoints */
1038                        if (i > 0 && memcmp(    getPoint_internal(pa, npoints),
1039                                                getPoint_internal(ppa[i], 0),
1040                                                *hasz?sizeof(POINT3D):sizeof(POINT2D)))
1041                                lwerror("invalid GML representation");
1042
1043                        /* Aggregate stuff */
1044                        memcpy( getPoint_internal(pa, npoints),
1045                                getPoint_internal(ppa[i], 0),
1046                                pointArray_ptsize(ppa[i]) * (ppa[i]->npoints + last));
1047
1048                        npoints += ppa[i]->npoints - 1;
1049                        lwfree(ppa[i]);
1050                }       
1051                lwfree(ppa);
1052        }
1053
1054        srs = parse_gml_srs(xnode);
1055        if (srs->reverse_axis) pa = gml_reverse_axis_pa(pa);
1056        if (!*root_srid) {
1057                *root_srid = srs->srid;
1058                geom = (LWGEOM *) lwline_construct(*root_srid, NULL, pa);
1059        } else  {
1060                if (srs->srid != *root_srid)
1061                        gml_reproject_pa(pa, srs->srid, *root_srid);
1062                geom = (LWGEOM *) lwline_construct(-1, NULL, pa);
1063        }
1064        lwfree(srs);
1065
1066        return geom;
1067}
1068
1069
1070/**
1071 * Parse GML Polygon (2.1.2, 3.1.1)
1072 */
1073static LWGEOM* parse_gml_polygon(xmlNodePtr xnode, bool *hasz, int *root_srid)
1074{
1075        gmlSrs *srs;
1076        int i, ring;
1077        LWGEOM *geom;
1078        xmlNodePtr xa, xb;
1079        POINTARRAY **ppa = NULL;
1080
1081        srs = parse_gml_srs(xnode);
1082        for (xa = xnode->children ; xa != NULL ; xa = xa->next) {
1083
1084                /* Polygon/outerBoundaryIs -> GML 2.1.2 */
1085                /* Polygon/exterior        -> GML 3.1.1 */
1086                if (xa->type != XML_ELEMENT_NODE) continue;
1087                if (!is_gml_namespace(xa, false)) continue;
1088                if  (strcmp((char *) xa->name, "outerBoundaryIs") &&
1089                     strcmp((char *) xa->name, "exterior")) continue;
1090               
1091                for (xb = xa->children ; xb != NULL ; xb = xb->next) {
1092
1093                        if (xb->type != XML_ELEMENT_NODE) continue;
1094                        if (!is_gml_namespace(xb, false)) continue;
1095                        if (strcmp((char *) xb->name, "LinearRing")) continue;
1096                       
1097                        ppa = (POINTARRAY**) lwalloc(sizeof(POINTARRAY*));
1098                        ppa[0] = parse_gml_data(xb->children, hasz, root_srid);
1099
1100                        if (ppa[0]->npoints < 4
1101                                || (!*hasz && !ptarray_isclosed2d(ppa[0]))
1102                                ||  (*hasz && !ptarray_isclosed3d(ppa[0])))
1103                                lwerror("invalid GML representation");
1104
1105                        if (srs->reverse_axis) ppa[0] = gml_reverse_axis_pa(ppa[0]);
1106                }
1107        }
1108
1109        for (ring=1, xa = xnode->children ; xa != NULL ; xa = xa->next) {
1110
1111                /* Polygon/innerBoundaryIs -> GML 2.1.2 */
1112                /* Polygon/interior        -> GML 3.1.1 */
1113                if (xa->type != XML_ELEMENT_NODE) continue;
1114                if (!is_gml_namespace(xa, false)) continue;
1115                if  (strcmp((char *) xa->name, "innerBoundaryIs") && 
1116                     strcmp((char *) xa->name, "interior")) continue;
1117               
1118                for (xb = xa->children ; xb != NULL ; xb = xb->next) {
1119
1120                        if (xb->type != XML_ELEMENT_NODE) continue;
1121                        if (!is_gml_namespace(xb, false)) continue;
1122                        if (strcmp((char *) xb->name, "LinearRing")) continue;
1123
1124                        ppa = (POINTARRAY**) lwrealloc((POINTARRAY *) ppa,
1125                                sizeof(POINTARRAY*) * (ring + 1));
1126                        ppa[ring] = parse_gml_data(xb->children, hasz, root_srid);
1127
1128                        if (ppa[ring]->npoints < 4
1129                                || (!*hasz && !ptarray_isclosed2d(ppa[ring]))
1130                                ||  (*hasz && !ptarray_isclosed3d(ppa[ring])))
1131                                lwerror("invalid GML representation");
1132
1133                        if (srs->reverse_axis) ppa[ring] = gml_reverse_axis_pa(ppa[ring]);
1134                        ring++;
1135                }
1136        }
1137                       
1138        /* Exterior Ring is mandatory */
1139        if (ppa == NULL || ppa[0] == NULL) lwerror("invalid GML representation");
1140
1141        if (!*root_srid) {
1142                *root_srid = srs->srid;
1143                geom = (LWGEOM *) lwpoly_construct(*root_srid, NULL, ring, ppa);
1144        } else  {
1145                if (srs->srid != *root_srid) {
1146                        for (i=0 ; i < ring ; i++)
1147                                gml_reproject_pa(ppa[i], srs->srid, *root_srid);
1148                }
1149                geom = (LWGEOM *) lwpoly_construct(-1, NULL, ring, ppa);
1150        }
1151        lwfree(srs);
1152
1153        return geom;
1154}
1155
1156
1157/**
1158 * Parse GML Surface (3.1.1)
1159 */
1160static LWGEOM* parse_gml_surface(xmlNodePtr xnode, bool *hasz, int *root_srid)
1161{
1162        xmlChar *interpolation = 0;
1163        xmlNodePtr xa, xb, xc;
1164        int i, patch, ring = 1;
1165        POINTARRAY **ppa = 0;
1166        LWGEOM *geom = 0;
1167        gmlSrs *srs = 0;
1168        bool found=false;
1169
1170        srs = parse_gml_srs(xnode);
1171        /* Looking for gml:patches */
1172        for (xa = xnode->children ; xa != NULL ; xa = xa->next) {
1173
1174                if (xa->type != XML_ELEMENT_NODE) continue;
1175                if (!is_gml_namespace(xa, false)) continue;
1176                if (!strcmp((char *) xa->name, "patches")) {
1177                        found = true;
1178                        break;
1179                }
1180        }
1181        if (!found) lwerror("invalid GML representation");
1182
1183        /* Processing gml:PolygonPatch */
1184        for (patch=0, xa = xa->children ; xa != NULL ; xa = xa->next) {
1185
1186                if (xa->type != XML_ELEMENT_NODE) continue;
1187                if (!is_gml_namespace(xa, false)) continue;
1188                if (strcmp((char *) xa->name, "PolygonPatch")) continue;
1189                patch++;
1190
1191                /* SQL/MM define ST_CurvePolygon as a single patch only,
1192                   cf ISO 13249-3 -> 4.2.9 (p27) */
1193                if (patch > 1) lwerror("invalid GML representation");
1194
1195                /* GML SF is resticted to planar interpolation  */
1196                interpolation = gmlGetProp(xa, (xmlChar *) "interpolation");
1197                if (interpolation != NULL) {
1198                        if (strcmp((char *) interpolation, "planar"))
1199                                lwerror("invalid GML representation");
1200                        xmlFree(interpolation);
1201                }
1202
1203                /* PolygonPatch/exterior */
1204                for (xb = xa->children ; xb != NULL ; xb = xb->next) {
1205                        if (strcmp((char *) xb->name, "exterior")) continue;
1206               
1207                        /* PolygonPatch/exterior/LinearRing */
1208                        for (xc = xb->children ; xc != NULL ; xc = xc->next) {
1209
1210                                if (xc->type != XML_ELEMENT_NODE) continue;
1211                                if (!is_gml_namespace(xc, false)) continue;
1212                                if (strcmp((char *) xc->name, "LinearRing")) continue;
1213                       
1214                                ppa = (POINTARRAY**) lwalloc(sizeof(POINTARRAY*));
1215                                ppa[0] = parse_gml_data(xc->children, hasz, root_srid);
1216
1217                                if (ppa[0]->npoints < 4
1218                                        || (!*hasz && !ptarray_isclosed2d(ppa[0]))
1219                                        ||  (*hasz && !ptarray_isclosed3d(ppa[0])))
1220                                        lwerror("invalid GML representation");
1221
1222                                if (srs->reverse_axis) ppa[0] = gml_reverse_axis_pa(ppa[0]);
1223                        }
1224                }
1225
1226                /* PolygonPatch/interior */
1227                for (ring=1, xb = xa->children ; xb != NULL ; xb = xb->next) {
1228
1229                        if (xb->type != XML_ELEMENT_NODE) continue;
1230                        if (!is_gml_namespace(xb, false)) continue;
1231                        if (strcmp((char *) xb->name, "interior")) continue;
1232
1233                        /* PolygonPatch/interior/LinearRing */
1234                        for (xc = xb->children ; xc != NULL ; xc = xc->next) {
1235                                if (xc->type != XML_ELEMENT_NODE) continue;
1236                                if (strcmp((char *) xc->name, "LinearRing")) continue;
1237
1238                                ppa = (POINTARRAY**) lwrealloc((POINTARRAY *) ppa,
1239                                        sizeof(POINTARRAY*) * (ring + 1));
1240                                ppa[ring] = parse_gml_data(xc->children, hasz, root_srid);
1241
1242                                if (ppa[ring]->npoints < 4
1243                                        || (!*hasz && !ptarray_isclosed2d(ppa[ring]))
1244                                        || ( *hasz && !ptarray_isclosed3d(ppa[ring])))
1245                                        lwerror("invalid GML representation");
1246
1247                                if (srs->reverse_axis) ppa[ring] = gml_reverse_axis_pa(ppa[ring]);
1248                                ring++;
1249                        }
1250                }
1251        }
1252
1253        /* Exterior Ring is mandatory */
1254        if (ppa == NULL || ppa[0] == NULL) lwerror("invalid GML representation");
1255
1256        if (!*root_srid) {
1257                *root_srid = srs->srid;
1258                geom = (LWGEOM *) lwpoly_construct(*root_srid, NULL, ring, ppa);
1259        } else  {
1260                if (srs->srid != *root_srid) {
1261                        for (i=0 ; i < ring ; i++)
1262                                gml_reproject_pa(ppa[i], srs->srid, *root_srid);
1263                }
1264                geom = (LWGEOM *) lwpoly_construct(-1, NULL, ring, ppa);
1265        }
1266        lwfree(srs);
1267
1268        return geom;
1269}
1270
1271
1272/**
1273 * Parse gml:MultiPoint (2.1.2, 3.1.1)
1274 */
1275static LWGEOM* parse_gml_mpoint(xmlNodePtr xnode, bool *hasz, int *root_srid)
1276{
1277        gmlSrs *srs;
1278        xmlNodePtr xa;
1279        LWGEOM *geom = NULL;
1280
1281        srs = parse_gml_srs(xnode);
1282        if (!*root_srid) {
1283                *root_srid = srs->srid;
1284                geom = (LWGEOM *)lwcollection_construct_empty(*root_srid, 1, 0);
1285                geom->type = lwgeom_makeType(1, 1, 0, MULTIPOINTTYPE);
1286        } else  {
1287                geom = (LWGEOM *)lwcollection_construct_empty(-1, 1, 0);
1288                geom->type = lwgeom_makeType(1, 0, 0, MULTIPOINTTYPE);
1289        }
1290        lwfree(srs);
1291
1292        for (xa = xnode->children ; xa != NULL ; xa = xa->next) {
1293
1294                /* MultiPoint/pointMember */
1295                if (xa->type != XML_ELEMENT_NODE) continue;
1296                if (!is_gml_namespace(xa, false)) continue;
1297                if (strcmp((char *) xa->name, "pointMember")) continue;
1298                if (xa->children != NULL)
1299                        geom = lwmpoint_add((LWMPOINT *)geom, -1,
1300                                parse_gml(xa->children, hasz, root_srid));
1301        }
1302
1303        return geom;
1304}
1305
1306
1307/**
1308 * Parse gml:MultiLineString (2.1.2, 3.1.1)
1309 */
1310static LWGEOM* parse_gml_mline(xmlNodePtr xnode, bool *hasz, int *root_srid)
1311{
1312        gmlSrs *srs;
1313        xmlNodePtr xa;
1314        LWGEOM *geom = NULL;
1315
1316        srs = parse_gml_srs(xnode);
1317        if (!*root_srid) {
1318                *root_srid = srs->srid;
1319                geom = (LWGEOM *)lwcollection_construct_empty(*root_srid, 1, 0);
1320                geom->type = lwgeom_makeType(1, 1, 0, MULTILINETYPE);
1321        } else  {
1322                geom = (LWGEOM *)lwcollection_construct_empty(-1, 1, 0);
1323                geom->type = lwgeom_makeType(1, 0, 0, MULTILINETYPE);
1324        }
1325        lwfree(srs);
1326
1327        for (xa = xnode->children ; xa != NULL ; xa = xa->next) {
1328
1329                /* MultiLineString/lineStringMember */
1330                if (xa->type != XML_ELEMENT_NODE) continue;
1331                if (!is_gml_namespace(xa, false)) continue;
1332                if (strcmp((char *) xa->name, "lineStringMember")) continue;
1333                if (xa->children != NULL)
1334                        geom = lwmline_add((LWMLINE *)geom, -1,
1335                                parse_gml(xa->children, hasz, root_srid));
1336        }
1337
1338        return geom;
1339}
1340
1341
1342/**
1343 * Parse GML MultiCurve (3.1.1)
1344 */
1345static LWGEOM* parse_gml_mcurve(xmlNodePtr xnode, bool *hasz, int *root_srid)
1346{
1347        gmlSrs *srs;
1348        xmlNodePtr xa;
1349        LWGEOM *geom = NULL;
1350
1351        srs = parse_gml_srs(xnode);
1352        if (!*root_srid) {
1353                *root_srid = srs->srid;
1354                geom = (LWGEOM *)lwcollection_construct_empty(*root_srid, 1, 0);
1355                geom->type = lwgeom_makeType(1, 1, 0, MULTILINETYPE);
1356        } else  {
1357                geom = (LWGEOM *)lwcollection_construct_empty(-1, 1, 0);
1358                geom->type = lwgeom_makeType(1, 0, 0, MULTILINETYPE);
1359        }
1360        lwfree(srs);
1361
1362        for (xa = xnode->children ; xa != NULL ; xa = xa->next) {
1363
1364                /* MultiCurve/curveMember */
1365                if (xa->type != XML_ELEMENT_NODE) continue;
1366                if (!is_gml_namespace(xa, false)) continue;
1367                if (strcmp((char *) xa->name, "curveMember")) continue;
1368                if (xa->children != NULL)
1369                        geom = lwmline_add((LWMLINE *)geom, -1,
1370                                parse_gml(xa->children, hasz, root_srid));
1371        }
1372
1373        return geom;
1374}
1375
1376
1377/**
1378 * Parse GML MultiPolygon (2.1.2, 3.1.1)
1379 */
1380static LWGEOM* parse_gml_mpoly(xmlNodePtr xnode, bool *hasz, int *root_srid)
1381{
1382        gmlSrs *srs;
1383        xmlNodePtr xa;
1384        LWGEOM *geom = NULL;
1385
1386        srs = parse_gml_srs(xnode);
1387        if (!*root_srid) {
1388                *root_srid = srs->srid;
1389                geom = (LWGEOM *)lwcollection_construct_empty(*root_srid, 1, 0);
1390                geom->type = lwgeom_makeType(1, 1, 0, MULTIPOLYGONTYPE);
1391        } else  {
1392                geom = (LWGEOM *)lwcollection_construct_empty(-1, 1, 0);
1393                geom->type = lwgeom_makeType(1, 0, 0, MULTIPOLYGONTYPE);
1394        }
1395        lwfree(srs);
1396
1397        for (xa = xnode->children ; xa != NULL ; xa = xa->next) {
1398
1399                /* MultiPolygon/polygonMember */
1400                if (xa->type != XML_ELEMENT_NODE) continue;
1401                if (!is_gml_namespace(xa, false)) continue;
1402                if (strcmp((char *) xa->name, "polygonMember")) continue;
1403                if (xa->children != NULL)
1404                        geom = lwmpoly_add((LWMPOLY *)geom, -1,
1405                                parse_gml(xa->children, hasz, root_srid));
1406        }
1407
1408        return geom;
1409}
1410
1411
1412/**
1413 * Parse GML MultiSurface (3.1.1)
1414 */
1415static LWGEOM* parse_gml_msurface(xmlNodePtr xnode, bool *hasz, int *root_srid)
1416{
1417        gmlSrs *srs;
1418        xmlNodePtr xa;
1419        LWGEOM *geom = NULL;
1420
1421        srs = parse_gml_srs(xnode);
1422        if (!*root_srid) {
1423                *root_srid = srs->srid;
1424                geom = (LWGEOM *)lwcollection_construct_empty(*root_srid, 1, 0);
1425                geom->type = lwgeom_makeType(1, 1, 0, MULTIPOLYGONTYPE);
1426        } else  {
1427                geom = (LWGEOM *)lwcollection_construct_empty(-1, 1, 0);
1428                geom->type = lwgeom_makeType(1, 0, 0, MULTIPOLYGONTYPE);
1429        }
1430        lwfree(srs);
1431
1432        for (xa = xnode->children ; xa != NULL ; xa = xa->next) {
1433
1434                /* MultiSurface/surfaceMember */
1435                if (xa->type != XML_ELEMENT_NODE) continue;
1436                if (!is_gml_namespace(xa, false)) continue;
1437                if (strcmp((char *) xa->name, "surfaceMember")) continue;
1438                if (xa->children != NULL)
1439                        geom = lwmpoly_add((LWMPOLY *)geom, -1,
1440                                parse_gml(xa->children, hasz, root_srid));
1441        }
1442
1443        return geom;
1444}
1445
1446
1447/**
1448 * Parse GML MultiGeometry (2.1.2, 3.1.1)
1449 */
1450static LWGEOM* parse_gml_coll(xmlNodePtr xnode, bool *hasz, int *root_srid) 
1451{
1452        gmlSrs *srs;
1453        xmlNodePtr xa;
1454        LWGEOM *geom = NULL;
1455
1456        srs = parse_gml_srs(xnode);
1457        if (!*root_srid) {
1458                *root_srid = srs->srid;
1459                geom = (LWGEOM *)lwcollection_construct_empty(*root_srid, 1, 0);
1460                geom->type = lwgeom_makeType(1, 1, 0, COLLECTIONTYPE);
1461        } else  {
1462                geom = (LWGEOM *)lwcollection_construct_empty(-1, 1, 0);
1463                geom->type = lwgeom_makeType(1, 0, 0, COLLECTIONTYPE);
1464        }
1465        lwfree(srs);
1466
1467        for (xa = xnode->children ; xa != NULL ; xa = xa->next) {
1468
1469                if (xa->type != XML_ELEMENT_NODE) continue;
1470                if (!is_gml_namespace(xa, false)) continue;
1471
1472                /*
1473                 * In GML 2.1.2 pointMember, lineStringMember and
1474                 * polygonMember are parts of geometryMember
1475                 * substitution group
1476                 */
1477                if (       !strcmp((char *) xa->name, "pointMember")
1478                        || !strcmp((char *) xa->name, "lineStringMember")
1479                        || !strcmp((char *) xa->name, "polygonMember")
1480                        || !strcmp((char *) xa->name, "geometryMember")) {
1481
1482                        if (xa->children == NULL) break;
1483                        geom = lwcollection_add((LWCOLLECTION *)geom, -1,
1484                                 parse_gml(xa->children, hasz, root_srid));
1485                }
1486        }
1487
1488        return geom;
1489}
1490
1491
1492/**
1493 * Parse GML
1494 */
1495static LWGEOM* parse_gml(xmlNodePtr xnode, bool *hasz, int *root_srid) 
1496{
1497        xmlNodePtr xa = xnode;
1498
1499        while (xa != NULL && (xa->type != XML_ELEMENT_NODE
1500                        || !is_gml_namespace(xa, false))) xa = xa->next;
1501
1502        if (xa == NULL) lwerror("invalid GML representation");
1503
1504        if (!strcmp((char *) xa->name, "Point"))
1505                return parse_gml_point(xa, hasz, root_srid);
1506
1507        if (!strcmp((char *) xa->name, "LineString"))
1508                return parse_gml_line(xa, hasz, root_srid);
1509
1510        if (!strcmp((char *) xa->name, "Curve"))
1511                return parse_gml_curve(xa, hasz, root_srid);
1512
1513        if (!strcmp((char *) xa->name, "Polygon"))
1514                return parse_gml_polygon(xa, hasz, root_srid);
1515
1516        if (!strcmp((char *) xa->name, "Surface"))
1517                return parse_gml_surface(xa, hasz, root_srid);
1518
1519        if (!strcmp((char *) xa->name, "MultiPoint"))
1520                return parse_gml_mpoint(xa, hasz, root_srid);
1521
1522        if (!strcmp((char *) xa->name, "MultiLineString"))
1523                return parse_gml_mline(xa, hasz, root_srid);
1524
1525        if (!strcmp((char *) xa->name, "MultiCurve"))
1526                return parse_gml_mcurve(xa, hasz, root_srid);
1527
1528        if (!strcmp((char *) xa->name, "MultiPolygon"))
1529                return parse_gml_mpoly(xa, hasz, root_srid);
1530
1531        if (!strcmp((char *) xa->name, "MultiSurface"))
1532                return parse_gml_msurface(xa, hasz, root_srid);
1533
1534        if (!strcmp((char *) xa->name, "MultiGeometry"))
1535                return parse_gml_coll(xa, hasz, root_srid);
1536       
1537        lwerror("invalid GML representation");
1538        return NULL; /* Never reach */
1539}
1540
1541#endif /* if HAVE_LIBXML2 */
Note: See TracBrowser for help on using the repository browser.