From: Olivier Courtin Date: Sun, 8 Nov 2009 19:05:50 +0000 (+0000) Subject: Initial version of ST_GeomFromKML function and related units tests X-Git-Tag: 1.5.0b1~270 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9e3a51f6015d4d90066dbae37b3bf5c880698e46;p=postgis Initial version of ST_GeomFromKML function and related units tests git-svn-id: http://svn.osgeo.org/postgis/trunk@4769 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/postgis/Makefile.in b/postgis/Makefile.in index fcd044350..f587d3ad0 100644 --- a/postgis/Makefile.in +++ b/postgis/Makefile.in @@ -42,8 +42,9 @@ PG_OBJS=lwgeom_pg.o \ lwgeom_svg.o \ lwgeom_gml.o \ lwgeom_kml.o \ - lwgeom_in_gml.o \ lwgeom_geojson.o \ + lwgeom_in_gml.o \ + lwgeom_in_kml.o \ lwgeom_triggers.o \ lwgeom_dump.o \ lwgeom_functions_lrs.o \ diff --git a/postgis/lwgeom_in_kml.c b/postgis/lwgeom_in_kml.c new file mode 100644 index 000000000..70fc95aa7 --- /dev/null +++ b/postgis/lwgeom_in_kml.c @@ -0,0 +1,470 @@ +/********************************************************************** + * $Id:$ + * + * PostGIS - Spatial Types for PostgreSQL + * http://postgis.refractions.net + * Copyright 2009 Oslandia + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU General Public Licence. See the COPYING file. + * + **********************************************************************/ + +/** +* @file KML input routines. +* Ability to parse KML geometry fragment and to return an LWGEOM +* or an error message. +* +* KML version supported: 2.2.0 +* Cf: +* +* Known limitations related to 3D: +* - Not support kml:Model geometries +* - Don't handle kml:extrude attribute +* +* Written by Olivier Courtin - Oslandia +* +**********************************************************************/ + + +#include "postgres.h" +#include "lwgeom_pg.h" +#include "liblwgeom.h" + + +#if HAVE_LIBXML2 +#include +#include + + +/* +TODO: + - OGC:LonLat84_5773 explicit support (rather than EPSG:4326) + - Don't return a GEOMETRYCOLLECTION if a MULTI one is enough + - altitudeModeGroup relativeToGround Z Altitude + computation upon Geoid +*/ + + +Datum geom_from_kml(PG_FUNCTION_ARGS); +static LWGEOM* parse_kml(xmlNodePtr xnode, bool *hasz); + +#define KML_NS ((char *) "http://www.opengis.net/kml/2.2") + + +/** + * Ability to parse KML geometry fragment and to return an LWGEOM + * or an error message. + */ +PG_FUNCTION_INFO_V1(geom_from_kml); +Datum geom_from_kml(PG_FUNCTION_ARGS) +{ + PG_LWGEOM *geom, *geom2d; + xmlDocPtr xmldoc; + text *xml_input; + LWGEOM *lwgeom; + int xml_size; + uchar *srl; + char *xml; + size_t size=0; + bool hasz=true; + xmlNodePtr xmlroot=NULL; + + + /* Get the KML stream */ + if (PG_ARGISNULL(0)) PG_RETURN_NULL(); + xml_input = PG_GETARG_TEXT_P(0); + + xml_size = VARSIZE(xml_input) - VARHDRSZ; /* actual letters */ + xml = palloc(xml_size + 1); /* +1 for null */ + memcpy(xml, VARDATA(xml_input), xml_size); + xml[xml_size] = 0; /* null term */ + + /* Begin to Parse XML doc */ + xmlInitParser(); + xmldoc = xmlParseMemory(xml, xml_size); + if (!xmldoc || (xmlroot = xmlDocGetRootElement(xmldoc)) == NULL) { + xmlFreeDoc(xmldoc); + xmlCleanupParser(); + lwerror("invalid KML representation"); + } + + lwgeom = parse_kml(xmlroot, &hasz); + lwgeom->bbox = lwgeom_compute_box2d(lwgeom); + geom = pglwgeom_serialize(lwgeom); + lwgeom_release(lwgeom); + + xmlFreeDoc(xmldoc); + xmlCleanupParser(); + + /* KML geometries could be either 2 or 3D + * + * So we deal with 3D in all structures allocation, and flag hasz + * to false if we met once a missing Z dimension + * In this case, we force recursive 2D. + */ + if (!hasz) { + srl = lwalloc(VARSIZE(geom)); + lwgeom_force2d_recursive(SERIALIZED_FORM(geom), srl, &size); + geom2d = PG_LWGEOM_construct(srl, pglwgeom_getSRID(geom), + lwgeom_hasBBOX(geom->type)); + lwfree(geom); + geom = geom2d; + } + + PG_RETURN_POINTER(geom); +} + + +/** + * Return false if current element namespace is not a KML one + * Return true otherwise. + */ +static bool is_kml_namespace(xmlNodePtr xnode, bool is_strict) +{ + xmlNsPtr *ns, *p; + + ns = xmlGetNsList(xnode->doc, xnode); + /* + * If no namespace is available we could return true anyway + * (because we work only on KML fragment, we don't want to + * 'oblige' to add namespace on the geometry root node) + */ + if (ns == NULL) return !is_strict; + + for (p=ns ; *p ; p++) { + if ((*p)->href == NULL) continue; + if (!strcmp((char *) (*p)->href, KML_NS)) { + if ( (*p)->prefix == NULL || + !xmlStrcmp(xnode->ns->prefix, (*p)->prefix)) { + + xmlFree(ns); + return true; + } + } + } + + xmlFree(ns); + return false; +} + + +/** + * Retrieve a KML propertie from a node or NULL otherwise + * Respect namespaces if presents in the node element + */ +static xmlChar *kmlGetProp(xmlNodePtr xnode, xmlChar *prop) +{ + xmlChar *value; + + if (!is_kml_namespace(xnode, true)) + return xmlGetProp(xnode, prop); + + value = xmlGetNsProp(xnode, prop, (xmlChar *) KML_NS); + + /* In last case try without explicit namespace */ + if (value == NULL) value = xmlGetNoNsProp(xnode, prop); + + return value; +} + + +/** + * Parse a string supposed to be a double + */ +static double parse_kml_double(char *d, bool space_before, bool space_after) +{ + char *p; + int st; + enum states { + INIT = 0, + NEED_DIG = 1, + DIG = 2, + NEED_DIG_DEC = 3, + DIG_DEC = 4, + EXP = 5, + NEED_DIG_EXP = 6, + DIG_EXP = 7, + END = 8 + }; + + /* + * Double pattern + * [-|\+]?[0-9]+(\.)?([0-9]+)?([Ee](\+|-)?[0-9]+)? + * We could also meet spaces before and/or after + * this pattern upon parameters + */ + + if (space_before) while (isspace(*d)) d++; + for (st = INIT, p = d ; *p ; p++) { + + if (isdigit(*p)) { + if (st == INIT || st == NEED_DIG) st = DIG; + else if (st == NEED_DIG_DEC) st = DIG_DEC; + else if (st == NEED_DIG_EXP || st == EXP) st = DIG_EXP; + else if (st == DIG || st == DIG_DEC || st == DIG_EXP); + else lwerror("invalid KML representation"); + } else if (*p == '.') { + if (st == DIG) st = NEED_DIG_DEC; + else lwerror("invalid KML representation"); + } else if (*p == '-' || *p == '+') { + if (st == INIT) st = NEED_DIG; + else if (st == EXP) st = NEED_DIG_EXP; + else lwerror("invalid KML representation"); + } else if (*p == 'e' || *p == 'E') { + if (st == DIG || st == DIG_DEC) st = EXP; + else lwerror("invalid KML representation"); + } else if (isspace(*p)) { + if (!space_after) lwerror("invalid KML representation"); + if (st == DIG || st == DIG_DEC || st == DIG_EXP)st = END; + else if (st == NEED_DIG_DEC) st = END; + else if (st == END); + else lwerror("invalid KML representation"); + } else lwerror("invalid KML representation"); + } + + if (st != DIG && st != NEED_DIG_DEC && st != DIG_DEC && st != DIG_EXP && st != END) + lwerror("invalid KML representation"); + + return atof(d); +} + + +/** + * Parse kml:coordinates + */ +static POINTARRAY* parse_kml_coordinates(xmlNodePtr xnode, bool *hasz) +{ + xmlChar *kml_coord; + bool digit, found; + DYNPTARRAY *dpa; + POINTARRAY *pa; + int kml_dims; + char *p, *q; + POINT4D pt; + uchar dims=0; + + if (xnode == NULL) lwerror("invalid KML representation"); + + for (found = false ; xnode != NULL ; xnode = xnode->next) { + if (xnode->type != XML_ELEMENT_NODE) continue; + if (!is_kml_namespace(xnode, false)) continue; + if (strcmp((char *) xnode->name, "coordinates")) continue; + + found = true; + break; + } + if (!found) lwerror("invalid KML representation"); + + /* We begin to retrieve coordinates string */ + kml_coord = xmlNodeGetContent(xnode); + p = (char *) kml_coord; + + /* KML coordinates pattern: x1,y1 x2,y2 + * x1,y1,z1 x2,y2,z2 + */ + + /* Now we create PointArray from coordinates values */ + TYPE_SETZM(dims, 1, 0); + dpa = dynptarray_create(1, dims); + + for (q = p, kml_dims=0, digit = false ; *p ; p++) { + + if (isdigit(*p)) digit = true; /* One state parser */ + + /* Coordinate Separator */ + if (*p == ',') { + *p = '\0'; + kml_dims++; + + if (*(p+1) == '\0') lwerror("invalid KML representation"); + + if (kml_dims == 1) pt.x = parse_kml_double(q, true, true); + else if (kml_dims == 2) pt.y = parse_kml_double(q, true, true); + q = p+1; + + /* Tuple Separator (or end string) */ + } else if (digit && (isspace(*p) || *(p+1) == '\0')) { + if (isspace(*p)) *p = '\0'; + kml_dims++; + + if (kml_dims < 2 || kml_dims > 3) + lwerror("invalid KML representation"); + + if (kml_dims == 3) + pt.z = parse_kml_double(q, true, true); + else { + pt.y = parse_kml_double(q, true, true); + *hasz = false; + } + + dynptarray_addPoint4d(dpa, &pt, 0); + digit = false; + q = p+1; + kml_dims = 0; + + } + } + + xmlFree(kml_coord); + pa = ptarray_clone(dpa->pa); + lwfree(dpa); + + return pa; +} + + +/** + * Parse KML point + */ +static LWGEOM* parse_kml_point(xmlNodePtr xnode, bool *hasz) +{ + POINTARRAY *pa; + + if (xnode->children == NULL) lwerror("invalid KML representation"); + pa = parse_kml_coordinates(xnode->children, hasz); + if (pa->npoints != 1) lwerror("invalid KML representation"); + + return (LWGEOM *) lwpoint_construct(4326, NULL, pa); +} + + +/** + * Parse KML lineString + */ +static LWGEOM* parse_kml_line(xmlNodePtr xnode, bool *hasz) +{ + POINTARRAY *pa; + + if (xnode->children == NULL) lwerror("invalid KML representation"); + pa = parse_kml_coordinates(xnode->children, hasz); + if (pa->npoints < 2) lwerror("invalid KML representation"); + + return (LWGEOM *) lwline_construct(4326, NULL, pa); +} + + +/** + * Parse KML Polygon + */ +static LWGEOM* parse_kml_polygon(xmlNodePtr xnode, bool *hasz) +{ + int ring; + xmlNodePtr xa, xb; + POINTARRAY **ppa = NULL; + + for (xa = xnode->children ; xa != NULL ; xa = xa->next) { + + /* Polygon/outerBoundaryIs */ + if (xa->type != XML_ELEMENT_NODE) continue; + if (!is_kml_namespace(xa, false)) continue; + if (strcmp((char *) xa->name, "outerBoundaryIs")) continue; + + for (xb = xa->children ; xb != NULL ; xb = xb->next) { + + if (xb->type != XML_ELEMENT_NODE) continue; + if (!is_kml_namespace(xb, false)) continue; + if (strcmp((char *) xb->name, "LinearRing")) continue; + + ppa = (POINTARRAY**) lwalloc(sizeof(POINTARRAY*)); + ppa[0] = parse_kml_coordinates(xb->children, hasz); + + if (ppa[0]->npoints < 4 + || (!*hasz && !ptarray_isclosed2d(ppa[0])) + || (*hasz && !ptarray_isclosed3d(ppa[0]))) + lwerror("invalid KML representation"); + } + } + + for (ring=1, xa = xnode->children ; xa != NULL ; xa = xa->next) { + + /* Polygon/innerBoundaryIs */ + if (xa->type != XML_ELEMENT_NODE) continue; + if (!is_kml_namespace(xa, false)) continue; + if (strcmp((char *) xa->name, "innerBoundaryIs")) continue; + + for (xb = xa->children ; xb != NULL ; xb = xb->next) { + + if (xb->type != XML_ELEMENT_NODE) continue; + if (!is_kml_namespace(xb, false)) continue; + if (strcmp((char *) xb->name, "LinearRing")) continue; + + ppa = (POINTARRAY**) lwrealloc((POINTARRAY *) ppa, + sizeof(POINTARRAY*) * (ring + 1)); + ppa[ring] = parse_kml_coordinates(xb->children, hasz); + + if (ppa[ring]->npoints < 4 + || (!*hasz && !ptarray_isclosed2d(ppa[ring])) + || (*hasz && !ptarray_isclosed3d(ppa[ring]))) + lwerror("invalid KML representation"); + + ring++; + } + } + + /* Exterior Ring is mandatory */ + if (ppa == NULL || ppa[0] == NULL) lwerror("invalid KML representation"); + + return (LWGEOM *) lwpoly_construct(4326, NULL, ring, ppa); +} + + +/** + * Parse KML MultiGeometry + */ +static LWGEOM* parse_kml_multi(xmlNodePtr xnode, bool *hasz) +{ + LWGEOM *geom; + xmlNodePtr xa; + + geom = (LWGEOM *)lwcollection_construct_empty(4326, 1, 0); + + for (xa = xnode->children ; xa != NULL ; xa = xa->next) { + + if (xa->type != XML_ELEMENT_NODE) continue; + if (!is_kml_namespace(xa, false)) continue; + + if ( !strcmp((char *) xa->name, "Point") + || !strcmp((char *) xa->name, "LineString") + || !strcmp((char *) xa->name, "Polygon") + || !strcmp((char *) xa->name, "MultiGeometry")) { + + if (xa->children == NULL) break; + geom = lwcollection_add((LWCOLLECTION *)geom, -1, + parse_kml(xa, hasz)); + } + } + + return geom; +} + + +/** + * Parse KML + */ +static LWGEOM* parse_kml(xmlNodePtr xnode, bool *hasz) +{ + xmlNodePtr xa = xnode; + + while (xa != NULL && (xa->type != XML_ELEMENT_NODE + || !is_kml_namespace(xa, false))) xa = xa->next; + + if (xa == NULL) lwerror("invalid KML representation"); + + if (!strcmp((char *) xa->name, "Point")) + return parse_kml_point(xa, hasz); + + if (!strcmp((char *) xa->name, "LineString")) + return parse_kml_line(xa, hasz); + + if (!strcmp((char *) xa->name, "Polygon")) + return parse_kml_polygon(xa, hasz); + + if (!strcmp((char *) xa->name, "MultiGeometry")) + return parse_kml_multi(xa, hasz); + + lwerror("invalid KML representation"); + return NULL; /* Never reach */ +} + +#endif /* if HAVE_LIBXML2 */ diff --git a/postgis/postgis.sql.in.c b/postgis/postgis.sql.in.c index 4af44663c..16108a083 100644 --- a/postgis/postgis.sql.in.c +++ b/postgis/postgis.sql.in.c @@ -4233,7 +4233,7 @@ CREATE OR REPLACE FUNCTION ST_Equals(geometry,geometry) #if HAVE_LIBXML2 ----------------------------------------------------------------------- --- GML INPUT +-- GML & KML INPUT -- Availability: 1.5.0 ----------------------------------------------------------------------- CREATE OR REPLACE FUNCTION ST_GeomFromGML(text) @@ -4246,6 +4246,11 @@ CREATE OR REPLACE FUNCTION ST_GMLToSQL(text) AS 'MODULE_PATHNAME','geom_from_gml' LANGUAGE 'C' IMMUTABLE STRICT; +CREATE OR REPLACE FUNCTION ST_GeomFromKML(text) + RETURNS geometry + AS 'MODULE_PATHNAME','geom_from_kml' + LANGUAGE 'C' IMMUTABLE STRICT; + #endif ----------------------------------------------------------------------- -- SVG OUTPUT diff --git a/regress/Makefile.in b/regress/Makefile.in index 1ac78a3b4..6f20291fe 100644 --- a/regress/Makefile.in +++ b/regress/Makefile.in @@ -82,9 +82,10 @@ ifeq ($(shell expr $(POSTGIS_GEOS_VERSION) ">=" 32),1) TESTS += hausdorff endif -# GeomFromGML need libxml2 +# Some import functions need libxml2 ifeq ($(shell expr $(HAVE_LIBXML2) "=" 1),1) TESTS += in_gml + TESTS += in_kml endif diff --git a/regress/in_kml.sql b/regress/in_kml.sql new file mode 100644 index 000000000..6c3803c50 --- /dev/null +++ b/regress/in_kml.sql @@ -0,0 +1,363 @@ +-- +-- GeomFromKML regression test +-- Written by Olivier Courtin - Oslandia +-- + + +-- +-- spatial_ref_sys datas +-- + +-- EPSG 4326 : WGS 84 +INSERT INTO "spatial_ref_sys" ("srid","auth_name","auth_srid","srtext","proj4text") VALUES (4326,'EPSG',4326,'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]','+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs '); + + +-- Empty Geometry +SELECT 'empty_geom', ST_AsEWKT(ST_GeomFromKML(NULL)); + +-- +-- XML +-- + +-- ERROR: Empty String +SELECT 'xml_1', ST_AsEWKT(ST_GeomFromKML('')); + +-- ERROR: Not well formed XML +SELECT 'xml_2', ST_AsEWKT(ST_GeomFromKML('')); + +-- ERROR: Not a KML Geometry +SELECT 'xml_3', ST_AsEWKT(ST_GeomFromKML('')); + + + +-- +-- Point +-- + +-- 1 Point +SELECT 'point_1', ST_AsEWKT(ST_GeomFromKML('1,2')); + +-- ERROR: 2 points +SELECT 'point_2', ST_AsEWKT(ST_GeomFromKML('1,2 3,4')); + +-- ERROR: empty point +SELECT 'point_3', ST_AsEWKT(ST_GeomFromKML('')); + + + +-- +-- LineString +-- + +-- 2 Points +SELECT 'linestring_1', ST_AsEWKT(ST_GeomFromKML('1,2 3,4')); + +-- ERROR 1 Point +SELECT 'linestring_2', ST_AsEWKT(ST_GeomFromKML('1,2')); + +-- ERROR: empty coordinates +SELECT 'linestring_3', ST_AsEWKT(ST_GeomFromKML('')); +SELECT 'linestring_4', ST_AsEWKT(ST_GeomFromKML('')); + +-- XML not elements handle +SELECT 'linestring_5', ST_AsEWKT(ST_GeomFromKML(' 1,2 3,4')); + + + + +-- +-- Polygon +-- + +-- 1 ring +SELECT 'polygon_1', ST_AsEWKT(ST_GeomFromKML('1,2 3,4 5,6 1,2')); + +-- ERROR: In exterior ring: Last point is not the same as the first one +SELECT 'polygon_2', ST_AsEWKT(ST_GeomFromKML('1,2 3,4 5,6 1,3')); + +-- ERROR: In exterior 3D ring: Last point is not the same as the first one in Z +SELECT 'polygon_3', ST_AsEWKT(ST_GeomFromKML('1,2,3 4,5,6 7,8,9 1,2,0')); + +-- ERROR: Only 3 points in exterior ring +SELECT 'polygon_4', ST_AsEWKT(ST_GeomFromKML('1,2 3,4 1,2')); + +-- ERROR: Empty exterior ring coordinates +SELECT 'polygon_5', ST_AsEWKT(ST_GeomFromKML('')); +SELECT 'polygon_6', ST_AsEWKT(ST_GeomFromKML('')); +SELECT 'polygon_7', ST_AsEWKT(ST_GeomFromKML('')); +SELECT 'polygon_8', ST_AsEWKT(ST_GeomFromKML('')); + +-- 2 rings +SELECT 'polygon_9', ST_AsEWKT(ST_GeomFromKML('1,2 3,4 5,6 1,27,8 9,10 11,12 7,8')); + +-- XML not elements handle +SELECT 'polygon_10', ST_AsEWKT(ST_GeomFromKML(' 1,2 3,4 5,6 1,2 7,8 9,10 11,12 7,8')); + +-- Empty interior ring coordinates (even if defined) +SELECT 'polygon_11', ST_AsEWKT(ST_GeomFromKML('1,2 3,4 5,6 1,2')); + +-- ERROR: Only 3 points in interior ring +SELECT 'polygon_12', ST_AsEWKT(ST_GeomFromKML('1,2 3,4 5,6 1,27,8 9,10 7,8')); + +-- ERROR: In interior ring: Last point is not the same as the first one +SELECT 'polygon_13', ST_AsEWKT(ST_GeomFromKML('1,2 3,4 5,6 1,27,8 9,10 11,12 7,9')); + +-- ERROR: In interior 3D ring: Last point is not the same as the first one in Z +SELECT 'polygon_14', ST_AsEWKT(ST_GeomFromKML('1,2,3 4,5,6 7,8,9 1,2,310,11,12 13,14,15 16,17,18 10,11,0')); + +-- 3 rings +SELECT 'polygon_15', ST_AsEWKT(ST_GeomFromKML('1,2 3,4 5,6 1,27,8 9,10 11,12 7,813,14 15,16 17,18 13,14')); + + + + +-- +-- MultiGeometry +-- + +-- 1 point +--SELECT 'multi_1', ST_AsEWKT(ST_GeomFromKML('1,2')); + +-- 2 points +--SELECT 'multi_2', ST_AsEWKT(ST_GeomFromKML('1,21,2')); + +-- 1 line +--SELECT 'multi_3', ST_AsEWKT(ST_GeomFromKML('1,2 3,4')); + +-- 2 lines +--SELECT 'multi_4', ST_AsEWKT(ST_GeomFromKML('1,2 3,45,6 7,8')); + +-- 1 polygon +--SELECT 'multi_5', ST_AsEWKT(ST_GeomFromKML('1,2 3,4 5,6 1,2')); + +-- 2 polygons +--SELECT 'multi_6', ST_AsEWKT(ST_GeomFromKML('1,2 3,4 5,6 1,27,8 9,10 11,12 7,8')); + +-- Point, LineString and Polygon +SELECT 'multi_7', ST_AsEWKT(ST_GeomFromKML('1,23,4 5,67,8 9,10 11,12 7,8')); + +-- Empty collection +SELECT 'multi_8', ST_AsEWKT(ST_GeomFromKML('')); + +-- Collection of collection +SELECT 'multi_9', ST_AsEWKT(ST_GeomFromKML('1,23,4 5,67,8 9,10 11,12 7,8')); + +-- XML not elements handle +SELECT 'multi_10', ST_AsEWKT(ST_GeomFromKML(' 1,2 3,4 5,6 7,8 9,10 11,12 7,8')); + + + + +-- +-- KML Namespace +-- + +-- KML namespace +SELECT 'ns_1', ST_AsEWKT(ST_GeomFromKML('1,2')); + +-- KML namespace without explicit prefix +SELECT 'ns_2', ST_AsEWKT(ST_GeomFromKML('1,2')); + +-- ERROR wrong namespace +SELECT 'ns_3', ST_AsEWKT(ST_GeomFromKML('1,2')); + +-- Several namespaces +SELECT 'ns_4', ST_AsEWKT(ST_GeomFromKML('1,2')); + +-- Ignore other namespace element +SELECT 'ns_5', ST_AsEWKT(ST_GeomFromKML('1,23,4')); + +-- Attribute without explicit namespace +-- TODO SELECT 'ns_6', ST_AsEWKT(ST_GeomFromKML('1,2')); + +-- Attribute with explicit KML namespace +-- TODO SELECT 'ns_8', ST_AsEWKT(ST_GeomFromKML('1,2')); + +-- Attribute with explicit prefix but unqualified namespace +-- TODO SELECT 'ns_10', ST_AsEWKT(ST_GeomFromKML('1,2')); + +-- Ignore other namespace attribute +-- TODO SELECT 'ns_11', ST_AsEWKT(ST_GeomFromKML('1,23,4')); + + + + +-- +-- Coordinates +-- + +-- X,Y +SELECT 'coordinates_1', ST_AsEWKT(ST_GeomFromKML('1,2')); + +-- ERROR: single X +SELECT 'coordinates_2', ST_AsEWKT(ST_GeomFromKML('1')); + +-- X,Y,Z +SELECT 'coordinates_3', ST_AsEWKT(ST_GeomFromKML('1,2,3')); + +-- ERROR: 4 dimension +SELECT 'coordinates_4', ST_AsEWKT(ST_GeomFromKML('1,2,3,4')); + +-- ERROR: Only commas +SELECT 'coordinates_5', ST_AsEWKT(ST_GeomFromKML(',')); +SELECT 'coordinates_6', ST_AsEWKT(ST_GeomFromKML(' , ')); + +-- ERROR: empty or spaces +SELECT 'coordinates_7', ST_AsEWKT(ST_GeomFromKML('')); +SELECT 'coordinates_8', ST_AsEWKT(ST_GeomFromKML(' ')); + +-- ERROR: End on comma +SELECT 'coordinates_9', ST_AsEWKT(ST_GeomFromKML('1,2,3,')); +SELECT 'coordinates_10', ST_AsEWKT(ST_GeomFromKML('1,2,')); + +-- ERROR: Begin on comma +SELECT 'coordinates_11', ST_AsEWKT(ST_GeomFromKML(',1 2,3')); + +-- Whitespaces before and after +SELECT 'coordinates_12', ST_AsEWKT(ST_GeomFromKML(' 1,2 3,4 ')); +SELECT 'coordinates_13', ST_AsEWKT(ST_GeomFromKML(' + 1,2 3,4 + ')); + +-- ERROR: Spaces insides +SELECT 'coordinates_14', ST_AsEWKT(ST_GeomFromKML('1, 2 3, 4')); + +-- Several spaces as tuples separator +SELECT 'coordinates_15', ST_AsEWKT(ST_GeomFromKML('1,2 3,4')); +SELECT 'coordinates_16', ST_AsEWKT(ST_GeomFromKML(' + 1,2 + 3,4 + ')); + +-- ERROR: Junk +SELECT 'coordinates_17', ST_AsEWKT(ST_GeomFromKML('!@#$%^*()"')); + + + + + + + + +-- +-- Bijective PostGIS KML test +-- + +-- Point +SELECT 'kml_1', ST_AsEWKT(ST_GeomFromKML(ST_AsKML(ST_AsEWKT('SRID=4326;POINT(1 2)')))); + +-- Point - 3D +SELECT 'kml_2', ST_AsEWKT(ST_GeomFromKML(ST_AsKML(ST_AsEWKT('SRID=4326;POINT(1 2 3)')))); + +-- Linestring +SELECT 'kml_3', ST_AsEWKT(ST_GeomFromKML(ST_AsKML(ST_AsEWKT('SRID=4326;LINESTRING(1 2,3 4)')))); + +-- Linestring - 3D +SELECT 'kml_4', ST_AsEWKT(ST_GeomFromKML(ST_AsKML(ST_AsEWKT('SRID=4326;LINESTRING(1 2 3,4 5 6)')))); + +-- Polygon KML +SELECT 'kml_5', ST_AsEWKT(ST_GeomFromKML(ST_AsKML(ST_AsEWKT('SRID=4326;POLYGON((1 2,3 4,5 6,1 2))')))); + +-- Polygon KML - 3D +SELECT 'kml_6', ST_AsEWKT(ST_GeomFromKML(ST_AsKML(ST_AsEWKT('SRID=4326;POLYGON((1 2 3,4 5 6,7 8 9,1 2 3))')))); + +-- Multipoint +--SELECT 'kml_7', ST_AsEWKT(ST_GeomFromKML(ST_AsKML(ST_AsEWKT('SRID=4326;MULTIPOINT(1 2)')))); + +-- Multipoint - 3D +--SELECT 'kml_8', ST_AsEWKT(ST_GeomFromKML(ST_AsKML(ST_AsEWKT('SRID=4326;MULTIPOINT(1 2 3)')))); + +-- Multilinestring +--SELECT 'kml_9', ST_AsEWKT(ST_GeomFromKML(ST_AsKML(ST_AsEWKT('SRID=4326;MULTILINESTRING((1 2,3 4))')))); + +-- Multilinestring - 3D +--SELECT 'kml_10', ST_AsEWKT(ST_GeomFromKML(ST_AsKML(ST_AsEWKT('SRID=4326;MULTILINESTRING((1 2 3,4 5 6))')))); + +-- Multipolygon +--SELECT 'kml_11', ST_AsEWKT(ST_GeomFromKML(ST_AsKML(ST_AsEWKT('SRID=4326;MULTIPOLYGON(((1 2,3 4,5 6,1 2)))')))); + +-- Multipolygon - 3D +--SELECT 'kml_12', ST_AsEWKT(ST_GeomFromKML(ST_AsKML(ST_AsEWKT('SRID=4326;MULTIPOLYGON(((1 2 3,4 5 6,7 8 9,1 2 3)))')))); + +-- Collection +--SELECT 'kml_13', ST_AsEWKT(ST_GeomFromKML(ST_AsKML(ST_AsEWKT('SRID=4326;GEOMETRYCOLLECTION(POINT(1 2))')))); + +-- Collection - 3D +--SELECT 'kml_14', ST_AsEWKT(ST_GeomFromKML(ST_AsKML(ST_AsEWKT('SRID=4326;GEOMETRYCOLLECTION(POINT(1 2 3))')))); + + + + +-- +-- Double +-- + +-- Several digits +SELECT 'double_1', ST_AsEWKT(ST_GeomFromKML('1,1234567890')); + +-- Sign +/- +SELECT 'double_2', ST_AsEWKT(ST_GeomFromKML('1,-1')); +SELECT 'double_3', ST_AsEWKT(ST_GeomFromKML('1,+1')); + +-- ERROR: double sign +SELECT 'double_4', ST_AsEWKT(ST_GeomFromKML('1,--1')); + +-- ERROR: sign inside digit +SELECT 'double_5', ST_AsEWKT(ST_GeomFromKML('1,1-1')); + +-- Decimal part +SELECT 'double_6', ST_AsEWKT(ST_GeomFromKML('1,1.2')); +SELECT 'double_7', ST_AsEWKT(ST_GeomFromKML('1,1.23')); + +-- no digit after dot +SELECT 'double_8', ST_AsEWKT(ST_GeomFromKML('1,1.')); + +-- ERROR: several dots +SELECT 'double_9', ST_AsEWKT(ST_GeomFromKML('1,1.2.3')); + +-- ERROR: no digit before dot +SELECT 'double_10', ST_AsEWKT(ST_GeomFromKML('1,.1')); +SELECT 'double_11', ST_AsEWKT(ST_GeomFromKML('1,-.1')); + +-- ERROR: not a digit +SELECT 'double_12', ST_AsEWKT(ST_GeomFromKML('1,a')); +SELECT 'double_13', ST_AsEWKT(ST_GeomFromKML('1,1a')); +SELECT 'double_14', ST_AsEWKT(ST_GeomFromKML('1,1a2')); + +-- Exp +SELECT 'double_15', ST_AsEWKT(ST_GeomFromKML('1,1e2')); +SELECT 'double_16', ST_AsEWKT(ST_GeomFromKML('1,1E+2')); +SELECT 'double_17', ST_AsEWKT(ST_GeomFromKML('1,1e-2')); +SELECT 'double_18', ST_AsEWKT(ST_GeomFromKML('1,1E-2')); + +-- Exp with decimal parts +SELECT 'double_19', ST_AsEWKT(ST_GeomFromKML('1,1.23E2')); +SELECT 'double_20', ST_AsEWKT(ST_GeomFromKML('1,1.23e2')); +SELECT 'double_21', ST_AsEWKT(ST_GeomFromKML('1,-1.23E2')); + +-- ERROR: no exp digit +SELECT 'double_22', ST_AsEWKT(ST_GeomFromKML('1,1E')); +SELECT 'double_23', ST_AsEWKT(ST_GeomFromKML('1,1e')); + +-- ERROR: dot inside exp digits +SELECT 'double_24', ST_AsEWKT(ST_GeomFromKML('1,1e2.3')); +SELECT 'double_25', ST_AsEWKT(ST_GeomFromKML('1,1E2.3')); + +-- ERROR: spaces inside +SELECT 'double_26', ST_AsEWKT(ST_GeomFromKML('1,- 1.23')); +SELECT 'double_27', ST_AsEWKT(ST_GeomFromKML('1,-1 .23')); +SELECT 'double_28', ST_AsEWKT(ST_GeomFromKML('1,-1. 23')); +SELECT 'double_29', ST_AsEWKT(ST_GeomFromKML('1,-1.23 E2')); +SELECT 'double_30', ST_AsEWKT(ST_GeomFromKML('1,-1.23E 2')); + +-- ERROR: Junk +SELECT 'double_31', ST_AsEWKT(ST_GeomFromKML('1,$0%@#$^%#')); + + + + + +-- +-- Delete inserted spatial data +-- +DELETE FROM spatial_ref_sys WHERE srid = 4326; diff --git a/regress/in_kml_expected b/regress/in_kml_expected new file mode 100644 index 000000000..f06094695 --- /dev/null +++ b/regress/in_kml_expected @@ -0,0 +1,90 @@ +empty_geom| +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +point_1|SRID=4326;POINT(1 2) +ERROR: invalid KML representation +ERROR: invalid KML representation +linestring_1|SRID=4326;LINESTRING(1 2,3 4) +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +linestring_5|SRID=4326;LINESTRING(1 2,3 4) +polygon_1|SRID=4326;POLYGON((1 2,3 4,5 6,1 2)) +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +polygon_9|SRID=4326;POLYGON((1 2,3 4,5 6,1 2),(7 8,9 10,11 12,7 8)) +polygon_10|SRID=4326;POLYGON((1 2,3 4,5 6,1 2),(7 8,9 10,11 12,7 8)) +polygon_11|SRID=4326;POLYGON((1 2,3 4,5 6,1 2)) +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +polygon_15|SRID=4326;POLYGON((1 2,3 4,5 6,1 2),(7 8,9 10,11 12,7 8),(13 14,15 16,17 18,13 14)) +multi_7|SRID=4326;GEOMETRYCOLLECTION(POINT(1 2),LINESTRING(3 4,5 6),POLYGON((7 8,9 10,11 12,7 8))) +multi_8|SRID=4326;GEOMETRYCOLLECTION EMPTY +multi_9|SRID=4326;GEOMETRYCOLLECTION(POINT(1 2),GEOMETRYCOLLECTION(LINESTRING(3 4,5 6),POLYGON((7 8,9 10,11 12,7 8)))) +multi_10|SRID=4326;GEOMETRYCOLLECTION(POINT(1 2),LINESTRING(3 4,5 6),POLYGON((7 8,9 10,11 12,7 8))) +ns_1|SRID=4326;POINT(1 2) +ns_2|SRID=4326;POINT(1 2) +ERROR: invalid KML representation +ns_4|SRID=4326;POINT(1 2) +ns_5|SRID=4326;POINT(1 2) +coordinates_1|SRID=4326;POINT(1 2) +ERROR: invalid KML representation +coordinates_3|SRID=4326;POINT(1 2 3) +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +coordinates_12|SRID=4326;LINESTRING(1 2,3 4) +coordinates_13|SRID=4326;LINESTRING(1 2,3 4) +ERROR: invalid KML representation +coordinates_15|SRID=4326;LINESTRING(1 2,3 4) +coordinates_16|SRID=4326;LINESTRING(1 2,3 4) +ERROR: invalid KML representation +kml_1|SRID=4326;POINT(1 2) +kml_2|SRID=4326;POINT(1 2 3) +kml_3|SRID=4326;LINESTRING(1 2,3 4) +kml_4|SRID=4326;LINESTRING(1 2 3,4 5 6) +kml_5|SRID=4326;POLYGON((1 2,3 4,5 6,1 2)) +kml_6|SRID=4326;POLYGON((1 2 3,4 5 6,7 8 9,1 2 3)) +double_1|SRID=4326;POINT(1 1234567890) +double_2|SRID=4326;POINT(1 -1) +double_3|SRID=4326;POINT(1 1) +ERROR: invalid KML representation +ERROR: invalid KML representation +double_6|SRID=4326;POINT(1 1.2) +double_7|SRID=4326;POINT(1 1.23) +double_8|SRID=4326;POINT(1 1) +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +double_15|SRID=4326;POINT(1 100) +double_16|SRID=4326;POINT(1 100) +double_17|SRID=4326;POINT(1 0.01) +double_18|SRID=4326;POINT(1 0.01) +double_19|SRID=4326;POINT(1 123) +double_20|SRID=4326;POINT(1 123) +double_21|SRID=4326;POINT(1 -123) +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation +ERROR: invalid KML representation