From: Sandro Santilli Date: Fri, 15 Dec 2006 10:48:56 +0000 (+0000) Subject: Patch by Eduin Carrillo : X-Git-Tag: 1.2.1~24 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a4e7249465d7a03af2a625203cd373fe7c9576a1;p=postgis Patch by Eduin Carrillo : * 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 --- diff --git a/ChangeLog b/ChangeLog index 2eb5b6231..33e35c1b2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,9 +1,16 @@ +2006-12-15 Eduin Carrillo + + * lwgeom/: Makefile, lwgeom_kml.c, lwpostgis.sql.in: + New AsKML() function + * doc/postgis.xml: document new AsKML function. + 2006-12-13 Markus Schaber * README.postgis Fixed "hard upgrade" instructions. 2006-12-05 Mark Leslie + * doc/postgis.xml: Added sections for the SQL-MM and ArcSDE function ports. diff --git a/doc/postgis.xml b/doc/postgis.xml index 5f5d639be..5e7530a22 100644 --- a/doc/postgis.xml +++ b/doc/postgis.xml @@ -4163,6 +4163,13 @@ in output (defaults to 15). + + + AsKML(geometry, [precision]) + + 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). + + diff --git a/lwgeom/Makefile b/lwgeom/Makefile index edd5a7ac3..3adb29b22 100644 --- a/lwgeom/Makefile +++ b/lwgeom/Makefile @@ -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 index 000000000..ddb8e33fb --- /dev/null +++ b/lwgeom/lwgeom_kml.c @@ -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("/") * 2; + return size; +} + +static size_t +askml_point_buf(LWPOINT *point, char *srs, char *output) +{ + char *ptr = output; + + ptr += sprintf(ptr, ""); + ptr += sprintf(ptr, ""); + ptr += pointArray_toKML(point->point, ptr); + ptr += sprintf(ptr, ""); + + 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("/") * 2; + return size; +} + +static size_t +askml_line_buf(LWLINE *line, char *srs, char *output) +{ + char *ptr=output; + + ptr += sprintf(ptr, ""); + ptr += sprintf(ptr, ""); + ptr += pointArray_toKML(line->points, ptr); + ptr += sprintf(ptr, ""); + + 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(""); + size += sizeof("/") * 2; + size += sizeof("/") * 2 * + poly->nrings; + + for (i=0; inrings; 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, ""); + ptr += sprintf(ptr, ""); + ptr += pointArray_toKML(poly->rings[0], ptr); + ptr += sprintf(ptr, ""); + for (i=1; inrings; i++) + { + ptr += sprintf(ptr, ""); + ptr += pointArray_toKML(poly->rings[i], ptr); + ptr += sprintf(ptr, ""); + } + ptr += sprintf(ptr, ""); + + 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(""); + + for (i=0; ingeometries; 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; ingeometries; 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, "", 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; inpoints; 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; inpoints; 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$ + **********************************************************************/ + diff --git a/lwgeom/lwpostgis.sql.in b/lwgeom/lwpostgis.sql.in index 53af5d67d..9268d9f19 100644 --- a/lwgeom/lwpostgis.sql.in +++ b/lwgeom/lwpostgis.sql.in @@ -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 ------------------------------------------------------------------------