From cf4d25428c84cb416c6b86c64a9525c96f274ace Mon Sep 17 00:00:00 2001 From: Olivier Courtin Date: Thu, 29 Oct 2009 18:42:17 +0000 Subject: [PATCH] Add mixed GML srs support. Add ability to deal with lat/lon issue in GML 3. Fix GML collection units tests. Update units tests git-svn-id: http://svn.osgeo.org/postgis/trunk@4699 b70326c6-7e19-0410-871a-916f4a2858ee --- postgis/lwgeom_in_gml.c | 506 ++++++++++++++++++++++++++++------------ regress/in_gml.sql | 89 ++++--- regress/in_gml_expected | 18 +- 3 files changed, 431 insertions(+), 182 deletions(-) diff --git a/postgis/lwgeom_in_gml.c b/postgis/lwgeom_in_gml.c index 1c2de0a52..eb3b97f44 100644 --- a/postgis/lwgeom_in_gml.c +++ b/postgis/lwgeom_in_gml.c @@ -1,10 +1,12 @@ /********************************************************************** + * $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 hte GNU General Public Licence. See the COPYING file. + * the terms of the GNU General Public Licence. See the COPYING file. * **********************************************************************/ @@ -25,7 +27,6 @@ * - Support only GML SF profile geometries (i.e no PostGIS curves support) * - Don't care about GML namespace * - Don't handle Xlink or PointRef construction -* - Don't reproject if mixed srsName in multi/aggregate geoms * * Written by Olivier Courtin - Oslandia * @@ -35,16 +36,24 @@ #include "postgres.h" #include "lwgeom_pg.h" #include "liblwgeom.h" +#include "lwgeom_transform.h" +#include "executor/spi.h" + #if HAVE_LIBXML2 #include #include -#include "lwgeom_export.h" /* getSRSbySRID */ - Datum geom_from_gml(PG_FUNCTION_ARGS); -static LWGEOM* parse_gml(xmlNodePtr xnode, bool *hasz); +static LWGEOM* parse_gml(xmlNodePtr xnode, bool *hasz, int *root_srid); + +typedef struct struct_gmlSrs +{ + int srid; + bool reverse_axis; +} +gmlSrs; /** @@ -68,6 +77,7 @@ Datum geom_from_gml(PG_FUNCTION_ARGS) char *xml; size_t size=0; bool hasz=true; + int root_srid=0; xmlNodePtr xmlroot=NULL; @@ -89,7 +99,8 @@ Datum geom_from_gml(PG_FUNCTION_ARGS) lwerror("invalid GML representation"); } - lwgeom = parse_gml(xmlroot, &hasz); + lwgeom = parse_gml(xmlroot, &hasz, &root_srid); + lwgeom->bbox = lwgeom_compute_box2d(lwgeom); geom = pglwgeom_serialize(lwgeom); lwgeom_release(lwgeom); @@ -539,17 +550,122 @@ static POINTARRAY* parse_gml_data(xmlNodePtr xnode, bool *hasz) } +/** + * Reverse X and Y axis on a given POINTARRAY + */ +static POINTARRAY* gml_reverse_axis_pa(POINTARRAY *pa) +{ + int i; + double d; + POINT4D p; + + for (i=0 ; i < pa->npoints ; i++) { + getPoint4d_p(pa, i, &p); + d = p.y; + p.y = p.x; + p.x = d; + setPoint4d(pa, i, &p); + } + + return pa; +} + + +/** + * Use Proj4 to reproject a given POINTARRAY + */ +static POINTARRAY* gml_reproject_pa(POINTARRAY *pa, int srid_in, int srid_out) +{ + int i; + POINT4D p; + projPJ in_pj, out_pj; + char *text_in, *text_out; + + if (srid_in == -1 || srid_out == -1) + lwerror("invalid GML representation"); + + text_in = GetProj4StringSPI(srid_in); + text_out = GetProj4StringSPI(srid_out); + + in_pj = make_project(text_in); + out_pj = make_project(text_out); + + lwfree(text_in); + lwfree(text_out); + + for (i=0 ; i < pa->npoints ; i++) { + getPoint4d_p(pa, i, &p); + transform_point(&p, in_pj, out_pj); + setPoint4d(pa, i, &p); + } + + pj_free(in_pj); + pj_free(out_pj); + + return pa; +} + + +/** + * TODO + * Return -1 if srid is not in spatial_ref_sys + */ +static int gml_is_srid_planar(int srid) +{ + char *result; + char query[256]; + int is_planar, err; + + if (SPI_OK_CONNECT != SPI_connect ()) + lwerror("gml_is_srid_lat_lon: could not connect to SPI manager"); + + /* A way to find if this projection is planar or geocentric */ + sprintf(query, "SELECT position('+units=m ' in proj4text) \ + FROM spatial_ref_sys WHERE srid='%d'", srid); + + err = SPI_exec(query, 1); + if (err < 0) lwerror("gml_is_srid_lat_lon: error executing query %d", err); + + /* No entry in spatial_ref_sys */ + if (SPI_processed <= 0) { + SPI_finish(); + return -1; + } + + result = SPI_getvalue(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1); + is_planar = atoi(result); + SPI_finish(); + + return is_planar; +} + + /** * Parse gml srsName attribute */ -static int parse_gml_srs(xmlNodePtr xnode) +static gmlSrs* parse_gml_srs(xmlNodePtr xnode) { - xmlChar *srs; char *p; - int srid=-1; + gmlSrs *srs; + int is_planar; + xmlNodePtr node; + xmlChar *srsname; + bool latlon = false; + char sep = ':'; + + node = xnode; + srsname = xmlGetProp(node, (xmlChar *) "srsName"); + if (!srsname) { + if (node->parent == NULL) { + srs = (gmlSrs*) lwalloc(sizeof(gmlSrs)); + srs->srid = -1; + srs->reverse_axis = false; + return srs; + } + return parse_gml_srs(node->parent); + } - srs = xmlGetProp(xnode, (xmlChar *) "srsName"); - if (!srs) return -1; /* Default srid if no srsName attribute */ + srs = (gmlSrs*) lwalloc(sizeof(gmlSrs)); /* Severals srsName formats are available... * cf WFS 1.1.0 -> 9.2 (p36) @@ -562,85 +678,116 @@ static int parse_gml_srs(xmlNodePtr xnode) urn:ogc:def:crs:EPSG:4326 urn:ogc:def:crs:EPSG::4326 urn:ogc:def:crs:EPSG:6.6:4326 - urn:x-ogc:def:crs:EPSG:6.6:4326 */ - - if ( !strncmp((char *) srs, "EPSG:", 5) - || !strncmp((char *) srs, "urn:ogc:def:crs:EPSG:", 21) - || !strncmp((char *) srs, "urn:x-ogc:def:crs:EPSG:", 23) - || !strncmp((char *) srs, "urn:EPSG:geographicCRS:", 23)) { - - /* retrieve the last ':' char */ - for (p = (char *) srs ; *p ; p++); - for (--p ; *p != ':' ; p--) - if (!isdigit(*p)) lwerror("unknown spatial reference system"); - - srid = atoi(++p); - - /* SRS pattern like: http://www.opengis.net/gml/srs/epsg.xml#4326 */ - } else if (!strncmp((char *) srs, "http://www.opengis.net/gml/srs/epsg.xml#", 40)) { - p = strchr((char *) srs, '#'); - srid = atoi(++p); - } + urn:x-ogc:def:crs:EPSG:6.6:4326 + http://www.opengis.net/gml/srs/epsg.xml#4326 + */ + + if (!strncmp((char *) srsname, "EPSG:", 5)) { + sep = ':'; + latlon = false; + } else if (!strncmp((char *) srsname, "urn:ogc:def:crs:EPSG:", 21) + || !strncmp((char *) srsname, "urn:x-ogc:def:crs:EPSG:", 23) + || !strncmp((char *) srsname, "urn:EPSG:geographicCRS:", 23)) { + sep = ':'; + latlon = true; + } else if (!strncmp((char *) srsname, "http://www.opengis.net/gml/srs/epsg.xml#", 40)) { + sep = '#'; + latlon = false; + } else lwerror("unknown spatial reference system"); + + /* retrieve the last ':' or '#' char */ + for (p = (char *) srsname ; *p ; p++); + for (--p ; *p != sep ; p--) + if (!isdigit(*p)) lwerror("unknown spatial reference system"); + + srs->srid = atoi(++p); /* Check into spatial_ref_sys that this SRID really exist */ - if (srid == -1 || getSRSbySRID(srid, true) == NULL) + is_planar = gml_is_srid_planar(srs->srid); + if (srs->srid == -1 || is_planar == -1) lwerror("unknown spatial reference system"); - xmlFree(srs); - return srid; + /* About lat/lon issue, Cf: http://tinyurl.com/yjpr55z */ + srs->reverse_axis = !is_planar && latlon; + + xmlFree(srsname); + return srs; } /** * Parse GML point (2.1.2, 3.x.x) */ -static LWGEOM* parse_gml_point(xmlNodePtr xnode, bool *hasz) +static LWGEOM* parse_gml_point(xmlNodePtr xnode, bool *hasz, int *root_srid) { - int srid; + gmlSrs *srs; + LWGEOM* geom; POINTARRAY *pa; - srid = parse_gml_srs(xnode); - pa = parse_gml_data(xnode->children, hasz); if (pa->npoints != 1) lwerror("invalid GML representation"); - return (LWGEOM *) lwpoint_construct(srid, NULL, pa); + srs = parse_gml_srs(xnode); + if (srs->reverse_axis) pa = gml_reverse_axis_pa(pa); + if (!*root_srid) { + *root_srid = srs->srid; + geom = (LWGEOM *) lwpoint_construct(*root_srid, NULL, pa); + } else { + if (srs->srid != *root_srid) + gml_reproject_pa(pa, srs->srid, *root_srid); + geom = (LWGEOM *) lwpoint_construct(-1, NULL, pa); + } + lwfree(srs); + + return geom; } /** * Parse GML lineString (2.1.2, 3.x.x) */ -static LWGEOM* parse_gml_line(xmlNodePtr xnode, bool *hasz) +static LWGEOM* parse_gml_line(xmlNodePtr xnode, bool *hasz, int *root_srid) { - int srid; + gmlSrs *srs; + LWGEOM *geom; POINTARRAY *pa; - srid = parse_gml_srs(xnode); pa = parse_gml_data(xnode->children, hasz); if (pa->npoints < 2) lwerror("invalid GML representation"); - return (LWGEOM *) lwline_construct(srid, NULL, pa); + srs = parse_gml_srs(xnode); + if (srs->reverse_axis) pa = gml_reverse_axis_pa(pa); + if (!*root_srid) { + *root_srid = srs->srid; + geom = (LWGEOM *) lwline_construct(*root_srid, NULL, pa); + } else { + if (srs->srid != *root_srid) + gml_reproject_pa(pa, srs->srid, *root_srid); + geom = (LWGEOM *) lwline_construct(-1, NULL, pa); + } + lwfree(srs); + + return geom; } /** * Parse GML Curve (3.x.x) */ -static LWGEOM* parse_gml_curve(xmlNodePtr xnode, bool *hasz) +static LWGEOM* parse_gml_curve(xmlNodePtr xnode, bool *hasz, int *root_srid) { - int srid, lss, last, i; xmlChar *interpolation; + int lss, last, i; POINTARRAY **ppa; POINTARRAY *pa; xmlNodePtr xa; + LWGEOM *geom; + gmlSrs *srs; bool found=false; unsigned int npoints=0; - srid = parse_gml_srs(xnode); - /* Looking for gml:segments */ for (xa = xnode->children ; xa != NULL ; xa = xa->next) { @@ -680,7 +827,7 @@ static LWGEOM* parse_gml_curve(xmlNodePtr xnode, bool *hasz) if (lss == 0) lwerror("invalid GML representation"); /* Most common case, a single segment */ - if (lss == 1) return (LWGEOM *) lwline_construct(srid, NULL, ppa[0]); + if (lss == 1) pa = ppa[0]; /* @@ -691,39 +838,56 @@ static LWGEOM* parse_gml_curve(xmlNodePtr xnode, bool *hasz) * So we must aggregate all the segments into a single one and avoid * to copy the redundants points */ - pa = ptarray_construct(1, 0, npoints - (lss - 1)); - for (last = npoints = i = 0; i < lss ; i++) { - - if (i + 1 == lss) last = 1; - /* Check if segments are not disjoints */ - if (i > 0 && memcmp( getPoint_internal(pa, npoints), - getPoint_internal(ppa[i], 0), - *hasz?sizeof(POINT3D):sizeof(POINT2D))) - lwerror("invalid GML representation"); + if (lss > 1) { + pa = ptarray_construct(1, 0, npoints - (lss - 1)); + for (last = npoints = i = 0; i < lss ; i++) { + + if (i + 1 == lss) last = 1; + /* Check if segments are not disjoints */ + if (i > 0 && memcmp( getPoint_internal(pa, npoints), + getPoint_internal(ppa[i], 0), + *hasz?sizeof(POINT3D):sizeof(POINT2D))) + lwerror("invalid GML representation"); + + /* Aggregate stuff */ + memcpy( getPoint_internal(pa, npoints), + getPoint_internal(ppa[i], 0), + pointArray_ptsize(ppa[i]) * (ppa[i]->npoints + last)); - /* Aggregate stuff */ - memcpy( getPoint_internal(pa, npoints), - getPoint_internal(ppa[i], 0), - pointArray_ptsize(ppa[i]) * (ppa[i]->npoints + last)); + npoints += ppa[i]->npoints - 1; + lwfree(ppa[i]); + } + lwfree(ppa); + } - npoints += ppa[i]->npoints - 1; - lwfree(ppa[i]); - } - lwfree(ppa); + srs = parse_gml_srs(xnode); + if (srs->reverse_axis) pa = gml_reverse_axis_pa(pa); + if (!*root_srid) { + *root_srid = srs->srid; + geom = (LWGEOM *) lwline_construct(*root_srid, NULL, pa); + } else { + if (srs->srid != *root_srid) + gml_reproject_pa(pa, srs->srid, *root_srid); + geom = (LWGEOM *) lwline_construct(-1, NULL, pa); + } + lwfree(srs); - return (LWGEOM *) lwline_construct(srid, NULL, pa); + return geom; } /** * Parse GML Polygon (2.1.2, 3.x.x) */ -static LWGEOM* parse_gml_polygon(xmlNodePtr xnode, bool *hasz) +static LWGEOM* parse_gml_polygon(xmlNodePtr xnode, bool *hasz, int *root_srid) { - int srid, ring; + gmlSrs *srs; + int i, ring; + LWGEOM *geom; xmlNodePtr xa, xb; POINTARRAY **ppa = NULL; + srs = parse_gml_srs(xnode); for (xa = xnode->children ; xa != NULL ; xa = xa->next) { /* Polygon/outerBoundaryIs -> GML 2.1.2 */ @@ -744,6 +908,8 @@ static LWGEOM* parse_gml_polygon(xmlNodePtr xnode, bool *hasz) || (!*hasz && !ptarray_isclosed2d(ppa[0])) || (*hasz && !ptarray_isclosed3d(ppa[0]))) lwerror("invalid GML representation"); + + if (srs->reverse_axis) ppa[0] = gml_reverse_axis_pa(ppa[0]); } } @@ -768,6 +934,8 @@ static LWGEOM* parse_gml_polygon(xmlNodePtr xnode, bool *hasz) || (!*hasz && !ptarray_isclosed2d(ppa[ring])) || (*hasz && !ptarray_isclosed3d(ppa[ring]))) lwerror("invalid GML representation"); + + if (srs->reverse_axis) ppa[ring] = gml_reverse_axis_pa(ppa[ring]); ring++; } } @@ -775,22 +943,36 @@ static LWGEOM* parse_gml_polygon(xmlNodePtr xnode, bool *hasz) /* Exterior Ring is mandatory */ if (ppa == NULL || ppa[0] == NULL) lwerror("invalid GML representation"); - srid = parse_gml_srs(xnode); - return (LWGEOM*) lwpoly_construct(srid, NULL, ring, ppa); + if (!*root_srid) { + *root_srid = srs->srid; + geom = (LWGEOM *) lwpoly_construct(*root_srid, NULL, ring, ppa); + } else { + if (srs->srid != *root_srid) { + for (i=0 ; i < ring ; i++) + gml_reproject_pa(ppa[i], srs->srid, *root_srid); + } + geom = (LWGEOM *) lwpoly_construct(-1, NULL, ring, ppa); + } + lwfree(srs); + + return geom; } /** * Parse GML Surface (3.x.x) */ -static LWGEOM* parse_gml_surface(xmlNodePtr xnode, bool *hasz) +static LWGEOM* parse_gml_surface(xmlNodePtr xnode, bool *hasz, int *root_srid) { xmlChar *interpolation; xmlNodePtr xa, xb, xc; - int patch, ring, srid; + int i, patch, ring; POINTARRAY **ppa; + LWGEOM *geom; + gmlSrs *srs; bool found=false; + srs = parse_gml_srs(xnode); /* Looking for gml:patches */ for (xa = xnode->children ; xa != NULL ; xa = xa->next) { @@ -838,6 +1020,8 @@ static LWGEOM* parse_gml_surface(xmlNodePtr xnode, bool *hasz) || (!*hasz && !ptarray_isclosed2d(ppa[0])) || (*hasz && !ptarray_isclosed3d(ppa[0]))) lwerror("invalid GML representation"); + + if (srs->reverse_axis) ppa[0] = gml_reverse_axis_pa(ppa[0]); } } @@ -862,6 +1046,8 @@ static LWGEOM* parse_gml_surface(xmlNodePtr xnode, bool *hasz) || (!*hasz && !ptarray_isclosed2d(ppa[ring])) || ( *hasz && !ptarray_isclosed3d(ppa[ring]))) lwerror("invalid GML representation"); + + if (srs->reverse_axis) ppa[ring] = gml_reverse_axis_pa(ppa[ring]); } ring++; } @@ -870,24 +1056,41 @@ static LWGEOM* parse_gml_surface(xmlNodePtr xnode, bool *hasz) /* Exterior Ring is mandatory */ if (ppa == NULL || ppa[0] == NULL) lwerror("invalid GML representation"); - srid = parse_gml_srs(xnode); - return (LWGEOM*) lwpoly_construct(srid, NULL, ring, ppa); + if (!*root_srid) { + *root_srid = srs->srid; + geom = (LWGEOM *) lwpoly_construct(*root_srid, NULL, ring, ppa); + } else { + if (srs->srid != *root_srid) { + for (i=0 ; i < ring ; i++) + gml_reproject_pa(ppa[i], srs->srid, *root_srid); + } + geom = (LWGEOM *) lwpoly_construct(-1, NULL, ring, ppa); + } + lwfree(srs); + + return geom; } /** * Parse gml:MultiPoint (2.1.2, 3.x.x) */ -static LWGEOM* parse_gml_mpoint(xmlNodePtr xnode, bool *hasz) +static LWGEOM* parse_gml_mpoint(xmlNodePtr xnode, bool *hasz, int *root_srid) { - int srid; + gmlSrs *srs; xmlNodePtr xa; LWGEOM *geom = NULL; - srid = parse_gml_srs(xnode); - - geom = (LWGEOM *)lwcollection_construct_empty(srid, 1, 0); - geom->type = lwgeom_makeType(1, 0, srid != -1, MULTIPOINTTYPE); + srs = parse_gml_srs(xnode); + if (!*root_srid) { + *root_srid = srs->srid; + geom = (LWGEOM *)lwcollection_construct_empty(*root_srid, 1, 0); + geom->type = lwgeom_makeType(1, 1, 0, MULTIPOINTTYPE); + } else { + geom = (LWGEOM *)lwcollection_construct_empty(-1, 1, 0); + geom->type = lwgeom_makeType(1, 0, 0, MULTIPOINTTYPE); + } + lwfree(srs); for (xa = xnode->children ; xa != NULL ; xa = xa->next) { @@ -896,7 +1099,7 @@ static LWGEOM* parse_gml_mpoint(xmlNodePtr xnode, bool *hasz) if (strcmp((char *) xa->name, "pointMember")) continue; if (xa->children != NULL) geom = lwmpoint_add((LWMPOINT *)geom, -1, - parse_gml(xa->children, hasz)); + parse_gml(xa->children, hasz, root_srid)); } return geom; @@ -906,25 +1109,31 @@ static LWGEOM* parse_gml_mpoint(xmlNodePtr xnode, bool *hasz) /** * Parse gml:MultiLineString (2.1.2, 3.x.x) */ -static LWGEOM* parse_gml_mline(xmlNodePtr xnode, bool *hasz) +static LWGEOM* parse_gml_mline(xmlNodePtr xnode, bool *hasz, int *root_srid) { - int srid; + gmlSrs *srs; xmlNodePtr xa; LWGEOM *geom = NULL; - srid = parse_gml_srs(xnode); - - geom = (LWGEOM *)lwcollection_construct_empty(srid, 1, 0); - geom->type = lwgeom_makeType(1, 0, srid != -1, MULTILINETYPE); + srs = parse_gml_srs(xnode); + if (!*root_srid) { + *root_srid = srs->srid; + geom = (LWGEOM *)lwcollection_construct_empty(*root_srid, 1, 0); + geom->type = lwgeom_makeType(1, 1, 0, MULTILINETYPE); + } else { + geom = (LWGEOM *)lwcollection_construct_empty(-1, 1, 0); + geom->type = lwgeom_makeType(1, 0, 0, MULTILINETYPE); + } + lwfree(srs); for (xa = xnode->children ; xa != NULL ; xa = xa->next) { - /* MultiLineString/lineMember */ + /* MultiLineString/lineStringMember */ if (xa->type != XML_ELEMENT_NODE) continue; if (strcmp((char *) xa->name, "lineStringMember")) continue; if (xa->children != NULL) geom = lwmline_add((LWMLINE *)geom, -1, - parse_gml(xa->children, hasz)); + parse_gml(xa->children, hasz, root_srid)); } return geom; @@ -934,16 +1143,22 @@ static LWGEOM* parse_gml_mline(xmlNodePtr xnode, bool *hasz) /** * Parse GML MultiCurve (3.x.x) */ -static LWGEOM* parse_gml_mcurve(xmlNodePtr xnode, bool *hasz) +static LWGEOM* parse_gml_mcurve(xmlNodePtr xnode, bool *hasz, int *root_srid) { - int srid; + gmlSrs *srs; xmlNodePtr xa; LWGEOM *geom = NULL; - srid = parse_gml_srs(xnode); - - geom = (LWGEOM *)lwcollection_construct_empty(srid, 1, 0); - geom->type = lwgeom_makeType(1, 0, srid != -1, MULTILINETYPE); + srs = parse_gml_srs(xnode); + if (!*root_srid) { + *root_srid = srs->srid; + geom = (LWGEOM *)lwcollection_construct_empty(*root_srid, 1, 0); + geom->type = lwgeom_makeType(1, 1, 0, MULTILINETYPE); + } else { + geom = (LWGEOM *)lwcollection_construct_empty(-1, 1, 0); + geom->type = lwgeom_makeType(1, 0, 0, MULTILINETYPE); + } + lwfree(srs); for (xa = xnode->children ; xa != NULL ; xa = xa->next) { @@ -952,7 +1167,7 @@ static LWGEOM* parse_gml_mcurve(xmlNodePtr xnode, bool *hasz) if (strcmp((char *) xa->name, "curveMember")) continue; if (xa->children != NULL) geom = lwmline_add((LWMLINE *)geom, -1, - parse_gml(xa->children, hasz)); + parse_gml(xa->children, hasz, root_srid)); } return geom; @@ -962,16 +1177,22 @@ static LWGEOM* parse_gml_mcurve(xmlNodePtr xnode, bool *hasz) /** * Parse GML MultiPolygon (2.1.2, 3.x.x) */ -static LWGEOM* parse_gml_mpoly(xmlNodePtr xnode, bool *hasz) +static LWGEOM* parse_gml_mpoly(xmlNodePtr xnode, bool *hasz, int *root_srid) { - int srid; + gmlSrs *srs; xmlNodePtr xa; LWGEOM *geom = NULL; - srid = parse_gml_srs(xnode); - - geom = (LWGEOM *)lwcollection_construct_empty(srid, 1, 0); - geom->type = lwgeom_makeType(1, 0, srid != -1, MULTIPOLYGONTYPE); + srs = parse_gml_srs(xnode); + if (!*root_srid) { + *root_srid = srs->srid; + geom = (LWGEOM *)lwcollection_construct_empty(*root_srid, 1, 0); + geom->type = lwgeom_makeType(1, 1, 0, MULTIPOLYGONTYPE); + } else { + geom = (LWGEOM *)lwcollection_construct_empty(-1, 1, 0); + geom->type = lwgeom_makeType(1, 0, 0, MULTIPOLYGONTYPE); + } + lwfree(srs); for (xa = xnode->children ; xa != NULL ; xa = xa->next) { @@ -980,7 +1201,7 @@ static LWGEOM* parse_gml_mpoly(xmlNodePtr xnode, bool *hasz) if (strcmp((char *) xa->name, "polygonMember")) continue; if (xa->children != NULL) geom = lwmpoly_add((LWMPOLY *)geom, -1, - parse_gml(xa->children, hasz)); + parse_gml(xa->children, hasz, root_srid)); } return geom; @@ -990,16 +1211,22 @@ static LWGEOM* parse_gml_mpoly(xmlNodePtr xnode, bool *hasz) /** * Parse GML MultiSurface (3.x.x) */ -static LWGEOM* parse_gml_msurface(xmlNodePtr xnode, bool *hasz) +static LWGEOM* parse_gml_msurface(xmlNodePtr xnode, bool *hasz, int *root_srid) { - int srid; + gmlSrs *srs; xmlNodePtr xa; LWGEOM *geom = NULL; - srid = parse_gml_srs(xnode); - - geom = (LWGEOM *)lwcollection_construct_empty(srid, 1, 0); - geom->type = lwgeom_makeType(1, 0, srid != -1, MULTIPOLYGONTYPE); + srs = parse_gml_srs(xnode); + if (!*root_srid) { + *root_srid = srs->srid; + geom = (LWGEOM *)lwcollection_construct_empty(*root_srid, 1, 0); + geom->type = lwgeom_makeType(1, 1, 0, MULTIPOLYGONTYPE); + } else { + geom = (LWGEOM *)lwcollection_construct_empty(-1, 1, 0); + geom->type = lwgeom_makeType(1, 0, 0, MULTIPOLYGONTYPE); + } + lwfree(srs); for (xa = xnode->children ; xa != NULL ; xa = xa->next) { @@ -1008,7 +1235,7 @@ static LWGEOM* parse_gml_msurface(xmlNodePtr xnode, bool *hasz) if (strcmp((char *) xa->name, "surfaceMember")) continue; if (xa->children != NULL) geom = lwmpoly_add((LWMPOLY *)geom, -1, - parse_gml(xa->children, hasz)); + parse_gml(xa->children, hasz, root_srid)); } return geom; @@ -1018,42 +1245,35 @@ static LWGEOM* parse_gml_msurface(xmlNodePtr xnode, bool *hasz) /** * Parse GML MultiGeometry (2.1.2, 3.x.x) */ -static LWGEOM* parse_gml_coll(xmlNodePtr xnode, bool *hasz) +static LWGEOM* parse_gml_coll(xmlNodePtr xnode, bool *hasz, int *root_srid) { - int srid; + gmlSrs *srs; xmlNodePtr xa; LWGEOM *geom = NULL; - srid = parse_gml_srs(xnode); - - geom = (LWGEOM *)lwcollection_construct_empty(srid, 1, 0); - geom->type = lwgeom_makeType(1, 0, srid != -1, COLLECTIONTYPE); + srs = parse_gml_srs(xnode); + if (!*root_srid) { + *root_srid = srs->srid; + geom = (LWGEOM *)lwcollection_construct_empty(*root_srid, 1, 0); + geom->type = lwgeom_makeType(1, 1, 0, COLLECTIONTYPE); + } else { + geom = (LWGEOM *)lwcollection_construct_empty(-1, 1, 0); + geom->type = lwgeom_makeType(1, 0, 0, COLLECTIONTYPE); + } + lwfree(srs); for (xa = xnode->children ; xa != NULL ; xa = xa->next) { if (xa->type != XML_ELEMENT_NODE) continue; if ( !strcmp((char *) xa->name, "pointMember") - || !strcmp((char *) xa->name, "lineMember") - || !strcmp((char *) xa->name, "curveMember") - || !strcmp((char *) xa->name, "polygonMember") - || !strcmp((char *) xa->name, "surfaceMember") + || !strcmp((char *) xa->name, "lineStringMember") + || !strcmp((char *) xa->name, "polygonMember") || !strcmp((char *) xa->name, "geometryMember")) { if (xa->children == NULL) break; geom = lwcollection_add((LWCOLLECTION *)geom, -1, - parse_gml(xa->children, hasz)); - - } else if ( !strcmp((char *) xa->name, "MultiPoint") - || !strcmp((char *) xa->name, "MultiLineString") - || !strcmp((char *) xa->name, "MultiCurve") - || !strcmp((char *) xa->name, "MultiPolygon") - || !strcmp((char *) xa->name, "MultiSurface") - || !strcmp((char *) xa->name, "MultiGeometry")) { - - if (xa->children == NULL) break; - geom = lwcollection_add((LWCOLLECTION *)geom, -1, - parse_gml(xa, hasz)); + parse_gml(xa->children, hasz, root_srid)); } } @@ -1064,44 +1284,44 @@ static LWGEOM* parse_gml_coll(xmlNodePtr xnode, bool *hasz) /** * Parse GML */ -static LWGEOM* parse_gml(xmlNodePtr xnode, bool *hasz) +static LWGEOM* parse_gml(xmlNodePtr xnode, bool *hasz, int *root_srid) { xmlNodePtr xa = xnode; while (xa != NULL && xa->type != XML_ELEMENT_NODE) xa = xa->next; if (!strcmp((char *) xa->name, "Point")) - return parse_gml_point(xa, hasz); + return parse_gml_point(xa, hasz, root_srid); if (!strcmp((char *) xa->name, "LineString")) - return parse_gml_line(xa, hasz); + return parse_gml_line(xa, hasz, root_srid); if (!strcmp((char *) xa->name, "Curve")) - return parse_gml_curve(xa, hasz); + return parse_gml_curve(xa, hasz, root_srid); if (!strcmp((char *) xa->name, "Polygon")) - return parse_gml_polygon(xa, hasz); + return parse_gml_polygon(xa, hasz, root_srid); if (!strcmp((char *) xa->name, "Surface")) - return parse_gml_surface(xa, hasz); + return parse_gml_surface(xa, hasz, root_srid); if (!strcmp((char *) xa->name, "MultiPoint")) - return parse_gml_mpoint(xa, hasz); + return parse_gml_mpoint(xa, hasz, root_srid); if (!strcmp((char *) xa->name, "MultiLineString")) - return parse_gml_mline(xa, hasz); + return parse_gml_mline(xa, hasz, root_srid); if (!strcmp((char *) xa->name, "MultiCurve")) - return parse_gml_mcurve(xa, hasz); + return parse_gml_mcurve(xa, hasz, root_srid); if (!strcmp((char *) xa->name, "MultiPolygon")) - return parse_gml_mpoly(xa, hasz); + return parse_gml_mpoly(xa, hasz, root_srid); if (!strcmp((char *) xa->name, "MultiSurface")) - return parse_gml_msurface(xa, hasz); + return parse_gml_msurface(xa, hasz, root_srid); if (!strcmp((char *) xa->name, "MultiGeometry")) - return parse_gml_coll(xa, hasz); + return parse_gml_coll(xa, hasz, root_srid); lwerror("invalid GML representation"); return NULL; /* Never reach */ diff --git a/regress/in_gml.sql b/regress/in_gml.sql index e9d905a4b..71346ae6e 100644 --- a/regress/in_gml.sql +++ b/regress/in_gml.sql @@ -5,10 +5,20 @@ -- --- spatial_ref_sys data +-- 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 '); +--- EPSG 27562 : NTF (Paris) / Lambert Centre France +INSERT INTO "spatial_ref_sys" ("srid","auth_name","auth_srid","srtext","proj4text") VALUES (27562,'EPSG',27562,'PROJCS["NTF (Paris) / Lambert Centre France",GEOGCS["NTF (Paris)",DATUM["Nouvelle_Triangulation_Francaise_Paris",SPHEROID["Clarke 1880 (IGN)",6378249.2,293.4660212936265,AUTHORITY["EPSG","7011"]],TOWGS84[-168,-60,320,0,0,0,0],AUTHORITY["EPSG","6807"]],PRIMEM["Paris",2.33722917,AUTHORITY["EPSG","8903"]],UNIT["grad",0.01570796326794897,AUTHORITY["EPSG","9105"]],AUTHORITY["EPSG","4807"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Lambert_Conformal_Conic_1SP"],PARAMETER["latitude_of_origin",52],PARAMETER["central_meridian",0],PARAMETER["scale_factor",0.99987742],PARAMETER["false_easting",600000],PARAMETER["false_northing",200000],AUTHORITY["EPSG","27562"],AXIS["X",EAST],AXIS["Y",NORTH]]','+proj=lcc +lat_1=46.8 +lat_0=46.8 +lon_0=0 +k_0=0.99987742 +x_0=600000 +y_0=200000 +a=6378249.2 +b=6356515 +towgs84=-168,-60,320,0,0,0,0 +pm=paris +units=m +no_defs '); + +--- EPSG 27582 : NTF (Paris) / France II (deprecated) +INSERT INTO "spatial_ref_sys" ("srid","auth_name","auth_srid","srtext","proj4text") VALUES (27582,'EPSG',27582,'PROJCS["NTF (Paris) / France II (deprecated)",GEOGCS["NTF (Paris)",DATUM["Nouvelle_Triangulation_Francaise_Paris",SPHEROID["Clarke 1880 (IGN)",6378249.2,293.4660212936265,AUTHORITY["EPSG","7011"]],TOWGS84[-168,-60,320,0,0,0,0],AUTHORITY["EPSG","6807"]],PRIMEM["Paris",2.33722917,AUTHORITY["EPSG","8903"]],UNIT["grad",0.01570796326794897,AUTHORITY["EPSG","9105"]],AUTHORITY["EPSG","4807"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Lambert_Conformal_Conic_1SP"],PARAMETER["latitude_of_origin",52],PARAMETER["central_meridian",0],PARAMETER["scale_factor",0.99987742],PARAMETER["false_easting",600000],PARAMETER["false_northing",2200000],AUTHORITY["EPSG","27582"],AXIS["X",EAST],AXIS["Y",NORTH]]','+proj=lcc +lat_1=46.8 +lat_0=46.8 +lon_0=0 +k_0=0.99987742 +x_0=600000 +y_0=2200000 +a=6378249.2 +b=6356515 +towgs84=-168,-60,320,0,0,0,0 +pm=paris +units=m +no_defs '); + + + -- Empty Geometry SELECT 'empty_geom', ST_AsEWKT(ST_GeomFromGML(NULL)); @@ -253,7 +263,8 @@ SELECT 'mpoint_5', ST_AsEWKT(ST_GeomFromGML('') -- XML not elements handle SELECT 'mpoint_6', ST_AsEWKT(ST_GeomFromGML(' 1,2 3,4')); --- TODO Mixed srsName +-- Mixed srsName +SELECT 'mpoint_7', ST_AsEWKT(ST_GeomFromGML('1,2400000,5000000')); @@ -282,7 +293,8 @@ SELECT 'mline_6', ST_AsEWKT(ST_GeomFromGML(' SELECT 'mcurve_7', ST_AsEWKT(ST_GeomFromGML('1 2 3 4 5 67 8 9 10')); SELECT 'mcurve_8', ST_AsEWKT(ST_GeomFromGML('1 2 3 45 6 7 8 9 10')); --- TODO Mixed srsName +-- Mixed srsName +SELECT 'mcurve_9', ST_AsEWKT(ST_GeomFromGML('1,2 3,4400000,5000000 400010,5000010')); @@ -340,7 +353,8 @@ SELECT 'mpoly_6', ST_AsEWKT(ST_GeomFromGML(' 1,2 3,4')); +SELECT 'collection_11', ST_AsEWKT(ST_GeomFromGML(' 1 2 3 4 5')); -- Mixed dimension -SELECT 'collection_12', ST_AsEWKT(ST_GeomFromGML('1 2 34 5')); -SELECT 'collection_13', ST_AsEWKT(ST_GeomFromGML('1 23 4 5')); - --- TODO Mixed srsName +SELECT 'collection_12', ST_AsEWKT(ST_GeomFromGML('1 2 34 5')); +SELECT 'collection_13', ST_AsEWKT(ST_GeomFromGML('1 23 4 5')); +-- Mixed srsName +SELECT 'collection_14', ST_AsEWKT(ST_GeomFromGML('1,2400000,5000000')); @@ -426,33 +441,39 @@ SELECT 'collection_13', ST_AsEWKT(ST_GeomFromGML('1 2')); SELECT 'srs_2', ST_AsEWKT(ST_GeomFromGML('1 2')); SELECT 'srs_3', ST_AsEWKT(ST_GeomFromGML('1 2')); -SELECT 'srs_4', ST_AsEWKT(ST_GeomFromGML('1 2')); -SELECT 'srs_5', ST_AsEWKT(ST_GeomFromGML('1 2')); -SELECT 'srs_6', ST_AsEWKT(ST_GeomFromGML('1 2')); -SELECT 'srs_7', ST_AsEWKT(ST_GeomFromGML('1 2')); -SELECT 'srs_8', ST_AsEWKT(ST_GeomFromGML('1 2')); +SELECT 'srs_4', ST_AsEWKT(ST_GeomFromGML('1 2')); +SELECT 'srs_5', ST_AsEWKT(ST_GeomFromGML('1 2')); +SELECT 'srs_6', ST_AsEWKT(ST_GeomFromGML('1 2')); +SELECT 'srs_7', ST_AsEWKT(ST_GeomFromGML('1 2')); -- ERROR not a valid pattern -SELECT 'srs_9', ST_AsEWKT(ST_GeomFromGML('1 2')); +SELECT 'srs_8', ST_AsEWKT(ST_GeomFromGML('1 2')); -- ERROR: not a defined SRID -SELECT 'srs_10', ST_AsEWKT(ST_GeomFromGML('1 2')); +SELECT 'srs_9', ST_AsEWKT(ST_GeomFromGML('1 2')); -- ERROR: SRID is not an int -SELECT 'srs_11', ST_AsEWKT(ST_GeomFromGML('1 2')); +SELECT 'srs_10', ST_AsEWKT(ST_GeomFromGML('1 2')); -- ERROR: SRID is not only int -SELECT 'srs_12', ST_AsEWKT(ST_GeomFromGML('1 2')); -SELECT 'srs_13', ST_AsEWKT(ST_GeomFromGML('1 2')); +SELECT 'srs_11', ST_AsEWKT(ST_GeomFromGML('1 2')); +SELECT 'srs_12', ST_AsEWKT(ST_GeomFromGML('1 2')); -- ERROR: srsName empty -SELECT 'srs_14', ST_AsEWKT(ST_GeomFromGML('1 2')); -SELECT 'srs_15', ST_AsEWKT(ST_GeomFromGML('1 2')); +SELECT 'srs_13', ST_AsEWKT(ST_GeomFromGML('1 2')); +SELECT 'srs_14', ST_AsEWKT(ST_GeomFromGML('1 2')); -- ERROR: srsName is defined as -1 -SELECT 'srs_16', ST_AsEWKT(ST_GeomFromGML('1 2')); +SELECT 'srs_15', ST_AsEWKT(ST_GeomFromGML('1 2')); + +-- Reverse axis with all kind of simples geometry types +SELECT 'srs_16', ST_AsEWKT(ST_GeomFromGML('1 23 4 5 67 8 9 1011 12 13 14 15 16 11 1217 18 19 20 21 22 17 1823 24 25 26 27 28 23 2425 26 27 28 29 30 25 26')); + + + +-- Reverse axis with severals multi geometry types -- -- TODO GML Namespace @@ -791,3 +812,5 @@ SELECT 'double_31', ST_AsEWKT(ST_GeomFromGML('1 $0%@#$^%#