]> granicus.if.org Git - postgis/commitdiff
Initial support of Xlink. Add related units tests. Few cleaning
authorOlivier Courtin <olivier.courtin@camptocamp.com>
Tue, 3 Nov 2009 22:26:25 +0000 (22:26 +0000)
committerOlivier Courtin <olivier.courtin@camptocamp.com>
Tue, 3 Nov 2009 22:26:25 +0000 (22:26 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@4731 b70326c6-7e19-0410-871a-916f4a2858ee

postgis/lwgeom_in_gml.c
regress/in_gml.sql
regress/in_gml_expected

index 8caaaee421f8a7e2317c27e09985a84c087453f5..54725023a3de33a25d463b93de2ebaa2fd5c7b97 100644 (file)
 * Cf: ISO 13249-3 -> 5.1.50 (p 134) 
 *
 * GML versions supported:
+*  - GML 3.2.1 Namespace
+*  - GML 3.1.1 Simple Features profile
+*  - GML 3.1.0 and 3.0.0 SF elements and attributes
 *  - GML 2.1.2
-*  - GML 3.1.1 Simple Features profile (1.0.0)
 * Cf: <http://www.opengeospatial.org/standards/gml>
 *
-* Current known limitations:
-*  - Support only GML SF profile geometries (i.e no PostGIS curves support)
-*  - Don't handle Xlink
+* NOTA: this code doesn't (yet ?) support SQL/MM curves
 *
 * Written by Olivier Courtin - Oslandia
 *
@@ -42,6 +42,9 @@
 #if HAVE_LIBXML2
 #include <libxml/tree.h> 
 #include <libxml/parser.h> 
+#include <libxml/xpath.h> 
+#include <libxml/xpathInternals.h>
+
 
 
 Datum geom_from_gml(PG_FUNCTION_ARGS);
@@ -54,6 +57,10 @@ typedef struct struct_gmlSrs
 }
 gmlSrs;
 
+#define XLINK_NS       ((char *) "http://www.w3.org/1999/xlink")
+#define GML_NS         ((char *) "http://www.opengis.net/gml")
+#define GML32_NS       ((char *) "http://www.opengis.net/gml/3.2")
+
 
 /**
  * Ability to parse GML geometry fragment and to return an LWGEOM
@@ -126,6 +133,85 @@ Datum geom_from_gml(PG_FUNCTION_ARGS)
 }
                        
 
+/**
+ * Return true if current node contains a simple xlink 
+ * Return false otherwise.
+ */
+static bool is_xlink(xmlNodePtr node)
+{
+       xmlChar *prop;
+
+       prop = xmlGetNsProp(node, (xmlChar *)"type", (xmlChar *) XLINK_NS);
+       if (prop == NULL) return false;
+       if (strcmp((char *) prop, "simple")) {
+               xmlFree(prop);
+               return false;
+       }
+
+       prop = xmlGetNsProp(node, (xmlChar *)"href", (xmlChar *) XLINK_NS);
+       if (prop == NULL) return false;
+       if (prop[0] != '#') {
+               xmlFree(prop);
+               return false;
+       }
+       xmlFree(prop);
+
+       return true;
+}
+
+
+/**
+ * Return a xmlNodePtr on a node referenced by a xlink or NULL otherwise 
+ */
+static xmlNodePtr get_xlink_node(xmlNodePtr xnode)
+{
+       char *id;
+        xmlNodePtr node;
+       xmlNsPtr *ns, *n;
+       xmlChar *href, *p;
+        xmlXPathContext *ctx;
+        xmlXPathObject *xpath;
+
+       href = xmlGetNsProp(xnode, (xmlChar *)"href", (xmlChar *) XLINK_NS);
+       id = lwalloc((xmlStrlen(xnode->ns->prefix) * 2 + xmlStrlen(xnode->name)
+                               + xmlStrlen(href) + sizeof("//:[@:id='']") + 1));
+       p = href;
+       p++; /* ignore '#' first char */
+
+       /* Xpath pattern look like:             //gml:point[@gml:id='p1']   */
+       sprintf(id, "//%s:%s[@%s:id='%s']",     (char *) xnode->ns->prefix,
+                                               (char *) xnode->name,
+                                               (char *) xnode->ns->prefix,
+                                               (char *) p);
+       xmlFree(href);
+
+        ctx = xmlXPathNewContext(xnode->doc);
+       if (ctx == NULL) {
+               lwfree(id);
+               return NULL;
+       }
+
+       /* Handle namespaces */
+       ns = xmlGetNsList(xnode->doc, xnode);
+       for (n=ns ; *n; n++) xmlXPathRegisterNs(ctx, (*n)->prefix, (*n)->href);
+       xmlFree(ns);
+
+       /* Execute Xpath expression */
+        xpath = xmlXPathEvalExpression((xmlChar *) id, ctx);
+       lwfree(id);
+        if (xpath == NULL || xpath->nodesetval == NULL || xpath->nodesetval->nodeNr != 1) {
+               xmlXPathFreeObject(xpath);
+               xmlXPathFreeContext(ctx);
+               return NULL;
+       }
+        node = xpath->nodesetval->nodeTab[0];
+       xmlXPathFreeObject(xpath);
+       xmlXPathFreeContext(ctx);
+
+       return node;
+}
+
+
 /**
  * Return false if current element namespace is not a GML one
  * Return true otherwise.
@@ -149,10 +235,11 @@ static bool is_gml_namespace(xmlNodePtr xnode, bool is_strict)
         */
        for (p=ns ; *p ; p++) {
                if ((*p)->href == NULL) continue;
-               if (!strcmp((char *) (*p)->href, "http://www.opengis.net/gml") ||
-                   !strcmp((char *) (*p)->href, "http://www.opengis.net/gml/3.2")) {
-                       if ((*p)->prefix == NULL ||
-                                       !xmlStrcmp(xnode->ns->prefix, (*p)->prefix)) {
+               if (!strcmp((char *) (*p)->href, GML_NS) ||
+                   !strcmp((char *) (*p)->href, GML32_NS)) {
+                       if (    (*p)->prefix == NULL ||
+                               !xmlStrcmp(xnode->ns->prefix, (*p)->prefix)) {
+
                                xmlFree(ns);
                                return true;
                        }
@@ -171,8 +258,6 @@ static bool is_gml_namespace(xmlNodePtr xnode, bool is_strict)
 static xmlChar *gmlGetProp(xmlNodePtr xnode, xmlChar *prop)
 {
        xmlChar *value;
-       xmlChar gmlns[]="http://www.opengis.net/gml"; 
-       xmlChar gmlns32[]="http://www.opengis.net/gml/3.2"; 
 
        if (!is_gml_namespace(xnode, true))
                return xmlGetProp(xnode, prop);
@@ -180,16 +265,16 @@ static xmlChar *gmlGetProp(xmlNodePtr xnode, xmlChar *prop)
        /* We begin to try without explicit namespace */
        value = xmlGetNoNsProp(xnode, prop);
        if (value != NULL && xnode->ns->href != NULL &&
-                  (!xmlStrcmp(xnode->ns->href, gmlns) ||
-                   !xmlStrcmp(xnode->ns->href, gmlns32))) return value;
+                  (!strcmp((char *) xnode->ns->href, GML_NS) ||
+                   !strcmp((char *) xnode->ns->href, GML32_NS))) return value;
 
        /*
         * Handle namespaces:
         *  - http://www.opengis.net/gml      (GML 3.1.1 and priors)
         *  - http://www.opengis.net/gml/3.2  (GML 3.2.1)
         */
-       value = xmlGetNsProp(xnode, prop, gmlns);
-       if (value == NULL) value = xmlGetNsProp(xnode, prop, gmlns32);
+       value = xmlGetNsProp(xnode, prop, (xmlChar *) GML_NS);
+       if (value == NULL)value = xmlGetNsProp(xnode, prop, (xmlChar *) GML32_NS);
 
        return value;
 }
@@ -794,14 +879,12 @@ static POINTARRAY* parse_gml_data(xmlNodePtr xnode, bool *hasz, int *root_srid)
                                        break;
                                }
                        }
-                       if (!found)
-                               lwerror("A invalid GML representation");
 
-                       if (xb == NULL)
-                               lwerror("B invalid GML representation");
+                       if (!found || xb == NULL) lwerror("invalid GML representation");
 
-                       if (!found || xb == NULL || xb->children == NULL)
-                               lwerror("C invalid GML representation");
+                       if (is_xlink(xb)) xb = get_xlink_node(xb);
+                       if (xb == NULL || xb->children == NULL)
+                               lwerror("invalid GML representation");
 
                        tmp_pa = parse_gml_data(xb->children, hasz, root_srid);
                        if (tmp_pa->npoints != 1) lwerror("invalid GML representation");
index 61988c39bfde33be0f65c38f4ef9ac1ff1527c87..ea0b0f9cdc9ee7f01c720581f914bef10e1d5965 100644 (file)
@@ -729,8 +729,38 @@ SELECT 'data_1', ST_AsEWKT(ST_GeomFromGML('<gml:LineString><gml:pos>1 2</gml:pos
 -- Mixed pos, posList, pointProperty, pointRep
 SELECT 'data_2', ST_AsEWKT(ST_GeomFromGML('<gml:LineString><gml:pos>1 2</gml:pos><gml:posList>3 4 5 6</gml:posList><gml:pointProperty><gml:point><gml:pos>7 8</gml:pos></gml:point></gml:pointProperty><gml:pointRep><gml:point><gml:coordinates>9,10</gml:coordinates></gml:point></gml:pointRep></gml:LineString>'));
 
--- TODO xlink pointProperty
---SELECT 'data_3', ST_AsEWKT(ST_GeomFromGML('<gml:LineString xmlns:xlink = "http://www.w3.org/1999/xlink"><gml:pointProperty><gml:point gml:id="p1"><gml:pos>1 2</gml:pos></gml:point></gml:pointProperty><gml:pointProperty><gml:point xlink:type="Simple" xlink:href="#p1"/></gml:pointProperty></gml:LineString>'));
+
+
+--
+-- Xlink
+--
+
+-- Xlink on a point
+SELECT 'xlink_1', ST_AsEWKT(ST_GeomFromGML('<gml:LineString xmlns:gml="http://www.opengis.net/gml" xmlns:xlink="http://www.w3.org/1999/xlink"><gml:pointProperty><gml:point gml:id="p1"><gml:pos>1 2</gml:pos></gml:point></gml:pointProperty><gml:pointProperty><gml:point xlink:type="simple" xlink:href="#p1"/></gml:pointProperty><gml:pos>3 4</gml:pos></gml:LineString>'));
+
+-- ERROR: xlink:href destination is not defined
+SELECT 'xlink_2', ST_AsEWKT(ST_GeomFromGML('<gml:LineString xmlns:gml="http://www.opengis.net/gml" xmlns:xlink="http://www.w3.org/1999/xlink"><gml:pointProperty><gml:point gml:id="p1"><gml:pos>1 2</gml:pos></gml:point></gml:pointProperty><gml:pointProperty><gml:point xlink:type="simple" xlink:href="#p2"/></gml:pointProperty><gml:pos>3 4</gml:pos></gml:LineString>'));
+
+-- ERROR: no href 
+SELECT 'xlink_3', ST_AsEWKT(ST_GeomFromGML('<gml:LineString xmlns:gml="http://www.opengis.net/gml" xmlns:xlink="http://www.w3.org/1999/xlink"><gml:pointProperty><gml:point gml:id="p1"><gml:pos>1 2</gml:pos></gml:point></gml:pointProperty><gml:pointProperty><gml:point xlink:type="simple" /></gml:pointProperty><gml:pos>3 4</gml:pos></gml:LineString>'));
+
+-- ERROR: empty href 
+SELECT 'xlink_4', ST_AsEWKT(ST_GeomFromGML('<gml:LineString xmlns:gml="http://www.opengis.net/gml" xmlns:xlink="http://www.w3.org/1999/xlink"><gml:pointProperty><gml:point gml:id="p1"><gml:pos>1 2</gml:pos></gml:point></gml:pointProperty><gml:pointProperty><gml:point xlink:type="simple" xlink:href=""/></gml:pointProperty><gml:pos>3 4</gml:pos></gml:LineString>'));
+
+-- ERROR: no sharp char in href
+SELECT 'xlink_5', ST_AsEWKT(ST_GeomFromGML('<gml:LineString xmlns:gml="http://www.opengis.net/gml" xmlns:xlink="http://www.w3.org/1999/xlink"><gml:pointProperty><gml:point gml:id="p1"><gml:pos>1 2</gml:pos></gml:point></gml:pointProperty><gml:pointProperty><gml:point xlink:type="simple" xlink:href="p2"/></gml:pointProperty><gml:pos>3 4</gml:pos></gml:LineString>'));
+
+-- ERROR: no xlink namespace
+SELECT 'xlink_6', ST_AsEWKT(ST_GeomFromGML('<gml:LineString xmlns:gml="http://www.opengis.net/gml"><gml:pointProperty><gml:point gml:id="p1"><gml:pos>1 2</gml:pos></gml:point></gml:pointProperty><gml:pointProperty><gml:point xlink:type="simple" xlink:href="#p1"/></gml:pointProperty><gml:pos>3 4</gml:pos></gml:LineString>'));
+
+-- ERROR: wrong xlink namespace
+SELECT 'xlink_7', ST_AsEWKT(ST_GeomFromGML('<gml:LineString xmlns:gml="http://www.opengis.net/gml" xmlns:xlink="http://foo.net"><gml:pointProperty><gml:point gml:id="p1"><gml:pos>1 2</gml:pos></gml:point></gml:pointProperty><gml:pointProperty><gml:point xlink:type="simple" xlink:href="#p1"/></gml:pointProperty><gml:pos>3 4</gml:pos></gml:LineString>'));
+
+-- ERROR: no xlink:type 
+SELECT 'xlink_8', ST_AsEWKT(ST_GeomFromGML('<gml:LineString xmlns:gml="http://www.opengis.net/gml" xmlns:xlink="http://www.w3.org/1999/xlink"><gml:pointProperty><gml:point gml:id="p1"><gml:pos>1 2</gml:pos></gml:point></gml:pointProperty><gml:pointProperty><gml:point xlink:href="#p1"/></gml:pointProperty><gml:pos>3 4</gml:pos></gml:LineString>'));
+
+-- ERROR: xlink:type not simple
+SELECT 'xlink_9', ST_AsEWKT(ST_GeomFromGML('<gml:LineString xmlns:gml="http://www.opengis.net/gml" xmlns:xlink="http://www.w3.org/1999/xlink"><gml:pointProperty><gml:point gml:id="p1"><gml:pos>1 2</gml:pos></gml:point></gml:pointProperty><gml:pointProperty><gml:point xlink:type="extended" xlink:href="#p1"/></gml:pointProperty><gml:pos>3 4</gml:pos></gml:LineString>'));
 
 
 
index be3b1a4db78a63d581197ba160089c6f9b056c4a..aa7841cfdbf38933e9fbf26f09dd8a1e8b57283b 100644 (file)
@@ -226,6 +226,15 @@ poslist_17|LINESTRING(1 2,3 4)
 ERROR:  invalid GML representation
 data_1|LINESTRING(1 2,3 4,5 6,7 8,9 10,11 12)
 data_2|LINESTRING(1 2,3 4,5 6,7 8,9 10)
+xlink_1|LINESTRING(1 2,1 2,3 4)
+ERROR:  invalid GML representation
+ERROR:  invalid GML representation
+ERROR:  invalid GML representation
+ERROR:  invalid GML representation
+ERROR:  invalid GML representation
+ERROR:  invalid GML representation
+ERROR:  invalid GML representation
+ERROR:  invalid GML representation
 coord_1|POINT(1 2)
 coord_2|POINT(1 2 3)
 ERROR:  invalid GML representation