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 \
--- /dev/null
+/**********************************************************************
+ * $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: <http://www.opengeospatial.org/standards/kml>
+*
+* 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 <libxml/tree.h>
+#include <libxml/parser.h>
+
+
+/*
+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 */
#if HAVE_LIBXML2
-----------------------------------------------------------------------
--- GML INPUT
+-- GML & KML INPUT
-- Availability: 1.5.0
-----------------------------------------------------------------------
CREATE OR REPLACE FUNCTION ST_GeomFromGML(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
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
--- /dev/null
+--
+-- 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('<foo>'));
+
+-- ERROR: Not a KML Geometry
+SELECT 'xml_3', ST_AsEWKT(ST_GeomFromKML('<foo/>'));
+
+
+
+--
+-- Point
+--
+
+-- 1 Point
+SELECT 'point_1', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,2</kml:coordinates></kml:Point>'));
+
+-- ERROR: 2 points
+SELECT 'point_2', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,2 3,4</kml:coordinates></kml:Point>'));
+
+-- ERROR: empty point
+SELECT 'point_3', ST_AsEWKT(ST_GeomFromKML('<kml:Point></kml:Point>'));
+
+
+
+--
+-- LineString
+--
+
+-- 2 Points
+SELECT 'linestring_1', ST_AsEWKT(ST_GeomFromKML('<kml:LineString><kml:coordinates>1,2 3,4</kml:coordinates></kml:LineString>'));
+
+-- ERROR 1 Point
+SELECT 'linestring_2', ST_AsEWKT(ST_GeomFromKML('<kml:LineString><kml:coordinates>1,2</kml:coordinates></kml:LineString>'));
+
+-- ERROR: empty coordinates
+SELECT 'linestring_3', ST_AsEWKT(ST_GeomFromKML('<kml:LineString><kml:coordinates></kml:coordinates></kml:LineString>'));
+SELECT 'linestring_4', ST_AsEWKT(ST_GeomFromKML('<kml:LineString></kml:LineString>'));
+
+-- XML not elements handle
+SELECT 'linestring_5', ST_AsEWKT(ST_GeomFromKML(' <!-- --> <kml:LineString> <!-- --> <kml:coordinates>1,2 3,4</kml:coordinates></kml:LineString>'));
+
+
+
+
+--
+-- Polygon
+--
+
+-- 1 ring
+SELECT 'polygon_1', ST_AsEWKT(ST_GeomFromKML('<kml:Polygon><kml:outerBoundaryIs><kml:LinearRing><kml:coordinates>1,2 3,4 5,6 1,2</kml:coordinates></kml:LinearRing></kml:outerBoundaryIs></kml:Polygon>'));
+
+-- ERROR: In exterior ring: Last point is not the same as the first one
+SELECT 'polygon_2', ST_AsEWKT(ST_GeomFromKML('<kml:Polygon><kml:outerBoundaryIs><kml:LinearRing><kml:coordinates>1,2 3,4 5,6 1,3</kml:coordinates></kml:LinearRing></kml:outerBoundaryIs></kml:Polygon>'));
+
+-- ERROR: In exterior 3D ring: Last point is not the same as the first one in Z
+SELECT 'polygon_3', ST_AsEWKT(ST_GeomFromKML('<kml:Polygon><kml:outerBoundaryIs><kml:LinearRing><kml:coordinates>1,2,3 4,5,6 7,8,9 1,2,0</kml:coordinates></kml:LinearRing></kml:outerBoundaryIs></kml:Polygon>'));
+
+-- ERROR: Only 3 points in exterior ring
+SELECT 'polygon_4', ST_AsEWKT(ST_GeomFromKML('<kml:Polygon><kml:outerBoundaryIs><kml:LinearRing><kml:coordinates>1,2 3,4 1,2</kml:coordinates></kml:LinearRing></kml:outerBoundaryIs></kml:Polygon>'));
+
+-- ERROR: Empty exterior ring coordinates
+SELECT 'polygon_5', ST_AsEWKT(ST_GeomFromKML('<kml:Polygon><kml:outerBoundaryIs><kml:LinearRing><kml:coordinates></kml:coordinates></kml:LinearRing></kml:outerBoundaryIs></kml:Polygon>'));
+SELECT 'polygon_6', ST_AsEWKT(ST_GeomFromKML('<kml:Polygon><kml:outerBoundaryIs><kml:LinearRing></kml:LinearRing></kml:outerBoundaryIs></kml:Polygon>'));
+SELECT 'polygon_7', ST_AsEWKT(ST_GeomFromKML('<kml:Polygon><kml:outerBoundaryIs></kml:outerBoundaryIs></kml:Polygon>'));
+SELECT 'polygon_8', ST_AsEWKT(ST_GeomFromKML('<kml:Polygon></kml:Polygon>'));
+
+-- 2 rings
+SELECT 'polygon_9', ST_AsEWKT(ST_GeomFromKML('<kml:Polygon><kml:outerBoundaryIs><kml:LinearRing><kml:coordinates>1,2 3,4 5,6 1,2</kml:coordinates></kml:LinearRing></kml:outerBoundaryIs><kml:innerBoundaryIs><kml:LinearRing><kml:coordinates>7,8 9,10 11,12 7,8</kml:coordinates></kml:LinearRing></kml:innerBoundaryIs></kml:Polygon>'));
+
+-- XML not elements handle
+SELECT 'polygon_10', ST_AsEWKT(ST_GeomFromKML(' <!-- --> <kml:Polygon> <!-- --> <kml:outerBoundaryIs> <!-- --> <kml:LinearRing> <!-- --> <kml:coordinates>1,2 3,4 5,6 1,2</kml:coordinates></kml:LinearRing></kml:outerBoundaryIs> <!-- --> <kml:innerBoundaryIs> <!-- --> <kml:LinearRing><kml:coordinates>7,8 9,10 11,12 7,8</kml:coordinates></kml:LinearRing></kml:innerBoundaryIs></kml:Polygon>'));
+
+-- Empty interior ring coordinates (even if defined)
+SELECT 'polygon_11', ST_AsEWKT(ST_GeomFromKML('<kml:Polygon><kml:outerBoundaryIs><kml:LinearRing><kml:coordinates>1,2 3,4 5,6 1,2</kml:coordinates></kml:LinearRing></kml:outerBoundaryIs><kml:innerBoundaryIs></kml:innerBoundaryIs></kml:Polygon>'));
+
+-- ERROR: Only 3 points in interior ring
+SELECT 'polygon_12', ST_AsEWKT(ST_GeomFromKML('<kml:Polygon><kml:outerBoundaryIs><kml:LinearRing><kml:coordinates>1,2 3,4 5,6 1,2</kml:coordinates></kml:LinearRing></kml:outerBoundaryIs><kml:innerBoundaryIs><kml:LinearRing><kml:coordinates>7,8 9,10 7,8</kml:coordinates></kml:LinearRing></kml:innerBoundaryIs></kml:Polygon>'));
+
+-- ERROR: In interior ring: Last point is not the same as the first one
+SELECT 'polygon_13', ST_AsEWKT(ST_GeomFromKML('<kml:Polygon><kml:outerBoundaryIs><kml:LinearRing><kml:coordinates>1,2 3,4 5,6 1,2</kml:coordinates></kml:LinearRing></kml:outerBoundaryIs><kml:innerBoundaryIs><kml:LinearRing><kml:coordinates>7,8 9,10 11,12 7,9</kml:coordinates></kml:LinearRing></kml:innerBoundaryIs></kml:Polygon>'));
+
+-- ERROR: In interior 3D ring: Last point is not the same as the first one in Z
+SELECT 'polygon_14', ST_AsEWKT(ST_GeomFromKML('<kml:Polygon><kml:outerBoundaryIs><kml:LinearRing><kml:coordinates>1,2,3 4,5,6 7,8,9 1,2,3</kml:coordinates></kml:LinearRing></kml:outerBoundaryIs><kml:innerBoundaryIs><kml:LinearRing><kml:coordinates>10,11,12 13,14,15 16,17,18 10,11,0</kml:coordinates></kml:LinearRing></kml:innerBoundaryIs></kml:Polygon>'));
+
+-- 3 rings
+SELECT 'polygon_15', ST_AsEWKT(ST_GeomFromKML('<kml:Polygon><kml:outerBoundaryIs><kml:LinearRing><kml:coordinates>1,2 3,4 5,6 1,2</kml:coordinates></kml:LinearRing></kml:outerBoundaryIs><kml:innerBoundaryIs><kml:LinearRing><kml:coordinates>7,8 9,10 11,12 7,8</kml:coordinates></kml:LinearRing></kml:innerBoundaryIs><kml:innerBoundaryIs><kml:LinearRing><kml:coordinates>13,14 15,16 17,18 13,14</kml:coordinates></kml:LinearRing></kml:innerBoundaryIs></kml:Polygon>'));
+
+
+
+
+--
+-- MultiGeometry
+--
+
+-- 1 point
+--SELECT 'multi_1', ST_AsEWKT(ST_GeomFromKML('<kml:MultiGeometry><kml:Point><kml:coordinates>1,2</kml:coordinates></kml:Point></kml:MultiGeometry>'));
+
+-- 2 points
+--SELECT 'multi_2', ST_AsEWKT(ST_GeomFromKML('<kml:MultiGeometry><kml:Point><kml:coordinates>1,2</kml:coordinates></kml:Point><kml:Point><kml:coordinates>1,2</kml:coordinates></kml:Point></kml:MultiGeometry>'));
+
+-- 1 line
+--SELECT 'multi_3', ST_AsEWKT(ST_GeomFromKML('<kml:MultiGeometry><kml:LineString><kml:coordinates>1,2 3,4</kml:coordinates></kml:LineString></kml:MultiGeometry>'));
+
+-- 2 lines
+--SELECT 'multi_4', ST_AsEWKT(ST_GeomFromKML('<kml:MultiGeometry><kml:LineString><kml:coordinates>1,2 3,4</kml:coordinates></kml:LineString><kml:LineString><kml:coordinates>5,6 7,8</kml:coordinates></kml:LineString></kml:MultiGeometry>'));
+
+-- 1 polygon
+--SELECT 'multi_5', ST_AsEWKT(ST_GeomFromKML('<kml:MultiGeometry><kml:Polygon><kml:outerBoundaryIs><kml:LinearRing><kml:coordinates>1,2 3,4 5,6 1,2</kml:coordinates></kml:LinearRing></kml:outerBoundaryIs></kml:Polygon></kml:MultiGeometry>'));
+
+-- 2 polygons
+--SELECT 'multi_6', ST_AsEWKT(ST_GeomFromKML('<kml:MultiGeometry><kml:Polygon><kml:outerBoundaryIs><kml:LinearRing><kml:coordinates>1,2 3,4 5,6 1,2</kml:coordinates></kml:LinearRing></kml:outerBoundaryIs></kml:Polygon><kml:Polygon><kml:outerBoundaryIs><kml:LinearRing><kml:coordinates>7,8 9,10 11,12 7,8</kml:coordinates></kml:LinearRing></kml:outerBoundaryIs></kml:Polygon></kml:MultiGeometry>'));
+
+-- Point, LineString and Polygon
+SELECT 'multi_7', ST_AsEWKT(ST_GeomFromKML('<kml:MultiGeometry><kml:Point><kml:coordinates>1,2</kml:coordinates></kml:Point><kml:LineString><kml:coordinates>3,4 5,6</kml:coordinates></kml:LineString><kml:Polygon><kml:outerBoundaryIs><kml:LinearRing><kml:coordinates>7,8 9,10 11,12 7,8</kml:coordinates></kml:LinearRing></kml:outerBoundaryIs></kml:Polygon></kml:MultiGeometry>'));
+
+-- Empty collection
+SELECT 'multi_8', ST_AsEWKT(ST_GeomFromKML('<kml:MultiGeometry></kml:MultiGeometry>'));
+
+-- Collection of collection
+SELECT 'multi_9', ST_AsEWKT(ST_GeomFromKML('<kml:MultiGeometry><kml:Point><kml:coordinates>1,2</kml:coordinates></kml:Point><kml:MultiGeometry><kml:LineString><kml:coordinates>3,4 5,6</kml:coordinates></kml:LineString><kml:Polygon><kml:outerBoundaryIs><kml:LinearRing><kml:coordinates>7,8 9,10 11,12 7,8</kml:coordinates></kml:LinearRing></kml:outerBoundaryIs></kml:Polygon></kml:MultiGeometry></kml:MultiGeometry>'));
+
+-- XML not elements handle
+SELECT 'multi_10', ST_AsEWKT(ST_GeomFromKML(' <!-- --> <kml:MultiGeometry> <!-- --> <kml:Point> <!-- --> <kml:coordinates>1,2</kml:coordinates></kml:Point> <!-- --> <kml:LineString><kml:coordinates>3,4 5,6</kml:coordinates></kml:LineString> <!-- --> <kml:Polygon><kml:outerBoundaryIs><kml:LinearRing><kml:coordinates>7,8 9,10 11,12 7,8</kml:coordinates></kml:LinearRing></kml:outerBoundaryIs></kml:Polygon></kml:MultiGeometry>'));
+
+
+
+
+--
+-- KML Namespace
+--
+
+-- KML namespace
+SELECT 'ns_1', ST_AsEWKT(ST_GeomFromKML('<kml:Point xmlns:kml="http://www.opengis.net/kml/2.2"><kml:coordinates>1,2</kml:coordinates></kml:Point>'));
+
+-- KML namespace without explicit prefix
+SELECT 'ns_2', ST_AsEWKT(ST_GeomFromKML('<kml:Point xmlns="http://www.opengis.net/kml/2.2"><kml:coordinates>1,2</kml:coordinates></kml:Point>'));
+
+-- ERROR wrong namespace
+SELECT 'ns_3', ST_AsEWKT(ST_GeomFromKML('<kml:Point xmlns:gml="http://foo.net"><kml:coordinates>1,2</kml:coordinates></kml:Point>'));
+
+-- Several namespaces
+SELECT 'ns_4', ST_AsEWKT(ST_GeomFromKML('<kml:Point xmlns:foo="http://bar.net" xmlns:kml="http://www.opengis.net/kml/2.2"><kml:coordinates>1,2</kml:coordinates></kml:Point>'));
+
+-- Ignore other namespace element
+SELECT 'ns_5', ST_AsEWKT(ST_GeomFromKML('<kml:Point xmlns:foo="http://foo.net" xmlns:kml="http://www.opengis.net/kml/2.2"><kml:coordinates>1,2</kml:coordinates><foo:coordinates>3,4</foo:coordinates></kml:Point>'));
+
+-- Attribute without explicit namespace
+-- TODO SELECT 'ns_6', ST_AsEWKT(ST_GeomFromKML('<kml:Point altitudeMode="relative" xmlns:gml="http://www.opengis.net/gml"><kml:coordinates>1,2</kml:coordinates></kml:Point>'));
+
+-- Attribute with explicit KML namespace
+-- TODO SELECT 'ns_8', ST_AsEWKT(ST_GeomFromKML('<kml:Point kml:srsName="EPSG:4326" xmlns:gml="http://www.opengis.net/gml"><kml:coordinates>1,2</kml:coordinates></kml:Point>'));
+
+-- Attribute with explicit prefix but unqualified namespace
+-- TODO SELECT 'ns_10', ST_AsEWKT(ST_GeomFromKML('<kml:Point foo:srsName="EPSG:4326" xmlns:gml="http://www.opengis.net/gml"><kml:coordinates>1,2</kml:coordinates></kml:Point>'));
+
+-- Ignore other namespace attribute
+-- TODO SELECT 'ns_11', ST_AsEWKT(ST_GeomFromKML('<kml:Point foo:srsName="EPSG:4326" xmlns:foo="http://foo.net" xmlns:gml="http://www.opengis.net/gml"><kml:coordinates>1,2</kml:coordinates><foo:coordinates>3,4</foo:coordinates></kml:Point>'));
+
+
+
+
+--
+-- Coordinates
+--
+
+-- X,Y
+SELECT 'coordinates_1', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,2</kml:coordinates></kml:Point>'));
+
+-- ERROR: single X
+SELECT 'coordinates_2', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1</kml:coordinates></kml:Point>'));
+
+-- X,Y,Z
+SELECT 'coordinates_3', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,2,3</kml:coordinates></kml:Point>'));
+
+-- ERROR: 4 dimension
+SELECT 'coordinates_4', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,2,3,4</kml:coordinates></kml:Point>'));
+
+-- ERROR: Only commas
+SELECT 'coordinates_5', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>,</kml:coordinates></kml:Point>'));
+SELECT 'coordinates_6', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates> , </kml:coordinates></kml:Point>'));
+
+-- ERROR: empty or spaces
+SELECT 'coordinates_7', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates></kml:coordinates></kml:Point>'));
+SELECT 'coordinates_8', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates> </kml:coordinates></kml:Point>'));
+
+-- ERROR: End on comma
+SELECT 'coordinates_9', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,2,3,</kml:coordinates></kml:Point>'));
+SELECT 'coordinates_10', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,2,</kml:coordinates></kml:Point>'));
+
+-- ERROR: Begin on comma
+SELECT 'coordinates_11', ST_AsEWKT(ST_GeomFromKML('<kml:LineString><kml:coordinates>,1 2,3</kml:coordinates></kml:LineString>'));
+
+-- Whitespaces before and after
+SELECT 'coordinates_12', ST_AsEWKT(ST_GeomFromKML('<kml:LineString><kml:coordinates> 1,2 3,4 </kml:coordinates></kml:LineString>'));
+SELECT 'coordinates_13', ST_AsEWKT(ST_GeomFromKML('<kml:LineString><kml:coordinates>
+ 1,2 3,4
+ </kml:coordinates></kml:LineString>'));
+
+-- ERROR: Spaces insides
+SELECT 'coordinates_14', ST_AsEWKT(ST_GeomFromKML('<kml:LineString><kml:coordinates>1, 2 3, 4</kml:coordinates></kml:LineString>'));
+
+-- Several spaces as tuples separator
+SELECT 'coordinates_15', ST_AsEWKT(ST_GeomFromKML('<kml:LineString><kml:coordinates>1,2 3,4</kml:coordinates></kml:LineString>'));
+SELECT 'coordinates_16', ST_AsEWKT(ST_GeomFromKML('<kml:LineString><kml:coordinates>
+ 1,2
+ 3,4
+ </kml:coordinates></kml:LineString>'));
+
+-- ERROR: Junk
+SELECT 'coordinates_17', ST_AsEWKT(ST_GeomFromKML('<kml:LineString><kml:coordinates>!@#$%^*()"</kml:coordinates></kml:LineString>'));
+
+
+
+
+
+
+
+
+--
+-- 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('<kml:Point><kml:coordinates>1,1234567890</kml:coordinates></kml:Point>'));
+
+-- Sign +/-
+SELECT 'double_2', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,-1</kml:coordinates></kml:Point>'));
+SELECT 'double_3', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,+1</kml:coordinates></kml:Point>'));
+
+-- ERROR: double sign
+SELECT 'double_4', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,--1</kml:coordinates></kml:Point>'));
+
+-- ERROR: sign inside digit
+SELECT 'double_5', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,1-1</kml:coordinates></kml:Point>'));
+
+-- Decimal part
+SELECT 'double_6', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,1.2</kml:coordinates></kml:Point>'));
+SELECT 'double_7', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,1.23</kml:coordinates></kml:Point>'));
+
+-- no digit after dot
+SELECT 'double_8', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,1.</kml:coordinates></kml:Point>'));
+
+-- ERROR: several dots
+SELECT 'double_9', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,1.2.3</kml:coordinates></kml:Point>'));
+
+-- ERROR: no digit before dot
+SELECT 'double_10', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,.1</kml:coordinates></kml:Point>'));
+SELECT 'double_11', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,-.1</kml:coordinates></kml:Point>'));
+
+-- ERROR: not a digit
+SELECT 'double_12', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,a</kml:coordinates></kml:Point>'));
+SELECT 'double_13', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,1a</kml:coordinates></kml:Point>'));
+SELECT 'double_14', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,1a2</kml:coordinates></kml:Point>'));
+
+-- Exp
+SELECT 'double_15', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,1e2</kml:coordinates></kml:Point>'));
+SELECT 'double_16', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,1E+2</kml:coordinates></kml:Point>'));
+SELECT 'double_17', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,1e-2</kml:coordinates></kml:Point>'));
+SELECT 'double_18', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,1E-2</kml:coordinates></kml:Point>'));
+
+-- Exp with decimal parts
+SELECT 'double_19', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,1.23E2</kml:coordinates></kml:Point>'));
+SELECT 'double_20', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,1.23e2</kml:coordinates></kml:Point>'));
+SELECT 'double_21', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,-1.23E2</kml:coordinates></kml:Point>'));
+
+-- ERROR: no exp digit
+SELECT 'double_22', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,1E</kml:coordinates></kml:Point>'));
+SELECT 'double_23', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,1e</kml:coordinates></kml:Point>'));
+
+-- ERROR: dot inside exp digits
+SELECT 'double_24', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,1e2.3</kml:coordinates></kml:Point>'));
+SELECT 'double_25', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,1E2.3</kml:coordinates></kml:Point>'));
+
+-- ERROR: spaces inside
+SELECT 'double_26', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,- 1.23</kml:coordinates></kml:Point>'));
+SELECT 'double_27', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,-1 .23</kml:coordinates></kml:Point>'));
+SELECT 'double_28', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,-1. 23</kml:coordinates></kml:Point>'));
+SELECT 'double_29', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,-1.23 E2</kml:coordinates></kml:Point>'));
+SELECT 'double_30', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,-1.23E 2</kml:coordinates></kml:Point>'));
+
+-- ERROR: Junk
+SELECT 'double_31', ST_AsEWKT(ST_GeomFromKML('<kml:Point><kml:coordinates>1,$0%@#$^%#</kml:coordinates></kml:Point>'));
+
+
+
+
+
+--
+-- Delete inserted spatial data
+--
+DELETE FROM spatial_ref_sys WHERE srid = 4326;
--- /dev/null
+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