]> granicus.if.org Git - postgis/commitdiff
Modified GML output as suggested by Martin Daly.
authorSandro Santilli <strk@keybit.net>
Thu, 23 Sep 2004 15:09:07 +0000 (15:09 +0000)
committerSandro Santilli <strk@keybit.net>
Thu, 23 Sep 2004 15:09:07 +0000 (15:09 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@888 b70326c6-7e19-0410-871a-916f4a2858ee

lwgeom/lwgeom_gml.c
lwgeom/lwpostgis.sql.in

index ab20d3591350f86a6950d1aa9c386aa5ab696f9b..e48374b3ffe41922c219eb125a7fa2ee1cf79f0a 100644 (file)
 
 
 #include "postgres.h"
+#include "executor/spi.h"
 #include "lwgeom.h"
 
 Datum LWGEOM_asGML(PG_FUNCTION_ARGS);
-char *geometry_to_gml(LWGEOM *geometry, int version);
-static char *asgml_point(LWPOINT *point, int version);
-static char *asgml_line(LWLINE *line, int version);
-static char *asgml_poly(LWPOLY *poly, int version);
-static size_t pointArray_GMLsize(POINTARRAY *pa, int version);
-static size_t pointArray_toGML(POINTARRAY *pa, int version, char *buf);
+char *geometry_to_gml(char *lwgeom, char *srs);
+
+static size_t asgml_point_size(LWPOINT *point, char *srs);
+static char *asgml_point(LWPOINT *point, char *srs);
+static size_t asgml_line_size(LWLINE *line, char *srs);
+static char *asgml_line(LWLINE *line, char *srs);
+static size_t asgml_poly_size(LWPOLY *poly, char *srs);
+static char *asgml_poly(LWPOLY *poly, char *srs);
+static size_t asgml_inspected_size(LWGEOM_INSPECTED *geom, char *srs);
+static char *asgml_inspected(LWGEOM_INSPECTED *geom, char *srs);
+static size_t pointArray_GMLsize(POINTARRAY *pa);
+static size_t pointArray_toGML(POINTARRAY *pa, char *buf);
+static char *getSRSbySRID(int SRID);
+
+#define DEF_PRECISION 15
+// Add dot, sign, exponent sign, 'e', exponent digits
+#define SHOW_DIGS (precision + 8)
+
+/* Globals */
+unsigned int precision;
 
-#define SHOW_DIGS_DOUBLE 15
-#define MAX_DIGS_DOUBLE (SHOW_DIGS_DOUBLE + 6 + 1 + 3 +1)
 
 /**
  * Encode feature in GML 
@@ -39,151 +52,360 @@ Datum LWGEOM_asGML(PG_FUNCTION_ARGS)
        char *gml;
        char *result;
        int len;
-       int version=0;
+       int version = 2;
+       precision = DEF_PRECISION;
+       char *srs;
+       int SRID;
 
        if ( PG_ARGISNULL(0) ) PG_RETURN_NULL();
 
        geom = (LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
 
-       // check for relative path notation
+       // Get precision (if provided) 
        if ( PG_NARGS() > 1 && ! PG_ARGISNULL(1) )
-                       version = PG_GETARG_INT32(1);
+                       precision = PG_GETARG_INT32(1);
+       
+       if ( precision < 1 || precision > 15 )
+       {
+               elog(ERROR, "Precision out of range 1..15");
+               PG_RETURN_NULL();
+       }
+
+       // Get version (if provided) 
+       if ( PG_NARGS() > 2 && ! PG_ARGISNULL(2) )
+                       version = PG_GETARG_INT32(2);
+
+       
+       if ( version != 2 )
+       {
+               elog(ERROR, "Only GML 2 is supported");
+               PG_RETURN_NULL();
+       }
+
+       SRID = lwgeom_getsrid(SERIALIZED_FORM(geom));
+       if ( SRID != -1 ) srs = getSRSbySRID(SRID);
+       else srs = NULL;
 
-       gml = geometry_to_gml(geom, version);
+       //elog(NOTICE, "srs=%s", srs);
+
+       gml = geometry_to_gml(SERIALIZED_FORM(geom), srs);
+       PG_FREE_IF_COPY(geom, 0);
 
        len = strlen(gml) + 5;
 
-       result= palloc(len);
+       result = palloc(len);
        *((int *) result) = len;
 
-       memcpy(result +4, gml, len-4);
+       memcpy(result+4, gml, len-4);
 
        pfree(gml);
-       PG_FREE_IF_COPY(geom, 0);
 
        PG_RETURN_CSTRING(result);
 }
 
-
 // takes a GEOMETRY and returns a GML representation
-char *geometry_to_gml(LWGEOM *geometry, int version)
+char *geometry_to_gml(char *geom, char *srs)
 {
-       char *result = NULL;
+       int type;
+       LWPOINT *point;
+       LWLINE *line;
+       LWPOLY *poly;
        LWGEOM_INSPECTED *inspected;
-       int i;
 
-       if (lwgeom_getType(geometry->type) >= 4 )
+       type = lwgeom_getType(geom[0]);
+
+       switch (type)
        {
-               return pstrdup("<!-- MULTI geoms not yet supported -->");
+
+               case POINTTYPE:
+                       point = lwpoint_deserialize(geom);
+                       return asgml_point(point, srs);
+
+               case LINETYPE:
+                       line = lwline_deserialize(geom);
+                       return asgml_line(line, srs);
+
+               case POLYGONTYPE:
+                       poly = lwpoly_deserialize(geom);
+                       return asgml_poly(poly, srs);
+
+               default:
+                       inspected = lwgeom_inspect(geom);
+                       return asgml_inspected(inspected, srs);
        }
+}
 
-       inspected = lwgeom_inspect(SERIALIZED_FORM(geometry));
-       for(i=0; i<inspected->ngeometries; i++)
-       {
-               char *subgeom = lwgeom_getsubgeometry_inspected(inspected, i);
-               LWPOINT *point;
-               LWLINE *line;
-               LWPOLY *poly;
+static size_t
+asgml_point_size(LWPOINT *point, char *srs)
+{
+       int size;
+       size = pointArray_GMLsize(point->point);
+       size += sizeof("<gml:point><gml:coordinates>/") * 2;
+       if ( srs ) size += strlen(srs) + sizeof(" srsName=..");
+       return size;
+}
 
-               if (lwgeom_getType(subgeom[0]) == POINTTYPE)
-               {
-                       point = lwpoint_deserialize(subgeom);
-                       return asgml_point(point, version);
-               }
-               if (lwgeom_getType(subgeom[0]) == LINETYPE)
-               {
-                       line = lwline_deserialize(subgeom);
-                       return asgml_line(line, version);
-               }
-               if (lwgeom_getType(subgeom[0]) == POLYGONTYPE)
-               {
-                       poly = lwpoly_deserialize(subgeom);
-                       return asgml_poly(poly, version);
-               }
+static size_t
+asgml_point_buf(LWPOINT *point, char *srs, char *output)
+{
+       char *ptr = output;
+
+       if ( srs ) {
+               ptr += sprintf(ptr, "<gml:Point srsName=\"%s\">", srs);
+       } else {
+               ptr += sprintf(ptr, "<gml:Point>");
        }
-       return(result);
+       ptr += sprintf(ptr, "<gml:coordinates>");
+       ptr += pointArray_toGML(point->point, ptr);
+       ptr += sprintf(ptr, "</gml:coordinates></gml:Point>");
+
+       return (ptr-output);
 }
 
 static char *
-asgml_point(LWPOINT *point, int version)
+asgml_point(LWPOINT *point, char *srs)
 {
        char *output;
        int size;
-       char *ptr;
        
-       size = pointArray_GMLsize(point->point, version);
-       size += sizeof("<point><coordinates>/") * 2;
-
+       size = asgml_point_size(point, srs);
        output = palloc(size);
-       ptr = output;
+       asgml_point_buf(point, srs, output);
+       return output;
+}
 
-       ptr += sprintf(ptr, "<Point><coordinates>");
-       ptr += pointArray_toGML(point->point, version, ptr);
-       ptr += sprintf(ptr, "</coordinates></Point>");
+static size_t
+asgml_line_size(LWLINE *line, char *srs)
+{
+       int size;
+       size = pointArray_GMLsize(line->points);
+       size += sizeof("<gml:linestring><gml:coordinates>/") * 2;
+       if ( srs ) size += strlen(srs) + sizeof(" srsName=..");
+       return size;
+}
 
-       return output;
+static size_t
+asgml_line_buf(LWLINE *line, char *srs, char *output)
+{
+       char *ptr=output;
+
+       if ( srs ) {
+               ptr += sprintf(ptr, "<gml:LineString srsName=\"%s\">", srs);
+       } else {
+               ptr += sprintf(ptr, "<gml:LineString>");
+       }
+       ptr += sprintf(ptr, "<gml:coordinates>");
+       ptr += pointArray_toGML(line->points, ptr);
+       ptr += sprintf(ptr, "</gml:coordinates></gml:LineString>");
+
+       return (ptr-output);
 }
 
 static char *
-asgml_line(LWLINE *line, int version)
+asgml_line(LWLINE *line, char *srs)
 {
        char *output;
        int size;
-       char *ptr;
-       
-       size = pointArray_GMLsize(line->points, version);
-       size += sizeof("<linestring><coordinates>/") * 2;
 
+       size = asgml_line_size(line, srs);
        output = palloc(size);
-       ptr = output;
+       asgml_line_buf(line, srs, output);
+       return output;
+}
+
+static size_t
+asgml_poly_size(LWPOLY *poly, char *srs)
+{
+       size_t size;
+       int i;
 
-       ptr += sprintf(ptr, "<LineString><coordinates>");
-       ptr += pointArray_toGML(line->points, version, ptr);
-       ptr += sprintf(ptr, "</coordinates></LineString>");
+       size = sizeof("<gml:polygon></gml:polygon>");
+       size += sizeof("<gml:outerboundaryis><gml:linearring>/") * 2;
+       size += sizeof("<gml:innerboundaryis><gml:linearring>/") * 2 *
+               poly->nrings;
+       if ( srs ) size += strlen(srs) + sizeof(" srsName=..");
 
-       return output;
+       for (i=0; i<poly->nrings; i++)
+               size += pointArray_GMLsize(poly->rings[i]);
+
+       return size;
+}
+
+static size_t
+asgml_poly_buf(LWPOLY *poly, char *srs, char *output)
+{
+       int i;
+       char *ptr=output;
+
+       if ( srs ) {
+               ptr += sprintf(ptr, "<gml:Polygon srsName=\"%s\">", srs);
+       } else {
+               ptr += sprintf(ptr, "<gml:Polygon>");
+       }
+       ptr += sprintf(ptr, "<gml:OuterBoundaryIs>");
+       ptr += pointArray_toGML(poly->rings[0], ptr);
+       ptr += sprintf(ptr, "</gml:OuterBoundaryIs>");
+       for (i=1; i<poly->nrings; i++)
+       {
+               ptr += sprintf(ptr, "<gml:InnerBoundaryIs>");
+               ptr += pointArray_toGML(poly->rings[i], ptr);
+               ptr += sprintf(ptr, "</gml:InnerBoundaryIs>");
+       }
+       ptr += sprintf(ptr, "</gml:Polygon>");
+
+       return (ptr-output);
 }
 
 static char *
-asgml_poly(LWPOLY *poly, int version)
+asgml_poly(LWPOLY *poly, char *srs)
 {
        char *output;
        int size;
+
+       size = asgml_poly_size(poly, srs);
+       output = palloc(size);
+       asgml_poly_buf(poly, srs, output);
+       return output;
+}
+
+/*
+ * Compute max size required for GML version of this 
+ * inspected geometry. Will recurse when needed.
+ * Don't call this with single-geoms inspected.
+ */
+static size_t
+asgml_inspected_size(LWGEOM_INSPECTED *insp, char *srs)
+{
        int i;
-       char *ptr;
+       size_t size;
 
-       size = sizeof("<polygon></outerboundaryis></linearring>") * 2;
-       size += sizeof("<innerboundaryis><linearring>/") * 2 * poly->nrings;
+       // the longest possible multi version
+       size = sizeof("<gml:MultiLineString></gml:MultiLineString>");
+       if ( srs ) size += strlen(srs) + sizeof(" srsName=..");
+
+       for (i=0; i<insp->ngeometries; i++)
+       {
+               LWPOINT *point;
+               LWLINE *line;
+               LWPOLY *poly;
+               LWGEOM_INSPECTED *subinsp;
+               char *subgeom;
+
+               if ((point=lwgeom_getpoint_inspected(insp, i)))
+               {
+                       size += asgml_point_size(point, 0);
+                       pfree_point(point);
+               }
+               else if ((line=lwgeom_getline_inspected(insp, i)))
+               {
+                       size += asgml_line_size(line, 0);
+                       pfree_line(line);
+               }
+               else if ((poly=lwgeom_getpoly_inspected(insp, i)))
+               {
+                       size += asgml_poly_size(poly, 0);
+                       pfree_polygon(poly);
+               }
+               else
+               {
+                       subgeom = lwgeom_getsubgeometry_inspected(insp, i);
+                       subinsp = lwgeom_inspect(subgeom);
+                       size += asgml_inspected_size(subinsp, 0);
+                       pfree_inspected(subinsp);
+               }
+       }
+
+       return size;
+}
+
+/*
+ * Don't call this with single-geoms inspected!
+ */
+static size_t
+asgml_inspected_buf(LWGEOM_INSPECTED *insp, char *srs, char *output)
+{
+       int type = lwgeom_getType(insp->serialized_form[0]);
+       char *ptr, *gmltype;
+       int i;
 
-       for (i=0; i<poly->nrings; i++)
-               size += pointArray_GMLsize(poly->rings[i], version);
-       
-       output = palloc(size);
        ptr = output;
 
-       ptr += sprintf(ptr, "<Polygon>");
-       ptr += sprintf(ptr, "<OuterBoundaryIs>");
-       ptr += pointArray_toGML(poly->rings[0], version, ptr);
-       ptr += sprintf(ptr, "</OuterBoundaryIs>");
-       for (i=1; i<poly->nrings; i++)
+       if (type == MULTIPOINTTYPE) gmltype = "MultiPoint";
+       else if (type == MULTILINETYPE) gmltype = "MultiLineString";
+       else if (type == MULTIPOLYGONTYPE) gmltype = "MultiPolygon";
+       else gmltype = "MultiGeometry";
+
+       // Open outmost tag
+       if ( srs ) {
+               ptr += sprintf(ptr, "<gml:%s srsName=\"%s\">", gmltype, srs);
+       } else {
+               ptr += sprintf(ptr, "<gml:%s>", gmltype);
+       }
+
+       for (i=0; i<insp->ngeometries; i++)
        {
-               ptr += sprintf(ptr, "<InnerBoundaryIs>");
-               ptr += pointArray_toGML(poly->rings[i], version, ptr);
-               ptr += sprintf(ptr, "</InnerBoundaryIs>");
+               LWPOINT *point;
+               LWLINE *line;
+               LWPOLY *poly;
+               LWGEOM_INSPECTED *subinsp;
+               char *subgeom;
+
+               if ((point=lwgeom_getpoint_inspected(insp, i)))
+               {
+                       ptr += asgml_point_buf(point, 0, ptr);
+                       pfree_point(point);
+               }
+               else if ((line=lwgeom_getline_inspected(insp, i)))
+               {
+                       ptr += asgml_line_buf(line, 0, ptr);
+                       pfree_line(line);
+               }
+               else if ((poly=lwgeom_getpoly_inspected(insp, i)))
+               {
+                       ptr += asgml_poly_buf(poly, 0, ptr);
+                       pfree_polygon(poly);
+               }
+               else
+               {
+                       subgeom = lwgeom_getsubgeometry_inspected(insp, i);
+                       subinsp = lwgeom_inspect(subgeom);
+                       ptr += asgml_inspected_buf(subinsp, 0, ptr);
+                       pfree_inspected(subinsp);
+               }
        }
-       ptr += sprintf(ptr, "</Polygon>");
 
-       return output;
+       // Close outmost tag
+       ptr += sprintf(ptr, "</gml:%s>", gmltype);
+
+       return (ptr-output);
+}
+
+/*
+ * Don't call this with single-geoms inspected!
+ */
+static char *
+asgml_inspected(LWGEOM_INSPECTED *insp, char *srs)
+{
+       char *gml;
+       size_t size;
+
+       size = asgml_inspected_size(insp, srs);
+       gml = palloc(size);
+       asgml_inspected_buf(insp, srs, gml);
+       return gml;
 }
 
+/*
+ * Returns maximum size of rendered pointarray in bytes.
+ */
 static size_t
-pointArray_GMLsize(POINTARRAY *pa, int version)
+pointArray_GMLsize(POINTARRAY *pa)
 {
-       return pa->ndims * (MAX_DIGS_DOUBLE+(pa->ndims-1));
+       return pa->ndims * pa->npoints * (SHOW_DIGS+(pa->ndims-1));
 }
 
 static size_t
-pointArray_toGML(POINTARRAY *pa, int version, char *output)
+pointArray_toGML(POINTARRAY *pa, char *output)
 {
        int i;
        POINT4D *pt;
@@ -197,8 +419,9 @@ pointArray_toGML(POINTARRAY *pa, int version, char *output)
                {
                        pt = (POINT4D *)getPoint(pa, i);
                        if ( i ) ptr += sprintf(ptr, " ");
-                       ptr += sprintf(ptr, "%g,%g",
-                               pt->x, pt->y);
+                       ptr += sprintf(ptr, "%.*g,%.*g",
+                               precision, pt->x,
+                               precision, pt->y);
                }
        }
        else if ( pa->ndims == 3 )
@@ -207,8 +430,10 @@ pointArray_toGML(POINTARRAY *pa, int version, char *output)
                {
                        pt = (POINT4D *)getPoint(pa, i);
                        if ( i ) ptr += sprintf(ptr, " ");
-                       ptr += sprintf(ptr, "%g,%g,%g",
-                               pt->x, pt->y, pt->z);
+                       ptr += sprintf(ptr, "%.*g,%.*g,%.*g",
+                               precision, pt->x,
+                               precision, pt->y,
+                               precision, pt->z);
                }
        }
        else if ( pa->ndims == 4 )
@@ -217,17 +442,80 @@ pointArray_toGML(POINTARRAY *pa, int version, char *output)
                {
                        pt = (POINT4D *)getPoint(pa, i);
                        if ( i ) ptr += sprintf(ptr, " ");
-                       ptr += sprintf(ptr, "%g,%g,%g,%g", 
-                               pt->x, pt->y, pt->z, pt->m);
+                       ptr += sprintf(ptr, "%.*g,%.*g,%.*g,%.*g", 
+                               precision, pt->x,
+                               precision, pt->y,
+                               precision, pt->z,
+                               precision, pt->m);
                }
        }
 
        return ptr-output;
 }
 
+static char *
+getSRSbySRID(int SRID)
+{
+       char query[128];
+       char *srs, *srscopy;
+       int size, err;
+
+       // connect to SPI
+       if (SPI_OK_CONNECT != SPI_connect ()) {
+               elog(NOTICE, "getSRSbySRID: could not connect to SPI manager");
+               SPI_finish();
+               return NULL;
+       }
+
+       // write query
+       sprintf(query, "SELECT textcat(auth_name, textcat(':', auth_srid)) \
+               FROM spatial_ref_sys WHERE srid = '%d'", SRID);
+#ifdef DEBUG
+       elog(NOTICE, "Query: %s", query);
+#endif
+
+       // execute query
+       err = SPI_exec(query, 1);
+       if ( err < 0 ) {
+               elog(NOTICE, "getSRSbySRID: error executing query %d", err);
+               SPI_finish();
+               return NULL;
+       }
+
+       // no entry in spatial_ref_sys
+       if (SPI_processed <= 0) {
+               //elog(NOTICE, "getSRSbySRID: no record for SRID %d", SRID);
+               SPI_finish();
+               return NULL;
+       }
+
+       // get result 
+       srs = SPI_getvalue(SPI_tuptable->vals[0],
+               SPI_tuptable->tupdesc, 1);
+       
+       // NULL result
+       if ( ! srs ) {
+               //elog(NOTICE, "getSRSbySRID: null result");
+               SPI_finish();
+               return NULL;
+       }
+
+       // copy result to upper executor context
+       size = strlen(srs)+1;
+       srscopy = SPI_palloc(size);
+       memcpy(srscopy, srs, size);
+
+       // disconnect from SPI
+       SPI_finish();
+
+       return srscopy;
+}
 
 /**********************************************************************
  * $Log$
+ * Revision 1.2  2004/09/23 15:09:07  strk
+ * Modified GML output as suggested by Martin Daly.
+ *
  * Revision 1.1  2004/09/23 11:12:47  strk
  * Initial GML output routines.
  *
index 04303a1aac55c3337992d6781faff1349766bc9c..dbdb2e01483f81ff98fa7badfa294fad65371157 100644 (file)
@@ -2925,11 +2925,19 @@ CREATEFUNCTION AsSVG(geometry)
 -----------------------------------------------------------------------
 -- GML OUTPUT
 -----------------------------------------------------------------------
+-- AsGML(geom, precision, version)
+CREATEFUNCTION AsGML(geometry, int4, int4)
+       RETURNS TEXT
+       AS '@MODULE_FILENAME@','LWGEOM_asGML'
+       LANGUAGE 'C';
+
+-- AsGML(geom, precision) / version=2
 CREATEFUNCTION AsGML(geometry, int4)
        RETURNS TEXT
        AS '@MODULE_FILENAME@','LWGEOM_asGML'
        LANGUAGE 'C';
 
+-- AsGML(geom) / precision=15 version=2
 CREATEFUNCTION AsGML(geometry)
        RETURNS TEXT
        AS '@MODULE_FILENAME@','LWGEOM_asGML'