*/
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).
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);
/******************************************************************/
}
/* Recurse into collections */
- if( lwgeom_contains_subgeoms(type1) )
+ if( lwgeom_is_collection(type1) )
{
int i;
double distance = MAXFLOAT;
}
/* Recurse into collections */
- if( lwgeom_contains_subgeoms(type2) )
+ if( lwgeom_is_collection(type2) )
{
int i;
double distance = MAXFLOAT;
}
+
+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())
** 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);
}
/** 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)
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 */
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 */
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);
// }
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;
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);
/*
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);
+}
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
/* 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);
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;
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);
}
** 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);
}