]> granicus.if.org Git - postgis/commitdiff
Add implementation for ST_Covers(geography, geography) in point-in-polygon case.
authorPaul Ramsey <pramsey@cleverelephant.ca>
Fri, 9 Oct 2009 19:23:05 +0000 (19:23 +0000)
committerPaul Ramsey <pramsey@cleverelephant.ca>
Fri, 9 Oct 2009 19:23:05 +0000 (19:23 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@4635 b70326c6-7e19-0410-871a-916f4a2858ee

liblwgeom/libgeom.h
liblwgeom/liblwgeom.h
liblwgeom/lwgeodetic.c
liblwgeom/lwgeom.c
liblwgeom/measures.c
postgis/geography.sql.in.c
postgis/geography_measurement.c
postgis/lwgeom_dump.c
postgis/lwgeom_functions_basic.c

index 2a0bc632156cb8fa21440c86911c7468880750d5..0d9e6d61e6f52b5deb748b8550ec2af9a88ebdc2 100644 (file)
@@ -382,6 +382,12 @@ extern double lwgeom_distance_sphere(LWGEOM *lwgeom1, LWGEOM *lwgeom2, GBOX gbox
 */
 extern double lwgeom_area_sphere(LWGEOM *lwgeom, GBOX gbox);
 
+/**
+* Calculate covers predicate for two lwgeoms on the sphere. Currently
+* only handles point-in-polygon.
+*/
+extern int lwgeom_covers_lwgeom_sphere(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2, GBOX gbox1, GBOX gbox2);
+
 /**
 * New function to read doubles directly from the double* coordinate array
 * of an aligned lwgeom #POINTARRAY (built by de-serializing a #GSERIALIZED).
index 801e8e7467b59c392632bc416fcb45a124847cec..b570fff7e72d732fedbd241e895c961e7f028d35 100644 (file)
@@ -538,7 +538,7 @@ extern void lwgeom_add_bbox(LWGEOM *lwgeom);
 extern void lwgeom_dropSRID(LWGEOM *lwgeom);
 
 /* Determine whether a LWGEOM can contain sub-geometries or not */
-extern int lwgeom_contains_subgeoms(int type);
+extern int lwgeom_is_collection(int type);
 
 /******************************************************************/
 
index 3ac393b255ae338e20735da59bdd06e1fbbdf7c1..f00c1e67c4aa699b45b87bbea404a180af4fc987 100644 (file)
@@ -1758,7 +1758,7 @@ double lwgeom_distance_sphere(LWGEOM *lwgeom1, LWGEOM *lwgeom2, GBOX gbox1, GBOX
        }
 
        /* Recurse into collections */
-       if( lwgeom_contains_subgeoms(type1) )
+       if( lwgeom_is_collection(type1) )
        {
                int i;
                double distance = MAXFLOAT;
@@ -1776,7 +1776,7 @@ double lwgeom_distance_sphere(LWGEOM *lwgeom1, LWGEOM *lwgeom2, GBOX gbox1, GBOX
        }
 
        /* Recurse into collections */
-       if( lwgeom_contains_subgeoms(type2) )
+       if( lwgeom_is_collection(type2) )
        {
                int i;
                double distance = MAXFLOAT;
@@ -1799,6 +1799,71 @@ double lwgeom_distance_sphere(LWGEOM *lwgeom1, LWGEOM *lwgeom2, GBOX gbox1, GBOX
        
 }
 
+
+int lwgeom_covers_lwgeom_sphere(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2, GBOX gbox1, GBOX gbox2)
+{
+       int type1, type2;
+       
+       assert(lwgeom1);
+       assert(lwgeom2);
+       
+       type1 = TYPE_GETTYPE(lwgeom1->type);
+       type2 = TYPE_GETTYPE(lwgeom2->type);
+       
+       /* Currently a restricted implementation */
+       if( ! ( (type1 == POLYGONTYPE || type1 == MULTIPOLYGONTYPE || type1 == COLLECTIONTYPE) &&
+               (type2 == POINTTYPE || type2 == MULTIPOINTTYPE || type2 == COLLECTIONTYPE) ) )
+       {
+               lwerror("lwgeom_covers_lwgeom_sphere: only POLYGON and POINT types are currently supported");
+               return LW_FALSE;
+       }
+
+       /* Handle the polygon/point case */
+       if( type1 == POLYGONTYPE && type2 == POINTTYPE )
+       {
+               POINT2D pt_to_test;
+               getPoint2d_p(((LWPOINT*)lwgeom2)->point, 0, &pt_to_test);
+               return lwpoly_covers_point2d((LWPOLY*)lwgeom1, gbox1, pt_to_test);
+       }
+       
+       /* If any of the first argument parts covers the second argument, it's true */
+       if( lwgeom_is_collection( type1 ) )
+       {
+               int i;
+               LWCOLLECTION *col = (LWCOLLECTION*)lwgeom1;
+               
+               for( i = 0; i < col->ngeoms; i++ )
+               {
+                       if( lwgeom_covers_lwgeom_sphere(col->geoms[i], lwgeom2, gbox1, gbox2) )
+                       {
+                               return LW_TRUE;
+                       }
+               }
+               return LW_FALSE;
+       }
+
+       /* Only if all of the second arguments are covered by the first argument is the condition true */
+       if( lwgeom_is_collection( type2 ) )
+       {
+               int i;
+               LWCOLLECTION *col = (LWCOLLECTION*)lwgeom2;
+               
+               for( i = 0; i < col->ngeoms; i++ )
+               {
+                       if( ! lwgeom_covers_lwgeom_sphere(lwgeom1, col->geoms[i], gbox1, gbox2) )
+                       {
+                               return LW_FALSE;
+                       }
+               }
+               return LW_TRUE;
+       }
+       
+       /* Don't get here */
+       lwerror("lwgeom_covers_lwgeom_sphere: reached end of function without resolution");
+       return LW_FALSE;
+       
+}
+
 /**
 * Given a polygon (lon/lat decimal degrees) and point (lon/lat decimal degrees) and
 * a guaranteed outside point (lon/lat decimal degrees) (calculate with gbox_pt_outside()) 
index f5d7aed0c2507b013c252716c63a7e03f474b265..c65749268dca117f5bdebb5dc4fc5eaed0940cf6 100644 (file)
@@ -404,7 +404,7 @@ lwgeom_as_multi(LWGEOM *lwgeom)
        ** This funx is a no-op only if a bbox cache is already present
        ** in input.
        */
-       if ( lwgeom_contains_subgeoms(TYPE_GETTYPE(lwgeom->type)) )
+       if ( lwgeom_is_collection(TYPE_GETTYPE(lwgeom->type)) )
        {
                return lwgeom_clone(lwgeom);
        }
@@ -928,7 +928,7 @@ lwgeom_longitude_shift(LWGEOM *lwgeom)
 
 /** Return TRUE if the geometry may contain sub-geometries, i.e. it is a MULTI* or COMPOUNDCURVE */
 int
-lwgeom_contains_subgeoms(int type)
+lwgeom_is_collection(int type)
 {
 
        switch (type)
index ea6592b81df7d74bdb90d06f789a444b3ce8c5e0..6f8ff375b7deb0b2c6a3648e44e4bcf334b72c2d 100644 (file)
@@ -667,7 +667,7 @@ lwgeom_mindistance2d_recursive_tolerance(uchar *lw1, uchar *lw2, double toleranc
                double dist=tolerance;
 
                /* Argument 1 is a multitype... recurse */
-               if ( lwgeom_contains_subgeoms(t1) )
+               if ( lwgeom_is_collection(t1) )
                {
                        dist = lwgeom_mindistance2d_recursive_tolerance(g1, lw2, tolerance);
                        if ( dist <= tolerance ) return tolerance; /* can't be closer */
@@ -682,7 +682,7 @@ lwgeom_mindistance2d_recursive_tolerance(uchar *lw1, uchar *lw2, double toleranc
                        int t2 = lwgeom_getType(g2[0]);
 
                        /* Argument 2 is a multitype... recurse */
-                       if ( lwgeom_contains_subgeoms(t2) )
+                       if ( lwgeom_is_collection(t2) )
                        {
                                dist = lwgeom_mindistance2d_recursive_tolerance(g1, g2, tolerance);
                                if ( dist <= tolerance ) return tolerance; /* can't be closer */
@@ -775,7 +775,7 @@ lwgeom_mindistance2d_recursive_tolerance(uchar *lw1, uchar *lw2, double toleranc
                                        lwerror("Unsupported geometry type: %s", lwgeom_typename(t2));
                                }
                        }
-//                     else if (lwgeom_contains_subgeoms(t1)) /* it's a multitype... recurse */
+//                     else if (lwgeom_is_collection(t1)) /* it's a multitype... recurse */
 //                     {
 //                             dist = lwgeom_mindistance2d_recursive_tolerance(g1, g2, tolerance);
 //                     }
index 7207909439ec8695a67eec98951fe37d62aa6d21..e40f4601f010d44701b6c8a8d3657fe71c9b8f21 100644 (file)
@@ -462,6 +462,27 @@ CREATE OR REPLACE FUNCTION ST_PointOutside(geography)
        AS 'MODULE_PATHNAME','geography_point_outside'
        LANGUAGE 'C' IMMUTABLE STRICT;
 
+-- Only implemented for polygon-over-point
+-- Availability: 1.5.0
+CREATE OR REPLACE FUNCTION _ST_Covers(geography, geography)
+       RETURNS boolean
+       AS 'MODULE_PATHNAME','geography_covers'
+       LANGUAGE 'C' IMMUTABLE STRICT;
+
+-- Only implemented for polygon-over-point
+-- Availability: 1.5.0
+CREATE OR REPLACE FUNCTION ST_Covers(geography, geography)
+       RETURNS boolean
+       AS 'SELECT $1 && $2 AND _ST_Covers($1, $2)'
+       LANGUAGE 'SQL' IMMUTABLE STRICT;
+
+-- Only implemented for polygon-over-point
+-- Availability: 1.5.0
+CREATE OR REPLACE FUNCTION ST_CoveredBy(geography, geography)
+       RETURNS boolean
+       AS 'SELECT $1 && $2 AND _ST_Covers($2, $1)'
+       LANGUAGE 'SQL' IMMUTABLE STRICT;
+
 -- ---------- ---------- ---------- ---------- ---------- ---------- ----------
 
 COMMIT;
index 45efdd5f1efa3c7018cd493b60b5866f77a12d57..ceb691e0e493c2fdd2efca502ed6f3900619487e 100644 (file)
@@ -26,6 +26,8 @@
 Datum geography_distance_sphere(PG_FUNCTION_ARGS);
 Datum geography_area_sphere(PG_FUNCTION_ARGS);
 Datum geography_expand(PG_FUNCTION_ARGS);
+Datum geography_point_outside(PG_FUNCTION_ARGS);
+Datum geography_covers(PG_FUNCTION_ARGS);
 
 
 /*
@@ -210,4 +212,54 @@ Datum geography_point_outside(PG_FUNCTION_ARGS)
        
        PG_RETURN_POINTER(g_out);
 
-}
\ No newline at end of file
+}
+
+PG_FUNCTION_INFO_V1(geography_covers);
+Datum geography_covers(PG_FUNCTION_ARGS)
+{
+       LWGEOM *lwgeom1 = NULL;
+       LWGEOM *lwgeom2 = NULL;
+       GBOX gbox1;
+       GBOX gbox2;
+       GSERIALIZED *g1 = NULL;
+       GSERIALIZED *g2 = NULL;
+       int type1, type2;
+       int result = LW_FALSE;
+       
+       /* Get our geometry objects loaded into memory. */
+       g1 = (GSERIALIZED*)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       g2 = (GSERIALIZED*)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+       
+       type1 = gserialized_get_type(g1);
+       type2 = gserialized_get_type(g2);
+
+       /* Right now we only handle points and polygons */
+       if( ! ( (type1 == POLYGONTYPE || type1 == MULTIPOLYGONTYPE || type1 == COLLECTIONTYPE) &&
+               (type2 == POINTTYPE || type2 == MULTIPOINTTYPE || type2 == COLLECTIONTYPE) ) )
+       {
+               elog(ERROR, "geography_covers: only POLYGON and POINT types are currently supported");
+               PG_RETURN_NULL();
+       }
+       
+       /* We need the bounding boxes in case of polygon calculations,
+          which requires them to generate a stab-line to test point-in-polygon. */
+       if( ! gbox_from_gserialized(g1, &gbox1) ||
+           ! gbox_from_gserialized(g2, &gbox2) )
+       {
+               elog(ERROR, "geography_covers: error in gbox_from_gserialized calculation.");
+               PG_RETURN_NULL();
+       }
+       
+       /* Construct our working geometries */
+       lwgeom1 = lwgeom_from_gserialized(g1);
+       lwgeom2 = lwgeom_from_gserialized(g2);
+       
+       /* Calculate answer */
+       result = lwgeom_covers_lwgeom_sphere(lwgeom1, lwgeom2, gbox1, gbox2);
+
+       /* Clean up, but not all the way to the point arrays */
+       lwgeom_release(lwgeom1);
+       lwgeom_release(lwgeom2);
+       
+       PG_RETURN_BOOL(result);
+}
index e439c6fb3354f8bcc987352eb2759af69e79d089..af971742750c3b53541cfd353346a1f37ca54a3d 100644 (file)
@@ -89,7 +89,7 @@ Datum LWGEOM_dump(PG_FUNCTION_ARGS)
                state->root = lwgeom;
                state->stacklen=0;
 
-               if ( lwgeom_contains_subgeoms(TYPE_GETTYPE(lwgeom->type)) )
+               if ( lwgeom_is_collection(TYPE_GETTYPE(lwgeom->type)) )
                {
                        /*
                         * Push a GEOMDUMPNODE on the state stack
@@ -127,7 +127,7 @@ Datum LWGEOM_dump(PG_FUNCTION_ARGS)
 
        /* Handled simple geometries */
        if ( ! state->root ) SRF_RETURN_DONE(funcctx);
-       if ( ! lwgeom_contains_subgeoms(TYPE_GETTYPE(state->root->type)) )
+       if ( ! lwgeom_is_collection(TYPE_GETTYPE(state->root->type)) )
        {
                values[0] = "{}";
                values[1] = lwgeom_to_hexwkb(state->root, PARSER_CHECK_NONE, -1);
@@ -146,7 +146,7 @@ Datum LWGEOM_dump(PG_FUNCTION_ARGS)
                if ( node->idx < lwcoll->ngeoms )
                {
                        lwgeom = lwcoll->geoms[node->idx];
-                       if ( ! lwgeom_contains_subgeoms(TYPE_GETTYPE(lwgeom->type)) )
+                       if ( ! lwgeom_is_collection(TYPE_GETTYPE(lwgeom->type)) )
                        {
                                /* write address of current geom */
                                ptr=address;
index 129790fd900d6ec7a7107ca0707df845b0310f59..bebcdcc046f6ebc4a17f284624efa9aaa9ee2238 100644 (file)
@@ -1460,7 +1460,7 @@ Datum LWGEOM_force_collection(PG_FUNCTION_ARGS)
        lwgeom = lwgeom_deserialize(SERIALIZED_FORM(geom));
 
        /* alread a multi*, just make it a collection */
-       if ( lwgeom_contains_subgeoms(TYPE_GETTYPE(lwgeom->type)) )
+       if ( lwgeom_is_collection(TYPE_GETTYPE(lwgeom->type)) )
        {
                TYPE_SETTYPE(lwgeom->type, COLLECTIONTYPE);
        }
@@ -1503,7 +1503,7 @@ Datum LWGEOM_force_multi(PG_FUNCTION_ARGS)
        ** in input. If bbox cache is not there we'll need to handle
        ** automatic bbox addition FOR_COMPLEX_GEOMS.
        */
-       if ( lwgeom_contains_subgeoms(TYPE_GETTYPE(geom->type)) && TYPE_HASBBOX(geom->type) )
+       if ( lwgeom_is_collection(TYPE_GETTYPE(geom->type)) && TYPE_HASBBOX(geom->type) )
        {
                PG_RETURN_POINTER(geom);
        }