]> granicus.if.org Git - postgis/commitdiff
Prepared geometries getting closer to readiness. Integrated into standard functions...
authorPaul Ramsey <pramsey@cleverelephant.ca>
Fri, 10 Oct 2008 04:39:54 +0000 (04:39 +0000)
committerPaul Ramsey <pramsey@cleverelephant.ca>
Fri, 10 Oct 2008 04:39:54 +0000 (04:39 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@3085 b70326c6-7e19-0410-871a-916f4a2858ee

lwgeom/Makefile.in
lwgeom/lwgeom_geos.h [new file with mode: 0644]
lwgeom/lwgeom_geos_c.c
lwgeom/lwgeom_geos_prepared.c [new file with mode: 0644]
lwgeom/lwgeom_geos_prepared.h [new file with mode: 0644]
lwgeom/lwgeom_rtree.h
lwgeom/lwpostgis.sql.in.c
regress/README
regress/regress_ogc_prep.sql
regress/regress_ogc_prep_expected

index c13a7478f4931f66f1b87a9c3d950a13e93dd5a9..683f017a6c2ea06e89fee99ffbb6fa5ac7683d28 100644 (file)
@@ -38,6 +38,7 @@ PG_OBJS=lwgeom_pg.o \
        lwgeom_box2dfloat4.o \
        lwgeom_chip.o \
        lwgeom_geos_c.o \
+       lwgeom_geos_prepared.o \
        lwgeom_svg.o \
        lwgeom_gml.o \
        lwgeom_kml.o \
diff --git a/lwgeom/lwgeom_geos.h b/lwgeom/lwgeom_geos.h
new file mode 100644 (file)
index 0000000..39a367d
--- /dev/null
@@ -0,0 +1,45 @@
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2008 Paul Ramsey <pramsey@cleverelephant.ca>
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ * 
+ **********************************************************************/
+
+#include <string.h>
+
+#include "../postgis_config.h"
+
+#include "postgres.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "fmgr.h"
+#include "miscadmin.h"
+
+#include "geos_c.h"
+
+#include "lwgeom_pg.h"
+#include "liblwgeom.h"
+#include "profile.h"
+
+#ifdef PROFILE
+#warning PROFILE enabled!
+#endif
+
+/*
+** Public prototypes for GEOS utility functions.
+*/
+
+PG_LWGEOM *GEOS2POSTGIS(GEOSGeom geom, char want3d);
+GEOSGeom POSTGIS2GEOS(PG_LWGEOM *g);
+
+LWGEOM *GEOS2LWGEOM(GEOSGeom geom, char want3d);
+GEOSGeom LWGEOM2GEOS(LWGEOM *g);
+
+POINTARRAY *ptarray_from_GEOSCoordSeq(GEOSCoordSeq cs, char want3d);
+void errorIfGeometryCollection(PG_LWGEOM *g1, PG_LWGEOM *g2);
+
index e240d182c631b45830f1ae61cde168b49133fc10..b1479d63a7211ecf355eaf525425661fda9f2395 100644 (file)
@@ -1,31 +1,20 @@
-#include "../postgis_config.h"
-
-#include "postgres.h"
-#include "utils/array.h"
-#include "utils/builtins.h"
-#include "fmgr.h"
-#include "miscadmin.h"
-
-
-#include "lwgeom_pg.h"
-#include "liblwgeom.h"
-#include "profile.h"
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2001-2003 Refractions Research Inc.
+ * Copyright 2008 Paul Ramsey <pramsey@cleverelephant.ca>
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ * 
+ **********************************************************************/
 
-#include "geos_c.h"
+#include "lwgeom_geos.h"
 #include "lwgeom_rtree.h"
+#include "lwgeom_geos_prepared.h"
 
-#include <string.h>
-
-
-#ifdef PROFILE
-#warning PROFILE enabled!
-#endif
-
-/*
- * Define this to have have many notices printed
- * during postgis->geos and geos->postgis conversions
- *
- */
 
 /*
  *
@@ -52,6 +41,7 @@ Datum intersects(PG_FUNCTION_ARGS);
 Datum crosses(PG_FUNCTION_ARGS);
 Datum within(PG_FUNCTION_ARGS);
 Datum contains(PG_FUNCTION_ARGS);
+Datum containsproperly(PG_FUNCTION_ARGS);
 Datum covers(PG_FUNCTION_ARGS);
 Datum overlaps(PG_FUNCTION_ARGS);
 Datum isvalid(PG_FUNCTION_ARGS);
@@ -77,14 +67,7 @@ Datum LWGEOM_buildarea(PG_FUNCTION_ARGS);
 Datum linemerge(PG_FUNCTION_ARGS);
 Datum coveredby(PG_FUNCTION_ARGS);
 
-
-LWGEOM *GEOS2LWGEOM(GEOSGeom geom, char want3d);
-PG_LWGEOM *GEOS2POSTGIS(GEOSGeom geom, char want3d);
-GEOSGeom POSTGIS2GEOS(PG_LWGEOM *g);
-GEOSGeom LWGEOM2GEOS(LWGEOM *g);
-POINTARRAY *ptarray_from_GEOSCoordSeq(GEOSCoordSeq cs, char want3d);
-
-void errorIfGeometryCollection(PG_LWGEOM *g1, PG_LWGEOM *g2);
+/* TODO: move these to a lwgeom_functions_analytic.h */
 int point_in_polygon_rtree(RTREE_NODE **root, int ringCount, LWPOINT *point);
 int point_in_multipolygon_rtree(RTREE_NODE **root, int polyCount, int ringCount, LWPOINT *point);
 int point_in_polygon(LWPOLY *polygon, LWPOINT *point);
@@ -92,6 +75,26 @@ int point_in_multipolygon(LWMPOLY *mpolygon, LWPOINT *pont);
 
 /* PROTOTYPES end */
 
+/*********************************************************************************
+**
+**  PreparedGeometry implementations that cache intermediate indexed versions
+**  of geometry in a special MemoryContext for re-used by future function
+**  invocations.
+**
+**  By creating a memory context to hold the GEOS PreparedGeometry and Geometry
+**  and making it a child of the fmgr memory context, we can get the memory held
+**  by the GEOS objects released when the memory context delete callback is
+**  invoked by the parent context.
+**
+*********************************************************************************/
+
+#include "utils/memutils.h"
+#include "executor/spi.h"
+#include "access/hash.h"
+#include "utils/hsearch.h"
+
+
+
 PG_FUNCTION_INFO_V1(postgis_geos_version);
 Datum postgis_geos_version(PG_FUNCTION_ARGS)
 {
@@ -126,7 +129,7 @@ Datum unite_garray(PG_FUNCTION_ARGS)
        static int call=1;
 #endif
 
-#if POSTGIS_DEBUG_LEVEL >= 2
+#if POSTGIS_DEBUG_LEVEL > 0
        call++;
        POSTGIS_DEBUGF(2, "GEOS incremental union (call %d)", call);
 #endif
@@ -1402,11 +1405,11 @@ Datum contains(PG_FUNCTION_ARGS)
        RTREE_POLY_CACHE *poly_cache;
        MemoryContext old_context;
        bool result;
-
-#ifdef PROFILE
-       profstart(PROF_QRUN);
+#ifdef PREPARED_GEOM
+       PrepGeomCache *prep_cache;
 #endif
 
+
        geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
        geom2 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
 
@@ -1491,31 +1494,26 @@ Datum contains(PG_FUNCTION_ARGS)
 
        initGEOS(lwnotice, lwnotice);
 
-#ifdef PROFILE
-       profstart(PROF_P2G1);
-#endif
-       g1 = POSTGIS2GEOS(geom1);
-#ifdef PROFILE
-       profstop(PROF_P2G1);
-#endif
-#ifdef PROFILE
-       profstart(PROF_P2G2);
-#endif
-       g2 = POSTGIS2GEOS(geom2);
-#ifdef PROFILE
-       profstop(PROF_P2G2);
-#endif
+#ifdef PREPARED_GEOM
+       prep_cache = GetPrepGeomCache( fcinfo, geom1, 0 );
 
-#ifdef PROFILE
-       profstart(PROF_GRUN);
-#endif
-       result = GEOSContains(g1,g2);
-#ifdef PROFILE
-       profstop(PROF_GRUN);
+       if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
+       {
+               g1 = POSTGIS2GEOS(geom2);
+               POSTGIS_DEBUG(4, "containsPrepared: cache is live, running preparedcontains");
+               result = GEOSPreparedContains( prep_cache->prepared_geom, g1);
+               GEOSGeom_destroy(g1);
+       }
+       else
 #endif
-
-       GEOSGeom_destroy(g1);
-       GEOSGeom_destroy(g2);
+       {
+               g1 = POSTGIS2GEOS(geom1);
+               g2 = POSTGIS2GEOS(geom2);
+               POSTGIS_DEBUG(4, "containsPrepared: cache is not ready, running standard contains");
+               result = GEOSContains( g1, g2);
+               GEOSGeom_destroy(g1);
+               GEOSGeom_destroy(g2);
+       }
 
        if (result == 2)
        {
@@ -1523,10 +1521,69 @@ Datum contains(PG_FUNCTION_ARGS)
                PG_RETURN_NULL(); /* never get here */
        }
 
-#ifdef PROFILE
-       profstop(PROF_QRUN);
-       profreport("cont",geom1, geom2, NULL);
+       PG_FREE_IF_COPY(geom1, 0);
+       PG_FREE_IF_COPY(geom2, 1);
+
+       PG_RETURN_BOOL(result);
+
+}
+
+PG_FUNCTION_INFO_V1(containsproperly);
+Datum containsproperly(PG_FUNCTION_ARGS)
+{
+       PG_LWGEOM *                             geom1;
+       PG_LWGEOM *                             geom2;
+       bool                                    result;
+       BOX2DFLOAT4                     box1, box2;
+#ifdef PREPARED_GEOM
+       PrepGeomCache * prep_cache;
+#endif
+
+       geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       geom2 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       errorIfGeometryCollection(geom1,geom2);
+       errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(geom2));
+
+       /*
+       * short-circuit: if geom2 bounding box is not completely inside
+       * geom1 bounding box we can prematurely return FALSE.
+       * Do the test IFF BOUNDING BOX AVAILABLE.
+       */
+       if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
+            getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
+       {
+               if (( box2.xmin < box1.xmin ) || ( box2.xmax > box1.xmax ) ||
+                   ( box2.ymin < box1.ymin ) || ( box2.ymax > box1.ymax ))
+                       PG_RETURN_BOOL(FALSE);
+       }
+
+       initGEOS(lwnotice, lwnotice);
+
+#ifdef PREPARED_GEOM
+       prep_cache = GetPrepGeomCache( fcinfo, geom1, 0 );
+
+       if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
+       {
+               GEOSGeom g = POSTGIS2GEOS(geom2);
+               result = GEOSPreparedContainsProperly( prep_cache->prepared_geom, g);
+               GEOSGeom_destroy(g);
+       }
+       else
 #endif
+       {
+               GEOSGeom g1 = POSTGIS2GEOS(geom1);
+               GEOSGeom g2 = POSTGIS2GEOS(geom2);
+               result = GEOSRelatePattern( g1, g2, "T**FF*FF*" );
+               GEOSGeom_destroy(g1);
+               GEOSGeom_destroy(g2);
+       }
+
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS contains() threw an error!");
+               PG_RETURN_NULL(); /* never get here */
+       }
 
        PG_FREE_IF_COPY(geom1, 0);
        PG_FREE_IF_COPY(geom2, 1);
@@ -1534,7 +1591,6 @@ Datum contains(PG_FUNCTION_ARGS)
        PG_RETURN_BOOL(result);
 }
 
-
 /*
  * Described at
  * http://lin-ear-th-inking.blogspot.com/2007/06/subtleties-of-ogc-covers-spatial.html
@@ -1544,19 +1600,15 @@ Datum covers(PG_FUNCTION_ARGS)
 {
        PG_LWGEOM *geom1;
        PG_LWGEOM *geom2;
-       GEOSGeom g1,g2;
        bool result;
        BOX2DFLOAT4 box1, box2;
        int type1, type2;
        LWGEOM *lwgeom;
-       /* LWMPOLY *mpoly; */
        LWPOINT *point;
        RTREE_POLY_CACHE *poly_cache;
        MemoryContext old_context;
-       char *patt = "******FF*";
-
-#ifdef PROFILE
-       profstart(PROF_QRUN);
+#ifdef PREPARED_GEOM
+       PrepGeomCache *prep_cache;
 #endif
 
        geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
@@ -1571,7 +1623,7 @@ Datum covers(PG_FUNCTION_ARGS)
         * Do the test IFF BOUNDING BOX AVAILABLE.
         */
        if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
-                       getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
+            getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
        {
                if ( box2.xmin < box1.xmin ) PG_RETURN_BOOL(FALSE);
                if ( box2.xmax > box1.xmax ) PG_RETURN_BOOL(FALSE);
@@ -1641,31 +1693,24 @@ Datum covers(PG_FUNCTION_ARGS)
 
        initGEOS(lwnotice, lwnotice);
 
-#ifdef PROFILE
-       profstart(PROF_P2G1);
-#endif
-       g1 = POSTGIS2GEOS(geom1);
-#ifdef PROFILE
-       profstop(PROF_P2G1);
-#endif
-#ifdef PROFILE
-       profstart(PROF_P2G2);
-#endif
-       g2 = POSTGIS2GEOS(geom2);
-#ifdef PROFILE
-       profstop(PROF_P2G2);
-#endif
+       prep_cache = GetPrepGeomCache( fcinfo, geom1, 0 );
 
-#ifdef PROFILE
-       profstart(PROF_GRUN);
-#endif
-       result = GEOSRelatePattern(g1,g2,patt);
-#ifdef PROFILE
-       profstop(PROF_GRUN);
+#ifdef PREPARED_GEOM
+       if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
+       {
+               GEOSGeom g1 = POSTGIS2GEOS(geom2);
+               result = GEOSPreparedCovers( prep_cache->prepared_geom, g1);
+               GEOSGeom_destroy(g1);
+       }
+       else
 #endif
-
-       GEOSGeom_destroy(g1);
-       GEOSGeom_destroy(g2);
+       {
+               GEOSGeom g1 = POSTGIS2GEOS(geom1);
+               GEOSGeom g2 = POSTGIS2GEOS(geom2);
+               result = GEOSRelatePattern( g1, g2, "******FF*" );
+               GEOSGeom_destroy(g1);
+               GEOSGeom_destroy(g2);
+       }
 
        if (result == 2)
        {
@@ -1673,17 +1718,15 @@ Datum covers(PG_FUNCTION_ARGS)
                PG_RETURN_NULL(); /* never get here */
        }
 
-#ifdef PROFILE
-       profstop(PROF_QRUN);
-       profreport("geos",geom1, geom2, NULL);
-#endif
-
        PG_FREE_IF_COPY(geom1, 0);
        PG_FREE_IF_COPY(geom2, 1);
 
        PG_RETURN_BOOL(result);
+
 }
 
+
+
 PG_FUNCTION_INFO_V1(within);
 Datum within(PG_FUNCTION_ARGS)
 {
@@ -2053,7 +2096,6 @@ Datum intersects(PG_FUNCTION_ARGS)
        PG_LWGEOM *geom1;
        PG_LWGEOM *geom2;
        uchar *serialized_poly;
-       GEOSGeom g1,g2;
        bool result;
        BOX2DFLOAT4 box1, box2;
        int type1, type2, polytype;
@@ -2061,9 +2103,8 @@ Datum intersects(PG_FUNCTION_ARGS)
        LWGEOM *lwgeom;
        MemoryContext old_context;
        RTREE_POLY_CACHE *poly_cache;
-
-#ifdef PROFILE
-       profstart(PROF_QRUN);
+#ifdef PREPARED_GEOM
+       PrepGeomCache *prep_cache;
 #endif
 
        geom1 = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
@@ -2154,42 +2195,40 @@ Datum intersects(PG_FUNCTION_ARGS)
        }
 
        initGEOS(lwnotice, lwnotice);
+#ifdef PREPARED_GEOM
+       prep_cache = GetPrepGeomCache( fcinfo, geom1, geom2 );
 
-#ifdef PROFILE
-       profstart(PROF_P2G1);
-#endif
-       g1 = POSTGIS2GEOS(geom1 );
-#ifdef PROFILE
-       profstop(PROF_P2G1);
-#endif
-#ifdef PROFILE
-       profstart(PROF_P2G2);
-#endif
-       g2 = POSTGIS2GEOS(geom2 );
-#ifdef PROFILE
-       profstop(PROF_P2G2);
+       if ( prep_cache && prep_cache->prepared_geom )
+       {
+               if ( prep_cache->argnum == 1 )
+               {
+                       GEOSGeom g = POSTGIS2GEOS(geom2);
+                       result = GEOSPreparedIntersects( prep_cache->prepared_geom, g);
+                       GEOSGeom_destroy(g);
+               }
+               else
+               {
+                       GEOSGeom g = POSTGIS2GEOS(geom1);
+                       result = GEOSPreparedIntersects( prep_cache->prepared_geom, g);
+                       GEOSGeom_destroy(g);
+               }
+       }
+       else
 #endif
+       {
+               GEOSGeom g1 = POSTGIS2GEOS(geom1);
+               GEOSGeom g2 = POSTGIS2GEOS(geom2);
+               result = GEOSIntersects( g1, g2);
+               GEOSGeom_destroy(g1);
+               GEOSGeom_destroy(g2);
+       }
 
-#ifdef PROFILE
-       profstart(PROF_GRUN);
-#endif
-       result = GEOSIntersects(g1,g2);
-#ifdef PROFILE
-       profstop(PROF_GRUN);
-#endif
-       GEOSGeom_destroy(g1);
-       GEOSGeom_destroy(g2);
        if (result == 2)
        {
                elog(ERROR,"GEOS intersects() threw an error!");
                PG_RETURN_NULL(); /* never get here */
        }
 
-#ifdef PROFILE
-       profstop(PROF_QRUN);
-       profreport("intr",geom1, geom2, NULL);
-#endif
-
        PG_FREE_IF_COPY(geom1, 0);
        PG_FREE_IF_COPY(geom2, 1);
 
@@ -3517,734 +3556,3 @@ Datum LWGEOM_buildarea(PG_FUNCTION_ARGS)
 
 }
 
-/*********************************************************************************
-**
-**  PreparedGeometry implementations that cache intermediate indexed versions
-**  of geometry in a special MemoryContext for re-used by future function
-**  invocations.
-**
-**  By creating a memory context to hold the GEOS PreparedGeometry and Geometry
-**  and making it a child of the fmgr memory context, we can get the memory held
-**  by the GEOS objects released when the memory context delete callback is
-**  invoked by the parent context.
-**
-*********************************************************************************/
-
-#include "utils/memutils.h"
-#include "executor/spi.h"
-#include "access/hash.h"
-#include "utils/hsearch.h"
-
-/*
-** GEOS prepared geometry is only available from GEOS 3.1 onwards
-*/
-#if POSTGIS_GEOS_VERSION >= 31
-#define PREPARED_GEOM 1
-#warning COMPILING PREPARED GEOMETRY
-#endif
-
-/* Prepared geometry function prototypes */
-Datum containsPrepared(PG_FUNCTION_ARGS);
-Datum containsProperlyPrepared(PG_FUNCTION_ARGS);
-Datum coversPrepared(PG_FUNCTION_ARGS);
-Datum intersectsPrepared(PG_FUNCTION_ARGS);
-
-/* 
-** Cache structure. Keys are unique for a unique geometry, usually the row
-** primary key is passed in. This avoid the need to do a memcmp to test
-** for geometry equality at each function invocation. The argnum gives
-** the number of function arguments we are caching. Intersects requires that
-** both arguments be checked for cacheability, while Contains only requires
-** that the containing argument be checked. Both the Geometry and the 
-** PreparedGeometry have to be cached, because the PreparedGeometry
-** contains a reference to the geometry.
-*/
-#ifdef PREPARED_GEOM
-typedef struct
-{
-       PG_LWGEOM*                    pg_geom1;
-       PG_LWGEOM*                    pg_geom2;
-       size_t                        pg_geom1_size;
-       size_t                        pg_geom2_size;
-       int32                         argnum;
-       const GEOSPreparedGeometry*   prepared_geom;
-       const GEOSGeometry*           geom;
-       MemoryContext                 context;
-} PrepGeomCache;
-
-/*
-** Backend prepared hash table
-**
-** The memory context call-backs use a MemoryContext as the parameter
-** so we need to map that over to actual references to GEOS objects to 
-** delete.
-**
-** This hash table stores a key/value pair of MemoryContext/Geom* objects.
-*/
-static HTAB* PrepGeomHash = NULL;
-
-#define PREPARED_BACKEND_HASH_SIZE     32              
-
-typedef struct 
-{
-       MemoryContext context;
-       const GEOSPreparedGeometry* prepared_geom;
-       const GEOSGeometry* geom;
-} PrepGeomHashEntry;
-
-/* Utility function to pull or prepare the current cache */
-PrepGeomCache *GetPrepGeomCache(FunctionCallInfoData *fcinfo, PG_LWGEOM *pg_geom1, PG_LWGEOM *pg_geom2);
-
-/* Memory context hash table function prototypes */
-uint32 mcxt_ptr_hasha(const void *key, Size keysize);
-static void CreatePrepGeomHash(void);
-static void AddPrepGeomHashEntry(PrepGeomHashEntry pghe);
-static PrepGeomHashEntry *GetPrepGeomHashEntry(MemoryContext mcxt);
-static void DeletePrepGeomHashEntry(MemoryContext mcxt);
-
-
-/* Memory context cache function prototypes */
-static void PreparedCacheInit(MemoryContext context);
-static void PreparedCacheReset(MemoryContext context);
-static void PreparedCacheDelete(MemoryContext context);
-static bool PreparedCacheIsEmpty(MemoryContext context);
-static void PreparedCacheStats(MemoryContext context, int level);
-#ifdef MEMORY_CONTEXT_CHECKING
-static void PreparedCacheCheck(MemoryContext context);
-#endif
-
-/* Memory context definition must match the current version of PostgreSQL */
-static MemoryContextMethods PreparedCacheContextMethods = {
-       NULL,
-       NULL,
-       NULL,
-       PreparedCacheInit,
-       PreparedCacheReset,
-       PreparedCacheDelete,
-       NULL,
-       PreparedCacheIsEmpty,
-       PreparedCacheStats
-#ifdef MEMORY_CONTEXT_CHECKING
-       , PreparedCacheCheck
-#endif
-};
-
-static void
-PreparedCacheInit(MemoryContext context)
-{
-       /*
-        * Do nothing as the cache is initialised when the transform()
-        * function is first called
-        */
-}
-
-static void
-PreparedCacheDelete(MemoryContext context)
-{
-       PrepGeomHashEntry* pghe;
-
-       /* Lookup the hash entry pointer in the global hash table so we can free it */
-       pghe = GetPrepGeomHashEntry(context);
-
-       if (!pghe)
-               elog(ERROR, "PreparedCacheDelete: Trying to delete non-existant hash entry object with MemoryContext key (%p)", (void *)context);
-
-       POSTGIS_DEBUGF(3, "deleting geom object (%p) and prepared geom object (%p) with MemoryContext key (%p)", pghe->geom, pghe->prepared_geom, context);
-
-       /* Free them */
-       if( pghe->prepared_geom )
-               GEOSPreparedGeom_destroy( pghe->prepared_geom );
-       if( pghe->geom )
-               GEOSGeom_destroy( pghe->geom );
-
-       /* Remove the hash entry as it is no longer needed */
-       DeletePrepGeomHashEntry(context);
-}
-
-static void
-PreparedCacheReset(MemoryContext context)
-{
-       /*
-        * Do nothing, but we must supply a function since this call is mandatory according to tgl
-        * (see postgis-devel archives July 2007)
-        */
-}
-
-static bool
-PreparedCacheIsEmpty(MemoryContext context)
-{
-       /*
-        * Always return false since this call is mandatory according to tgl
-        * (see postgis-devel archives July 2007)
-        */
-       return FALSE;
-}
-
-static void
-PreparedCacheStats(MemoryContext context, int level)
-{
-       /*
-        * Simple stats display function - we must supply a function since this call is mandatory according to tgl
-        * (see postgis-devel archives July 2007)
-        */
-
-       fprintf(stderr, "%s: Prepared context\n", context->name);
-}
-
-#ifdef MEMORY_CONTEXT_CHECKING
-static void
-PreparedCacheCheck(MemoryContext context)
-{
-       /*
-        * Do nothing - stub required for when PostgreSQL is compiled
-        * with MEMORY_CONTEXT_CHECKING defined
-        */
-}
-#endif
-
-/* TODO: put this in common are for both transform and prepared
-** mcxt_ptr_hash
-** Build a key from a pointer and a size value.
-*/
-uint32 
-mcxt_ptr_hasha(const void *key, Size keysize)
-{
-       uint32 hashval;
-
-       hashval = DatumGetUInt32(hash_any(key, keysize));
-
-       return hashval;
-}
-
-static void
-CreatePrepGeomHash(void)
-{
-       HASHCTL ctl;
-
-       ctl.keysize = sizeof(MemoryContext);
-       ctl.entrysize = sizeof(PrepGeomHashEntry);
-       ctl.hash = mcxt_ptr_hasha;
-
-       PrepGeomHash = hash_create("PostGIS Prepared Geometry Backend MemoryContext Hash", PREPARED_BACKEND_HASH_SIZE, &ctl, (HASH_ELEM | HASH_FUNCTION));
-}
-
-static void 
-AddPrepGeomHashEntry(PrepGeomHashEntry pghe)
-{
-       bool found;
-       void **key;
-       PrepGeomHashEntry *he;
-
-       /* The hash key is the MemoryContext pointer */
-       key = (void *)&(pghe.context);
-       
-       he = (PrepGeomHashEntry *) hash_search(PrepGeomHash, key, HASH_ENTER, &found);
-       if (!found)
-       {
-               /* Insert the entry into the new hash element */
-               he->context = pghe.context;
-               he->geom = pghe.geom;
-               he->prepared_geom = pghe.prepared_geom;
-       }
-       else
-       {
-               elog(ERROR, "AddPrepGeomHashEntry: This memory context is already in use! (%p)", (void *)pghe.context);
-       }
-}
-
-static PrepGeomHashEntry*
-GetPrepGeomHashEntry(MemoryContext mcxt)
-{
-       void **key;
-       PrepGeomHashEntry *he;
-
-       /* The hash key is the MemoryContext pointer */
-       key = (void *)&mcxt;
-
-       /* Return the projection object from the hash */
-       he = (PrepGeomHashEntry *) hash_search(PrepGeomHash, key, HASH_FIND, NULL);
-
-       return he;
-}
-
-
-static void 
-DeletePrepGeomHashEntry(MemoryContext mcxt)
-{
-       void **key;
-       PrepGeomHashEntry *he;  
-
-       /* The hash key is the MemoryContext pointer */
-       key = (void *)&mcxt;
-
-       /* Delete the projection object from the hash */
-       he = (PrepGeomHashEntry *) hash_search(PrepGeomHash, key, HASH_REMOVE, NULL);
-
-       he->prepared_geom = NULL;
-       he->geom = NULL;
-
-       if (!he)
-               elog(ERROR, "DeletePrepGeomHashEntry: There was an error removing the geometry object from this MemoryContext (%p)", (void *)mcxt);
-}
-
-/*
-** GetPrepGeomCache
-**
-** Pull the current prepared geometry from the cache or make
-** one if there is not one available. Only prepare geometry
-** if we are seeing a key for the second time. That way rapidly 
-** cycling keys don't cause too much preparing.
-**
-*/
-PrepGeomCache* 
-GetPrepGeomCache(FunctionCallInfoData *fcinfo, PG_LWGEOM *pg_geom1, PG_LWGEOM *pg_geom2)
-{
-       MemoryContext old_context;
-       PrepGeomCache* cache = fcinfo->flinfo->fn_extra;
-       int copy_keys = 1;
-       size_t pg_geom1_size = 0;
-       size_t pg_geom2_size = 0;
-
-       if (!PrepGeomHash)
-               CreatePrepGeomHash();
-
-       if( pg_geom1 ) 
-               pg_geom1_size = VARSIZE(pg_geom1) + VARHDRSZ;
-
-       if( pg_geom2 ) 
-               pg_geom2_size = VARSIZE(pg_geom2) + VARHDRSZ;
-
-       if ( cache == NULL)
-       {
-               /*
-               ** Cache hit, but the cache isn't set up yet.
-               ** Set it up, but don't prepare the geometry yet.
-               */
-               PrepGeomHashEntry pghe;
-       
-               old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
-               cache = palloc(sizeof(PrepGeomCache));          
-               MemoryContextSwitchTo(old_context);
-       
-               cache->prepared_geom = 0;
-               cache->geom = 0;
-               cache->argnum = 0;
-               cache->pg_geom1 = 0;
-               cache->pg_geom2 = 0;
-               cache->pg_geom1_size = 0;
-               cache->pg_geom2_size = 0;
-               cache->context = MemoryContextCreate(T_AllocSetContext, 8192,
-                                &PreparedCacheContextMethods,
-                                fcinfo->flinfo->fn_mcxt,
-                                "PostGIS Prepared Geometry Context");
-
-               POSTGIS_DEBUGF(1, "GetPrepGeomCache: creating cache: %p", cache);
-
-               pghe.context = cache->context;
-               pghe.geom = 0;
-               pghe.prepared_geom = 0;
-               AddPrepGeomHashEntry( pghe );
-
-               fcinfo->flinfo->fn_extra = cache;
-
-               POSTGIS_DEBUGF(3, "GetPrepGeomCache: adding context to hash: %p", cache);
-       }
-       else if ( pg_geom1 &&
-                 cache->argnum != 2 &&
-                 cache->pg_geom1_size == pg_geom1_size && 
-                 memcmp(cache->pg_geom1, pg_geom1, pg_geom1_size) == 0)
-       {
-               if ( !cache->prepared_geom )
-               {
-                       /*
-                       ** Cache hit, but we haven't prepared our geometry yet.
-                       ** Prepare it.
-                       */
-                       PrepGeomHashEntry* pghe;
-               
-                       GEOSGeom g = POSTGIS2GEOS( pg_geom1 );
-                       cache->geom = g;
-                       cache->prepared_geom = GEOSPrepare( g );
-                       cache->argnum = 1;
-                       POSTGIS_DEBUG(1, "GetPrepGeomCache: preparing obj in argument 1");
-
-                       pghe = GetPrepGeomHashEntry(cache->context);
-                       pghe->geom = cache->geom;
-                       pghe->prepared_geom = cache->prepared_geom;
-                       POSTGIS_DEBUG(3, "GetPrepGeomCache: storing references to prepared obj in argument 1");
-               }
-               else
-               {       
-                       /*
-                       ** Cache hit, and we're good to go. Do nothing.
-                       */
-                       POSTGIS_DEBUG(1, "GetPrepGeomCache: cache hit, argument 1");
-               }
-               /* we don't need new keys until we have a cache miss */
-               copy_keys = 0;
-       }
-       else if ( pg_geom2 && 
-                 cache->argnum != 1 &&
-                 cache->pg_geom2_size == pg_geom2_size && 
-                 memcmp(cache->pg_geom2, pg_geom2, pg_geom2_size) == 0)
-       {
-                       if ( !cache->prepared_geom )
-                       {
-                               /*
-                               ** Cache hit on arg2, but we haven't prepared our geometry yet.
-                               ** Prepare it.
-                               */
-                               PrepGeomHashEntry* pghe;
-                               GEOSGeom g = POSTGIS2GEOS( pg_geom2 );
-                               cache->geom = g;
-                               cache->prepared_geom = GEOSPrepare( g );
-                               cache->argnum = 2;
-                               POSTGIS_DEBUG(1, "GetPrepGeomCache: preparing obj in argument 2");
-                       
-                               pghe = GetPrepGeomHashEntry(cache->context);
-                               pghe->geom = cache->geom;
-                               pghe->prepared_geom = cache->prepared_geom;
-                               POSTGIS_DEBUG(3, "GetPrepGeomCache: storing references to prepared obj in argument 2");
-                       }
-                       else 
-                       {
-                               /*
-                               ** Cache hit, and we're good to go. Do nothing.
-                               */
-                                       POSTGIS_DEBUG(1, "GetPrepGeomCache: cache hit, argument 2");
-                       }
-                       /* we don't need new keys until we have a cache miss */
-                       copy_keys = 0;
-       }
-       else if ( cache->prepared_geom )
-       {
-               /*
-               ** No cache hits, so this must be a miss.
-               ** Destroy the GEOS objects, empty the cache.
-               */
-               PrepGeomHashEntry* pghe;
-
-               pghe = GetPrepGeomHashEntry(cache->context);
-               pghe->geom = 0;
-               pghe->prepared_geom = 0;
-
-               POSTGIS_DEBUGF(1, "GetPrepGeomCache: cache miss, argument %d", cache->argnum);
-               GEOSPreparedGeom_destroy( cache->prepared_geom );
-               GEOSGeom_destroy( cache->geom );
-               
-               cache->prepared_geom = 0;
-               cache->geom = 0;
-               cache->argnum = 0;
-
-       }
-       
-       if( copy_keys && pg_geom1 ) 
-       {
-               POSTGIS_DEBUG(1, "GetPrepGeomCache: copying pg_geom1 into cache");
-               old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
-               if( cache->pg_geom1 ) 
-                       pfree(cache->pg_geom1);
-               cache->pg_geom1 = palloc(pg_geom1_size);
-               MemoryContextSwitchTo(old_context);
-               memcpy(cache->pg_geom1, pg_geom1, pg_geom1_size);
-               cache->pg_geom1_size = pg_geom1_size;
-       }
-       if( copy_keys && pg_geom2 )
-       { 
-               POSTGIS_DEBUG(1, "GetPrepGeomCache: copying pg_geom2 into cache");
-               old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
-               if( cache->pg_geom2 ) 
-                       pfree(cache->pg_geom2);
-               cache->pg_geom2 = palloc(pg_geom2_size);
-               MemoryContextSwitchTo(old_context);
-               memcpy(cache->pg_geom2, pg_geom2, pg_geom2_size);
-               cache->pg_geom2_size = pg_geom2_size;
-       }
-
-       return cache;
-       
-       /* select sum(v.gid) from vada2005 v, ed2000 e where st_contains(e.the_geom, v.the_geom, e.gid) and e.id like 'PR%' */
-
-}
-#endif /* PREPARED_GEOM */
-
-
-PG_FUNCTION_INFO_V1(containsPrepared);
-Datum containsPrepared(PG_FUNCTION_ARGS)
-{
-#ifndef PREPARED_GEOM
-       elog(ERROR,"Not implemented in this version!");
-       PG_RETURN_NULL(); 
-#else
-       PG_LWGEOM *              geom1;
-       PG_LWGEOM *              geom2;
-       bool                     result;
-       BOX2DFLOAT4              box1, box2;
-       PrepGeomCache *          prep_cache;
-       int32                    key1;
-
-       geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
-       geom2 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
-       key1 = PG_GETARG_INT32(2);
-
-       errorIfGeometryCollection(geom1,geom2);
-       errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(geom2));
-
-       POSTGIS_DEBUG(3, "containsPrepared: entered function");
-
-       /*
-       * short-circuit: if geom2 bounding box is not completely inside
-       * geom1 bounding box we can prematurely return FALSE.
-       * Do the test IFF BOUNDING BOX AVAILABLE.
-       */
-       if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
-            getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
-       {
-               if (( box2.xmin < box1.xmin ) || ( box2.xmax > box1.xmax ) ||
-                   ( box2.ymin < box1.ymin ) || ( box2.ymax > box1.ymax ))
-                       PG_RETURN_BOOL(FALSE);
-       }
-
-       prep_cache = GetPrepGeomCache( fcinfo, geom1, 0 );
-
-       initGEOS(lwnotice, lwnotice);
-
-       if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
-       {
-               GEOSGeom g = POSTGIS2GEOS(geom2);
-               POSTGIS_DEBUG(4, "containsPrepared: cache is live, running preparedcontains");
-               result = GEOSPreparedContains( prep_cache->prepared_geom, g);
-               GEOSGeom_destroy(g);
-       }
-       else
-       {
-               GEOSGeom g1 = POSTGIS2GEOS(geom1);
-               GEOSGeom g2 = POSTGIS2GEOS(geom2);
-               POSTGIS_DEBUG(4, "containsPrepared: cache is not ready, running standard contains");
-               result = GEOSContains( g1, g2);
-               GEOSGeom_destroy(g1);
-               GEOSGeom_destroy(g2);
-       }
-
-       if (result == 2)
-       {
-               elog(ERROR,"GEOS contains() threw an error!");
-               PG_RETURN_NULL(); /* never get here */
-       }
-
-       PG_FREE_IF_COPY(geom1, 0);
-       PG_FREE_IF_COPY(geom2, 1);
-
-       PG_RETURN_BOOL(result);
-#endif /* PREPARED_GEOM */
-}
-
-PG_FUNCTION_INFO_V1(containsProperlyPrepared);
-Datum containsProperlyPrepared(PG_FUNCTION_ARGS)
-{
-#ifndef PREPARED_GEOM
-       elog(ERROR,"Not implemented in this version!");
-       PG_RETURN_NULL(); /* never get here */
-#else
-       PG_LWGEOM *                             geom1;
-       PG_LWGEOM *                             geom2;
-       bool                                    result;
-       BOX2DFLOAT4                     box1, box2;
-       PrepGeomCache * prep_cache;
-       int32                                   key1;
-
-       geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
-       geom2 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
-       key1 = PG_GETARG_INT32(2);
-
-       errorIfGeometryCollection(geom1,geom2);
-       errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(geom2));
-
-       /*
-       * short-circuit: if geom2 bounding box is not completely inside
-       * geom1 bounding box we can prematurely return FALSE.
-       * Do the test IFF BOUNDING BOX AVAILABLE.
-       */
-       if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
-                       getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
-       {
-               if (( box2.xmin < box1.xmin ) || ( box2.xmax > box1.xmax ) ||
-                   ( box2.ymin < box1.ymin ) || ( box2.ymax > box1.ymax ))
-                       PG_RETURN_BOOL(FALSE);
-       }
-
-       prep_cache = GetPrepGeomCache( fcinfo, geom1, 0 );
-
-       initGEOS(lwnotice, lwnotice);
-
-       if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
-       {
-               GEOSGeom g = POSTGIS2GEOS(geom2);
-               result = GEOSPreparedContainsProperly( prep_cache->prepared_geom, g);
-               GEOSGeom_destroy(g);
-       }
-       else
-       {
-               GEOSGeom g1 = POSTGIS2GEOS(geom1);
-               GEOSGeom g2 = POSTGIS2GEOS(geom2);
-               result = GEOSRelatePattern( g1, g2, "T**FF*FF*" );
-               GEOSGeom_destroy(g1);
-               GEOSGeom_destroy(g2);
-       }
-
-       if (result == 2)
-       {
-               elog(ERROR,"GEOS contains() threw an error!");
-               PG_RETURN_NULL(); /* never get here */
-       }
-
-       PG_FREE_IF_COPY(geom1, 0);
-       PG_FREE_IF_COPY(geom2, 1);
-
-       PG_RETURN_BOOL(result);
-#endif /* PREPARED_GEOM */
-}
-
-PG_FUNCTION_INFO_V1(coversPrepared);
-Datum coversPrepared(PG_FUNCTION_ARGS)
-{
-#ifndef PREPARED_GEOM
-       elog(ERROR,"Not implemented in this version!");
-       PG_RETURN_NULL(); /* never get here */
-#else
-       PG_LWGEOM *                             geom1;
-       PG_LWGEOM *                             geom2;
-       bool                                    result;
-       BOX2DFLOAT4                     box1, box2;
-       PrepGeomCache * prep_cache;
-       int32                                   key1;
-
-       geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
-       geom2 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
-       key1 = PG_GETARG_INT32(2);
-
-       errorIfGeometryCollection(geom1,geom2);
-       errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(geom2));
-
-       /*
-       * short-circuit: if geom2 bounding box is not completely inside
-       * geom1 bounding box we can prematurely return FALSE.
-       * Do the test IFF BOUNDING BOX AVAILABLE.
-       */
-       if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
-                       getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
-       {
-               if (( box2.xmin < box1.xmin ) || ( box2.xmax > box1.xmax ) ||
-                   ( box2.ymin < box1.ymin ) || ( box2.ymax > box1.ymax ))
-                       PG_RETURN_BOOL(FALSE);
-       }
-
-       prep_cache = GetPrepGeomCache( fcinfo, geom1, 0 );
-
-       initGEOS(lwnotice, lwnotice);
-
-       if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
-       {
-               GEOSGeom g = POSTGIS2GEOS(geom2);
-               result = GEOSPreparedCovers( prep_cache->prepared_geom, g);
-               GEOSGeom_destroy(g);
-       }
-       else
-       {
-               GEOSGeom g1 = POSTGIS2GEOS(geom1);
-               GEOSGeom g2 = POSTGIS2GEOS(geom2);
-               result = GEOSRelatePattern( g1, g2, "******FF*" );
-               GEOSGeom_destroy(g1);
-               GEOSGeom_destroy(g2);
-       }
-
-       if (result == 2)
-       {
-               elog(ERROR,"GEOS contains() threw an error!");
-               PG_RETURN_NULL(); /* never get here */
-       }
-
-       PG_FREE_IF_COPY(geom1, 0);
-       PG_FREE_IF_COPY(geom2, 1);
-
-       PG_RETURN_BOOL(result);
-#endif /* PREPARED_GEOM */
-}
-
-
-PG_FUNCTION_INFO_V1(intersectsPrepared);
-Datum intersectsPrepared(PG_FUNCTION_ARGS)
-{
-#ifndef PREPARED_GEOM
-       elog(ERROR,"Not implemented in this version!");
-       PG_RETURN_NULL(); /* never get here */
-#else
-       PG_LWGEOM *                             geom1;
-       PG_LWGEOM *                             geom2;
-       bool                                    result;
-       BOX2DFLOAT4                     box1, box2;
-       PrepGeomCache * prep_cache;
-       int32                                   key1, key2;
-
-       geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
-       geom2 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
-       key1 = PG_GETARG_INT32(2);
-       key2 = PG_GETARG_INT32(3);
-
-       errorIfGeometryCollection(geom1,geom2);
-       errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(geom2));
-
-       /*
-       * short-circuit 1: if geom2 bounding box does not overlap
-       * geom1 bounding box we can prematurely return FALSE.
-       * Do the test IFF BOUNDING BOX AVAILABLE.
-       */
-       if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
-                       getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
-       {
-               if (( box2.xmax < box1.xmin ) || ( box2.xmin > box1.xmax ) ||
-                   ( box2.ymax < box1.ymin ) || ( box2.ymin > box1.ymax ))
-                       PG_RETURN_BOOL(FALSE);
-       }
-
-       prep_cache = GetPrepGeomCache( fcinfo, geom1, geom2 );
-
-       initGEOS(lwnotice, lwnotice);
-
-       if ( prep_cache && prep_cache->prepared_geom )
-       {
-               if ( prep_cache->argnum == 1 )
-               {
-                       GEOSGeom g = POSTGIS2GEOS(geom2);
-                       result = GEOSPreparedIntersects( prep_cache->prepared_geom, g);
-                       GEOSGeom_destroy(g);
-               }
-               else
-               {
-                       GEOSGeom g = POSTGIS2GEOS(geom1);
-                       result = GEOSPreparedIntersects( prep_cache->prepared_geom, g);
-                       GEOSGeom_destroy(g);
-               }
-       }
-       else
-       {
-               GEOSGeom g1 = POSTGIS2GEOS(geom1);
-               GEOSGeom g2 = POSTGIS2GEOS(geom2);
-               result = GEOSIntersects( g1, g2);
-               GEOSGeom_destroy(g1);
-               GEOSGeom_destroy(g2);
-       }
-
-       if (result == 2)
-       {
-               elog(ERROR,"GEOS contains() threw an error!");
-               PG_RETURN_NULL(); /* never get here */
-       }
-
-       PG_FREE_IF_COPY(geom1, 0);
-       PG_FREE_IF_COPY(geom2, 1);
-
-       PG_RETURN_BOOL(result);
-#endif /* PREPARED_GEOM */
-}
-
diff --git a/lwgeom/lwgeom_geos_prepared.c b/lwgeom/lwgeom_geos_prepared.c
new file mode 100644 (file)
index 0000000..5d221e8
--- /dev/null
@@ -0,0 +1,679 @@
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2007 Refractions Research Inc.
+ * Copyright 2008 Paul Ramsey <pramsey@cleverelephant.ca>
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ * 
+ **********************************************************************/
+
+#include "lwgeom_geos_prepared.h"
+
+/* 
+** Prepared geometry function prototypes 
+*/
+Datum containsPrepared(PG_FUNCTION_ARGS);
+Datum coversPrepared(PG_FUNCTION_ARGS);
+Datum intersectsPrepared(PG_FUNCTION_ARGS);
+
+/**********************************************************************
+** Internal prototype and structure definitions for the 
+** prepared geometry code.
+**
+** Working parts:
+**
+** PrepGeomCache, the actual struct that holds the keys we compare
+** to determine if our cache is stale, and references to the GEOS
+** objects used in computations.
+**
+** PrepGeomHash, a global hash table that uses a MemoryContext as
+** key and returns a structure holding references to the GEOS
+** objects used in computations.
+**
+** PreparedCacheContextMethods, a set of callback functions that
+** get hooked into a MemoryContext that is in turn used as a 
+** key in the PrepGeomHash.
+**
+** All this is to allow us to clean up external malloc'ed objects
+** (the GEOS Geometry and PreparedGeometry) before the structure
+** that references them (PrepGeomCache) is pfree'd by PgSQL. The
+** methods in the PreparedCacheContext are called just before the
+** function context is freed, allowing us to look up the references
+** in the PrepGeomHash and free them before the function context
+** is freed.
+**/
+
+#ifdef PREPARED_GEOM
+
+#warning COMPILING PREPARED GEOMETRY
+
+/*
+** Backend prepared hash table
+**
+** The memory context call-backs use a MemoryContext as the parameter
+** so we need to map that over to actual references to GEOS objects to 
+** delete.
+**
+** This hash table stores a key/value pair of MemoryContext/Geom* objects.
+*/
+static HTAB* PrepGeomHash = NULL;
+
+#define PREPARED_BACKEND_HASH_SIZE     32              
+
+typedef struct 
+{
+       MemoryContext context;
+       const GEOSPreparedGeometry* prepared_geom;
+       const GEOSGeometry* geom;
+} PrepGeomHashEntry;
+
+/* Memory context hash table function prototypes */
+uint32 mcxt_ptr_hasha(const void *key, Size keysize);
+static void CreatePrepGeomHash(void);
+static void AddPrepGeomHashEntry(PrepGeomHashEntry pghe);
+static PrepGeomHashEntry *GetPrepGeomHashEntry(MemoryContext mcxt);
+static void DeletePrepGeomHashEntry(MemoryContext mcxt);
+
+/* Memory context cache function prototypes */
+static void PreparedCacheInit(MemoryContext context);
+static void PreparedCacheReset(MemoryContext context);
+static void PreparedCacheDelete(MemoryContext context);
+static bool PreparedCacheIsEmpty(MemoryContext context);
+static void PreparedCacheStats(MemoryContext context, int level);
+#ifdef MEMORY_CONTEXT_CHECKING
+static void PreparedCacheCheck(MemoryContext context);
+#endif
+
+/* Memory context definition must match the current version of PostgreSQL */
+static MemoryContextMethods PreparedCacheContextMethods = {
+       NULL,
+       NULL,
+       NULL,
+       PreparedCacheInit,
+       PreparedCacheReset,
+       PreparedCacheDelete,
+       NULL,
+       PreparedCacheIsEmpty,
+       PreparedCacheStats
+#ifdef MEMORY_CONTEXT_CHECKING
+       , PreparedCacheCheck
+#endif
+};
+
+static void
+PreparedCacheInit(MemoryContext context)
+{
+       /*
+        * Do nothing as the cache is initialised when the transform()
+        * function is first called
+        */
+}
+
+static void
+PreparedCacheDelete(MemoryContext context)
+{
+       PrepGeomHashEntry* pghe;
+
+       /* Lookup the hash entry pointer in the global hash table so we can free it */
+       pghe = GetPrepGeomHashEntry(context);
+
+       if (!pghe)
+               elog(ERROR, "PreparedCacheDelete: Trying to delete non-existant hash entry object with MemoryContext key (%p)", (void *)context);
+
+       POSTGIS_DEBUGF(3, "deleting geom object (%p) and prepared geom object (%p) with MemoryContext key (%p)", pghe->geom, pghe->prepared_geom, context);
+
+       /* Free them */
+       if( pghe->prepared_geom )
+               GEOSPreparedGeom_destroy( pghe->prepared_geom );
+       if( pghe->geom )
+               GEOSGeom_destroy( pghe->geom );
+
+       /* Remove the hash entry as it is no longer needed */
+       DeletePrepGeomHashEntry(context);
+}
+
+static void
+PreparedCacheReset(MemoryContext context)
+{
+       /*
+        * Do nothing, but we must supply a function since this call is mandatory according to tgl
+        * (see postgis-devel archives July 2007)
+        */
+}
+
+static bool
+PreparedCacheIsEmpty(MemoryContext context)
+{
+       /*
+        * Always return false since this call is mandatory according to tgl
+        * (see postgis-devel archives July 2007)
+        */
+       return FALSE;
+}
+
+static void
+PreparedCacheStats(MemoryContext context, int level)
+{
+       /*
+        * Simple stats display function - we must supply a function since this call is mandatory according to tgl
+        * (see postgis-devel archives July 2007)
+        */
+
+       fprintf(stderr, "%s: Prepared context\n", context->name);
+}
+
+#ifdef MEMORY_CONTEXT_CHECKING
+static void
+PreparedCacheCheck(MemoryContext context)
+{
+       /*
+        * Do nothing - stub required for when PostgreSQL is compiled
+        * with MEMORY_CONTEXT_CHECKING defined
+        */
+}
+#endif
+
+/* TODO: put this in common are for both transform and prepared
+** mcxt_ptr_hash
+** Build a key from a pointer and a size value.
+*/
+uint32 
+mcxt_ptr_hasha(const void *key, Size keysize)
+{
+       uint32 hashval;
+
+       hashval = DatumGetUInt32(hash_any(key, keysize));
+
+       return hashval;
+}
+
+static void
+CreatePrepGeomHash(void)
+{
+       HASHCTL ctl;
+
+       ctl.keysize = sizeof(MemoryContext);
+       ctl.entrysize = sizeof(PrepGeomHashEntry);
+       ctl.hash = mcxt_ptr_hasha;
+
+       PrepGeomHash = hash_create("PostGIS Prepared Geometry Backend MemoryContext Hash", PREPARED_BACKEND_HASH_SIZE, &ctl, (HASH_ELEM | HASH_FUNCTION));
+}
+
+static void 
+AddPrepGeomHashEntry(PrepGeomHashEntry pghe)
+{
+       bool found;
+       void **key;
+       PrepGeomHashEntry *he;
+
+       /* The hash key is the MemoryContext pointer */
+       key = (void *)&(pghe.context);
+       
+       he = (PrepGeomHashEntry *) hash_search(PrepGeomHash, key, HASH_ENTER, &found);
+       if (!found)
+       {
+               /* Insert the entry into the new hash element */
+               he->context = pghe.context;
+               he->geom = pghe.geom;
+               he->prepared_geom = pghe.prepared_geom;
+       }
+       else
+       {
+               elog(ERROR, "AddPrepGeomHashEntry: This memory context is already in use! (%p)", (void *)pghe.context);
+       }
+}
+
+static PrepGeomHashEntry*
+GetPrepGeomHashEntry(MemoryContext mcxt)
+{
+       void **key;
+       PrepGeomHashEntry *he;
+
+       /* The hash key is the MemoryContext pointer */
+       key = (void *)&mcxt;
+
+       /* Return the projection object from the hash */
+       he = (PrepGeomHashEntry *) hash_search(PrepGeomHash, key, HASH_FIND, NULL);
+
+       return he;
+}
+
+
+static void 
+DeletePrepGeomHashEntry(MemoryContext mcxt)
+{
+       void **key;
+       PrepGeomHashEntry *he;  
+
+       /* The hash key is the MemoryContext pointer */
+       key = (void *)&mcxt;
+
+       /* Delete the projection object from the hash */
+       he = (PrepGeomHashEntry *) hash_search(PrepGeomHash, key, HASH_REMOVE, NULL);
+
+       he->prepared_geom = NULL;
+       he->geom = NULL;
+
+       if (!he)
+               elog(ERROR, "DeletePrepGeomHashEntry: There was an error removing the geometry object from this MemoryContext (%p)", (void *)mcxt);
+}
+
+/*
+** GetPrepGeomCache
+**
+** Pull the current prepared geometry from the cache or make
+** one if there is not one available. Only prepare geometry
+** if we are seeing a key for the second time. That way rapidly 
+** cycling keys don't cause too much preparing.
+*/
+PrepGeomCache* 
+GetPrepGeomCache(FunctionCallInfoData *fcinfo, PG_LWGEOM *pg_geom1, PG_LWGEOM *pg_geom2)
+{
+       MemoryContext old_context;
+       PrepGeomCache* cache = fcinfo->flinfo->fn_extra;
+       int copy_keys = 1;
+       size_t pg_geom1_size = 0;
+       size_t pg_geom2_size = 0;
+
+       if (!PrepGeomHash)
+               CreatePrepGeomHash();
+
+       if( pg_geom1 ) 
+               pg_geom1_size = VARSIZE(pg_geom1) + VARHDRSZ;
+
+       if( pg_geom2 ) 
+               pg_geom2_size = VARSIZE(pg_geom2) + VARHDRSZ;
+
+       if ( cache == NULL)
+       {
+               /*
+               ** Cache requested, but the cache isn't set up yet.
+               ** Set it up, but don't prepare the geometry yet.
+               ** That way if the next call is a cache miss we haven't
+               ** wasted time preparing a geometry we don't need.
+               */
+               PrepGeomHashEntry pghe;
+       
+               old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
+               cache = palloc(sizeof(PrepGeomCache));          
+               MemoryContextSwitchTo(old_context);
+       
+               cache->prepared_geom = 0;
+               cache->geom = 0;
+               cache->argnum = 0;
+               cache->pg_geom1 = 0;
+               cache->pg_geom2 = 0;
+               cache->pg_geom1_size = 0;
+               cache->pg_geom2_size = 0;
+               cache->context = MemoryContextCreate(T_AllocSetContext, 8192,
+                                &PreparedCacheContextMethods,
+                                fcinfo->flinfo->fn_mcxt,
+                                "PostGIS Prepared Geometry Context");
+
+               POSTGIS_DEBUGF(1, "GetPrepGeomCache: creating cache: %p", cache);
+
+               pghe.context = cache->context;
+               pghe.geom = 0;
+               pghe.prepared_geom = 0;
+               AddPrepGeomHashEntry( pghe );
+
+               fcinfo->flinfo->fn_extra = cache;
+
+               POSTGIS_DEBUGF(3, "GetPrepGeomCache: adding context to hash: %p", cache);
+       }
+       else if ( pg_geom1 &&
+                 cache->argnum != 2 &&
+                 cache->pg_geom1_size == pg_geom1_size && 
+                 memcmp(cache->pg_geom1, pg_geom1, pg_geom1_size) == 0)
+       {
+               if ( !cache->prepared_geom )
+               {
+                       /*
+                       ** Cache hit, but we haven't prepared our geometry yet.
+                       ** Prepare it.
+                       */
+                       PrepGeomHashEntry* pghe;
+               
+                       cache->geom = POSTGIS2GEOS( pg_geom1 );
+                       cache->prepared_geom = GEOSPrepare( cache->geom );
+                       cache->argnum = 1;
+                       POSTGIS_DEBUG(1, "GetPrepGeomCache: preparing obj in argument 1");
+
+                       pghe = GetPrepGeomHashEntry(cache->context);
+                       pghe->geom = cache->geom;
+                       pghe->prepared_geom = cache->prepared_geom;
+                       POSTGIS_DEBUG(3, "GetPrepGeomCache: storing references to prepared obj in argument 1");
+               }
+               else
+               {       
+                       /*
+                       ** Cache hit, and we're good to go. Do nothing.
+                       */
+                       POSTGIS_DEBUG(1, "GetPrepGeomCache: cache hit, argument 1");
+               }
+               /* We don't need new keys until we have a cache miss */
+               copy_keys = 0;
+       }
+       else if ( pg_geom2 && 
+                 cache->argnum != 1 &&
+                 cache->pg_geom2_size == pg_geom2_size && 
+                 memcmp(cache->pg_geom2, pg_geom2, pg_geom2_size) == 0)
+       {
+                       if ( !cache->prepared_geom )
+                       {
+                               /*
+                               ** Cache hit on arg2, but we haven't prepared our geometry yet.
+                               ** Prepare it.
+                               */
+                               PrepGeomHashEntry* pghe;
+                               
+                               cache->geom = POSTGIS2GEOS( pg_geom2 );
+                               cache->prepared_geom = GEOSPrepare( cache->geom );
+                               cache->argnum = 2;
+                               POSTGIS_DEBUG(1, "GetPrepGeomCache: preparing obj in argument 2");
+                       
+                               pghe = GetPrepGeomHashEntry(cache->context);
+                               pghe->geom = cache->geom;
+                               pghe->prepared_geom = cache->prepared_geom;
+                               POSTGIS_DEBUG(3, "GetPrepGeomCache: storing references to prepared obj in argument 2");
+                       }
+                       else 
+                       {
+                               /*
+                               ** Cache hit, and we're good to go. Do nothing.
+                               */
+                               POSTGIS_DEBUG(1, "GetPrepGeomCache: cache hit, argument 2");
+                       }
+                       /* We don't need new keys until we have a cache miss */
+                       copy_keys = 0;
+       }
+       else if ( cache->prepared_geom )
+       {
+               /*
+               ** No cache hits, so this must be a miss.
+               ** Destroy the GEOS objects, empty the cache.
+               */
+               PrepGeomHashEntry* pghe;
+
+               pghe = GetPrepGeomHashEntry(cache->context);
+               pghe->geom = 0;
+               pghe->prepared_geom = 0;
+
+               POSTGIS_DEBUGF(1, "GetPrepGeomCache: cache miss, argument %d", cache->argnum);
+               GEOSPreparedGeom_destroy( cache->prepared_geom );
+               GEOSGeom_destroy( cache->geom );
+               
+               cache->prepared_geom = 0;
+               cache->geom = 0;
+               cache->argnum = 0;
+
+       }
+       
+       if( copy_keys && pg_geom1 ) 
+       {
+               /*
+               ** If this is a new key (cache miss) we flip into the function
+               ** manager memory context and make a copy. We can't just store a pointer
+               ** because this copy will be pfree'd at the end of this function
+               ** call.
+               */
+               POSTGIS_DEBUG(1, "GetPrepGeomCache: copying pg_geom1 into cache");
+               old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
+               if( cache->pg_geom1 ) 
+                       pfree(cache->pg_geom1);
+               cache->pg_geom1 = palloc(pg_geom1_size);
+               MemoryContextSwitchTo(old_context);
+               memcpy(cache->pg_geom1, pg_geom1, pg_geom1_size);
+               cache->pg_geom1_size = pg_geom1_size;
+       }
+       if( copy_keys && pg_geom2 )
+       { 
+               POSTGIS_DEBUG(1, "GetPrepGeomCache: copying pg_geom2 into cache");
+               old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
+               if( cache->pg_geom2 ) 
+                       pfree(cache->pg_geom2);
+               cache->pg_geom2 = palloc(pg_geom2_size);
+               MemoryContextSwitchTo(old_context);
+               memcpy(cache->pg_geom2, pg_geom2, pg_geom2_size);
+               cache->pg_geom2_size = pg_geom2_size;
+       }
+
+       return cache;
+       
+}
+#endif /* PREPARED_GEOM */
+
+
+
+#if 0
+/*
+** No longer directly called. Left as example for now.
+** Functionality directly embedded in contains()
+*/
+PG_FUNCTION_INFO_V1(containsPrepared);
+Datum containsPrepared(PG_FUNCTION_ARGS)
+{
+#ifndef PREPARED_GEOM
+       elog(ERROR,"Not implemented in this version!");
+       PG_RETURN_NULL(); 
+#else
+       PG_LWGEOM *              geom1;
+       PG_LWGEOM *              geom2;
+       bool                     result;
+       BOX2DFLOAT4              box1, box2;
+       PrepGeomCache *          prep_cache;
+       int32                    key1;
+
+       geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       geom2 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+       key1 = PG_GETARG_INT32(2);
+
+       errorIfGeometryCollection(geom1,geom2);
+       errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(geom2));
+
+       POSTGIS_DEBUG(3, "containsPrepared: entered function");
+
+       /*
+       * short-circuit: if geom2 bounding box is not completely inside
+       * geom1 bounding box we can prematurely return FALSE.
+       * Do the test IFF BOUNDING BOX AVAILABLE.
+       */
+       if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
+            getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
+       {
+               if (( box2.xmin < box1.xmin ) || ( box2.xmax > box1.xmax ) ||
+                   ( box2.ymin < box1.ymin ) || ( box2.ymax > box1.ymax ))
+                       PG_RETURN_BOOL(FALSE);
+       }
+
+       prep_cache = GetPrepGeomCache( fcinfo, geom1, 0 );
+
+       initGEOS(lwnotice, lwnotice);
+
+       if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
+       {
+               GEOSGeom g = POSTGIS2GEOS(geom2);
+               POSTGIS_DEBUG(4, "containsPrepared: cache is live, running preparedcontains");
+               result = GEOSPreparedContains( prep_cache->prepared_geom, g);
+               GEOSGeom_destroy(g);
+       }
+       else
+       {
+               GEOSGeom g1 = POSTGIS2GEOS(geom1);
+               GEOSGeom g2 = POSTGIS2GEOS(geom2);
+               POSTGIS_DEBUG(4, "containsPrepared: cache is not ready, running standard contains");
+               result = GEOSContains( g1, g2);
+               GEOSGeom_destroy(g1);
+               GEOSGeom_destroy(g2);
+       }
+
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS contains() threw an error!");
+               PG_RETURN_NULL(); /* never get here */
+       }
+
+       PG_FREE_IF_COPY(geom1, 0);
+       PG_FREE_IF_COPY(geom2, 1);
+
+       PG_RETURN_BOOL(result);
+#endif /* PREPARED_GEOM */
+}
+
+
+/*
+** No longer directly called. Left as example for now.
+** Functionality directly embedded in covers()
+*/
+PG_FUNCTION_INFO_V1(coversPrepared);
+Datum coversPrepared(PG_FUNCTION_ARGS)
+{
+#ifndef PREPARED_GEOM
+       elog(ERROR,"Not implemented in this version!");
+       PG_RETURN_NULL(); /* never get here */
+#else
+       PG_LWGEOM *                             geom1;
+       PG_LWGEOM *                             geom2;
+       bool                                    result;
+       BOX2DFLOAT4                     box1, box2;
+       PrepGeomCache * prep_cache;
+       int32                                   key1;
+
+       geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       geom2 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+       key1 = PG_GETARG_INT32(2);
+
+       errorIfGeometryCollection(geom1,geom2);
+       errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(geom2));
+
+       /*
+       * short-circuit: if geom2 bounding box is not completely inside
+       * geom1 bounding box we can prematurely return FALSE.
+       * Do the test IFF BOUNDING BOX AVAILABLE.
+       */
+       if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
+                       getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
+       {
+               if (( box2.xmin < box1.xmin ) || ( box2.xmax > box1.xmax ) ||
+                   ( box2.ymin < box1.ymin ) || ( box2.ymax > box1.ymax ))
+                       PG_RETURN_BOOL(FALSE);
+       }
+
+       prep_cache = GetPrepGeomCache( fcinfo, geom1, 0 );
+
+       initGEOS(lwnotice, lwnotice);
+
+       if ( prep_cache && prep_cache->prepared_geom && prep_cache->argnum == 1 )
+       {
+               GEOSGeom g = POSTGIS2GEOS(geom2);
+               result = GEOSPreparedCovers( prep_cache->prepared_geom, g);
+               GEOSGeom_destroy(g);
+       }
+       else
+       {
+               GEOSGeom g1 = POSTGIS2GEOS(geom1);
+               GEOSGeom g2 = POSTGIS2GEOS(geom2);
+               result = GEOSRelatePattern( g1, g2, "******FF*" );
+               GEOSGeom_destroy(g1);
+               GEOSGeom_destroy(g2);
+       }
+
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS contains() threw an error!");
+               PG_RETURN_NULL(); /* never get here */
+       }
+
+       PG_FREE_IF_COPY(geom1, 0);
+       PG_FREE_IF_COPY(geom2, 1);
+
+       PG_RETURN_BOOL(result);
+#endif /* PREPARED_GEOM */
+}
+
+
+/*
+** No longer directly called. Left as example for now.
+** Functionality directly embedded in intersects()
+*/
+PG_FUNCTION_INFO_V1(intersectsPrepared);
+Datum intersectsPrepared(PG_FUNCTION_ARGS)
+{
+#ifndef PREPARED_GEOM
+       elog(ERROR,"Not implemented in this version!");
+       PG_RETURN_NULL(); /* never get here */
+#else
+       PG_LWGEOM *                             geom1;
+       PG_LWGEOM *                             geom2;
+       bool                                    result;
+       BOX2DFLOAT4                     box1, box2;
+       PrepGeomCache * prep_cache;
+       int32                                   key1, key2;
+
+       geom1 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       geom2 = (PG_LWGEOM *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+       key1 = PG_GETARG_INT32(2);
+       key2 = PG_GETARG_INT32(3);
+
+       errorIfGeometryCollection(geom1,geom2);
+       errorIfSRIDMismatch(pglwgeom_getSRID(geom1), pglwgeom_getSRID(geom2));
+
+       /*
+       * short-circuit 1: if geom2 bounding box does not overlap
+       * geom1 bounding box we can prematurely return FALSE.
+       * Do the test IFF BOUNDING BOX AVAILABLE.
+       */
+       if ( getbox2d_p(SERIALIZED_FORM(geom1), &box1) &&
+                       getbox2d_p(SERIALIZED_FORM(geom2), &box2) )
+       {
+               if (( box2.xmax < box1.xmin ) || ( box2.xmin > box1.xmax ) ||
+                   ( box2.ymax < box1.ymin ) || ( box2.ymin > box1.ymax ))
+                       PG_RETURN_BOOL(FALSE);
+       }
+
+       prep_cache = GetPrepGeomCache( fcinfo, geom1, geom2 );
+
+       initGEOS(lwnotice, lwnotice);
+
+       if ( prep_cache && prep_cache->prepared_geom )
+       {
+               if ( prep_cache->argnum == 1 )
+               {
+                       GEOSGeom g = POSTGIS2GEOS(geom2);
+                       result = GEOSPreparedIntersects( prep_cache->prepared_geom, g);
+                       GEOSGeom_destroy(g);
+               }
+               else
+               {
+                       GEOSGeom g = POSTGIS2GEOS(geom1);
+                       result = GEOSPreparedIntersects( prep_cache->prepared_geom, g);
+                       GEOSGeom_destroy(g);
+               }
+       }
+       else
+       {
+               GEOSGeom g1 = POSTGIS2GEOS(geom1);
+               GEOSGeom g2 = POSTGIS2GEOS(geom2);
+               result = GEOSIntersects( g1, g2);
+               GEOSGeom_destroy(g1);
+               GEOSGeom_destroy(g2);
+       }
+
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS contains() threw an error!");
+               PG_RETURN_NULL(); /* never get here */
+       }
+
+       PG_FREE_IF_COPY(geom1, 0);
+       PG_FREE_IF_COPY(geom2, 1);
+
+       PG_RETURN_BOOL(result);
+#endif /* PREPARED_GEOM */
+}
+#endif /* 0 */
+
diff --git a/lwgeom/lwgeom_geos_prepared.h b/lwgeom/lwgeom_geos_prepared.h
new file mode 100644 (file)
index 0000000..aee360c
--- /dev/null
@@ -0,0 +1,70 @@
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2008 Paul Ramsey <pramsey@cleverelephant.ca>
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ * 
+ **********************************************************************/
+
+#include "../postgis_config.h"
+
+#include "postgres.h"
+#include "fmgr.h"
+#include "miscadmin.h"
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
+#include "access/hash.h"
+
+#include "geos_c.h"
+
+#include "lwgeom_pg.h"
+#include "liblwgeom.h"
+#include "lwgeom_geos.h"
+
+/*
+** GEOS prepared geometry is only available from GEOS 3.1 onwards
+*/
+#if POSTGIS_GEOS_VERSION >= 31
+#define PREPARED_GEOM 
+#endif
+
+/* 
+** Cache structure. We use PG_LWGEOM as keys so no transformations
+** are needed before we memcmp them with other keys. We store the 
+** size to avoid having to calculate the size every time.
+** The argnum gives the number of function arguments we are caching. 
+** Intersects requires that both arguments be checked for cacheability, 
+** while Contains only requires that the containing argument be checked. 
+** Both the Geometry and the PreparedGeometry have to be cached, 
+** because the PreparedGeometry contains a reference to the geometry.
+*/
+#ifdef PREPARED_GEOM
+typedef struct
+{
+       PG_LWGEOM*                    pg_geom1;
+       PG_LWGEOM*                    pg_geom2;
+       size_t                        pg_geom1_size;
+       size_t                        pg_geom2_size;
+       int32                         argnum;
+       const GEOSPreparedGeometry*   prepared_geom;
+       const GEOSGeometry*           geom;
+       MemoryContext                 context;
+} PrepGeomCache;
+
+/* 
+** Get the current cache, given the input geometries.
+** Function will create cache if none exists, and prepare geometries in
+** cache if necessary, or pull an existing cache if possible.
+**
+** If you are only caching one argument (e.g., in contains) supply 0 as the
+** value for pg_geom2.
+*/
+PrepGeomCache *GetPrepGeomCache(FunctionCallInfoData *fcinfo, PG_LWGEOM *pg_geom1, PG_LWGEOM *pg_geom2);
+
+
+#endif /* PREPARED_GEOM */
+
index cc694cfb269336c26d4a1af2dbc023320bcae4a6..26d45d648af3e02e1565496e66e4a86709422aec 100644 (file)
@@ -61,4 +61,6 @@ RTREE_POLY_CACHE *createCache(void);
 void populateCache(RTREE_POLY_CACHE *cache, LWGEOM *lwgeom, uchar *serializedPoly);\r
 void clearCache(RTREE_POLY_CACHE *cache);\r
 \r
+\r
+\r
 #endif /* !defined _LIBLWGEOM_H */\r
index 83c0ba5dc03f9699de53cb52b2fd6cecce6f5aa7..ebed6622caef760077c8ed3b50a692e45f782717 100644 (file)
@@ -3875,6 +3875,21 @@ CREATEFUNCTION ST_Covers(geometry,geometry)
    LANGUAGE 'SQL' _IMMUTABLE; -- WITH (iscachable);
 #endif
 
+#if POSTGIS_GEOS_VERSION >= 31
+-- Availability: 1.4.0
+CREATEFUNCTION _ST_ContainsProperly(geometry,geometry)
+    RETURNS boolean
+    AS 'MODULE_PATHNAME','containsproperly'
+    LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
+
+-- Availability: 1.4.0
+-- Inlines index magic
+CREATEFUNCTION ST_ContainsProperly(geometry,geometry)
+    RETURNS boolean
+    AS 'SELECT $1 && $2 AND _ST_ContainsProperly($1,$2)'
+    LANGUAGE 'SQL' _IMMUTABLE; -- WITH (iscachable);
+#endif
+
 -- Deprecation in 1.2.3
 CREATEFUNCTION overlaps(geometry,geometry)
    RETURNS boolean
@@ -3979,74 +3994,6 @@ CREATEFUNCTION ST_Equals(geometry,geometry)
     AS 'MODULE_PATHNAME','geomequals'
     LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
 
------------------------------------------------------------------------
--- Prepared Geometry Predicates
--- requires GEOS 3.1.0-CAPI-1.5.0 or better
------------------------------------------------------------------------
-
-#if POSTGIS_GEOS_VERSION >= 31
-
--- Availability: 1.4.0
-CREATEFUNCTION _ST_ContainsPrepared(geometry,geometry,integer)
-    RETURNS boolean
-    AS 'MODULE_PATHNAME','containsPrepared'
-    LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
-
--- Availability: 1.4.0
--- Inlines index magic
-CREATEFUNCTION ST_Contains(geometry,geometry,integer)
-    RETURNS boolean
-    AS 'SELECT $1 && $2 AND _ST_ContainsPrepared($1,$2,$3)'
-    LANGUAGE 'SQL' _IMMUTABLE; -- WITH (iscachable);
-
--- Availability: 1.4.0
-CREATEFUNCTION _ST_ContainsProperlyPrepared(geometry,geometry,integer)
-    RETURNS boolean
-    AS 'MODULE_PATHNAME','containsProperlyPrepared'
-    LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
-
--- Availability: 1.4.0
--- Inlines index magic
-CREATEFUNCTION ST_ContainsProperly(geometry,geometry,integer)
-    RETURNS boolean
-    AS 'SELECT $1 && $2 AND _ST_ContainsProperlyPrepared($1,$2,$3)'
-    LANGUAGE 'SQL' _IMMUTABLE; -- WITH (iscachable);
-
--- Availability: 1.4.0
--- Added for completeness, and to make testing ST_ContainsProperlyPrepared easier
-CREATE OR REPLACE FUNCTION ST_ContainsProperly(geometry,geometry)
-    RETURNS boolean
-    AS 'SELECT $1 && $2 AND ST_relate($1,$2,''T**FF*FF*'')'
-    LANGUAGE 'SQL' IMMUTABLE; 
-       
--- Availability: 1.4.0
-CREATEFUNCTION _ST_CoversPrepared(geometry,geometry,integer)
-    RETURNS boolean
-    AS 'MODULE_PATHNAME','coversPrepared'
-    LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
-       
--- Availability: 1.4.0
--- Inlines index magic
-CREATEFUNCTION ST_Covers(geometry,geometry,integer)
-    RETURNS boolean
-    AS 'SELECT $1 && $2 AND _ST_CoversPrepared($1,$2,$3)'
-    LANGUAGE 'SQL' _IMMUTABLE; -- WITH (iscachable);
-
--- Availability: 1.4.0
-CREATEFUNCTION _ST_IntersectsPrepared(geometry,geometry,integer,integer)
-    RETURNS boolean
-    AS 'MODULE_PATHNAME','intersectsPrepared'
-    LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
-       
--- Availability: 1.4.0
--- Inlines index magic
-CREATEFUNCTION ST_Intersects(geometry,geometry,integer,integer)
-    RETURNS boolean
-    AS 'SELECT $1 && $2 AND _ST_IntersectsPrepared($1,$2,$3,$4)'
-    LANGUAGE 'SQL' _IMMUTABLE; -- WITH (iscachable);
-
-#endif
-
 -----------------------------------------------------------------------
 -- SVG OUTPUT
 -----------------------------------------------------------------------
index c7ea310f8754cd85a9ef5af8e786694bd6123a75..396bf8fb6b66b4ba38adc643b645f33a79c0fe5e 100644 (file)
@@ -9,6 +9,9 @@ How to add a regression test
       -a -- unaligned columns
       -f | -- use | (pipe) as the field separator between columns
       -t -- output rows only, ie. no table header
+
+   cat file.sql | psql -F\| -t -A > file_expected
+
 3. Edit regress/Makefile adding <testname> to the TESTS variable.
    Any _expected.in files need to be added to the PREPROC variable.
 
index d10e918eefa74e85b292ac2132b23eacdeb08599..1b3acd11ac280272407d69573d0b475e2ec95d68 100644 (file)
 ---
 ---
 
-SELECT 'intersects', ST_intersects('LINESTRING(0 10, 0 -10)', p, 0) from ( values 
-('LINESTRING(0 0, 1 1)'),('LINESTRING(0 0, 1 1)'),('LINESTRING(0 0, 1 1)')
-) as v(p);
+SELECT c, ST_Intersects(ply, pt) FROM 
+( VALUES 
+-- PIP - point within polygon (no cache)
+('intersects099', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(5 5)'), 
 -- PIP - point within polygon
-SELECT 'intersects100', ST_intersects('POINT(5 5)', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 0);
+('intersects100', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(5 5)'), 
 -- PIP - point on polygon vertex
-SELECT 'intersects101', ST_intersects('POINT(0 0)', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 0);
+('intersects101', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 0)'), 
 -- PIP - point outside polygon
-SELECT 'intersects102', ST_intersects('POINT(-1 0)', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 0);
+('intersects102', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(-1 0)'),
 -- PIP - point on polygon edge
-SELECT 'intersects103', ST_intersects('POINT(0 5)', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 0);
+('intersects103', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 5)'),
 -- PIP - point in line with polygon edge
-SELECT 'intersects104', ST_intersects('POINT(0 12)', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 0);
--- PIP - point vertically aligned with polygon vertex 
-SELECT 'intersects105', ST_intersects(ST_GeomFromText('POINT(521513 5377804)', 32631), ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), 0);
--- PIP - repeated vertex
-SELECT 'intersects106', ST_intersects(ST_GeomFromText('POINT(521543 5377804)', 32631), ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), 0);
+('intersects104', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 12)')
+) AS v(c,ply,pt);
+
+SELECT c, ST_Contains(ply, pt) FROM 
+( VALUES 
+-- PIP - point within polygon (no cache)
+('contains099', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(5 5)'), 
 -- PIP - point within polygon
-SELECT 'intersects150', ST_intersects('POINT(5 5)', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 0);
+('contains100', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(5 5)'), 
 -- PIP - point on polygon vertex
-SELECT 'intersects151', ST_intersects('POINT(0 0)', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 0);
+('contains101', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 0)'), 
 -- PIP - point outside polygon
-SELECT 'intersects152', ST_intersects('POINT(-1 0)', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 0);
+('contains102', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(-1 0)'),
 -- PIP - point on polygon edge
-SELECT 'intersects153', ST_intersects('POINT(0 5)', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 0);
+('contains103', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 5)'),
 -- PIP - point in line with polygon edge
-SELECT 'intersects154', ST_intersects('POINT(0 12)', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 0);
--- PIP - point vertically aligned with polygon vertex 
-SELECT 'intersects155', ST_intersects(ST_GeomFromText('POINT(521513 5377804)', 32631), ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), 0);
--- PIP - repeated vertex
-SELECT 'intersects156', ST_intersects(ST_GeomFromText('POINT(521543 5377804)', 32631), ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), 0);
-
-SELECT 'intersects200', ST_intersects('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(5 5)'),('POINT(5 5)'),('POINT(5 5)') 
-) as v(p);
--- PIP - point on vertex of polygon
-SELECT 'intersects201', ST_intersects('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 0)'),('POINT(0 0)'),('POINT(0 0)')
-) as v(p);
+('contains104', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 12)')
+) AS v(c,ply,pt);
+
+SELECT c, ST_Covers(ply, pt) FROM 
+( VALUES 
+-- PIP - point within polygon (no cache)
+('covers099', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(5 5)'), 
+-- PIP - point within polygon
+('covers100', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(5 5)'), 
+-- PIP - point on polygon vertex
+('covers101', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 0)'), 
 -- PIP - point outside polygon
-SELECT 'intersects202', ST_intersects('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(-1 0)'),('POINT(-1 0)'),('POINT(-1 0)')
-) as v(p);
--- PIP - point on edge of polygon
-SELECT 'intersects203', ST_intersects('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 5)'),('POINT(0 5)'),('POINT(0 5)')
-) as v(p);
+('covers102', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(-1 0)'),
+-- PIP - point on polygon edge
+('covers103', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 5)'),
 -- PIP - point in line with polygon edge
-SELECT 'intersects204', ST_intersects('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 12)'),('POINT(0 12)'),('POINT(0 12)')
-) as v(p);
--- PIP - point vertically aligned with polygon vertex 
-SELECT 'intersects205', ST_intersects(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), p, 0) from ( values 
-(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631))
-) as v(p);
--- PIP - repeated vertex 
-SELECT 'intersects206', ST_intersects(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), p, 0) from ( values 
-(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631))
-) as v(p);
-SELECT 'intersects210', ST_intersects('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('LINESTRING(1 10, 9 10, 9 8)'),('LINESTRING(1 10, 9 10, 9 8)'),('LINESTRING(1 10, 9 10, 9 8)')
-) as v(p);
-SELECT 'intersects211', ST_intersects('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('LINESTRING(1 10, 10 10, 10 8)'),('LINESTRING(1 10, 10 10, 10 8)'),('LINESTRING(1 10, 10 10, 10 8)')
-) as v(p);
-
+('covers104', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 12)')
+) AS v(c,ply,pt);
 
+SELECT c, ST_ContainsProperly(ply, pt) FROM 
+( VALUES 
+-- PIP - point within polygon (no cache)
+('containsproperly099', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(5 5)'), 
 -- PIP - point within polygon
-SELECT 'contains100', ST_Contains('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(5 5)'),('POINT(5 5)'),('POINT(5 5)')
-) as v(p);
--- PIP - point on vertex of polygon
-SELECT 'contains101', ST_Contains('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 0)'),('POINT(0 0)'),('POINT(0 0)')
-) as v(p);
+('containsproperly100', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(5 5)'), 
+-- PIP - point on polygon vertex
+('containsproperly101', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 0)'), 
 -- PIP - point outside polygon
-SELECT 'contains102', ST_Contains('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(-1 0)'),('POINT(-1 0)'),('POINT(-1 0)')
-) as v(p);
--- PIP - point on edge of rect
-SELECT 'contains103', ST_Contains('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 5)'),('POINT(0 5)'),('POINT(0 5)')
-) as v(p);
--- PIP - point on other edge of rect
-SELECT 'contains103a', ST_Contains('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(5 0)'),('POINT(5 0)'),('POINT(5 0)')
-) as v(p);
--- PIP - point on edge of polygon
-SELECT 'contains103b', ST_Contains('POLYGON((0 0, 0 10, 10 10, 15 0, 0 0))', p, 0) from ( values 
-('POINT(0 5)'),('POINT(0 5)'),('POINT(0 5)')
-) as v(p);
--- PIP - point on other edge of polygon
-SELECT 'contains103c', ST_Contains('POLYGON((0 0, 0 10, 10 10, 15 0, 0 0))', p, 0) from ( values 
-('POINT(5 0)'),('POINT(5 0)'),('POINT(5 0)')
-) as v(p);
+('containsproperly102', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(-1 0)'),
+-- PIP - point on polygon edge
+('containsproperly103', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 5)'),
 -- PIP - point in line with polygon edge
-SELECT 'contains104', ST_Contains('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 12)'),('POINT(0 12)'),('POINT(0 12)')
-) as v(p);
--- PIP - point vertically aligned with polygon vertex 
-SELECT 'contains105', ST_Contains(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), p, 0) from ( values 
-(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631))
-) as v(p);
--- PIP - repeated vertex 
-SELECT 'contains106', ST_Contains(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), p, 0) from ( values 
-(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631))
-) as v(p);
-SELECT 'contains110', ST_Contains('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
+('containsproperly104', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POINT(0 12)')
+) AS v(c,ply,pt);
+
+
+-- PIP - point vertically aligned with polygon vertex, poly first
+SELECT 'intersects105', ST_Intersects(p, ST_GeomFromText('POINT(521513 5377804)', 32631)) FROM 
+( VALUES 
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)) 
+) AS v(p);
+-- PIP - point vertically aligned with polygon vertex, point first
+SELECT 'intersects106', ST_Intersects(ST_GeomFromText('POINT(521513 5377804)', 32631), p) FROM 
+( VALUES 
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)) 
+) AS v(p);
+-- PIP - repeated vertex, poly first
+SELECT 'intersects107', ST_Intersects(p, ST_GeomFromText('POINT(521543 5377804)', 32631)) FROM 
+( VALUES 
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631))
+) AS v(p);
+-- PIP - repeated vertex, point first
+SELECT 'intersects108', ST_Intersects(ST_GeomFromText('POINT(521543 5377804)', 32631), p) FROM 
+( VALUES 
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631))
+) AS v(p);
+
+
+-- PIP - point vertically aligned with polygon vertex, poly first
+SELECT 'contains105', ST_Contains(p, ST_GeomFromText('POINT(521513 5377804)', 32631)) FROM 
+( VALUES 
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)) 
+) AS v(p);
+-- PIP - point vertically aligned with polygon vertex, point first
+SELECT 'contains106', ST_Contains(ST_GeomFromText('POINT(521513 5377804)', 32631), p) FROM 
+( VALUES 
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)) 
+) AS v(p);
+-- PIP - repeated vertex, poly first
+SELECT 'contains107', ST_Contains(p, ST_GeomFromText('POINT(521543 5377804)', 32631)) FROM 
+( VALUES 
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631))
+) AS v(p);
+-- PIP - repeated vertex, point first
+SELECT 'contains108', ST_Contains(ST_GeomFromText('POINT(521543 5377804)', 32631), p) FROM 
+( VALUES 
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631))
+) AS v(p);
+
+-- PIP - point vertically aligned with polygon vertex, poly first
+SELECT 'containsproperly105', ST_ContainsProperly(p, ST_GeomFromText('POINT(521513 5377804)', 32631)) FROM 
+( VALUES 
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)) 
+) AS v(p);
+-- PIP - point vertically aligned with polygon vertex, point first
+SELECT 'containsproperly106', ST_ContainsProperly(ST_GeomFromText('POINT(521513 5377804)', 32631), p) FROM 
+( VALUES 
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)) 
+) AS v(p);
+-- PIP - repeated vertex, poly first
+SELECT 'containsproperly107', ST_ContainsProperly(p, ST_GeomFromText('POINT(521543 5377804)', 32631)) FROM 
+( VALUES 
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631))
+) AS v(p);
+-- PIP - repeated vertex, point first
+SELECT 'containsproperly108', ST_ContainsProperly(ST_GeomFromText('POINT(521543 5377804)', 32631), p) FROM 
+( VALUES 
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631))
+) AS v(p);
+
+-- PIP - point vertically aligned with polygon vertex, poly first
+SELECT 'covers105', ST_Covers(p, ST_GeomFromText('POINT(521513 5377804)', 32631)) FROM 
+( VALUES 
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)) 
+) AS v(p);
+-- PIP - point vertically aligned with polygon vertex, point first
+SELECT 'covers106', ST_Covers(ST_GeomFromText('POINT(521513 5377804)', 32631), p) FROM 
+( VALUES 
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)) 
+) AS v(p);
+-- PIP - repeated vertex, poly first
+SELECT 'covers107', ST_Covers(p, ST_GeomFromText('POINT(521543 5377804)', 32631)) FROM 
+( VALUES 
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631))
+) AS v(p);
+-- PIP - repeated vertex, point first
+SELECT 'covers108', ST_Covers(ST_GeomFromText('POINT(521543 5377804)', 32631), p) FROM 
+( VALUES 
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631)),
+       (ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631))
+) AS v(p);
+
+
+SELECT c, ST_Intersects(p1, p2) AS intersects_p1p2, ST_Intersects(p2, p1) AS intersects_p2p1 FROM
+( VALUES
+('intersects200', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('intersects201', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('intersects202', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((0 0, 2 0, 2 2, 0 2, 0 0))'),
+('intersects203', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((-5 -5, 5 -5, 5 5, -5 5, -5 -5))'), 
+('intersects204', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((-2 -2, -2 -3, -3 -3, -3 -2, -2 -2))'),
+('intersects205', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('intersects206', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('intersects207', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((0 0, 2 0, 2 2, 0 2, 0 0))'),
+('intersects208', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((-5 -5, 5 -5, 5 5, -5 5, -5 -5))'), 
+('intersects209', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((-2 -2, -2 -3, -3 -3, -3 -2, -2 -2))')
+) AS v(c,p1,p2);
+
+SELECT c, ST_Contains(p1, p2) AS contains_p1p2, ST_Contains(p2, p1) AS contains_p2p1 FROM
+( VALUES
+('contains200', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('contains201', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('contains202', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((0 0, 2 0, 2 2, 0 2, 0 0))'),
+('contains203', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((-5 -5, 5 -5, 5 5, -5 5, -5 -5))'), 
+('contains204', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((-2 -2, -2 -3, -3 -3, -3 -2, -2 -2))'),
+('contains205', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('contains206', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('contains207', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((0 0, 2 0, 2 2, 0 2, 0 0))'),
+('contains208', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((-5 -5, 5 -5, 5 5, -5 5, -5 -5))'), 
+('contains209', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((-2 -2, -2 -3, -3 -3, -3 -2, -2 -2))')
+) AS v(c,p1,p2);
+
+SELECT c, ST_ContainsProperly(p1, p2) AS containsproperly_p1p2, ST_ContainsProperly(p2, p1) AS containsproperly_p2p1 FROM
+( VALUES
+('containsproperly200', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('containsproperly201', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('containsproperly202', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((0 0, 2 0, 2 2, 0 2, 0 0))'),
+('containsproperly203', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((-5 -5, 5 -5, 5 5, -5 5, -5 -5))'), 
+('containsproperly204', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((-2 -2, -2 -3, -3 -3, -3 -2, -2 -2))'),
+('containsproperly205', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('containsproperly206', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('containsproperly207', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((0 0, 2 0, 2 2, 0 2, 0 0))'),
+('containsproperly208', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((-5 -5, 5 -5, 5 5, -5 5, -5 -5))'), 
+('containsproperly209', 'POLYGON((0 0, 0 10, 10 11, 10 0, 0 0))', 'POLYGON((-2 -2, -2 -3, -3 -3, -3 -2, -2 -2))')
+) AS v(c,p1,p2);
+
+SELECT c, ST_Covers(p1, p2) AS covers_p1p2, ST_Covers(p2, p1) AS covers_p2p1 FROM
+( VALUES
+('covers200', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('covers201', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('covers202', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((0 0, 2 0, 2 2, 0 2, 0 0))'),
+('covers203', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((-5 -5, 5 -5, 5 5, -5 5, -5 -5))'), 
+('covers204', 'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 'POLYGON((-2 -2, -2 -3, -3 -3, -3 -2, -2 -2))'),
+('covers205', 'POLYGON((0 0, 0 10, 10 10, 11 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('covers206', 'POLYGON((0 0, 0 10, 10 10, 11 0, 0 0))', 'POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))'), 
+('covers207', 'POLYGON((0 0, 0 10, 10 10, 11 0, 0 0))', 'POLYGON((0 0, 2 0, 2 2, 0 2, 0 0))'),
+('covers208', 'POLYGON((0 0, 0 10, 10 10, 11 0, 0 0))', 'POLYGON((-5 -5, 5 -5, 5 5, -5 5, -5 -5))'), 
+('covers209', 'POLYGON((0 0, 0 10, 10 10, 11 0, 0 0))', 'POLYGON((-2 -2, -2 -3, -3 -3, -3 -2, -2 -2))')
+) AS v(c,p1,p2);
+
+SELECT 'intersects310', ST_intersects('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p) FROM ( VALUES 
 ('LINESTRING(1 10, 9 10, 9 8)'),('LINESTRING(1 10, 9 10, 9 8)'),('LINESTRING(1 10, 9 10, 9 8)')
-) as v(p);
-SELECT 'contains111', ST_Contains('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
+) AS v(p);
+SELECT 'intersects311', ST_intersects('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p) FROM ( VALUES 
 ('LINESTRING(1 10, 10 10, 10 8)'),('LINESTRING(1 10, 10 10, 10 8)'),('LINESTRING(1 10, 10 10, 10 8)')
-) as v(p);
+) AS v(p);
 
--- PIP - point within polygon
-SELECT 'containsproperly100', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(5 5)'),('POINT(5 5)'),('POINT(5 5)')
-) as v(p);
--- PIP - point on vertex of polygon
-SELECT 'containsproperly101', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 0)'),('POINT(0 0)'),('POINT(0 0)')
-) as v(p);
--- PIP - point outside polygon
-SELECT 'containsproperly102', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(-1 0)'),('POINT(-1 0)'),('POINT(-1 0)')
-) as v(p);
--- PIP - point on edge of rect
-SELECT 'containsproperly103', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 5)'),('POINT(0 5)'),('POINT(0 5)')
-) as v(p);
--- PIP - point on other edge of rect
-SELECT 'containsproperly103a', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(5 0)'),('POINT(5 0)'),('POINT(5 0)')
-) as v(p);
--- PIP - point on edge of polygon
-SELECT 'containsproperly103b', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 15 0, 0 0))', p, 0) from ( values 
-('POINT(0 5)'),('POINT(0 5)'),('POINT(0 5)')
-) as v(p);
--- PIP - point on other edge of polygon
-SELECT 'containsproperly103c', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 15 0, 0 0))', p, 0) from ( values 
-('POINT(5 0)'),('POINT(5 0)'),('POINT(5 0)')
-) as v(p);
--- PIP - point in line with polygon edge
-SELECT 'containsproperly104', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 12)'),('POINT(0 12)'),('POINT(0 12)')
-) as v(p);
--- PIP - point vertically aligned with polygon vertex 
-SELECT 'containsproperly105', ST_ContainsProperly(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), p, 0) from ( values 
-(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631))
-) as v(p);
--- PIP - repeated vertex 
-SELECT 'containsproperly106', ST_ContainsProperly(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), p, 0) from ( values 
-(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631))
-) as v(p);
-SELECT 'containsproperly110', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
+SELECT 'contains310', ST_Contains('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p) FROM ( VALUES 
 ('LINESTRING(1 10, 9 10, 9 8)'),('LINESTRING(1 10, 9 10, 9 8)'),('LINESTRING(1 10, 9 10, 9 8)')
-) as v(p);
-SELECT 'containsproperly111', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
+) AS v(p);
+SELECT 'contains311', ST_Contains('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p) FROM ( VALUES 
 ('LINESTRING(1 10, 10 10, 10 8)'),('LINESTRING(1 10, 10 10, 10 8)'),('LINESTRING(1 10, 10 10, 10 8)')
-) as v(p);
+) AS v(p);
 
--- Covers cases
-SELECT 'covers100', ST_Covers('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
+SELECT 'containsproperly310', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p) FROM ( VALUES 
 ('LINESTRING(1 10, 9 10, 9 8)'),('LINESTRING(1 10, 9 10, 9 8)'),('LINESTRING(1 10, 9 10, 9 8)')
-) as v(p);
-SELECT 'covers101', ST_Covers('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
+) AS v(p);
+SELECT 'containsproperly311', ST_ContainsProperly('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p) FROM ( VALUES 
 ('LINESTRING(1 10, 10 10, 10 8)'),('LINESTRING(1 10, 10 10, 10 8)'),('LINESTRING(1 10, 10 10, 10 8)')
-) as v(p);
--- PIP - point within polygon
-SELECT 'covers102', ST_Covers('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(5 5)'),('POINT(5 5)'),('POINT(5 5)')
-) as v(p);
--- PIP - point on vertex of polygon
-SELECT 'covers103', ST_Covers('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 0)'),('POINT(0 0)'),('POINT(0 0)')
-) as v(p);
--- PIP - point outside polygon
-SELECT 'covers104', ST_Covers('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(-1 0)'),('POINT(-1 0)'),('POINT(-1 0)')
-) as v(p);
--- PIP - point on edge of polygon
-SELECT 'covers105', ST_Covers('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 5)'),('POINT(0 5)'),('POINT(0 5)')
-) as v(p);
--- PIP - point in line with polygon edge
-SELECT 'covers106', ST_Covers('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p, 0) from ( values 
-('POINT(0 12)'),('POINT(0 12)'),('POINT(0 12)')
-) as v(p);
--- PIP - point vertically aligned with polygon vertex 
-SELECT 'covers107', ST_Covers(ST_GeomFromText('POLYGON((521526 5377783, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), p, 0) from ( values 
-(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631))
-) as v(p);
--- PIP - repeated vertex 
-SELECT 'covers108', ST_Covers(ST_GeomFromText('POLYGON((521526 5377783, 521482 5377811, 521481 5377811, 521494 5377832, 521539 5377804, 521526 5377783))', 32631), p, 0) from ( values 
-(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631)),(ST_GeomFromText('POINT(521513 5377804)', 32631))
-) as v(p);
+) AS v(p);
+
+SELECT 'covers310', ST_Covers('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p) FROM ( VALUES 
+('LINESTRING(1 10, 9 10, 9 8)'),('LINESTRING(1 10, 9 10, 9 8)'),('LINESTRING(1 10, 9 10, 9 8)')
+) AS v(p);
+SELECT 'covers311', ST_Covers('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', p) FROM ( VALUES 
+('LINESTRING(1 10, 10 10, 10 8)'),('LINESTRING(1 10, 10 10, 10 8)'),('LINESTRING(1 10, 10 10, 10 8)')
+) AS v(p);
+
index 0d26077b459c3e1c20e9abed6c5bf05a0aa50297..72ecd68834537104ea5a5c01cd07651df9018562 100644 (file)
-intersects|t
-intersects|t
-intersects|t
+intersects099|t
 intersects100|t
 intersects101|t
 intersects102|f
 intersects103|t
 intersects104|f
-intersects105|t
-intersects106|f
-intersects150|t
-intersects151|t
-intersects152|f
-intersects153|t
-intersects154|f
-intersects155|t
-intersects156|f
-intersects200|t
-intersects200|t
-intersects200|t
-intersects201|t
-intersects201|t
-intersects201|t
-intersects202|f
-intersects202|f
-intersects202|f
-intersects203|t
-intersects203|t
-intersects203|t
-intersects204|f
-intersects204|f
-intersects204|f
-intersects205|t
-intersects205|t
-intersects205|t
-intersects206|t
-intersects206|t
-intersects206|t
-intersects210|t
-intersects210|t
-intersects210|t
-intersects211|t
-intersects211|t
-intersects211|t
-contains100|t
-contains100|t
+contains099|t
 contains100|t
 contains101|f
-contains101|f
-contains101|f
-contains102|f
 contains102|f
-contains102|f
-contains103|f
-contains103|f
 contains103|f
-contains103a|f
-contains103a|f
-contains103a|f
-contains103b|f
-contains103b|f
-contains103b|f
-contains103c|f
-contains103c|f
-contains103c|f
 contains104|f
-contains104|f
-contains104|f
-contains105|t
-contains105|t
-contains105|t
-contains106|t
-contains106|t
-contains106|t
-contains110|t
-contains110|t
-contains110|t
-contains111|f
-contains111|f
-contains111|f
-containsproperly100|t
-containsproperly100|t
+covers099|t
+covers100|t
+covers101|t
+covers102|f
+covers103|t
+covers104|f
+containsproperly099|t
 containsproperly100|t
 containsproperly101|f
-containsproperly101|f
-containsproperly101|f
-containsproperly102|f
 containsproperly102|f
-containsproperly102|f
-containsproperly103|f
-containsproperly103|f
 containsproperly103|f
-containsproperly103a|f
-containsproperly103a|f
-containsproperly103a|f
-containsproperly103b|f
-containsproperly103b|f
-containsproperly103b|f
-containsproperly103c|f
-containsproperly103c|f
-containsproperly103c|f
-containsproperly104|f
-containsproperly104|f
 containsproperly104|f
+intersects105|t
+intersects105|t
+intersects105|t
+intersects106|t
+intersects106|t
+intersects106|t
+intersects107|f
+intersects107|f
+intersects107|f
+intersects108|f
+intersects108|f
+intersects108|f
+contains105|t
+contains105|t
+contains105|t
+contains106|f
+contains106|f
+contains106|f
+contains107|f
+contains107|f
+contains107|f
+contains108|f
+contains108|f
+contains108|f
 containsproperly105|t
 containsproperly105|t
 containsproperly105|t
-containsproperly106|t
-containsproperly106|t
-containsproperly106|t
-containsproperly110|f
-containsproperly110|f
-containsproperly110|f
-containsproperly111|f
-containsproperly111|f
-containsproperly111|f
-covers100|t
-covers100|t
-covers100|t
-covers101|t
-covers101|t
-covers101|t
-covers102|t
-covers102|t
-covers102|t
-covers103|t
-covers103|t
-covers103|t
-covers104|f
-covers104|f
-covers104|f
+containsproperly106|f
+containsproperly106|f
+containsproperly106|f
+containsproperly107|f
+containsproperly107|f
+containsproperly107|f
+containsproperly108|f
+containsproperly108|f
+containsproperly108|f
 covers105|t
 covers105|t
 covers105|t
 covers106|f
 covers106|f
 covers106|f
-covers107|t
-covers107|t
-covers107|t
-covers108|t
-covers108|t
-covers108|t
+covers107|f
+covers107|f
+covers107|f
+covers108|f
+covers108|f
+covers108|f
+intersects200|t|t
+intersects201|t|t
+intersects202|t|t
+intersects203|t|t
+intersects204|f|f
+intersects205|t|t
+intersects206|t|t
+intersects207|t|t
+intersects208|t|t
+intersects209|f|f
+contains200|t|f
+contains201|t|f
+contains202|t|f
+contains203|f|f
+contains204|f|f
+contains205|t|f
+contains206|t|f
+contains207|t|f
+contains208|f|f
+contains209|f|f
+containsproperly200|t|f
+containsproperly201|t|f
+containsproperly202|f|f
+containsproperly203|f|f
+containsproperly204|f|f
+containsproperly205|t|f
+containsproperly206|t|f
+containsproperly207|f|f
+containsproperly208|f|f
+containsproperly209|f|f
+covers200|t|f
+covers201|t|f
+covers202|t|f
+covers203|f|f
+covers204|f|f
+covers205|t|f
+covers206|t|f
+covers207|t|f
+covers208|f|f
+covers209|f|f
+intersects310|t
+intersects310|t
+intersects310|t
+intersects311|t
+intersects311|t
+intersects311|t
+contains310|t
+contains310|t
+contains310|t
+contains311|f
+contains311|f
+contains311|f
+containsproperly310|f
+containsproperly310|f
+containsproperly310|f
+containsproperly311|f
+containsproperly311|f
+containsproperly311|f
+covers310|t
+covers310|t
+covers310|t
+covers311|t
+covers311|t
+covers311|t