From 08f331e6b07b28d057f713a13c66cb64e585d6b4 Mon Sep 17 00:00:00 2001 From: Paul Ramsey Date: Tue, 31 Jan 2012 18:19:36 +0000 Subject: [PATCH] ST_SnapToGrid returns a value out of range (#1292) git-svn-id: http://svn.osgeo.org/postgis/trunk@8980 b70326c6-7e19-0410-871a-916f4a2858ee --- liblwgeom/liblwgeom.h.in | 5 ++ liblwgeom/lwgeodetic.c | 101 ++++++++++++++++++++++++++++++++++++++ postgis/geography_inout.c | 18 ++++--- 3 files changed, 118 insertions(+), 6 deletions(-) diff --git a/liblwgeom/liblwgeom.h.in b/liblwgeom/liblwgeom.h.in index c8d226f6c..ce1fcdfe1 100644 --- a/liblwgeom/liblwgeom.h.in +++ b/liblwgeom/liblwgeom.h.in @@ -1437,6 +1437,11 @@ extern GSERIALIZED* gserialized_copy(const GSERIALIZED *g); */ extern int lwgeom_check_geodetic(const LWGEOM *geom); +/** +* Check that coordinates of LWGEOM are all within the geodetic range. +*/ +extern int lwgeom_nudge_geodetic(LWGEOM *geom); + /** * Set the FLAGS geodetic bit on geometry an all sub-geometries and pointlists */ diff --git a/liblwgeom/lwgeodetic.c b/liblwgeom/lwgeodetic.c index 83d7db663..0db9d4893 100644 --- a/liblwgeom/lwgeodetic.c +++ b/liblwgeom/lwgeodetic.c @@ -2653,3 +2653,104 @@ double lwgeom_length_spheroid(const LWGEOM *geom, const SPHEROID *s) lwerror("unsupported type passed to lwgeom_length_sphere"); return 0.0; } + +/** +* When features are snapped or sometimes they are just this way, they are very close to +* the geodetic bounds but slightly over. This routine nudges those points, and only +* those points, back over to the bounds. +* http://trac.osgeo.org/postgis/ticket/1292 +*/ +int ptarray_nudge_geodetic(POINTARRAY *pa) +{ + int i; + POINT4D p; + int altered = LW_FALSE; + int rv = LW_FALSE; + static double tolerance = 1e-10; + if ( ! pa ) return; + for(i = 0; i < pa->npoints; i++ ) + { + getPoint4d_p(pa, i, &p); + if ( p.x < -180.0 && (-180.0 - p.x < tolerance) ) + { + p.x = -180.0; + altered = LW_TRUE; + } + else if ( p.x > 180.0 && (p.x - 180.0 < tolerance) ) + { + p.x = 180.0; + altered = LW_TRUE; + } + else if ( p.y < -90.0 && (-90.0 - p.y < tolerance) ) + { + p.y = -90.0; + altered = LW_TRUE; + } + else if ( p.y > 90.0 && (p.y - 90.0 < tolerance) ) + { + p.y = 90.0; + altered = LW_TRUE; + } + if ( altered == LW_TRUE ) + { + ptarray_set_point4d(pa, i, &p); + altered = LW_FALSE; + rv = LW_TRUE; + } + } + return rv; +} + +/** +* When features are snapped or sometimes they are just this way, they are very close to +* the geodetic bounds but slightly over. This routine nudges those points, and only +* those points, back over to the bounds. +* http://trac.osgeo.org/postgis/ticket/1292 +*/ +int lwgeom_nudge_geodetic(LWGEOM *geom) +{ + int type; + int i = 0; + int rv = LW_FALSE; + + assert(geom); + + /* No points in nothing */ + if ( lwgeom_is_empty(geom) ) + return LW_FALSE; + + type = geom->type; + + if ( type == POINTTYPE ) + return ptarray_nudge_geodetic(((LWPOINT*)geom)->point); + + if ( type == LINETYPE ) + return ptarray_nudge_geodetic(((LWLINE*)geom)->points); + + if ( type == POLYGONTYPE ) + { + LWPOLY *poly = (LWPOLY*)geom; + for ( i = 0; i < poly->nrings; i++ ) + { + rv = (rv == LW_TRUE ? rv : ptarray_nudge_geodetic(poly->rings[i])); + } + return rv; + } + + if ( type == TRIANGLETYPE ) + return ptarray_nudge_geodetic(((LWTRIANGLE*)geom)->points); + + if ( lwtype_is_collection( type ) ) + { + LWCOLLECTION *col = (LWCOLLECTION*)geom; + + for ( i = 0; i < col->ngeoms; i++ ) + { + rv = (rv == LW_TRUE ? rv : ptarray_nudge_geodetic(col->geoms[i])); + } + return rv; + } + + lwerror("unsupported type (%s) passed to lwgeom_nudge_geodetic", lwtype_name(type)); + return rv; +} \ No newline at end of file diff --git a/postgis/geography_inout.c b/postgis/geography_inout.c index f710dc416..d121a4b21 100644 --- a/postgis/geography_inout.c +++ b/postgis/geography_inout.c @@ -87,9 +87,12 @@ GSERIALIZED* gserialized_geography_from_lwgeom(LWGEOM *lwgeom, int32 geog_typmod /* Check that the coordinates are in range */ if ( lwgeom_check_geodetic(lwgeom) == LW_FALSE ) { - ereport(ERROR, ( - errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("Coordinate values are out of range [-180 -90, 180 90] for GEOGRAPHY type" ))); + if ( (! lwgeom_nudge_geodetic(lwgeom)) || lwgeom_check_geodetic(lwgeom) == LW_FALSE ) + { + ereport(ERROR, ( + errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("Coordinate values are out of range [-180 -90, 180 90] for GEOGRAPHY type" ))); + } } /* Force default SRID to the default */ @@ -562,9 +565,12 @@ Datum geography_from_geometry(PG_FUNCTION_ARGS) /* Check if the geography has valid coordinate range. */ if ( lwgeom_check_geodetic(lwgeom) == LW_FALSE ) { - ereport(ERROR, ( - errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("Coordinate values are out of range [-180 -90, 180 90] for GEOGRAPHY type" ))); + if ( (! lwgeom_nudge_geodetic(lwgeom)) || lwgeom_check_geodetic(lwgeom) == LW_FALSE ) + { + ereport(ERROR, ( + errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("Coordinate values are out of range [-180 -90, 180 90] for GEOGRAPHY type" ))); + } } /* -- 2.40.0