]> granicus.if.org Git - postgis/commitdiff
Patch by Eduin Carrillo <yecarrillo@yahoo.com>:
authorSandro Santilli <strk@keybit.net>
Fri, 15 Dec 2006 10:48:56 +0000 (10:48 +0000)
committerSandro Santilli <strk@keybit.net>
Fri, 15 Dec 2006 10:48:56 +0000 (10:48 +0000)
        * lwgeom/: Makefile, lwgeom_kml.c, lwpostgis.sql.in:
          New AsKML() function
        * doc/postgis.xml: document new AsKML function.

git-svn-id: http://svn.osgeo.org/postgis/trunk@2550 b70326c6-7e19-0410-871a-916f4a2858ee

ChangeLog
doc/postgis.xml
lwgeom/Makefile
lwgeom/lwgeom_kml.c [new file with mode: 0644]
lwgeom/lwpostgis.sql.in

index 2eb5b62318e2a308d164fa053fd30fb48b9e806e..33e35c1b254ce07daa308209f7991bf1d925f883 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,9 +1,16 @@
+2006-12-15 Eduin Carrillo <yecarrillo@yahoo.com>
+
+       * lwgeom/: Makefile, lwgeom_kml.c, lwpostgis.sql.in:
+         New AsKML() function
+       * doc/postgis.xml: document new AsKML function.
+
 2006-12-13 Markus Schaber <schabi@logix-tt.com>
 
        * README.postgis
           Fixed "hard upgrade" instructions.
 
 2006-12-05 Mark Leslie <mleslie@refractions.net>
+
        * doc/postgis.xml:
          Added sections for the SQL-MM and ArcSDE function ports.
 
index 5f5d639bedbf17368d57f76ee30df964467ced2f..5e7530a2213a72c2e6c472f0b9fad63b08dee460 100644 (file)
               in output (defaults to 15).</para>
             </listitem>
           </varlistentry>
+
+          <varlistentry>
+            <term>AsKML(geometry, [precision])</term>
+            <listitem>
+              <para>Return the geometry as a KML element.  Second argument may be used to reduce the maximum number of significant digits used in output (defaults to 15).</para>
+            </listitem>
+          </varlistentry>
         </variablelist>
       </sect2>
 
index edd5a7ac3abbee2a3b1bf4273dad378b3e93cefc..3adb29b229233c25d1d774356998e7bafa0187e3 100644 (file)
@@ -84,7 +84,7 @@ override CXXFLAGS += $(CSTAR_FLAGS)
 
 SA_OBJS=measures.o box2d.o ptarray.o lwgeom_api.o lwgeom.o lwpoint.o lwline.o lwpoly.o lwmpoint.o lwmline.o lwmpoly.o lwcollection.o $(GEOS_WRAPPER) $(JTS_WRAPPER) wktunparse.o lwgparse.o wktparse.tab.o lex.yy.o vsprintf.o
 
-OBJS=$(SA_OBJS) liblwgeom.o lwgeom_pg.o lwgeom_debug.o lwgeom_spheroid.o lwgeom_ogc.o lwgeom_functions_analytic.o $(JTS_OBJ) lwgeom_inout.o lwgeom_estimate.o lwgeom_functions_basic.o lwgeom_gist.o lwgeom_btree.o lwgeom_transform.o stringBuffer.o lwgeom_box.o lwgeom_box3d.o lwgeom_box2dfloat4.o lwgeom_chip.o lwgeom_svg.o lwgeom_gml.o lwgeom_triggers.o lwgeom_dump.o lwgeom_functions_lrs.o long_xact.o lwcurve.o lwcompound.o lwcurvepoly.o lwmcurve.o lwmsurface.o lwgeom_sqlmm.o
+OBJS=$(SA_OBJS) liblwgeom.o lwgeom_pg.o lwgeom_debug.o lwgeom_spheroid.o lwgeom_ogc.o lwgeom_functions_analytic.o $(JTS_OBJ) lwgeom_inout.o lwgeom_estimate.o lwgeom_functions_basic.o lwgeom_gist.o lwgeom_btree.o lwgeom_transform.o stringBuffer.o lwgeom_box.o lwgeom_box3d.o lwgeom_box2dfloat4.o lwgeom_chip.o lwgeom_svg.o lwgeom_gml.o lwgeom_kml.o lwgeom_triggers.o lwgeom_dump.o lwgeom_functions_lrs.o long_xact.o lwcurve.o lwcompound.o lwcurvepoly.o lwmcurve.o lwmsurface.o lwgeom_sqlmm.o
 
 #OTHERS=y.output lex.yy.c wktparse.tab.c wktparse.tab.h lwpostgis.sql
 OTHERS=y.output postgis_geos_version.h
diff --git a/lwgeom/lwgeom_kml.c b/lwgeom/lwgeom_kml.c
new file mode 100644 (file)
index 0000000..ddb8e33
--- /dev/null
@@ -0,0 +1,494 @@
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2001-2003 Refractions Research Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of hte GNU General Public Licence. See the COPYING file.
+ *
+ **********************************************************************
+ *
+ * KML output routines.
+ *
+ **********************************************************************/
+
+
+#include "postgres.h"
+#include "executor/spi.h"
+
+#include "lwgeom_pg.h"
+#include "liblwgeom.h"
+
+Datum LWGEOM_asKML(PG_FUNCTION_ARGS);
+char *geometry_to_kml(uchar *srl, char *srs);
+
+static size_t askml_point_size(LWPOINT *point, char *srs);
+static char *askml_point(LWPOINT *point, char *srs);
+static size_t askml_line_size(LWLINE *line, char *srs);
+static char *askml_line(LWLINE *line, char *srs);
+static size_t askml_poly_size(LWPOLY *poly, char *srs);
+static char *askml_poly(LWPOLY *poly, char *srs);
+static size_t askml_inspected_size(LWGEOM_INSPECTED *geom, char *srs);
+static char *askml_inspected(LWGEOM_INSPECTED *geom, char *srs);
+static size_t pointArray_KMLsize(POINTARRAY *pa);
+static size_t pointArray_toKML(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 */
+int precision;
+
+
+/**
+ * Encode feature in KML 
+ */
+PG_FUNCTION_INFO_V1(LWGEOM_asKML);
+Datum LWGEOM_asKML(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM *geom;
+       char *kml;
+       text *result;
+       int len;
+       int version = 2;
+       char *srs;
+       int SRID;
+
+       precision = DEF_PRECISION;
+
+       if ( PG_ARGISNULL(0) ) PG_RETURN_NULL();
+
+       geom = (PG_LWGEOM *)PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0));
+
+       /* Get precision (if provided)  */
+       if ( PG_NARGS() > 1 && ! PG_ARGISNULL(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 KML 2 is supported");
+               PG_RETURN_NULL();
+       }
+
+       SRID = lwgeom_getsrid(SERIALIZED_FORM(geom));
+       if ( SRID != -1 ) {
+         srs = getSRSbySRID(SRID);
+       } else {
+               PG_FREE_IF_COPY(geom, 0);
+               elog(ERROR,"Input geometry has unknown (-1) SRID");
+               PG_RETURN_NULL();
+       }
+
+       /*elog(NOTICE, "srs=%s", srs); */
+
+       kml = geometry_to_kml(SERIALIZED_FORM(geom), srs);
+       PG_FREE_IF_COPY(geom, 0);
+
+       len = strlen(kml) + VARHDRSZ;
+
+       result = palloc(len);
+       VARATT_SIZEP(result) = len;
+
+       memcpy(VARDATA(result), kml, len-VARHDRSZ);
+
+       pfree(kml);
+
+       PG_RETURN_POINTER(result);
+}
+
+/* takes a GEOMETRY and returns a KML representation */
+char *
+geometry_to_kml(uchar *geom, char *srs)
+{
+       int type;
+       LWPOINT *point;
+       LWLINE *line;
+       LWPOLY *poly;
+       LWGEOM_INSPECTED *inspected;
+
+       type = lwgeom_getType(geom[0]);
+
+       switch (type)
+       {
+
+               case POINTTYPE:
+                       point = lwpoint_deserialize(geom);
+                       return askml_point(point, srs);
+
+               case LINETYPE:
+                       line = lwline_deserialize(geom);
+                       return askml_line(line, srs);
+
+               case POLYGONTYPE:
+                       poly = lwpoly_deserialize(geom);
+                       return askml_poly(poly, srs);
+
+               case MULTIPOINTTYPE:
+               case MULTILINETYPE:
+               case MULTIPOLYGONTYPE:
+                       inspected = lwgeom_inspect(geom);
+                       return askml_inspected(inspected, srs);
+               
+               default:
+                       lwerror("geometry_to_kml: '%s' geometry type not supported by Google Earth", lwgeom_typename(type));
+                       return NULL;
+       }
+}
+
+static size_t
+askml_point_size(LWPOINT *point, char *srs)
+{
+       int size;
+       size = pointArray_KMLsize(point->point);
+       size += sizeof("<point><coordinates>/") * 2;
+       return size;
+}
+
+static size_t
+askml_point_buf(LWPOINT *point, char *srs, char *output)
+{
+       char *ptr = output;
+
+       ptr += sprintf(ptr, "<Point>");
+       ptr += sprintf(ptr, "<coordinates>");
+       ptr += pointArray_toKML(point->point, ptr);
+       ptr += sprintf(ptr, "</coordinates></Point>");
+
+       return (ptr-output);
+}
+
+static char *
+askml_point(LWPOINT *point, char *srs)
+{
+       char *output;
+       int size;
+       
+       size = askml_point_size(point, srs);
+       output = palloc(size);
+       askml_point_buf(point, srs, output);
+       return output;
+}
+
+static size_t
+askml_line_size(LWLINE *line, char *srs)
+{
+       int size;
+       size = pointArray_KMLsize(line->points);
+       size += sizeof("<linestring><coordinates>/") * 2;
+       return size;
+}
+
+static size_t
+askml_line_buf(LWLINE *line, char *srs, char *output)
+{
+       char *ptr=output;
+
+       ptr += sprintf(ptr, "<LineString>");
+       ptr += sprintf(ptr, "<coordinates>");
+       ptr += pointArray_toKML(line->points, ptr);
+       ptr += sprintf(ptr, "</coordinates></LineString>");
+
+       return (ptr-output);
+}
+
+static char *
+askml_line(LWLINE *line, char *srs)
+{
+       char *output;
+       int size;
+
+       size = askml_line_size(line, srs);
+       output = palloc(size);
+       askml_line_buf(line, srs, output);
+       return output;
+}
+
+static size_t
+askml_poly_size(LWPOLY *poly, char *srs)
+{
+       size_t size;
+       int i;
+
+       size = sizeof("<polygon></polygon>");
+       size += sizeof("<outerboundaryis><linearring><coordinates>/") * 2;
+       size += sizeof("<innerboundaryis><linearring><coordinates>/") * 2 *
+               poly->nrings;
+
+       for (i=0; i<poly->nrings; i++)
+               size += pointArray_KMLsize(poly->rings[i]);
+
+       return size;
+}
+
+static size_t
+askml_poly_buf(LWPOLY *poly, char *srs, char *output)
+{
+       int i;
+       char *ptr=output;
+
+       ptr += sprintf(ptr, "<Polygon>");
+       ptr += sprintf(ptr, "<outerBoundaryIs><LinearRing><coordinates>");
+       ptr += pointArray_toKML(poly->rings[0], ptr);
+       ptr += sprintf(ptr, "</coordinates></LinearRing></outerBoundaryIs>");
+       for (i=1; i<poly->nrings; i++)
+       {
+               ptr += sprintf(ptr, "<innerBoundaryIs><LinearRing><coordinates>");
+               ptr += pointArray_toKML(poly->rings[i], ptr);
+               ptr += sprintf(ptr, "</coordinates></LinearRing></innerBoundaryIs>");
+       }
+       ptr += sprintf(ptr, "</Polygon>");
+
+       return (ptr-output);
+}
+
+static char *
+askml_poly(LWPOLY *poly, char *srs)
+{
+       char *output;
+       int size;
+
+       size = askml_poly_size(poly, srs);
+       output = palloc(size);
+       askml_poly_buf(poly, srs, output);
+       return output;
+}
+
+/*
+ * Compute max size required for KML version of this 
+ * inspected geometry. Will recurse when needed.
+ * Don't call this with single-geoms inspected.
+ */
+static size_t
+askml_inspected_size(LWGEOM_INSPECTED *insp, char *srs)
+{
+       int i;
+       size_t size;
+
+       /* the longest possible multi version */
+       size = sizeof("<MultiGeometry></MultiGeometry>");
+
+       for (i=0; i<insp->ngeometries; i++)
+       {
+               LWPOINT *point;
+               LWLINE *line;
+               LWPOLY *poly;
+               LWGEOM_INSPECTED *subinsp;
+               uchar *subgeom;
+
+               if ((point=lwgeom_getpoint_inspected(insp, i)))
+               {
+                       size += askml_point_size(point, 0);
+                       pfree_point(point);
+               }
+               else if ((line=lwgeom_getline_inspected(insp, i)))
+               {
+                       size += askml_line_size(line, 0);
+                       pfree_line(line);
+               }
+               else if ((poly=lwgeom_getpoly_inspected(insp, i)))
+               {
+                       size += askml_poly_size(poly, 0);
+                       pfree_polygon(poly);
+               }
+               else
+               {
+                       subgeom = lwgeom_getsubgeometry_inspected(insp, i);
+                       subinsp = lwgeom_inspect(subgeom);
+                       size += askml_inspected_size(subinsp, 0);
+                       pfree_inspected(subinsp);
+               }
+       }
+
+       return size;
+}
+
+/*
+ * Don't call this with single-geoms inspected!
+ */
+static size_t
+askml_inspected_buf(LWGEOM_INSPECTED *insp, char *srs, char *output)
+{
+       char *ptr, *kmltype;
+       int i;
+
+       ptr = output;
+       kmltype = "MultiGeometry";
+
+       ptr += sprintf(ptr, "<%s>", kmltype);
+
+       for (i=0; i<insp->ngeometries; i++)
+       {
+               LWPOINT *point;
+               LWLINE *line;
+               LWPOLY *poly;
+               LWGEOM_INSPECTED *subinsp;
+               uchar *subgeom;
+
+               if ((point=lwgeom_getpoint_inspected(insp, i)))
+               {
+                       ptr += askml_point_buf(point, 0, ptr);
+                       pfree_point(point);
+               }
+               else if ((line=lwgeom_getline_inspected(insp, i)))
+               {
+                       ptr += askml_line_buf(line, 0, ptr);
+                       pfree_line(line);
+               }
+               else if ((poly=lwgeom_getpoly_inspected(insp, i)))
+               {
+                       ptr += askml_poly_buf(poly, 0, ptr);
+                       pfree_polygon(poly);
+               }
+               else
+               {
+                       subgeom = lwgeom_getsubgeometry_inspected(insp, i);
+                       subinsp = lwgeom_inspect(subgeom);
+                       ptr += askml_inspected_buf(subinsp, 0, ptr);
+                       pfree_inspected(subinsp);
+               }
+       }
+
+       /* Close outmost tag */
+       ptr += sprintf(ptr, "</%s>", kmltype);
+
+       return (ptr-output);
+}
+
+/*
+ * Don't call this with single-geoms inspected!
+ */
+static char *
+askml_inspected(LWGEOM_INSPECTED *insp, char *srs)
+{
+       char *kml;
+       size_t size;
+
+       size = askml_inspected_size(insp, srs);
+       kml = palloc(size);
+       askml_inspected_buf(insp, srs, kml);
+       return kml;
+}
+
+/*
+ * Returns maximum size of rendered pointarray in bytes.
+ */
+static size_t
+pointArray_KMLsize(POINTARRAY *pa)
+{
+       return TYPE_NDIMS(pa->dims) * pa->npoints * (SHOW_DIGS+(TYPE_NDIMS(pa->dims)-1));
+}
+
+static size_t
+pointArray_toKML(POINTARRAY *pa, char *output)
+{
+       int i;
+       char *ptr;
+
+       ptr = output;
+
+       if ( ! TYPE_HASZ(pa->dims) )
+       {
+               for (i=0; i<pa->npoints; i++)
+               {
+                       POINT2D pt;
+                       getPoint2d_p(pa, i, &pt);
+                       if ( i ) ptr += sprintf(ptr, " ");
+                       ptr += sprintf(ptr, "%.*g,%.*g,0",
+                               precision, pt.x,
+                               precision, pt.y);
+               }
+       }
+       else 
+       {
+               for (i=0; i<pa->npoints; i++)
+               {
+                       POINT4D pt;
+                       getPoint4d_p(pa, i, &pt);
+                       if ( i ) ptr += sprintf(ptr, " ");
+                       ptr += sprintf(ptr, "%.*g,%.*g,%.*g",
+                               precision, pt.x,
+                               precision, pt.y,
+                               precision, pt.z);
+               }
+       }
+
+       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 PGIS_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$
+ **********************************************************************/
+
index 53af5d67d5606fe11dda1b3c93d75599ef4e10b0..9268d9f1921d4d416e1198ee8446823173770aa1 100644 (file)
@@ -3057,6 +3057,45 @@ CREATEFUNCTION AsGML(geometry)
        AS '@MODULE_FILENAME@','LWGEOM_asGML'
        LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
 
+-----------------------------------------------------------------------
+-- KML OUTPUT
+-----------------------------------------------------------------------
+-- AsUKML(geom, precision, version)
+CREATEFUNCTION AsUKML(geometry, int4, int4)
+       RETURNS TEXT
+       AS '@MODULE_FILENAME@','LWGEOM_asKML'
+       LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
+
+-- AsUKML(geom, precision) / version=2
+CREATEFUNCTION AsUKML(geometry, int4)
+       RETURNS TEXT
+       AS '@MODULE_FILENAME@','LWGEOM_asKML'
+       LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
+
+-- AsUKML(geom) / precision=15 version=2
+CREATEFUNCTION AsUKML(geometry)
+       RETURNS TEXT
+       AS '@MODULE_FILENAME@','LWGEOM_asKML'
+       LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
+
+-- AsKML(geom, precision, version)
+CREATE OR REPLACE FUNCTION AsKML(geometry, int4, int4)
+       RETURNS TEXT
+       AS 'SELECT AsUKML(transform($1,4326),$2,$3)' 
+       LANGUAGE 'SQL' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
+
+-- AsKML(geom, precision) / version=2
+CREATE OR REPLACE FUNCTION AsKML(geometry, int4) 
+       RETURNS TEXT
+       AS 'SELECT AsUKML(transform($1,4326),$2)' 
+       LANGUAGE 'SQL' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
+
+-- AsKML(geom) / precision=15 version=2
+CREATE OR REPLACE FUNCTION AsKML(geometry) 
+       RETURNS TEXT
+       AS 'SELECT AsUKML(transform($1,4326))' 
+       LANGUAGE 'SQL' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
+
 ------------------------------------------------------------------------
 -- OGC defined
 ------------------------------------------------------------------------