]> granicus.if.org Git - postgis/commitdiff
prepared to contain old internal representation code
authorSandro Santilli <strk@keybit.net>
Mon, 20 Sep 2004 07:50:06 +0000 (07:50 +0000)
committerSandro Santilli <strk@keybit.net>
Mon, 20 Sep 2004 07:50:06 +0000 (07:50 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@832 b70326c6-7e19-0410-871a-916f4a2858ee

17 files changed:
hwgeom/Makefile [new file with mode: 0644]
hwgeom/postgis.h [new file with mode: 0644]
hwgeom/postgis.sql.in [new file with mode: 0644]
hwgeom/postgis_algo.c [new file with mode: 0644]
hwgeom/postgis_chip.c [new file with mode: 0644]
hwgeom/postgis_debug.c [new file with mode: 0644]
hwgeom/postgis_estimate.c [new file with mode: 0644]
hwgeom/postgis_fn.c [new file with mode: 0644]
hwgeom/postgis_geos.c [new file with mode: 0644]
hwgeom/postgis_geos_wrapper.cpp [new file with mode: 0644]
hwgeom/postgis_gist_71.c [new file with mode: 0644]
hwgeom/postgis_gist_72.c [new file with mode: 0644]
hwgeom/postgis_inout.c [new file with mode: 0644]
hwgeom/postgis_ops.c [new file with mode: 0644]
hwgeom/postgis_proj.c [new file with mode: 0644]
hwgeom/postgis_svg.c [new file with mode: 0644]
hwgeom/postgis_transform.c [new file with mode: 0644]

diff --git a/hwgeom/Makefile b/hwgeom/Makefile
new file mode 100644 (file)
index 0000000..f1bd9a1
--- /dev/null
@@ -0,0 +1,247 @@
+#---------------------------------------------------------------
+# Configuration Directives
+#
+# We recommend that you install the Proj4 and GEOS libraries
+# referenced below to get the most use out of your PostGIS
+# database.
+
+#---------------------------------------------------------------
+# Set USE_PROJ to 1 for Proj4 reprojection support (recommended)
+#
+# Reprojection allows you to transform coordinate systems
+# in the database with the Transform() function.
+#
+# Download from: http://www.remotesensing.org/proj
+#
+USE_PROJ=1
+ifeq (${PROJ_DIR},) 
+       PROJ_DIR=/usr/local
+endif
+
+#---------------------------------------------------------------
+# Set USE_GEOS to 1 for GEOS spatial predicate and operator
+# support (recommended)
+#
+# GEOS allows you to do exact topological tests, such as
+# Intersects() and Touches(), as well as geometry operations,
+# such as Buffer(), GeomUnion() and Difference().
+#
+# Download from: http://geos.refractions.net
+#
+USE_GEOS=1
+ifeq (${GEOS_DIR},) 
+       GEOS_DIR=/usr/local
+endif
+
+#---------------------------------------------------------------
+# Set USE_STATS to 1 for new GiST statistics collection support
+# Note that this support requires additional columns in 
+# GEOMETRY_COLUMNS, so see the list archives for info or
+# install a fresh database using postgis.sql
+#
+USE_STATS=1
+
+#---------------------------------------------------------------
+subdir=contrib/postgis
+
+#---------------------------------------------------------------
+# Default the root of the PostgreSQL source tree 
+# To use a non-standard location set the PGSQL_SRC environment
+# variable to the appropriate location.
+#
+ifeq (${PGSQL_SRC},) 
+       PGSQL_SRC = ../..
+endif
+
+#---------------------------------------------------------------
+# Path to library (to be specified in CREATE FUNCTION queries)
+# Defaults to $libdir.
+# Set LPATH environment variable to change it.
+#
+ifeq (${LPATH},)
+       LPATH := \$$libdir
+endif
+
+#---------------------------------------------------------------
+top_builddir = ${PGSQL_SRC}
+include $(top_builddir)/src/Makefile.global
+
+#---------------------------------------------------------------
+# Default missing CXX variable to c++
+# 
+ifeq ($(CXX),) 
+       CXX = c++
+endif
+
+#---------------------------------------------------------------
+# Test the version string and set the USE_VERSION macro
+# appropriately.
+#
+ifneq ($(findstring 7.1,$(VERSION)),)
+       USE_VERSION=71
+else
+       ifneq ($(findstring 7.2,$(VERSION)),)
+               USE_VERSION=72
+       else
+               ifneq ($(findstring 7.3,$(VERSION)),)
+                       USE_VERSION=73
+               else
+                       ifneq ($(findstring 7.4,$(VERSION)),)
+                               USE_VERSION=74
+                       else
+                               USE_VERSION=80
+                       endif
+               endif
+       endif
+endif
+
+#---------------------------------------------------------------
+# Shared library parameters.
+#
+NAME=postgis
+SO_MAJOR_VERSION=0
+SO_MINOR_VERSION=9
+SO_MICRO_VERSION=0
+SCRIPTS_VERSION=0.0.1
+ifeq (${USE_VERSION}, 71) 
+       MODULE_FILENAME = $(LPATH)/$(shlib)
+       MODULE_INSTALLDIR = $(libdir)
+else
+       MODULE_FILENAME = $(LPATH)/$(shlib)
+       MODULE_INSTALLDIR = $(pkglibdir)
+endif
+
+#---------------------------------------------------------------
+# Postgis version
+#---------------------------------------------------------------
+
+POSTGIS_LIB_VERSION = $(SO_MAJOR_VERSION).$(SO_MINOR_VERSION).$(SO_MICRO_VERSION)
+POSTGIS_VERSION = $(SO_MAJOR_VERSION).$(SO_MINOR_VERSION) USE_GEOS=$(USE_GEOS) USE_PROJ=$(USE_PROJ) USE_STATS=$(USE_STATS)
+
+#---------------------------------------------------------------
+
+override CFLAGS += -g -fexceptions 
+override CFLAGS += -I$(srcdir) -DFRONTEND -DSYSCONFDIR='"$(sysconfdir)"' 
+override CFLAGS += -DUSE_VERSION=$(USE_VERSION)
+override CFLAGS += -DPOSTGIS_LIB_VERSION='"$(POSTGIS_LIB_VERSION)"'
+override CFLAGS += -DPOSTGIS_SCRIPTS_VERSION='"$(SCRIPTS_VERSION)"'
+
+ifeq ($(USE_GEOS),1)
+       override CFLAGS += -I$(GEOS_DIR)/include -DUSE_GEOS
+       GEOS_RULES=detect_geos_version
+endif
+ifeq ($(USE_PROJ),1)
+       override CFLAGS += -I$(PROJ_DIR)/include -DUSE_PROJ 
+endif
+
+override DLLLIBS += $(BE_DLLLIBS) 
+
+override CXXFLAGS := $(CFLAGS)
+# memory debug for gcc 2.91, 2.95, 3.0 and 3.1
+# for gcc >= 3.2.2 set GLIBCPP_FORCE_NEW at runtime instead
+#override CXXFLAGS += -D__USE_MALLOC
+
+# this seems to be needed by gcc3.3.2 / Solaris7 combination
+# as reported by  Havard Tveite <havard.tveite@nlh.no>
+override CXXFLAGS += -fPIC
+
+#---------------------------------------------------------------
+# Add index selectivity to C flags
+#
+ifeq ($(USE_STATS),1)
+       override CFLAGS += -DUSE_STATS
+endif
+#---------------------------------------------------------------
+# Select proper GiST support C file
+#
+ifeq ($(USE_VERSION),71) 
+       GIST_SUPPORT=71
+       GIST_ESTIMATE=
+else
+       GIST_SUPPORT=72
+       GIST_ESTIMATE=postgis_estimate.o
+endif
+
+ifeq ($(USE_GEOS),1)
+       GEOS_WRAPPER=postgis_geos_wrapper.o
+endif
+
+OBJS=postgis_debug.o postgis_ops.o postgis_fn.o postgis_inout.o postgis_proj.o postgis_chip.o postgis_transform.o postgis_svg.o postgis_gist_$(GIST_SUPPORT).o $(GIST_ESTIMATE) postgis_geos.o $(GEOS_WRAPPER) postgis_algo.o
+
+#---------------------------------------------------------------
+# Add libraries that libpq depends (or might depend) on into the
+# shared library link.  (The order in which you list them here doesn't
+# matter.)
+
+SHLIB_LINK = $(filter -L%, $(LDFLAGS)) 
+ifeq ($(USE_GEOS),1)
+       SHLIB_LINK += -lstdc++ -L$(GEOS_DIR)/lib -lgeos
+endif
+ifeq ($(USE_PROJ),1)
+       SHLIB_LINK += -L$(PROJ_DIR)/lib -lproj
+endif
+SHLIB_LINK += $(BE_DLLLIBS) 
+
+#---------------------------------------------------------------
+# Makefile targets
+
+include $(top_srcdir)/src/Makefile.shlib
+
+postgis_geos_wrapper.o: postgis_geos_wrapper.cpp
+
+all: $(GEOS_RULES) all-lib postgis.sql postgis_undef.sql loaderdumper
+
+loaderdumper:
+       $(MAKE) -C loader
+
+# Shared library stuff
+
+postgis_old.sql: postgis_sql_common.sql.in postgis_sql_$(USE_VERSION)_end.sql.in postgis_sql_$(USE_VERSION)_start.sql.in 
+       cat postgis_sql_$(USE_VERSION)_start.sql.in postgis_sql_common.sql.in postgis_sql_$(USE_VERSION)_end.sql.in | sed -e 's:@MODULE_FILENAME@:$(MODULE_FILENAME):g;s:@POSTGIS_VERSION@:$(POSTGIS_VERSION):g'  > $@ 
+
+postgis.sql: postgis.sql.in
+       cpp -P -traditional-cpp -DUSE_VERSION=$(USE_VERSION) $< | sed -e 's:@MODULE_FILENAME@:$(MODULE_FILENAME):g;s:@POSTGIS_VERSION@:$(POSTGIS_VERSION):g;s:@POSTGIS_SCRIPTS_VERSION@:$(SCRIPTS_VERSION):g' > $@
+
+postgis_undef.sql: postgis.sql create_undef.pl
+       perl create_undef.pl $< $(USE_VERSION) > $@ 
+
+install: all installdirs install-postgis-lib
+       $(INSTALL_DATA) postgis.sql $(DESTDIR)$(datadir)
+       $(INSTALL_DATA) postgis_undef.sql $(DESTDIR)$(datadir)
+       $(INSTALL_DATA) spatial_ref_sys.sql $(DESTDIR)$(datadir)
+       $(INSTALL_DATA) README.postgis $(DESTDIR)$(datadir)
+       $(MAKE) DESTDIR=$(DESTDIR) -C loader install
+
+#- This has been copied from postgresql and adapted
+install-postgis-lib: $(shlib)
+       $(INSTALL_SHLIB) $< $(DESTDIR)$(MODULE_INSTALLDIR)/$(shlib)
+ifneq ($(PORTNAME), win)
+ifneq ($(shlib), lib$(NAME)$(DLSUFFIX).$(SO_MAJOR_VERSION))
+       cd $(DESTDIR)$(MODULE_INSTALLDIR) && \
+       rm -f lib$(NAME)$(DLSUFFIX).$(SO_MAJOR_VERSION) && \
+       $(LN_S) $(shlib) lib$(NAME)$(DLSUFFIX).$(SO_MAJOR_VERSION)
+endif
+ifneq ($(shlib), lib$(NAME)$(DLSUFFIX))
+       cd $(DESTDIR)$(MODULE_INSTALLDIR) && \
+       rm -f lib$(NAME)$(DLSUFFIX) && \
+       $(LN_S) $(shlib) lib$(NAME)$(DLSUFFIX)
+endif
+
+endif # not win
+#----------------------------------------------------------
+
+detect_geos_version: 
+       ./geos_version.sh $(GEOS_DIR) > postgis_geos_version.h
+
+installdirs:
+       $(mkinstalldirs) $(docdir)/contrib $(datadir)/contrib $(libdir)
+
+uninstall: uninstall-lib
+       @rm -f $(docdir)/contrib/README.postgis $(datadir)/contrib/postgis.sql
+
+clean distclean maintainer-clean: clean-lib
+       @rm -f $(OBJS) postgis.sql postgis_undef.sql postgis_geos_version.h
+       $(MAKE) -C loader clean
+       $(MAKE) -C doc clean
+
diff --git a/hwgeom/postgis.h b/hwgeom/postgis.h
new file mode 100644 (file)
index 0000000..ed1671f
--- /dev/null
@@ -0,0 +1,716 @@
+
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2001-2003 Refractions Research Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of hte GNU General Public Licence. See the COPYING file.
+ *
+ **********************************************************************
+ * $Log$
+ * Revision 1.1  2004/09/20 07:50:06  strk
+ * prepared to contain old internal representation code
+ *
+ * Revision 1.51  2004/09/16 15:50:59  mleslie
+ * Added the distance_sphere function to calculate the distance between two points
+ * on an earth-sized sphere using an algorithm implemented by Bruno Wolff III.
+ * Added the postgresql loader function.
+ *
+ * Revision 1.50  2004/08/19 06:15:58  strk
+ * USE_VERSION gets 80 where it got 75
+ *
+ * Revision 1.49  2004/07/28 13:37:43  strk
+ * Added postgis_uses_stats and postgis_scripts_version.
+ * Experimented with PIP short-circuit in within/contains functions.
+ * Documented new version functions.
+ *
+ * Revision 1.48  2004/07/23 21:24:33  strk
+ * Added postgis_proj_version()
+ *
+ * Revision 1.47  2004/07/22 16:20:09  strk
+ * Added postgis_lib_version() and postgis_geos_version()
+ *
+ * Revision 1.46  2004/06/09 09:06:55  strk
+ * Added Romi's Win32 patches.
+ *
+ * Revision 1.45  2004/06/08 15:18:12  strk
+ * Deleted prototype for isspace() in postgis.h
+ * and included <ctype.h> in postgis_inout.c,
+ * which is the only module calling isspace().
+ * This was needed to compile postgis against PG75(CVS).
+ *
+ * Revision 1.44  2004/06/03 16:44:56  strk
+ * Added expand_geometry - expand(geometry, int8)
+ *
+ * Revision 1.43  2004/03/26 00:54:09  dblasby
+ * added full support for fluffType(<geom>)
+ * postgis09=# select fluffType('POINT(0 0)');
+ *         flufftype
+ *             -------------------------
+ *              SRID=-1;MULTIPOINT(0 0)
+ *
+ * Revision 1.42  2004/02/23 12:18:55  strk
+ * added skeleton functions for pg75 stats integration
+ *
+ * Revision 1.41  2004/01/25 19:33:00  pramsey
+ * Test commit on new CVS archive.
+ *
+ * Revision 1.40  2004/01/21 19:04:03  strk
+ * Added line_interpolate_point function by jsunday@rochgrp.com
+ *
+ * Revision 1.39  2003/11/28 11:06:49  strk
+ * Added WKB_recv function for binary WKB input
+ *
+ * Revision 1.38  2003/11/19 15:44:51  strk
+ * added prototypes for geometry_{le,ge,cmp}
+ *
+ * Revision 1.37  2003/10/28 16:57:35  strk
+ * Added collect_garray() function.
+ *
+ * Revision 1.36  2003/10/28 15:16:17  strk
+ * unite_sfunc() from postgis_geos.c renamed to geom_accum() and moved in postgis_fn.c
+ *
+ * Revision 1.35  2003/10/28 11:16:46  strk
+ * Added postgis_algo.c prototypes
+ *
+ * Revision 1.34  2003/10/16 16:35:42  dblasby
+ * added #include <sys/types.h> for people using freeBSD (strk@keybit.net patch)
+ *
+ * Revision 1.33  2003/08/08 18:19:20  dblasby
+ * Conformance changes.
+ * Removed junk from postgis_debug.c and added the first run of the long
+ * transaction locking support.  (this will change - dont use it)
+ * conformance tests were corrected
+ * some dos cr/lf removed
+ * empty geometries i.e. GEOMETRYCOLLECT(EMPTY) added (with indexing support)
+ * pointN(<linestring>,1) now returns the first point (used to return 2nd)
+ *
+ * Revision 1.32  2003/08/06 19:31:18  dblasby
+ * Added the WKB parser.  Added all the functions like
+ * PolyFromWKB(<WKB>,[<SRID>]).
+ *
+ * Added all the functions like PolyFromText(<WKT>,[<SRID>])
+ *
+ * Minor problem in GEOS library fixed.
+ *
+ * Revision 1.31  2003/07/25 17:08:37  pramsey
+ * Moved Cygwin endian define out of source files into postgis.h common
+ * header file.
+ *
+ * Revision 1.30  2003/07/01 18:30:55  pramsey
+ * Added CVS revision headers.
+ *
+ *
+ **********************************************************************/
+
+/*
+ * Everything is stored in a geometry3d, which is just a conglomeration
+ * of the base types (and a little bit of other info).
+ */
+
+#include <sys/types.h>
+#include "utils/geo_decls.h"
+
+#define        POINTTYPE       1
+#define        LINETYPE        2
+#define        POLYGONTYPE     3
+#define        MULTIPOINTTYPE  4
+#define        MULTILINETYPE   5
+#define        MULTIPOLYGONTYPE        6
+#define        COLLECTIONTYPE  7
+#define        BBOXONLYTYPE    99
+
+/*
+ * Norman Vine found this problem for compiling under cygwin
+ * it defines BYTE_ORDER and LITTLE_ENDIAN
+ */
+
+#if defined(__CYGWIN__) || defined(__MINGW32__)
+#include <sys/param.h>       // FOR ENDIAN DEFINES
+#endif
+
+
+/*
+ * standard definition of an ellipsoid (what wkt calls a spheroid)
+ *    f = (a-b)/a
+ *    e_sq = (a*a - b*b)/(a*a)
+ *    b = a - fa
+ */
+
+typedef struct
+{
+       double  a;      //semimajor axis
+       double  b;      //semiminor axis
+       double  f;      //flattening
+       double  e;      //eccentricity (first)
+       double  e_sq; //eccentricity (first), squared
+       char            name[20]; //name of ellipse
+} SPHEROID;
+
+
+/*---------------------------------------------------------------------
+ * POINT3D - (x,y,z)
+ *            Base type for all geometries
+ *               Also used for POINT type and MULTIPOINT type
+ *-------------------------------------------------------------------*/
+typedef struct
+{
+       double          x,y,z;  //for lat/long   x=long, y=lat
+} POINT3D;
+
+/*---------------------------------------------------------------------
+ * BOX3D       - Specified by two corner points, which are
+ *              sorted to save calculation time later.
+ *
+ *           LLB -- lower right bottom point (ie. South West, Z low)
+ *           URT -- upper right top point    (ie. North East, Z high)
+ *
+ *           this is the long diagonal
+ *
+ * example:
+ *   BOX([0.0,0.0,0.0],[10.0,10.0,10.0])
+ *
+ *     NOTE:  You CAN make columns in your database of this type
+ *              IT IS NOT A TRUE GEOMETRY!
+ *-------------------------------------------------------------------*/
+typedef struct
+{
+       POINT3D         LLB,URT; /* corner POINT3Ds on long diagonal */
+} BOX3D;
+
+
+
+
+/*---------------------------------------------------------------------
+ * LINE  - set of points (directed arcs)
+ *             P1->P2->P3->P4...->Pn
+ *-------------------------------------------------------------------*/
+typedef struct
+{
+       int32   npoints; // how many points in the line
+       int32   junk;      // double-word alignment
+       POINT3D         points[1]; // array of actual points
+} LINE3D;
+
+/*---------------------------------------------------------------------
+ * POLYGON  - set of closed rings.  First ring in outer boundary
+ *                                             other rings are "holes"
+ *
+ * NOTE: this is acually stored a bit differently.  It is more like:
+ *             int32 nrings
+ *             char    filler[]
+ *
+ *  where the 1st 4 byes of filler[] is npoints[0] the next 4 bytes
+ *  are npoints[1], etc...
+ *
+ *  points[0] is either at filler[ 4 * nrings]
+ *            or at filler [ 4* nrings + 4]
+ *               Which ever one is double-word-aligned
+ *-------------------------------------------------------------------*/
+typedef struct
+{
+       int32   nrings;  // how many rings in this polygon
+       int32           npoints[1]; //how many points in each ring
+       /* could be 4 byes of filler here to make sure points[] is
+         double-word aligned*/
+       POINT3D         points[1]; // array of actual points
+} POLYGON3D;
+
+
+/*---------------------------------------------------------------------
+ * Geometry - all columns in postgres are of this type
+ *
+ *             Geometries are collections of simple geometry types
+ *             (point, line, polygon).
+ *
+ *             A Point is a geometry with a single point in it.
+ *             A MultiPoint is a geometry with a list of 'point' in it.
+ *             A Line is a geometry with a single line in it.
+ *             A MultiLine is a geometry with a list of 'line' in it.
+ *             A Polygon is a geometry with a single polygon in it.
+ *             A MultiPolygon is a geometry with a list of 'polygon' in it.
+ *             A Collection is a geometry with a (mixed) list of
+ *                             point, line, and polygon.
+ *
+ *             The bvol is the bounding volume of all the subobjects.
+ *
+ *             is3d is true if the original data was sent in a 3d data.
+ *             2d data is 3d data with z=0.0.
+ *
+ *     'type' has values:
+ *             Point                   -> POINTTYPE
+ *             MultiPoint              -> MULTIPOINTTYPE
+ *             Line                    -> LINETYPE
+ *             MultiLine               -> MULTILINETYPE
+ *             Polygon         -> POLYGONTYPE
+ *             MultiPolygon    -> MULTIPOLYGONTYPE
+ *             Collection              -> COLLECTIOMTYPE
+ *
+ *     'objType' has values:
+ *             Point                   -> POINTTYPE
+ *             Line                    -> LINETYPE
+ *             Polygon         -> POLYGONTYPE
+ *
+ *     'objOffset' is an offset (in bytes) into this structure of where
+ *     the subobject is defined.  THESE ARE ALWAYS DOUBLE-WORD ALIGNED.
+ *
+ *
+ *     In reality the structure looks like:
+ *             int32   size;
+ *             int32           type;
+ *             bool            is3d;
+ *             <there is almost certainly some type of padding here>
+ *             BOX3D           bvol;
+ *             int32           nobjs;
+ *             char            data[...];
+ *
+ *     AND:
+ *             &objType[0] = &data[0]
+ *             &objType[1] = &data[4]
+ *                     ...
+ *             &obgOffset[0] = &data[ 4* nobjs]
+ *             &obgOffset[1] = &data[ 4* nobjs + 4]
+ *                     ...
+ *             &objData[0] = &GEOMETRY + objOffset[0]  //always double-word aligned
+ *             &objData[1] = &GEOMETRY + objOffset[1]  //always double-word aligned
+ *                     ...
+ *
+ *     ALL GEOMETRY COLUMNS IN YOUR DATABASE SHOULD BE OF THIS TYPE
+ *-------------------------------------------------------------------*/
+typedef struct
+{
+       int32           size;           // postgres variable-length type requirement
+       int32           SRID;           // spatial reference system id
+       double  offsetX;        // for precision grid (future improvement)
+       double  offsetY;        // for precision grid (future improvement)
+       double  scale;  // for precision grid (future improvement)
+       int32           type;           // this type of geometry
+       bool            is3d;           // true if the points are 3d (only for output)
+       BOX3D           bvol;           // bounding volume of all the geo objects
+       int32           nobjs;  // how many sub-objects in this object
+       int32           objType[1];     // type of object
+       int32           objOffset[1];// offset (in bytes) into this structure where
+                                        // the object is located
+       char            objData[1];  // store for actual objects
+
+} GEOMETRY;
+
+
+typedef struct chiptag
+{
+       int     size; //unused (for use by postgresql)
+
+       int     endian_hint;  // the number 1 in the endian of this datastruct
+
+       BOX3D    bvol;
+       int      SRID;
+       char     future[4];
+       float  factor;           //usually 1.0.  Integer values are multiplied by this number
+                              //to get the actual height value (for sub-meter accuracy
+                                       //height data).
+
+       int     datatype;        // 1 = float32, 5 = 24bit integer, 6 = 16bit integer (short)
+                                // 101 = float32 (NDR), 105 = 24bit integer (NDR), 106=16bit int (NDR)
+       int     height;
+       int     width;
+       int   compression;      //# 0 = no compression, 1 = differencer
+                                                               //     0x80 = new value
+                                                               //     0x7F = nodata
+
+               // this is provided for convenience, it should be set to
+               //  sizeof(chip) bytes into the struct because the serialized form is:
+               //    <header><data>
+               // NULL when serialized
+       void  *data;            // data[0] = bottm left, data[width] = 1st pixel, 2nd row (uncompressed)
+} CHIP;
+
+
+//for GiST indexing
+
+//This is the BOX type from geo_decls.h
+// Its included here for the GiST index.
+//  Originally, we used BOXONLYTYPE geometries as our keys, but after
+//   Oleg and teodor (http://www.sai.msu.su/~megera/postgres/gist/)
+//   have released a more generic rtree/gist index for geo_decls.h polygon
+//   type.  I am using a slightly modified version of this, so
+//   it will be easier to maintain.
+//
+//   Their indexing is based on the BOX object, so we include it here.
+
+
+// ONLY FOR INDEXING
+typedef struct geomkey {
+       int32 size; /* size in varlena terms */
+       BOX     key;
+       int32   SRID; //spatial reference system identifier
+} GEOMETRYKEY;
+
+
+// WKB structure  -- exactly the same as TEXT
+typedef struct Well_known_bin {
+       int32 size;    // total size of this structure
+       unsigned char  data[1]; //THIS HOLD VARIABLE LENGTH DATA
+} WellKnownBinary;
+
+// --------------------------------------------
+// histogram2d type
+
+// 2d histogram is a bounding box with a bunch of cells in it.
+// The cells will have width (xmax-xmin)/boxesPerSide
+//                 and height(ymax-ymin)/boxesPerSide
+// The first box is the ll corner's box, the send is directly to the right
+//   (row-major).
+//
+//  Size of structure is:
+//        4 (size) + 32 (box) + 4 (boxesPerSide) +
+//                 boxesPerSide*boxesPerSide*4 (data)
+typedef struct histotag
+{
+       int32           size;           // postgres variable-length type requirement
+       int                     boxesPerSide;   //boxesPerSide * boxesPerSide = total boxes in grid
+       double          avgFeatureArea; // average bbox area of features in this histogram
+               // double will be correctly aligned
+       double      xmin,ymin, xmax, ymax; // BOX of area
+       unsigned int value[1]; // variable length # of ints for histogram
+} HISTOGRAM2D;
+
+
+
+//prototypes
+
+
+/* constructors*/
+POLYGON3D      *make_polygon(int nrings, int *pts_per_ring, POINT3D *pts, int npoints, int *size);
+void set_point( POINT3D *pt,double x, double y, double z);
+GEOMETRY       *make_oneobj_geometry(int sub_obj_size, char *sub_obj, int type, bool is3d, int SRID,double scale, double offx,double offy);
+
+
+void print_point(char *result, POINT3D *pt,bool is3d);
+void print_many_points(char *result, POINT3D *pt ,int npoints, bool is3d);
+void swap(double *d1, double *d2);
+int    numb_points_in_list(char        *str);
+bool   parse_points_in_list(char       *str, POINT3D   *points, int32  max_points, bool *is3d);
+bool   parse_points_in_list_exact(char *str, POINT3D   *points, int32  max_points, bool *is3d);
+int    find_outer_list_length(char *str);
+bool   points_per_sublist( char *str, int32 *npoints, int32 max_lists);
+char   *scan_to_same_level(char        *str);
+int objects_inside_point(char *str);
+int objects_inside_line(char *str);
+int objects_inside_polygon(char *str);
+int objects_inside_multipoint(char *str);
+int objects_inside_multiline(char *str);
+int objects_inside_multipolygon(char *str);
+int objects_inside_collection(char *str);
+int objects_inside(char *str);
+bool parse_objects_inside_point(int32 *obj_size,char **objs, int32 *obj_types, int32 nobjs, char *str, int *offset, bool* is3d);
+bool parse_objects_inside_multipoint(int32 *obj_size,char **objs, int32 *obj_types, int32 nobjs, char *str, int *offset, bool* is3d);
+bool parse_objects_inside_line(int32 *obj_size,char **objs, int32 *obj_types, int32 nobjs, char *str, int *offset, bool* is3d);
+bool parse_objects_inside_multiline(int32 *obj_size,char **objs, int32 *obj_types, int32 nobjs, char *str, int *offset, bool* is3d);
+bool parse_objects_inside_polygon(int32 *obj_size,char **objs, int32 *obj_types, int32 nobjs, char *str, int *offset, bool* is3d);
+bool parse_objects_inside_multipolygon(int32 *obj_size,char **objs, int32 *obj_types, int32 nobjs, char *str, int *offset, bool* is3d);
+bool parse_objects(int32 *obj_size,char **objs,int32   *obj_types,int32 nobjs,char *str, int *offset, bool *is3d);
+bool parse_objects_inside_collection(int32 *obj_size,char **objs, int32 *obj_types, int32 nobjs, char *str, int *offset, bool* is3d);
+BOX3D  *bbox_of_point(POINT3D *pt);
+BOX3D  *bbox_of_polygon(POLYGON3D *polygon);
+BOX3D  *bbox_of_line(LINE3D *line);
+BOX3D *union_box3d(BOX3D *a, BOX3D *b);
+BOX3D  *bbox_of_geometry(GEOMETRY *geom);
+GEOMETRY *make_bvol_geometry(BOX3D *box);
+ bool box3d_ov(BOX3D *box1, BOX3D *box2);
+bool is_same_point(POINT3D     *p1, POINT3D    *p2);
+bool is_same_line(LINE3D       *l1, LINE3D     *l2);
+bool is_same_polygon(POLYGON3D *poly1, POLYGON3D       *poly2);
+BOX    *convert_box3d_to_box(BOX3D *in);
+double line_length2d(LINE3D *line);
+double line_length3d(LINE3D *line);
+double polygon_area2d_old(POLYGON3D *poly1);
+double         polygon_perimeter3d(POLYGON3D   *poly1);
+double         polygon_perimeter2d(POLYGON3D   *poly1);
+int PIP( POINT3D *P, POINT3D *V, int n );
+bool point_truely_inside(POINT3D       *point, BOX3D   *box);
+int    compute_outcode( POINT3D *p, BOX3D *box);
+bool lineseg_inside_box( POINT3D *P1, POINT3D *P2, BOX3D *box);
+bool   linestring_inside_box(POINT3D *pts, int npoints, BOX3D *box);
+bool polygon_truely_inside(POLYGON3D   *poly, BOX3D *box);
+bool line_truely_inside( LINE3D *line, BOX3D   *box);
+void   translate_points(POINT3D *pt, int npoints,double x_off, double y_off, double z_off);
+int    size_subobject (char *sub_obj, int type);
+GEOMETRY       *add_to_geometry(GEOMETRY *geom,int sub_obj_size, char *sub_obj, int type);
+LINE3D *make_line(int  npoints, POINT3D        *pts, int       *size);
+//bool point_within_polygon(POINT3D *, POLYGON3D *);
+
+void  swap_char(char *a, char*b);
+void   flip_endian_double(char *dd);
+void           flip_endian_int32(char  *ii);
+
+char   *to_wkb(GEOMETRY *geom, bool flip_endian);
+char   *wkb_multipolygon(POLYGON3D     **polys,int numb_polys,int32 *size, bool flipbytes, char byte_order,bool use3d);
+char   *wkb_polygon(POLYGON3D  *poly,int32 *size, bool flipbytes, char byte_order,bool use3d, char *mem);
+char   *wkb_multiline(LINE3D **lines,int32 *size, int numb_lines, bool flipbytes, char byte_order,bool use3d);
+char   *wkb_line(LINE3D *line,int32 *size, bool flipbytes, char byte_order,bool use3d, char *mem);
+char   *wkb_point(POINT3D *pt,int32 *size, bool flipbytes, char byte_order, bool use3d);
+char   *wkb_multipoint(POINT3D *pt,int32 numb_points,int32 *size, bool flipbytes, char byte_order,bool use3d);
+
+char *to_wkb_collection(GEOMETRY *geom, bool flip_endian, int32 *size);
+char   *to_wkb_sub(GEOMETRY *geom, bool flip_endian, int32 *wkb_size);
+
+
+
+double deltaLongitude(double azimuth, double sigma, double tsm,SPHEROID *sphere);
+double bigA(double u2);
+double bigB(double u2);
+double distance_ellipse(double lat1, double long1,
+                                       double lat2, double long2,
+                                       SPHEROID *sphere);
+double mu2(double azimuth,SPHEROID *sphere);
+
+double length2d_ellipse_linestring(LINE3D      *line, SPHEROID         *sphere);
+double length3d_ellipse_linestring(LINE3D      *line, SPHEROID         *sphere);
+
+double distance_pt_pt(POINT3D *p1, POINT3D *p2);
+double distance_pt_line(POINT3D *p1, LINE3D *l2);
+double distance_pt_poly(POINT3D *p1, POLYGON3D *poly2);
+double distance_line_line(LINE3D *l1, LINE3D *l2);
+double distance_line_poly(LINE3D *l1, POLYGON3D *poly2);
+double distance_poly_poly(POLYGON3D *poly1, POLYGON3D *poly2);
+double distance_pt_seg(POINT3D *p, POINT3D *A, POINT3D *B);
+double distance_seg_seg(POINT3D *A, POINT3D *B, POINT3D *C, POINT3D *D);
+bool point_in_poly(POINT3D *p, POLYGON3D *poly);
+
+POINT3D        *segmentize_ring(POINT3D        *points, double dist, int num_points_in, int *num_points_out);
+
+
+Datum optimistic_overlap(PG_FUNCTION_ARGS);
+
+
+
+unsigned char  parse_hex(char *str);
+void deparse_hex(unsigned char str, unsigned char *result);
+
+
+char *geometry_to_text(GEOMETRY  *geometry);
+
+BOX3D  *parse_box3d(char *str);
+
+int getint(char *c);
+double getdouble(char *c);
+GEOMETRY *WKBtoGeometry(char *WKB, int length, int *bytes_read);
+
+POINT3D *wkb_linearring(char *WKB,char is3d, char flip_endian, int *numbPoints, int *bytes,int bytes_in_stream);
+
+GEOMETRY *makeNullGeometry(int SRID);
+
+void compressType(GEOMETRY *g);
+
+void DP_findsplit(POINT3D *, int, int, int, int *, double *);
+void DP_simplify(POINT3D *, int, POINT3D **, int *, double);
+char *simplify_line3d(LINE3D *, double);
+char *simplify_polygon3d(POLYGON3D *, double);
+char *simplify_point3d(POINT3D *, double);
+
+//exposed to psql
+
+Datum box3d_in(PG_FUNCTION_ARGS);
+Datum box3d_out(PG_FUNCTION_ARGS);
+Datum geometry_in(PG_FUNCTION_ARGS);
+Datum geometry_out(PG_FUNCTION_ARGS);
+
+Datum astext_geometry(PG_FUNCTION_ARGS);
+
+Datum geometry_text(PG_FUNCTION_ARGS);
+
+
+Datum get_bbox_of_geometry(PG_FUNCTION_ARGS);
+Datum get_geometry_of_bbox(PG_FUNCTION_ARGS);
+Datum box3d_same(PG_FUNCTION_ARGS);
+Datum geometry_overleft(PG_FUNCTION_ARGS);
+Datum geometry_left(PG_FUNCTION_ARGS);
+Datum geometry_right(PG_FUNCTION_ARGS);
+Datum geometry_overright(PG_FUNCTION_ARGS);
+Datum geometry_contained(PG_FUNCTION_ARGS);
+Datum geometry_contain(PG_FUNCTION_ARGS);
+Datum geometry_overlap(PG_FUNCTION_ARGS);
+Datum geometry_same(PG_FUNCTION_ARGS);
+Datum box3d_overlap(PG_FUNCTION_ARGS);
+Datum box3d_overleft(PG_FUNCTION_ARGS);
+Datum box3d_right(PG_FUNCTION_ARGS);
+Datum box3d_contain(PG_FUNCTION_ARGS);
+Datum geometry_union(PG_FUNCTION_ARGS);
+Datum geometry_inter(PG_FUNCTION_ARGS);
+Datum geometry_size(PG_FUNCTION_ARGS);
+Datum length3d(PG_FUNCTION_ARGS);
+Datum length2d(PG_FUNCTION_ARGS);
+Datum area2d(PG_FUNCTION_ARGS);
+Datum perimeter3d(PG_FUNCTION_ARGS);
+Datum perimeter2d(PG_FUNCTION_ARGS);
+Datum truly_inside(PG_FUNCTION_ARGS);
+
+Datum geometry_lt(PG_FUNCTION_ARGS);
+Datum geometry_le(PG_FUNCTION_ARGS);
+Datum geometry_eq(PG_FUNCTION_ARGS);
+Datum geometry_gt(PG_FUNCTION_ARGS);
+Datum geometry_ge(PG_FUNCTION_ARGS);
+Datum geometry_cmp(PG_FUNCTION_ARGS);
+
+Datum npoints(PG_FUNCTION_ARGS);
+Datum nrings(PG_FUNCTION_ARGS);
+Datum mem_size(PG_FUNCTION_ARGS);
+Datum summary(PG_FUNCTION_ARGS);
+Datum translate(PG_FUNCTION_ARGS);
+
+Datum asbinary_specify(PG_FUNCTION_ARGS);
+Datum asbinary_simple(PG_FUNCTION_ARGS);
+
+Datum force_2d(PG_FUNCTION_ARGS);
+Datum force_3d(PG_FUNCTION_ARGS);
+Datum force_collection(PG_FUNCTION_ARGS);
+
+Datum combine_bbox(PG_FUNCTION_ARGS);
+
+Datum dimension(PG_FUNCTION_ARGS);
+Datum geometrytype(PG_FUNCTION_ARGS);
+Datum envelope(PG_FUNCTION_ARGS);
+
+Datum x_point(PG_FUNCTION_ARGS);
+Datum y_point(PG_FUNCTION_ARGS);
+Datum z_point(PG_FUNCTION_ARGS);
+
+Datum numpoints_linestring(PG_FUNCTION_ARGS);
+Datum pointn_linestring(PG_FUNCTION_ARGS);
+
+Datum exteriorring_polygon(PG_FUNCTION_ARGS);
+Datum numinteriorrings_polygon(PG_FUNCTION_ARGS);
+Datum interiorringn_polygon(PG_FUNCTION_ARGS);
+
+Datum numgeometries_collection(PG_FUNCTION_ARGS);
+Datum geometryn_collection(PG_FUNCTION_ARGS);
+
+Datum ellipsoid_out(PG_FUNCTION_ARGS);
+Datum ellipsoid_in(PG_FUNCTION_ARGS);
+Datum length_ellipsoid(PG_FUNCTION_ARGS);
+Datum length3d_ellipsoid(PG_FUNCTION_ARGS);
+Datum distance_ellipsoid(PG_FUNCTION_ARGS);
+Datum distance_sphere(PG_FUNCTION_ARGS);
+
+Datum point_inside_circle(PG_FUNCTION_ARGS);
+Datum distance(PG_FUNCTION_ARGS);
+
+Datum expand_bbox(PG_FUNCTION_ARGS);
+Datum expand_geometry(PG_FUNCTION_ARGS);
+Datum srid_geom(PG_FUNCTION_ARGS);
+Datum geometry_from_text(PG_FUNCTION_ARGS);
+
+Datum startpoint(PG_FUNCTION_ARGS);
+
+Datum endpoint(PG_FUNCTION_ARGS);
+Datum isclosed(PG_FUNCTION_ARGS);
+
+Datum centroid(PG_FUNCTION_ARGS);
+
+Datum postgis_gist_sel(PG_FUNCTION_ARGS);
+
+Datum WKB_in(PG_FUNCTION_ARGS);
+Datum WKB_out(PG_FUNCTION_ARGS);
+
+Datum WKB_recv(PG_FUNCTION_ARGS);
+
+Datum CHIP_in(PG_FUNCTION_ARGS);
+Datum CHIP_out(PG_FUNCTION_ARGS);
+Datum CHIP_to_geom(PG_FUNCTION_ARGS);
+Datum srid_chip(PG_FUNCTION_ARGS);
+Datum setsrid_chip(PG_FUNCTION_ARGS);
+Datum width_chip(PG_FUNCTION_ARGS);
+Datum height_chip(PG_FUNCTION_ARGS);
+Datum datatype_chip(PG_FUNCTION_ARGS);
+Datum compression_chip(PG_FUNCTION_ARGS);
+Datum setfactor_chip(PG_FUNCTION_ARGS);
+Datum factor_chip(PG_FUNCTION_ARGS);
+
+
+Datum segmentize(PG_FUNCTION_ARGS);
+
+Datum box3d_xmin(PG_FUNCTION_ARGS);
+Datum box3d_ymin(PG_FUNCTION_ARGS);
+Datum box3d_zmin(PG_FUNCTION_ARGS);
+
+Datum box3d_xmax(PG_FUNCTION_ARGS);
+Datum box3d_ymax(PG_FUNCTION_ARGS);
+Datum box3d_zmax(PG_FUNCTION_ARGS);
+Datum box3dtobox(PG_FUNCTION_ARGS);
+
+
+
+Datum transform_geom(PG_FUNCTION_ARGS);
+
+Datum max_distance(PG_FUNCTION_ARGS);
+Datum geom_accum(PG_FUNCTION_ARGS);
+Datum collect_garray(PG_FUNCTION_ARGS);
+Datum collector(PG_FUNCTION_ARGS);
+
+Datum WKBtoBYTEA(PG_FUNCTION_ARGS);
+
+Datum histogram2d_in(PG_FUNCTION_ARGS);
+Datum histogram2d_out(PG_FUNCTION_ARGS);
+Datum create_histogram2d(PG_FUNCTION_ARGS);
+
+Datum build_histogram2d(PG_FUNCTION_ARGS);
+
+Datum geometry2box(PG_FUNCTION_ARGS);
+
+Datum explode_histogram2d(PG_FUNCTION_ARGS);
+Datum estimate_histogram2d(PG_FUNCTION_ARGS);
+#if USE_VERSION >= 80
+Datum geometry_analyze(PG_FUNCTION_ARGS);
+#endif
+
+Datum postgisgistcostestimate(PG_FUNCTION_ARGS);
+
+Datum geometryfromWKB(PG_FUNCTION_ARGS);
+Datum geometryfromWKB_SRID(PG_FUNCTION_ARGS);
+
+Datum PointfromWKB_SRID(PG_FUNCTION_ARGS);
+Datum LinefromWKB_SRID(PG_FUNCTION_ARGS);
+Datum PolyfromWKB_SRID(PG_FUNCTION_ARGS);
+Datum MPointfromWKB_SRID(PG_FUNCTION_ARGS);
+Datum MLinefromWKB_SRID(PG_FUNCTION_ARGS);
+Datum MPolyfromWKB_SRID(PG_FUNCTION_ARGS);
+Datum GCfromWKB_SRID(PG_FUNCTION_ARGS);
+
+
+Datum geometry_from_text_poly(PG_FUNCTION_ARGS);
+Datum geometry_from_text_mpoly(PG_FUNCTION_ARGS);
+Datum geometry_from_text_point(PG_FUNCTION_ARGS);
+Datum geometry_from_text_mpoint(PG_FUNCTION_ARGS);
+Datum geometry_from_text_line(PG_FUNCTION_ARGS);
+Datum geometry_from_text_mline(PG_FUNCTION_ARGS);
+Datum geometry_from_text_gc(PG_FUNCTION_ARGS);
+Datum isempty(PG_FUNCTION_ARGS);
+Datum simplify(PG_FUNCTION_ARGS);
+Datum line_interpolate_point(PG_FUNCTION_ARGS);
+
+Datum fluffType(PG_FUNCTION_ARGS);
+Datum postgis_uses_stats(PG_FUNCTION_ARGS);
+Datum postgis_lib_version(PG_FUNCTION_ARGS);
+Datum postgis_geos_version(PG_FUNCTION_ARGS);
+Datum postgis_proj_version(PG_FUNCTION_ARGS);
+
+/*--------------------------------------------------------------------
+ * Useful floating point utilities and constants.
+ * from postgres geo_decls.c
+ * EPSILON modified to be more "double" friendly
+ *-------------------------------------------------------------------*/
+
+
+
+// from contrib/cube/cube.c
+
+#if ! defined(__MINGW32__)
+#define max(a,b)               ((a) >  (b) ? (a) : (b))
+#define min(a,b)               ((a) <= (b) ? (a) : (b))
+#endif
+#define abs(a)                 ((a) <  (0) ? (-a) : (a))
+
+
diff --git a/hwgeom/postgis.sql.in b/hwgeom/postgis.sql.in
new file mode 100644 (file)
index 0000000..c6fb431
--- /dev/null
@@ -0,0 +1,2801 @@
+-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+-- 
+-- $Id$
+--
+-- PostGIS - Spatial Types for PostgreSQL
+-- http://postgis.refractions.net
+-- Copyright 2001-2003 Refractions Research Inc.
+--
+-- This is free software; you can redistribute and/or modify it under
+-- the terms of the GNU General Public Licence. See the COPYING file.
+--  
+-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+#if USE_VERSION > 71
+#define CREATEFUNCTION CREATE OR REPLACE FUNCTION
+#else
+#define CREATEFUNCTION CREATE FUNCTION
+#endif
+
+BEGIN TRANSACTION;
+
+-- You might have to define the PL/PgSQL language usually done with the
+-- changelang script.
+
+-- Here's some hokey code to test to see if PL/PgSQL is installed
+-- if it is, you get a message "PL/PgSQL is installed" 
+-- otherwise it will give a big error message.
+
+(select 'PL/PgSQL is installed.' as message from pg_language where lanname='plpgsql') union (select 'You must install PL/PgSQL before running this SQL file,\nor you will get an error. To install PL/PgSQL run:\n\tcreatelang plpgsql <dbname>'::text as message) order by message limit 1;
+
+
+-------------------------------------------------------------------
+--  HISTOGRAM2D TYPE
+-------------------------------------------------------------------
+
+#if USE_VERSION < 73
+# define HISTOGRAM_IN_REP opaque
+# define HISTOGRAM_OUT_REP opaque
+#else
+# define HISTOGRAM_IN_REP histogram2d
+# define HISTOGRAM_OUT_REP cstring
+#endif
+
+CREATEFUNCTION histogram2d_in(HISTOGRAM_OUT_REP)
+       RETURNS HISTOGRAM_IN_REP
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION histogram2d_out(HISTOGRAM_IN_REP)
+       RETURNS HISTOGRAM_OUT_REP
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATE TYPE histogram2d (
+       alignment = double,
+       internallength = variable,
+       input = histogram2d_in,
+       output = histogram2d_out,
+       storage = main
+);
+
+-------------------------------------------------------------------
+--  BOX3D TYPE
+-------------------------------------------------------------------
+
+#if USE_VERSION < 73
+# define BOX3D_IN_REP opaque
+# define BOX3D_OUT_REP opaque
+#else
+# define BOX3D_IN_REP box3d
+# define BOX3D_OUT_REP cstring
+#endif
+
+CREATEFUNCTION box3d_in(BOX3D_OUT_REP)
+       RETURNS BOX3D_IN_REP
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION box3d_out(BOX3D_IN_REP)
+       RETURNS BOX3D_OUT_REP
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATE TYPE box3d (
+       alignment = double,
+       internallength = 48,
+       input = box3d_in,
+       output = box3d_out
+);
+
+-------------------------------------------------------------------
+--  SPHEROID TYPE
+-------------------------------------------------------------------
+
+#if USE_VERSION < 73
+# define SPHEROID_IN_REP opaque
+# define SPHEROID_OUT_REP opaque
+#else
+# define SPHEROID_IN_REP spheroid
+# define SPHEROID_OUT_REP cstring
+#endif
+
+CREATEFUNCTION spheroid_in(SPHEROID_OUT_REP)
+       RETURNS SPHEROID_IN_REP
+       AS '@MODULE_FILENAME@','ellipsoid_in'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION spheroid_out(SPHEROID_IN_REP)
+       RETURNS SPHEROID_OUT_REP
+       AS '@MODULE_FILENAME@','ellipsoid_out'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATE TYPE spheroid (
+       alignment = double,
+       internallength = 65,
+       input = spheroid_in,
+       output = spheroid_out
+);
+
+-------------------------------------------------------------------
+--  WKB TYPE
+-------------------------------------------------------------------
+
+#if USE_VERSION < 73
+# define WKB_IN_REP opaque
+# define WKB_OUT_REP opaque
+#else
+# define WKB_IN_REP wkb
+# define WKB_OUT_REP cstring
+#endif
+
+CREATEFUNCTION wkb_in(WKB_OUT_REP)
+       RETURNS WKB_IN_REP
+       AS '@MODULE_FILENAME@','WKB_in'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION wkb_out(WKB_IN_REP)
+       RETURNS WKB_OUT_REP
+       AS '@MODULE_FILENAME@','WKB_out'
+       LANGUAGE 'C' WITH (isstrict);
+
+#if USE_VERSION > 73
+CREATEFUNCTION wkb_recv(internal)
+       RETURNS wkb
+       AS '@MODULE_FILENAME@','WKB_recv'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION wkb_send(wkb)
+       RETURNS bytea
+       AS '@MODULE_FILENAME@','WKBtoBYTEA'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+#endif
+
+CREATE TYPE wkb (
+       internallength = variable,
+       input = wkb_in,
+       output = wkb_out,
+#if USE_VERSION > 73
+       send = wkb_send,
+       receive = wkb_recv,
+#endif
+       storage = extended
+);
+
+-------------------------------------------------------------------
+--  CHIP TYPE
+-------------------------------------------------------------------
+
+#if USE_VERSION < 73
+# define CHIP_IN_REP opaque
+# define CHIP_OUT_REP opaque
+#else
+# define CHIP_IN_REP chip
+# define CHIP_OUT_REP cstring
+#endif
+
+CREATEFUNCTION chip_in(CHIP_OUT_REP)
+       RETURNS CHIP_IN_REP
+       AS '@MODULE_FILENAME@','CHIP_in'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION chip_out(CHIP_IN_REP)
+       RETURNS CHIP_OUT_REP
+       AS '@MODULE_FILENAME@','CHIP_out'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATE TYPE chip (
+       alignment = double,
+       internallength = variable,
+       input = chip_in,
+       output = chip_out,
+       storage = extended
+);
+
+-------------------------------------------------------------------
+--  GEOMETRY TYPE
+-------------------------------------------------------------------
+
+#if USE_VERSION < 73
+# define GEOMETRY_IN_REP opaque
+# define GEOMETRY_OUT_REP opaque
+#else
+# define GEOMETRY_IN_REP geometry
+# define GEOMETRY_OUT_REP cstring
+#endif
+
+CREATEFUNCTION geometry_in(GEOMETRY_OUT_REP)
+       RETURNS GEOMETRY_IN_REP
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION geometry_out(GEOMETRY_IN_REP)
+       RETURNS GEOMETRY_OUT_REP
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict);
+
+#if USE_VERSION >= 80
+CREATEFUNCTION geometry_analyze(internal)
+       RETURNS bool
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict);
+#endif
+
+CREATE TYPE geometry (
+       alignment = double,
+       internallength = variable,
+       input = geometry_in,
+       output = geometry_out,
+#if USE_VERSION >= 80
+       analyze = geometry_analyze,
+#endif
+       storage = main
+);
+
+
+-------------------------------------------------------------------
+-- Workaround for old user defined variable length datatype 
+-- default value bug. Should not be necessary > 7.2
+-------------------------------------------------------------------
+#if USE_VERSION <= 72
+UPDATE pg_type SET typdefault = NULL WHERE typname = 'wkb';
+UPDATE pg_type SET typdefault = NULL WHERE typname = 'geometry';
+UPDATE pg_type SET typdefault = NULL WHERE typname = 'histogram2d';
+#endif
+
+
+
+-------------------------------------------------------------------
+-- GiST Selectivity Function
+-------------------------------------------------------------------
+#if USE_VERSION == 71
+CREATEFUNCTION postgis_gist_sel(oid, oid, int2, opaque, int4)
+#elif USE_VERSION == 72
+CREATEFUNCTION postgis_gist_sel(opaque, oid,  opaque, int4)
+#else
+CREATEFUNCTION postgis_gist_sel (internal, oid, internal, int4)
+#endif
+       RETURNS float8
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C';
+
+-------------------------------------------------------------------
+-- SPATIAL_REF_SYS
+-------------------------------------------------------------------
+CREATE TABLE spatial_ref_sys (
+        srid integer not null primary key,
+        auth_name varchar(256), 
+        auth_srid integer, 
+        srtext varchar(2048),
+        proj4text varchar(2048) 
+);
+
+-------------------------------------------------------------------
+-- GEOMETRY_COLUMNS
+-------------------------------------------------------------------
+CREATE TABLE geometry_columns (
+       f_table_catalog varchar(256) not null,
+       f_table_schema varchar(256) not null,
+       f_table_name varchar(256) not null,
+       f_geometry_column varchar(256) not null,
+       coord_dimension integer not null,
+       srid integer not null,
+       type varchar(30) not null,
+#if USE_VERSION < 80
+       attrelid oid,
+       varattnum int,
+       stats histogram2d,
+#endif
+       CONSTRAINT geometry_columns_pk primary key ( 
+               f_table_catalog, 
+               f_table_schema, 
+               f_table_name, 
+               f_geometry_column ) );
+
+-----------------------------------------------------------------------
+-- POSTGIS_VERSION()
+-----------------------------------------------------------------------
+
+CREATEFUNCTION postgis_version() RETURNS text
+AS 'SELECT \'@POSTGIS_VERSION@\'::text AS version'
+LANGUAGE 'sql';
+
+CREATEFUNCTION postgis_lib_version() RETURNS text
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C';
+
+CREATEFUNCTION postgis_geos_version() RETURNS text
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C';
+
+CREATEFUNCTION postgis_proj_version() RETURNS text
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C';
+
+CREATEFUNCTION postgis_scripts_installed() RETURNS text
+AS 'SELECT \'@POSTGIS_SCRIPTS_VERSION@\'::text AS version'
+LANGUAGE 'sql';
+
+CREATEFUNCTION postgis_scripts_released() RETURNS text
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C';
+
+CREATEFUNCTION postgis_uses_stats() RETURNS bool
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C';
+
+CREATEFUNCTION postgis_full_version() RETURNS text
+AS '
+DECLARE
+       libver text;
+       projver text;
+       geosver text;
+       usestats bool;
+       dbproc text;
+       relproc text;
+       fullver text;
+BEGIN
+       SELECT postgis_lib_version() INTO libver;
+       SELECT postgis_proj_version() INTO projver;
+       SELECT postgis_geos_version() INTO geosver;
+       SELECT postgis_uses_stats() INTO usestats;
+       SELECT postgis_scripts_installed() INTO dbproc;
+       SELECT postgis_scripts_released() INTO relproc;
+
+       fullver = \'POSTGIS="\' || libver || \'"\';
+
+       IF  geosver IS NOT NULL THEN
+               fullver = fullver || \' GEOS="\' || geosver || \'"\';
+       END IF;
+
+       IF  projver IS NOT NULL THEN
+               fullver = fullver || \' PROJ="\' || projver || \'"\';
+       END IF;
+
+       IF usestats THEN
+               fullver = fullver || \' USE_STATS\';
+       END IF;
+
+       fullver = fullver || \' DBPROC="\' || dbproc || \'"\';
+       fullver = fullver || \' RELPROC="\' || relproc || \'"\';
+
+       IF dbproc != relproc THEN
+               fullver = fullver || \' (needs proc upgrade)\';
+       END IF;
+
+       RETURN fullver;
+END
+' LANGUAGE 'plpgsql';
+
+-----------------------------------------------------------------------
+-- FIND_SRID( <schema>, <table>, <geom col> )
+-----------------------------------------------------------------------
+CREATEFUNCTION find_srid(varchar,varchar,varchar) RETURNS int4 AS
+'DECLARE
+   schem text;
+   tabl text;
+   sr int4;
+BEGIN
+   IF $1 IS NULL THEN
+      RAISE EXCEPTION ''find_srid() - schema is NULL!'';
+   END IF;
+   IF $2 IS NULL THEN
+      RAISE EXCEPTION ''find_srid() - table name is NULL!'';
+   END IF;
+   IF $3 IS NULL THEN
+      RAISE EXCEPTION ''find_srid() - column name is NULL!'';
+   END IF;
+   schem = $1;
+   tabl = $2;
+-- if the table contains a . and the schema is empty
+-- split the table into a schema and a table
+-- otherwise drop through to default behavior
+   IF ( schem = '''' and tabl LIKE ''%.%'' ) THEN
+     schem = substr(tabl,1,strpos(tabl,''.'')-1);
+     tabl = substr(tabl,length(schem)+2);
+   ELSE
+     schem = schem || ''%'';
+   END IF;
+
+   select SRID into sr from geometry_columns where f_table_schema like schem and f_table_name = tabl and f_geometry_column = $3;
+   IF NOT FOUND THEN
+       RAISE EXCEPTION ''find_srid() - couldnt find the corresponding SRID - is the geometry registered in the GEOMETRY_COLUMNS table?  Is there an uppercase/lowercase missmatch?'';
+   END IF;
+  return sr;
+END;
+'
+LANGUAGE 'plpgsql' WITH (iscachable); 
+
+-----------------------------------------------------------------------
+-- GET_PROJ4_FROM_SRID( <srid> )
+-----------------------------------------------------------------------
+CREATEFUNCTION get_proj4_from_srid(integer) RETURNS text AS
+'SELECT proj4text::text FROM spatial_ref_sys WHERE srid= $1' 
+LANGUAGE 'sql' WITH (iscachable,isstrict);
+
+
+-----------------------------------------------------------------------
+-- RENAME_GEOMETRY_TABLE_CONSTRAINTS()
+-----------------------------------------------------------------------
+-- This function renames geometrytype and srid constraints
+-- applied to spatial tables by old AddGeometryColumn to
+-- new meaningful name 'enforce_geotype_<geomcolname>'
+-- and 'enforce_srid_<geomcolname>'
+-- Needs to be called only when upgrading from postgis < 0.8.3
+-----------------------------------------------------------------------
+CREATEFUNCTION rename_geometry_table_constraints() RETURNS text
+AS 
+'
+UPDATE pg_constraint 
+       SET conname = textcat(''enforce_geotype_'', a.attname)
+       FROM pg_attribute a
+       WHERE
+               a.attrelid = conrelid
+               AND a.attnum = conkey[1]
+               AND consrc LIKE ''((geometrytype(%) = %'';
+
+UPDATE pg_constraint
+       SET conname = textcat(''enforce_srid_'', a.attname)
+       FROM pg_attribute a
+       WHERE
+               a.attrelid = conrelid
+               AND a.attnum = conkey[1]
+               AND consrc LIKE ''(srid(% = %)'';
+
+SELECT ''spatial table constraints renamed''::text;
+
+' LANGUAGE 'SQL';
+
+-----------------------------------------------------------------------
+-- FIX_GEOMETRY_COLUMNS() 
+-----------------------------------------------------------------------
+-- This function will:
+--
+--     o try to fix the schema of records with an invalid one
+--             (for PG>=73)
+--
+--     o link records to system tables through attrelid and varattnum
+--             (for PG<80)
+--
+--     o delete all records for which no linking was possible
+--             (for PG<80)
+--     
+-- 
+-----------------------------------------------------------------------
+CREATEFUNCTION fix_geometry_columns() RETURNS text
+AS 
+'
+DECLARE
+       result text;
+       linked integer;
+       deleted integer;
+#if USE_VERSION >= 73
+       foundschema integer;
+#endif
+BEGIN
+
+#if USE_VERSION >= 73
+       -- Since 7.3 schema support has been added.
+       -- Previous postgis versions used to put the database name in
+       -- the schema column. This needs to be fixed, so we try to 
+       -- set the correct schema for each geometry_colums record
+       -- looking at table, column, type and srid.
+       UPDATE geometry_columns SET f_table_schema = n.nspname
+               FROM pg_namespace n, pg_class c, pg_attribute a,
+                       pg_constraint sridcheck, pg_constraint typecheck
+                WHERE ( f_table_schema is NULL
+               OR f_table_schema = ''''
+                OR f_table_schema NOT IN (
+                        SELECT nspname::varchar
+                        FROM pg_namespace nn, pg_class cc, pg_attribute aa
+                        WHERE cc.relnamespace = nn.oid
+                        AND cc.relname = f_table_name::name
+                        AND aa.attrelid = cc.oid
+                        AND aa.attname = f_geometry_column::name))
+                AND f_table_name::name = c.relname
+                AND c.oid = a.attrelid
+                AND c.relnamespace = n.oid
+                AND f_geometry_column::name = a.attname
+                AND sridcheck.conrelid = c.oid
+                --AND sridcheck.conname = ''$1''
+               AND sridcheck.consrc LIKE ''(srid(% = %)''
+                AND typecheck.conrelid = c.oid
+                --AND typecheck.conname = ''$2''
+               AND typecheck.consrc LIKE
+       ''((geometrytype(%) = ''''%''''::text) OR (% IS NULL))''
+                AND sridcheck.consrc ~ textcat('' = '', srid::text)
+                AND typecheck.consrc ~ textcat('' = '''''', type::text)
+                AND NOT EXISTS (
+                        SELECT oid FROM geometry_columns gc
+                        WHERE c.relname::varchar = gc.f_table_name
+#if USE_VERSION >= 73
+                        AND n.nspname::varchar = gc.f_table_schema
+#endif
+                        AND a.attname::varchar = gc.f_geometry_column
+                );
+
+       GET DIAGNOSTICS foundschema = ROW_COUNT;
+#endif
+
+#if USE_VERSION >= 80
+       -- no linkage to system table needed
+       return ''fixed:''||foundschema::text;
+#endif
+
+       -- fix linking to system tables
+       UPDATE geometry_columns SET
+               attrelid = NULL,
+               varattnum = NULL,
+               stats = NULL;
+
+       UPDATE geometry_columns SET
+               attrelid = c.oid,
+               varattnum = a.attnum
+#if USE_VERSION >= 73
+               FROM pg_class c, pg_attribute a, pg_namespace n
+               WHERE n.nspname = f_table_schema::name
+               AND c.relname = f_table_name::name
+               AND c.relnamespace = n.oid
+#else // USE_VERSION < 73 
+               FROM pg_class c, pg_attribute a
+               WHERE c.relname = f_table_name::name
+#endif
+               AND a.attname = f_geometry_column::name
+               AND a.attrelid = c.oid;
+       
+       GET DIAGNOSTICS linked = ROW_COUNT;
+
+       -- remove stale records
+       DELETE FROM geometry_columns WHERE attrelid IS NULL;
+
+       GET DIAGNOSTICS deleted = ROW_COUNT;
+
+       result = 
+#if USE_VERSION >= 73
+               ''fixed:'' || foundschema::text ||
+#endif
+               '' linked:'' || linked::text || 
+               '' deleted:'' || deleted::text;
+
+       return result;
+
+END;
+'
+LANGUAGE 'plpgsql' ;
+
+-----------------------------------------------------------------------
+-- PROBE_GEOMETRY_COLUMNS() 
+-----------------------------------------------------------------------
+-- Fill the geometry_columns table with values probed from the system
+-- catalogues. 3d flag can not be probed, it defaults to 2
+--
+-- Note that bogus records already in geometry_columns are not
+-- overridden (a check for schema.table.column is performed), so
+-- to have a fresh probe backup your geometry_column, delete from
+-- it and probe.
+-----------------------------------------------------------------------
+CREATEFUNCTION probe_geometry_columns() RETURNS text AS
+'
+DECLARE
+       inserted integer;
+       oldcount integer;
+       probed integer;
+       stale integer;
+BEGIN
+
+       SELECT count(*) INTO oldcount FROM geometry_columns;
+
+       SELECT count(*) INTO probed
+               FROM pg_class c, pg_attribute a, pg_type t, 
+#if USE_VERSION >= 73
+                       pg_namespace n,
+#endif
+                       pg_constraint sridcheck, pg_constraint typecheck
+               WHERE t.typname = ''geometry''
+               AND a.atttypid = t.oid
+               AND a.attrelid = c.oid
+#if USE_VERSION >= 73
+               AND c.relnamespace = n.oid
+               AND sridcheck.connamespace = n.oid
+               AND typecheck.connamespace = n.oid
+#endif
+               AND sridcheck.conrelid = c.oid
+               --AND sridcheck.conname = ''$1''
+               AND sridcheck.consrc LIKE ''(srid(% = %)''
+               AND typecheck.conrelid = c.oid
+               --AND typecheck.conname = ''$2'';
+               AND typecheck.consrc LIKE
+       ''((geometrytype(%) = ''''%''''::text) OR (% IS NULL))''
+               ;
+
+       INSERT INTO geometry_columns SELECT
+               ''''::varchar as f_table_catalogue,
+#if USE_VERSION >= 73
+               n.nspname::varchar as f_table_schema,
+#else
+               ''''::varchar as f_table_schema,
+#endif
+               c.relname::varchar as f_table_name,
+               a.attname::varchar as f_geometry_column,
+               2 as coord_dimension,
+               trim(both  '' =)'' from substr(sridcheck.consrc,
+                       strpos(sridcheck.consrc, ''='')))::integer as srid,
+               trim(both '' =)'''''' from substr(typecheck.consrc, 
+                       strpos(typecheck.consrc, ''=''),
+                       strpos(typecheck.consrc, ''::'')-
+                       strpos(typecheck.consrc, ''='')
+                       ))::varchar as type,
+#if USE_VERSION < 80
+               a.attrelid,
+               a.attnum as varattnum,
+               null::histogram2d as stats
+#endif
+               FROM pg_class c, pg_attribute a, pg_type t, 
+#if USE_VERSION >= 73
+                       pg_namespace n,
+#endif
+                       pg_constraint sridcheck, pg_constraint typecheck
+               WHERE t.typname = ''geometry''
+               AND a.atttypid = t.oid
+               AND a.attrelid = c.oid
+#if USE_VERSION >= 73
+               AND c.relnamespace = n.oid
+               AND sridcheck.connamespace = n.oid
+               AND typecheck.connamespace = n.oid
+#endif
+               AND sridcheck.conrelid = c.oid
+               --AND sridcheck.conname = ''$1''
+               AND sridcheck.consrc LIKE ''(srid(% = %)''
+               AND typecheck.conrelid = c.oid
+               --AND typecheck.conname = ''$2''
+               AND typecheck.consrc LIKE
+       ''((geometrytype(%) = ''''%''''::text) OR (% IS NULL))''
+
+                AND NOT EXISTS (
+                        SELECT oid FROM geometry_columns gc
+                        WHERE c.relname::varchar = gc.f_table_name
+#if USE_VERSION >= 73
+                        AND n.nspname::varchar = gc.f_table_schema
+#endif
+                        AND a.attname::varchar = gc.f_geometry_column
+                );
+
+       GET DIAGNOSTICS inserted = ROW_COUNT;
+
+       IF oldcount > probed THEN
+               stale = oldcount-probed;
+       ELSE
+               stale = 0;
+       END IF;
+
+        RETURN ''probed:''||probed||
+               '' inserted:''||inserted||
+               '' conflicts:''||probed-inserted||
+               '' stale:''||stale;
+END
+
+' LANGUAGE 'plpgsql';
+
+-----------------------------------------------------------------------
+-- FIND_EXTENT( <schema name>, <table name>, <column name> )
+-----------------------------------------------------------------------
+CREATEFUNCTION find_extent(text,text,text) RETURNS box3d AS
+'
+DECLARE
+       schemaname alias for $1;
+       tablename alias for $2;
+       columnname alias for $3;
+       okay boolean;
+ myrec RECORD;
+
+BEGIN
+       FOR myrec IN EXECUTE ''SELECT extent("''||columnname||''") FROM "''||schemaname||''"."''||tablename||''"'' LOOP
+               return myrec.extent;
+       END LOOP; 
+END;
+'
+LANGUAGE 'plpgsql' WITH (isstrict);
+
+-----------------------------------------------------------------------
+-- FIND_EXTENT( <table name>, <column name> )
+-----------------------------------------------------------------------
+CREATEFUNCTION find_extent(text,text) RETURNS box3d AS
+'
+DECLARE
+       tablename alias for $1;
+       columnname alias for $2;
+       okay boolean;
+ myrec RECORD;
+
+BEGIN
+       FOR myrec IN EXECUTE ''SELECT extent("''||columnname||''") FROM "''||tablename||''"'' LOOP
+               return myrec.extent;
+       END LOOP; 
+END;
+'
+LANGUAGE 'plpgsql' WITH (isstrict);
+
+
+-----------------------------------------------------------------------
+-- TRANSFORM ( <geometry>, <srid> )
+-----------------------------------------------------------------------
+--
+-- Test:
+--
+-- trans=# select * from spatial_ref_sys ;
+--
+--  srid |   auth_name   | auth_srid | srtext | proj4text 
+-- ------+---------------+-----------+--------+--------------------------------------------------------------------------
+--     1 | latlong WGS84 |         1 |        | +proj=longlat +datum=WGS84
+--     2 | BC albers     |         2 |        | proj=aea ellps=GRS80 lon_0=-126 lat_0=45 lat_1=50 lat_2=58.5 x_0=1000000
+--
+-- select transform( 'SRID=1;POINT(-120.8 50.3)', 2);
+--      -> 'SRID=2;POINT(1370033.37046971 600755.810968684)'
+--
+-----------------------------------------------------------------------
+CREATEFUNCTION transform_geometry(geometry,text,text,int)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','transform_geom'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION transform(geometry,integer) RETURNS geometry AS
+'BEGIN
+ RETURN transform_geometry( $1 , get_proj4_from_srid(SRID( $1 ) ), get_proj4_from_srid( $2 ), $2 );
+ END;'
+LANGUAGE 'plpgsql' WITH (iscachable,isstrict);
+
+
+
+-----------------------------------------------------------------------
+-- COMMON FUNCTIONS
+-----------------------------------------------------------------------
+
+CREATEFUNCTION srid(chip)
+       RETURNS int4
+       AS '@MODULE_FILENAME@','srid_chip'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION height(chip)
+       RETURNS int4
+       AS '@MODULE_FILENAME@','height_chip'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION factor(chip)
+       RETURNS FLOAT4
+       AS '@MODULE_FILENAME@','factor_chip'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION width(chip)
+       RETURNS int4
+       AS '@MODULE_FILENAME@','width_chip'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION datatype(chip)
+       RETURNS int4
+       AS '@MODULE_FILENAME@','datatype_chip'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION compression(chip)
+       RETURNS int4
+       AS '@MODULE_FILENAME@','compression_chip'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION setSRID(chip,int4)
+       RETURNS chip
+       AS '@MODULE_FILENAME@','setsrid_chip'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION setfactor(chip,float4)
+       RETURNS chip
+       AS '@MODULE_FILENAME@','setfactor_chip'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION geometry(CHIP)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','CHIP_to_geom'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION box3d(geometry)
+       RETURNS box3d
+       AS '@MODULE_FILENAME@','get_bbox_of_geometry'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+CREATEFUNCTION box(geometry)
+       RETURNS BOX
+       AS '@MODULE_FILENAME@','geometry2box'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+CREATEFUNCTION geometry(box3d)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','get_geometry_of_bbox'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+CREATEFUNCTION geometry(text)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_text'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+CREATEFUNCTION expand(box3d,float8)
+       RETURNS box3d
+       AS '@MODULE_FILENAME@','expand_bbox'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+CREATEFUNCTION expand(geometry,float8)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','expand_geometry'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+--
+-- Functions for converting to WKB
+--
+
+CREATEFUNCTION asbinary(geometry)
+       RETURNS wkb
+       AS '@MODULE_FILENAME@','asbinary_simple'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+CREATEFUNCTION asbinary(geometry,TEXT)
+       RETURNS wkb
+       AS '@MODULE_FILENAME@','asbinary_specify'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+CREATEFUNCTION bytea(wkb)
+       RETURNS bytea
+       AS '@MODULE_FILENAME@','WKBtoBYTEA'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+       
+CREATEFUNCTION geometry(wkb)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','geometryfromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+       
+CREATEFUNCTION GeomFromWKB(wkb)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','geometryfromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+CREATEFUNCTION GeomFromWKB(wkb,int)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','geometryfromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+       
+CREATEFUNCTION PointFromWKB(wkb,int)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','PointfromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+CREATEFUNCTION PointFromWKB(wkb)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','PointfromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+       
+CREATEFUNCTION LineFromWKB(wkb,int)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','LinefromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+CREATEFUNCTION LineFromWKB(wkb)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','LinefromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+
+CREATEFUNCTION LinestringFromWKB(wkb,int)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','LinefromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+CREATEFUNCTION LinestringFromWKB(wkb)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','LinefromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+       
+CREATEFUNCTION PolyFromWKB(wkb,int)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','PolyfromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+CREATEFUNCTION PolyFromWKB(wkb)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','PolyfromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+       
+CREATEFUNCTION PolygonFromWKB(wkb,int)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','PolyfromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+CREATEFUNCTION PolygonFromWKB(wkb)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','PolyfromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+
+CREATEFUNCTION MPointFromWKB(wkb,int)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','MPointfromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+CREATEFUNCTION MPointFromWKB(wkb)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','MPointfromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+
+CREATEFUNCTION MultiPointFromWKB(wkb,int)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','MPointfromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+CREATEFUNCTION MultiPointFromWKB(wkb)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','MPointfromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+CREATEFUNCTION MultiLineFromWKB(wkb,int)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','MLinefromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+CREATEFUNCTION MultiLineFromWKB(wkb)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','MLinefromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+       
+       
+CREATEFUNCTION MLineFromWKB(wkb,int)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','MLinefromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+CREATEFUNCTION MLineFromWKB(wkb)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','MLinefromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+CREATEFUNCTION MPolyFromWKB(wkb,int)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','MPolyfromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+CREATEFUNCTION MPolyFromWKB(wkb)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','MPolyfromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+       
+CREATEFUNCTION MultiPolyFromWKB(wkb,int)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','MPolyfromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+CREATEFUNCTION MultiPolyFromWKB(wkb)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','MPolyfromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+
+       
+CREATEFUNCTION GeomCollFromWKB(wkb,int)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','GCfromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+
+CREATEFUNCTION GeomCollFromWKB(wkb)
+       RETURNS GEOMETRY
+       AS '@MODULE_FILENAME@','GCfromWKB_SRID'
+       LANGUAGE 'C' WITH (iscachable,isstrict);
+       
+       
+-- CREATEFUNCTION index_thing(geometry)
+-- RETURNS BOOL
+-- AS '@MODULE_FILENAME@'
+-- LANGUAGE 'C' WITH (isstrict);
+
+--
+-- Debugging functions
+--
+
+CREATEFUNCTION npoints(geometry)
+       RETURNS int4
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION nrings(geometry)
+       RETURNS int4
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict) ;
+
+CREATEFUNCTION mem_size(geometry)
+       RETURNS int4
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict);
+
+
+CREATEFUNCTION summary(geometry)
+       RETURNS text
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION translate(geometry,float8,float8,float8)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict) ;
+
+CREATEFUNCTION dimension(geometry)
+       RETURNS int4
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict) ;
+
+CREATEFUNCTION geometrytype(geometry)
+       RETURNS text
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION envelope(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION x(geometry)
+       RETURNS float8
+       AS '@MODULE_FILENAME@','x_point'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION y(geometry)
+       RETURNS float8
+       AS '@MODULE_FILENAME@','y_point'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION z(geometry)
+       RETURNS float8
+       AS '@MODULE_FILENAME@','z_point'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION numpoints(geometry)
+       RETURNS integer
+       AS '@MODULE_FILENAME@','numpoints_linestring'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION pointn(geometry,integer)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','pointn_linestring'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION exteriorring(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','exteriorring_polygon'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION numinteriorrings(geometry)
+       RETURNS integer
+       AS '@MODULE_FILENAME@','numinteriorrings_polygon'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION interiorringn(geometry,integer)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','interiorringn_polygon'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION numgeometries(geometry)
+       RETURNS integer
+       AS '@MODULE_FILENAME@','numgeometries_collection'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION geometryn(geometry,integer)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometryn_collection'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION max_distance(geometry,geometry)
+       RETURNS float8
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION optimistic_overlap(geometry,geometry,FLOAT8)
+       RETURNS BOOL
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION segmentize(geometry,FLOAT8)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION distance(geometry,geometry)
+       RETURNS float8
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION astext(geometry)
+       RETURNS TEXT
+       AS '@MODULE_FILENAME@','astext_geometry'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION srid(geometry)
+       RETURNS int4
+       AS '@MODULE_FILENAME@','srid_geom'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION geometryfromtext(geometry,int4)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+       
+       
+CREATEFUNCTION geometryfromtext(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION geomfromtext(geometry,int4)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION geomfromtext(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION polyfromtext(geometry,int4)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_poly'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION polygonfromtext(geometry,int4)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_poly'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION polygonfromtext(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_poly'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+       
+CREATEFUNCTION mpolyfromtext(geometry,int4)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_mpoly'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION linefromtext(geometry,int4)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_line'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+       
+CREATEFUNCTION mlinefromtext(geometry,int4)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_mline'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION multilinestringfromtext(geometry,int4)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_mline'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION multilinestringfromtext(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_mline'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+       
+CREATEFUNCTION pointfromtext(geometry,int4)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_point'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION mpointfromtext(geometry,int4)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_mpoint'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION multipointfromtext(geometry,int4)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_mpoint'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION multipointfromtext(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_mpoint'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+       
+CREATEFUNCTION geomcollfromtext(geometry,int4)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_gc'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION setSRID(geometry,int4)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION polyfromtext(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_poly'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+
+CREATEFUNCTION mpolyfromtext(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_mpoly'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION multipolygonfromtext(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_mpoly'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION multipolygonfromtext(geometry,int)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_mpoly'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+       
+CREATEFUNCTION linefromtext(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_line'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+       
+CREATEFUNCTION linestringfromtext(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_line'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION linestringfromtext(geometry,int)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_line'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION mlinefromtext(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_mline'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION pointfromtext(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_point'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION mpointfromtext(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_mpoint'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION geomcollfromtext(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geometry_from_text_gc'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+
+CREATEFUNCTION isempty(geometry)
+       RETURNS boolean
+       AS '@MODULE_FILENAME@','isempty'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION issimple(geometry)
+       RETURNS boolean
+       AS '@MODULE_FILENAME@','issimple'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+       
+
+CREATEFUNCTION equals(geometry,geometry)
+       RETURNS boolean
+       AS '@MODULE_FILENAME@','geomequals'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+
+--
+-- Special spheroid functions
+--
+
+CREATEFUNCTION length_spheroid(geometry,spheroid)
+       RETURNS FLOAT8
+       AS '@MODULE_FILENAME@','length_ellipsoid'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION length3d_spheroid(geometry,spheroid)
+       RETURNS FLOAT8
+       AS '@MODULE_FILENAME@','length3d_ellipsoid'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION distance_spheroid(geometry,geometry,spheroid)
+       RETURNS FLOAT8
+       AS '@MODULE_FILENAME@','distance_ellipsoid'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION distance_sphere(geometry,geometry)
+       RETURNS FLOAT8
+       AS '@MODULE_FILENAME@', 'distance_sphere'
+       LANGUAGE 'C' WITH (isstrict);
+
+--
+-- Generic operations
+--
+
+CREATEFUNCTION multi(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','fluffType'
+       LANGUAGE 'C' WITH (isstrict);
+       
+CREATEFUNCTION length3d(geometry)
+       RETURNS FLOAT8
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION length(geometry)
+       RETURNS FLOAT8
+       AS '@MODULE_FILENAME@','length2d'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION area2d(geometry)
+       RETURNS FLOAT8
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION area(geometry)
+       RETURNS FLOAT8
+       AS '@MODULE_FILENAME@','area2d'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION perimeter3d(geometry)
+       RETURNS FLOAT8
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION perimeter(geometry)
+       RETURNS FLOAT8
+       AS '@MODULE_FILENAME@','perimeter2d'
+       LANGUAGE 'C' WITH (isstrict);
+
+---CREATEFUNCTION truly_inside(geometry,geometry)
+---    RETURNS bool
+---    AS '@MODULE_FILENAME@'
+---    LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION point_inside_circle(geometry,float8,float8,float8)
+       RETURNS bool
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION startpoint(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION endpoint(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION isclosed(geometry)
+       RETURNS boolean
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION centroid(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict);
+       
+CREATEFUNCTION isring(geometry)
+       RETURNS boolean
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION pointonsurface(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C' WITH (isstrict);
+       
+
+--
+-- BBox operations
+--
+
+CREATEFUNCTION xmin(box3d)
+       RETURNS FLOAT8
+       AS '@MODULE_FILENAME@','box3d_xmin'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION ymin(box3d)
+       RETURNS FLOAT8
+       AS '@MODULE_FILENAME@','box3d_ymin'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION zmin(box3d)
+       RETURNS FLOAT8
+       AS '@MODULE_FILENAME@','box3d_zmin'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION xmax(box3d)
+       RETURNS FLOAT8
+       AS '@MODULE_FILENAME@','box3d_xmax'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION ymax(box3d)
+       RETURNS FLOAT8
+       AS '@MODULE_FILENAME@','box3d_ymax'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION zmax(box3d)
+       RETURNS FLOAT8
+       AS '@MODULE_FILENAME@','box3d_zmax'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+CREATEFUNCTION box3dtobox(box3d)
+       RETURNS BOX
+       AS '@MODULE_FILENAME@','box3dtobox'
+       LANGUAGE 'C' WITH (isstrict,iscachable);
+
+--
+-- Aggregate functions
+--
+
+CREATEFUNCTION geom_accum (geometry[],geometry)
+       RETURNS geometry[]
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C';
+
+CREATEFUNCTION combine_bbox(box3d,geometry)
+       RETURNS box3d
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C';
+
+CREATE AGGREGATE extent(
+       sfunc = combine_bbox,
+       basetype = geometry,
+       stype = box3d
+       );
+
+CREATEFUNCTION collector(geometry,geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C';
+
+CREATE AGGREGATE memcollect(
+       sfunc = collector,
+       basetype = geometry,
+       stype = geometry
+       );
+
+CREATEFUNCTION collect_garray (geometry[])
+        RETURNS geometry
+        AS '@MODULE_FILENAME@'
+        LANGUAGE 'C';
+
+CREATE AGGREGATE collect (
+       sfunc = geom_accum,
+       basetype = geometry,
+       stype = geometry[],
+       finalfunc = collect_garray
+       );
+
+
+--
+-- Operator definitions
+--
+
+CREATEFUNCTION geometry_overleft(geometry, geometry) 
+       RETURNS bool
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION geometry_overright(geometry, geometry) 
+       RETURNS bool
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION geometry_left(geometry, geometry) 
+       RETURNS bool
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION geometry_right(geometry, geometry) 
+       RETURNS bool
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION geometry_contain(geometry, geometry) 
+       RETURNS bool
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION geometry_contained(geometry, geometry) 
+       RETURNS bool
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION geometry_overlap(geometry, geometry) 
+       RETURNS bool
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION geometry_same(geometry, geometry) 
+       RETURNS bool
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C' WITH (isstrict);
+
+--
+-- Sorting functions
+--
+
+CREATEFUNCTION geometry_lt(geometry, geometry) 
+       RETURNS bool
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION geometry_le(geometry, geometry) 
+       RETURNS bool
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION geometry_gt(geometry, geometry) 
+       RETURNS bool
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION geometry_ge(geometry, geometry) 
+       RETURNS bool
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION geometry_eq(geometry, geometry) 
+       RETURNS bool
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION geometry_cmp(geometry, geometry) 
+       RETURNS integer
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C' WITH (isstrict);
+
+--
+-- Two dimensional to three dimensional forces
+-- 
+
+CREATEFUNCTION force_2d(geometry) 
+       RETURNS geometry
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION force_3d(geometry) 
+       RETURNS geometry
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C' WITH (isstrict);
+
+--
+-- Force collection
+--
+
+CREATEFUNCTION force_collection(geometry) 
+       RETURNS geometry
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C' WITH (isstrict);
+
+-- 
+-- Operator definitions
+--
+
+CREATE OPERATOR << (
+   LEFTARG = GEOMETRY, RIGHTARG = GEOMETRY, PROCEDURE = geometry_left,
+   COMMUTATOR = '>>',
+   RESTRICT = positionsel, JOIN = positionjoinsel
+);
+
+CREATE OPERATOR &< (
+   LEFTARG = GEOMETRY, RIGHTARG = GEOMETRY, PROCEDURE = geometry_overleft,
+   COMMUTATOR = '&>',
+   RESTRICT = positionsel, JOIN = positionjoinsel
+);
+
+CREATE OPERATOR && (
+   LEFTARG = GEOMETRY, RIGHTARG = GEOMETRY, PROCEDURE = geometry_overlap,
+   COMMUTATOR = '&&',
+   RESTRICT = postgis_gist_sel, JOIN = positionjoinsel
+);
+
+CREATE OPERATOR &> (
+   LEFTARG = GEOMETRY, RIGHTARG = GEOMETRY, PROCEDURE = geometry_overright,
+   COMMUTATOR = '&<',
+   RESTRICT = positionsel, JOIN = positionjoinsel
+);
+
+CREATE OPERATOR >> (
+   LEFTARG = GEOMETRY, RIGHTARG = GEOMETRY, PROCEDURE = geometry_right,
+   COMMUTATOR = '<<',
+   RESTRICT = positionsel, JOIN = positionjoinsel
+);
+
+CREATE OPERATOR ~= (
+   LEFTARG = GEOMETRY, RIGHTARG = GEOMETRY, PROCEDURE = geometry_same,
+   COMMUTATOR = '~=', 
+   RESTRICT = eqsel, JOIN = eqjoinsel
+);
+
+CREATE OPERATOR @ (
+   LEFTARG = GEOMETRY, RIGHTARG = GEOMETRY, PROCEDURE = geometry_contained,
+   COMMUTATOR = '~',
+   RESTRICT = contsel, JOIN = contjoinsel
+);
+
+CREATE OPERATOR ~ (
+   LEFTARG = GEOMETRY, RIGHTARG = GEOMETRY, PROCEDURE = geometry_contain,
+   COMMUTATOR = '@',
+   RESTRICT = contsel, JOIN = contjoinsel
+);
+
+--
+-- Sorting operators for Btree
+--
+
+CREATE OPERATOR < (
+   LEFTARG = GEOMETRY, RIGHTARG = GEOMETRY, PROCEDURE = geometry_lt,
+   COMMUTATOR = '>', NEGATOR = '>=',
+   RESTRICT = contsel, JOIN = contjoinsel
+);
+
+CREATE OPERATOR <= (
+   LEFTARG = GEOMETRY, RIGHTARG = GEOMETRY, PROCEDURE = geometry_le,
+   COMMUTATOR = '>=', NEGATOR = '>',
+   RESTRICT = contsel, JOIN = contjoinsel
+);
+
+CREATE OPERATOR = (
+   LEFTARG = GEOMETRY, RIGHTARG = GEOMETRY, PROCEDURE = geometry_eq,
+   COMMUTATOR = '=', -- we might implement a faster negator here
+   RESTRICT = contsel, JOIN = contjoinsel
+);
+
+CREATE OPERATOR >= (
+   LEFTARG = GEOMETRY, RIGHTARG = GEOMETRY, PROCEDURE = geometry_ge,
+   COMMUTATOR = '<=', NEGATOR = '<',
+   RESTRICT = contsel, JOIN = contjoinsel
+);
+CREATE OPERATOR > (
+   LEFTARG = GEOMETRY, RIGHTARG = GEOMETRY, PROCEDURE = geometry_gt,
+   COMMUTATOR = '<', NEGATOR = '<=',
+   RESTRICT = contsel, JOIN = contjoinsel
+);
+
+--
+-- GEOS Functions
+--
+
+
+CREATEFUNCTION intersection(geometry,geometry)
+   RETURNS geometry
+   AS '@MODULE_FILENAME@','intersection'
+   LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION buffer(geometry,float8)
+   RETURNS geometry
+   AS '@MODULE_FILENAME@','buffer'
+   LANGUAGE 'C' WITH (isstrict);
+   
+CREATEFUNCTION convexhull(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','convexhull'
+       LANGUAGE 'C' WITH (isstrict);
+  
+  
+CREATEFUNCTION difference(geometry,geometry)
+       RETURNS geometry
+        AS '@MODULE_FILENAME@','difference'
+       LANGUAGE 'C' WITH (isstrict);
+   
+CREATEFUNCTION boundary(geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','boundary'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION symdifference(geometry,geometry)
+        RETURNS geometry
+        AS '@MODULE_FILENAME@','symdifference'
+   LANGUAGE 'C' WITH (isstrict);
+
+
+CREATEFUNCTION symmetricdifference(geometry,geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','symdifference'
+       LANGUAGE 'C' WITH (isstrict);
+
+
+CREATEFUNCTION GeomUnion(geometry,geometry)
+       RETURNS geometry
+       AS '@MODULE_FILENAME@','geomunion'
+       LANGUAGE 'C' WITH (isstrict);
+
+CREATE AGGREGATE MemGeomUnion (
+       basetype = geometry,
+       sfunc = geomunion,
+       stype = geometry
+       );
+
+CREATEFUNCTION unite_garray (geometry[])
+       RETURNS geometry
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C'; 
+
+CREATE AGGREGATE GeomUnion (
+       sfunc = geom_accum,
+       basetype = geometry,
+       stype = geometry[],
+       finalfunc = unite_garray
+       );
+
+
+CREATEFUNCTION relate(geometry,geometry)
+   RETURNS text
+   AS '@MODULE_FILENAME@','relate_full'
+   LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION relate(geometry,geometry,text)
+   RETURNS boolean
+   AS '@MODULE_FILENAME@','relate_pattern'
+   LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION disjoint(geometry,geometry)
+   RETURNS boolean
+   AS '@MODULE_FILENAME@'
+   LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION touches(geometry,geometry)
+   RETURNS boolean
+   AS '@MODULE_FILENAME@'
+   LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION intersects(geometry,geometry)
+   RETURNS boolean
+   AS '@MODULE_FILENAME@'
+   LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION crosses(geometry,geometry)
+   RETURNS boolean
+   AS '@MODULE_FILENAME@'
+   LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION within(geometry,geometry)
+   RETURNS boolean
+   AS '@MODULE_FILENAME@'
+   LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION contains(geometry,geometry)
+   RETURNS boolean
+   AS '@MODULE_FILENAME@'
+   LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION overlaps(geometry,geometry)
+   RETURNS boolean
+   AS '@MODULE_FILENAME@'
+   LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION isvalid(geometry)
+   RETURNS boolean
+   AS '@MODULE_FILENAME@'
+   LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION geosnoop(geometry)
+   RETURNS geometry
+   AS '@MODULE_FILENAME@', 'GEOSnoop'
+   LANGUAGE 'C' WITH (isstrict);
+   
+
+--
+-- Algorithms
+--
+
+CREATEFUNCTION simplify(geometry, float8)
+   RETURNS geometry
+   AS '@MODULE_FILENAME@'
+   LANGUAGE 'C' WITH (isstrict);
+
+CREATEFUNCTION line_interpolate_point(geometry, float8)
+   RETURNS geometry
+   AS '@MODULE_FILENAME@'
+   LANGUAGE 'C' WITH (isstrict);
+
+-------------------------------------------------------------------
+-- GiST support functions
+-------------------------------------------------------------------
+
+#if USE_VERSION < 73
+#define OPAQUE_TYPE opaque
+#else
+#define OPAQUE_TYPE internal
+#endif
+
+CREATEFUNCTION ggeometry_consistent(OPAQUE_TYPE,geometry,int4) 
+       RETURNS bool 
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C';
+
+CREATEFUNCTION ggeometry_compress(OPAQUE_TYPE) 
+       RETURNS OPAQUE_TYPE 
+       AS '@MODULE_FILENAME@'
+       LANGUAGE 'C';
+
+CREATEFUNCTION rtree_decompress(OPAQUE_TYPE) 
+       RETURNS OPAQUE_TYPE
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C';
+
+#if USE_VERSION == 71
+CREATEFUNCTION ggeometry_penalty(OPAQUE_TYPE,OPAQUE_TYPE,OPAQUE_TYPE) 
+#else
+CREATEFUNCTION gbox_penalty(OPAQUE_TYPE,OPAQUE_TYPE,OPAQUE_TYPE) 
+#endif
+       RETURNS OPAQUE_TYPE 
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C';
+
+#if USE_VERSION == 71
+CREATEFUNCTION ggeometry_picksplit(OPAQUE_TYPE, OPAQUE_TYPE) 
+#else
+CREATEFUNCTION gbox_picksplit(OPAQUE_TYPE, OPAQUE_TYPE) 
+#endif
+       RETURNS OPAQUE_TYPE 
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C';
+
+#if USE_VERSION == 71
+CREATEFUNCTION ggeometry_union(bytea, OPAQUE_TYPE) 
+#else
+CREATEFUNCTION gbox_union(bytea, OPAQUE_TYPE) 
+#endif
+       RETURNS OPAQUE_TYPE 
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C';
+
+#if USE_VERSION == 71
+CREATEFUNCTION ggeometry_same(OPAQUE_TYPE, OPAQUE_TYPE, OPAQUE_TYPE) 
+#else
+CREATEFUNCTION gbox_same(box, box, OPAQUE_TYPE) 
+#endif
+       RETURNS OPAQUE_TYPE 
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C';
+
+-------------------------------------------------------------------
+-- R-Tree support functions
+-------------------------------------------------------------------
+
+CREATEFUNCTION geometry_union(geometry,geometry) 
+       RETURNS geometry 
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C';
+
+CREATEFUNCTION geometry_inter(geometry,geometry) 
+       RETURNS geometry 
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C';
+
+CREATEFUNCTION geometry_size(geometry,opaque) 
+       RETURNS float4 
+       AS '@MODULE_FILENAME@' 
+       LANGUAGE 'C';
+
+
+#if USE_VERSION == 71
+
+--
+-- Create opclass index binding entries.
+--
+
+INSERT INTO pg_opclass (opcname, opcdeftype)
+       SELECT 'gist_geometry_ops', oid
+       FROM pg_type
+       WHERE typname = 'geometry';
+
+SELECT o.oid AS opoid, o.oprname
+       INTO TABLE rt_ops_tmp
+       FROM pg_operator o, pg_type t
+       WHERE o.oprleft = t.oid AND t.typname = 'geometry';
+
+-- box_left
+INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy) 
+       SELECT am.oid, opcl.oid, c.opoid, 1
+       FROM pg_am am, pg_opclass opcl, rt_ops_tmp c
+       WHERE amname = 'gist' AND opcname = 'gist_geometry_ops' 
+               AND c.oprname = '<<';
+
+-- box_overleft
+INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy) 
+       SELECT am.oid, opcl.oid, c.opoid, 2
+       FROM pg_am am, pg_opclass opcl, rt_ops_tmp c
+       WHERE amname = 'gist' AND opcname = 'gist_geometry_ops' 
+               AND c.oprname = '&<';
+
+-- box_overlap
+INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy) 
+       SELECT am.oid, opcl.oid, c.opoid, 3
+       FROM pg_am am, pg_opclass opcl, rt_ops_tmp c
+       WHERE amname = 'gist' AND opcname = 'gist_geometry_ops' 
+               AND c.oprname = '&&';
+
+-- box_overright
+INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy) 
+       SELECT am.oid, opcl.oid, c.opoid, 4
+       FROM pg_am am, pg_opclass opcl, rt_ops_tmp c
+       WHERE amname = 'gist' AND opcname = 'gist_geometry_ops' 
+               AND c.oprname = '&>';
+
+-- box_right
+INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy)
+       SELECT am.oid, opcl.oid, c.opoid, 5
+       FROM pg_am am, pg_opclass opcl, rt_ops_tmp c
+       WHERE amname = 'gist' AND opcname = 'gist_geometry_ops' 
+               AND c.oprname = '>>';
+
+-- box_same
+INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy) 
+       SELECT am.oid, opcl.oid, c.opoid, 6
+       FROM pg_am am, pg_opclass opcl, rt_ops_tmp c
+       WHERE amname = 'gist' AND opcname = 'gist_geometry_ops' 
+               AND c.oprname = '~=';
+
+-- box_contains
+INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy)
+       SELECT am.oid, opcl.oid, c.opoid, 7
+       FROM pg_am am, pg_opclass opcl, rt_ops_tmp c
+       WHERE amname = 'gist' AND opcname = 'gist_geometry_ops' 
+               AND c.oprname = '~';
+
+-- box_contained
+INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy)
+       SELECT am.oid, opcl.oid, c.opoid, 8
+       FROM pg_am am, pg_opclass opcl, rt_ops_tmp c
+       WHERE amname = 'gist' AND opcname = 'gist_geometry_ops' 
+               AND c.oprname = '@';
+
+DROP TABLE rt_ops_tmp;
+
+--
+-- Add the entries to amproc for the support methods.
+-- Note the amprocnum numbers associated with each are specific!
+--
+
+INSERT INTO pg_amproc (amid, amopclaid, amproc, amprocnum)
+       SELECT am.oid, opcl.oid, pro.oid, 1
+       FROM pg_am am, pg_opclass opcl, pg_proc pro
+       WHERE amname = 'gist' AND opcname = 'gist_geometry_ops'
+               AND proname = 'ggeometry_consistent';
+
+INSERT INTO pg_amproc (amid, amopclaid, amproc, amprocnum)
+       SELECT am.oid, opcl.oid, pro.oid, 2
+       FROM pg_am am, pg_opclass opcl, pg_proc pro
+       WHERE amname = 'gist' AND opcname = 'gist_geometry_ops'
+               AND proname = 'ggeometry_union';
+
+INSERT INTO pg_amproc (amid, amopclaid, amproc, amprocnum)
+       SELECT am.oid, opcl.oid, pro.oid, 3
+       FROM pg_am am, pg_opclass opcl, pg_proc pro
+       WHERE amname = 'gist' AND opcname = 'gist_geometry_ops'
+               AND proname = 'ggeometry_compress';
+
+INSERT INTO pg_amproc (amid, amopclaid, amproc, amprocnum)
+       SELECT am.oid, opcl.oid, pro.oid, 4
+       FROM pg_am am, pg_opclass opcl, pg_proc pro
+       WHERE amname = 'gist' AND opcname = 'gist_geometry_ops'
+               AND proname = 'rtree_decompress';
+
+INSERT INTO pg_amproc (amid, amopclaid, amproc, amprocnum)
+       SELECT am.oid, opcl.oid, pro.oid, 5
+       FROM pg_am am, pg_opclass opcl, pg_proc pro
+       WHERE amname = 'gist' AND opcname = 'gist_geometry_ops'
+               AND proname = 'ggeometry_penalty';
+
+INSERT INTO pg_amproc (amid, amopclaid, amproc, amprocnum)
+       SELECT am.oid, opcl.oid, pro.oid, 6
+       FROM pg_am am, pg_opclass opcl, pg_proc pro
+       WHERE amname = 'gist' AND opcname = 'gist_geometry_ops'
+               AND proname = 'ggeometry_picksplit';
+
+INSERT INTO pg_amproc (amid, amopclaid, amproc, amprocnum)
+       SELECT am.oid, opcl.oid, pro.oid, 7
+       FROM pg_am am, pg_opclass opcl, pg_proc pro
+       WHERE amname = 'gist' AND opcname = 'gist_geometry_ops'
+               AND proname = 'ggeometry_same';
+
+#elsif USE_VERSION == 72
+
+--
+-- Create opclass index binding entries.
+--
+
+INSERT INTO pg_opclass (opcamid, opcname, opcintype, opcdefault, opckeytype)
+    VALUES (
+        (SELECT oid FROM pg_am WHERE amname = 'gist'),
+        'gist_geometry_ops',
+        (SELECT oid FROM pg_type WHERE typname = 'geometry'),
+        true,
+        (SELECT oid FROM pg_type WHERE typname = 'box'));
+
+-- drop table rt_ops_tmp;
+
+SELECT o.oid AS opoid, o.oprname
+       INTO TABLE rt_ops_tmp
+       FROM pg_operator o, pg_type t
+       WHERE o.oprleft = t.oid 
+               AND t.typname = 'geometry';
+
+-- poly_left
+INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr) 
+   SELECT opcl.oid, 1, true, c.opoid
+   FROM pg_opclass opcl, rt_ops_tmp c
+   WHERE
+      opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
+      and opcname = 'gist_geometry_ops' 
+      and c.oprname = '<<';
+
+-- poly_overleft
+INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr) 
+   SELECT opcl.oid, 2, true, c.opoid
+   FROM pg_opclass opcl, rt_ops_tmp c
+   WHERE
+      opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
+      and opcname = 'gist_geometry_ops' 
+      and c.oprname = '&<';
+
+-- poly_overlap
+INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr) 
+   SELECT opcl.oid, 3, true, c.opoid
+   FROM pg_opclass opcl, rt_ops_tmp c
+   WHERE
+      opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
+      and opcname = 'gist_geometry_ops' 
+      and c.oprname = '&&';
+
+-- poly_overright
+INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr) 
+   SELECT opcl.oid, 4, true, c.opoid
+   FROM pg_opclass opcl, rt_ops_tmp c
+   WHERE
+      opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
+      and opcname = 'gist_geometry_ops' 
+      and c.oprname = '&>';
+
+-- poly_right
+INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr)
+   SELECT opcl.oid, 5, true, c.opoid
+   FROM pg_opclass opcl, rt_ops_tmp c
+   WHERE
+      opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
+      and opcname = 'gist_geometry_ops' 
+      and c.oprname = '>>';
+
+-- poly_same
+INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr) 
+   SELECT opcl.oid, 6, true, c.opoid
+   FROM pg_opclass opcl, rt_ops_tmp c
+   WHERE
+      opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
+      and opcname = 'gist_geometry_ops' 
+      and c.oprname = '~=';
+
+-- poly_contains
+INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr)
+   SELECT opcl.oid, 7, true, c.opoid
+   FROM pg_opclass opcl, rt_ops_tmp c
+   WHERE
+      opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
+      and opcname = 'gist_geometry_ops' 
+      and c.oprname = '~';
+
+-- poly_contained
+INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr)
+   SELECT opcl.oid, 8, true, c.opoid
+   FROM pg_opclass opcl, rt_ops_tmp c
+   WHERE
+      opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
+      and opcname = 'gist_geometry_ops' 
+      and c.oprname = '@';
+
+DROP TABLE rt_ops_tmp;
+
+-- add the entries to amproc for the support methods
+-- note the amprocnum numbers associated with each are specific!
+INSERT INTO pg_amproc (amopclaid, amprocnum, amproc)
+   SELECT opcl.oid, 1, pro.oid
+   FROM pg_opclass opcl, pg_proc pro
+   WHERE
+      opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
+      and opcname = 'gist_geometry_ops'
+      and proname = 'ggeometry_consistent';
+
+INSERT INTO pg_amproc (amopclaid, amprocnum, amproc)
+   SELECT opcl.oid, 2, pro.oid
+   FROM pg_opclass opcl, pg_proc pro
+   WHERE
+      opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
+      and opcname = 'gist_geometry_ops'
+      and proname = 'gbox_union';
+
+INSERT INTO pg_amproc (amopclaid, amprocnum, amproc)
+   SELECT opcl.oid, 3, pro.oid
+   FROM pg_opclass opcl, pg_proc pro
+   WHERE
+      opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
+      and opcname = 'gist_geometry_ops'
+      and proname = 'ggeometry_compress';
+
+INSERT INTO pg_amproc (amopclaid, amprocnum, amproc)
+   SELECT opcl.oid, 4, pro.oid
+   FROM pg_opclass opcl, pg_proc pro
+   WHERE
+      opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
+      and opcname = 'gist_geometry_ops'
+      and proname = 'rtree_decompress';
+
+INSERT INTO pg_amproc (amopclaid, amprocnum, amproc)
+   SELECT opcl.oid, 5, pro.oid
+   FROM pg_opclass opcl, pg_proc pro
+   WHERE
+      opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
+      and opcname = 'gist_geometry_ops'
+      and proname = 'gbox_penalty';
+
+INSERT INTO pg_amproc (amopclaid, amprocnum, amproc)
+   SELECT opcl.oid, 6, pro.oid
+   FROM pg_opclass opcl, pg_proc pro
+   WHERE
+      opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
+      and opcname = 'gist_geometry_ops'
+      and proname = 'gbox_picksplit';
+
+INSERT INTO pg_amproc (amopclaid, amprocnum, amproc)
+   SELECT opcl.oid, 7, pro.oid
+   FROM pg_opclass opcl, pg_proc pro
+   WHERE
+      opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
+      and opcname = 'gist_geometry_ops'
+      and proname = 'gbox_same';
+
+#else // USE_VERSION >= 73
+
+--
+-- Create opclass index bindings 
+--
+
+CREATE OPERATOR CLASS gist_geometry_ops
+       DEFAULT FOR TYPE geometry USING gist AS
+       OPERATOR        1       <<      RECHECK,
+       OPERATOR        2       &<      RECHECK,
+       OPERATOR        3       &&      RECHECK,
+       OPERATOR        4       &>      RECHECK,
+       OPERATOR        5       >>      RECHECK,
+       OPERATOR        6       ~=      RECHECK,
+       OPERATOR        7       ~       RECHECK,
+       OPERATOR        8       @       RECHECK,
+       FUNCTION        1       ggeometry_consistent (internal, geometry, int4),
+       FUNCTION        2       gbox_union (bytea, internal),
+       FUNCTION        3       ggeometry_compress (internal),
+       FUNCTION        4       rtree_decompress (internal),
+       FUNCTION        5       gbox_penalty (internal, internal, internal),
+       FUNCTION        6       gbox_picksplit (internal, internal),
+       FUNCTION        7       gbox_same (box, box, internal);
+
+UPDATE pg_opclass 
+       SET opckeytype = (select oid from pg_type where typname = 'box') 
+       WHERE opcname = 'gist_geometry_ops';
+
+#if USE_VERSION >= 74
+
+CREATE OPERATOR CLASS btree_geometry_ops
+       DEFAULT FOR TYPE geometry USING btree AS
+       OPERATOR        1       < ,
+       OPERATOR        2       <= ,
+       OPERATOR        3       = ,
+       OPERATOR        4       >= ,
+       OPERATOR        5       > ,
+       FUNCTION        1       geometry_cmp (geometry, geometry);
+
+#endif // USE_VERSION >= 74
+
+
+#endif // USE_VERSION >= 73
+
+
+-----------------------------------------------------------------------
+-- 7.3+ explicit casting definitions
+-----------------------------------------------------------------------
+#if USE_VERSION >= 73
+--CREATE CAST ( chip AS geometry ) WITH FUNCTION geometry(chip) AS IMPLICIT;
+CREATE CAST ( geometry AS box3d ) WITH FUNCTION box3d(geometry) AS IMPLICIT;
+CREATE CAST ( geometry AS box ) WITH FUNCTION box(geometry) AS IMPLICIT;
+CREATE CAST ( box3d AS geometry ) WITH FUNCTION geometry(box3d) AS IMPLICIT;
+CREATE CAST ( text AS geometry) WITH FUNCTION geometry(text) AS IMPLICIT;
+CREATE CAST ( wkb AS bytea ) WITH FUNCTION bytea(wkb) AS IMPLICIT;
+CREATE CAST ( box3d AS box ) WITH FUNCTION box3dtobox(box3d);
+CREATE CAST ( geometry AS text ) WITH FUNCTION astext(geometry);
+#endif
+
+-----------------------------------------------------------------------
+-- ADDGEOMETRYCOLUMN
+--   <catalogue>, <schema>, <table>, <column>, <srid>, <type>, <dim>
+-----------------------------------------------------------------------
+--
+-- Type can be one of geometry, GEOMETRYCOLLECTION, POINT, MULTIPOINT, POLYGON,
+-- MULTIPOLYGON, LINESTRING, or MULTILINESTRING.
+--
+-- Types (except geometry) are checked for consistency using a CHECK constraint
+-- uses SQL ALTER TABLE command to add the geometry column to the table.
+-- Addes a row to geometry_columns.
+-- Addes a constraint on the table that all the geometries MUST have the same 
+-- SRID. Checks the coord_dimension to make sure its between 0 and 3.
+-- Should also check the precision grid (future expansion).
+-- Calls fix_geometry_columns() at the end.
+--
+-----------------------------------------------------------------------
+CREATEFUNCTION AddGeometryColumn(varchar,varchar,varchar,varchar,integer,varchar,integer)
+       RETURNS text
+       AS 
+'
+DECLARE
+       catalog_name alias for $1;
+       schema_name alias for $2;
+       table_name alias for $3;
+       column_name alias for $4;
+       new_srid alias for $5;
+       new_type alias for $6;
+       new_dim alias for $7;
+#if USE_VERSION >= 73
+       rec RECORD;
+       schema_ok bool;
+       real_schema name;
+#endif
+       fixgeomres text;
+
+BEGIN
+
+       IF ( not ( (new_type =''GEOMETRY'') or
+                  (new_type =''GEOMETRYCOLLECTION'') or
+                  (new_type =''POINT'') or 
+                  (new_type =''MULTIPOINT'') or
+                  (new_type =''POLYGON'') or
+                  (new_type =''MULTIPOLYGON'') or
+                  (new_type =''LINESTRING'') or
+                  (new_type =''MULTILINESTRING'')) )
+       THEN
+               RAISE EXCEPTION ''Invalid type name - valid ones are: 
+                       GEOMETRY, GEOMETRYCOLLECTION, POINT, 
+                       MULTIPOINT, POLYGON, MULTIPOLYGON, 
+                       LINESTRING, or MULTILINESTRING '';
+               return ''fail'';
+       END IF;
+
+       IF ( (new_dim >3) or (new_dim <0) ) THEN
+               RAISE EXCEPTION ''invalid dimension'';
+               return ''fail'';
+       END IF;
+
+#if USE_VERSION >= 73
+       IF ( schema_name != '''' ) THEN
+               schema_ok = ''f'';
+               FOR rec IN SELECT nspname FROM pg_namespace WHERE text(nspname) = schema_name LOOP
+                       schema_ok := ''t'';
+               END LOOP;
+
+               if ( schema_ok <> ''t'' ) THEN
+                       RAISE NOTICE ''Invalid schema name - using current_schema()'';
+                       SELECT current_schema() into real_schema;
+               ELSE
+                       real_schema = schema_name;
+               END IF;
+
+       ELSE
+               SELECT current_schema() into real_schema;
+       END IF;
+#endif
+
+
+       -- Add geometry column
+
+       EXECUTE ''ALTER TABLE '' ||
+#if USE_VERSION >= 73
+               quote_ident(real_schema) || ''.'' || quote_ident(table_name)
+#else
+               quote_ident(table_name)
+#endif
+               || '' ADD COLUMN '' || quote_ident(column_name) || 
+               '' geometry '';
+
+
+       -- Delete stale record in geometry_column (if any)
+
+       EXECUTE ''DELETE FROM geometry_columns WHERE
+               f_table_catalog = '' || quote_literal('''') || 
+               '' AND f_table_schema = '' ||
+#if USE_VERSION >= 73
+               quote_literal(real_schema) || 
+#else
+               quote_literal('''') || 
+#endif
+               '' AND f_table_name = '' || quote_literal(table_name) ||
+               '' AND f_geometry_column = '' || quote_literal(column_name);
+
+
+       -- Add record in geometry_column 
+
+       EXECUTE ''INSERT INTO geometry_columns VALUES ('' ||
+               quote_literal('''') || '','' ||
+#if USE_VERSION >= 73
+               quote_literal(real_schema) || '','' ||
+#else
+               quote_literal('''') || '','' ||
+#endif
+               quote_literal(table_name) || '','' ||
+               quote_literal(column_name) || '','' ||
+               new_dim || '','' || new_srid || '','' ||
+               quote_literal(new_type) || '')'';
+
+       -- Add table checks
+
+       EXECUTE ''ALTER TABLE '' || 
+#if USE_VERSION >= 73
+               quote_ident(real_schema) || ''.'' || quote_ident(table_name)
+#else
+               quote_ident(table_name)
+#endif
+               || '' ADD CONSTRAINT "enforce_srid_'' || 
+               column_name || ''" CHECK (SRID('' || quote_ident(column_name) ||
+               '') = '' || new_srid || '')'' ;
+
+       IF (not(new_type = ''GEOMETRY'')) THEN
+               EXECUTE ''ALTER TABLE '' || 
+#if USE_VERSION >= 73
+               quote_ident(real_schema) || ''.'' || quote_ident(table_name)
+#else
+               quote_ident(table_name)
+#endif
+               || '' ADD CONSTRAINT "enforce_geotype_'' ||
+               column_name || ''" CHECK (geometrytype('' ||
+               quote_ident(column_name) || '')='' ||
+               quote_literal(new_type) || '' OR ('' ||
+               quote_ident(column_name) || '') is null)'';
+       END IF;
+
+       SELECT fix_geometry_columns() INTO fixgeomres;
+
+       return 
+#if USE_VERSION >= 73
+               real_schema || ''.'' || 
+#endif
+               table_name || ''.'' || column_name ||
+               '' SRID:'' || new_srid ||
+               '' TYPE:'' || new_type || ''\n '' ||
+               ''geometry_column '' || fixgeomres;
+END;
+' LANGUAGE 'plpgsql' WITH (isstrict);
+
+----------------------------------------------------------------------------
+-- ADDGEOMETRYCOLUMN ( <schema>, <table>, <column>, <srid>, <type>, <dim> )
+----------------------------------------------------------------------------
+--
+-- This is a wrapper to the real AddGeometryColumn, for use
+-- when catalogue is undefined
+--
+----------------------------------------------------------------------------
+CREATEFUNCTION AddGeometryColumn(varchar,varchar,varchar,integer,varchar,integer) RETURNS text AS '
+DECLARE
+       ret  text;
+BEGIN
+       SELECT AddGeometryColumn('''',$1,$2,$3,$4,$5,$6) into ret;
+       RETURN ret;
+END;
+' LANGUAGE 'plpgsql' WITH (isstrict);
+
+----------------------------------------------------------------------------
+-- ADDGEOMETRYCOLUMN ( <table>, <column>, <srid>, <type>, <dim> )
+----------------------------------------------------------------------------
+--
+-- This is a wrapper to the real AddGeometryColumn, for use
+-- when catalogue and schema are undefined
+--
+----------------------------------------------------------------------------
+CREATEFUNCTION AddGeometryColumn(varchar,varchar,integer,varchar,integer) RETURNS text AS '
+DECLARE
+       ret  text;
+BEGIN
+       SELECT AddGeometryColumn('''','''',$1,$2,$3,$4,$5) into ret;
+       RETURN ret;
+END;
+' LANGUAGE 'plpgsql' WITH (isstrict);
+
+-----------------------------------------------------------------------
+-- DROPGEOMETRYCOLUMN
+--   <catalogue>, <schema>, <table>, <column>
+-----------------------------------------------------------------------
+--
+-- Removes geometry column reference from geometry_columns table.
+-- Drops the column with pgsql >= 73.
+-- Make some silly enforcements on it for pgsql < 73
+--
+-----------------------------------------------------------------------
+CREATEFUNCTION DropGeometryColumn(varchar, varchar,varchar,varchar)
+       RETURNS text
+       AS 
+'
+DECLARE
+       catalog_name alias for $1; 
+       schema_name alias for $2;
+       table_name alias for $3;
+       column_name alias for $4;
+       myrec RECORD;
+       okay boolean;
+       real_schema name;
+
+BEGIN
+
+
+#if USE_VERSION >= 73
+       -- Find, check or fix schema_name
+       IF ( schema_name != '''' ) THEN
+               okay = ''f'';
+
+               FOR myrec IN SELECT nspname FROM pg_namespace WHERE text(nspname) = schema_name LOOP
+                       okay := ''t'';
+               END LOOP;
+
+               IF ( okay <> ''t'' ) THEN
+                       RAISE NOTICE ''Invalid schema name - using current_schema()'';
+                       SELECT current_schema() into real_schema;
+               ELSE
+                       real_schema = schema_name;
+               END IF;
+       ELSE
+               SELECT current_schema() into real_schema;
+       END IF;
+#else
+       real_schema = schema_name;
+#endif // USE_VERSION >= 73
+
+       -- Find out if the column is in the geometry_columns table
+       okay = ''f'';
+       FOR myrec IN SELECT * from geometry_columns where f_table_schema = text(real_schema) and f_table_name = table_name and f_geometry_column = column_name LOOP
+               okay := ''t'';
+       END LOOP; 
+       IF (okay <> ''t'') THEN 
+               RAISE EXCEPTION ''column not found in geometry_columns table'';
+               RETURN ''f'';
+       END IF;
+
+       -- Remove ref from geometry_columns table
+       EXECUTE ''delete from geometry_columns where f_table_schema = '' ||
+               quote_literal(real_schema) || '' and f_table_name = '' ||
+               quote_literal(table_name)  || '' and f_geometry_column = '' ||
+               quote_literal(column_name);
+       
+#if USE_VERSION < 73
+       -- Remove not-null constraint to table column 
+       EXECUTE ''update pg_attribute set attnotnull = false from pg_class where pg_attribute.attrelid = pg_class.oid and pg_class.relname = '' || quote_literal(table_name) ||'' and pg_attribute.attname = '' || quote_literal(column_name);
+       -- update the given table/column so that it it all NULLS
+       EXECUTE ''update "''||table_name||''" set "''||column_name||''"= NULL'';
+       -- add = NULL constraint to given table/column
+       EXECUTE ''ALTER TABLE "''||table_name||''" ADD CHECK ("''||column_name||''" IS NULL)'';
+#else
+       -- Remove table column
+       EXECUTE ''ALTER TABLE '' || quote_ident(real_schema) || ''.'' ||
+               quote_ident(table_name) || '' DROP COLUMN '' ||
+               quote_ident(column_name);
+#endif 
+
+
+       RETURN real_schema || ''.'' || table_name || ''.'' || column_name ||'' effectively removed.'';
+       
+END;
+'
+LANGUAGE 'plpgsql' WITH (isstrict);
+
+-----------------------------------------------------------------------
+-- DROPGEOMETRYCOLUMN
+--   <schema>, <table>, <column>
+-----------------------------------------------------------------------
+--
+-- This is a wrapper to the real DropGeometryColumn, for use
+-- when catalogue is undefined
+--
+-----------------------------------------------------------------------
+CREATEFUNCTION DropGeometryColumn(varchar,varchar,varchar)
+       RETURNS text
+       AS 
+'
+DECLARE
+       ret text;
+BEGIN
+       SELECT DropGeometryColumn('''',$1,$2,$3) into ret;
+       RETURN ret;
+END;
+' LANGUAGE 'plpgsql' WITH (isstrict);
+
+-----------------------------------------------------------------------
+-- DROPGEOMETRYCOLUMN
+--   <table>, <column>
+-----------------------------------------------------------------------
+--
+-- This is a wrapper to the real DropGeometryColumn, for use
+-- when catalogue and schema is undefined. 
+--
+-----------------------------------------------------------------------
+CREATEFUNCTION DropGeometryColumn(varchar,varchar)
+       RETURNS text
+       AS 
+'
+DECLARE
+       ret text;
+BEGIN
+       SELECT DropGeometryColumn('''','''',$1,$2) into ret;
+       RETURN ret;
+END;
+' LANGUAGE 'plpgsql' WITH (isstrict);
+
+-----------------------------------------------------------------------
+-- DROPGEOMETRYTABLE
+--   <catalogue>, <schema>, <table>
+-----------------------------------------------------------------------
+--
+-- Drop a table and all its references in geometry_columns
+--
+-----------------------------------------------------------------------
+CREATEFUNCTION DropGeometryTable(varchar, varchar,varchar)
+       RETURNS text
+       AS 
+'
+DECLARE
+       catalog_name alias for $1; 
+       schema_name alias for $2;
+       table_name alias for $3;
+       real_schema name;
+
+BEGIN
+
+#if USE_VERSION >= 73
+       IF ( schema_name = '''' ) THEN
+               SELECT current_schema() into real_schema;
+       ELSE
+               real_schema = schema_name;
+       END IF;
+#endif // USE_VERSION >= 73
+
+       -- Remove refs from geometry_columns table
+       EXECUTE ''DELETE FROM geometry_columns WHERE '' ||
+#if USE_VERSION >= 73
+               ''f_table_schema = '' || quote_literal(real_schema) ||
+               '' AND '' ||
+#endif
+               '' f_table_name = '' || quote_literal(table_name);
+       
+       -- Remove table 
+       EXECUTE ''DROP TABLE ''
+#if USE_VERSION >= 73
+               || quote_ident(real_schema) || ''.'' ||
+#endif 
+               quote_ident(table_name);
+
+       RETURN
+#if USE_VERSION >= 73
+               real_schema || ''.'' ||
+#endif 
+               table_name ||'' dropped.'';
+       
+END;
+'
+LANGUAGE 'plpgsql' WITH (isstrict);
+
+-----------------------------------------------------------------------
+-- DROPGEOMETRYTABLE
+--   <schema>, <table>
+-----------------------------------------------------------------------
+--
+-- Drop a table and all its references in geometry_columns
+--
+-----------------------------------------------------------------------
+CREATEFUNCTION DropGeometryTable(varchar,varchar) RETURNS text AS 
+'SELECT DropGeometryTable('''',$1,$2)'
+LANGUAGE 'sql' WITH (isstrict);
+
+-----------------------------------------------------------------------
+-- DROPGEOMETRYTABLE
+--   <table>
+-----------------------------------------------------------------------
+--
+-- Drop a table and all its references in geometry_columns
+-- For PG>=73 use current_schema()
+--
+-----------------------------------------------------------------------
+CREATEFUNCTION DropGeometryTable(varchar) RETURNS text AS 
+'SELECT DropGeometryTable('''','''',$1)'
+LANGUAGE 'sql' WITH (isstrict);
+
+-----------------------------------------------------------------------
+-- UPDATE_GEOMETRY_STATS()
+-----------------------------------------------------------------------
+--
+-- Only meaningful for PG<80.
+-- Gather statisticts about geometry columns for use
+-- with cost estimator.
+--
+-- It is defined also for PG>=80 for back-compatibility
+--
+-----------------------------------------------------------------------
+#if USE_VERSION >= 80
+CREATEFUNCTION update_geometry_stats() RETURNS text
+AS ' SELECT ''update_geometry_stats() has been obsoleted. Statistics are automatically built running the ANALYZE command''::text' LANGUAGE 'sql';
+#else // USE_VERSION < 80
+CREATEFUNCTION update_geometry_stats()
+RETURNS text
+AS
+'
+DECLARE
+       result text;
+       stated integer;
+       fixres text;
+BEGIN
+
+       SELECT fix_geometry_columns() INTO fixres;
+
+       UPDATE geometry_columns SET
+#if USE_VERSION >= 73
+               stats = (build_histogram2d(create_histogram2d(
+                       find_extent(f_table_schema, f_table_name, f_geometry_column), 40), f_table_schema, f_table_name, f_geometry_column))
+               FROM pg_class c, pg_attribute a, pg_namespace n
+               WHERE n.nspname = f_table_schema::name
+               AND c.relname = f_table_name::name
+               AND c.relnamespace = n.oid
+#else // USE_VERSION < 73 
+               stats = (build_histogram2d(create_histogram2d(
+                       find_extent(f_table_name, f_geometry_column),
+                       40), f_table_name, f_geometry_column))
+               FROM pg_class c, pg_attribute a
+               WHERE c.relname = f_table_name::name
+#endif
+               AND a.attname = f_geometry_column::name
+               AND a.attrelid = c.oid
+               AND geometry_columns.attrelid is not null;
+
+       GET DIAGNOSTICS stated = ROW_COUNT;
+
+       result = fixres || '' stats:'' || stated::text;
+
+       return result;
+END;
+'
+LANGUAGE 'plpgsql' ;
+#endif // USE_VERSION < 80
+
+-----------------------------------------------------------------------
+-- UPDATE_GEOMETRY_STATS( <table>, <column> )
+-----------------------------------------------------------------------
+--
+-- Only meaningful for PG<80.
+-- Gather statisticts about a geometry column for use
+-- with cost estimator.
+--
+-- It is defined also for PG>=80 for back-compatibility
+--
+-----------------------------------------------------------------------
+#if USE_VERSION >= 80
+CREATEFUNCTION update_geometry_stats(varchar,varchar) RETURNS text
+AS 'SELECT update_geometry_stats();' LANGUAGE 'sql' ;
+#else
+CREATEFUNCTION update_geometry_stats(varchar,varchar) RETURNS text
+AS
+'
+DECLARE
+       tablename aliAS for $1;
+       columnname aliAS for $2;
+       stated integer;
+       result text;
+       fixres text;
+BEGIN
+
+       SELECT fix_geometry_columns() INTO fixres;
+
+       EXECUTE ''UPDATE geometry_columns SET
+#if USE_VERSION >= 73
+                       stats = (build_histogram2d(create_histogram2d(
+                               find_extent(f_table_schema,
+                                       f_table_name,
+                                       f_geometry_column), 40),
+                                       f_table_schema, f_table_name,
+                                       f_geometry_column))
+                       FROM pg_class c, pg_attribute a, pg_namespace n
+                       WHERE n.nspname = f_table_schema::name
+                       AND c.relname = f_table_name::name
+                       AND a.attname = f_geometry_column::name
+                       AND c.relnamespace = n.oid
+                       AND a.attrelid = c.oid
+#else // USE_VERSION < 73 
+                       stats = (build_histogram2d(create_histogram2d(
+                               find_extent(f_table_name, f_geometry_column),
+                               40), f_table_name, f_geometry_column))
+                       FROM pg_class c, pg_attribute a
+                       WHERE c.relname = f_table_name::name
+                       AND a.attname = f_geometry_column::name
+                       AND a.attrelid = c.oid
+#endif
+                       AND f_table_name = '' || quote_literal(tablename) || ''
+                       AND f_geometry_column = '' || quote_literal(columnname)
+                       || '' AND geometry_columns.attrelid is not null'';
+
+       GET DIAGNOSTICS stated = ROW_COUNT;
+
+       result = fixres || '' stats:'' || stated::text;
+
+       return result;
+END;
+'
+LANGUAGE 'plpgsql' ;
+
+#endif  // USE_VERSION < 80
+
+-----------------------------------------------------------------------
+-- CREATE_HISTOGRAM2D( <box>, <size> )
+-----------------------------------------------------------------------
+--
+-- Returns a histgram with 0s in all the boxes.
+--
+-----------------------------------------------------------------------
+CREATEFUNCTION create_histogram2d(box3d,int)
+       RETURNS histogram2d
+       AS '@MODULE_FILENAME@','create_histogram2d'
+       LANGUAGE 'C'  with (isstrict);
+
+-----------------------------------------------------------------------
+-- BUILD_HISTOGRAM2D( <histogram2d>, <tablename>, <columnname> )
+-----------------------------------------------------------------------
+CREATEFUNCTION build_histogram2d (histogram2d,text,text)
+       RETURNS histogram2d
+       AS '@MODULE_FILENAME@','build_histogram2d'
+       LANGUAGE 'C'  with (isstrict);
+
+#if USE_VERSION >= 73
+-----------------------------------------------------------------------
+-- BUILD_HISTOGRAM2D(<histogram2d>,<schema>,<tablename>,<columnname>)
+-----------------------------------------------------------------------
+-- This is a wrapper to the omonimous schema unaware function,
+-- thanks to Carl Anderson for the idea.
+-----------------------------------------------------------------------
+CREATEFUNCTION build_histogram2d (histogram2d,text,text,text)
+RETURNS histogram2d
+AS '
+BEGIN
+       EXECUTE ''SET local search_path = ''||$2||'',public'';
+       RETURN public.build_histogram2d($1,$3,$4);
+END
+' LANGUAGE 'plpgsql'  with (isstrict);
+#endif // USE_VERSION >= 73
+
+-----------------------------------------------------------------------
+-- EXPLODE_HISTOGRAM2D( <histogram2d>, <tablename> )
+-----------------------------------------------------------------------
+CREATEFUNCTION explode_histogram2d (HISTOGRAM2D,text)
+       RETURNS histogram2d
+       AS '@MODULE_FILENAME@','explode_histogram2d'
+       LANGUAGE 'C'  with (isstrict);
+
+-----------------------------------------------------------------------
+-- ESTIMATE_HISTOGRAM2D( <histogram2d>, <box> )
+-----------------------------------------------------------------------
+CREATEFUNCTION estimate_histogram2d(HISTOGRAM2D,box)
+       RETURNS float8
+       AS '@MODULE_FILENAME@','estimate_histogram2d'
+       LANGUAGE 'C'  with (isstrict);
+
+
+-----------------------------------------------------------------------
+-- SVG OUTPUT
+-----------------------------------------------------------------------
+CREATEFUNCTION AsSvg(geometry,int4,int4)
+       RETURNS TEXT
+       AS '@MODULE_FILENAME@','assvg_geometry'
+       LANGUAGE 'C';
+
+CREATEFUNCTION AsSvg(geometry,int4)
+       RETURNS TEXT
+       AS '@MODULE_FILENAME@','assvg_geometry'
+       LANGUAGE 'C';
+
+CREATEFUNCTION AsSvg(geometry)
+       RETURNS TEXT
+       AS '@MODULE_FILENAME@','assvg_geometry'
+       LANGUAGE 'C';
+
+END TRANSACTION;
diff --git a/hwgeom/postgis_algo.c b/hwgeom/postgis_algo.c
new file mode 100644 (file)
index 0000000..f0994bc
--- /dev/null
@@ -0,0 +1,512 @@
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2001-2003 Refractions Research Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ *
+ **********************************************************************
+ * $Log$
+ * Revision 1.1  2004/09/20 07:50:06  strk
+ * prepared to contain old internal representation code
+ *
+ * Revision 1.3  2004/04/27 13:50:59  strk
+ * Fixed bug in simplify() that was using the square of the given tolerance.
+ *
+ * Revision 1.2  2004/01/21 19:04:03  strk
+ * Added line_interpolate_point function by jsunday@rochgrp.com
+ *
+ * Revision 1.1  2003/10/27 10:21:15  strk
+ * Initial import.
+ *
+ **********************************************************************/
+
+#include "postgres.h"
+#include "postgis.h"
+
+
+/***********************************************************************
+ * Simple Douglas-Peucker line simplification. 
+ * No checks are done to avoid introduction of self-intersections.
+ * No topology relations are considered.
+ *
+ * --strk@keybit.net;
+ ***********************************************************************/
+
+#define SAMEPOINT(a,b) ((a)->x==(b)->x&&(a)->y==(b)->y&&(a)->z==(b)->z)
+#define VERBOSE 0
+
+#if VERBOSE > 0
+#undef REPORT_POINTS_REDUCTION
+#undef REPORT_RINGS_REDUCTION
+#define REPORT_RINGS_ADJUSTMENTS
+#endif
+
+#undef CHECK_RING_IS_CLOSE
+
+
+/*
+ * Search farthest point from segment p1-p2
+ * returns distance in an int pointer
+ */
+void DP_findsplit(POINT3D *pts, int npts, int p1, int p2,
+   int *split, double *dist)
+{
+   int k;
+   POINT3D *pa, *pb, *pk;
+   double tmp;
+
+#if VERBOSE > 4
+elog(NOTICE, "DP_findsplit called");
+#endif
+
+   *dist = -1;
+   *split = p1;
+
+   if (p1 + 1 < p2)
+   {
+
+      pa = &(pts[p1]);
+      pb = &(pts[p2]);
+
+#if VERBOSE > 4
+elog(NOTICE, "DP_findsplit: P%d(%f,%f) to P%d(%f,%f)",
+   p1, pa->x, pa->y, p2, pb->x, pb->y);
+#endif
+
+      for (k=p1+1; k<p2; k++)
+      {
+         pk = &(pts[k]);
+
+#if VERBOSE > 4
+elog(NOTICE, "DP_findsplit: P%d(%f,%f)", k, pk->x, pk->y);
+#endif
+
+         /* distance computation */
+         tmp = distance_pt_seg(pk, pa, pb);
+
+         if (tmp > *dist) 
+         {
+            *dist = tmp;       /* record the maximum */
+            *split = k;
+#if VERBOSE > 4
+elog(NOTICE, "DP_findsplit: P%d is farthest (%g)", k, *dist);
+#endif
+         }
+      }
+
+   } /* length---should be redone if can == 0 */
+
+   else
+   {
+#if VERBOSE > 3
+elog(NOTICE, "DP_findsplit: segment too short, no split/no dist");
+#endif
+   }
+
+}
+
+
+void
+DP_simplify(POINT3D *inpts, int inptsn, POINT3D **outpts, int *outptsn, double epsilon)
+{
+   int stack[inptsn];          /* recursion stack */
+   int sp=-1;              /* recursion stack pointer */
+   int p1, split; 
+   double dist;
+   POINT3D *outpoints;
+   int numoutpoints=0;
+
+   p1 = 0;
+   stack[++sp] = inptsn-1;
+
+#if VERBOSE > 4
+   elog(NOTICE, "DP_simplify called");
+#endif
+
+   outpoints = (POINT3D *)palloc( sizeof(POINT3D) * inptsn);
+   memcpy(outpoints, inpts, sizeof(POINT3D));
+   numoutpoints++;
+
+#if VERBOSE > 3
+elog(NOTICE, "DP_simplify: added P0 to simplified point array (size 1)");
+#endif
+
+
+   do
+   {
+
+      DP_findsplit(inpts, inptsn, p1, stack[sp], &split, &dist);
+#if VERBOSE > 3
+      elog(NOTICE, "DP_simplify: farthest point from P%d-P%d is P%d (dist. %g)", p1, stack[sp], split, dist);
+#endif
+
+      if (dist > epsilon) {
+         stack[++sp] = split;
+      } else {
+         /*
+         outpoints = repalloc( outpoints, sizeof(POINT3D) * numoutpoints+1 );
+         if ( outpoints == NULL ) elog(NOTICE, "Out of virtual memory");
+         */
+         memcpy(outpoints+numoutpoints, &(inpts[stack[sp]]),
+            sizeof(POINT3D));
+         numoutpoints++;
+#if VERBOSE > 3
+elog(NOTICE, "DP_simplify: added P%d to simplified point array (size: %d)",
+   stack[sp], numoutpoints);
+#endif
+             p1 = stack[sp--];
+      }
+#if VERBOSE > 5
+elog(NOTICE, "stack pointer = %d", sp);
+#endif
+   }
+   while (! (sp<0) );
+
+   /*
+    * If we have reduced the number of points realloc
+    * outpoints array to free up some memory.
+    * Might be turned on and of with a SAVE_MEMORY define ...
+    */
+   if ( numoutpoints < inptsn )
+   {
+      outpoints = (POINT3D *)repalloc(outpoints, sizeof(POINT3D)*numoutpoints);
+      if ( outpoints == NULL ) {
+         elog(ERROR, "Out of virtual memory");
+      }
+   }
+
+   *outpts = outpoints;
+   *outptsn = numoutpoints;
+}
+
+char *
+simplify_line3d(LINE3D *iline, double dist)
+{
+   POINT3D *ipts;
+   POINT3D *opts;
+   int iptsn, optsn, olinesize;
+   LINE3D *oline;
+
+   ipts = iline->points;
+   iptsn = iline->npoints;
+
+   DP_simplify(ipts, iptsn, &opts, &optsn, dist);
+
+   oline = make_line(optsn, opts, &olinesize);
+
+   return (char *)oline;
+}
+
+char *
+simplify_polygon3d(POLYGON3D *ipoly, double dist)
+{
+   POINT3D *ipts;
+   POINT3D *opts;
+   int iptsn, optsn, size;
+   int nrings;
+   int pts_per_ring[ipoly->nrings];
+   POLYGON3D *opoly;
+   int ri;
+   POINT3D *allpts = NULL;
+   int allptsn = 0;
+   int pt_off = 0; /* point offset for each ring */
+
+   nrings = 0;
+
+#ifdef REPORT_RINGS_REDUCTION
+elog(NOTICE, "simplify_polygon3d: simplifying polygon with %d rings",
+   ipoly->nrings);
+#endif
+
+   /* Points start here */
+   ipts = (POINT3D *) ((char *)&(ipoly->npoints[ipoly->nrings] ));
+
+   pt_off=0;
+   for (ri=0; ri<ipoly->nrings; pt_off += ipoly->npoints[ri++])
+   {
+      iptsn = ipoly->npoints[ri];
+
+#ifdef CHECK_RING_IS_CLOSE
+      if ( ! SAMEPOINT(ipts+pt_off, ipts+pt_off+iptsn-1) )
+         elog(NOTICE, "First point != Last point");
+#endif
+
+      DP_simplify(ipts+pt_off, iptsn, &opts, &optsn, dist);
+      if ( optsn < 2 )
+      {
+         /* There as to be an error in DP_simplify */
+         elog(NOTICE, "DP_simplify returned a <2 pts array");
+         pfree(opts);
+         continue;
+      }
+
+#ifdef CHECK_RING_IS_CLOSE
+      if ( ! SAMEPOINT(opts, opts+optsn-1) )
+         elog(NOTICE, "First point != Last point");
+#endif
+
+
+      if ( optsn < 4 )
+      {
+         pfree(opts);
+#ifdef REPORT_RINGS_ADJUSTMENTS
+         elog(NOTICE, "simplify_polygon3d: ring%d skipped ( <4 pts )", ri);
+#endif
+         if ( ri ) continue;
+         else break;
+      }
+
+
+#ifdef REPORT_POINTS_REDUCTION
+elog(NOTICE, "simplify_polygon3d: ring%d simplified from %d to %d points", ri, iptsn, optsn);
+#endif
+
+
+      /*
+       * Add ring to simplified ring array
+       * (TODO: dinamic allocation of pts_per_ring)
+       */
+      pts_per_ring[nrings++] = optsn;
+      if ( ! allptsn ) {
+         allptsn = optsn;
+         allpts = palloc(sizeof(POINT3D)*allptsn);
+         memcpy(allpts, opts, sizeof(POINT3D)*optsn);
+      } else {
+         allptsn += optsn;
+         allpts = repalloc(allpts, sizeof(POINT3D)*allptsn);
+         memcpy(allpts+(allptsn-optsn), opts, sizeof(POINT3D)*optsn);
+      }
+      pfree(opts);
+
+      if ( ! allpts ) {
+         elog(NOTICE, "Error allocating memory for all ring points");
+         return NULL;
+      }
+
+   }
+
+#ifdef REPORT_RINGS_REDUCTION
+elog(NOTICE, "simplify_polygon3d: simplified polygon with %d rings", nrings);
+#endif
+
+   if ( nrings )
+   {
+      opoly = make_polygon(nrings, pts_per_ring, allpts, allptsn, &size);
+      pfree(allpts);
+      return (char *)opoly;
+   }
+   else
+   {
+      return NULL;
+   }
+
+}
+
+char *
+simplify_point3d(POINT3D *ipoint, double dist)
+{
+   return (char *)ipoint;
+}
+
+PG_FUNCTION_INFO_V1(simplify);
+Datum simplify(PG_FUNCTION_ARGS)
+{
+   Datum datum;
+   BOX3D *bbox;
+   GEOMETRY *orig_geom;
+   GEOMETRY *simp_geom = NULL;
+   char *orig_obj;       /* pointer to each object in orig_geom */
+   char *simp_obj;       /* pointer to each simplified object */
+   int simp_obj_size;    /* size of simplified object */
+   int32 *offsets;
+   int i;
+   double dist;
+
+   if ( PG_ARGISNULL(0) ) PG_RETURN_NULL();
+   datum = PG_GETARG_DATUM(0);
+   orig_geom = (GEOMETRY *)PG_DETOAST_DATUM(datum);
+
+   if ( PG_ARGISNULL(1) ) PG_RETURN_NULL();
+   dist = PG_GETARG_FLOAT8(1);
+
+   /*
+    * Three or more points on a straight line will still collapse!
+    */
+   // if ( dist == 0 ) PG_RETURN_POINTER(orig_geom);
+
+   offsets = (int32 *) ( ((char *) &(orig_geom->objType[0] )) +
+      sizeof(int32) * orig_geom->nobjs );
+
+
+   /*
+    * Simplify each subobject indipendently.
+    * No topology relations kept.
+    */
+   for(i=0;i<orig_geom->nobjs; i++)
+   {
+      orig_obj = (char *) orig_geom+offsets[i];
+
+      if ( orig_geom->objType[i] == LINETYPE )
+      {
+         simp_obj = simplify_line3d((LINE3D *)orig_obj, dist);
+      }
+      else if ( orig_geom->objType[i] == POLYGONTYPE )
+      {
+         simp_obj = simplify_polygon3d((POLYGON3D *)orig_obj, dist);
+      }
+      else if ( orig_geom->objType[i] == POINTTYPE )
+      {
+         simp_obj = simplify_point3d((POINT3D *)orig_obj, dist);
+      }
+      else
+      {
+         elog(NOTICE, "Unknown geometry type");
+         PG_RETURN_NULL();
+      }
+
+      /* Simplified object degenerated to empty set */
+      if ( ! simp_obj ) continue;
+
+      /* Get size of simplified object */
+      simp_obj_size = size_subobject(simp_obj, orig_geom->objType[i]);
+
+      /* Create one-object geometry (will add objects later) */
+      if ( simp_geom == NULL )
+      {
+         simp_geom = make_oneobj_geometry(
+            simp_obj_size, simp_obj, orig_geom->objType[i],
+            orig_geom->is3d, orig_geom->SRID, orig_geom->scale,
+            orig_geom->offsetX, orig_geom->offsetY);
+      }
+
+      /* Add object to already initialized geometry  */
+      else
+      {
+         simp_geom = add_to_geometry(
+                  simp_geom, simp_obj_size, simp_obj, 
+                  orig_geom->objType[i] );
+      }
+
+      /* Error in simplified geometry construction */
+      if ( ! simp_geom )
+      {
+         elog(ERROR, "geometry construction failed at iteration %d", i);
+         PG_RETURN_NULL();
+      }
+
+   }
+
+   if ( simp_geom == NULL ) PG_RETURN_NULL();
+
+   /* Calculate the bounding box */
+   bbox = bbox_of_geometry(simp_geom);
+   memcpy(&(simp_geom->bvol), bbox, sizeof(BOX3D));
+   pfree(bbox);
+
+
+   simp_geom->type = orig_geom->type;
+   PG_RETURN_POINTER(simp_geom);
+}
+/***********************************************************************
+ * --strk@keybit.net;
+ ***********************************************************************/
+
+/***********************************************************************
+ * Interpolate a point along a line, useful for Geocoding applications
+ * SELECT line_interpolate_point( 'LINESTRING( 0 0, 2 2'::geometry, .5 )
+ * returns POINT( 1 1 )
+ *
+ * -- jsunday@rochgrp.com;
+ ***********************************************************************/
+PG_FUNCTION_INFO_V1(line_interpolate_point);
+Datum line_interpolate_point(PG_FUNCTION_ARGS)
+{
+       GEOMETRY *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       double distance = PG_GETARG_FLOAT8(1);
+
+       int32 *offsets1;
+       LINE3D *line;
+       POINT3D point;
+       int nsegs, i;
+       double length, slength, tlength;
+
+       if( distance < 0 || distance > 1 ) {
+               elog(ERROR,"line_interpolate_point: 2nd arg isnt within [0,1]");
+               PG_RETURN_NULL();
+       }
+
+       if( geom->type != LINETYPE ) {
+               elog(ERROR,"line_interpolate_point: 1st arg isnt a line");
+               PG_RETURN_NULL();
+       }
+
+       offsets1 = (int32 *) ( ((char *) &(geom->objType[0] )) +
+                       sizeof(int32) * geom->nobjs );
+       line = (LINE3D*) ( (char *)geom + offsets1[0] );
+
+       /* If distance is one of the two extremes, return the point on that
+        * end rather than doing any expensive computations
+        */
+       if( distance == 0.0 ) {
+               PG_RETURN_POINTER(
+                               make_oneobj_geometry(sizeof(POINT3D),
+                                       /*(char*)&(line->points[0]), POINTTYPE, */
+                                       (char*)&(line->points[0]), POINTTYPE,
+                                       geom->is3d, geom->SRID, geom->scale, geom->offsetX, geom->offsetY
+                                       )
+                               );
+       }
+
+       if( distance == 1.0 ) {
+               PG_RETURN_POINTER(
+                               make_oneobj_geometry(sizeof(POINT3D),
+                                       (char*)&(line->points[line->npoints-1]), POINTTYPE, 
+                                       geom->is3d, geom->SRID, geom->scale, geom->offsetX, geom->offsetY
+                                       )
+                               );
+       }
+
+       /* Interpolate a point on the line */
+       nsegs = line->npoints - 1;
+       length = line_length2d( line );
+       tlength = 0;
+       for( i = 0; i < nsegs; i++ ) {
+               POINT3D *p1, *p2;
+               p1 = &(line->points[i]);
+               p2 = &(line->points[i+1]);
+               /* Find the relative length of this segment */
+               slength = distance_pt_pt( p1, p2 )/length;
+               /* If our target distance is before the total length we've seen
+                * so far. create a new point some distance down the current
+                * segment.
+                */
+               if( distance < tlength + slength ) {
+                       double dseg = (distance - tlength) / slength;
+                       point.x = (p1->x) + ((p2->x - p1->x) * dseg);
+                       point.y = (p1->y) + ((p2->y - p1->y) * dseg);
+                       point.z = 0;
+                       PG_RETURN_POINTER(
+                                       make_oneobj_geometry(sizeof(POINT3D),
+                                               (char*)&point, POINTTYPE, 
+                                               geom->is3d, geom->SRID, geom->scale, geom->offsetX, geom->offsetY
+                                               )
+                                       );
+               }
+               tlength += slength;
+       }
+       /* Return the last point on the line. This shouldn't happen, but
+        * could if there's some floating point rounding errors. */
+       PG_RETURN_POINTER(
+                       make_oneobj_geometry(sizeof(POINT3D),
+                               (char*)&(line->points[line->npoints-1]), POINTTYPE, 
+                               geom->is3d, geom->SRID, geom->scale, geom->offsetX, geom->offsetY
+                               )
+                       );
+}
+/***********************************************************************
+ * --jsunday@rochgrp.com;
+ ***********************************************************************/
diff --git a/hwgeom/postgis_chip.c b/hwgeom/postgis_chip.c
new file mode 100644 (file)
index 0000000..4602edb
--- /dev/null
@@ -0,0 +1,277 @@
+
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2001-2003 Refractions Research Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ * 
+ **********************************************************************
+ * $Log$
+ * Revision 1.1  2004/09/20 07:50:06  strk
+ * prepared to contain old internal representation code
+ *
+ * Revision 1.7  2004/04/28 22:26:02  pramsey
+ * Fixed spelling mistake in header text.
+ *
+ * Revision 1.6  2003/07/01 18:30:55  pramsey
+ * Added CVS revision headers.
+ *
+ *
+ **********************************************************************/
+
+#include "postgres.h"
+
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "access/gist.h"
+#include "access/itup.h"
+#include "access/rtree.h"
+
+
+#include "fmgr.h"
+
+
+#include "postgis.h"
+#include "utils/elog.h"
+
+
+
+// input is a string with hex chars in it.  Convert to binary and put in the result
+PG_FUNCTION_INFO_V1(CHIP_in);
+Datum CHIP_in(PG_FUNCTION_ARGS)
+{
+       char                    *str = PG_GETARG_CSTRING(0);
+       CHIP                    *result;
+       int                     size;
+       int                     t;
+       int                     input_str_len;
+       int                     datum_size;
+
+//printf("chip_in called\n");
+
+       input_str_len = strlen(str);
+
+       if  ( ( ( (int)(input_str_len/2.0) ) *2.0) != input_str_len)
+       {
+               elog(ERROR,"CHIP_in parser - should be even number of characters!");
+               PG_RETURN_NULL();
+       }
+
+       if (strspn(str,"0123456789ABCDEF") != strlen(str) )
+       {
+               elog(ERROR,"CHIP_in parser - input contains bad characters.  Should only have '0123456789ABCDEF'!");
+               PG_RETURN_NULL();       
+       }
+       size = (input_str_len/2) ;
+       result = (CHIP *) palloc(size);
+       
+       
+       for (t=0;t<size;t++)
+       {
+               ((unsigned char *)result)[t] = parse_hex( &str[t*2]) ;
+       }
+// if endian is wrong, flip it otherwise do nothing
+       result->size = size;
+       if (result->size < sizeof(CHIP) )
+       {
+               elog(ERROR,"CHIP_in parser - bad data (too small to be a CHIP)!");
+               PG_RETURN_NULL();       
+       }
+
+
+       if (result->endian_hint != 1)
+       {
+               //need to do an endian flip
+               flip_endian_int32( (char *)   &result->endian_hint);
+
+               flip_endian_double((char *)  &result->bvol.LLB.x);
+               flip_endian_double((char *)  &result->bvol.LLB.y);
+               flip_endian_double((char *)  &result->bvol.LLB.z);
+
+               flip_endian_double((char *)  &result->bvol.URT.x);
+               flip_endian_double((char *)  &result->bvol.URT.y);
+               flip_endian_double((char *)  &result->bvol.URT.z);
+
+               flip_endian_int32( (char *)  & result->SRID);   
+                       //dont know what to do with future[8] ...
+
+               flip_endian_int32( (char *)  & result->height); 
+               flip_endian_int32( (char *)  & result->width);
+               flip_endian_int32( (char *)  & result->compression);
+               flip_endian_int32( (char *)  & result->factor);
+               flip_endian_int32( (char *)  & result->datatype);
+
+       }
+       if (result->endian_hint != 1 )
+       {
+               elog(ERROR,"CHIP_in parser - bad data (endian flag != 1)!");
+               PG_RETURN_NULL();       
+       }
+       datum_size = 4;
+
+       if ( (result->datatype == 6) || (result->datatype == 7) || (result->datatype == 106) || (result->datatype == 107) )
+       {
+               datum_size = 2;
+       }
+       if ( (result->datatype == 8) || (result->datatype == 108) )
+       {
+               datum_size=1;
+       }
+
+       if (result->compression ==0) //only true for non-compressed data
+       {
+               if (result->size != (sizeof(CHIP) + datum_size * result->width*result->height) )
+               {
+                       elog(ERROR,"CHIP_in parser - bad data (actual size != computed size)!");
+                       PG_RETURN_NULL();
+               }
+       }
+
+       PG_RETURN_POINTER(result);
+}
+
+
+//given a CHIP structure, convert it to Hex and put it in a string
+PG_FUNCTION_INFO_V1(CHIP_out);
+Datum CHIP_out(PG_FUNCTION_ARGS)
+{
+       CHIP                                  *chip = (CHIP *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       char                                    *result;
+       int                                     size_result;
+       int                                     t;
+
+//printf("chip_out called\n");
+
+       size_result = (chip->size ) *2 +1; //+1 for null char
+       result = palloc (size_result);
+       result[size_result-1] = 0; //null terminate
+
+       for (t=0; t< (chip->size); t++)
+       {
+               deparse_hex( ((unsigned char *) chip)[t], &result[t*2]);                        
+       }
+       PG_RETURN_CSTRING(result);      
+}
+
+
+
+PG_FUNCTION_INFO_V1(CHIP_to_geom);
+Datum CHIP_to_geom(PG_FUNCTION_ARGS)
+{
+       CHIP                                  *chip = (CHIP *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                                *result;
+       
+       POLYGON3D                       *poly;
+       POINT3D                 pts[5]; //5 points around box
+       int                             pts_per_ring[1];
+       int                             poly_size;
+       
+       //use LLB's z value (we're going to set is3d to false)
+
+       set_point( &pts[0], chip->bvol.LLB.x , chip->bvol.LLB.y , chip->bvol.LLB.z );
+       set_point( &pts[1], chip->bvol.URT.x , chip->bvol.LLB.y , chip->bvol.LLB.z );
+       set_point( &pts[2], chip->bvol.URT.x , chip->bvol.URT.y , chip->bvol.LLB.z );
+       set_point( &pts[3], chip->bvol.LLB.x , chip->bvol.URT.y , chip->bvol.LLB.z );
+       set_point( &pts[4], chip->bvol.LLB.x , chip->bvol.LLB.y , chip->bvol.LLB.z );
+       
+       pts_per_ring[0] = 5; //ring has 5 points
+
+               //make a polygon
+       poly = make_polygon(1, pts_per_ring, pts, 5, &poly_size);
+
+       result = make_oneobj_geometry(poly_size, (char *)poly, POLYGONTYPE, FALSE,chip->SRID, 1.0, 0.0, 0.0);
+
+       PG_RETURN_POINTER(result);      
+
+}
+
+PG_FUNCTION_INFO_V1(srid_chip);
+Datum srid_chip(PG_FUNCTION_ARGS)
+{ 
+       CHIP               *c = (CHIP *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+       PG_RETURN_INT32(c->SRID);
+}
+
+PG_FUNCTION_INFO_V1(factor_chip);
+Datum factor_chip(PG_FUNCTION_ARGS)
+{ 
+       CHIP               *c = (CHIP *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+       PG_RETURN_FLOAT4(c->factor);
+}
+
+
+PG_FUNCTION_INFO_V1(datatype_chip);
+Datum datatype_chip(PG_FUNCTION_ARGS)
+{ 
+       CHIP               *c = (CHIP *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+       PG_RETURN_INT32(c->datatype);
+}
+
+PG_FUNCTION_INFO_V1(compression_chip);
+Datum compression_chip(PG_FUNCTION_ARGS)
+{ 
+       CHIP               *c = (CHIP *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+       PG_RETURN_INT32(c->compression);
+}
+
+
+PG_FUNCTION_INFO_V1(height_chip);
+Datum height_chip(PG_FUNCTION_ARGS)
+{ 
+       CHIP               *c = (CHIP *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+       PG_RETURN_INT32(c->height);
+}
+
+PG_FUNCTION_INFO_V1(width_chip);
+Datum width_chip(PG_FUNCTION_ARGS)
+{ 
+       CHIP               *c = (CHIP *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+       PG_RETURN_INT32(c->width);
+}
+
+
+
+PG_FUNCTION_INFO_V1(setsrid_chip);
+Datum setsrid_chip(PG_FUNCTION_ARGS)
+{ 
+       CHIP    *c = (CHIP *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       int32   new_srid = PG_GETARG_INT32(1);
+       CHIP  *result;
+
+       result = (CHIP *) palloc(c->size);
+
+       memcpy(result,c,c->size);
+       result->SRID = new_srid;
+
+       PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(setfactor_chip);
+Datum setfactor_chip(PG_FUNCTION_ARGS)
+{ 
+       CHIP    *c = (CHIP *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       float   factor = PG_GETARG_FLOAT4(1);
+       CHIP  *result;
+
+       result = (CHIP *) palloc(c->size);
+
+       memcpy(result,c,c->size);
+       result->factor = factor;
+
+       PG_RETURN_POINTER(result);
+}
diff --git a/hwgeom/postgis_debug.c b/hwgeom/postgis_debug.c
new file mode 100644 (file)
index 0000000..2521a10
--- /dev/null
@@ -0,0 +1,337 @@
+
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2001-2003 Refractions Research Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ *
+ **********************************************************************
+ * $Log$
+ * Revision 1.1  2004/09/20 07:50:06  strk
+ * prepared to contain old internal representation code
+ *
+ * Revision 1.16  2004/08/20 10:23:19  strk
+ * removed leak from mem_size()
+ *
+ * Revision 1.15  2004/04/28 22:26:02  pramsey
+ * Fixed spelling mistake in header text.
+ *
+ * Revision 1.14  2004/01/13 20:30:19  strk
+ * Added useless PG_RETURN_NULL() call to make compiler happy
+ *
+ * Revision 1.13  2003/11/19 18:01:31  strk
+ * CR removed
+ *
+ * Revision 1.12  2003/09/16 20:27:12  dblasby
+ * added ability to delete geometries.
+ *
+ * Revision 1.11  2003/08/08 18:19:20  dblasby
+ * Conformance changes.
+ * Removed junk from postgis_debug.c and added the first run of the long
+ * transaction locking support.  (this will change - dont use it)
+ * conformance tests were corrected
+ * some dos cr/lf removed
+ * empty geometries i.e. GEOMETRYCOLLECT(EMPTY) added (with indexing support)
+ * pointN(<linestring>,1) now returns the first point (used to return 2nd)
+ *
+ * Revision 1.10  2003/07/25 17:08:37  pramsey
+ * Moved Cygwin endian define out of source files into postgis.h common
+ * header file.
+ *
+ * Revision 1.9  2003/07/01 18:30:55  pramsey
+ * Added CVS revision headers.
+ *
+ *
+ **********************************************************************/
+
+#include "postgres.h"
+
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "access/gist.h"
+#include "access/itup.h"
+#include "access/rtree.h"
+
+
+#include "fmgr.h"
+
+
+#include "postgis.h"
+#include "utils/elog.h"
+
+#include "executor/spi.h"
+#include "commands/trigger.h"
+
+
+#define SHOW_DIGS_DOUBLE 15
+#define MAX_DIGS_DOUBLE (SHOW_DIGS_DOUBLE + 6 + 1 + 3 +1)
+
+
+// #define DEBUG_GIST
+//#define DEBUG_GIST2
+
+
+
+
+
+//find the size of geometry
+PG_FUNCTION_INFO_V1(mem_size);
+Datum mem_size(PG_FUNCTION_ARGS)
+{
+       GEOMETRY *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       int32 size = geom->size;
+       PG_FREE_IF_COPY(geom,0);
+       PG_RETURN_INT32(size);
+}
+
+
+
+//get summary info on a GEOMETRY
+PG_FUNCTION_INFO_V1(summary);
+Datum summary(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                      *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       int32                           *offsets1;
+       char                            *o1;
+       int32                           type1,j,i;
+       POLYGON3D                       *poly;
+       LINE3D                  *line;
+       char                            *result;
+       int                             size;
+       char                            tmp[100];
+       text                            *mytext;
+
+
+       size = 1;
+       result = palloc(1);
+       result[0] = 0; //null terminate it
+
+
+       offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
+
+       //now have to do a scan of each object
+
+       for (j=0; j< geom1->nobjs; j++)         //for each object in geom1
+       {
+               o1 = (char *) geom1 +offsets1[j] ;
+               type1=  geom1->objType[j];
+
+               if (type1 == POINTTYPE) //point
+               {
+                       size += 30;
+                       result = repalloc(result,size);
+                       sprintf(tmp,"Object %i is a POINT()\n",j);
+                       strcat(result,tmp);
+               }
+
+               if (type1 == LINETYPE)  //line
+               {
+                       line = (LINE3D *) o1;
+
+                       size += 57;
+                       result = repalloc(result,size);
+                       sprintf(tmp,"Object %i is a LINESTRING() with %i points\n",j,line->npoints);
+                       strcat(result,tmp);
+               }
+
+               if (type1 == POLYGONTYPE)       //POLYGON
+               {
+                       poly = (POLYGON3D *) o1;
+
+                       size += 57*(poly->nrings +1);
+                       result = repalloc(result,size);
+                       sprintf(tmp,"Object %i is a POLYGON() with %i rings\n",j,poly->nrings);
+                       strcat(result,tmp);
+                       for (i=0; i<poly->nrings;i++)
+                       {
+                               sprintf(tmp,"     + ring %i has %i points\n",i,poly->npoints[i]);
+                               strcat(result,tmp);
+                       }
+               }
+       }
+
+               // create a text obj to return
+       mytext = (text *) palloc(VARHDRSZ  + strlen(result) );
+       VARATT_SIZEP(mytext) = VARHDRSZ + strlen(result) ;
+       memcpy(VARDATA(mytext) , result, strlen(result) );
+       pfree(result);
+       PG_RETURN_POINTER(mytext);
+}
+
+
+
+
+
+extern Datum lockcheck(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(lockcheck);
+
+Datum lockcheck (PG_FUNCTION_ARGS)
+{
+       TriggerData *trigdata =      (TriggerData *) fcinfo->context;
+       char            *colname ;
+       int                     id;
+       HeapTuple   rettuple;
+       TupleDesc   tupdesc;
+       int         SPIcode;
+       char            *relname;
+       bool            isnull;
+       char            query[1024];
+       int                     pk_type_oid;
+       char            *pk_id = NULL;
+
+
+       elog(NOTICE,"in lockcheck()");
+
+       /* Make sure trigdata is pointing at what I expect */
+    if (!CALLED_AS_TRIGGER(fcinfo))
+        elog(ERROR, "lockcheck: not fired by trigger manager");
+
+       rettuple = trigdata->tg_newtuple;
+       tupdesc = trigdata->tg_relation->rd_att;
+
+           /* Connect to SPI manager */
+    SPIcode = SPI_connect();
+
+       if (SPIcode  != SPI_OK_CONNECT)
+       {
+               elog(ERROR,"lockcheck: couldnt open a connection to SPI");
+               PG_RETURN_NULL() ;
+       }
+
+       relname = SPI_getrelname(trigdata->tg_relation);
+
+       colname  =       trigdata->tg_trigger->tgargs[0];
+
+
+
+
+       elog(NOTICE,"trigger was executed on the relation: '%s', with attribute named '%s', with locktable named '%s'", relname,colname,"authorization_table");
+
+       //get the value
+
+       pk_type_oid =SPI_gettypeid ( tupdesc  , SPI_fnumber(tupdesc,colname));
+       elog(NOTICE,"primary key type # = %i", pk_type_oid  ) ;
+
+       if (pk_type_oid == 23) //int4
+       {
+               int id = (int) DatumGetInt32( SPI_getbinval(rettuple, tupdesc, SPI_fnumber(tupdesc,colname), &isnull) );
+               if (isnull)
+               {
+                       elog(ERROR,"lockcheck - column (%s) of table (%s) is null!",colname,relname);
+               }
+               pk_id = palloc(100);
+               sprintf(pk_id,"%i",id);
+       }
+       else if (pk_type_oid == 25) // text
+       {
+                Datum id = ( SPI_getbinval(rettuple, tupdesc, SPI_fnumber(tupdesc,colname), &isnull) );
+                if (isnull)
+                {
+                                       elog(ERROR,"lockcheck - column (%s) of table (%s) is null!",colname,relname);
+                }
+                pk_id  = DatumGetCString(DirectFunctionCall1(textout,id));
+
+       }
+       else if (pk_type_oid == 1043) // varchar
+       {
+               Datum id = ( SPI_getbinval(rettuple, tupdesc, SPI_fnumber(tupdesc,colname), &isnull) );
+                if (isnull)
+                {
+                                       elog(ERROR,"lockcheck - column (%s) of table (%s) is null!",colname,relname);
+                }
+                pk_id  = DatumGetCString(DirectFunctionCall1(varcharout,id));
+       }
+       else
+       {
+               elog(ERROR,"id column (%s) of table (%s) has to be either int4, text, or varchar.  Its - %s (oid %i)",colname,relname,SPI_gettype ( tupdesc  , SPI_fnumber(tupdesc,colname) ) ,pk_type_oid );
+       }
+
+
+       id = (int) DatumGetInt32( SPI_getbinval(rettuple, tupdesc, SPI_fnumber(tupdesc,colname), &isnull) );
+
+       sprintf(query,"SELECT authid FROM %s WHERE expires >= now() AND tname = '%s' and  fid = '%s'::text", "authorization_table",relname,pk_id);
+       elog(NOTICE,"about to execute :%s", query);
+
+       SPIcode = SPI_exec(query,0);
+       if (SPIcode !=SPI_OK_SELECT )
+               elog(ERROR,"couldnt execute to test for lock :%s",query);
+
+
+       if (SPI_processed >0)
+       {
+               // there is a lock - check to see if I have rights to it!
+
+               TupleDesc tupdesc = SPI_tuptable->tupdesc;
+               SPITupleTable *tuptable = SPI_tuptable;
+               HeapTuple tuple = tuptable->vals[0];
+               char    *lockcode = SPI_getvalue(tuple, tupdesc, 1);
+
+               elog(NOTICE,"there is a lock on this row!");
+
+               // check to see if temp_lock_have_table table exists (it might not exist if they own no locks
+               sprintf(query,"SELECT * FROM pg_class WHERE relname = 'temp_lock_have_table'");
+               SPIcode = SPI_exec(query,0);
+               if (SPIcode !=SPI_OK_SELECT )
+                       elog(ERROR,"couldnt execute to test for lockkey temp table :%s",query);
+               if (SPI_processed ==0)
+               {
+                       elog(NOTICE,"I do not own any locks (no lock table) - I cannot modify the row");
+                       //PG_RETURN_NULL();
+                       SPI_finish();
+                       //return NULL;
+                       elog(ERROR,"attemted to modify a locked row that I do not have authorization for!");
+               }
+
+
+               sprintf(query,"SELECT * FROM temp_lock_have_table  WHERE xideq(transid , getTransactionID() ) AND lockcode ='%s'",lockcode);
+               elog(NOTICE,"about to execute :%s", query);
+
+               SPIcode = SPI_exec(query,0);
+               if (SPIcode !=SPI_OK_SELECT )
+                       elog(ERROR,"couldnt execute to test for lock aquire:%s",query);
+
+               if (SPI_processed >0)
+               {
+                       elog(NOTICE,"I own the lock - I can modify the row");
+                       SPI_finish();
+                       return PointerGetDatum(rettuple);
+               }
+
+               elog(NOTICE,"I do not own the lock - I cannot modify the row");
+               //PG_RETURN_NULL();
+               SPI_finish();
+               //return NULL;
+               elog(ERROR,"attemted to modify a locked row that I do not have authorization for!");
+               PG_RETURN_NULL();
+       }
+       else
+       {
+               elog(NOTICE,"there is NOT a lock on this row!");
+               SPI_finish();
+               return PointerGetDatum(rettuple);
+       }
+
+}
+
+
+extern Datum getTransactionID(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(getTransactionID);
+
+Datum getTransactionID(PG_FUNCTION_ARGS)
+{
+    TransactionId xid = GetCurrentTransactionId();
+    PG_RETURN_DATUM( TransactionIdGetDatum(xid) );
+}
+
+
diff --git a/hwgeom/postgis_estimate.c b/hwgeom/postgis_estimate.c
new file mode 100644 (file)
index 0000000..995067c
--- /dev/null
@@ -0,0 +1,1950 @@
+
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2001-2003 Refractions Research Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ * 
+ **********************************************************************
+ * $Log$
+ * Revision 1.1  2004/09/20 07:50:06  strk
+ * prepared to contain old internal representation code
+ *
+ * Revision 1.31  2004/08/19 06:15:58  strk
+ * USE_VERSION gets 80 where it got 75
+ *
+ * Revision 1.30  2004/08/16 11:03:25  mcayland
+ * Added DLLIMPORT reference to "default_statistics_target" if we are compiling under Win32. This should make it unnecessary to apply Romi's patch to the PostgreSQL source tree when compiling PostgreSQL ready for PostGIS.
+ *
+ * Revision 1.29  2004/06/22 16:52:17  strk
+ * Standard deviation factor used in histogram extent computation made
+ * a compile-time define.
+ *
+ * Revision 1.28  2004/06/14 07:48:10  strk
+ * Histogram extent redefinition after hard deviant removal fixed to be
+ * "at most" the standard deviation based computed.
+ *
+ * Revision 1.27  2004/06/11 11:38:57  strk
+ * Infinite geometries handling.
+ * Histogram extent re-computation after 'hard deviant' features removal.
+ *
+ * Revision 1.26  2004/06/10 18:54:12  strk
+ * histogram grid size refined to use near-square cells.
+ *
+ * Revision 1.25  2004/06/10 15:44:43  strk
+ * Added standard deviation based histogram extent refinement
+ *
+ * Revision 1.24  2004/06/10 13:42:17  strk
+ * Separated the estimator code in an estimate_selectivity() function.
+ * Handled complete contaiment and complete miss of histogram by searc box.
+ *
+ * Revision 1.23  2004/06/09 09:35:49  strk
+ * Removed partial pgsql List API copy
+ *
+ * Revision 1.22  2004/06/08 17:49:14  strk
+ * Fixed to build cleanly agains pg75
+ *
+ * Revision 1.21  2004/04/28 22:26:02  pramsey
+ * Fixed spelling mistake in header text.
+ *
+ * Revision 1.20  2004/03/15 17:07:05  strk
+ * Added calls to vacuum_delay_point() to give backend a chance of
+ * interrupting geometry stats computation.
+ * Set default DEBUG_GEOMETRY_STATS to 0.
+ *
+ * Revision 1.19  2004/03/09 00:21:02  strk
+ * Removed useless code blocks in histogram builder
+ *
+ * Revision 1.18  2004/03/09 00:09:56  strk
+ * estimator applies a gain of AOI/cell_area on each cell it intersects (reverted to previous behaviour)
+ *
+ * Revision 1.17  2004/03/04 13:50:45  strk
+ * postgis_gist_sel(): added warnings if search_box goes outside of histogram grid
+ *
+ * Revision 1.16  2004/03/04 09:44:57  strk
+ * The selectivity estimator does add the full value of each cell it overlaps,
+ * regardless of the actual overlapping area. Final gain is not applied
+ * (formerly 1 / minimun between average feature cells occupation and
+ *  search_box cells occupation)
+ *
+ * Revision 1.15  2004/03/03 21:59:48  strk
+ * added check to keep selectivity value in the range of validity (suggested by m.cave)
+ *
+ * Revision 1.14  2004/03/01 16:02:41  strk
+ * histogram's boxesPerSide computed as a function of the column's statistic target
+ *
+ * Revision 1.13  2004/02/29 21:53:42  strk
+ * bug fix in postgis_gist_sel (for PG75): SysCache is not released if not acquired
+ *
+ * Revision 1.12  2004/02/26 16:42:59  strk
+ * Fixed bugs reported by Mark Cave-Ayland <m.cave-ayland@webbased.co.uk>.
+ * Re-introduced previously removed estimate value incrementation by
+ * the fractional part of each of the cells' value computed as the fraction
+ * of overlapping area.
+ *
+ * Revision 1.11  2004/02/25 12:00:32  strk
+ * Added handling for point features in histogram creation (add 1 instead of AOI/cell_area when AOI is 0).
+ * Fixed a wrong cast of BOX3D to BOX (called the convertion func).
+ * Added some comments and an implementation on how to change evaluation
+ * based on the average feature and search box cells occupation.
+ *
+ * Revision 1.10  2004/02/25 00:46:26  strk
+ * initial version of && selectivity estimation for PG75
+ *
+ * Revision 1.9  2004/02/23 21:59:16  strk
+ * geometry analyzer builds the histogram
+ *
+ * Revision 1.8  2004/02/23 12:18:55  strk
+ * added skeleton functions for pg75 stats integration
+ *
+ * Revision 1.7  2003/11/11 10:14:57  strk
+ * Added support for PG74
+ *
+ * Revision 1.6  2003/07/25 17:08:37  pramsey
+ * Moved Cygwin endian define out of source files into postgis.h common
+ * header file.
+ *
+ * Revision 1.5  2003/07/01 18:30:55  pramsey
+ * Added CVS revision headers.
+ *
+ *
+ **********************************************************************/
+
+ // If you're modifiying this file you should read the postgis mail list as it has
+ // detailed descriptions of whats happening here and why.
+
+#include "postgres.h"
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "access/gist.h"
+#include "access/itup.h"
+#include "access/rtree.h"
+
+#include "fmgr.h"
+
+#include "postgis.h"
+#include "utils/elog.h"
+
+#define SHOW_DIGS_DOUBLE 15
+#define MAX_DIGS_DOUBLE (SHOW_DIGS_DOUBLE + 6 + 1 + 3 +1)
+
+//--------------------------------------------------------------------------
+
+#include "access/heapam.h"
+#include "catalog/catname.h"
+#include "catalog/pg_operator.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_statistic.h"
+#include "catalog/pg_type.h"
+#include "mb/pg_wchar.h"
+#include "nodes/makefuncs.h"
+#include "optimizer/clauses.h"
+#include "optimizer/cost.h"
+#include "optimizer/pathnode.h"
+#include "optimizer/plancat.h"
+#include "optimizer/prep.h"
+#include "parser/parse_func.h"
+#include "parser/parse_oper.h"
+#include "parser/parsetree.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/selfuncs.h"
+#include "utils/syscache.h"
+
+
+
+#include "executor/spi.h"
+
+#if USE_VERSION >= 80
+
+#include "commands/vacuum.h"
+
+/*
+ *     Assign a number to the postgis statistics kind
+ *
+ *     tgl suggested:
+ *
+ *     1-100:  reserved for assignment by the core Postgres project
+ *     100-199: reserved for assignment by PostGIS
+ *     200-9999: reserved for other globally-known stats kinds
+ *     10000-32767: reserved for private site-local use
+ *
+ */
+#define STATISTIC_KIND_GEOMETRY 100
+
+#define DEBUG_GEOMETRY_STATS 1
+
+/*
+ * Define this if you want to use standard deviation based
+ * histogram extent computation. If you do, you can also 
+ * tweak the deviation factor used in computation with
+ * SDFACTOR.
+ */
+#define USE_STANDARD_DEVIATION 1
+#define SDFACTOR 2
+
+/*
+ * Default geometry selectivity factor
+ */
+#define DEFAULT_GEOMETRY_SEL 0.000005 
+
+typedef struct GEOM_STATS_T
+{
+       // cols * rows = total boxes in grid
+       float4 cols;
+       float4 rows;
+       
+       // average bounding box area of not-null features 
+       float4 avgFeatureArea;
+
+       // average number of histogram cells
+       // covered by the sample not-null features
+       float4 avgFeatureCells;
+
+       // BOX of area
+       float4 xmin,ymin, xmax, ymax;
+       // variable length # of floats for histogram
+       float4 value[1];
+} GEOM_STATS;
+
+#endif
+
+
+// For Win32 we must declare default_statistics_target as DLLIMPORT *after*
+// including all the relevant header files otherwise we get a link error.
+#if defined(__CYGWIN__) || defined(__MINGW32__)
+extern DLLIMPORT int default_statistics_target;
+#endif
+
+
+//estimate_histogram2d(histogram2d, box)
+// returns a % estimate of the # of features that will be returned by that box query
+//
+//For each grid cell that intersects the query box
+//       Calculate area of intersection (AOI)
+//    IF AOI < avgFeatureArea THEN set AOI = avgFeatureArea
+//    SUM AOI/area-of-cell*value-of-cell
+//
+// change : instead of avgFeatureArea, use avgFeatureArea or 10% of a grid cell (whichever is smaller)
+
+PG_FUNCTION_INFO_V1(estimate_histogram2d);
+Datum estimate_histogram2d(PG_FUNCTION_ARGS)
+{
+       HISTOGRAM2D   *histo = (HISTOGRAM2D *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       BOX                       *box = (BOX *) PG_GETARG_POINTER(1);
+       double box_area;
+       int                     x_idx_min, x_idx_max, y_idx_min, y_idx_max;
+       double intersect_x, intersect_y, AOI;
+       int x,y;
+       double xmin,ymin,xmax,ymax;
+       int32 result_sum;
+       double cell_area;
+       int total,t;
+       double  avg_feature_size;
+
+
+
+       result_sum = 0;
+    xmin = histo->xmin;
+    ymin = histo->ymin;
+    xmax = histo->xmax;
+    ymax = histo->ymax;
+
+                       cell_area = ( (xmax-xmin)*(ymax-ymin)/(histo->boxesPerSide*histo->boxesPerSide) );
+
+                       avg_feature_size = histo->avgFeatureArea;
+                       if ( avg_feature_size > cell_area*0.1)
+                       {
+                               avg_feature_size = cell_area*0.1;
+                       }
+
+
+//elog(NOTICE,"start estimate_histogram2d: ");
+//elog(NOTICE,"box is : (%.15g,%.15g to %.15g,%.15g)",box->low.x,box->low.y, box->high.x, box->high.y);
+
+                                       box_area = (box->high.x-box->low.x)*(box->high.y-box->low.y);
+
+                                       if (box_area<0)
+                                           box_area =0;  // for precision!
+
+                                       //check to see which boxes this intersects
+                                       x_idx_min = (box->low.x-xmin)/(xmax-xmin)*histo->boxesPerSide;
+                                       if (x_idx_min <0)
+                                               x_idx_min = 0;
+                                       if (x_idx_min >= histo->boxesPerSide)
+                                               x_idx_min = histo->boxesPerSide-1;
+                                       y_idx_min = (box->low.y-ymin)/(ymax-ymin)*histo->boxesPerSide;
+                                       if (y_idx_min <0)
+                                               y_idx_min = 0;
+                                       if (y_idx_min >= histo->boxesPerSide)
+                                               y_idx_min = histo->boxesPerSide-1;
+
+                                       x_idx_max = (box->high.x-xmin)/(xmax-xmin)*histo->boxesPerSide;
+                                       if (x_idx_max <0)
+                                               x_idx_max = 0;
+                                       if (x_idx_max >= histo->boxesPerSide)
+                                               x_idx_max = histo->boxesPerSide-1;
+                                       y_idx_max = (box->high.y-ymin)/(ymax-ymin)*histo->boxesPerSide ;
+                                       if (y_idx_max <0)
+                                               y_idx_max = 0;
+                                       if (y_idx_max >= histo->boxesPerSide)
+                                               y_idx_max = histo->boxesPerSide-1;
+
+                                       //the {x,y}_idx_{min,max} define the grid squares that the box intersects
+
+
+//elog(NOTICE,"        search is in x: %i to %i   y: %i to %i",x_idx_min, x_idx_max, y_idx_min,y_idx_max);
+                                       for (y= y_idx_min; y<=y_idx_max;y++)
+                                       {
+                                               for (x=x_idx_min;x<= x_idx_max;x++)
+                                               {
+                                                               intersect_x = min(box->high.x, xmin+ (x+1) * (xmax-xmin)/histo->boxesPerSide ) -
+                                                                                       max(box->low.x, xmin+ x*(xmax-xmin)/histo->boxesPerSide ) ;
+                                                               intersect_y = min(box->high.y, ymin+ (y+1) * (ymax-ymin)/histo->boxesPerSide ) -
+                                                                                       max(box->low.y, ymin+ y*(ymax-ymin)/histo->boxesPerSide ) ;
+
+                                                                       // for a point, intersect_x=0, intersect_y=0, box_area =0
+//elog(NOTICE,"x=%i,y=%i, intersect_x= %.15g, intersect_y = %.15g",x,y,intersect_x,intersect_y);
+                                                               if ( (intersect_x>=0) && (intersect_y>=0) )
+                                                               {
+                                                                       AOI = intersect_x*intersect_y;
+                                                                       if (AOI< avg_feature_size)
+                                                                                       AOI = avg_feature_size;
+                                                                       result_sum += AOI/cell_area* histo->value[x+y*histo->boxesPerSide];
+                                                               }
+                                               }
+                                       }
+                       total = 0;
+                       for(t=0;t<histo->boxesPerSide*histo->boxesPerSide;t++)
+                       {
+                               total+=histo->value[t];
+                       }
+
+                       if ( (histo->avgFeatureArea <=0) && (box_area <=0) )
+                               PG_RETURN_FLOAT8(1.0/((double)(total)));
+                       else
+                               PG_RETURN_FLOAT8(result_sum/((double)total));
+
+}
+
+//explode_histogram2d(histogram2d, tablename::text)
+// executes CREATE TABLE tablename (the_geom geometry, id int, hits int, percent float)
+// then populates it
+//  DOES NOT UPDATE GEOMETRY_COLUMNS
+PG_FUNCTION_INFO_V1(explode_histogram2d);
+Datum explode_histogram2d(PG_FUNCTION_ARGS)
+{
+       HISTOGRAM2D   *histo = (HISTOGRAM2D *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       char    *tablename;
+       char    sql[1000];
+       char    geom[1000];
+       int t;
+    int total;
+       double cellx,celly;
+       int x,y;
+       int SPIcode;
+
+
+       cellx = (histo->xmax-histo->xmin)/histo->boxesPerSide;
+       celly = (histo->ymax-histo->ymin)/histo->boxesPerSide;
+
+       tablename  = DatumGetCString(DirectFunctionCall1(textout,
+                       PointerGetDatum(PG_GETARG_DATUM(1))));
+
+       total = 0;
+       for(t=0;t<histo->boxesPerSide*histo->boxesPerSide;t++)
+       {
+               total+=histo->value[t];
+       }
+       if (total==0)
+               total=1;
+
+               SPIcode = SPI_connect();
+               if (SPIcode  != SPI_OK_CONNECT)
+               {
+                       elog(ERROR,"build_histogram2d: couldnt open a connection to SPI");
+                       PG_RETURN_NULL() ;
+
+               }
+
+               sprintf(sql,"CREATE TABLE %s (the_geom geometry, id int, hits int, percent float)",tablename);
+
+               SPIcode = SPI_exec(sql, 2147483640 ); // max signed int32
+
+                       if (SPIcode  != SPI_OK_UTILITY )
+                       {
+                                       elog(ERROR,"explode_histogram2d: couldnt create table");
+                                       PG_RETURN_NULL() ;
+                       }
+                       t=0;
+                       for(y=0;y<histo->boxesPerSide;y++)
+                       {
+                                       for(x=0;x<histo->boxesPerSide;x++)
+                                       {
+
+                                               sprintf(geom,"POLYGON((%.15g %.15g, %.15g %.15g, %.15g %.15g, %.15g %.15g, %.15g %.15g ))",
+                                                       histo->xmin + x*cellx,     histo->ymin+y*celly,
+                                                       histo->xmin + (x)*cellx,   histo->ymin+ (y+1)*celly,
+                                                       histo->xmin + (x+1)*cellx, histo->ymin+ (y+1)*celly,
+                                                       histo->xmin + (x+1)*cellx, histo->ymin+y*celly,
+                                                       histo->xmin + x*cellx,     histo->ymin+y*celly
+                                                       );
+                                               sprintf(sql,"INSERT INTO %s VALUES('%s'::geometry,%i,%i,%.15g)",tablename,geom,t,histo->value[t],histo->value[t]/((double)total)*100.0);
+//elog(NOTICE,"SQL:%s",sql);
+                                               t++;
+                                               SPIcode = SPI_exec(sql, 2147483640 ); // max signed int32
+                                               if (SPIcode  != SPI_OK_INSERT )
+                                               {
+                                                               elog(ERROR,"explode_histogram2d: couldnt insert into");
+                                                               PG_RETURN_NULL() ;
+                                               }
+                                       }
+                       }
+
+           SPIcode =SPI_finish();
+           if (SPIcode  != SPI_OK_FINISH )
+               {
+                                       elog(ERROR,"build_histogram2d: couldnt disconnect from SPI");
+                                       PG_RETURN_NULL() ;
+               }
+
+       PG_RETURN_POINTER(histo) ;
+}
+
+
+//build_histogram2d (HISTOGRAM2D, tablename, columnname)
+// executes the SPI 'SELECT box3d(columnname) FROM tablename'
+// and sticks all the results in the histogram
+PG_FUNCTION_INFO_V1(build_histogram2d);
+Datum build_histogram2d(PG_FUNCTION_ARGS)
+{
+       HISTOGRAM2D   *histo = (HISTOGRAM2D *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+       char    *tablename, *columnname;
+       HISTOGRAM2D   *result;
+       int SPIcode;
+       char    sql[1000];
+       SPITupleTable *tuptable;
+       TupleDesc tupdesc ;
+       int ntuples,t;
+       Datum   datum;
+       bool    isnull;
+       HeapTuple tuple ;
+       BOX     *box;
+    double box_area, area_intersect, cell_area;
+    int x_idx_min, x_idx_max;
+    int y_idx_min, y_idx_max;
+    double xmin,ymin, xmax,ymax;
+    double intersect_x, intersect_y;
+    int x,y;
+    int total;
+    double sum_area;
+    int    sum_area_numb;
+
+       double sum_area_new      = 0;
+    int    sum_area_numb_new =0;
+       int bump=0;
+
+       int tuplimit = 500000;  // No. of tuples returned on each cursor fetch
+       bool moredata;
+       void *SPIplan;
+       void *SPIportal;
+
+
+    xmin = histo->xmin;
+    ymin = histo->ymin;
+    xmax = histo->xmax;
+    ymax = histo->ymax;
+
+
+       result = (HISTOGRAM2D *) malloc(histo->size);
+       memcpy(result,histo,histo->size);
+
+
+               total = 0;
+               for(t=0;t<histo->boxesPerSide*histo->boxesPerSide;t++)
+               {
+                       total+=histo->value[t];
+               }
+
+
+
+               sum_area = histo->avgFeatureArea * total;
+               sum_area_numb = total;
+
+
+
+           tablename  = DatumGetCString(DirectFunctionCall1(textout,
+                                                       PointerGetDatum(PG_GETARG_DATUM(1))));
+
+               columnname = DatumGetCString(DirectFunctionCall1(textout,
+                                                       PointerGetDatum(PG_GETARG_DATUM(2))));
+
+       //elog(NOTICE,"Start build_histogram2d with %i items already existing", sum_area_numb);
+       //elog(NOTICE,"table=\"%s\", column = \"%s\"", tablename, columnname);
+
+
+               SPIcode = SPI_connect();
+
+               if (SPIcode  != SPI_OK_CONNECT)
+               {
+                       elog(ERROR,"build_histogram2d: couldnt open a connection to SPI");
+                       PG_RETURN_NULL() ;
+
+               }
+
+
+                       sprintf(sql,"SELECT box(\"%s\") FROM \"%s\"",columnname,tablename);
+                       //elog(NOTICE,"executing %s",sql);
+
+                       SPIplan = SPI_prepare(sql, 0, NULL);
+                       if (SPIplan  == NULL)
+                       {
+                                       elog(ERROR,"build_histogram2d: couldnt create query plan via SPI");
+                                       PG_RETURN_NULL() ;
+                       }
+
+                       SPIportal = SPI_cursor_open(NULL, SPIplan, NULL, NULL);
+                       if (SPIportal == NULL)
+                       {
+                                       elog(ERROR,"build_histogram2d: couldn't create cursor via SPI");
+                                       PG_RETURN_NULL() ;
+                       }
+
+                       
+                       moredata = TRUE;
+                       while (moredata==TRUE) {
+
+                               //elog(NOTICE,"about to fetch...");
+                               SPI_cursor_fetch(SPIportal, TRUE, tuplimit);
+
+                               ntuples = SPI_processed;
+                               //elog(NOTICE,"processing %d records", ntuples);
+
+                               if (ntuples > 0) {
+
+                                       tuptable = SPI_tuptable;
+                                       tupdesc = SPI_tuptable->tupdesc;
+
+                                       cell_area = ( (xmax-xmin)*(ymax-ymin)/(histo->boxesPerSide*histo->boxesPerSide) );
+
+                                       for (t=0;t<ntuples;t++)
+                                       {
+                                               tuple = tuptable->vals[t];
+                                               datum = SPI_getbinval(tuple, tupdesc, 1, &isnull);
+                                               if (!(isnull))
+                                               {
+                                                       box = (BOX *)DatumGetPointer(datum);
+                                                       box_area = (box->high.x-box->low.x)*(box->high.y-box->low.y);
+
+                                                       sum_area_new += box_area;
+                                                       sum_area_numb_new ++;
+
+                                                       if (box_area > cell_area )
+                                                               box_area = cell_area;
+                                                       if (box_area<0)
+                                                               box_area =0;  // for precision!
+
+                                                       //check to see which boxes this intersects
+                                                       x_idx_min = (box->low.x-xmin)/(xmax-xmin)*histo->boxesPerSide;
+                                                       if (x_idx_min <0)
+                                                               x_idx_min = 0;
+                                                       if (x_idx_min >= histo->boxesPerSide)
+                                                               x_idx_min = histo->boxesPerSide-1;
+                                                       y_idx_min = (box->low.y-ymin)/(ymax-ymin)*histo->boxesPerSide;
+                                                       if (y_idx_min <0)
+                                                               y_idx_min = 0;
+                                                       if (y_idx_min >= histo->boxesPerSide)
+                                                               y_idx_min = histo->boxesPerSide-1;
+
+                                                       x_idx_max = (box->high.x-xmin)/(xmax-xmin)*histo->boxesPerSide;
+                                                       if (x_idx_max <0)
+                                                               x_idx_max = 0;
+                                                       if (x_idx_max >= histo->boxesPerSide)
+                                                               x_idx_max = histo->boxesPerSide-1;
+                                                       y_idx_max = (box->high.y-ymin)/(ymax-ymin)*histo->boxesPerSide ;
+                                                       if (y_idx_max <0)
+                                                               y_idx_max = 0;
+                                                       if (y_idx_max >= histo->boxesPerSide)
+                                                               y_idx_max = histo->boxesPerSide-1;
+
+                                                       //the {x,y}_idx_{min,max} define the grid squares that the box intersects
+                                                       // if the area of the intersect between the box and the grid square > 5% of
+
+                       //elog(NOTICE,"box is : (%.15g,%.15g to %.15g,%.15g)",box->low.x,box->low.y, box->high.x, box->high.y);
+               //elog(NOTICE,"        search is in x: %i to %i   y: %i to %i",x_idx_min, x_idx_max, y_idx_min,y_idx_max);
+                                                       for (y= y_idx_min; y<=y_idx_max;y++)
+                                                       {
+                                                               for (x=x_idx_min;x<= x_idx_max;x++)
+                                                               {
+                                                                               intersect_x = min(box->high.x, xmin+ (x+1) * (xmax-xmin)/histo->boxesPerSide ) -
+                                                                                                       max(box->low.x, xmin+ x*(xmax-xmin)/histo->boxesPerSide ) ;
+                                                                               intersect_y = min(box->high.y, ymin+ (y+1) * (ymax-ymin)/histo->boxesPerSide ) -
+                                                                                                       max(box->low.y, ymin+ y*(ymax-ymin)/histo->boxesPerSide ) ;
+
+                                                                                       // for a point, intersect_x=0, intersect_y=0, box_area =0
+               //elog(NOTICE,"x=%i,y=%i, intersect_x= %.15g, intersect_y = %.15g",x,y,intersect_x,intersect_y);
+                                                                               if ( (intersect_x>=0) && (intersect_y>=0) )
+                                                                               {
+                                                                                       area_intersect = intersect_x*intersect_y;
+                                                                                       if (area_intersect >= box_area*0.05)
+                                                                                       {
+               //elog(NOTICE,"bump");
+                                                                                               bump++;
+                                                                                               result->value[x+y*histo->boxesPerSide]++;
+                                                                                       }
+                                                                               }
+                                                               }
+                                                       } // End of y
+
+                                               } // End isnull
+
+                                       } // End of for loop
+
+                                       // Free all the results after each fetch, otherwise all tuples stay
+                                       // in memory until the end of the table...
+                                       SPI_freetuptable(tuptable);
+
+                               } else {
+                                               moredata = FALSE;
+                               } // End of if ntuples > 0
+
+                       } // End of while loop
+
+
+               // Close the cursor
+               SPI_cursor_close(SPIportal);
+
+           SPIcode =SPI_finish();
+           if (SPIcode  != SPI_OK_FINISH )
+               {
+                                       elog(ERROR,"build_histogram2d: couldnt disconnect from SPI");
+                                       PG_RETURN_NULL() ;
+               }
+
+               //elog(NOTICE,"finishing up build_histogram2d ");
+
+       //pfree(tablename);
+       //pfree(columnname);
+
+       total = 0;
+       for(t=0;t<histo->boxesPerSide*histo->boxesPerSide;t++)
+       {
+               total+=result->value[t];
+       }
+       //elog(NOTICE ,"histogram finishes with %i items in it - acutally added %i rows and %i bumps\n",total,sum_area_numb_new,bump);
+       //elog(NOTICE,"done build_histogram2d ");
+
+
+       //re-calculate statistics on avg bbox size
+       if (sum_area_numb_new >0)
+               result->avgFeatureArea = (sum_area_new+sum_area)/((double)(sum_area_numb_new+sum_area_numb));
+
+       PG_RETURN_POINTER(result) ;
+}
+
+
+
+
+
+#if USE_VERSION < 80
+/*
+ * get_restriction_var
+ *             Examine the args of a restriction clause to see if it's of the
+ *             form (var op something) or (something op var).  If so, extract
+ *             and return the var and the other argument.
+ *
+ * Inputs:
+ *     args: clause argument list
+ *     varRelid: see specs for restriction selectivity functions
+ *
+ * Outputs: (these are set only if TRUE is returned)
+ *     *var: gets Var node
+ *     *other: gets other clause argument
+ *     *varonleft: set TRUE if var is on the left, FALSE if on the right
+ *
+ * Returns TRUE if a Var is identified, otherwise FALSE.
+ */
+static bool
+get_restriction_var(List *args,
+                                       int varRelid,
+                                       Var **var,
+                                       Node **other,
+                                       bool *varonleft)
+{
+       Node       *left,
+                          *right;
+
+       if (length(args) != 2)
+               return false;
+
+       left = (Node *) lfirst(args);
+       right = (Node *) lsecond(args);
+
+       /* Ignore any binary-compatible relabeling */
+
+       if (IsA(left, RelabelType))
+               left = (Node *)((RelabelType *) left)->arg;
+       if (IsA(right, RelabelType))
+               right = (Node *)((RelabelType *) right)->arg;
+
+       /* Look for the var */
+
+       if (IsA(left, Var) &&
+               (varRelid == 0 || varRelid == ((Var *) left)->varno))
+       {
+               *var = (Var *) left;
+               *other = right;
+               *varonleft = true;
+       }
+       else if (IsA(right, Var) &&
+                        (varRelid == 0 || varRelid == ((Var *) right)->varno))
+       {
+               *var = (Var *) right;
+               *other = left;
+               *varonleft = false;
+       }
+       else
+       {
+               /* Duh, it's too complicated for me... */
+               return false;
+       }
+
+       return true;
+}
+
+//restriction in the GiST && operator
+PG_FUNCTION_INFO_V1(postgis_gist_sel);
+Datum postgis_gist_sel(PG_FUNCTION_ARGS)
+{
+               Query      *root = (Query *) PG_GETARG_POINTER(0);
+       //      Oid                     operator = PG_GETARG_OID(1);
+               List       *args = (List *) PG_GETARG_POINTER(2);
+               int                     varRelid = PG_GETARG_INT32(3);
+               GEOMETRY        *in;
+               BOX                     *search_box;
+               char            sql[1000];
+
+               SPITupleTable *tuptable;
+               TupleDesc tupdesc ;
+               HeapTuple tuple ;
+
+               Datum datum;
+               bool isnull;
+
+
+               Var     *var;
+               Node    *other;
+               bool varonleft;
+               Oid relid;
+               int SPIcode;
+
+               double myest;
+
+#ifndef USE_STATS
+       PG_RETURN_FLOAT8(0.000005);
+#endif
+
+                 //PG_RETURN_FLOAT8(0.000005);
+
+               //elog(NOTICE,"postgis_gist_sel was called");
+
+               if (!get_restriction_var(args, varRelid,
+                                                        &var, &other, &varonleft))
+               {
+                       //elog(NOTICE,"get_restriction_var FAILED -returning early");
+                       PG_RETURN_FLOAT8(0.000005);
+               }
+
+               relid = getrelid(var->varno, root->rtable);
+               if (relid == InvalidOid)
+               {
+                       //elog(NOTICE,"getrelid FAILED (invalid oid) -returning early");
+                       PG_RETURN_FLOAT8(0.000005);
+               }
+
+               //elog(NOTICE,"operator's oid = %i (this should be GEOMETRY && GEOMETRY)",operator);
+               //elog(NOTICE,"relations' oid = %i (this should be the relation that the && is working on) ",relid);
+               //elog(NOTICE,"varatt oid = %i (basically relations column #) ",var->varattno);
+
+
+               if (IsA(other, Const) &&((Const *) other)->constisnull)
+               {
+                       //elog(NOTICE,"other operand of && is NULL - returning early");
+                       PG_RETURN_FLOAT8(0.000005);
+               }
+
+               if (IsA(other, Const))
+               {
+                       //elog(NOTICE,"The other side of the && is a constant with type (oid) = %i and length %i.  This should be GEOMETRY with length -1 (variable length)",((Const*)other)->consttype,((Const*)other)->constlen);
+
+               }
+               else
+               {
+                       //elog(NOTICE,"the other side of && isnt a constant - returning early");
+                       PG_RETURN_FLOAT8(0.000005);
+               }
+
+               //get the BOX thats being searched in
+               in = (GEOMETRY*)PG_DETOAST_DATUM( ((Const*)other)->constvalue );
+               search_box = convert_box3d_to_box(&in->bvol);
+
+               //elog(NOTICE,"requested search box is : (%.15g %.15g, %.15g %.15g)",search_box->low.x,search_box->low.y,search_box->high.x,search_box->high.y);
+
+
+                       SPIcode = SPI_connect();
+                       if (SPIcode  != SPI_OK_CONNECT)
+                       {
+                               elog(NOTICE,"postgis_gist_sel: couldnt open a connection to SPI:%i",SPIcode);
+                               PG_RETURN_FLOAT8(0.000005) ;
+                       }
+
+                       sprintf(sql,"SELECT stats FROM GEOMETRY_COLUMNS WHERE attrelid=%u AND varattnum=%i",relid,var->varattno);
+                       //elog(NOTICE,"sql:%s",sql);
+                       SPIcode = SPI_exec(sql, 1 );
+                       if (SPIcode  != SPI_OK_SELECT )
+                       {
+                               SPI_finish();
+                               elog(NOTICE,"postgis_gist_sel: couldnt execute sql via SPI");
+                               PG_RETURN_FLOAT8(0.000005) ;
+                       }
+
+                       if (SPI_processed !=1)
+                       {
+                               SPI_finish();
+                               //elog(NOTICE,"postgis_gist_sel: geometry_columns didnt return a unique value");
+                               PG_RETURN_FLOAT8(0.000005) ;
+                       }
+
+                       tuptable = SPI_tuptable;
+                       tupdesc = SPI_tuptable->tupdesc;
+                       tuple = tuptable->vals[0];
+                       datum = SPI_getbinval(tuple, tupdesc, 1, &isnull);
+                       if (isnull)
+                       {
+                               SPI_finish();
+                               //elog(NOTICE,"postgis_gist_sel: geometry_columns returned a null histogram");
+                               PG_RETURN_FLOAT8(0.000005) ;
+                       }
+//elog(NOTICE,"postgis_gist_sel: checking against estimate_histogram2d");
+                       // now we have the histogram, and our search box - use the estimate_histogram2d(histo,box) to get the result!
+                       myest =
+                                       DatumGetFloat8( DirectFunctionCall2( estimate_histogram2d, datum, PointerGetDatum(search_box) ) );
+
+                       if ( (myest<0) || (myest!=myest) ) // <0?  or NaN?
+                       {
+                               //elog(NOTICE,"postgis_gist_sel: got something crazy back from estimate_histogram2d");
+                               PG_RETURN_FLOAT8(0.000005) ;
+                       }
+
+
+
+
+                       SPIcode =SPI_finish();
+                       if (SPIcode  != SPI_OK_FINISH )
+                       {
+                               //elog(NOTICE,"postgis_gist_sel: couldnt disconnect from SPI");
+                               PG_RETURN_FLOAT8(0.000005) ;
+                       }
+//elog(NOTICE,"postgis_gist_sel: finished, returning with %lf",myest);
+        PG_RETURN_FLOAT8(myest);
+}
+
+static void
+genericcostestimate2(Query *root, RelOptInfo *rel,
+                                       IndexOptInfo *index, List *indexQuals,
+                                       Cost *indexStartupCost,
+                                       Cost *indexTotalCost,
+                                       Selectivity *indexSelectivity,
+                                       double *indexCorrelation)
+{
+       double          numIndexTuples;
+       double          numIndexPages;
+       List            *selectivityQuals = indexQuals;
+#if USE_VERSION >= 74
+       QualCost        index_qual_cost;
+#endif
+
+
+//elog(NOTICE,"in genericcostestimate");
+
+
+       /*
+        * If the index is partial, AND the index predicate with the
+        * explicitly given indexquals to produce a more accurate idea of the
+        * index restriction.  This may produce redundant clauses, which we
+        * hope that cnfify and clauselist_selectivity will deal with
+        * intelligently.
+        *
+        * Note that index->indpred and indexQuals are both in implicit-AND form
+        * to start with, which we have to make explicit to hand to
+        * canonicalize_qual, and then we get back implicit-AND form again.
+        */
+       if (index->indpred != NIL)
+       {
+               Expr       *andedQuals;
+
+               andedQuals = make_ands_explicit(nconc(listCopy(index->indpred),
+                                                                                         indexQuals));
+               selectivityQuals = canonicalize_qual(andedQuals, true);
+       }
+
+
+
+       /* Estimate the fraction of main-table tuples that will be visited */
+       *indexSelectivity = clauselist_selectivity(root, selectivityQuals,
+#if USE_VERSION < 74
+               lfirsti(rel->relids));
+#else
+               rel->relid, JOIN_INNER);
+#endif
+
+       /*
+        * Estimate the number of tuples that will be visited.  We do it in
+        * this rather peculiar-looking way in order to get the right answer
+        * for partial indexes.  We can bound the number of tuples by the
+        * index size, in any case.
+        */
+       numIndexTuples = *indexSelectivity * rel->tuples;
+
+       //elog(NOTICE,"relation has %li pages",rel->pages);
+       //elog(NOTICE,"indexselectivity is %lf, ntuples = %lf, numindexTuples = %lf, index->tuples = %lf",*indexSelectivity, rel->tuples, numIndexTuples,index->tuples);
+
+
+       if (numIndexTuples > index->tuples)
+               numIndexTuples = index->tuples;
+
+       /*
+        * Always estimate at least one tuple is touched, even when
+        * indexSelectivity estimate is tiny.
+        */
+       if (numIndexTuples < 1.0)
+               numIndexTuples = 1.0;
+
+       /*
+        * Estimate the number of index pages that will be retrieved.
+        *
+        * For all currently-supported index types, the first page of the index
+        * is a metadata page, and we should figure on fetching that plus a
+        * pro-rated fraction of the remaining pages.
+        */
+
+
+        //elog(NOTICE,"index->pages =  %li ",index->pages);
+
+       if (index->pages > 1 && index->tuples > 0)
+       {
+               numIndexPages = (numIndexTuples / index->tuples) * (index->pages - 1);
+               numIndexPages += 1;             /* count the metapage too */
+               numIndexPages = ceil(numIndexPages);
+       }
+       else
+               numIndexPages = 1.0;
+
+
+//elog(NOTICE,"numIndexPages =  %lf ",numIndexPages);
+       /*
+        * Compute the index access cost.
+        *
+        * Our generic assumption is that the index pages will be read
+        * sequentially, so they have cost 1.0 each, not random_page_cost.
+        * Also, we charge for evaluation of the indexquals at each index
+        * tuple. All the costs are assumed to be paid incrementally during
+        * the scan.
+        */
+
+#if USE_VERSION < 74
+       *indexStartupCost = 0;
+       *indexTotalCost = numIndexPages +
+               (cpu_index_tuple_cost + cost_qual_eval(indexQuals)) *
+               numIndexTuples;
+#else
+       cost_qual_eval(&index_qual_cost, indexQuals);
+       *indexStartupCost = index_qual_cost.startup;
+       *indexTotalCost = numIndexPages +
+               (cpu_index_tuple_cost + index_qual_cost.per_tuple) *
+               numIndexTuples;
+#endif
+
+
+       //elog(NOTICE,"cpu_index_tuple_cost = %lf, cost_qual_eval(indexQuals)) = %lf",
+       //      cpu_index_tuple_cost,cost_qual_eval(indexQuals));
+
+       //elog(NOTICE,"indexTotalCost =  %lf ",*indexTotalCost);
+
+       /*
+        * Generic assumption about index correlation: there isn't any.
+        */
+       *indexCorrelation = 0.97;
+       //elog(NOTICE,"indexcorrelation =  %lf ",*indexCorrelation);
+}
+
+
+PG_FUNCTION_INFO_V1(postgisgistcostestimate);
+Datum
+postgisgistcostestimate(PG_FUNCTION_ARGS)
+{
+    Query      *root = (Query *) PG_GETARG_POINTER(0);
+    RelOptInfo *rel = (RelOptInfo *) PG_GETARG_POINTER(1);
+    IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(2);
+    List       *indexQuals = (List *) PG_GETARG_POINTER(3);
+    Cost       *indexStartupCost = (Cost *) PG_GETARG_POINTER(4);
+    Cost       *indexTotalCost = (Cost *) PG_GETARG_POINTER(5);
+    Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(6);
+    double     *indexCorrelation = (double *) PG_GETARG_POINTER(7);
+
+//elog(NOTICE,"postgisgistcostestimate was called");
+
+    genericcostestimate2(root, rel, index, indexQuals,
+                        indexStartupCost, indexTotalCost,
+                        indexSelectivity, indexCorrelation);
+//elog(NOTICE,"postgisgistcostestimate is going to return void");
+
+    PG_RETURN_VOID();
+}
+
+#else // USE_VERSION >= 80
+
+/*
+ * This function returns an estimate of the selectivity
+ * of a search_box looking at data in the GEOM_STATS 
+ * structure.
+ *
+ * TODO: handle box dimension collapses
+ */
+static float8
+estimate_selectivity(BOX *box, GEOM_STATS *geomstats)
+{
+       int x, y;
+       int x_idx_min, x_idx_max, y_idx_min, y_idx_max;
+       double intersect_x, intersect_y, AOI;
+       double cell_area, box_area;
+       double geow, geoh; // width and height of histogram
+       int histocols, historows; // histogram grid size
+       double value;
+       float overlapping_cells;
+       float avg_feat_cells;
+       double gain;
+       float8 selectivity;
+
+
+       /*
+        * Search box completely miss histogram extent
+        */
+       if ( box->high.x < geomstats->xmin ||
+               box->low.x > geomstats->xmax ||
+               box->high.y < geomstats->ymin ||
+               box->low.y > geomstats->ymax )
+       {
+#if DEBUG_GEOMETRY_STATS
+               elog(NOTICE, " search_box does not overlaps histogram, returning 0");
+#endif
+               return 0.0;
+       }
+
+       /*
+        * Search box completely contains histogram extent
+        */
+       if ( box->high.x >= geomstats->xmax &&
+               box->low.x <= geomstats->xmin &&
+               box->high.y >= geomstats->ymax &&
+               box->low.y <= geomstats->ymin )
+       {
+#if DEBUG_GEOMETRY_STATS
+               elog(NOTICE, " search_box contains histogram, returning 1");
+#endif
+               return 1.0;
+       }
+
+       geow = geomstats->xmax-geomstats->xmin;
+       geoh = geomstats->ymax-geomstats->ymin;
+
+       histocols = geomstats->cols;
+       historows = geomstats->rows;
+
+       cell_area = (geow*geoh) / (histocols*historows);
+       box_area = (box->high.x-box->low.x)*(box->high.y-box->low.y);
+       value = 0;
+
+       /* Find first overlapping column */
+       x_idx_min = (box->low.x-geomstats->xmin) / geow * histocols;
+       if (x_idx_min < 0) {
+#if DEBUG_GEOMETRY_STATS
+               elog(NOTICE, " search_box overlaps %d columns on the left of histogram grid", -x_idx_min);
+#endif
+               // should increment the value somehow
+               x_idx_min = 0;
+       }
+       if (x_idx_min >= histocols)
+       {
+#if DEBUG_GEOMETRY_STATS
+               elog(NOTICE, " search_box overlaps %d columns on the right of histogram grid", x_idx_min-histocols+1);
+#endif
+               // should increment the value somehow
+               x_idx_min = histocols-1;
+       }
+
+       /* Find first overlapping row */
+       y_idx_min = (box->low.y-geomstats->ymin) / geoh * historows;
+       if (y_idx_min <0)
+       {
+#if DEBUG_GEOMETRY_STATS
+               elog(NOTICE, " search_box overlaps %d columns on the bottom of histogram grid", -y_idx_min);
+#endif
+               // should increment the value somehow
+               y_idx_min = 0;
+       }
+       if (y_idx_min >= historows)
+       {
+#if DEBUG_GEOMETRY_STATS
+               elog(NOTICE, " search_box overlaps %d columns on the top of histogram grid", y_idx_min-historows+1);
+#endif
+               // should increment the value somehow
+               y_idx_min = historows-1;
+       }
+
+       /* Find last overlapping column */
+       x_idx_max = (box->high.x-geomstats->xmin) / geow * histocols;
+       if (x_idx_max <0)
+       {
+               // should increment the value somehow
+               x_idx_max = 0;
+       }
+       if (x_idx_max >= histocols )
+       {
+               // should increment the value somehow
+               x_idx_max = histocols-1;
+       }
+
+       /* Find last overlapping row */
+       y_idx_max = (box->high.y-geomstats->ymin) / geoh * historows;
+       if (y_idx_max <0)
+       {
+               // should increment the value somehow
+               y_idx_max = 0;
+       }
+       if (y_idx_max >= historows)
+       {
+               // should increment the value somehow
+               y_idx_max = historows-1;
+       }
+
+       /*
+        * the {x,y}_idx_{min,max}
+        * define the grid squares that the box intersects
+        */
+       for (y=y_idx_min; y<=y_idx_max; y++)
+       {
+               for (x=x_idx_min; x<=x_idx_max; x++)
+               {
+                       double val;
+                       double gain;
+
+                       val = geomstats->value[x+y*histocols];
+
+                       /*
+                        * Of the cell value we get
+                        * only the overlap fraction.
+                        */
+
+                       intersect_x = min(box->high.x, geomstats->xmin + (x+1) * geow / histocols) - max(box->low.x, geomstats->xmin + x * geow / histocols );
+                       intersect_y = min(box->high.y, geomstats->ymin + (y+1) * geoh / historows) - max(box->low.y, geomstats->ymin+ y * geoh / historows) ;
+                       
+                       AOI = intersect_x*intersect_y;
+                       gain = AOI/cell_area;
+
+#if DEBUG_GEOMETRY_STATS > 1
+                       elog(NOTICE, " [%d,%d] cell val %.15f",
+                                       x, y, val);
+                       elog(NOTICE, " [%d,%d] AOI %.15f",
+                                       x, y, AOI);
+                       elog(NOTICE, " [%d,%d] gain %.15f",
+                                       x, y, gain);
+#endif
+
+                       val *= gain;
+
+#if DEBUG_GEOMETRY_STATS > 1
+                       elog(NOTICE, " [%d,%d] adding %.15f to value",
+                               x, y, val);
+#endif
+                       value += val;
+               }
+       }
+
+
+       /*
+        * If the search_box is a point, it will
+        * overlap a single cell and thus get
+        * it's value, which is the fraction of
+        * samples (we can presume of row set also)
+        * which bumped to that cell.
+        *
+        * If the table features are points, each
+        * of them will overlap a single histogram cell.
+        * Our search_box value would then be correctly
+        * computed as the sum of the bumped cells values.
+        *
+        * If both our search_box AND the sample features
+        * overlap more then a single histogram cell we
+        * need to consider the fact that our sum computation
+        * will have many duplicated included. E.g. each
+        * single sample feature would have contributed to
+        * raise the search_box value by as many times as
+        * many cells in the histogram are commonly overlapped
+        * by both searc_box and feature. We should then
+        * divide our value by the number of cells in the virtual
+        * 'intersection' between average feature cell occupation
+        * and occupation of the search_box. This is as 
+        * fuzzy as you understand it :)
+        *
+        * Consistency check: whenever the number of cells is
+        * one of whichever part (search_box_occupation,
+        * avg_feature_occupation) the 'intersection' must be 1.
+        * If sounds that our 'intersaction' is actually the
+        * minimun number between search_box_occupation and
+        * avg_feat_occupation.
+        *
+        */
+       overlapping_cells = (x_idx_max-x_idx_min+1) *
+               (y_idx_max-y_idx_min+1);
+       avg_feat_cells = geomstats->avgFeatureCells;
+
+#if DEBUG_GEOMETRY_STATS
+elog(NOTICE, " search_box overlaps %f cells", overlapping_cells);
+elog(NOTICE, " avg feat overlaps %f cells", avg_feat_cells);
+#endif
+
+       gain = 1/min(overlapping_cells, avg_feat_cells);
+       selectivity = value*gain;
+
+#if DEBUG_GEOMETRY_STATS
+       elog(NOTICE, " SUM(ov_histo_cells)=%f", value);
+       elog(NOTICE, " gain=%f", gain);
+       elog(NOTICE, " selectivity=%f", selectivity);
+#endif
+
+       /* prevent rounding overflows */
+       if (selectivity > 1.0) selectivity = 1.0;
+       else if (selectivity < 0) selectivity = 0.0;
+
+       return selectivity;
+}
+
+/*
+ * This function should return an estimation of the number of
+ * rows returned by a query involving an overlap check 
+ * ( it's the restrict function for the && operator )
+ *
+ * It can make use (if available) of the statistics collected
+ * by the geometry analyzer function.
+ *
+ * Note that the good work is done by estimate_selectivity() above.
+ * This function just tries to find the search_box, loads the statistics
+ * and invoke the work-horse.
+ *
+ * This is the one used for PG version >= 7.5
+ *
+ */
+PG_FUNCTION_INFO_V1(postgis_gist_sel);
+Datum postgis_gist_sel(PG_FUNCTION_ARGS)
+{
+       Query *root = (Query *) PG_GETARG_POINTER(0);
+       //Oid operator = PG_GETARG_OID(1);
+       List *args = (List *) PG_GETARG_POINTER(2);
+       int varRelid = PG_GETARG_INT32(3);
+       Oid relid;
+       HeapTuple stats_tuple;
+       GEOM_STATS *geomstats;
+       int geomstats_nvalues=0;
+       Node *other;
+       Var *self;
+       GEOMETRY *in;
+       BOX *search_box;
+       float8 selectivity=0;
+
+#if DEBUG_GEOMETRY_STATS
+       elog(NOTICE, "postgis_gist_sel called");
+#endif
+
+        /* Fail if not a binary opclause (probably shouldn't happen) */
+       if (list_length(args) != 2)
+       {
+#if DEBUG_GEOMETRY_STATS
+               elog(NOTICE, "postgis_gist_sel: not a binary opclause");
+#endif
+               PG_RETURN_FLOAT8(DEFAULT_GEOMETRY_SEL);
+       }
+
+
+       /*
+        * Find the constant part
+        */
+       other = (Node *) linitial(args);
+       if ( ! IsA(other, Const) ) 
+       {
+               self = (Var *)other;
+               other = (Node *) lsecond(args);
+       }
+       else
+       {
+               self = (Var *) lsecond(args);
+       }
+
+       if ( ! IsA(other, Const) ) 
+       {
+#if DEBUG_GEOMETRY_STATS
+               elog(NOTICE, " no constant arguments - returning default selectivity");
+#endif
+               PG_RETURN_FLOAT8(DEFAULT_GEOMETRY_SEL);
+       }
+
+       /*
+        * We are working on two constants..
+        * TODO: check if expression is true,
+        *       returned set would be either 
+        *       the whole or none.
+        */
+       if ( ! IsA(self, Var) ) 
+       {
+#if DEBUG_GEOMETRY_STATS
+               elog(NOTICE, " no variable argument ? - returning default selectivity");
+#endif
+               PG_RETURN_FLOAT8(DEFAULT_GEOMETRY_SEL);
+       }
+
+       /*
+        * Convert the constant to a BOX
+        */
+
+       in = (GEOMETRY*)PG_DETOAST_DATUM( ((Const*)other)->constvalue );
+       search_box = convert_box3d_to_box(&in->bvol);
+#if DEBUG_GEOMETRY_STATS > 1
+       elog(NOTICE," requested search box is : %.15g %.15g, %.15g %.15g",search_box->low.x,search_box->low.y,search_box->high.x,search_box->high.y);
+#endif
+
+       /*
+        * Get pg_statistic row
+        */
+
+       relid = getrelid(varRelid, root->rtable);
+
+       stats_tuple = SearchSysCache(STATRELATT, ObjectIdGetDatum(relid), Int16GetDatum(self->varattno), 0, 0);
+       if ( ! stats_tuple )
+       {
+#if DEBUG_GEOMETRY_STATS
+               elog(NOTICE, " No statistics, returning default estimate");
+#endif
+               PG_RETURN_FLOAT8(DEFAULT_GEOMETRY_SEL);
+       }
+
+
+       if ( ! get_attstatsslot(stats_tuple, 0, 0,
+               STATISTIC_KIND_GEOMETRY, InvalidOid, NULL, NULL,
+               (float4 **)&geomstats, &geomstats_nvalues) )
+       {
+#if DEBUG_GEOMETRY_STATS
+               elog(NOTICE, " STATISTIC_KIND_GEOMETRY stats not found - returning default geometry selectivity");
+#endif
+               ReleaseSysCache(stats_tuple);
+               PG_RETURN_FLOAT8(DEFAULT_GEOMETRY_SEL);
+
+       }
+
+#if DEBUG_GEOMETRY_STATS > 1
+               elog(NOTICE, " %d read from stats", geomstats_nvalues);
+#endif
+
+#if DEBUG_GEOMETRY_STATS > 1
+       elog(NOTICE, " histo: xmin,ymin: %f,%f",
+               geomstats->xmin, geomstats->ymin);
+       elog(NOTICE, " histo: xmax,ymax: %f,%f",
+               geomstats->xmax, geomstats->ymax);
+       elog(NOTICE, " histo: cols: %f", geomstats->rows);
+       elog(NOTICE, " histo: rows: %f", geomstats->cols);
+       elog(NOTICE, " histo: avgFeatureArea: %f", geomstats->avgFeatureArea);
+       elog(NOTICE, " histo: avgFeatureCells: %f", geomstats->avgFeatureCells);
+#endif
+
+       /*
+        * Do the estimation
+        */
+       selectivity = estimate_selectivity(search_box, geomstats);
+
+
+#if DEBUG_GEOMETRY_STATS
+       elog(NOTICE, " returning computed value: %f", selectivity);
+#endif
+
+       free_attstatsslot(0, NULL, 0, (float *)geomstats, geomstats_nvalues);
+       ReleaseSysCache(stats_tuple);
+       PG_RETURN_FLOAT8(selectivity);
+
+}
+
+/*
+ * This function is called by the analyze function iff
+ * the geometry_analyze() function give it its pointer
+ * (this is always the case so far).
+ * The geometry_analyze() function is also responsible
+ * of deciding the number of "sample" rows we will receive
+ * here. It is able to give use other 'custom' data, but we
+ * won't use them so far.
+ *
+ * Our job is to build some statistics on the sample data
+ * for use by operator estimators.
+ *
+ * Currently we only need statistics to estimate the number of rows
+ * overlapping a given extent (estimation function bound
+ * to the && operator).
+ *
+ */
+static void
+compute_geometry_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc,
+               int samplerows, double totalrows)
+{
+       MemoryContext old_context;
+       int i;
+       int geom_stats_size;
+       BOX **sampleboxes;
+       GEOM_STATS *geomstats;
+       bool isnull;
+       int null_cnt=0, notnull_cnt=0, examinedsamples=0;
+       BOX3D *sample_extent=NULL;
+       double total_width=0;
+       double total_boxes_area=0;
+       int total_boxes_cells=0;
+       double cell_area;
+       double cell_width;
+       double cell_height;
+#if USE_STANDARD_DEVIATION
+       /* for standard deviation */
+       double avgLOWx, avgLOWy, avgHIGx, avgHIGy;
+       double sumLOWx=0, sumLOWy=0, sumHIGx=0, sumHIGy=0;
+       double sdLOWx=0, sdLOWy=0, sdHIGx=0, sdHIGy=0;
+       BOX *newhistobox=NULL;
+#endif
+       double geow, geoh; // width and height of histogram
+       int histocells;
+       int cols, rows; // histogram grid size
+       BOX histobox;
+
+       /*
+        * This is where geometry_analyze
+        * should put its' custom parameters.
+        */
+       //void *mystats = stats->extra_data;
+       
+       /*
+        * We'll build an histogram having from 40 to 400 boxesPerSide
+        * Total number of cells is determined by attribute stat
+        * target. It can go from  1600 to 160000 (stat target: 10,1000)
+        */
+       histocells = 160*stats->attr->attstattarget;
+
+
+#if DEBUG_GEOMETRY_STATS
+       elog(NOTICE, "compute_geometry_stats called");
+       elog(NOTICE, " samplerows: %d", samplerows);
+       elog(NOTICE, " histogram cells: %d", histocells);
+#endif
+
+       /*
+        * We might need less space, but don't think
+        * its worth saving...
+        */
+       sampleboxes = palloc(sizeof(BOX *)*samplerows);
+
+       /*
+        * First scan:
+        *  o find extent of the sample rows
+        *  o count null-infinite/not-null values
+        *  o compute total_width
+        *  o compute total features's box area (for avgFeatureArea)
+        *  o sum features box coordinates (for standard deviation)
+        */
+       for (i=0; i<samplerows; i++)
+       {
+               Datum datum;
+               GEOMETRY *geom;
+               BOX *box;
+
+               datum = fetchfunc(stats, i, &isnull);
+
+               /*
+                * Skip nulls
+                */
+               if ( isnull ) {
+                       null_cnt++;
+                       continue;
+               }
+
+               geom = (GEOMETRY *) PG_DETOAST_DATUM(datum);
+
+               /*
+                * Skip infinite geoms
+                */
+               if ( ! finite(geom->bvol.LLB.x) ||
+                       ! finite(geom->bvol.LLB.y) ||
+                       ! finite(geom->bvol.URT.x) ||
+                       ! finite(geom->bvol.URT.y) )
+               {
+#if DEBUG_GEOMETRY_STATS 
+                       elog(NOTICE, " skipped infinite geometry %d", i);
+#endif
+                       continue;
+               }
+
+               /*
+                * Cache bounding box
+                */
+               box = convert_box3d_to_box(&(geom->bvol));
+               sampleboxes[notnull_cnt] = box;
+
+               /*
+                * Add to sample extent union
+                */
+               sample_extent = union_box3d(&(geom->bvol), sample_extent);
+               
+               // TODO: ask if we need geom or bvol size for stawidth
+               total_width += geom->size;
+               total_boxes_area += (box->high.x-box->low.x)*(box->high.y-box->low.y);
+
+#if USE_STANDARD_DEVIATION
+               /*
+                * Add bvol coordinates to sum for standard deviation 
+                * computation.
+                */
+               sumLOWx += box->low.x;
+               sumLOWy += box->low.y;
+               sumHIGx += box->high.x;
+               sumHIGy += box->high.y;
+#endif
+
+               notnull_cnt++;
+
+               /* give backend a chance of interrupting us */
+               vacuum_delay_point();
+
+       }
+
+       if ( ! notnull_cnt ) {
+               elog(NOTICE, " no notnull values, invalid stats");
+               stats->stats_valid = false;
+               return;
+       }
+
+#if USE_STANDARD_DEVIATION
+
+#if DEBUG_GEOMETRY_STATS 
+       elog(NOTICE, " sample_extent: xmin,ymin: %f,%f",
+               sample_extent->LLB.x, sample_extent->LLB.y);
+       elog(NOTICE, " sample_extent: xmax,ymax: %f,%f",
+               sample_extent->URT.x, sample_extent->URT.y);
+#endif
+
+       /*
+        * Second scan:
+        *  o compute standard deviation
+        */
+       avgLOWx = sumLOWx/notnull_cnt;
+       avgLOWy = sumLOWy/notnull_cnt;
+       avgHIGx = sumHIGx/notnull_cnt;
+       avgHIGy = sumHIGy/notnull_cnt;
+       for (i=0; i<notnull_cnt; i++)
+       {
+               BOX *box;
+               box = (BOX *)sampleboxes[i];
+
+               sdLOWx += (box->low.x - avgLOWx) * (box->low.x - avgLOWx);
+               sdLOWy += (box->low.y - avgLOWy) * (box->low.y - avgLOWy);
+               sdHIGx += (box->high.x - avgHIGx) * (box->high.x - avgHIGx);
+               sdHIGy += (box->high.y - avgHIGy) * (box->high.y - avgHIGy);
+       }
+       sdLOWx = sqrt(sdLOWx/(notnull_cnt-1));
+       sdLOWy = sqrt(sdLOWy/(notnull_cnt-1));
+       sdHIGx = sqrt(sdHIGx/(notnull_cnt-1));
+       sdHIGy = sqrt(sdHIGy/(notnull_cnt-1));
+
+#if DEBUG_GEOMETRY_STATS 
+       elog(NOTICE, " standard deviations:");
+       elog(NOTICE, "  LOWx - avg:%f sd:%f", avgLOWx, sdLOWx);
+       elog(NOTICE, "  LOWy - avg:%f sd:%f", avgLOWy, sdLOWy);
+       elog(NOTICE, "  HIGx - avg:%f sd:%f", avgHIGx, sdHIGx);
+       elog(NOTICE, "  HIGy - avg:%f sd:%f", avgHIGy, sdHIGy);
+#endif
+
+       histobox.low.x = max((avgLOWx - SDFACTOR * sdLOWx),
+               sample_extent->LLB.x);
+       histobox.low.y = max((avgLOWy - SDFACTOR * sdLOWy),
+               sample_extent->LLB.y);
+       histobox.high.x = min((avgHIGx + SDFACTOR * sdHIGx),
+               sample_extent->URT.x); 
+       histobox.high.y = min((avgHIGy + SDFACTOR * sdHIGy),
+               sample_extent->URT.y); 
+
+#if DEBUG_GEOMETRY_STATS 
+       elog(NOTICE, " sd_extent: xmin,ymin: %f,%f",
+               histobox.low.x, histobox.low.y);
+       elog(NOTICE, " sd_extent: xmax,ymax: %f,%f",
+               histobox.high.x, histobox.high.y);
+#endif
+
+       /*
+        * Third scan:
+        *   o skip hard deviants
+        *   o compute new histogram box
+        */
+       for (i=0; i<notnull_cnt; i++)
+       {
+               BOX *box;
+               box = (BOX *)sampleboxes[i];
+
+               if ( box->low.x > histobox.high.x ||
+                       box->high.x < histobox.low.x ||
+                       box->low.y > histobox.high.y ||
+                       box->high.y < histobox.low.y )
+               {
+#if DEBUG_GEOMETRY_STATS > 1
+       elog(NOTICE, " feat %d is an hard deviant, skipped", i);
+#endif
+                       sampleboxes[i] = NULL;
+                       continue;
+               }
+               if ( ! newhistobox ) {
+                       newhistobox = palloc(sizeof(BOX));
+                       memcpy(newhistobox, box, sizeof(BOX));
+               } else {
+                       if ( box->low.x < newhistobox->low.x )
+                               newhistobox->low.x = box->low.x;
+                       if ( box->low.y < newhistobox->low.y )
+                               newhistobox->low.y = box->low.y;
+                       if ( box->high.x > newhistobox->high.x )
+                               newhistobox->high.x = box->high.x;
+                       if ( box->high.y > newhistobox->high.y )
+                               newhistobox->high.y = box->high.y;
+               }
+       }
+
+       /*
+        * Set histogram extent as the intersection between
+        * standard deviation based histogram extent 
+        * and computed sample extent after removal of
+        * hard deviants (there might be no hard deviants).
+        */
+       if ( histobox.low.x < newhistobox->low.x )
+               histobox.low.x = newhistobox->low.x;
+       if ( histobox.low.y < newhistobox->low.y )
+               histobox.low.y = newhistobox->low.y;
+       if ( histobox.high.x > newhistobox->high.x )
+               histobox.high.x = newhistobox->high.x;
+       if ( histobox.high.y > newhistobox->high.y )
+               histobox.high.y = newhistobox->high.y;
+
+
+#else // ! USE_STANDARD_DEVIATION
+
+       /*
+        * Set histogram extent box
+        */
+       histobox.low.x = sample_extent->LLB.x;
+       histobox.low.y = sample_extent->LLB.y;
+       histobox.high.x = sample_extent->URT.x;
+       histobox.high.y = sample_extent->URT.y;
+#endif // USE_STANDARD_DEVIATION
+
+
+#if DEBUG_GEOMETRY_STATS 
+       elog(NOTICE, " histogram_extent: xmin,ymin: %f,%f",
+               histobox.low.x, histobox.low.y);
+       elog(NOTICE, " histogram_extent: xmax,ymax: %f,%f",
+               histobox.high.x, histobox.high.y);
+#endif
+
+
+       geow = histobox.high.x - histobox.low.x;
+       geoh = histobox.high.y - histobox.low.y;
+
+       /*
+        * Compute histogram cols and rows based on aspect ratio
+        * of histogram extent
+        */
+       if ( ! geow && ! geoh ) {
+               cols = 1;
+               rows = 1;
+               histocells = 1;
+       } else if ( ! geow ) {
+               cols = 1;
+               rows = histocells;
+       } else if ( ! geoh ) {
+               cols = histocells;
+               rows = 1;
+       } else {
+               if ( geow<geoh) {
+                       cols = ceil(sqrt((double)histocells*(geow/geoh)));
+                       rows = ceil((double)histocells/cols);
+               } else {
+                       rows = ceil(sqrt((double)histocells*(geoh/geow)));
+                       cols = ceil((double)histocells/rows);
+               }
+               histocells = cols*rows;
+       }
+#if DEBUG_GEOMETRY_STATS 
+       elog(NOTICE, " computed histogram grid size (CxR): %dx%d (%d cells)", cols, rows, histocells);
+#endif
+
+
+       /*
+        * Create the histogram (GEOM_STATS)
+        */
+       old_context = MemoryContextSwitchTo(stats->anl_context);
+       geom_stats_size=sizeof(GEOM_STATS)+(histocells-1)*sizeof(float4);
+       geomstats = palloc(geom_stats_size);
+       MemoryContextSwitchTo(old_context);
+
+       geomstats->avgFeatureArea = total_boxes_area/notnull_cnt;
+       geomstats->xmin = histobox.low.x;
+       geomstats->ymin = histobox.low.y;
+       geomstats->xmax = histobox.high.x;
+       geomstats->ymax = histobox.high.y;
+       geomstats->cols = cols;
+       geomstats->rows = rows;
+
+       // Initialize all values to 0
+       for (i=0;i<histocells; i++) geomstats->value[i] = 0;
+
+       cell_width = geow/cols;
+       cell_height = geoh/rows;
+       cell_area = cell_width*cell_height;
+
+#if DEBUG_GEOMETRY_STATS > 2
+       elog(NOTICE, "cell_width: %f", cell_width);
+       elog(NOTICE, "cell_height: %f", cell_height);
+#endif
+       
+
+       /*
+        * Fourth scan:
+        *  o fill histogram values with the number of
+        *    features' bbox overlaps: a feature's bvol
+        *    can fully overlap (1) or partially overlap
+        *    (fraction of 1) an histogram cell.
+        *
+        *  o compute total cells occupation
+        *
+        */
+       for (i=0; i<notnull_cnt; i++)
+       {
+               BOX *box;
+               int x_idx_min, x_idx_max, x;
+               int y_idx_min, y_idx_max, y;
+               int numcells=0;
+
+               box = (BOX *)sampleboxes[i];
+               if ( ! box ) continue; // hard deviant..
+
+               /* give backend a chance of interrupting us */
+               vacuum_delay_point();
+
+#if DEBUG_GEOMETRY_STATS > 2
+               elog(NOTICE, " feat %d box is %f %f, %f %f",
+                               i, box->high.x, box->high.y,
+                               box->low.x, box->low.y);
+#endif
+                                                       
+               /* Find first overlapping column */
+               x_idx_min = (box->low.x-geomstats->xmin) / geow * cols;
+               if (x_idx_min <0) x_idx_min = 0;
+               if (x_idx_min >= cols) x_idx_min = cols-1;
+
+               /* Find first overlapping row */
+               y_idx_min = (box->low.y-geomstats->ymin) / geoh * rows;
+               if (y_idx_min <0) y_idx_min = 0;
+               if (y_idx_min >= rows) y_idx_min = rows-1;
+
+               /* Find last overlapping column */
+               x_idx_max = (box->high.x-geomstats->xmin) / geow * cols;
+               if (x_idx_max <0) x_idx_max = 0;
+               if (x_idx_max >= cols ) x_idx_max = cols-1;
+
+               /* Find last overlapping row */
+               y_idx_max = (box->high.y-geomstats->ymin) / geoh * rows;
+               if (y_idx_max <0) y_idx_max = 0;
+               if (y_idx_max >= rows) y_idx_max = rows-1;
+#if DEBUG_GEOMETRY_STATS > 2
+               elog(NOTICE, " feat %d overlaps columns %d-%d, rows %d-%d",
+                               i, x_idx_min, x_idx_max, y_idx_min, y_idx_max);
+#endif
+
+               /*
+                * the {x,y}_idx_{min,max}
+                * define the grid squares that the box intersects
+                */
+               for (y=y_idx_min; y<=y_idx_max; y++)
+               {
+                       for (x=x_idx_min; x<=x_idx_max; x++)
+                       {
+                               geomstats->value[x+y*cols] += 1;
+                               numcells++;
+                       }
+               }
+
+               // before adding to the total cells
+               // we could decide if we really 
+               // want this feature to count
+               total_boxes_cells += numcells;
+
+               examinedsamples++;
+       }
+#if DEBUG_GEOMETRY_STATS
+       elog(NOTICE, " examined_samples: %d/%d", examinedsamples, samplerows);
+#endif
+
+       if ( ! examinedsamples ) {
+               elog(NOTICE, " no examined values, invalid stats");
+               stats->stats_valid = false;
+#if DEBUG_GEOMETRY_STATS
+       elog(NOTICE, " no stats have been gathered");
+#endif
+               return;
+       }
+
+       // what about null features (TODO) ?
+       geomstats->avgFeatureCells = (float4)total_boxes_cells/examinedsamples;
+
+#if DEBUG_GEOMETRY_STATS
+       elog(NOTICE, " histo: total_boxes_cells: %d", total_boxes_cells);
+       elog(NOTICE, " histo: avgFeatureArea: %f", geomstats->avgFeatureArea);
+       elog(NOTICE, " histo: avgFeatureCells: %f", geomstats->avgFeatureCells);
+#endif
+
+
+       /*
+        * Normalize histogram
+        *
+        * We divide each histogram cell value
+        * by the number of samples examined.
+        *
+        */
+       for (i=0; i<histocells; i++)
+               geomstats->value[i] /= examinedsamples;
+
+#if DEBUG_GEOMETRY_STATS > 1
+       {
+               int x, y;
+               for (x=0; x<cols; x++)
+               {
+                       for (y=0; y<rows; y++)
+                       {
+                               elog(NOTICE, " histo[%d,%d] = %.15f", x, y, geomstats->value[x+y*cols]);
+                       }
+               }
+       }
+#endif
+
+       /*
+        * Write the statistics data 
+        */
+       stats->stakind[0] = STATISTIC_KIND_GEOMETRY;
+       stats->staop[0] = InvalidOid;
+       stats->stanumbers[0] = (float4 *)geomstats;
+       stats->numnumbers[0] = geom_stats_size/sizeof(float4);
+
+       stats->stanullfrac = null_cnt/samplerows;
+       stats->stawidth = total_width/notnull_cnt;
+       stats->stadistinct = -1.0;
+
+#if DEBUG_GEOMETRY_STATS
+       elog(NOTICE, " out: slot 0: kind %d (STATISTIC_KIND_GEOMETRY)",
+               stats->stakind[0]);
+       elog(NOTICE, " out: slot 0: op %d (InvalidOid)", stats->staop[0]);
+       elog(NOTICE, " out: slot 0: numnumbers %d", stats->numnumbers[0]);
+       elog(NOTICE, " out: null fraction: %d/%d", null_cnt, samplerows);
+       elog(NOTICE, " out: average width: %d bytes", stats->stawidth);
+       elog(NOTICE, " out: distinct values: all (no check done)");
+#endif
+
+       stats->stats_valid = true;
+}
+
+/*
+ * This function will be called when the ANALYZE command is run
+ * on a column of the "geometry" type.
+ * 
+ * It will need to return a stats builder function reference
+ * and a "minimum" sample rows to feed it.
+ * If we want analisys to be completely skipped we can return
+ * FALSE and leave output vals untouched.
+ *
+ * What we know from this call is:
+ *
+ *     o The pg_attribute row referring to the specific column.
+ *       Could be used to get reltuples from pg_class (which 
+ *       might quite inexact though...) and use them to set an
+ *       appropriate minimum number of sample rows to feed to
+ *       the stats builder. The stats builder will also receive
+ *       a more accurate "estimation" of the number or rows.
+ *
+ *     o The pg_type row for the specific column.
+ *       Could be used to set stat builder / sample rows
+ *       based on domain type (when postgis will be implemented
+ *       that way).
+ *
+ * Being this experimental we'll stick to a static stat_builder/sample_rows
+ * value for now.
+ *
+ */
+PG_FUNCTION_INFO_V1(geometry_analyze);
+Datum geometry_analyze(PG_FUNCTION_ARGS)
+{
+       VacAttrStats *stats = (VacAttrStats *)PG_GETARG_POINTER(0);
+       Form_pg_attribute attr = stats->attr;
+
+#if DEBUG_GEOMETRY_STATS
+       elog(NOTICE, "geometry_analyze called");
+#endif
+
+       /* If the attstattarget column is negative, use the default value */
+       /* NB: it is okay to scribble on stats->attr since it's a copy */
+        if (attr->attstattarget < 0)
+                attr->attstattarget = default_statistics_target;
+
+#if DEBUG_GEOMETRY_STATS
+       elog(NOTICE, " attribute stat target: %d", attr->attstattarget);
+#endif
+
+       /*
+        * There might be a reason not to analyze this column
+        * (can we detect the absence of an index?)
+        */
+       //elog(NOTICE, "compute_geometry_stats not implemented yet");
+       //PG_RETURN_BOOL(false);
+
+       /* Setup the minimum rows and the algorithm function */
+       stats->minrows = 300 * stats->attr->attstattarget;
+       stats->compute_stats = compute_geometry_stats;
+
+#if DEBUG_GEOMETRY_STATS
+       elog(NOTICE, " minrows: %d", stats->minrows);
+#endif
+
+       /* Indicate we are done successfully */
+       PG_RETURN_BOOL(true);
+}
+       
+
+
+#endif
+
+
diff --git a/hwgeom/postgis_fn.c b/hwgeom/postgis_fn.c
new file mode 100644 (file)
index 0000000..a449ffb
--- /dev/null
@@ -0,0 +1,3218 @@
+
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2001-2003 Refractions Research Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ *
+ **********************************************************************
+ * $Log$
+ * Revision 1.1  2004/09/20 07:50:06  strk
+ * prepared to contain old internal representation code
+ *
+ * Revision 1.40  2004/08/26 16:55:09  strk
+ * max_distance() raises an 'unimplemented yet' error.
+ *
+ * Revision 1.39  2004/07/28 16:10:59  strk
+ * Changed all version functions to return text.
+ * Renamed postgis_scripts_version() to postgis_scripts_installed()
+ * Added postgis_scripts_released().
+ * Added postgis_full_version().
+ *
+ * Revision 1.38  2004/07/28 13:37:43  strk
+ * Added postgis_uses_stats and postgis_scripts_version.
+ * Experimented with PIP short-circuit in within/contains functions.
+ * Documented new version functions.
+ *
+ * Revision 1.37  2004/07/22 16:20:10  strk
+ * Added postgis_lib_version() and postgis_geos_version()
+ *
+ * Revision 1.36  2004/06/03 16:44:56  strk
+ * Added expand_geometry - expand(geometry, int8)
+ *
+ * Revision 1.35  2004/04/28 22:26:02  pramsey
+ * Fixed spelling mistake in header text.
+ *
+ * Revision 1.34  2004/03/26 00:54:09  dblasby
+ * added full support for fluffType(<geom>)
+ * postgis09=# select fluffType('POINT(0 0)');
+ *         flufftype
+ *             -------------------------
+ *              SRID=-1;MULTIPOINT(0 0)
+ *
+ * Revision 1.33  2004/03/25 00:43:41  dblasby
+ * added function fluffType() that takes POINT LINESTRING or POLYGON
+ * type and converts it to a multi*.
+ * Needs to be integrated into a proper Postgresql function and given an
+ * SQL CREATE FUNCTION
+ *
+ * Revision 1.32  2004/02/12 10:34:49  strk
+ * changed USE_GEOS check from ifdef / ifndef to if / if !
+ *
+ * Revision 1.31  2003/11/11 10:58:43  strk
+ * Fixed a typo in envelope()
+ *
+ * Revision 1.30  2003/10/29 15:53:10  strk
+ * geoscentroid() removed. both geos and pgis versions are called 'centroid'.
+ * only one version will be compiled based on USE_GEOS flag.
+ *
+ * Revision 1.29  2003/10/28 16:57:35  strk
+ * Added collect_garray() function.
+ *
+ * Revision 1.28  2003/10/28 15:16:17  strk
+ * unite_sfunc() from postgis_geos.c renamed to geom_accum() and moved in postgis_fn.c
+ *
+ * Revision 1.27  2003/10/17 16:12:23  dblasby
+ * Made Envelope() CW instead of CCW.
+ *
+ * Revision 1.26  2003/10/17 16:07:05  dblasby
+ * made isEmpty() return true/false
+ *
+ * Revision 1.25  2003/09/16 20:27:12  dblasby
+ * added ability to delete geometries.
+ *
+ * Revision 1.24  2003/08/08 18:19:20  dblasby
+ * Conformance changes.
+ * Removed junk from postgis_debug.c and added the first run of the long
+ * transaction locking support.  (this will change - dont use it)
+ * conformance tests were corrected
+ * some dos cr/lf removed
+ * empty geometries i.e. GEOMETRYCOLLECT(EMPTY) added (with indexing support)
+ * pointN(<linestring>,1) now returns the first point (used to return 2nd)
+ *
+ * Revision 1.23  2003/07/25 17:08:37  pramsey
+ * Moved Cygwin endian define out of source files into postgis.h common
+ * header file.
+ *
+ * Revision 1.22  2003/07/01 18:30:55  pramsey
+ * Added CVS revision headers.
+ *
+ *
+ **********************************************************************/
+
+#include "postgres.h"
+
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "access/gist.h"
+#include "access/itup.h"
+#include "access/rtree.h"
+
+#include "fmgr.h"
+
+#include "postgis.h"
+#include "utils/elog.h"
+#include "utils/array.h"
+
+#define NfunctionFirstPoint 1
+
+// if you use #define NfunctionFirstPoint 0, you get 0-based indexing (this is what programmers want)::
+//  pointN(<linestring>, 0) is the 1st point, and pointN(<linestring>, 1) is the second point.
+
+// if you use #define NfunctionFirstPoint 1, you get 1-based indexing (which seems to be what the spec wants)::
+//  pointN(<linestring>, 1) is the 1st point, and pointN(<linestring>, 2) is the second point.
+
+
+#define SHOW_DIGS_DOUBLE 15
+#define MAX_DIGS_DOUBLE (SHOW_DIGS_DOUBLE + 6 + 1 + 3 +1)
+
+
+// #define DEBUG_GIST
+//#define DEBUG_GIST2
+
+
+
+
+/**************************************************************************
+ * GENERAL PURPOSE GEOMETRY FUNCTIONS
+ **************************************************************************/
+
+
+double line_length2d(LINE3D *line)
+{
+       int     i;
+       POINT3D *frm,*to;
+       double  dist = 0.0;
+
+       if (line->npoints <2)
+               return 0.0;     //must have >1 point to make sense
+
+       frm = &line->points[0];
+
+       for (i=1; i<line->npoints;i++)
+       {
+               to = &line->points[i];
+
+               dist += sqrt( ( (frm->x - to->x)*(frm->x - to->x) )  +
+                                 ( (frm->y - to->y)*(frm->y - to->y) ) );
+
+               frm = to;
+       }
+       return dist;
+}
+
+double line_length3d(LINE3D *line)
+{
+       int     i;
+       POINT3D *frm,*to;
+       double  dist = 0.0;
+
+       if (line->npoints <2)
+               return 0.0;     //must have >1 point to make sense
+
+       frm = &line->points[0];
+
+       for (i=1; i<line->npoints;i++)
+       {
+               to = &line->points[i];
+
+               dist += sqrt( ( (frm->x - to->x)*(frm->x - to->x) )  +
+                               ((frm->y - to->y)*(frm->y - to->y) ) +
+                               ((frm->z - to->z)*(frm->z - to->z) ) );
+
+               frm = to;
+       }
+       return dist;
+}
+
+
+//find the "length of a geometry"
+// length3d(point) = 0
+// length3d(line) = length of line
+// length3d(polygon) = 0  -- could make sense to return sum(ring perimeter)
+// uses euclidian 3d length
+
+PG_FUNCTION_INFO_V1(length3d);
+Datum length3d(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                   *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       int32                           *offsets1;
+       char                            *o1;
+       int32                           type1,j;
+       LINE3D                  *line;
+       double                  dist = 0.0;
+
+       offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
+
+
+       //now have to do a scan of each object
+
+       for (j=0; j< geom1->nobjs; j++)         //for each object in geom1
+       {
+               o1 = (char *) geom1 +offsets1[j] ;
+               type1=  geom1->objType[j];
+               if (type1 == LINETYPE)  //LINESTRING
+               {
+                       line = (LINE3D *) o1;
+                       dist += line_length3d(line);
+               }
+       }
+       PG_RETURN_FLOAT8(dist);
+}
+
+//find the "length of a geometry"
+// length3d(point) = 0
+// length3d(line) = length of line
+// length3d(polygon) = 0  -- could make sense to return sum(ring perimeter)
+// uses euclidian 2d length
+
+PG_FUNCTION_INFO_V1(length2d);
+Datum length2d(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                   *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       int32                           *offsets1;
+       char                            *o1;
+       int32                           type1,j;
+       LINE3D                  *line;
+       double                  dist = 0.0;
+
+       offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
+
+
+       //now have to do a scan of each object
+
+       for (j=0; j< geom1->nobjs; j++)         //for each object in geom1
+       {
+               o1 = (char *) geom1 +offsets1[j] ;
+               type1=  geom1->objType[j];
+               if (type1 == LINETYPE)  //LINESTRING
+               {
+                       line = (LINE3D *) o1;
+                       dist += line_length2d(line);
+               }
+       }
+       PG_RETURN_FLOAT8(dist);
+}
+
+
+//find the 2d area of the outer ring - sum (area 2d of inner rings)
+// Could use a more numerically stable calculator...
+double polygon_area2d_old(POLYGON3D *poly1)
+{
+       double  poly_area=0.0, ringarea=0.0;
+       int i,j,ring,pt_offset;
+       POINT3D *pts1;
+
+
+       pts1 = (POINT3D *) ( (char *)&(poly1->npoints[poly1->nrings] )  );
+       pts1 = (POINT3D *) MAXALIGN(pts1);
+
+//elog(NOTICE,"in polygon_area2d_old");
+
+       pt_offset = 0; //index to first point in ring
+       for (ring = 0; ring < poly1->nrings; ring++)
+       {
+               ringarea = 0.0;
+
+               for (i=0;i<(poly1->npoints[ring]-1);i++)
+       {
+               //      j = (i+1) % (poly1->npoints[ring]);
+                       j = i+1;
+                       ringarea  += pts1[pt_offset+ i].x * pts1[pt_offset+j].y - pts1[pt_offset+ i].y * pts1[pt_offset+j].x;
+               }
+
+               ringarea  /= 2.0;
+//elog(NOTICE," ring 1 has area %lf",ringarea);
+               ringarea  = fabs(ringarea );
+               if (ring != 0)  //outer
+                       ringarea  = -1.0*ringarea ; // its a hole
+
+               poly_area += ringarea;
+
+               pt_offset += poly1->npoints[ring];
+       }
+
+       return poly_area;
+}
+
+
+
+//calculate the area of all the subobj in a polygon
+// area(point) = 0
+// area (line) = 0
+// area(polygon) = find its 2d area
+PG_FUNCTION_INFO_V1(area2d);
+Datum area2d(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                      *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       int32                           *offsets1;
+       char                            *o1;
+       int32                           type1,j;
+       POLYGON3D                       *poly;
+       double                  area = 0.0;
+
+       offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
+
+       //now have to do a scan of each object
+
+       for (j=0; j< geom1->nobjs; j++)         //for each object in geom1
+       {
+               o1 = (char *) geom1 +offsets1[j] ;
+               type1=  geom1->objType[j];
+               if (type1 == POLYGONTYPE)       //POLYGON
+               {
+                       poly = (POLYGON3D *) o1;
+                       area += polygon_area2d_old(poly);
+               }
+       }
+       PG_RETURN_FLOAT8(area);
+}
+
+double         polygon_perimeter3d(POLYGON3D   *poly1)
+{
+       double  poly_perimeter=0.0, ring_perimeter=0.0;
+       int i,ring,pt_offset;
+       POINT3D *pts1;
+       POINT3D *to,*frm;
+
+
+       pts1 = (POINT3D *) ( (char *)&(poly1->npoints[poly1->nrings] )  );
+       pts1 = (POINT3D *) MAXALIGN(pts1);
+
+
+
+       pt_offset = 0; //index to first point in ring
+       for (ring = 0; ring < poly1->nrings; ring++)
+       {
+               ring_perimeter = 0.0;
+
+
+               frm = &pts1[pt_offset];
+
+               for (i=1; i<poly1->npoints[ring];i++)
+               {
+                       to = &pts1[pt_offset+ i];
+
+                       ring_perimeter += sqrt( ( (frm->x - to->x)*(frm->x - to->x) )  +
+                               ((frm->y - to->y)*(frm->y - to->y) ) +
+                               ((frm->z - to->z)*(frm->z - to->z) ) );
+
+                       frm = to;
+               }
+
+               poly_perimeter += ring_perimeter;
+
+               pt_offset += poly1->npoints[ring];
+       }
+
+       return poly_perimeter;
+
+}
+
+double         polygon_perimeter2d(POLYGON3D   *poly1)
+{
+       double  poly_perimeter=0.0, ring_perimeter=0.0;
+       int i,ring,pt_offset;
+       POINT3D *pts1;
+       POINT3D *to,*frm;
+
+
+       pts1 = (POINT3D *) ( (char *)&(poly1->npoints[poly1->nrings] )  );
+       pts1 = (POINT3D *) MAXALIGN(pts1);
+
+
+       pt_offset = 0; //index to first point in ring
+       for (ring = 0; ring < poly1->nrings; ring++)
+       {
+               ring_perimeter = 0.0;
+
+
+               frm = &pts1[pt_offset];
+
+               for (i=1; i<poly1->npoints[ring];i++)
+               {
+                       to = &pts1[pt_offset+ i];
+
+                       ring_perimeter += sqrt( ( (frm->x - to->x)*(frm->x - to->x) )  +
+                               ((frm->y - to->y)*(frm->y - to->y) )   );
+
+                       frm = to;
+               }
+
+               poly_perimeter += ring_perimeter;
+
+               pt_offset += poly1->npoints[ring];
+       }
+
+       return poly_perimeter;
+
+}
+
+
+
+//calculate the perimeter of polys (sum of length of all rings)
+PG_FUNCTION_INFO_V1(perimeter3d);
+Datum perimeter3d(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                      *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       int32                           *offsets1;
+       char                            *o1;
+       int32                           type1,j;
+       POLYGON3D                       *poly;
+       double                  area = 0.0;
+
+       offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
+
+       //now have to do a scan of each object
+
+       for (j=0; j< geom1->nobjs; j++)         //for each object in geom1
+       {
+               o1 = (char *) geom1 +offsets1[j] ;
+               type1=  geom1->objType[j];
+               if (type1 == POLYGONTYPE)       //POLYGON
+               {
+                       poly = (POLYGON3D *) o1;
+                       area += polygon_perimeter3d(poly);
+               }
+       }
+       PG_RETURN_FLOAT8(area);
+}
+
+//calculate the perimeter of polys (sum of length of all rings)
+PG_FUNCTION_INFO_V1(perimeter2d);
+Datum perimeter2d(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                      *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       int32                           *offsets1;
+       char                            *o1;
+       int32                           type1,j;
+       POLYGON3D                       *poly;
+       double                  area = 0.0;
+
+       offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
+
+       //now have to do a scan of each object
+
+       for (j=0; j< geom1->nobjs; j++)         //for each object in geom1
+       {
+               o1 = (char *) geom1 +offsets1[j] ;
+               type1=  geom1->objType[j];
+               if (type1 == POLYGONTYPE)       //POLYGON
+               {
+                       poly = (POLYGON3D *) o1;
+                       area += polygon_perimeter2d(poly);
+               }
+       }
+       PG_RETURN_FLOAT8(area);
+}
+
+
+// cn_PnPoly(): crossing number test for a point in a polygon
+//      input:   P = a point,
+//               V[] = vertex points of a polygon V[n+1] with V[n]=V[0]
+//      returns: 0 = outside, 1 = inside
+//
+//     Our polygons have first and last point the same,
+//
+int PIP( POINT3D *P, POINT3D *V, int n )
+{
+    int    cn = 0;    // the crossing number counter
+               int i;
+
+       double vt;
+
+    // loop through all edges of the polygon
+
+       for (i=0; i< (n-1) ; i++)
+       {    // edge from V[i] to V[i+1]
+
+               if (((V[i].y <= P->y) && (V[i+1].y > P->y))    // an upward crossing
+                        || ((V[i].y > P->y) && (V[i+1].y <= P->y))) // a downward crossing
+               {
+
+                  vt = (double)(P->y - V[i].y) / (V[i+1].y - V[i].y);
+                  if (P->x < V[i].x + vt * (V[i+1].x - V[i].x))                        // P.x <intersect
+                               ++cn;   // a valid crossing of y=P.y right of P.x
+       }
+    }
+    return (cn&1);    // 0 if even (out), and 1 if odd (in)
+
+}
+
+
+
+
+
+//this one is easy - is the point inside a box?
+bool point_truely_inside(POINT3D       *point, BOX3D   *box)
+{
+       return (
+                       (point->x >= box->LLB.x) && (point->x <= box->URT.x)  &&
+                     (point->y >= box->LLB.y) && (point->y <= box->URT.y)
+                );
+}
+
+
+// see if a linestring is inside a box - check to see if each its line
+//     segments are inside the box
+// NOTE: all this is happening in 2d - third dimention is ignored
+//
+//use a modified CohenSutherland line clipping algoritm
+//
+//Step one - compute outcodes for Pi and Pi+1
+//
+//
+//                     |               |
+//             1001    |   1000        |  1010
+//                     |               |
+//      ----------+-----------+------------
+//                     | (inside)      |
+//             0001    |               |  0010
+//                     |   0000        |
+//      ----------+-----------+------------
+//                     |               |
+//             0101    |   0100        |  0110
+//                     |               |
+//
+// If Pi or Pi+1 have code 0000 then this line seg is inside the box  (trivial accept)
+//  if Pi && Pi+1 != 0 then seg is NOT in the box (trivial reject)
+//
+//     Otherwise the line seg might traverse through the box.
+//             => see if the seg intersects any of the walls of the box.
+//
+
+int    compute_outcode( POINT3D *p, BOX3D *box)
+{
+       int     Code=0;
+
+//printf("compute outcode:\n");
+//print_box(box);
+//printf("point = [%g,%g]\n",p->x,p->y);
+       if (p->y > box->URT.y )
+               Code = 8;
+       else
+       {
+         if (p->y <box->LLB.y )
+         {
+               Code = 4;
+         }
+       }
+       if (p->x > box->URT.x )
+       {
+         return (Code+2);
+       }
+       if (p->x <box->LLB.x )
+       {
+         return (Code+1);
+       }
+
+       return (Code);
+}
+
+
+bool lineseg_inside_box( POINT3D *P1, POINT3D *P2, BOX3D *box)
+{
+       int     outcode_p1, outcode_p2;
+       double  Ax,Ay, Bx,By, Cx,Cy, Dx,Dy;
+       double  r,s;
+
+       outcode_p1 = compute_outcode(P1, box);
+       if (outcode_p1 ==0)
+               return TRUE;
+
+       outcode_p2 = compute_outcode(P2, box);
+       if (outcode_p2 ==0)
+               return TRUE;
+       if ((outcode_p1 & outcode_p2) != 0)
+               return FALSE;
+
+
+       if ((outcode_p1 + outcode_p2) == 12)
+               return TRUE; //vertically slices box
+
+       if ((outcode_p1 + outcode_p2) == 3)
+               return TRUE; //horizontally slices box
+
+
+//printf("have to do boundry calcs\n");
+
+       // know that its a diagonal line
+
+       //this is the tough case - it may or maynot intersect the box
+       //see if it intersects the horizonal and vertical box lines
+
+       // from comp.graphics.algo's faq:
+       /*
+              (Ay-Cy)(Dx-Cx)-(Ax-Cx)(Dy-Cy)
+       r =  ------------------------------------
+                 (Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx)
+
+               (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)
+       s =     -----------------------------------
+               (Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx)
+
+       */
+       // if  0<= r&s <=1 then intersection exists
+
+       Ax = P1->x; Ay = P1->y;
+       Bx = P2->x; By = P2->y;
+
+       //top hor part of box
+       Cx = box->LLB.x; Cy= box->URT.y;
+       Dx = box->URT.x; Dy= box->URT.y;
+
+               r= ((Ay-Cy)*(Dx-Cx)-(Ax-Cx)*(Dy-Cy) )/
+                       ((Bx-Ax)*(Dy-Cy)-(By-Ay)*(Dx-Cx) ) ;
+
+               s=  ((Ay-Cy)*(Bx-Ax)-(Ax-Cx)*(By-Ay))/
+                       ((Bx-Ax)*(Dy-Cy)-(By-Ay)*(Dx-Cx) ) ;
+
+       if  ( (r>=0) && (r<=1) && (s>=0) && (s<=1)    )
+               return TRUE;
+
+       //bottom hor part of box
+       Cx = box->LLB.x; Cy= box->LLB.y;
+       Dx = box->URT.x; Dy= box->LLB.y;
+
+               r= ((Ay-Cy)*(Dx-Cx)-(Ax-Cx)*(Dy-Cy) )/
+                       ((Bx-Ax)*(Dy-Cy)-(By-Ay)*(Dx-Cx) ) ;
+
+               s=  ((Ay-Cy)*(Bx-Ax)-(Ax-Cx)*(By-Ay))/
+                       ((Bx-Ax)*(Dy-Cy)-(By-Ay)*(Dx-Cx) ) ;
+       if  ( (r>=0) && (r<=1) && (s>=0) && (s<=1)    )
+               return TRUE;
+
+       //left ver part of box
+       Cx = box->LLB.x; Cy= box->LLB.y;
+       Dx = box->LLB.x; Dy= box->URT.y;
+
+               r= ((Ay-Cy)*(Dx-Cx)-(Ax-Cx)*(Dy-Cy) )/
+                       ((Bx-Ax)*(Dy-Cy)-(By-Ay)*(Dx-Cx) ) ;
+
+               s=  ((Ay-Cy)*(Bx-Ax)-(Ax-Cx)*(By-Ay))/
+                       ((Bx-Ax)*(Dy-Cy)-(By-Ay)*(Dx-Cx) ) ;
+
+       if  ( (r>=0) && (r<=1) && (s>=0) && (s<=1)    )
+               return TRUE;
+
+       //right ver part of box
+       Cx = box->URT.x; Cy= box->LLB.y;
+       Dx = box->URT.x; Dy= box->URT.y;
+
+               r= ((Ay-Cy)*(Dx-Cx)-(Ax-Cx)*(Dy-Cy) )/
+                       ((Bx-Ax)*(Dy-Cy)-(By-Ay)*(Dx-Cx) ) ;
+
+               s=  ((Ay-Cy)*(Bx-Ax)-(Ax-Cx)*(By-Ay))/
+                       ((Bx-Ax)*(Dy-Cy)-(By-Ay)*(Dx-Cx) ) ;
+       if  ( (r>=0) && (r<=1) && (s>=0) && (s<=1)    )
+               return TRUE;
+
+       //otherwise we did not intersect the box
+       return FALSE;
+
+}
+
+
+//give the work to an easier process
+bool   linestring_inside_box(POINT3D *pts, int npoints, BOX3D *box)
+{
+       POINT3D *frm,*to;
+       int             i;
+
+       if (npoints <2)
+               return FALSE;   //must have >1 point to make sense
+
+       frm = &pts[0];
+
+       for (i=1; i< npoints;i++)
+       {
+               to = &pts[i];
+
+               if (lineseg_inside_box( frm, to,  box))
+                       return TRUE;
+
+               frm = to;
+       }
+       return FALSE;
+}
+
+
+//this ones the most complex
+//     1. treat the outer ring as a line and see if its inside
+//             + if it is, then poly is inside box
+//     2. The polygon could be completely contained inside the box
+//             + line detector (step 1) would have found this
+//     3. The polygon could be completely surrounding the box
+//             + point-in-polygon of one of the box corners
+//             + COMPLEXITY: box could be totally inside a single hole
+//                     +test all holes like step 1.  Any that pass arent "bad" holes
+//                     +  each of the bad holes do a point-in-polygon if true =-> totally in hole
+
+bool polygon_truely_inside(POLYGON3D   *poly, BOX3D *box)
+{
+       bool            outer_ring_inside,outer_ring_surrounds;
+       POINT3D *pts1;
+       POINT3D test_point;
+       int32           ring;
+       int             point_offset;
+
+       pts1 = (POINT3D *) ( (char *)&(poly->npoints[poly->nrings] )  );
+       pts1 = (POINT3D *) MAXALIGN(pts1);
+
+
+       //step 1 (and 2)
+       outer_ring_inside = linestring_inside_box(pts1, poly->npoints[0], box);
+
+       if (outer_ring_inside)
+               return TRUE;
+
+       test_point.x = box->LLB.x;
+       test_point.y = box->LLB.y;
+               //.z unimportant
+
+       outer_ring_surrounds = PIP(&test_point, pts1,poly->npoints[0] );
+
+       if (!(outer_ring_surrounds))
+               return FALSE;   // disjoing
+
+       //printf("polygon - have to check holes\n");
+
+       //step 3
+
+       point_offset = poly->npoints[0];
+       for (ring =1; ring< poly->nrings; ring ++)
+       {
+               if (!( linestring_inside_box(&pts1[point_offset], poly->npoints[ring], box) ))
+               {
+                       //doesnt intersect the box
+                       // so if one of the corners of the box is inside the hole, it totally
+                       //   surrounds the box
+
+                       if (PIP(&test_point, &pts1[point_offset],poly->npoints[ring] ) )
+                               return FALSE;
+               }
+               point_offset += poly->npoints[ring];
+       }
+
+       return TRUE;
+}
+
+
+
+
+
+//This is more complicated because all the points in the line can be outside the box,
+// but one of the edges can cross into the box.
+//  We send the work off to another function because its generically usefull for
+//   polygons too
+bool line_truely_inside( LINE3D *line, BOX3D   *box)
+{
+       return  linestring_inside_box(&line->points[0], line->npoints, box);
+}
+
+// is point inside polygon surface ?
+//bool point_within_polygon(POINT3D *point, POLYGON3D *poly)
+//{
+//     POINT3D *ring;
+//     int ringpoints;
+//     int ri; // ring index
+//
+//     // if point is outside the shell return false.
+//     ring = (POINT3D *) ( (char *)&(poly->npoints[poly->nrings] )  );
+//     ringpoints = poly->npoints[0];
+//     if ( !PIP(point, ring, ringpoints) ) return FALSE;
+//
+//     // if point is inside any hole return false.
+//     //ring += ringpoints*sizeof(POINT3D);
+//     //for (ri=1; ri<poly->nrings; ri++)
+//     //{
+//     //      ringpoints = poly->npoints[ri];
+//     //      if ( PIP(point, ring, ringpoints) ) return FALSE;
+//     //      ring += ringpoints*sizeof(POINT3D);
+//     //}
+//
+//     // return true
+//     return TRUE;
+//}
+
+//is anything in geom1 really inside the the bvol (2d only) defined in geom2?
+// send each object to its proper handler
+PG_FUNCTION_INFO_V1(truly_inside);
+Datum truly_inside(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                      *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                      *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+       BOX3D                           *the_bbox;
+       int32                           *offsets1;
+       char                            *o1;
+       int32                           type1,j;
+       POLYGON3D                       *poly;
+       LINE3D                  *line;
+       POINT3D                 *point;
+
+       if (geom1->SRID != geom2->SRID)
+       {
+               elog(ERROR,"Operation on two GEOMETRIES with different SRIDs\n");
+               PG_RETURN_NULL();
+       }
+
+       the_bbox = &geom2->bvol;
+
+       offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
+
+       //now have to do a scan of each object
+
+       for (j=0; j< geom1->nobjs; j++)         //for each object in geom1
+       {
+               o1 = (char *) geom1 +offsets1[j] ;
+               type1=  geom1->objType[j];
+
+               if (type1 == POINTTYPE) //point
+               {
+                       point = (POINT3D *) o1;
+                       if ( point_truely_inside(point, the_bbox))
+                               PG_RETURN_BOOL(TRUE);
+               }
+
+               if (type1 == LINETYPE)  //line
+               {
+                       line = (LINE3D *) o1;
+                       if ( line_truely_inside(line, the_bbox))
+                               PG_RETURN_BOOL(TRUE);
+               }
+
+               if (type1 == POLYGONTYPE)       //POLYGON
+               {
+                       poly = (POLYGON3D *) o1;
+
+                       if ( polygon_truely_inside(poly, the_bbox))
+                               PG_RETURN_BOOL(TRUE);
+               }
+       }
+       PG_RETURN_BOOL(FALSE);
+}
+
+
+
+//number of points in an object
+PG_FUNCTION_INFO_V1(npoints);
+Datum npoints(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                      *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       int32                           *offsets1;
+       char                            *o1;
+       int32                           type1,j,i;
+       POLYGON3D                       *poly;
+       LINE3D                  *line;
+       int32                           numb_points = 0;
+
+       offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
+
+       //now have to do a scan of each object
+
+       for (j=0; j< geom1->nobjs; j++)         //for each object in geom1
+       {
+               o1 = (char *) geom1 +offsets1[j] ;
+               type1=  geom1->objType[j];
+
+               if (type1 == POINTTYPE) //point
+               {
+                       numb_points ++;
+               }
+
+               if (type1 == LINETYPE)  //line
+               {
+                       line = (LINE3D *) o1;
+                       numb_points += line->npoints;
+               }
+
+               if (type1 == POLYGONTYPE)       //POLYGON
+               {
+                       poly = (POLYGON3D *) o1;
+                       for (i=0; i<poly->nrings;i++)
+                       {
+                               numb_points += poly->npoints[i];
+                       }
+               }
+       }
+
+       PG_RETURN_INT32(numb_points);
+}
+
+//number of rings in an object
+PG_FUNCTION_INFO_V1(nrings);
+Datum nrings(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                      *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       int32                           *offsets1;
+       char                            *o1;
+       int32                           type1,j;
+       POLYGON3D                       *poly;
+       int32                           numb_rings = 0;
+
+       offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
+
+       //now have to do a scan of each object
+
+       for (j=0; j< geom1->nobjs; j++)         //for each object in geom1
+       {
+               o1 = (char *) geom1 +offsets1[j] ;
+               type1=  geom1->objType[j];
+
+               if (type1 == POLYGONTYPE)       //POLYGON
+               {
+                       poly = (POLYGON3D *) o1;
+                       numb_rings += poly->nrings;
+               }
+       }
+
+       PG_RETURN_INT32(numb_rings);
+}
+
+
+
+void   translate_points(POINT3D *pt, int npoints,double x_off, double y_off, double z_off)
+{
+       int i;
+
+//printf("translating %i points by [%g,%g,%g]\n",npoints,x_off,y_off,z_off);
+       if (npoints <1)
+               return; //nothing to do
+
+       for (i=0;i<npoints;i++)
+       {
+//printf("before: [%g,%g,%g]\n", pt[i].x, pt[i].y,pt[i].z);
+
+               pt[i].x += x_off;
+               pt[i].y += y_off;
+               pt[i].z += z_off;
+
+//printf("after: [%g,%g,%g]\n", pt[i].x, pt[i].y,pt[i].z);
+       }
+}
+
+
+
+//translate geometry
+PG_FUNCTION_INFO_V1(translate);
+Datum translate(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                      *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                        *geom1;
+       double                  x_off =  PG_GETARG_FLOAT8(1);
+       double                  y_off =  PG_GETARG_FLOAT8(2);
+       double                  z_off =  PG_GETARG_FLOAT8(3);
+       int32                           *offsets1;
+       char                            *o1;
+       int32                           type,j,i;
+       POLYGON3D                       *poly;
+       POINT3D                 *point,*pts;
+       LINE3D                  *line;
+       int                             numb_points;
+
+
+
+
+       //make a copy of geom so we can return a new version
+
+       geom1 = (GEOMETRY *) palloc (geom->size);
+       memcpy(geom1, geom, geom->size);                        //Will handle SRID and grid
+
+
+       offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
+
+       //now have to do a scan of each object
+
+       for (j=0; j< geom1->nobjs; j++)         //for each object in geom1
+       {
+               o1 = (char *) geom1 +offsets1[j] ;
+               type=  geom1->objType[j];
+
+               if (type == POINTTYPE)  //point
+               {
+                       point = (POINT3D *) o1;
+                       translate_points(point, 1,x_off,y_off,z_off);
+               }
+
+               if (type == LINETYPE)   //line
+               {
+                       line = (LINE3D *) o1;
+                       translate_points(&(line->points[0]), line->npoints,x_off,y_off,z_off);
+               }
+
+               if (type == POLYGONTYPE)        //POLYGON
+               {
+                       poly = (POLYGON3D *) o1;
+                       //find where the points are and where to put them
+                       numb_points =0;
+                       for (i=0; i<poly->nrings;i++)
+                       {
+                               numb_points += poly->npoints[i];
+                       }
+                       pts = (POINT3D *) ( (char *)&(poly->npoints[poly->nrings] )  );
+                       pts = (POINT3D *) MAXALIGN(pts);
+                       translate_points(pts, numb_points,x_off,y_off,z_off);
+
+
+               }
+       }
+       //translate the bounding box as well
+       geom1->bvol.LLB.x += x_off;
+       geom1->bvol.LLB.y += y_off;
+       geom1->bvol.LLB.z += z_off;
+       geom1->bvol.URT.x += x_off;
+       geom1->bvol.URT.y += y_off;
+       geom1->bvol.URT.z += z_off;
+
+       PG_RETURN_POINTER(geom1);
+}
+
+
+
+
+// set the is3d flag so the system think the geometry is 2d or 3d
+
+PG_FUNCTION_INFO_V1(force_2d);
+Datum force_2d(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                      *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                        *result;
+
+       result = (GEOMETRY *) palloc(geom->size);
+       memcpy(result,geom, geom->size);
+
+       result->is3d = FALSE;
+       PG_RETURN_POINTER(result);
+}
+
+
+PG_FUNCTION_INFO_V1(force_3d);
+Datum force_3d(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                      *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               GEOMETRY                        *result;
+
+       result = (GEOMETRY *) palloc(geom->size);
+       memcpy(result,geom, geom->size);
+
+       result->is3d = TRUE;
+       PG_RETURN_POINTER(result);
+
+}
+
+
+//NULL safe bbox union
+// combine_bbox(BOX3D, geometry) => union BOX3D and geometry->bvol
+// For finding the extent of a set of rows using the extent() aggregate
+
+PG_FUNCTION_INFO_V1(combine_bbox);
+Datum combine_bbox(PG_FUNCTION_ARGS)
+{
+                Pointer                 box3d_ptr = PG_GETARG_POINTER(0);
+                Pointer                 geom_ptr =  PG_GETARG_POINTER(1);
+                BOX3D                  *a,*b;
+                GEOMETRY                       *geom;
+               BOX3D                           *box;
+
+       if  ( (box3d_ptr == NULL) && (geom_ptr == NULL) )
+       {
+               PG_RETURN_NULL(); // combine_bbox(null,null) => null
+       }
+
+       if (box3d_ptr == NULL)
+       {
+               geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+               box = (BOX3D *) palloc(sizeof(BOX3D));
+               memcpy(box, &(geom->bvol), sizeof(BOX3D) );
+
+               PG_RETURN_POINTER(box);  // combine_bbox(null, geometry) => geometry->bvol
+       }
+
+       if (geom_ptr == NULL)
+       {
+               box = (BOX3D *) palloc(sizeof(BOX3D));
+               memcpy(box, (char *) PG_GETARG_DATUM(0) , sizeof(BOX3D) );
+
+
+               PG_RETURN_POINTER( box ); // combine_bbox(BOX3D, null) => BOX3D
+       }
+
+       //combine_bbox(BOX3D, geometry) => union(BOX3D, geometry->bvol)
+
+       box = (BOX3D *) palloc(sizeof(BOX3D));
+
+       a = (BOX3D *) PG_GETARG_DATUM(0) ;
+       b= &(( (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1)) )->bvol);
+
+       box->LLB.x = b->LLB.x;
+       box->LLB.y = b->LLB.y;
+       box->LLB.z = b->LLB.z;
+
+       box->URT.x = b->URT.x;
+       box->URT.y = b->URT.y;
+       box->URT.z = b->URT.z;
+
+       if (a->LLB.x < b->LLB.x)
+               box->LLB.x = a->LLB.x;
+       if (a->LLB.y < b->LLB.y)
+               box->LLB.y = a->LLB.y;
+       if (a->LLB.z < b->LLB.z)
+               box->LLB.z = a->LLB.z;
+
+       if (a->URT.x > b->URT.x)
+               box->URT.x = a->URT.x;
+       if (a->URT.y > b->URT.y)
+               box->URT.y = a->URT.y;
+       if (a->URT.z > b->URT.z)
+               box->URT.z = a->URT.z;
+
+       PG_RETURN_POINTER(  box  );
+}
+
+
+//returns 0 for points, 1 for lines, 2 for polygons.
+//returns max dimension for a collection.
+PG_FUNCTION_INFO_V1(dimension);
+Datum dimension(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                      *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               int t;
+               int result,dim,type;
+
+
+       if ((geom->type == COLLECTIONTYPE) && (geom->nobjs ==0))
+               PG_RETURN_INT32(-1);
+
+       if (geom->type == POINTTYPE)
+               PG_RETURN_INT32(0);
+
+       if (geom->type == LINETYPE)
+               PG_RETURN_INT32(1);
+
+       if (geom->type == POLYGONTYPE)
+               PG_RETURN_INT32(2);
+
+
+       if (geom->type == MULTIPOINTTYPE)
+               PG_RETURN_INT32(0);
+
+       if (geom->type == MULTILINETYPE)
+               PG_RETURN_INT32(1);
+
+       if (geom->type == MULTIPOLYGONTYPE)
+               PG_RETURN_INT32(2);
+
+       result = -1;
+       dim =0;
+       //its a collection -look for the largest one
+       for (t=0;t<geom->nobjs;t++)
+       {
+               type=  geom->objType[t];
+
+               if (type == POINTTYPE)
+                               dim=0;
+
+               if (type == LINETYPE)
+                       dim=1;
+
+               if (type == POLYGONTYPE)
+                       dim=2;
+
+               if (dim>result)
+                       result = dim;
+
+       }
+       PG_RETURN_INT32(result);
+}
+
+//returns a string representation of this geometry's type
+PG_FUNCTION_INFO_V1(geometrytype);
+Datum geometrytype(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                      *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               char                            *text_ob = palloc(20+4);
+               char                            *result = text_ob+4;
+               int32                           size;
+
+
+       memset(result,0,20);
+
+       if (geom->type == POINTTYPE)
+               strcpy(result,"POINT");
+       if (geom->type == MULTIPOINTTYPE)
+               strcpy(result,"MULTIPOINT");
+
+       if (geom->type == LINETYPE)
+               strcpy(result,"LINESTRING");
+       if (geom->type == MULTILINETYPE)
+               strcpy(result,"MULTILINESTRING");
+
+       if (geom->type == POLYGONTYPE)
+               strcpy(result,"POLYGON");
+       if (geom->type == MULTIPOLYGONTYPE)
+               strcpy(result,"MULTIPOLYGON");
+
+       if (geom->type == COLLECTIONTYPE)
+               strcpy(result,"GEOMETRYCOLLECTION");
+
+       if (strlen(result) == 0)
+               strcpy(result,"UNKNOWN");
+
+       size = strlen(result) +4 ;
+
+       memcpy(text_ob, &size,4); // size of string
+
+
+       PG_RETURN_POINTER(text_ob);
+
+}
+
+
+//makes a polygon of a features bvol - 1st point = LL 3rd=UR
+// 2d only
+//
+// create new geometry of type polygon, 1 ring, 5 points
+
+PG_FUNCTION_INFO_V1(envelope);
+Datum envelope(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                      *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                        *result;
+       POLYGON3D                       *poly;
+       POINT3D                 pts[5]; //5 points around box
+       int                             pts_per_ring[1];
+       int                             poly_size;
+
+       //use LLB's z value (we're going to set is3d to false)
+
+               //0,1,2,3,4 --> CCW order,  4,3,2,1,0 --> CW order
+       set_point( &pts[4], geom->bvol.LLB.x , geom->bvol.LLB.y , geom->bvol.LLB.z );
+       set_point( &pts[3], geom->bvol.URT.x , geom->bvol.LLB.y , geom->bvol.LLB.z );
+       set_point( &pts[2], geom->bvol.URT.x , geom->bvol.URT.y , geom->bvol.LLB.z );
+       set_point( &pts[1], geom->bvol.LLB.x , geom->bvol.URT.y , geom->bvol.LLB.z );
+       set_point( &pts[0], geom->bvol.LLB.x , geom->bvol.LLB.y , geom->bvol.LLB.z );
+
+       pts_per_ring[0] = 5; //ring has 5 points
+
+               //make a polygon
+       poly = make_polygon(1, pts_per_ring, pts, 5, &poly_size);
+
+       result = make_oneobj_geometry(poly_size, (char *)poly, POLYGONTYPE, FALSE,geom->SRID, geom->scale, geom->offsetX, geom->offsetY);
+
+       PG_RETURN_POINTER(result);
+
+}
+
+//X(GEOMETRY) -- find the first POINT(..) in GEOMETRY, returns its X value.
+//Return NULL if there is no POINT(..) in GEOMETRY
+PG_FUNCTION_INFO_V1(x_point);
+Datum x_point(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                      *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       char                            *o;
+       int                             type1,j;
+       int32                           *offsets1;
+
+
+       offsets1 = (int32 *) ( ((char *) &(geom->objType[0] ))+ sizeof(int32) * geom->nobjs ) ;
+
+       //now have to do a scan of each object
+
+       for (j=0; j< geom->nobjs; j++)          //for each object in geom1
+       {
+               o = (char *) geom +offsets1[j] ;
+               type1=  geom->objType[j];
+
+               if (type1 == POINTTYPE) //point
+               {
+                       PG_RETURN_FLOAT8( ((POINT3D *)o)->x) ;
+               }
+
+       }
+       PG_RETURN_NULL();
+}
+
+//Y(GEOMETRY) -- find the first POINT(..) in GEOMETRY, returns its Y value.
+//Return NULL if there is no POINT(..) in GEOMETRY
+PG_FUNCTION_INFO_V1(y_point);
+Datum y_point(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                      *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       char                            *o;
+       int                             type1,j;
+       int32                           *offsets1;
+
+
+       offsets1 = (int32 *) ( ((char *) &(geom->objType[0] ))+ sizeof(int32) * geom->nobjs ) ;
+
+       //now have to do a scan of each object
+
+       for (j=0; j< geom->nobjs; j++)          //for each object in geom1
+       {
+               o = (char *) geom +offsets1[j] ;
+               type1=  geom->objType[j];
+
+               if (type1 == POINTTYPE) //point
+               {
+                       PG_RETURN_FLOAT8( ((POINT3D *)o)->y) ;
+               }
+
+       }
+       PG_RETURN_NULL();
+}
+
+//Z(GEOMETRY) -- find the first POINT(..) in GEOMETRY, returns its Z value.
+//Return NULL if there is no POINT(..) in GEOMETRY
+PG_FUNCTION_INFO_V1(z_point);
+Datum z_point(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                      *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       char                            *o;
+       int                             type1,j;
+       int32                           *offsets1;
+
+
+       offsets1 = (int32 *) ( ((char *) &(geom->objType[0] ))+ sizeof(int32) * geom->nobjs ) ;
+
+       //now have to do a scan of each object
+
+       for (j=0; j< geom->nobjs; j++)          //for each object in geom1
+       {
+               o = (char *) geom +offsets1[j] ;
+               type1=  geom->objType[j];
+
+               if (type1 == POINTTYPE) //point
+               {
+                       PG_RETURN_FLOAT8( ((POINT3D *)o)->z) ;
+               }
+
+       }
+       PG_RETURN_NULL();
+}
+
+//numpoints(GEOMETRY) -- find the first linestring in GEOMETRY, return
+//the number of points in it.  Return NULL if there is no LINESTRING(..)
+//in GEOMETRY
+PG_FUNCTION_INFO_V1(numpoints_linestring);
+Datum numpoints_linestring(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                      *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       char                            *o;
+       int                             type1,j;
+       int32                           *offsets1;
+
+       offsets1 = (int32 *) ( ((char *) &(geom->objType[0] ))+ sizeof(int32) * geom->nobjs ) ;
+
+       for (j=0; j< geom->nobjs; j++)          //for each object in geom1
+       {
+               o = (char *) geom +offsets1[j] ;
+               type1=  geom->objType[j];
+
+               if (type1 == LINETYPE)  //linestring
+               {
+                       PG_RETURN_INT32( ((LINE3D *)o)->npoints) ;
+               }
+
+       }
+       PG_RETURN_NULL();
+}
+
+//pointn(GEOMETRY,INTEGER) -- find the first linestring in GEOMETRY,
+//return the point at index INTEGER (0 is 1st point).  Return NULL if
+//there is no LINESTRING(..) in GEOMETRY or INTEGER is out of bounds.
+// keeps is3d flag
+
+PG_FUNCTION_INFO_V1(pointn_linestring);
+Datum pointn_linestring(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                      *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       int32                           wanted_index =PG_GETARG_INT32(1);
+       char                            *o;
+       int                             type1,j;
+       int32                           *offsets1;
+       LINE3D                  *line;
+
+
+       wanted_index -= NfunctionFirstPoint;
+
+       offsets1 = (int32 *) ( ((char *) &(geom->objType[0] ))+ sizeof(int32) * geom->nobjs ) ;
+
+       for (j=0; j< geom->nobjs; j++)          //for each object in geom1
+       {
+               o = (char *) geom +offsets1[j] ;
+               type1=  geom->objType[j];
+
+               if (type1 == LINETYPE)  //linestring
+               {
+                       line = (LINE3D *)o;
+                       if ( (wanted_index<0) || (wanted_index> (line->npoints-1) ) )
+                               PG_RETURN_NULL(); //index out of range
+                       //get the point, make a new geometry
+                       PG_RETURN_POINTER(
+                               make_oneobj_geometry(sizeof(POINT3D),
+                                                        (char *)&line->points[wanted_index],
+                                                          POINTTYPE,  geom->is3d, geom->SRID, geom->scale, geom->offsetX, geom->offsetY)
+                                               );
+               }
+
+       }
+       PG_RETURN_NULL();
+}
+
+// exteriorRing(GEOMETRY) -- find the first polygon in GEOMETRY, return
+//its exterior ring (as a linestring).  Return NULL if there is no
+//POLYGON(..) in GEOMETRY.
+
+PG_FUNCTION_INFO_V1(exteriorring_polygon);
+Datum exteriorring_polygon(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                      *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+       char                            *o;
+       int                             type1,j;
+       int32                           *offsets1;
+       LINE3D                  *line;
+       POLYGON3D                       *poly;
+       POINT3D                 *pts;
+       int                             size_line;
+
+
+       offsets1 = (int32 *) ( ((char *) &(geom->objType[0] ))+ sizeof(int32) * geom->nobjs ) ;
+
+       for (j=0; j< geom->nobjs; j++)          //for each object in geom1
+       {
+               o = (char *) geom +offsets1[j] ;
+               type1=  geom->objType[j];
+
+               if (type1 == POLYGONTYPE)       //polygon object
+               {
+                       poly = (POLYGON3D *)o;
+                       pts = (POINT3D *) ( (char *)&(poly->npoints[poly->nrings] )  );
+                       pts = (POINT3D *) MAXALIGN(pts);
+
+                       line = make_line(poly->npoints[0], pts, &size_line);
+
+                       //get the ring, make a new geometry
+                       PG_RETURN_POINTER(
+                               make_oneobj_geometry(size_line,
+                                                        (char *) line,
+                                                          LINETYPE,  geom->is3d,geom->SRID,  geom->scale, geom->offsetX, geom->offsetY)
+                                               );
+               }
+
+       }
+       PG_RETURN_NULL();
+}
+
+//NumInteriorRings(GEOMETRY) -- find the first polygon in GEOMETRY,
+//return the number of interior rings.  Return NULL if there is no
+//POLYGON(..) in GEOMETRY.
+
+PG_FUNCTION_INFO_V1(numinteriorrings_polygon);
+Datum numinteriorrings_polygon(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                      *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+       char                            *o;
+       int                             type1,j;
+       int32                           *offsets1;
+       POLYGON3D                       *poly;
+
+
+
+       offsets1 = (int32 *) ( ((char *) &(geom->objType[0] ))+ sizeof(int32) * geom->nobjs ) ;
+
+       for (j=0; j< geom->nobjs; j++)          //for each object in geom1
+       {
+               o = (char *) geom +offsets1[j] ;
+               type1=  geom->objType[j];
+
+               if (type1 == POLYGONTYPE)       //polygon object
+               {
+                       poly = (POLYGON3D *)o;
+                       PG_RETURN_INT32( poly->nrings -1 ) ;
+               }
+       }
+       PG_RETURN_NULL();
+}
+
+//        InteriorRingN(GEOMETRY,INTEGER) --  find the first polygon in GEOMETRY,
+//return the interior ring at index INTEGER (as a linestring).  Return
+//NULL if there is no POLYGON(..) in GEOMETRY or INTEGER is out of bounds.
+// 1st ring = exerior ring
+
+PG_FUNCTION_INFO_V1(interiorringn_polygon);
+Datum interiorringn_polygon(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                      *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       int32                           wanted_index =PG_GETARG_INT32(1);
+       char                            *o;
+       int                             type1,j,t,point_offset;
+       int32                           *offsets1;
+       POLYGON3D                       *poly;
+       LINE3D                  *line;
+       POINT3D                 *pts;
+       int                             size_line;
+
+
+       wanted_index -= NfunctionFirstPoint;
+
+       offsets1 = (int32 *) ( ((char *) &(geom->objType[0] ))+ sizeof(int32) * geom->nobjs ) ;
+
+       for (j=0; j< geom->nobjs; j++)          //for each object in geom1
+       {
+               o = (char *) geom +offsets1[j] ;
+               type1=  geom->objType[j];
+
+               if (type1 == POLYGONTYPE)       //polygon object
+               {
+                       poly = (POLYGON3D *)o;
+
+                       if ( (wanted_index<0) || (wanted_index> (poly->nrings-2) ) )
+                               PG_RETURN_NULL(); //index out of range
+
+
+                       pts = (POINT3D *) ( (char *)&(poly->npoints[poly->nrings] )  );
+                       pts = (POINT3D *) MAXALIGN(pts);
+
+                       //find the 1st point in wanted ring
+                       point_offset=0;
+                       for (t=0; t< (wanted_index+1); t++)
+                       {
+                               point_offset += poly->npoints[t];
+                       }
+
+                       line = make_line(poly->npoints[wanted_index+1], &pts[point_offset], &size_line);
+
+                       //get the ring, make a new geometry
+                       PG_RETURN_POINTER(
+                               make_oneobj_geometry(size_line,
+                                                        (char *) line,
+                                                          LINETYPE,  geom->is3d, geom->SRID, geom->scale, geom->offsetX, geom->offsetY)
+                                               );
+               }
+       }
+       PG_RETURN_NULL();
+}
+
+
+//        numgeometries(GEOMETRY) -- if GEOMETRY is a GEOMETRYCOLLECTION, return
+//the number of geometries in it, otherwise return NULL.
+//             if GEOMETRY is a MULTI* type, return the number of sub-types in it.
+
+PG_FUNCTION_INFO_V1(numgeometries_collection);
+Datum numgeometries_collection(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                      *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+               if  ( (geom->type == COLLECTIONTYPE) ||
+                     (geom->type == MULTIPOINTTYPE) ||
+                       (geom->type == MULTILINETYPE) ||
+                       (geom->type == MULTIPOLYGONTYPE)
+                   )
+                       PG_RETURN_INT32( geom->nobjs ) ;
+               else
+                       PG_RETURN_NULL();
+}
+
+
+//geometryN(GEOMETRY, INTEGER) -- if GEOMETRY is a GEOMETRYCOLLECTION,
+//return the sub-geometry at index INTEGER (0=first geometry), otherwise
+//return NULL.   NOTE: MULTIPOINT, MULTILINESTRING,MULTIPOLYGON are
+//converted to sets of POINT,LINESTRING, and POLYGON so the index may
+//change.
+// if GEOMETRY is a MULTI* type, return the Nth sub-geometry
+
+
+PG_FUNCTION_INFO_V1(geometryn_collection);
+Datum geometryn_collection(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                      *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               int32                           wanted_index =PG_GETARG_INT32(1);
+               int                             type;
+               int32                           *offsets1;
+               char                            *o;
+
+       wanted_index -= NfunctionFirstPoint;
+
+       offsets1 = (int32 *) ( ((char *) &(geom->objType[0] ))+ sizeof(int32) * geom->nobjs ) ;
+
+               if  (!(( (geom->type == COLLECTIONTYPE) ||
+                     (geom->type == MULTIPOINTTYPE) ||
+                       (geom->type == MULTILINETYPE) ||
+                       (geom->type == MULTIPOLYGONTYPE)
+                   )))
+                               PG_RETURN_NULL();
+
+
+               if ( (wanted_index <0) || (wanted_index > (geom->nobjs-1) ) )
+                       PG_RETURN_NULL();       //bad index
+
+               type = geom->objType[wanted_index];
+               o = (char *) geom +offsets1[wanted_index] ;
+
+               if (type == POINTTYPE)
+               {
+                               PG_RETURN_POINTER(
+                               make_oneobj_geometry(sizeof(POINT3D),
+                                                        o,
+                                                          POINTTYPE,  geom->is3d, geom->SRID, geom->scale, geom->offsetX, geom->offsetY)
+                                               );
+               }
+               if (type == LINETYPE)
+               {
+                               PG_RETURN_POINTER(
+                               make_oneobj_geometry(   size_subobject (o, LINETYPE),
+                                                        o,
+                                                          LINETYPE,  geom->is3d, geom->SRID, geom->scale, geom->offsetX, geom->offsetY)
+                                               );
+               }
+               if (type == POLYGONTYPE)
+               {
+                               PG_RETURN_POINTER(
+                               make_oneobj_geometry(   size_subobject (o, POLYGONTYPE),
+                                                        o,
+                                                          POLYGONTYPE,  geom->is3d, geom->SRID, geom->scale, geom->offsetX, geom->offsetY)
+                                               );
+               }
+
+               PG_RETURN_NULL();
+}
+
+
+//force the geometry to be a geometrycollection type
+
+PG_FUNCTION_INFO_V1(force_collection);
+Datum force_collection(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                      *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                        *result;
+
+       result = (GEOMETRY *) palloc(geom->size);
+       memcpy(result,geom, geom->size);
+
+       result->type = COLLECTIONTYPE;
+
+       PG_RETURN_POINTER(result);
+}
+
+
+
+// point_inside_circle(geometry, Px, Py, d)
+// returns true if there is a point in geometry whose distance to (Px,Py) is < d
+
+PG_FUNCTION_INFO_V1(point_inside_circle);
+Datum point_inside_circle(PG_FUNCTION_ARGS)
+{
+
+       GEOMETRY                      *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       char                            *o;
+       int                             type1,j;
+       int32                           *offsets1;
+       POINT3D                 *pt;
+       double                  Px  = PG_GETARG_FLOAT8(1);
+       double                  Py =  PG_GETARG_FLOAT8(2);
+       double                  d =   PG_GETARG_FLOAT8(3);
+       double                  dd = d*d; //d squared
+
+       offsets1 = (int32 *) ( ((char *) &(geom->objType[0] ))+ sizeof(int32) * geom->nobjs ) ;
+
+       //now have to do a scan of each object
+
+       for (j=0; j< geom->nobjs; j++)          //for each object in geom
+       {
+               o = (char *) geom +offsets1[j] ;
+               type1=  geom->objType[j];
+
+               if (type1 == POINTTYPE) //point
+               {
+                       //see if its within d of Px,Py
+                       pt = (POINT3D *) o;
+                       if  ( (   (pt->x - Px)*(pt->x - Px) + (pt->y - Py)*(pt->y - Py) ) < dd)
+                       {
+                               PG_RETURN_BOOL(TRUE);
+                       }
+               }
+
+       }
+       PG_RETURN_BOOL(FALSE);
+}
+
+
+//distance from p to line A->B
+double distance_pt_seg(POINT3D *p, POINT3D *A, POINT3D *B)
+{
+       double  r,s;
+
+
+       //if start==end, then use pt distance
+       if (  ( A->x == B->x) && (A->y == B->y) )
+               return distance_pt_pt(p,A);
+
+       //otherwise, we use comp.graphics.algorithms Frequently Asked Questions method
+
+       /*(1)                 AC dot AB
+                   r = ---------
+                         ||AB||^2
+               r has the following meaning:
+               r=0 P = A
+               r=1 P = B
+               r<0 P is on the backward extension of AB
+               r>1 P is on the forward extension of AB
+               0<r<1 P is interior to AB
+       */
+
+       r = ( (p->x-A->x) * (B->x-A->x) + (p->y-A->y) * (B->y-A->y) )/( (B->x-A->x)*(B->x-A->x) +(B->y-A->y)*(B->y-A->y) );
+
+       if (r<0)
+               return (distance_pt_pt(p,A));
+       if (r>1)
+               return(distance_pt_pt(p,B));
+
+
+       /*(2)
+                    (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)
+               s = -----------------------------
+                               L^2
+
+               Then the distance from C to P = |s|*L.
+
+       */
+
+       s = ((A->y-p->y)*(B->x-A->x)- (A->x-p->x)*(B->y-A->y) )/ ((B->x-A->x)*(B->x-A->x) +(B->y-A->y)*(B->y-A->y) );
+
+       return abs(s) * sqrt(((B->x-A->x)*(B->x-A->x) +(B->y-A->y)*(B->y-A->y) ));
+}
+
+
+
+// find the distance from AB to CD
+double distance_seg_seg(POINT3D *A, POINT3D *B, POINT3D *C, POINT3D *D)
+{
+
+       double  s_top, s_bot,s;
+       double  r_top, r_bot,r;
+
+
+//printf("seg_seg [%g,%g]->[%g,%g]  by [%g,%g]->[%g,%g]  \n",A->x,A->y,B->x,B->y, C->x,C->y, D->x, D->y);
+               //A and B are the same point
+
+       if (  ( A->x == B->x) && (A->y == B->y) )
+               return distance_pt_seg(A,C,D);
+
+               //U and V are the same point
+
+       if (  ( C->x == D->x) && (C->y == D->y) )
+               return distance_pt_seg(D,A,B);
+
+       // AB and CD are line segments
+       /* from comp.graphics.algo
+
+       Solving the above for r and s yields
+                               (Ay-Cy)(Dx-Cx)-(Ax-Cx)(Dy-Cy)
+                  r = ----------------------------- (eqn 1)
+                               (Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx)
+
+                       (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)
+               s = ----------------------------- (eqn 2)
+                       (Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx)
+       Let P be the position vector of the intersection point, then
+               P=A+r(B-A) or
+               Px=Ax+r(Bx-Ax)
+               Py=Ay+r(By-Ay)
+       By examining the values of r & s, you can also determine some other limiting conditions:
+               If 0<=r<=1 & 0<=s<=1, intersection exists
+               r<0 or r>1 or s<0 or s>1 line segments do not intersect
+               If the denominator in eqn 1 is zero, AB & CD are parallel
+               If the numerator in eqn 1 is also zero, AB & CD are collinear.
+
+       */
+       r_top = (A->y-C->y)*(D->x-C->x) - (A->x-C->x)*(D->y-C->y) ;
+       r_bot = (B->x-A->x)*(D->y-C->y) - (B->y-A->y)*(D->x-C->x) ;
+
+       s_top = (A->y-C->y)*(B->x-A->x) - (A->x-C->x)*(B->y-A->y);
+       s_bot = (B->x-A->x)*(D->y-C->y) - (B->y-A->y)*(D->x-C->x);
+
+       if  ( (r_bot==0) || (s_bot == 0) )
+       {
+               return (
+                       min(distance_pt_seg(A,C,D),
+                               min(distance_pt_seg(B,C,D),
+                                       min(distance_pt_seg(C,A,B),
+                                               distance_pt_seg(D,A,B)    ) ) )
+                        );
+       }
+       s = s_top/s_bot;
+       r=  r_top/r_bot;
+
+       if ((r<0) || (r>1) || (s<0) || (s>1) )
+       {
+               //no intersection
+               return (
+                       min(distance_pt_seg(A,C,D),
+                               min(distance_pt_seg(B,C,D),
+                                       min(distance_pt_seg(C,A,B),
+                                               distance_pt_seg(D,A,B)    ) ) )
+                        );
+
+       }
+       else
+               return -0; //intersection exists
+
+}
+
+
+
+//trivial
+double distance_pt_pt(POINT3D *p1, POINT3D *p2)
+{
+       //print_point_debug(p1);
+       //print_point_debug(p2);
+       return ( sqrt(  (p2->x - p1->x) * (p2->x - p1->x)  + (p2->y - p1->y) * (p2->y - p1->y)  ) );
+}
+
+
+//search all the segments of line to see which one is closest to p1
+double distance_pt_line(POINT3D *p1, LINE3D *l2)
+{
+       double  result = 99999999999.9,dist_this;
+       bool            result_okay = FALSE; //result is a valid min
+       int             t;
+       POINT3D *start,*end;
+
+       start = &(l2->points[0]);
+
+       for (t =1;t<l2->npoints;t++)
+       {
+               end = &(l2->points[t]);
+
+               dist_this = distance_pt_seg(p1, start,end);
+               if (result_okay)
+                       result= min(result,dist_this);
+               else
+               {
+                       result_okay = TRUE;
+                       result = dist_this;
+               }
+
+               start =end;
+       }
+
+       return result;
+}
+
+
+
+// test each segment of l1 against each segment of l2.  Return min
+double distance_line_line(LINE3D *l1, LINE3D *l2)
+{
+
+       double  result = 99999999999.9,dist_this;
+       bool            result_okay = FALSE; //result is a valid min
+       int             t,u;
+       POINT3D *start,*end;
+       POINT3D *start2,*end2;
+
+
+       start = &(l1->points[0]);
+
+       for (t =1;t<l1->npoints;t++)            //for each segment in L1
+       {
+               end = &(l1->points[t]);
+
+               start2 = &(l2->points[0]);
+
+               for (u=1; u< l2->npoints; u++)  //for each segment in L2
+               {
+                       end2 = &(l2->points[u]);
+
+                               dist_this = distance_seg_seg(start,end,start2,end2);
+//printf("line_line; seg %i * seg %i, dist = %g\n",t,u,dist_this);
+
+                               if (result_okay)
+                                       result= min(result,dist_this);
+                               else
+                               {
+                                       result_okay = TRUE;
+                                       result = dist_this;
+                               }
+                               if (result <=0)
+                                       return 0;       //intersection
+
+                       start2 = end2;
+               }
+               start = end;
+       }
+
+       return result;
+}
+
+
+// 1. see if pt in outer boundary.  if no, then treat the outer ring like a line
+// 2. if in the boundary, test to see if its in a hole.  if so, then return dist to hole
+double distance_pt_poly(POINT3D *p1, POLYGON3D *poly2)
+{
+       POINT3D *pts; //pts array for polygon
+       double  result;
+       LINE3D  *line;
+       int             junk,t;
+       int             offset;
+
+
+                       pts = (POINT3D *) ( (char *)&(poly2->npoints[poly2->nrings] )  );
+                       pts = (POINT3D *) MAXALIGN(pts);
+
+
+       if (PIP( p1, pts, poly2->npoints[0] ) )
+       {
+               //inside the outer ring.  scan though each of the inner rings looking to
+               // see if its inside.  If not, distance =0.  Otherwise, distance =
+               // pt to ring distance
+
+               offset = poly2->npoints[0];     //where ring t starts;
+               for (t=1; t<poly2->nrings;t++)  //foreach inner ring
+               {
+                       if (    PIP( p1, &pts[offset], poly2->npoints[t] ) )
+                       {
+                                       //inside a hole.  Distance = pt -> ring
+
+                                       line = make_line (poly2->npoints[t] , &pts[offset] , &junk);
+                                       result = distance_pt_line(p1, line);
+                                       pfree(line);
+                                       return result;
+                       }
+                       offset += poly2->npoints[t];
+               }
+
+               return 0; //its inside the polygon
+       }
+       else
+       {
+               //outside the outer ring.  Distance = pt -> ring
+
+               line = make_line (poly2->npoints[0] , pts, &junk);
+               result = distance_pt_line(p1, line);
+               pfree(line);
+               return result;
+       }
+
+
+}
+
+
+// brute force.  Test l1 against each ring.  If there's an intersection then return 0 (crosses boundary)
+// otherwise, test to see if a point inside outer rings of polygon, but not in inner rings.
+// if so, return 0  (line inside polygon)
+// otherwise return min distance to a ring (could be outside polygon or inside a hole)
+double distance_line_poly(LINE3D *l1, POLYGON3D *poly2)
+{
+       double  min_dist=9999999.0,this_dist;
+       int             t,junk;
+       LINE3D  *line;
+       POINT3D *pts; //pts array for polygon
+       int             offset;
+
+
+
+                       pts = (POINT3D *) ( (char *)&(poly2->npoints[poly2->nrings] )  );
+                       pts = (POINT3D *) MAXALIGN(pts);
+
+       offset =0;
+       for (t=0;t<poly2->nrings; t++)
+       {
+               line = make_line (poly2->npoints[t] , &pts[offset] , &junk);
+                       this_dist = distance_line_line(l1, line);
+               pfree(line);
+
+//printf("line_poly; ring %i dist = %g\n",t,this_dist);
+               if (t==0)
+                       min_dist = this_dist;
+               else
+                       min_dist = min(min_dist,this_dist);
+
+               if (min_dist <=0)
+                       return 0;               //intersection
+
+               offset += poly2->npoints[t];
+       }
+
+       //no intersection, have to check if a point is inside the outer ring
+
+       if (PIP( &l1->points[0], pts, poly2->npoints[0] ) )
+       {
+               //its in the polygon.  Have to check if its inside a hole
+
+//printf("line_poly; inside outer ring\n");
+               offset =poly2->npoints[0] ;
+               for (t=1; t<poly2->nrings;t++)          //foreach inner ring
+               {
+                       if (    PIP( &l1->points[0],  &pts[offset], poly2->npoints[t] ) )
+                       {
+                               //its inside a hole, then the actual distance is the min ring distance
+//printf("line_poly; inside inner ring %i\n",t);
+                               return min_dist;
+                       }
+                       offset += poly2->npoints[t];
+               }
+               // not in hole, there for inside the polygon
+               return 0;
+       }
+       else
+       {
+               //not in the outside ring, so min distance to a ring is the actual min distance
+               return min_dist;
+       }
+
+}
+
+// true if point is in poly (and not in its holes)
+bool point_in_poly(POINT3D *p, POLYGON3D *poly)
+{
+       int     t;
+       POINT3D *pts1;
+       int             offset;
+
+               pts1 = (POINT3D *) ( (char *)&(poly->npoints[poly->nrings] )  );
+               pts1 = (POINT3D *) MAXALIGN(pts1);
+
+       if (PIP(p, pts1, poly->npoints[0]))
+       {
+               offset = poly->npoints[0];
+               for (t=1;t<poly->nrings;t++)
+               {
+                       if (PIP(p, &pts1[offset], poly->npoints[t]) )
+                       {
+                               return FALSE; //inside a hole
+                       }
+                       offset += poly->npoints[t];
+               }
+               return TRUE; //in outer ring, not in holes
+       }
+       else
+               return FALSE; //not in outer ring
+}
+
+// brute force.
+// test to see if any rings intersect.  if yes, dist =0
+// test to see if one inside the other and if they are inside holes.
+// find min distance ring-to-ring
+double distance_poly_poly(POLYGON3D *poly1, POLYGON3D *poly2)
+{
+       //foreach ring in Poly1
+       // foreach ring in Poly2
+       //   if intersect, return 0
+
+       // if poly1 inside poly2 return 0
+       // if poly2 inside poly1 return 0
+
+       // otherwise return closest approach of rings
+
+       int     t,junk;
+       POINT3D *pts1,*pts2;
+       int32           offset1;
+       double  min_dist= 99999999999.9, this_dist;
+       LINE3D  *line;
+
+
+               pts1 = (POINT3D *) ( (char *)&(poly1->npoints[poly1->nrings] )  );
+               pts1 = (POINT3D *) MAXALIGN(pts1);
+               pts2 = (POINT3D *) ( (char *)&(poly2->npoints[poly2->nrings] )  );
+               pts2 = (POINT3D *) MAXALIGN(pts2);
+
+
+                       //do this first as its a quick test
+
+               //test if poly1 inside poly2
+       if (point_in_poly(pts1, poly2) )
+               return 0;
+
+               //test if poly2 inside poly1
+       if (point_in_poly(pts2, poly1) )
+               return 0;
+
+
+       offset1 =0;
+       for (t=0; t<poly1->nrings; t++) //for each ring in poly1
+       {
+               line = make_line (poly1->npoints[t] , &pts1[offset1] , &junk);
+                       this_dist = distance_line_poly(line, poly2);
+               pfree(line);
+//printf("poly_poly; ring %i dist = %g\n",t,this_dist);
+               if (t==0)
+                       min_dist = this_dist;
+               else
+                       min_dist = min(min_dist,this_dist);
+
+               if (min_dist <=0)
+                       return 0;               //intersection
+
+
+               offset1 += poly1->npoints[t];
+       }
+
+       //rings do not intersect
+
+       return min_dist;
+}
+
+//minimum distance between objects in geom1 and geom2.  returns null if it doesnt exist (future null-safe version).
+PG_FUNCTION_INFO_V1(distance);
+Datum distance(PG_FUNCTION_ARGS)
+{
+
+       GEOMETRY                      *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                      *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+       int     g1_i, g2_i;
+       double          dist,this_dist = 0;
+       bool                    dist_set = FALSE; //true once dist makes sense.
+       int32                   *offsets1,*offsets2;
+       int                     type1,type2;
+       char                    *o1,*o2;
+
+
+       if (geom1->SRID != geom2->SRID)
+       {
+               elog(ERROR,"Operation on two GEOMETRIES with different SRIDs\n");
+               PG_RETURN_NULL();
+       }
+
+       dist = 99999999999999999999.9; //very far
+
+
+       offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
+       offsets2 = (int32 *) ( ((char *) &(geom2->objType[0] ))+ sizeof(int32) * geom2->nobjs ) ;
+
+
+       for (g1_i=0; g1_i < geom1->nobjs; g1_i++)
+       {
+               o1 = (char *) geom1 +offsets1[g1_i] ;
+               type1=  geom1->objType[g1_i];
+               for (g2_i=0; g2_i < geom2->nobjs; g2_i++)
+               {
+                       o2 = (char *) geom2 +offsets2[g2_i] ;
+                       type2=  geom2->objType[g2_i];
+
+                       if  ( (type1 == POINTTYPE) && (type2 == POINTTYPE) )
+                       {
+                               this_dist = distance_pt_pt( (POINT3D *)o1, (POINT3D *)o2 );
+                       }
+                       if  ( (type1 == POINTTYPE) && (type2 == LINETYPE) )
+                       {
+                               this_dist = distance_pt_line( (POINT3D *)o1, (LINE3D *)o2 );
+                       }
+                       if  ( (type1 == POINTTYPE) && (type2 == POLYGONTYPE) )
+                       {
+                               this_dist = distance_pt_poly( (POINT3D *)o1, (POLYGON3D *)o2 );
+                       }
+                       if  ( (type1 == LINETYPE) && (type2 == LINETYPE) )
+                       {
+                               this_dist = distance_line_line( (LINE3D *)o1, (LINE3D *)o2 );
+                       }
+                       if  ( (type1 == LINETYPE) && (type2 == POLYGONTYPE) )
+                       {
+                               this_dist = distance_line_poly( (LINE3D *)o1, (POLYGON3D *)o2 );
+                       }
+                       if  ( (type1 == POLYGONTYPE) && (type2 == POLYGONTYPE) )
+                       {
+                               this_dist = distance_poly_poly( (POLYGON3D *)o1, (POLYGON3D *)o2 );
+                       }
+                               //trival versions based on above dist(a,b) = dist(b,a)
+
+                       if  ( (type1 == LINETYPE) && (type2 == POINTTYPE) )
+                       {
+                               this_dist = distance_pt_line( (POINT3D *)o2, (LINE3D *)o1 );
+                       }
+                       if  ( (type1 == POLYGONTYPE) && (type2 == POINTTYPE) )
+                       {
+                               this_dist = distance_pt_poly( (POINT3D *)o2, (POLYGON3D *)o1 );
+                       }
+                       if  ( (type1 == POLYGONTYPE) && (type2 == LINETYPE) )
+                       {
+                               this_dist = distance_line_poly( (LINE3D *)o2, (POLYGON3D *)o1 );
+                       }
+
+                       if (dist_set)
+                       {
+                               dist = min(dist,this_dist);
+                       }
+                       else
+                       {
+                               dist = this_dist;   //first one through
+                               dist_set = TRUE;
+                       }
+
+                       if (dist <= 0.0)
+                               PG_RETURN_FLOAT8( (double) 0.0); // no need to look for things closer
+               }
+       }
+       if (dist <0)
+               dist = 0; //computational error, may get -0.00000000001
+       PG_RETURN_FLOAT8(dist);
+}
+
+
+//expand_bbox(bbox3d, d)
+// returns a new bbox which is exanded d unit in all directions
+PG_FUNCTION_INFO_V1(expand_bbox);
+Datum expand_bbox(PG_FUNCTION_ARGS)
+{
+       BOX3D  *bbox   = (BOX3D *) PG_GETARG_POINTER(0);
+       double  d          = PG_GETARG_FLOAT8(1);
+       BOX3D    *result = (BOX3D *) palloc(sizeof(BOX3D));
+
+
+       result->LLB.x = bbox->LLB.x - d;
+       result->LLB.y = bbox->LLB.y - d;
+       result->LLB.z = bbox->LLB.z - d;
+
+
+       result->URT.x = bbox->URT.x + d;
+       result->URT.y = bbox->URT.y + d;
+       result->URT.z = bbox->URT.z + d;
+
+
+       PG_RETURN_POINTER(result);
+}
+
+// makes a polygon of the expanded features bvol - 1st point = LL 3rd=UR
+// 2d only
+// create new geometry of type polygon, 1 ring, 5 points
+PG_FUNCTION_INFO_V1(expand_geometry);
+Datum expand_geometry(PG_FUNCTION_ARGS)
+{
+       GEOMETRY *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       double d = PG_GETARG_FLOAT8(1);
+       GEOMETRY *result;
+       POLYGON3D *poly;
+       POINT3D pts[5]; //5 points around box
+       int pts_per_ring[1];
+       int poly_size;
+
+       //use LLB's z value (we're going to set is3d to false)
+
+       //0,1,2,3,4 --> CCW order,  4,3,2,1,0 --> CW order
+       set_point( &pts[4], geom->bvol.LLB.x - d, geom->bvol.LLB.y - d,
+               geom->bvol.LLB.z - d );
+       set_point( &pts[3], geom->bvol.URT.x + d, geom->bvol.LLB.y - d,
+               geom->bvol.LLB.z - d );
+       set_point( &pts[2], geom->bvol.URT.x + d, geom->bvol.URT.y + d,
+               geom->bvol.LLB.z - d);
+       set_point( &pts[1], geom->bvol.LLB.x - d, geom->bvol.URT.y + d,
+               geom->bvol.LLB.z - d);
+       memcpy(&pts[0], &pts[4], sizeof(POINT3D));
+
+       pts_per_ring[0] = 5; //ring has 5 points
+
+       //make a polygon
+       poly = make_polygon(1, pts_per_ring, pts, 5, &poly_size);
+
+       result = make_oneobj_geometry(poly_size, (char *)poly, POLYGONTYPE, FALSE,geom->SRID, geom->scale, geom->offsetX, geom->offsetY);
+
+       PG_RETURN_POINTER(result);
+
+}
+
+//startpoint(geometry) :- if geometry is a linestring, return the first
+//point.  Otherwise, return NULL.
+
+PG_FUNCTION_INFO_V1(startpoint);
+Datum startpoint(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                      *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               POINT3D                 *pt;
+               LINE3D                  *line;
+               int32                           *offsets1;
+
+       if ( (geom1->type != LINETYPE) && (geom1->type != MULTILINETYPE) )
+               PG_RETURN_NULL();
+
+       offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
+
+
+       line = (LINE3D *) ( (char *) geom1 +offsets1[0]);
+
+       pt = &line->points[0];
+
+       PG_RETURN_POINTER(
+                               make_oneobj_geometry(sizeof(POINT3D),
+                                                        (char *) pt,
+                                                          POINTTYPE,  geom1->is3d, geom1->SRID, geom1->scale, geom1->offsetX, geom1->offsetY)
+                               );
+}
+
+//endpoint(geometry) :- if geometry is a linestring, return the last
+//point.  Otherwise, return NULL.
+
+PG_FUNCTION_INFO_V1(endpoint);
+Datum endpoint(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                      *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               POINT3D                 *pt;
+               LINE3D                  *line;
+               int32                           *offsets1;
+
+
+
+       if ( (geom1->type != LINETYPE) && (geom1->type != MULTILINETYPE) )
+               PG_RETURN_NULL();
+
+       offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
+
+
+       line = (LINE3D *) ( (char *) geom1 +offsets1[0]);
+
+
+       pt = &line->points[line->npoints-1];
+
+       PG_RETURN_POINTER(
+                               make_oneobj_geometry(sizeof(POINT3D),
+                                                        (char *) pt,
+                                                          POINTTYPE,  geom1->is3d, geom1->SRID, geom1->scale, geom1->offsetX, geom1->offsetY)
+                               );
+}
+
+
+
+//isclosed(geometry) :- if geometry is a linestring then returns
+//startpoint == endpoint.  If its not a linestring then return NULL.  If
+//its a multilinestring, return true only if all the sub-linestrings have
+//startpoint=endpoint.
+//calculations always done in 3d (even if you do a force2d)
+
+
+PG_FUNCTION_INFO_V1(isclosed);
+Datum isclosed(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                      *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               POINT3D                 *pt1,*pt2;
+               LINE3D                  *line;
+               int32                           *offsets1;
+               int                             t;
+
+       if  (!((geom1->type == LINETYPE) || (geom1->type == MULTILINETYPE) ))
+               PG_RETURN_NULL();
+
+       offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
+
+
+       for (t=0; t< geom1->nobjs; t++)
+       {
+               line = (LINE3D *) ( (char *) geom1 +offsets1[t]);
+               pt1 = &line->points[0];
+               pt2 = &line->points[line->npoints-1];
+
+               if ( (pt1->x != pt2->x) || (pt1->y != pt2->y) || (pt1->z != pt2->z) )
+                       PG_RETURN_BOOL(FALSE);
+       }
+       PG_RETURN_BOOL(TRUE);
+}
+
+#if ! USE_GEOS
+PG_FUNCTION_INFO_V1(centroid);
+Datum centroid(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                      *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               int32                           *offsets1;
+               POINT3D                 *pts,*cent;
+               POLYGON3D                       *poly;
+               int                             t,v;
+               int                             num_points,num_points_tot;
+               double                  tot_x,tot_y,tot_z;
+               GEOMETRY                        *result;
+
+
+
+       offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
+
+       if  (!((geom1->type == POLYGONTYPE) || (geom1->type == MULTIPOLYGONTYPE) ))
+               PG_RETURN_NULL();
+
+       //find the centroid
+       num_points_tot = 0;
+               tot_x = 0; tot_y =0; tot_z=0;
+
+       for (t=0;t<geom1->nobjs;t++)
+       {
+               poly = (POLYGON3D *) ( (char *) geom1 +offsets1[t]);
+               pts = (POINT3D *) ( (char *)&(poly->npoints[poly->nrings] )  );
+               pts = (POINT3D *) MAXALIGN(pts);
+               num_points =poly->npoints[0];
+
+               // just do the outer ring
+       //      for (u=0;u<poly->nrings;u++)
+       //      {
+       //              num_points += poly->npoints[u];
+       //      }
+
+               num_points_tot += num_points-1; //last point = 1st point
+               for (v=0;v<num_points-1;v++)
+               {
+                       tot_x  += pts[v].x;
+                       tot_y  += pts[v].y;
+                       tot_z  += pts[v].z;
+               }
+       }
+       cent = palloc(sizeof(POINT3D));
+       set_point(cent, tot_x/num_points_tot, tot_y/num_points_tot,tot_z/num_points_tot);
+        result = (
+                               make_oneobj_geometry(sizeof(POINT3D),
+                                                        (char *) cent,
+                                                          POINTTYPE,  geom1->is3d, geom1->SRID, geom1->scale, geom1->offsetX, geom1->offsetY)
+                               );
+       pfree(cent);
+       PG_RETURN_POINTER(result);
+
+}
+#endif // ! defined USE_GEOS
+
+// max_distance(geom,geom)  (both geoms must be linestrings)
+//find max distance between l1 and l2
+// method: for each point in l1, find distance to l2.
+//             return max distance
+//
+// note: max_distance(l1,l2) != max_distance(l2,l1)
+// returns double
+
+PG_FUNCTION_INFO_V1(max_distance);
+Datum max_distance(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                      *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               GEOMETRY                      *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+               LINE3D                  *l1,*l2;
+
+               int32                   *offsets1,*offsets2;
+
+               int                     t;
+               POINT3D         *pt;
+
+               double          result,dist;
+
+       elog(ERROR, "This function is unimplemented yet");
+       PG_RETURN_NULL();
+
+       if (geom1->SRID != geom2->SRID)
+       {
+               elog(ERROR,"Operation on two GEOMETRIES with different SRIDs\n");
+               PG_RETURN_NULL();
+       }
+
+       // only works with lines
+       if  (  (geom1->type != LINETYPE) || (geom2->type != LINETYPE) )
+               PG_RETURN_NULL();
+
+       offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
+       offsets2 = (int32 *) ( ((char *) &(geom2->objType[0] ))+ sizeof(int32) * geom2->nobjs ) ;
+
+       l1 =  (LINE3D *) ( (char *) geom1 +offsets1[0]  );
+       l2 =  (LINE3D *) ( (char *) geom2 +offsets2[0]  ) ;
+
+       result = -9999; // all distances should be positive
+
+       for(t=0;t<l1->npoints;t++) //for each point in l1
+       {
+               pt = &l1->points[t];
+               dist =  distance_pt_line(pt, l2);
+
+               if (dist > result)
+               {
+                       result = dist;
+               }
+       }
+
+       if (result <0.0)
+               result = 0;
+
+       PG_RETURN_FLOAT8(result);
+}
+
+// optimistic_overlap(Polygon P1, Multipolygon MP2, double dist)
+// returns true if P1 overlaps MP2
+//   method: bbox check - is separation < dist?  no - return false (quick)
+//                                               yes  - return distance(P1,MP2) < dist
+
+PG_FUNCTION_INFO_V1(optimistic_overlap);
+Datum optimistic_overlap(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                      *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               GEOMETRY                      *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+               double                  dist = PG_GETARG_FLOAT8(2);
+               BOX3D                           g1_bvol;
+               double                  calc_dist;
+
+       if (geom1->SRID != geom2->SRID)
+       {
+               elog(ERROR,"optimistic_overlap:Operation on two GEOMETRIES with different SRIDs\n");
+               PG_RETURN_NULL();
+       }
+
+       if (geom1->type != POLYGONTYPE)
+       {
+               elog(ERROR,"optimistic_overlap: first arg isnt a polygon\n");
+               PG_RETURN_NULL();
+       }
+
+       if ( (geom2->type != POLYGONTYPE) &&  (geom2->type != MULTIPOLYGONTYPE) )
+       {
+               elog(ERROR,"optimistic_overlap: 2nd arg isnt a [multi-]polygon\n");
+               PG_RETURN_NULL();
+       }
+
+       //bbox check
+
+       memcpy(&g1_bvol, &geom1->bvol, sizeof(BOX3D) );
+
+       g1_bvol.LLB.x = g1_bvol.LLB.x - dist;
+       g1_bvol.LLB.y = g1_bvol.LLB.y - dist;
+
+       g1_bvol.URT.x = g1_bvol.URT.x + dist;
+       g1_bvol.URT.y = g1_bvol.URT.y + dist;
+
+       //xmin = LLB.x, xmax = URT.x
+
+
+       if (  (g1_bvol.LLB.x > geom2->bvol.URT.x) ||
+               (g1_bvol.URT.x < geom2->bvol.LLB.x) ||
+               (g1_bvol.LLB.y > geom2->bvol.URT.y) ||
+               (g1_bvol.URT.y < geom2->bvol.LLB.y)
+          )
+       {
+               PG_RETURN_BOOL(FALSE);  //bbox not overlap
+       }
+
+       //compute distances
+               //should be a fast calc if they actually do intersect
+       calc_dist =     DatumGetFloat8 (DirectFunctionCall2(distance,   PointerGetDatum( geom1 ),       PointerGetDatum( geom2 )));
+
+       PG_RETURN_BOOL(calc_dist < dist);
+}
+
+//returns a list of points for a single polygon
+// foreach segment
+//  (1st and last points will be unaltered, but
+//   there will be more points inbetween if segment length is
+POINT3D        *segmentize_ring(POINT3D        *points, double dist, int num_points_in, int *num_points_out)
+{
+       double  seg_distance;
+       int     max_points, offset_new,offset_old;
+       POINT3D *result,*r;
+       bool    keep_going;
+       POINT3D *last_point, *next_point;
+
+
+               //initial storage
+       max_points = 1000;
+       offset_new=0; //start at beginning of points list
+       result = (POINT3D *) palloc (sizeof(POINT3D) * max_points);
+
+       memcpy(result, points, sizeof(POINT3D) ); //1st point
+       offset_new++;
+
+       last_point = points;
+       offset_old = 1;
+
+       keep_going = 1;
+       while(keep_going)
+       {
+               next_point = &points[offset_old];
+
+                       //distance last->next > dist
+               seg_distance = sqrt(
+                       (next_point->x-last_point->x)*(next_point->x-last_point->x) +
+                       (next_point->y-last_point->y)*(next_point->y-last_point->y) );
+               if (offset_new >= max_points)
+               {
+                       //need to add new points to result
+                       r = result;
+                       result = (POINT3D *) palloc (sizeof(POINT3D) * max_points *2);//double size
+                       memcpy(result,r, sizeof(POINT3D)*max_points);
+                       max_points *=2;
+                       pfree(r);
+               }
+
+               if (seg_distance > dist)
+               {
+                       //add a point at the distance location
+                       // and set last_point to this loc
+
+                       result[offset_new].x = last_point->x + (next_point->x-last_point->x)/seg_distance * dist;
+                       result[offset_new].y = last_point->y + (next_point->y-last_point->y)/seg_distance * dist;
+                       last_point = &result[offset_new];
+                       offset_new ++;
+               }
+               else
+               {
+                       //everything fine, just add the next_point and pop forward
+                       result[offset_new].x = next_point->x;
+                       result[offset_new].y = next_point->y;
+                       offset_new++;
+                       offset_old++;
+                       last_point = next_point;
+               }
+               keep_going = (offset_old < num_points_in);
+       }
+       *num_points_out = offset_new;
+       return result;
+}
+
+// select segmentize('POLYGON((0 0, 0 10, 5 5, 0 0))',1);
+
+//segmentize(GEOMETRY P1, double maxlen)
+//given a [multi-]polygon, return a new polygon with each segment at most a given length
+PG_FUNCTION_INFO_V1(segmentize);
+Datum segmentize(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               GEOMETRY                *result = NULL, *result2;
+               double                  maxdist = PG_GETARG_FLOAT8(1);
+               int32                   *offsets1,*p_npoints_ring;
+               int                             g1_i,r;
+               POLYGON3D               *p,*poly;
+               POINT3D                 *polypts;
+               int                             num_polypts;
+               POINT3D                 *all_polypts;
+               int                             all_num_polypts,all_num_polypts_max;
+               POINT3D                 *p_points,*rr;
+               int                             new_size;
+               bool                    first_one;
+               int                             poly_size;
+               BOX3D                   *bbox;
+
+       first_one = 1;
+
+
+       if ( (geom1->type != POLYGONTYPE) &&  (geom1->type != MULTIPOLYGONTYPE) )
+       {
+               elog(ERROR,"segmentize: 1st arg isnt a [multi-]polygon\n");
+               PG_RETURN_NULL();
+       }
+
+
+
+       offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
+
+       for (g1_i=0; g1_i < geom1->nobjs; g1_i++)
+       {
+
+               all_num_polypts = 0;
+               all_num_polypts_max = 1000;
+               all_polypts = (POINT3D *) palloc (sizeof(POINT3D) * all_num_polypts_max );
+
+               p = (POLYGON3D*)((char *) geom1 +offsets1[g1_i]) ;  // the polygon
+
+               p_npoints_ring = (int32 *) palloc(sizeof(int32) * p->nrings);
+
+               p_points = (POINT3D *) ( (char *)&(p->npoints[p->nrings] )  );
+               p_points = (POINT3D *) MAXALIGN(p_points);
+
+               for (r=0;r<p->nrings;r++)  //foreach ring in the polygon
+               {
+                       polypts = segmentize_ring(p_points, maxdist, p->npoints[r], &num_polypts);
+                       if ( (all_num_polypts + num_polypts) < all_num_polypts_max )
+                       {
+                               //just add
+                               memcpy( &all_polypts[all_num_polypts], polypts, sizeof(POINT3D) *num_polypts );
+                               all_num_polypts += num_polypts;
+                       }
+                       else
+                       {
+                               //need more space
+                               new_size = all_num_polypts_max + num_polypts + 1000;
+                               rr = (POINT3D*) palloc(sizeof(POINT3D) * new_size);
+                               memcpy(rr,all_polypts, sizeof(POINT3D) *all_num_polypts);
+                               memcpy(&rr[all_num_polypts], polypts , sizeof(POINT3D) *num_polypts);
+                               pfree(all_polypts);
+                               all_polypts = rr;
+                               all_num_polypts += num_polypts;
+                       }
+                       //set points in ring value
+                       pfree(polypts);
+                       p_npoints_ring[r] = num_polypts;
+               } //for each ring
+
+               poly = make_polygon(p->nrings, p_npoints_ring, all_polypts, all_num_polypts, &poly_size);
+
+               if (first_one)
+               {
+                       first_one = 0;
+                       result = make_oneobj_geometry(poly_size, (char *)poly, POLYGONTYPE, FALSE,geom1->SRID, geom1->scale, geom1->offsetX, geom1->offsetY);
+                       pfree(poly);
+                       pfree(all_polypts);
+               }
+               else
+               {
+                       result2 = add_to_geometry(result,poly_size, (char *) poly, POLYGONTYPE);
+                       bbox = bbox_of_geometry( result2 ); // make bounding box
+                       memcpy( &result2->bvol, bbox, sizeof(BOX3D) ); // copy bounding box
+                       pfree(bbox); // free bounding box
+                       pfree(result);
+                       result = result2;
+                       pfree(poly);
+                       pfree(all_polypts);
+               }
+
+       } // foreach polygon
+       PG_RETURN_POINTER(result);
+}
+
+/*
+ * This is a geometry array constructor
+ * for use as aggregates sfunc.
+ * Will have * as input an array of Geometry pointers and a Geometry.
+ * Will DETOAST given geometry and put a pointer to it
+ * in the given array. DETOASTED value is first copied
+ * to a safe memory context to avoid premature deletion.
+ */
+PG_FUNCTION_INFO_V1(geom_accum);
+Datum geom_accum(PG_FUNCTION_ARGS)
+{
+       ArrayType *array;
+       int nelems, nbytes;
+       Datum datum;
+       GEOMETRY *geom;
+       ArrayType *result;
+       Pointer **pointers;
+       MemoryContext oldcontext;
+
+       datum = PG_GETARG_DATUM(0);
+       if ( (Pointer *)datum == NULL ) {
+               array = NULL;
+               nelems = 0;
+               //elog(NOTICE, "geom_accum: NULL array, nelems=%d", nelems);
+       } else {
+               array = (ArrayType *) PG_DETOAST_DATUM_COPY(datum);
+               nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
+       }
+
+       datum = PG_GETARG_DATUM(1);
+       // Do nothing, return state array
+       if ( (Pointer *)datum == NULL )
+       {
+               //elog(NOTICE, "geom_accum: NULL geom, nelems=%d", nelems);
+               PG_RETURN_ARRAYTYPE_P(array);
+       }
+
+       /*
+        * Switch to * flinfo->fcinfo->fn_mcxt
+        * memory context to be sure both detoasted
+        * geometry AND array of pointers to it
+        * last till the call to unite_finalfunc.
+        */
+       oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
+
+       /* Make a DETOASTED copy of input geometry */
+       geom = (GEOMETRY *)PG_DETOAST_DATUM_COPY(datum);
+
+       //elog(NOTICE, "geom_accum: adding %p (nelems=%d)", geom, nelems);
+
+       /*
+        * Might use a more optimized version instead of repalloc'ing
+        * at every iteration. This is not the bottleneck anyway.
+        *              --strk(TODO);
+        */
+       ++nelems;
+       nbytes = ARR_OVERHEAD(1) + sizeof(Pointer *) * nelems;
+       if ( ! array ) {
+               result = (ArrayType *) palloc(nbytes);
+               result->size = nbytes;
+               result->ndim = 1;
+               *((int *) ARR_DIMS(result)) = nelems;
+       } else {
+               result = (ArrayType *) repalloc(array, nbytes);
+               result->size = nbytes;
+               result->ndim = 1;
+               *((int *) ARR_DIMS(result)) = nelems;
+       }
+
+       pointers = (Pointer **)ARR_DATA_PTR(result);
+       pointers[nelems-1] = (Pointer *)geom;
+
+       /* Go back to previous memory context */
+       MemoryContextSwitchTo(oldcontext);
+
+
+       PG_RETURN_ARRAYTYPE_P(result);
+
+}
+
+/*
+ * collect_garray ( GEOMETRY[] ) returns a geometry which contains
+ * all the sub_objects from all of the geometries in given array.
+ *
+ * returned geometry is the simplest possible, based on the types
+ * of the collected objects
+ * ie. if all are of either X or multiX, then a multiX is returned
+ * bboxonly types are treated as null geometries (no sub_objects)
+ */
+PG_FUNCTION_INFO_V1( collect_garray );
+Datum collect_garray ( PG_FUNCTION_ARGS )
+{
+       Datum datum;
+       ArrayType *array;
+       int nelems, srid=-1, is3d=0;
+       GEOMETRY **geoms;
+       GEOMETRY *result=NULL, *geom, *tmp;
+       int i, o;
+       BOX3D *bbox;
+
+       /* Get input datum */
+       datum = PG_GETARG_DATUM(0);
+
+       /* Return null on null input */
+       if ( (Pointer *)datum == NULL ) PG_RETURN_NULL();
+
+       /* Get actual ArrayType */
+       array = (ArrayType *) PG_DETOAST_DATUM(datum);
+
+       /* Get number of geometries in array */
+       nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
+
+       /* Return null on 0-elements input array */
+       if ( nelems == 0 ) PG_RETURN_NULL();
+
+       /* Get pointer to GEOMETRY pointers array */
+       geoms = (GEOMETRY **)ARR_DATA_PTR(array);
+
+       /* Return the only present element of a 1-element array */
+       if ( nelems == 1 ) PG_RETURN_POINTER(geoms[0]);
+
+       /* Iterate over all geometries in array */
+       for (i=0; i<nelems; i++)
+       {
+               int32 *offsets;
+
+               geom = geoms[i];
+
+               /* Skip NULL array elements (are them possible?) */
+               if ( geom == NULL ) continue;
+
+               /* Use first NOT-NULL GEOMETRY as the base */
+               if ( ! result )
+               {
+                       /* Remember first geometry's SRID for later checks */
+                       srid = geom->SRID;
+
+                       /* Get first geometry's is3d flag as base is3d */
+                       is3d = geom->is3d;
+
+                       result = (GEOMETRY *)palloc(geom->size);
+                       if ( ! result ) {
+       elog(ERROR, "collect_garray: out of virtual memory");
+       PG_RETURN_NULL();
+                       }
+                       memcpy(result, geom, geom->size);
+
+                       /*
+                        * I belive memory associated with geometries
+                        * in array can be safely removed. Comment
+                        * this out if you get memory faults!
+                        * TODO: inspect this (as long as passed
+                        * array is the result of geom_accum this
+                        * is true because geom_accum will DETOAST_COPY
+                        * while for direct user call I do not know)
+                        */
+                       pfree(geom);
+
+                       continue;
+               }
+
+               /* Skip geometry if it contains no sub-objects */
+               if ( ! geom->nobjs )
+               {
+                       if ( geom->is3d ) is3d = 1; // should I care ?
+                       pfree(geom); // se note above
+                       continue;
+               }
+
+               /*
+                * If we are here this means we are in front of a
+                * good (non empty) geometry beside the first
+                */
+
+               /* Fist let's check for SRID compatibility */
+               if ( geom->SRID != srid )
+               {
+       elog(ERROR, "Operation on GEOMETRIES with different SRIDs");
+       PG_RETURN_NULL();
+               }
+
+               /*
+                * Set result is3d flag to true if at least one
+                * of geometries in set has is set to true
+                */
+               if ( geom->is3d ) is3d = 1;
+
+               /* Get to sub-objects offset */
+               offsets = (int32 *)(((char *)&(geom->objType[0])) +
+                               sizeof(int32) * geom->nobjs ) ;
+
+               /* Iterate over geometry sub-objects */
+               for (o=0; o<geom->nobjs; o++)
+               {
+                       int size, type;
+                       char *obj;
+
+                       /* Get object pointer */
+                       obj = (char *) geom+offsets[o];
+
+                       /* Get object type */
+                       type = geom->objType[o];
+
+                       /* Get object size (fast way) */
+                       if( o == geom->nobjs-1 ) {
+                               size = geom->size - offsets[o];
+                       } else {
+                               size = offsets[o+1] - offsets[o];
+                       }
+
+                       /*
+                        * Add sub-object to base geometry,
+                        * replace base geometry with new one.
+                        */
+                       tmp = add_to_geometry(result, size, obj, type);
+                       pfree( result );
+                       result = tmp;
+               }
+               pfree(geom); // se note above
+       }
+
+       /* Check we got something in our result */
+       if ( result == NULL ) PG_RETURN_NULL();
+
+       /*
+        * We should now have a big fat geometry composed of all
+        * sub-objects from all geometries in array
+        */
+
+       /* Set is3d flag */
+       result->is3d = is3d;
+
+       /* Construct bounding volume */
+       bbox = bbox_of_geometry( result ); // make
+       memcpy( &result->bvol, bbox, sizeof(BOX3D) ); // copy
+       pfree( bbox ); // release
+
+       PG_RETURN_POINTER( result );
+}
+
+// collector( geom, geom ) returns a geometry which contains
+// all the sub_objects from both of the argument geometries
+
+// returned geometry is the simplest possible, based on the types
+// of the colelct objects
+// ie. if all are of either X or multiX, then a multiX is returned
+// bboxonly types are treated as null geometries (no sub_objects)
+PG_FUNCTION_INFO_V1( collector );
+Datum collector( PG_FUNCTION_ARGS )
+{
+       Pointer         geom1_ptr = PG_GETARG_POINTER(0);
+       Pointer         geom2_ptr =  PG_GETARG_POINTER(1);
+       GEOMETRY        *geom1, *geom2, *temp, *result;
+       BOX3D           *bbox;
+       int32           i, size, *offsets2;
+
+       // return null if both geoms are null
+       if ( (geom1_ptr == NULL) && (geom2_ptr == NULL) )
+       {
+               PG_RETURN_NULL();
+       }
+
+       // return a copy of the second geom if only first geom is null
+       if (geom1_ptr == NULL)
+       {
+               geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+               result = (GEOMETRY *)palloc( geom2->size );
+               memcpy( result, geom2, geom2->size );
+               PG_RETURN_POINTER(result);
+       }
+
+       // return a copy of the first geom if only second geom is null
+       if (geom2_ptr == NULL)
+       {
+               geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               result = (GEOMETRY *)palloc( geom1->size );
+               memcpy( result, geom1, geom1->size );
+               PG_RETURN_POINTER(result);
+       }
+
+       geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       if ( geom1->SRID != geom2->SRID )
+       {
+               elog(ERROR,"Operation on two GEOMETRIES with different SRIDs\n");
+               PG_RETURN_NULL();
+       }
+
+       if (geom1->nobjs ==0)
+       {
+               geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+               result = (GEOMETRY *)palloc( geom2->size );
+               memcpy( result, geom2, geom2->size );
+               PG_RETURN_POINTER(result);
+       }
+       if (geom2->nobjs ==0)
+       {
+               geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               result = (GEOMETRY *)palloc( geom1->size );
+               memcpy( result, geom1, geom1->size );
+               PG_RETURN_POINTER(result);
+       }
+
+       result = (GEOMETRY *)palloc( geom1->size );
+       memcpy( result, geom1, geom1->size );
+
+       offsets2 = (int32 *)( ((char *)&(geom2->objType[0])) + sizeof( int32 ) * geom2->nobjs ) ;
+
+       for (i=0; i<geom2->nobjs; i++)
+       {
+               if( i == geom2->nobjs-1 )
+               {
+                       size = geom2->size - offsets2[i];
+               }
+               else
+               {
+                       size = offsets2[i+1] - offsets2[i];
+               }
+               temp = add_to_geometry( result, size, ((char *) geom2 + offsets2[i]), geom2->objType[i] );
+               pfree( result );
+               result = temp;
+       }
+
+       result->is3d = geom1->is3d || geom2->is3d;
+       bbox = bbox_of_geometry( result ); // make bounding box
+       memcpy( &result->bvol, bbox, sizeof(BOX3D) ); // copy bounding box
+       pfree( bbox ); // free bounding box
+
+       PG_RETURN_POINTER( result );
+}
+
+PG_FUNCTION_INFO_V1(box3d_xmin);
+Datum box3d_xmin(PG_FUNCTION_ARGS)
+{
+       BOX3D              *box1 = (BOX3D *) PG_GETARG_POINTER(0);
+
+       PG_RETURN_FLOAT8(box1->LLB.x);
+}
+
+PG_FUNCTION_INFO_V1(box3d_ymin);
+Datum box3d_ymin(PG_FUNCTION_ARGS)
+{
+       BOX3D              *box1 = (BOX3D *) PG_GETARG_POINTER(0);
+
+       PG_RETURN_FLOAT8(box1->LLB.y);
+}
+
+PG_FUNCTION_INFO_V1(box3d_zmin);
+Datum box3d_zmin(PG_FUNCTION_ARGS)
+{
+       BOX3D              *box1 = (BOX3D *) PG_GETARG_POINTER(0);
+
+       PG_RETURN_FLOAT8(box1->LLB.z);
+}
+
+PG_FUNCTION_INFO_V1(box3d_xmax);
+Datum box3d_xmax(PG_FUNCTION_ARGS)
+{
+       BOX3D              *box1 = (BOX3D *) PG_GETARG_POINTER(0);
+
+       PG_RETURN_FLOAT8(box1->URT.x);
+}
+
+PG_FUNCTION_INFO_V1(box3d_ymax);
+Datum box3d_ymax(PG_FUNCTION_ARGS)
+{
+       BOX3D              *box1 = (BOX3D *) PG_GETARG_POINTER(0);
+
+       PG_RETURN_FLOAT8(box1->URT.y);
+}
+
+PG_FUNCTION_INFO_V1(box3d_zmax);
+Datum box3d_zmax(PG_FUNCTION_ARGS)
+{
+       BOX3D              *box1 = (BOX3D *) PG_GETARG_POINTER(0);
+
+       PG_RETURN_FLOAT8(box1->URT.z);
+}
+
+PG_FUNCTION_INFO_V1(box3dtobox);
+Datum box3dtobox(PG_FUNCTION_ARGS)
+{
+       BOX3D *box1 = (BOX3D *) PG_GETARG_POINTER(0);
+       BOX *out;
+       out = convert_box3d_to_box(box1);
+       PG_RETURN_POINTER(out);
+}
+
+
+PG_FUNCTION_INFO_V1(isempty);
+Datum isempty(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                      *geom1= (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+
+       if (geom1->nobjs ==0)
+               PG_RETURN_BOOL(TRUE);
+       PG_RETURN_BOOL(FALSE);
+}
+
+
+// converts multi* types with 1 element to the single-element version.
+// ie. MULTIPOINT(0 0) --> POINT(0 0)
+void compressType(GEOMETRY *g)
+{
+       if (g->nobjs ==1)
+       {
+               if (g->type == MULTIPOINTTYPE)
+               {
+                       g->type = POINTTYPE;
+                       return;
+               }
+               if (g->type == MULTILINETYPE)
+               {
+                       g->type = LINETYPE;
+                       return;
+               }
+               if (g->type == MULTIPOLYGONTYPE)
+               {
+                       g->type = POLYGONTYPE;
+                       return;
+               }
+
+       }
+}
+
+
+// converts single-type (point,linestring,polygon)
+// to multi* types with 1 element
+// ie. POINT(0 0) --> MULTIPOINT(0 0)
+//
+//postgis09=# select fluffType('POINT(0 0)');
+//        flufftype
+//-------------------------
+// SRID=-1;MULTIPOINT(0 0)
+//(1 row)
+//
+//postgis09=# select fluffType('LINESTRING(0 0, 1 1)');
+//             flufftype
+//------------------------------------
+// SRID=-1;MULTILINESTRING((0 0,1 1))
+//(1 row)
+
+PG_FUNCTION_INFO_V1(fluffType);
+Datum fluffType(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                      *geom1= (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               GEOMETRY                          *g;
+
+               g = (GEOMETRY*) palloc( *((int *) geom1) );
+               memcpy(g,geom1, *((int *) geom1));
+
+                       if (g->type == POINTTYPE)
+                       {
+                               g->type = MULTIPOINTTYPE;
+                       }
+                       if (g->type == LINETYPE)
+                       {
+                               g->type = MULTILINETYPE;
+                       }
+                       if (g->type == POLYGONTYPE)
+                       {
+                               g->type = MULTIPOLYGONTYPE;
+                       }
+
+       PG_FREE_IF_COPY(geom1,0);
+       PG_RETURN_POINTER(g);
+}
+
+PG_FUNCTION_INFO_V1(postgis_lib_version);
+Datum postgis_lib_version(PG_FUNCTION_ARGS)
+{
+       char *ver = POSTGIS_LIB_VERSION;
+       text *result;
+       result = (text *) palloc(VARHDRSZ  + strlen(ver));
+       VARATT_SIZEP(result) = VARHDRSZ + strlen(ver) ;
+       memcpy(VARDATA(result), ver, strlen(ver));
+       PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(postgis_scripts_released);
+Datum postgis_scripts_released(PG_FUNCTION_ARGS)
+{
+       char *ver = POSTGIS_SCRIPTS_VERSION;
+       text *result;
+       result = (text *) palloc(VARHDRSZ  + strlen(ver));
+       VARATT_SIZEP(result) = VARHDRSZ + strlen(ver) ;
+       memcpy(VARDATA(result), ver, strlen(ver));
+       PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(postgis_uses_stats);
+Datum postgis_uses_stats(PG_FUNCTION_ARGS)
+{
+#ifdef USE_STATS
+       PG_RETURN_BOOL(TRUE);
+#else
+       PG_RETURN_BOOL(FALSE);
+#endif
+}
diff --git a/hwgeom/postgis_geos.c b/hwgeom/postgis_geos.c
new file mode 100644 (file)
index 0000000..1713368
--- /dev/null
@@ -0,0 +1,2142 @@
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2001-2003 Refractions Research Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ *
+ **********************************************************************
+ * $Log$
+ * Revision 1.1  2004/09/20 07:50:06  strk
+ * prepared to contain old internal representation code
+ *
+ * Revision 1.39  2004/08/23 15:37:16  strk
+ * Changed SCRIPTS_VERSION to 0.0.1
+ *
+ * Revision 1.38  2004/07/28 16:10:59  strk
+ * Changed all version functions to return text.
+ * Renamed postgis_scripts_version() to postgis_scripts_installed()
+ * Added postgis_scripts_released().
+ * Added postgis_full_version().
+ *
+ * Revision 1.37  2004/07/28 13:37:43  strk
+ * Added postgis_uses_stats and postgis_scripts_version.
+ * Experimented with PIP short-circuit in within/contains functions.
+ * Documented new version functions.
+ *
+ * Revision 1.36  2004/07/27 17:51:50  strk
+ * short-circuit test for 'contains'
+ *
+ * Revision 1.35  2004/07/27 17:49:59  strk
+ * Added short-circuit test for the within function.
+ *
+ * Revision 1.34  2004/07/22 16:58:08  strk
+ * Updated to reflect geos version string split.
+ *
+ * Revision 1.33  2004/07/22 16:20:10  strk
+ * Added postgis_lib_version() and postgis_geos_version()
+ *
+ * Revision 1.32  2004/07/01 17:02:05  strk
+ * Updated to support latest GEOS (actually removed all geos-version related
+ * switches).
+ * Fixed an access to unallocated memory.
+ *
+ * Revision 1.31  2004/06/16 19:59:36  strk
+ * Changed GEOS_VERSION to POSTGIS_GEOS_VERSION to avoid future clashes
+ *
+ * Revision 1.30  2004/06/16 19:37:54  strk
+ * Added cleanup needed for GEOS > 1.0
+ *
+ * Revision 1.29  2004/06/16 18:47:59  strk
+ * Added code to detect geos version.
+ * Added appropriate includes in geos connectors.
+ *
+ * Revision 1.28  2004/04/28 22:26:02  pramsey
+ * Fixed spelling mistake in header text.
+ *
+ * Revision 1.27  2004/02/12 10:34:49  strk
+ * changed USE_GEOS check from ifdef / ifndef to if / if !
+ *
+ * Revision 1.26  2003/12/12 12:03:29  strk
+ * More debugging output, some code cleanup.
+ *
+ * Revision 1.25  2003/12/12 10:28:50  strk
+ * added GEOSnoop OUTPUT debugging info
+ *
+ * Revision 1.24  2003/12/12 10:08:24  strk
+ * Added GEOSnoop function and some optional debugging output for
+ * geos<->postgis converter (define DEBUG_CONVERTER at top postgis_geos.c)
+ *
+ * Revision 1.23  2003/11/05 18:25:08  strk
+ * moved #ifdef USE_GEOS below prototypes, added NULL implementation of unite_garray
+ *
+ * Revision 1.22  2003/11/05 18:02:41  strk
+ * renamed unite_finalfunc to unite_garray
+ *
+ * Revision 1.21  2003/11/04 19:06:08  strk
+ * added missing first geom pfree in unite_finalfunc
+ *
+ * Revision 1.20  2003/10/29 15:53:10  strk
+ * geoscentroid() removed. both geos and pgis versions are called 'centroid'.
+ * only one version will be compiled based on USE_GEOS flag.
+ *
+ * Revision 1.19  2003/10/29 13:59:40  strk
+ * Added geoscentroid function.
+ *
+ * Revision 1.18  2003/10/28 15:16:17  strk
+ * unite_sfunc() from postgis_geos.c renamed to geom_accum() and moved in postgis_fn.c
+ *
+ * Revision 1.17  2003/10/28 10:59:55  strk
+ * handled NULL state array in unite_finalfunc, cleaned up some spurios code
+ *
+ * Revision 1.16  2003/10/27 23:44:54  strk
+ * unite_sfunc made always copy input array in long lived memory context.
+ * It should now work with safer memory.
+ *
+ * Revision 1.15  2003/10/27 20:13:05  strk
+ * Made GeomUnion release memory soon, Added fastunion support functions
+ *
+ * Revision 1.14  2003/10/24 21:52:35  strk
+ * Modified strcmp-based if-else with switch-case in GEOS2POSTGIS()
+ * using new GEOSGeometryTypeId() interface.
+ *
+ * Revision 1.13  2003/10/24 08:28:49  strk
+ * Fixed memory leak in GEOSGetCoordinates(), made sure that GEOS2POSTGIS
+ * free type string even in case of collapsed geoms. Made sure that geomunion
+ * release memory in case of exception thrown by GEOSUnion. Sooner release
+ * of palloced memory in PolyFromGeometry (pts_per_ring).
+ *
+ * Revision 1.12  2003/10/16 20:16:18  dblasby
+ * Added NOTICE_HANDLER function.  For some reason this didnt get properly
+ * committed last time.
+ *
+ * Revision 1.11  2003/10/14 23:19:19  dblasby
+ * GEOS2POSTGIS - added protection to pfree(NULL) for multi* geoms
+ *
+ * Revision 1.10  2003/10/03 16:45:37  dblasby
+ * added pointonsurface() as a sub.  Some memory management fixes/tests.
+ * removed a few NOTICEs.
+ *
+ * Revision 1.9  2003/09/16 20:27:12  dblasby
+ * added ability to delete geometries.
+ *
+ * Revision 1.8  2003/08/08 18:19:20  dblasby
+ * Conformance changes.
+ * Removed junk from postgis_debug.c and added the first run of the long
+ * transaction locking support.  (this will change - dont use it)
+ * conformance tests were corrected
+ * some dos cr/lf removed
+ * empty geometries i.e. GEOMETRYCOLLECT(EMPTY) added (with indexing support)
+ * pointN(<linestring>,1) now returns the first point (used to return 2nd)
+ *
+ * Revision 1.7  2003/08/06 19:31:18  dblasby
+ * Added the WKB parser.  Added all the functions like
+ * PolyFromWKB(<WKB>,[<SRID>]).
+ *
+ * Added all the functions like PolyFromText(<WKT>,[<SRID>])
+ *
+ * Minor problem in GEOS library fixed.
+ *
+ * Revision 1.6  2003/08/05 18:27:21  dblasby
+ * Added null implementations of new GEOS-returning-geometry functions (ie.
+ * buffer).
+ *
+ * Revision 1.5  2003/08/01 23:58:08  dblasby
+ * added the functionality to convert GEOS->PostGIS geometries.  Added those geos
+ * functions to postgis.
+ *
+ * Revision 1.4  2003/07/01 18:30:55  pramsey
+ * Added CVS revision headers.
+ *
+ *
+ **********************************************************************/
+
+
+//--------------------------------------------------------------------------
+//
+//#define DEBUG
+
+#include "postgres.h"
+
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "fmgr.h"
+
+#include "postgis.h"
+#include "utils/elog.h"
+#include "utils/array.h"
+
+#include "access/gist.h"
+#include "access/itup.h"
+#include "access/rtree.h"
+
+#include "utils/builtins.h"
+
+//#include "postgis_geos_version.h"
+
+ /*
+  * Define this to have have many notices printed
+  * during postgis->geos and geos->postgis conversions
+  */
+#undef DEBUG_CONVERTER
+#undef DEBUG_POSTGIS2GEOS
+#undef DEBUG_GEOS2POSTGIS
+
+
+typedef  struct Geometry Geometry;
+
+
+extern const char * createGEOSPoint(POINT3D *pt);
+extern void initGEOS(int maxalign);
+extern char *GEOSrelate(Geometry *g1, Geometry*g2);
+extern char GEOSrelatePattern(Geometry *g1, Geometry*g2,char *pat);
+extern  char GEOSrelateDisjoint(Geometry *g1, Geometry*g2);
+extern  char GEOSrelateTouches(Geometry *g1, Geometry*g2);
+extern  char GEOSrelateIntersects(Geometry *g1, Geometry*g2);
+extern  char GEOSrelateCrosses(Geometry *g1, Geometry*g2);
+extern  char GEOSrelateWithin(Geometry *g1, Geometry*g2);
+extern  char GEOSrelateContains(Geometry *g1, Geometry*g2);
+extern  char GEOSrelateOverlaps(Geometry *g1, Geometry*g2);
+
+extern char *GEOSasText(Geometry *g1);
+extern char GEOSisEmpty(Geometry *g1);
+extern char *GEOSGeometryType(Geometry *g1);
+extern int GEOSGeometryTypeId(Geometry *g1);
+extern char *GEOSversion();
+extern char *GEOSjtsport();
+
+
+extern void GEOSdeleteChar(char *a);
+extern void GEOSdeleteGeometry(Geometry *a);
+
+extern  Geometry *PostGIS2GEOS_point(POINT3D *point,int SRID, bool is3d);
+extern  Geometry *PostGIS2GEOS_linestring(LINE3D *line,int SRID, bool is3d);
+extern  Geometry *PostGIS2GEOS_polygon(POLYGON3D *polygon,int SRID, bool is3d);
+extern  Geometry *PostGIS2GEOS_multipolygon(POLYGON3D **polygons,int npolys, int SRID, bool is3d);
+extern Geometry *PostGIS2GEOS_multilinestring(LINE3D **lines,int nlines, int SRID, bool is3d);
+extern Geometry *PostGIS2GEOS_multipoint(POINT3D **points,int npoints, int SRID, bool is3d);
+extern Geometry *PostGIS2GEOS_box3d(BOX3D *box, int SRID);
+extern Geometry *PostGIS2GEOS_collection(Geometry **geoms, int ngeoms,int SRID, bool is3d);
+
+extern char GEOSisvalid(Geometry *g1);
+
+
+
+extern char *throw_exception(Geometry *g);
+extern Geometry *GEOSIntersection(Geometry *g1, Geometry *g2);
+
+
+extern POINT3D  *GEOSGetCoordinate(Geometry *g1);
+extern POINT3D  *GEOSGetCoordinates(Geometry *g1);
+extern int      GEOSGetNumCoordinate(Geometry *g1);
+extern Geometry        *GEOSGetGeometryN(Geometry *g1, int n);
+extern Geometry        *GEOSGetExteriorRing(Geometry *g1);
+extern Geometry        *GEOSGetInteriorRingN(Geometry *g1,int n);
+extern int     GEOSGetNumInteriorRings(Geometry *g1);
+extern int      GEOSGetSRID(Geometry *g1);
+extern int      GEOSGetNumGeometries(Geometry *g1);
+
+extern  Geometry *GEOSBuffer(Geometry *g1,double width);
+extern  Geometry *GEOSConvexHull(Geometry *g1);
+extern  Geometry *GEOSDifference(Geometry *g1,Geometry *g2);
+extern  Geometry *GEOSBoundary(Geometry *g1);
+extern  Geometry *GEOSSymDifference(Geometry *g1,Geometry *g2);
+extern  Geometry *GEOSUnion(Geometry *g1,Geometry *g2);
+extern   char GEOSequals(Geometry *g1, Geometry*g2);
+
+extern  char GEOSisSimple(Geometry *g1);
+extern char GEOSisRing(Geometry *g1);
+
+extern Geometry *GEOSpointonSurface(Geometry *g1);
+extern Geometry *GEOSGetCentroid(Geometry *g);
+
+
+Datum relate_full(PG_FUNCTION_ARGS);
+Datum relate_pattern(PG_FUNCTION_ARGS);
+Datum disjoint(PG_FUNCTION_ARGS);
+Datum touches(PG_FUNCTION_ARGS);
+Datum intersects(PG_FUNCTION_ARGS);
+Datum crosses(PG_FUNCTION_ARGS);
+Datum within(PG_FUNCTION_ARGS);
+Datum contains(PG_FUNCTION_ARGS);
+Datum overlaps(PG_FUNCTION_ARGS);
+Datum isvalid(PG_FUNCTION_ARGS);
+
+
+Datum buffer(PG_FUNCTION_ARGS);
+Datum intersection(PG_FUNCTION_ARGS);
+Datum convexhull(PG_FUNCTION_ARGS);
+Datum difference(PG_FUNCTION_ARGS);
+Datum boundary(PG_FUNCTION_ARGS);
+Datum symdifference(PG_FUNCTION_ARGS);
+Datum geomunion(PG_FUNCTION_ARGS);
+Datum unite_garray(PG_FUNCTION_ARGS);
+
+
+Datum issimple(PG_FUNCTION_ARGS);
+Datum isring(PG_FUNCTION_ARGS);
+Datum geomequals(PG_FUNCTION_ARGS);
+Datum pointonsurface(PG_FUNCTION_ARGS);
+
+Datum GEOSnoop(PG_FUNCTION_ARGS);
+
+
+Geometry *POSTGIS2GEOS(GEOMETRY *g);
+void errorIfGeometryCollection(GEOMETRY *g1, GEOMETRY *g2);
+GEOMETRY *GEOS2POSTGIS(Geometry *g, char want3d );
+
+POLYGON3D *PolyFromGeometry(Geometry *g, int *size);
+LINE3D *LineFromGeometry(Geometry *g, int *size);
+void NOTICE_MESSAGE(char *msg);
+
+#if USE_GEOS
+
+//-----------------------------------------------
+// return a GEOS Geometry from a POSTGIS GEOMETRY
+//----------------------------------------------
+
+
+
+void NOTICE_MESSAGE(char *msg)
+{
+       elog(NOTICE,msg);
+}
+
+
+/*
+ * Resize an ArrayType of 1 dimension to contain num (Pointer *) elements
+ * array will be repalloc'ed
+ */
+static ArrayType *
+resize_ptrArrayType(ArrayType *a, int num)
+{
+       int nelems = ArrayGetNItems(ARR_NDIM(a), ARR_DIMS(a));
+       int nbytes = ARR_OVERHEAD(1) + sizeof(Pointer *) * num;
+
+       if (num == nelems) return a;
+
+       a = (ArrayType *) repalloc(a, nbytes);
+
+       a->size = nbytes;
+       a->ndim = 1;
+
+       *((int *) ARR_DIMS(a)) = num;
+       return a;
+}
+
+
+/*
+ * This is the final function for union/fastunite/geomunion
+ * aggregate (still discussing the name). Will have
+ * as input an array of Geometry pointers.
+ * Will iteratively call GEOSUnion on the GEOS-converted
+ * versions of them and return PGIS-converted version back.
+ * Changing combination order *might* speed up performance.
+ *
+ * Geometries in the array are pfree'd as soon as possible.
+ *
+ */
+PG_FUNCTION_INFO_V1(unite_garray);
+Datum unite_garray(PG_FUNCTION_ARGS)
+{
+       Datum datum;
+       ArrayType *array;
+       int is3d = 0;
+       int nelems, i;
+       GEOMETRY **geoms, *result, *pgis_geom;
+       Geometry *g1, *g2, *geos_result;
+#ifdef DEBUG
+       static int call=1;
+#endif
+
+#ifdef DEBUG
+       call++;
+#endif
+
+       datum = PG_GETARG_DATUM(0);
+
+       /* Null array, null geometry (should be empty?) */
+       if ( (Pointer *)datum == NULL ) PG_RETURN_NULL();
+
+       array = (ArrayType *) PG_DETOAST_DATUM(datum);
+
+       nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
+
+#ifdef DEBUG
+       elog(NOTICE, "unite_garray: number of elements: %d", nelems);
+#endif
+
+       if ( nelems == 0 ) PG_RETURN_NULL();
+
+       geoms = (GEOMETRY **)ARR_DATA_PTR(array);
+
+       /* One-element union is the element itself */
+       if ( nelems == 1 ) PG_RETURN_POINTER(geoms[0]);
+
+       /* Ok, we really need geos now ;) */
+       initGEOS(MAXIMUM_ALIGNOF);
+
+       if ( geoms[0]->is3d ) is3d = 1;
+       geos_result = POSTGIS2GEOS(geoms[0]);
+       pfree(geoms[0]);
+       for (i=1; i<nelems; i++)
+       {
+               pgis_geom = geoms[i];
+               
+               g1 = POSTGIS2GEOS(pgis_geom);
+               /*
+                * If we free this memory now we'll have
+                * more space for the growing result geometry.
+                * We don't need it anyway.
+                */
+               pfree(pgis_geom);
+
+#ifdef DEBUG
+               elog(NOTICE, "unite_garray(%d): adding geom %d to union",
+                               call, i);
+#endif
+
+               g2 = GEOSUnion(g1,geos_result);
+               if ( g2 == NULL )
+               {
+                       GEOSdeleteGeometry(g1);
+                       GEOSdeleteGeometry(geos_result);
+                       elog(ERROR,"GEOS union() threw an error!");
+               }
+               GEOSdeleteGeometry(g1);
+               GEOSdeleteGeometry(geos_result);
+               geos_result = g2;
+       }
+
+       result = GEOS2POSTGIS(geos_result, is3d);
+       GEOSdeleteGeometry(geos_result);
+       if ( result == NULL )
+       {
+               elog(ERROR, "GEOS2POSTGIS returned an error");
+               PG_RETURN_NULL(); //never get here
+       }
+
+       compressType(result);
+
+       PG_RETURN_POINTER(result);
+
+}
+
+
+//select geomunion('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))','POLYGON((5 5, 15 5, 15 7, 5 7, 5 5))');
+PG_FUNCTION_INFO_V1(geomunion);
+Datum geomunion(PG_FUNCTION_ARGS)
+{
+       GEOMETRY *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+       int is3d = geom1->is3d || geom2->is3d;
+
+       Geometry *g1,*g2,*g3;
+       GEOMETRY *result;
+
+       initGEOS(MAXIMUM_ALIGNOF);
+//elog(NOTICE,"in geomunion");
+
+       g1 = POSTGIS2GEOS(geom1);
+       g2 = POSTGIS2GEOS(geom2);
+
+//elog(NOTICE,"g1=%s",GEOSasText(g1));
+//elog(NOTICE,"g2=%s",GEOSasText(g2));
+       g3 = GEOSUnion(g1,g2);
+
+//elog(NOTICE,"g3=%s",GEOSasText(g3));
+
+       PG_FREE_IF_COPY(geom1, 0);
+       PG_FREE_IF_COPY(geom2, 1);
+       GEOSdeleteGeometry(g1);
+       GEOSdeleteGeometry(g2);
+
+       if (g3 == NULL)
+       {
+               elog(ERROR,"GEOS union() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+//elog(NOTICE,"result: %s", GEOSasText(g3) ) ;
+
+       result = GEOS2POSTGIS(g3, is3d);
+
+       GEOSdeleteGeometry(g3);
+
+       if (result == NULL)
+       {
+               elog(ERROR,"GEOS union() threw an error (result postgis geometry formation)!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+       compressType(result);  // convert multi* to single item if appropriate
+       PG_RETURN_POINTER(result);
+}
+
+
+// select symdifference('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))','POLYGON((5 5, 15 5, 15 7, 5 7, 5 5))');
+PG_FUNCTION_INFO_V1(symdifference);
+Datum symdifference(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               GEOMETRY                *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+               Geometry *g1,*g2,*g3;
+               GEOMETRY *result;
+
+               initGEOS(MAXIMUM_ALIGNOF);
+
+               g1 =    POSTGIS2GEOS(geom1 );
+               g2 =    POSTGIS2GEOS(geom2 );
+               g3 =    GEOSSymDifference(g1,g2);
+
+       if (g3 == NULL)
+       {
+               elog(ERROR,"GEOS symdifference() threw an error!");
+               GEOSdeleteGeometry(g1);
+               GEOSdeleteGeometry(g2);
+               PG_RETURN_NULL(); //never get here
+       }
+
+//     elog(NOTICE,"result: %s", GEOSasText(g3) ) ;
+
+       result = GEOS2POSTGIS(g3, geom1->is3d || geom2->is3d);
+       if (result == NULL)
+       {
+               GEOSdeleteGeometry(g1);
+               GEOSdeleteGeometry(g2);
+               GEOSdeleteGeometry(g3);
+               elog(ERROR,"GEOS symdifference() threw an error (result postgis geometry formation)!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+
+               GEOSdeleteGeometry(g1);
+               GEOSdeleteGeometry(g2);
+               GEOSdeleteGeometry(g3);
+
+               compressType(result);  // convert multi* to single item if appropriate
+
+               PG_RETURN_POINTER(result);
+}
+
+
+PG_FUNCTION_INFO_V1(boundary);
+Datum boundary(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+               Geometry *g1,*g3;
+               GEOMETRY *result;
+               initGEOS(MAXIMUM_ALIGNOF);
+
+               g1 =    POSTGIS2GEOS(geom1 );
+               g3 =    GEOSBoundary(g1);
+
+       if (g3 == NULL)
+       {
+               elog(ERROR,"GEOS bounary() threw an error!");
+               GEOSdeleteGeometry(g1);
+               PG_RETURN_NULL(); //never get here
+       }
+
+//     elog(NOTICE,"result: %s", GEOSasText(g3) ) ;
+
+       result = GEOS2POSTGIS(g3, geom1->is3d);
+       if (result == NULL)
+       {
+               GEOSdeleteGeometry(g1);
+
+               GEOSdeleteGeometry(g3);
+               elog(ERROR,"GEOS bounary() threw an error (result postgis geometry formation)!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+
+               GEOSdeleteGeometry(g1);
+               GEOSdeleteGeometry(g3);
+
+               compressType(result);  // convert multi* to single item if appropriate
+
+               PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(convexhull);
+Datum convexhull(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               Geometry *g1,*g3;
+               GEOMETRY *result;
+
+               initGEOS(MAXIMUM_ALIGNOF);
+
+               g1 =    POSTGIS2GEOS(geom1 );
+               g3 =    GEOSConvexHull(g1);
+
+
+       if (g3 == NULL)
+       {
+               elog(ERROR,"GEOS convexhull() threw an error!");
+               GEOSdeleteGeometry(g1);
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+//     elog(NOTICE,"result: %s", GEOSasText(g3) ) ;
+
+       result = GEOS2POSTGIS(g3, geom1->is3d);
+       if (result == NULL)
+       {
+               GEOSdeleteGeometry(g1);
+               GEOSdeleteGeometry(g3);
+               elog(ERROR,"GEOS convexhull() threw an error (result postgis geometry formation)!");
+               PG_RETURN_NULL(); //never get here
+       }
+               GEOSdeleteGeometry(g1);
+               GEOSdeleteGeometry(g3);
+
+
+               compressType(result);  // convert multi* to single item if appropriate
+
+               PG_RETURN_POINTER(result);
+
+}
+
+PG_FUNCTION_INFO_V1(buffer);
+Datum buffer(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               double                  size   = PG_GETARG_FLOAT8(1);
+               Geometry *g1,*g3;
+               GEOMETRY *result;
+
+               initGEOS(MAXIMUM_ALIGNOF);
+
+               g1 =    POSTGIS2GEOS(geom1 );
+               g3 =    GEOSBuffer(g1,size);
+
+
+       if (g3 == NULL)
+       {
+               elog(ERROR,"GEOS buffer() threw an error!");
+               GEOSdeleteGeometry(g1);
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+//     elog(NOTICE,"result: %s", GEOSasText(g3) ) ;
+
+       result = GEOS2POSTGIS(g3, geom1->is3d);
+       if (result == NULL)
+       {
+               GEOSdeleteGeometry(g1);
+               GEOSdeleteGeometry(g3);
+               elog(ERROR,"GEOS buffer() threw an error (result postgis geometry formation)!");
+               PG_RETURN_NULL(); //never get here
+       }
+               GEOSdeleteGeometry(g1);
+               GEOSdeleteGeometry(g3);
+
+
+               compressType(result);  // convert multi* to single item if appropriate
+               PG_RETURN_POINTER(result);
+
+}
+
+
+//select intersection('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))','POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))');
+//select intersection('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))','POLYGON((5 5, 15 5, 15 7, 5 7, 5 5))');
+//select intersection('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))','POLYGON((25 5, 35 5, 35 7, 25 7, 25 5))');
+//select intersection('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))','POINT(5 5)');
+//select intersection('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))','LINESTRING(5 5, 10 10)');
+//select intersection('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))','MULTIPOINT(5 5, 7 7, 9 9, 10 10, 11 11)');
+//select intersection('POLYGON(( 0 0, 10 0, 10 10, 0 10, 0 0))','POLYGON((5 5, 15 5, 15 7, 5 7, 5 5 ),(6 6,6.5 6, 6.5 6.5,6 6.5,6 6))');
+
+
+////select intersection('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))','MULTIPOINT(5 5, 7 7, 9 9, 10 10, 11 11)');
+// select intersection('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))','MULTILINESTRING((5 5, 10 10),(1 1, 2 2) )');
+//select intersection('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))','MULTILINESTRING((5 5, 10 10),(1 1, 2 2) )');
+//select intersection('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))','MULTIPOLYGON(((5 5, 15 5, 15 7, 5 7, 5 5)),((1 1,1 2,2 2,1 2, 1 1)))');
+PG_FUNCTION_INFO_V1(intersection);
+Datum intersection(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               GEOMETRY                *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+               Geometry *g1,*g2,*g3;
+               GEOMETRY *result;
+
+               initGEOS(MAXIMUM_ALIGNOF);
+
+//elog(NOTICE,"intersection() START");
+
+               g1 =    POSTGIS2GEOS(geom1 );
+               g2 =    POSTGIS2GEOS(geom2 );
+
+//elog(NOTICE,"               constructed geometrys - calling geos");
+
+//elog(NOTICE,"g1 = %s",GEOSasText(g1));
+//elog(NOTICE,"g2 = %s",GEOSasText(g2));
+
+
+//if (g1==NULL)
+//     elog(NOTICE,"g1 is null");
+//if (g2==NULL)
+//     elog(NOTICE,"g2 is null");
+//elog(NOTICE,"g2 is valid = %i",GEOSisvalid(g2));
+//elog(NOTICE,"g1 is valid = %i",GEOSisvalid(g1));
+
+
+               g3 =    GEOSIntersection(g1,g2);
+
+//elog(NOTICE,"               intersection finished");
+
+       if (g3 == NULL)
+       {
+               elog(ERROR,"GEOS Intersection() threw an error!");
+               GEOSdeleteGeometry(g1);
+               GEOSdeleteGeometry(g2);
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+
+
+//     elog(NOTICE,"result: %s", GEOSasText(g3) ) ;
+
+       result = GEOS2POSTGIS(g3, geom1->is3d || geom2->is3d);
+       if (result == NULL)
+       {
+               GEOSdeleteGeometry(g1);
+               GEOSdeleteGeometry(g2);
+               GEOSdeleteGeometry(g3);
+               elog(ERROR,"GEOS Intersection() threw an error (result postgis geometry formation)!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+
+               GEOSdeleteGeometry(g1);
+               GEOSdeleteGeometry(g2);
+               GEOSdeleteGeometry(g3);
+
+               compressType(result);  // convert multi* to single item if appropriate
+
+               PG_RETURN_POINTER(result);
+}
+
+//select difference('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))','POLYGON((5 5, 15 5, 15 7, 5 7, 5 5))');
+PG_FUNCTION_INFO_V1(difference);
+Datum difference(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               GEOMETRY                *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+               Geometry *g1,*g2,*g3;
+               GEOMETRY *result;
+
+               initGEOS(MAXIMUM_ALIGNOF);
+
+               g1 =    POSTGIS2GEOS(geom1 );
+               g2 =    POSTGIS2GEOS(geom2 );
+               g3 =    GEOSDifference(g1,g2);
+
+       if (g3 == NULL)
+       {
+               elog(ERROR,"GEOS difference() threw an error!");
+               GEOSdeleteGeometry(g1);
+               GEOSdeleteGeometry(g2);
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+
+
+//     elog(NOTICE,"result: %s", GEOSasText(g3) ) ;
+
+       result = GEOS2POSTGIS(g3, geom1->is3d || geom2->is3d);
+       if (result == NULL)
+       {
+               GEOSdeleteGeometry(g1);
+               GEOSdeleteGeometry(g2);
+               GEOSdeleteGeometry(g3);
+               elog(ERROR,"GEOS difference() threw an error (result postgis geometry formation)!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+
+               GEOSdeleteGeometry(g1);
+               GEOSdeleteGeometry(g2);
+               GEOSdeleteGeometry(g3);
+
+
+               compressType(result);  // convert multi* to single item if appropriate
+
+               PG_RETURN_POINTER(result);
+}
+
+
+//select pointonsurface('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))');
+PG_FUNCTION_INFO_V1(pointonsurface);
+Datum pointonsurface(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+               Geometry *g1,*g3;
+               GEOMETRY *result;
+
+               initGEOS(MAXIMUM_ALIGNOF);
+
+               g1 =    POSTGIS2GEOS(geom1 );
+               g3 =    GEOSpointonSurface(g1);
+
+       if (g3 == NULL)
+       {
+               elog(ERROR,"GEOS pointonsurface() threw an error!");
+               GEOSdeleteGeometry(g1);
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+
+
+//     elog(NOTICE,"result: %s", GEOSasText(g3) ) ;
+
+       result = GEOS2POSTGIS(g3, geom1->is3d);
+       if (result == NULL)
+       {
+               GEOSdeleteGeometry(g1);
+               GEOSdeleteGeometry(g3);
+               elog(ERROR,"GEOS pointonsurface() threw an error (result postgis geometry formation)!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+
+               GEOSdeleteGeometry(g1);
+               GEOSdeleteGeometry(g3);
+
+               compressType(result);  // convert multi* to single item if appropriate
+
+               PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(centroid);
+Datum centroid(PG_FUNCTION_ARGS)
+{
+       GEOMETRY *geom, *result;
+       Geometry *geosgeom, *geosresult;
+
+       geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+       initGEOS(MAXIMUM_ALIGNOF);
+
+       geosgeom = POSTGIS2GEOS(geom);
+
+       geosresult = GEOSGetCentroid(geosgeom);
+       if ( geosresult == NULL )
+       {
+               GEOSdeleteGeometry(geosgeom);
+               elog(ERROR,"GEOS getCentroid() threw an error!");
+               PG_RETURN_NULL(); 
+       }
+
+       result = GEOS2POSTGIS(geosresult, geom->is3d);
+       if (result == NULL)
+       {
+               GEOSdeleteGeometry(geosgeom);
+               GEOSdeleteGeometry(geosresult);
+               elog(ERROR,"Error in GEOS-PGIS conversion");
+               PG_RETURN_NULL(); 
+       }
+       GEOSdeleteGeometry(geosgeom);
+       GEOSdeleteGeometry(geosresult);
+
+       PG_RETURN_POINTER(result);
+}
+
+
+
+//----------------------------------------------
+
+
+
+void errorIfGeometryCollection(GEOMETRY *g1, GEOMETRY *g2)
+{
+       if (  (g1->type == COLLECTIONTYPE) || (g2->type == COLLECTIONTYPE) )
+               elog(ERROR,"Relate Operation called with a GEOMETRYCOLLECTION type.  This is unsupported");
+}
+
+PG_FUNCTION_INFO_V1(isvalid);
+Datum isvalid(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               bool result;
+               Geometry *g1;
+
+               initGEOS(MAXIMUM_ALIGNOF);
+
+               g1 =    POSTGIS2GEOS(geom1 );
+
+               result = GEOSisvalid(g1);
+               GEOSdeleteGeometry(g1);
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS isvalid() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+       PG_RETURN_BOOL(result);
+}
+
+
+// overlaps(GEOMETRY g1,GEOMETRY g2)
+// returns  if GEOS::g1->overlaps(g2) returns true
+// throws an error (elog(ERROR,...)) if GEOS throws an error
+PG_FUNCTION_INFO_V1(overlaps);
+Datum overlaps(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+
+       Geometry *g1,*g2;
+       bool result;
+
+       errorIfGeometryCollection(geom1,geom2);
+       initGEOS(MAXIMUM_ALIGNOF);
+
+       g1 =    POSTGIS2GEOS(geom1 );
+       g2 =    POSTGIS2GEOS(geom2 );
+
+
+
+       result = GEOSrelateOverlaps(g1,g2);
+       GEOSdeleteGeometry(g1);
+       GEOSdeleteGeometry(g2);
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS overlaps() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+       PG_RETURN_BOOL(result);
+}
+
+
+
+PG_FUNCTION_INFO_V1(contains);
+Datum contains(PG_FUNCTION_ARGS)
+{
+       GEOMETRY *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+       Geometry *g1,*g2;
+       bool result;
+       //POINT3D *testpoint;
+       //POLYGON3D *poly;
+
+       errorIfGeometryCollection(geom1,geom2);
+
+       /*
+        * short-circuit 1: if geom2 bounding box is not completely inside
+        * geom1 bounding box we can prematurely return FALSE
+        */
+       if ( geom2->bvol.LLB.x < geom1->bvol.LLB.x ) PG_RETURN_BOOL(FALSE);
+       if ( geom2->bvol.URT.x > geom1->bvol.URT.x ) PG_RETURN_BOOL(FALSE);
+       if ( geom2->bvol.LLB.y < geom1->bvol.LLB.y ) PG_RETURN_BOOL(FALSE);
+       if ( geom2->bvol.URT.y > geom1->bvol.URT.y ) PG_RETURN_BOOL(FALSE);
+
+       /*
+        * short-circuit 2: if geom1 is a polygon and any corner of
+        * geom2 bounding box is not 'within' geom1 we can prematurely
+        * return FALSE
+        */
+       //if ( geom1->type == POLYGONTYPE )
+       //{
+       //      poly = (POLYGON3D *)geom1->objData;
+       //      testpoint.x = geom2->bvol.LLB.x;
+       //      testpoint.y = geom2->bvol.LLB.y;
+       //      if ( !point_within_polygon(&testpoint, poly) )
+       //              PG_RETURN_BOOL(FALSE);
+       //      testpoint.x = geom2->bvol.LLB.x;
+       //      testpoint.y = geom2->bvol.URT.y;
+       //      if ( !point_within_polygon(&testpoint, poly) )
+       //              PG_RETURN_BOOL(FALSE);
+       //      testpoint.x = geom2->bvol.URT.x;
+       //      testpoint.y = geom2->bvol.URT.y;
+       //      if ( !point_within_polygon(&testpoint, poly) )
+       //              PG_RETURN_BOOL(FALSE);
+       //      testpoint.x = geom2->bvol.URT.x;
+       //      testpoint.y = geom2->bvol.LLB.y;
+       //      if ( !point_within_polygon(&testpoint, poly) )
+       //              PG_RETURN_BOOL(FALSE);
+       //}
+
+       initGEOS(MAXIMUM_ALIGNOF);
+
+       g1 =    POSTGIS2GEOS(geom1 );
+       g2 =    POSTGIS2GEOS(geom2 );
+
+
+
+       result = GEOSrelateContains(g1,g2);
+       GEOSdeleteGeometry(g1);
+       GEOSdeleteGeometry(g2);
+
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS contains() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+
+       PG_RETURN_BOOL(result);
+}
+
+
+PG_FUNCTION_INFO_V1(within);
+Datum within(PG_FUNCTION_ARGS)
+{
+       GEOMETRY *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+       Geometry *g1,*g2;
+       bool result;
+       //POINT3D testpoint;
+       //POLYGON3D *poly;
+
+       errorIfGeometryCollection(geom1,geom2);
+
+
+       /*
+        * short-circuit 1: if geom1 bounding box is not completely inside
+        * geom2 bounding box we can prematurely return FALSE
+        */
+       if ( geom1->bvol.LLB.x < geom2->bvol.LLB.x ) PG_RETURN_BOOL(FALSE);
+       if ( geom1->bvol.URT.x > geom2->bvol.URT.x ) PG_RETURN_BOOL(FALSE);
+       if ( geom1->bvol.LLB.y < geom2->bvol.LLB.y ) PG_RETURN_BOOL(FALSE);
+       if ( geom1->bvol.URT.y > geom2->bvol.URT.y ) PG_RETURN_BOOL(FALSE);
+
+#if 1
+       /*
+        * short-circuit 2: if geom2 is a polygon and any corner of
+        * geom1 bounding box is not 'within' geom2 we can prematurely
+        * return FALSE
+        */
+       //if ( geom2->type == POLYGONTYPE )
+       //{
+       //      poly = (POLYGON3D *)geom2->objData;
+       //      testpoint.x = geom1->bvol.LLB.x;
+       //      testpoint.y = geom1->bvol.LLB.y;
+       //      if ( !point_within_polygon(&testpoint, poly) )
+       //              PG_RETURN_BOOL(FALSE);
+       //      testpoint.x = geom1->bvol.LLB.x;
+       //      testpoint.y = geom1->bvol.URT.y;
+       //      if ( !point_within_polygon(&testpoint, poly) )
+       //              PG_RETURN_BOOL(FALSE);
+       //      testpoint.x = geom1->bvol.URT.x;
+       //      testpoint.y = geom1->bvol.URT.y;
+       //      if ( !point_within_polygon(&testpoint, poly) )
+       //              PG_RETURN_BOOL(FALSE);
+       //      testpoint.x = geom1->bvol.URT.x;
+       //      testpoint.y = geom1->bvol.LLB.y;
+       //      if ( !point_within_polygon(&testpoint, poly) )
+       //              PG_RETURN_BOOL(FALSE);
+       //}
+#endif
+
+       initGEOS(MAXIMUM_ALIGNOF);
+       g1 =    POSTGIS2GEOS(geom1 );
+       g2 =    POSTGIS2GEOS(geom2 );
+
+       result = GEOSrelateWithin(g1,g2);
+       GEOSdeleteGeometry(g1);
+       GEOSdeleteGeometry(g2);
+
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS within() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+
+       PG_RETURN_BOOL(result);
+}
+
+
+
+PG_FUNCTION_INFO_V1(crosses);
+Datum crosses(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+
+       Geometry *g1,*g2;
+       bool result;
+
+       errorIfGeometryCollection(geom1,geom2);
+       initGEOS(MAXIMUM_ALIGNOF);
+
+       g1 =    POSTGIS2GEOS(geom1 );
+       g2 =    POSTGIS2GEOS(geom2 );
+
+
+
+       result = GEOSrelateCrosses(g1,g2);
+
+       GEOSdeleteGeometry(g1);
+       GEOSdeleteGeometry(g2);
+
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS crosses() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+
+       PG_RETURN_BOOL(result);
+}
+
+
+
+PG_FUNCTION_INFO_V1(intersects);
+Datum intersects(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       Geometry *g1,*g2;
+       bool result;
+
+
+
+
+       errorIfGeometryCollection(geom1,geom2);
+       initGEOS(MAXIMUM_ALIGNOF);
+
+       g1 =    POSTGIS2GEOS(geom1 );
+       g2 =    POSTGIS2GEOS(geom2 );
+
+
+
+       result = GEOSrelateIntersects(g1,g2);
+       GEOSdeleteGeometry(g1);
+       GEOSdeleteGeometry(g2);
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS intersects() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+
+       PG_RETURN_BOOL(result);
+}
+
+
+PG_FUNCTION_INFO_V1(touches);
+Datum touches(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+
+       Geometry *g1,*g2;
+       bool result;
+
+       errorIfGeometryCollection(geom1,geom2);
+       initGEOS(MAXIMUM_ALIGNOF);
+
+       g1 =    POSTGIS2GEOS(geom1 );
+       g2 =    POSTGIS2GEOS(geom2 );
+
+
+
+       result = GEOSrelateTouches(g1,g2);
+
+       GEOSdeleteGeometry(g1);
+       GEOSdeleteGeometry(g2);
+
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS touches() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+
+       PG_RETURN_BOOL(result);
+}
+
+
+PG_FUNCTION_INFO_V1(disjoint);
+Datum disjoint(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+
+       Geometry *g1,*g2;
+       bool result;
+
+       errorIfGeometryCollection(geom1,geom2);
+       initGEOS(MAXIMUM_ALIGNOF);
+
+       g1 =    POSTGIS2GEOS(geom1 );
+       g2 =    POSTGIS2GEOS(geom2 );
+
+
+       result = GEOSrelateDisjoint(g1,g2);
+       GEOSdeleteGeometry(g1);
+       GEOSdeleteGeometry(g2);
+
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS disjoin() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+       PG_RETURN_BOOL(result);
+}
+
+
+PG_FUNCTION_INFO_V1(relate_pattern);
+Datum relate_pattern(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+       char *patt;
+       bool result;
+
+       Geometry *g1,*g2;
+
+
+       errorIfGeometryCollection(geom1,geom2);
+       initGEOS(MAXIMUM_ALIGNOF);
+
+       g1 =    POSTGIS2GEOS(geom1 );
+       g2 =    POSTGIS2GEOS(geom2 );
+
+
+       patt =  DatumGetCString(DirectFunctionCall1(textout,
+                        PointerGetDatum(PG_GETARG_DATUM(2))));
+
+       result = GEOSrelatePattern(g1,g2,patt);
+       GEOSdeleteGeometry(g1);
+       GEOSdeleteGeometry(g2);
+       pfree(patt);
+
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS relate_pattern() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+       PG_RETURN_BOOL(result);
+
+
+}
+
+
+
+PG_FUNCTION_INFO_V1(relate_full);
+Datum relate_full(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       Geometry *g1,*g2;
+       char    *relate_str;
+       int len;
+       char *result;
+
+//elog(NOTICE,"in relate_full()");
+
+       errorIfGeometryCollection(geom1,geom2);
+
+
+       initGEOS(MAXIMUM_ALIGNOF);
+
+//elog(NOTICE,"GEOS init()");
+
+       g1 =    POSTGIS2GEOS(geom1 );
+       g2 =    POSTGIS2GEOS(geom2 );
+
+//elog(NOTICE,"constructed geometries ");
+
+
+
+
+
+if ((g1==NULL) || (g2 == NULL))
+       elog(NOTICE,"g1 or g2 are null");
+
+//elog(NOTICE,GEOSasText(g1));
+//elog(NOTICE,GEOSasText(g2));
+
+//elog(NOTICE,"valid g1 = %i", GEOSisvalid(g1));
+//elog(NOTICE,"valid g2 = %i",GEOSisvalid(g2));
+
+//elog(NOTICE,"about to relate()");
+
+
+               relate_str = GEOSrelate(g1, g2);
+
+//elog(NOTICE,"finished relate()");
+
+       GEOSdeleteGeometry(g1);
+       GEOSdeleteGeometry(g2);
+
+
+
+       if (relate_str == NULL)
+       {
+               //free(relate_str);
+               elog(ERROR,"GEOS relate() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+
+       len = strlen(relate_str) + 4;
+
+       result= palloc(len);
+       *((int *) result) = len;
+
+       memcpy(result +4, relate_str, len-4);
+
+       free(relate_str);
+
+
+       PG_RETURN_POINTER(result);
+}
+
+//==============================
+
+PG_FUNCTION_INFO_V1(geomequals);
+Datum geomequals(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+
+       Geometry *g1,*g2;
+       bool result;
+
+       errorIfGeometryCollection(geom1,geom2);
+       initGEOS(MAXIMUM_ALIGNOF);
+
+       g1 =    POSTGIS2GEOS(geom1 );
+       g2 =    POSTGIS2GEOS(geom2 );
+
+
+       result = GEOSequals(g1,g2);
+       GEOSdeleteGeometry(g1);
+       GEOSdeleteGeometry(g2);
+
+       if (result == 2)
+       {
+               elog(ERROR,"GEOS equals() threw an error!");
+               PG_RETURN_NULL(); //never get here
+       }
+
+       PG_RETURN_BOOL(result);
+}
+
+PG_FUNCTION_INFO_V1(issimple);
+Datum issimple(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+       Geometry *g1;
+       int result;
+
+       if (geom->nobjs == 0)
+               PG_RETURN_BOOL(true);
+
+               initGEOS(MAXIMUM_ALIGNOF);
+
+       //elog(NOTICE,"GEOS init()");
+
+               g1 =    POSTGIS2GEOS(geom );
+
+               result = GEOSisSimple(g1);
+               GEOSdeleteGeometry(g1);
+
+
+                       if (result == 2)
+                       {
+                               elog(ERROR,"GEOS issimple() threw an error!");
+                               PG_RETURN_NULL(); //never get here
+                       }
+
+       PG_RETURN_BOOL(result);
+}
+
+PG_FUNCTION_INFO_V1(isring);
+Datum isring(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+       Geometry *g1;
+       int result;
+
+       if (geom->type != LINETYPE)
+       {
+               elog(ERROR,"isring() should only be called on a LINE");
+       }
+
+       if (geom->nobjs == 0)
+               PG_RETURN_BOOL(false);
+
+               initGEOS(MAXIMUM_ALIGNOF);
+
+       //elog(NOTICE,"GEOS init()");
+
+               g1 =    POSTGIS2GEOS(geom );
+
+               result = GEOSisRing(g1);
+               GEOSdeleteGeometry(g1);
+
+
+                       if (result == 2)
+                       {
+                               elog(ERROR,"GEOS isring() threw an error!");
+                               PG_RETURN_NULL(); //never get here
+                       }
+
+       PG_RETURN_BOOL(result);
+}
+
+
+
+//===================================================
+
+POLYGON3D *PolyFromGeometry(Geometry *g, int *size)
+{
+
+               int ninteriorrings;
+               POINT3D *pts;
+               int             *pts_per_ring;
+               int t;
+               POLYGON3D *poly;
+               int npoints;
+
+               ninteriorrings = GEOSGetNumInteriorRings(g);
+
+               npoints =       GEOSGetNumCoordinate(g);
+               pts = GEOSGetCoordinates(g);
+               if (npoints <3)
+               {
+                       GEOSdeleteChar( (char*) pts);
+                       return NULL;
+               }
+
+               pts_per_ring = palloc(sizeof(int) *  (ninteriorrings+1));
+               pts_per_ring[0] = GEOSGetNumCoordinate(GEOSGetExteriorRing(g));
+
+               for (t=0;t<ninteriorrings;t++)
+               {
+
+                       pts_per_ring[t+1] = GEOSGetNumCoordinate(GEOSGetInteriorRingN(g,t));
+               }
+
+
+
+               poly = make_polygon( ninteriorrings+1, pts_per_ring,
+                                                       pts, GEOSGetNumCoordinate(g), size);
+
+
+
+               GEOSdeleteChar( (char*) pts);
+               pfree(pts_per_ring);
+               return poly;
+}
+
+
+
+LINE3D *LineFromGeometry(Geometry *g,int *size)
+{
+//     int t;
+               POINT3D *pts = GEOSGetCoordinates(g);
+
+               LINE3D  *line;
+               int npoints = GEOSGetNumCoordinate(g);
+
+
+               if (npoints <2)
+               {
+                       GEOSdeleteChar( (char*) pts);
+                       return NULL;
+               }
+
+               line = make_line(npoints, pts, size);
+//elog(NOTICE,"line::");
+//for (t=0;t<npoints;t++)
+//{
+//     elog(NOTICE,"point (in): %g,%g,%g",  pts[t].x,pts[t].y,pts[t].z);
+//     elog(NOTICE,"point (line): %g,%g,%g",line->points[t].x,line->points[t].y,line->points[t].z);
+//}
+               GEOSdeleteChar( (char*) pts);
+               return line;
+}
+
+
+
+
+GEOMETRY *
+GEOS2POSTGIS(Geometry *g,char want3d)
+{
+       int type = GEOSGeometryTypeId(g) ;
+       GEOMETRY *result = NULL;
+       POINT3D *pts;
+       LINE3D *line;
+       BOX3D *bbox ;
+       GEOMETRY *geom, *g2, *r;
+       POLYGON3D *poly;
+       GEOMETRY *g_new,*g_old;
+       int ngeoms,t,size;
+
+       switch (type)
+       {
+               /* From slower to faster.. compensation rule :) */
+
+               case COLLECTIONTYPE:
+#ifdef DEBUG_GEOS2POSTGIS
+                       elog(NOTICE, "GEOS2POSTGIS: It's a COLLECTION");
+#endif
+                       //this is more difficult because GEOS allows GCs of GCs
+                       ngeoms = GEOSGetNumGeometries(g);
+                       if (ngeoms ==0)
+                       {
+                               result =  makeNullGeometry(GEOSGetSRID(g));
+                               return result;
+                       }
+                       if (ngeoms == 1)
+                       {
+                               return GEOS2POSTGIS(GEOSGetGeometryN(g,0),
+                                       want3d);  // short cut!
+                       }
+                       geom = GEOS2POSTGIS(GEOSGetGeometryN(g,0) , want3d);
+                       for (t=1;t<ngeoms;t++)
+                       {
+                               g2 = GEOS2POSTGIS(GEOSGetGeometryN(g,t),
+                                       want3d);
+                               r = geom;
+#if 1
+                               geom = (GEOMETRY *) DatumGetPointer(
+                                       DirectFunctionCall2(collector,
+                                       PointerGetDatum(r),
+                                       PointerGetDatum(g2)));
+#else
+                               geom = collector_raw(r, g2);
+#endif
+                               pfree(r);
+                               pfree(g2);
+                       }
+                       return geom;
+
+               case MULTIPOLYGONTYPE:
+                       ngeoms =        GEOSGetNumGeometries(g);
+#ifdef DEBUG_GEOS2POSTGIS
+                       elog(NOTICE, "GEOS2POSTGIS: It's a MULTIPOLYGON");
+#endif // DEBUG_GEOS2POSTGIS
+                       if (ngeoms ==0)
+                       {
+                               result =  makeNullGeometry(GEOSGetSRID(g));
+                               result->type = MULTIPOLYGONTYPE;
+                               return result;
+                       }
+                       for (t=0;t<ngeoms;t++)
+                       {
+                               poly = PolyFromGeometry(GEOSGetGeometryN(g,t) ,&size);
+                               if (t==0)
+                               {
+                                       result = make_oneobj_geometry(size,
+                                                               (char *) poly,
+                                                                  MULTIPOLYGONTYPE,  want3d, GEOSGetSRID(g),1.0, 0.0, 0.0
+                                                       );
+                               }
+                               else
+                               {
+                                       g_old = result;
+                                       result =        add_to_geometry(g_old,size, (char*) poly, POLYGONTYPE);
+                                       pfree(g_old);
+                               }
+                               pfree(poly);
+                       }
+                       // make bounding box
+                       bbox = bbox_of_geometry( result );
+                       // copy bounding box
+                       memcpy( &result->bvol, bbox, sizeof(BOX3D) );
+                       // free bounding box
+                       pfree( bbox );
+                       return result;
+
+               case MULTILINETYPE:
+#ifdef DEBUG_GEOS2POSTGIS
+                       elog(NOTICE, "GEOS2POSTGIS: It's a MULTILINE");
+#endif
+                       ngeoms = GEOSGetNumGeometries(g);
+                       if (ngeoms ==0)
+                       {
+                               result =  makeNullGeometry(GEOSGetSRID(g));
+                               result->type = MULTILINETYPE;
+                               return result;
+                       }
+
+                       line = LineFromGeometry(GEOSGetGeometryN(g,0), &size);
+                       result = make_oneobj_geometry(size, (char *)line,
+                               MULTILINETYPE,  want3d,
+                               GEOSGetSRID(g),1.0, 0.0, 0.0);
+#ifdef DEBUG_GEOS2POSTGIS
+       elog(NOTICE,"GEOS2POSTGIS: t0: %s",geometry_to_text(result));
+       elog(NOTICE,"    size = %i", result->size);
+#endif
+
+                       for (t=1;t<ngeoms;t++)
+                       {
+                               line = LineFromGeometry(GEOSGetGeometryN(g,t),
+                                               &size);
+                               g_old = result;
+                               result = add_to_geometry(g_old,size,
+                                               (char*) line, LINETYPE);
+#ifdef DEBUG_GEOS2POSTGIS
+       elog(NOTICE,"GEOS2POSTGIS: t%d: %s", t, geometry_to_text(result));
+       elog(NOTICE,"    size = %i", result->size);
+#endif
+                               pfree(g_old);
+                       }
+                       bbox = bbox_of_geometry( result ); // make bounding box
+                       memcpy( &result->bvol, bbox, sizeof(BOX3D) ); // copy bounding box
+                       pfree( bbox ); // free bounding box
+#ifdef DEBUG_GEOS2POSTGIS
+       elog(NOTICE,"GEOS2POSTGIS: end: %s",geometry_to_text(result));
+       elog(NOTICE,"    size = %i", result->size);
+#endif
+
+                       return result;
+
+               case MULTIPOINTTYPE:
+#ifdef DEBUG_GEOS2POSTGIS
+                       elog(NOTICE, "GEOS2POSTGIS: It's a MULTIPOINT");
+#endif
+                       g_new = NULL;
+                       ngeoms = GEOSGetNumGeometries(g);
+                       if (ngeoms ==0)
+                       {
+                               result =  makeNullGeometry(GEOSGetSRID(g));
+                               result->type = MULTIPOINTTYPE;
+                               return result;
+                       }
+                       pts = GEOSGetCoordinates(g);
+                       g_old = make_oneobj_geometry(sizeof(POINT3D),
+                               (char *) pts, MULTIPOINTTYPE, want3d,
+                               GEOSGetSRID(g),1.0, 0.0, 0.0);
+                       for (t=1;t<ngeoms;t++)
+                       {
+                               g_new = add_to_geometry(g_old, sizeof(POINT3D),
+                                       (char*)&pts[t], POINTTYPE);
+                               pfree(g_old);
+                               g_old =g_new;
+                       }
+                       GEOSdeleteChar( (char*) pts);
+                       // make bounding box
+                       bbox = bbox_of_geometry( g_new );
+                       // copy bounding box
+                       memcpy( &g_new->bvol, bbox, sizeof(BOX3D) );
+                       // free bounding box
+                       pfree( bbox );
+                       return g_new;
+
+               case POLYGONTYPE:
+#ifdef DEBUG_GEOS2POSTGIS
+                       elog(NOTICE, "GEOS2POSTGIS: It's a POLYGON");
+#endif
+                       poly = PolyFromGeometry(g,&size);
+                       if (poly == NULL) return NULL;
+                       result = make_oneobj_geometry(size,
+                               (char *) poly, POLYGONTYPE, want3d,
+                               GEOSGetSRID(g),1.0, 0.0, 0.0);
+                       return result;
+
+               case LINETYPE:
+#ifdef DEBUG_GEOS2POSTGIS
+                       elog(NOTICE, "GEOS2POSTGIS: It's a LINE");
+#endif
+                       line = LineFromGeometry(g,&size);
+                       if (line == NULL) return NULL;
+                       result = make_oneobj_geometry(size,
+                               (char *) line, LINETYPE, want3d,
+                               GEOSGetSRID(g),1.0, 0.0, 0.0);
+                       return result;
+
+               case POINTTYPE: 
+#ifdef DEBUG_GEOS2POSTGIS
+                       elog(NOTICE, "GEOS2POSTGIS: It's a POINT");
+#endif
+                       pts = GEOSGetCoordinate(g);
+                       result = make_oneobj_geometry(sizeof(POINT3D),
+                               (char *) pts, POINTTYPE, want3d,
+                               GEOSGetSRID(g), 1.0, 0.0, 0.0);
+                       GEOSdeleteChar( (char*) pts);
+                       return result;
+
+               default:
+#ifdef DEBUG_GEOS2POSTGIS
+                       elog(NOTICE, "GEOS2POSTGIS: It's UNKNOWN!");
+#endif
+                       return NULL;
+
+       }
+}
+
+
+
+//BBOXONLYTYPE -> returns as a 2d polygon
+Geometry *
+POSTGIS2GEOS(GEOMETRY *g)
+{
+       POINT3D *pt;
+       LINE3D *line;
+       POLYGON3D *poly;
+       POLYGON3D **polys;
+       LINE3D **lines;
+       POINT3D **points;
+       Geometry **geoms;
+       Geometry *geos;
+       char     *obj;
+       int      obj_type;
+       int t;
+       Geometry        *result;
+
+       int32 *offsets1 = (int32 *) ( ((char *) &(g->objType[0] ))+ sizeof(int32) * g->nobjs ) ;
+
+       switch(g->type)
+       {
+               case POINTTYPE:
+#ifdef DEBUG_POSTGIS2GEOS
+                       elog(NOTICE, "POSTGIS2GEOS: it's a POINT");
+#endif
+                       pt = (POINT3D*) ((char *) g +offsets1[0]) ;
+                       result =  PostGIS2GEOS_point(pt,g->SRID,g->is3d);
+                       if (result == NULL)
+                       {
+                               elog(ERROR,"Couldnt convert the postgis geometry to GEOS!");
+                       }
+                       return result;
+                       break;
+
+               case LINETYPE:
+#ifdef DEBUG_POSTGIS2GEOS
+                       elog(NOTICE, "POSTGIS2GEOS: it's a LINE");
+#endif
+                       line = (LINE3D*) ((char *) g +offsets1[0]) ;
+                       result =  PostGIS2GEOS_linestring(line,g->SRID,g->is3d);
+                       if (result == NULL)
+                       {
+                               elog(ERROR,"Couldnt convert the postgis geometry to GEOS!");
+                       }
+                       return result;
+                       break;
+
+               case POLYGONTYPE:
+#ifdef DEBUG_POSTGIS2GEOS
+                       elog(NOTICE, "POSTGIS2GEOS: it's a POLYGON");
+#endif
+                       poly = (POLYGON3D*) ((char *) g +offsets1[0]) ;
+                       result =  PostGIS2GEOS_polygon(poly,g->SRID,g->is3d);
+                       if (result == NULL)
+                       {
+                               elog(ERROR,"Couldnt convert the postgis geometry to GEOS!");
+                       }
+                       return result;
+                       break;
+
+               case MULTIPOLYGONTYPE:
+#ifdef DEBUG_POSTGIS2GEOS
+                       elog(NOTICE, "POSTGIS2GEOS: it's a MULTIPOLYGON");
+#endif
+                       //make an array of POLYGON3Ds
+                       polys = NULL;
+                       if (g->nobjs >0)
+                               polys = (POLYGON3D**) palloc(sizeof (POLYGON3D*) * g->nobjs);
+                       for (t=0;t<g->nobjs;t++)
+                       {
+                               polys[t] =      (POLYGON3D*) ((char *) g +offsets1[t]) ;
+                       }
+                       geos= PostGIS2GEOS_multipolygon(polys, g->nobjs, g->SRID,g->is3d);
+                       if (polys != NULL) pfree(polys);
+                       if (geos == NULL)
+                       {
+                               elog(ERROR,"Couldnt convert the postgis geometry to GEOS!");
+                       }
+                       return geos;
+                       break;
+
+               case MULTILINETYPE:
+#ifdef DEBUG_POSTGIS2GEOS
+                       elog(NOTICE, "POSTGIS2GEOS: it's a MULTILINE");
+#endif
+                       //make an array of LINES3D
+                       lines = NULL;
+                       if (g->nobjs >0)
+                               lines = (LINE3D**) palloc(sizeof (LINE3D*) * g->nobjs);
+                       for (t=0;t<g->nobjs;t++)
+                       {
+#ifdef DEBUG_POSTGIS2GEOS
+               elog(NOTICE, "Line %d has offset %d", t, offsets1[t]);
+#endif
+                               lines[t] = (LINE3D*) ((char *)g+offsets1[t]) ;
+                       }
+                       geos= PostGIS2GEOS_multilinestring(lines, g->nobjs, g->SRID,g->is3d);
+                       if (lines != NULL) pfree(lines);
+                       if (geos == NULL)
+                       {
+                               elog(ERROR,"Couldnt convert the postgis geometry to GEOS!");
+                       }
+                       return geos;
+                       break;
+
+               case MULTIPOINTTYPE:
+#ifdef DEBUG_POSTGIS2GEOS
+                       elog(NOTICE, "POSTGIS2GEOS: it's a MULTIPOINT");
+#endif
+                       //make an array of POINT3Ds
+                       points = NULL;
+                       if (g->nobjs >0)
+                               points = (POINT3D**) palloc(sizeof (POINT3D*) * g->nobjs);
+                       for (t=0;t<g->nobjs;t++)
+                       {
+                               points[t] =     (POINT3D*) ((char *) g +offsets1[t]) ;
+                       }
+                       geos= PostGIS2GEOS_multipoint(points, g->nobjs,g->SRID,g->is3d);
+                       if (points != NULL) pfree(points);
+                       if (geos == NULL)
+                       {
+                               elog(ERROR,"Couldnt convert the postgis geometry to GEOS!");
+                       }
+                       return geos;
+                       break;
+
+               case BBOXONLYTYPE:
+#ifdef DEBUG_POSTGIS2GEOS
+                       elog(NOTICE, "POSTGIS2GEOS: it's a BBOXONLY");
+#endif
+                       result =   PostGIS2GEOS_box3d(&g->bvol, g->SRID);
+                       if (result == NULL)
+                       {
+                               elog(ERROR,"Couldnt convert the postgis geometry to GEOS!");
+                       }
+                       return result;
+                       break;
+
+               case COLLECTIONTYPE:
+#ifdef DEBUG_POSTGIS2GEOS
+                       elog(NOTICE, "POSTGIS2GEOS: it's a COLLECTION");
+#endif
+                       //make an array of GEOS Geometries
+                       geoms = NULL;
+                       if (g->nobjs >0)
+                               geoms = (Geometry**) palloc(sizeof (Geometry*) * g->nobjs);
+                       for (t=0;t<g->nobjs;t++)
+                       {
+                               obj = ((char *) g +offsets1[t]);
+                               obj_type =  g->objType[t];
+                               switch (obj_type)
+                               {
+                                       case POINTTYPE:
+                                               pt = (POINT3D*) obj ;
+                                               geoms[t] = PostGIS2GEOS_point(pt,g->SRID,g->is3d);
+                                               if (geoms[t] == NULL)
+                                               {
+                                                       pfree(geoms);
+                                                       elog(ERROR,"Couldnt convert the postgis geometry to GEOS!");
+                                                       return NULL;
+                                               }
+                                               break;
+                                       case LINETYPE:
+                                               line = (LINE3D*) obj ;
+                                               geoms[t] = PostGIS2GEOS_linestring(line,g->SRID,g->is3d);
+                                               if (geoms[t] == NULL)
+                                               {
+                                                       pfree(geoms);
+                                                       elog(ERROR,"Couldnt convert the postgis geometry to GEOS!");
+                                                       return NULL;
+                                               }
+                                               break;
+                                       case POLYGONTYPE:
+                                               poly = (POLYGON3D*) obj ;
+                                               geoms[t] = PostGIS2GEOS_polygon(poly,g->SRID,g->is3d);
+                                               if (geoms[t] == NULL)
+                                               {
+                                                       pfree(geoms);
+                                                       elog(ERROR,"Couldnt convert the postgis geometry to GEOS!");
+                                                       return NULL;
+                                               }
+                                               break;
+                               }
+                       }
+#ifdef DEBUG_POSTGIS2GEOS
+                       elog(NOTICE, "POSTGIS2GEOS: COLLECTION has %d objs, srid %d and is %s 3d", g->nobjs, g->SRID, g->is3d ? "" : "not");
+#endif
+                       geos = PostGIS2GEOS_collection(geoms,g->nobjs,g->SRID,g->is3d);
+                       if (geoms != NULL) pfree(geoms);
+                       if (geos == NULL)
+                       {
+                               elog(ERROR,"Couldnt convert the postgis geometry to GEOS!");
+                       }
+                       return geos;
+                       break;
+
+       }
+       return NULL;
+}
+
+PG_FUNCTION_INFO_V1(GEOSnoop);
+Datum GEOSnoop(PG_FUNCTION_ARGS)
+{
+       GEOMETRY *geom;
+       Geometry *geosgeom;
+       GEOMETRY *result;
+       
+       initGEOS(MAXIMUM_ALIGNOF);
+
+       geom = (GEOMETRY *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+#ifdef DEBUG_CONVERTER
+       elog(NOTICE, "GEOSnoop: IN: %s", geometry_to_text(geom));
+#endif
+
+       geosgeom = POSTGIS2GEOS(geom);
+       if ( (Pointer *)geom != (Pointer *)PG_GETARG_DATUM(0) ) pfree(geom);
+
+       result = GEOS2POSTGIS(geosgeom, geom->is3d);
+       GEOSdeleteGeometry(geosgeom);
+
+#ifdef DEBUG_CONVERTER
+       elog(NOTICE, "GEOSnoop: OUT: %s", geometry_to_text(result));
+#endif
+
+       PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(postgis_geos_version);
+Datum postgis_geos_version(PG_FUNCTION_ARGS)
+{
+       char *ver = GEOSversion();
+       text *result;
+       result = (text *) palloc(VARHDRSZ  + strlen(ver));
+       VARATT_SIZEP(result) = VARHDRSZ + strlen(ver) ;
+       memcpy(VARDATA(result), ver, strlen(ver));
+       free(ver);
+       PG_RETURN_POINTER(result);
+}
+
+//----------------------------------------------------------------------------
+// NULL implementation here
+// ---------------------------------------------------------------------------
+#else // ndef USE_GEOS
+
+
+#include "postgres.h"
+
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "fmgr.h"
+
+
+Datum relate_full(PG_FUNCTION_ARGS);
+Datum relate_pattern(PG_FUNCTION_ARGS);
+Datum disjoint(PG_FUNCTION_ARGS);
+Datum touches(PG_FUNCTION_ARGS);
+Datum intersects(PG_FUNCTION_ARGS);
+Datum crosses(PG_FUNCTION_ARGS);
+Datum within(PG_FUNCTION_ARGS);
+Datum contains(PG_FUNCTION_ARGS);
+Datum overlaps(PG_FUNCTION_ARGS);
+Datum isvalid(PG_FUNCTION_ARGS);
+
+
+Datum buffer(PG_FUNCTION_ARGS);
+Datum intersection(PG_FUNCTION_ARGS);
+Datum convexhull(PG_FUNCTION_ARGS);
+Datum difference(PG_FUNCTION_ARGS);
+Datum boundary(PG_FUNCTION_ARGS);
+Datum symdifference(PG_FUNCTION_ARGS);
+Datum geomunion(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(intersection);
+Datum intersection(PG_FUNCTION_ARGS)
+{
+       elog(ERROR,"intersection:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL(); // never get here
+}
+PG_FUNCTION_INFO_V1(convexhull);
+Datum convexhull(PG_FUNCTION_ARGS)
+{
+       elog(ERROR,"convexhull:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL(); // never get here
+}
+PG_FUNCTION_INFO_V1(difference);
+Datum difference(PG_FUNCTION_ARGS)
+{
+       elog(ERROR,"difference:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL(); // never get here
+}
+PG_FUNCTION_INFO_V1(boundary);
+Datum boundary(PG_FUNCTION_ARGS)
+{
+       elog(ERROR,"boundary:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL(); // never get here
+}
+PG_FUNCTION_INFO_V1(symdifference);
+Datum symdifference(PG_FUNCTION_ARGS)
+{
+       elog(ERROR,"symdifference:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL(); // never get here
+}
+PG_FUNCTION_INFO_V1(geomunion);
+Datum geomunion(PG_FUNCTION_ARGS)
+{
+       elog(ERROR,"geomunion:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL(); // never get here
+}
+
+PG_FUNCTION_INFO_V1(buffer);
+Datum buffer(PG_FUNCTION_ARGS)
+{
+       elog(ERROR,"buffer:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL(); // never get here
+}
+
+
+
+PG_FUNCTION_INFO_V1(relate_full);
+Datum relate_full(PG_FUNCTION_ARGS)
+{
+       elog(ERROR,"relate_full:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL(); // never get here
+}
+PG_FUNCTION_INFO_V1(relate_pattern);
+Datum relate_pattern(PG_FUNCTION_ARGS)
+{
+       elog(ERROR,"relate_pattern:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL(); // never get here
+}
+PG_FUNCTION_INFO_V1(disjoint);
+Datum disjoint(PG_FUNCTION_ARGS)
+{
+       elog(ERROR,"disjoint:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL(); // never get here
+}
+PG_FUNCTION_INFO_V1(intersects);
+Datum intersects(PG_FUNCTION_ARGS)
+{
+       elog(ERROR,"intersects:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL(); // never get here
+}
+PG_FUNCTION_INFO_V1(touches);
+Datum touches(PG_FUNCTION_ARGS)
+{
+       elog(ERROR,"touches:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL(); // never get here
+}
+PG_FUNCTION_INFO_V1(crosses);
+Datum crosses(PG_FUNCTION_ARGS)
+{
+       elog(ERROR,"crosses:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL(); // never get here
+}
+PG_FUNCTION_INFO_V1(within);
+Datum within(PG_FUNCTION_ARGS)
+{
+       elog(ERROR,"within:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL(); // never get here
+}
+PG_FUNCTION_INFO_V1(contains);
+Datum contains(PG_FUNCTION_ARGS)
+{
+       elog(ERROR,"contains:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL(); // never get here
+}
+PG_FUNCTION_INFO_V1(overlaps);
+Datum overlaps(PG_FUNCTION_ARGS)
+{
+       elog(ERROR,"overlaps:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL(); // never get here
+}
+PG_FUNCTION_INFO_V1(isvalid);
+Datum isvalid(PG_FUNCTION_ARGS)
+{
+       elog(ERROR,"isvalid:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL(); // never get here
+}
+
+PG_FUNCTION_INFO_V1(issimple);
+Datum issimple(PG_FUNCTION_ARGS)
+{
+       elog(ERROR,"issimple:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL(); // never get here
+}
+PG_FUNCTION_INFO_V1(geomequals);
+Datum geomequals(PG_FUNCTION_ARGS)
+{
+       elog(ERROR,"geomequals:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL(); // never get here
+}
+PG_FUNCTION_INFO_V1(isring);
+Datum isring(PG_FUNCTION_ARGS)
+{
+       elog(ERROR,"isring:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL(); // never get here
+}
+
+PG_FUNCTION_INFO_V1(pointonsurface);
+Datum pointonsurface(PG_FUNCTION_ARGS)
+{
+       elog(ERROR,"pointonsurface:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL(); // never get here
+}
+
+PG_FUNCTION_INFO_V1(unite_garray);
+Datum unite_garray(PG_FUNCTION_ARGS)
+{
+       elog(ERROR,"unite_garray:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL(); // never get here
+}
+
+PG_FUNCTION_INFO_V1(GEOSnoop);
+Datum GEOSnoop(PG_FUNCTION_ARGS)
+{
+       elog(ERROR,"GEOSnoop:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL();
+}
+
+PG_FUNCTION_INFO_V1(postgis_geos_version);
+Datum postgis_geos_version(PG_FUNCTION_ARGS)
+{
+       //elog(ERROR,"GEOSversion:: operation not implemented - compile PostGIS with GEOS support");
+       PG_RETURN_NULL();
+}
+
+#endif
+
diff --git a/hwgeom/postgis_geos_wrapper.cpp b/hwgeom/postgis_geos_wrapper.cpp
new file mode 100644 (file)
index 0000000..49e3558
--- /dev/null
@@ -0,0 +1,1551 @@
+//  g++ postgis_GEOSwrapper.cpp -c -I/usr/local/include  -I/usr/local/include/geos -I/usr/local/src/postgresql-7.2.3//src/include
+
+/*
+* $Log$
+* Revision 1.1  2004/09/20 07:50:06  strk
+* prepared to contain old internal representation code
+*
+* Revision 1.27  2004/07/22 16:58:08  strk
+* Updated to reflect geos version string split.
+*
+* Revision 1.26  2004/07/22 16:20:10  strk
+* Added postgis_lib_version() and postgis_geos_version()
+*
+* Revision 1.25  2004/07/17 09:52:48  strk
+* GEOS multi-version support switches implemented with GEOS_LAST_INTERFACE
+*
+* Revision 1.24  2004/07/08 19:33:51  strk
+* Updated to respect CoordinateSequence GEOS interface switch.
+*
+* Revision 1.23  2004/07/02 13:33:23  strk
+* Changed GEOS header inclusion mechanism to be more polite
+*
+* Revision 1.22  2004/07/01 17:02:26  strk
+* Updated to support latest GEOS API.
+*
+* Revision 1.21  2004/06/16 19:59:36  strk
+* Changed GEOS_VERSION to POSTGIS_GEOS_VERSION to avoid future clashes
+*
+* Revision 1.20  2004/06/16 19:37:54  strk
+* Added cleanup needed for GEOS > 1.0
+*
+* Revision 1.19  2004/06/16 18:47:59  strk
+* Added code to detect geos version.
+* Added appropriate includes in geos connectors.
+*
+* Revision 1.18  2004/04/27 07:44:26  strk
+* Removed use of geometryFactory->toGeometry(), indicated by Martin Davis
+* as being intended for internal use only. Created a linear ring instead
+* (the function converts a box3d to a geos geometry).
+*
+* Revision 1.17  2003/12/12 13:34:20  strk
+* added missing 'const' in prototypes
+*
+* Revision 1.16  2003/12/12 12:03:30  strk
+* More debugging output, some code cleanup.
+*
+* Revision 1.15  2003/11/12 16:36:04  strk
+* delete all caught exceptions after use
+*
+* Revision 1.14  2003/10/29 13:58:28  strk
+* Added GEOSGetCentroid() function
+*
+* Revision 1.13  2003/10/24 21:33:21  strk
+* Added GEOSGeometryTypeId(Geometry *) wrapper function.
+* Added GEOSGetCoordinates_Polygon(Polygon *) as an experimental optimized
+* version of GEOSGetCoordinates(Geometry *); More to add ...
+*
+* Revision 1.12  2003/10/24 14:29:53  strk
+* GEOSGetCoordinates() reverted to getCoordinates() call so to be compatible
+* to all type of geometries (not only LineStrings)
+*
+* Revision 1.11  2003/10/24 08:28:50  strk
+* Fixed memory leak in GEOSGetCoordinates(), made sure that GEOS2POSTGIS
+* free type string even in case of collapsed geoms. Made sure that geomunion
+* release memory in case of exception thrown by GEOSUnion. Sooner release
+* of palloced memory in PolyFromGeometry (pts_per_ring).
+*
+* Revision 1.10  2003/10/20 19:50:49  strk
+* Removed some memory leaks in PostGIS2* converters.
+*
+*/
+
+
+#include <stdio.h>
+
+#include <string>
+#include <iostream>
+#include <fstream>
+
+#include "postgis_geos_version.h"
+#include "geos/geom.h"
+#include "geos/util.h"
+
+using namespace geos;
+
+//WARNING THIS *MUST* BE SET CORRECTLY.
+int MAXIMUM_ALIGNOF = -999999;    // to be set during initialization - this will be either 4 (intel) or 8 (sparc)
+
+//for getting things to align properly  double are on 8byte align on solaris machines, and 4bytes on intel
+
+#define TYPEALIGN(ALIGNVAL,LEN) (((long)(LEN) + (ALIGNVAL-1)) & ~(ALIGNVAL-1))
+#define MAXALIGN(LEN)           TYPEALIGN(MAXIMUM_ALIGNOF, (LEN))
+
+typedef  int int32;
+
+typedef struct
+{
+       double          x,y,z;  //for lat/long   x=long, y=lat
+} POINT3D;
+
+typedef struct
+{
+       POINT3D         LLB,URT; /* corner POINT3Ds on long diagonal */
+} BOX3D;
+
+typedef struct
+{
+       int32   npoints; // how many points in the line
+       int32   junk;      // double-word alignment
+       POINT3D         points[1]; // array of actual points
+} LINE3D;
+
+
+typedef struct
+{
+       int32   nrings;  // how many rings in this polygon
+       int32           npoints[1]; //how many points in each ring
+       /* could be 4 byes of filler here to make sure points[] is
+         double-word aligned*/
+       POINT3D         points[1]; // array of actual points
+} POLYGON3D;
+
+
+#define        POINTTYPE       1
+#define        LINETYPE        2
+#define        POLYGONTYPE     3
+#define        MULTIPOINTTYPE  4
+#define        MULTILINETYPE   5
+#define        MULTIPOLYGONTYPE        6
+#define        COLLECTIONTYPE  7
+
+//###########################################################
+
+extern "C" char *GEOSrelate(Geometry *g1, Geometry*g2);
+extern "C" void initGEOS(int maxalign);
+
+
+extern "C" void GEOSdeleteChar(char *a);
+extern "C" void GEOSdeleteGeometry(Geometry *a);
+extern "C" char GEOSrelatePattern(Geometry *g1, Geometry*g2,char *pat);
+extern "C" char GEOSrelateDisjoint(Geometry *g1, Geometry*g2);
+extern "C" char GEOSrelateTouches(Geometry *g1, Geometry*g2);
+extern "C" char GEOSrelateIntersects(Geometry *g1, Geometry*g2);
+extern "C" char GEOSrelateCrosses(Geometry *g1, Geometry*g2);
+extern "C" char GEOSrelateWithin(Geometry *g1, Geometry*g2);
+extern "C" char GEOSrelateContains(Geometry *g1, Geometry*g2);
+extern "C" char GEOSrelateOverlaps(Geometry *g1, Geometry*g2);
+extern "C" char *GEOSversion();
+extern "C" char *GEOSjtsport();
+
+extern "C" Geometry *PostGIS2GEOS_point(POINT3D *point,int SRID, bool is3d);
+extern "C" Geometry *PostGIS2GEOS_linestring(const LINE3D *line,int SRID, bool is3d);
+extern "C" Geometry *PostGIS2GEOS_polygon(POLYGON3D *polygon,int SRID, bool is3d);
+extern "C" Geometry *PostGIS2GEOS_multipolygon(POLYGON3D **polygons,int npolys, int SRID, bool is3d);
+extern "C" Geometry *PostGIS2GEOS_multilinestring(const LINE3D **lines,int nlines, int SRID, bool is3d);
+extern "C" Geometry *PostGIS2GEOS_multipoint(POINT3D **points,int npoints, int SRID, bool is3d);
+
+extern "C" Geometry *PostGIS2GEOS_box3d(BOX3D *box, int SRID);
+extern "C" Geometry *PostGIS2GEOS_collection(Geometry **geoms, int ngeoms,int SRID, bool is3d);
+
+extern "C" char GEOSisvalid(Geometry *g1);
+
+
+extern "C" char *GEOSasText(Geometry *g1);
+extern "C" char GEOSisEmpty(Geometry *g1);
+extern "C" char *GEOSGeometryType(Geometry *g1);
+extern "C" int GEOSGeometryTypeId(Geometry *g1);
+
+
+extern "C" char *throw_exception(Geometry *g);
+
+extern "C" Geometry *GEOSIntersection(Geometry *g1,Geometry *g1);
+extern "C" Geometry *GEOSBuffer(Geometry *g1,double width);
+extern "C" Geometry *GEOSConvexHull(Geometry *g1);
+extern "C" Geometry *GEOSDifference(Geometry *g1,Geometry *g2);
+extern "C" Geometry *GEOSBoundary(Geometry *g1);
+extern "C" Geometry *GEOSSymDifference(Geometry *g1,Geometry *g2);
+extern "C" Geometry *GEOSUnion(Geometry *g1,Geometry *g2);
+
+
+extern "C" POINT3D  *GEOSGetCoordinate(Geometry *g1);
+extern "C" POINT3D  *GEOSGetCoordinates_Polygon(Polygon *g1);
+extern "C" POINT3D  *GEOSGetCoordinates(Geometry *g1);
+extern "C" int      GEOSGetNumCoordinate(Geometry *g1);
+extern "C" const Geometry *GEOSGetGeometryN(Geometry *g1, int n);
+extern "C" const Geometry *GEOSGetExteriorRing(Geometry *g1);
+extern "C" const Geometry *GEOSGetInteriorRingN(Geometry *g1, int n);
+extern "C" int      GEOSGetNumInteriorRings(Geometry *g1);
+extern "C" int      GEOSGetSRID(Geometry *g1);
+extern "C" int      GEOSGetNumGeometries(Geometry *g1);
+
+extern "C" char GEOSisSimple(Geometry *g1);
+extern "C" char GEOSequals(Geometry *g1, Geometry*g2);
+
+extern "C" char GEOSisRing(Geometry *g1);
+
+extern "C" Geometry *GEOSpointonSurface(Geometry *g1);
+
+extern "C" Geometry *GEOSGetCentroid(Geometry *g1);
+
+extern "C" void NOTICE_MESSAGE(char *msg);
+
+
+
+//###########################################################
+#if GEOS_LAST_INTERFACE < 2
+# define CoordinateSequence CoordinateList
+# define DefaultCoordinateSequence BasicCoordinateList
+#endif
+
+GeometryFactory *geomFactory = NULL;
+
+
+void initGEOS (int maxalign)
+{
+       if (geomFactory == NULL)
+       {
+               geomFactory = new GeometryFactory( new PrecisionModel(), -1); // NOTE: SRID will have to be changed after geometry creation
+               MAXIMUM_ALIGNOF = maxalign;
+       }
+}
+
+// ------------------------------------------------------------------------------
+// geometry constuctors - return NULL if there was an error
+//-------------------------------------------------------------------------------
+
+
+
+               //note: you lose the 3d from this!
+Geometry *PostGIS2GEOS_box3d(BOX3D *box, int SRID)
+{
+       DefaultCoordinateSequence  *cl = new DefaultCoordinateSequence(5);
+       try {
+               Geometry *g;
+               Coordinate c;
+               c.x = box->LLB.x; c.y = box->LLB.y;
+               cl->setAt(c, 0);
+               c.x = box->LLB.x; c.y = box->URT.y;
+               cl->setAt(c, 1);
+               c.x = box->URT.x; c.y = box->URT.y;
+               cl->setAt(c, 2);
+               c.x = box->URT.x; c.y = box->LLB.y;
+               cl->setAt(c, 3);
+               c.x = box->LLB.x; c.y = box->LLB.y;
+               cl->setAt(c, 4);
+
+               g = geomFactory->createLinearRing(cl);
+#if GEOS_LAST_INTERFACE < 2
+               delete cl;
+#endif
+               if (g==NULL) return NULL;
+               g->setSRID(SRID);
+               return g;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete cl;
+               return NULL;
+       }
+       catch (...)
+       {
+               delete cl;
+               return NULL;
+       }
+}
+
+Geometry *PostGIS2GEOS_collection(Geometry **geoms, int ngeoms,int SRID, bool is3d)
+{
+       try
+       {
+               Geometry *g;
+               int t;
+               vector<Geometry *> *subGeos=new vector<Geometry *>;
+
+               for (t =0; t< ngeoms; t++)
+               {
+                       subGeos->push_back(geoms[t]);
+               }
+               g = geomFactory->buildGeometry(subGeos);
+#if GEOS_LAST_INTERFACE < 2
+               delete subGeos;
+#endif
+               if (g==NULL)
+                       return NULL;
+               g->setSRID(SRID);
+               return g;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+
+       catch (...)
+       {
+               return NULL;
+       }
+}
+
+Geometry *PostGIS2GEOS_point(POINT3D *point,int SRID, bool is3d)
+{
+       try
+       {
+                       Coordinate *c;
+
+                       if (is3d)
+                               c = new Coordinate(point->x, point->y);
+                       else
+                               c = new Coordinate(point->x, point->y, point->z);
+                       Geometry *g = geomFactory->createPoint(*c);
+                       delete c;
+                       if (g==NULL)
+                               return NULL;
+                       g->setSRID(SRID);
+                       return g;
+               }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+
+       catch (...)
+       {
+               return NULL;
+       }
+}
+
+
+/*
+ * This function must return an all-new allocated object
+ */
+Geometry *
+PostGIS2GEOS_linestring(const LINE3D *line,int SRID, bool is3d)
+{
+       try{
+               int t;
+               Coordinate c;
+
+               //build coordinatelist & pre-allocate space
+               DefaultCoordinateSequence  *coords = new DefaultCoordinateSequence(line->npoints);
+               if (is3d)
+               {
+                       for (t=0;t<line->npoints;t++)
+                       {
+                               c.x = line->points[t].x;
+                               c.y = line->points[t].y;
+                               c.z = line->points[t].z;
+                               coords->setAt( c ,t);
+                       }
+               }
+               else  //make 2d points
+               {
+                       for (t=0;t<line->npoints;t++)
+                       {
+                               c.x = line->points[t].x;
+                               c.y = line->points[t].y;
+                               c.z = DoubleNotANumber;
+                               coords->setAt( c ,t);
+                       }
+               }
+               Geometry *g = geomFactory->createLineString(coords);
+#if GEOS_LAST_INTERFACE < 2
+               delete coords;
+#endif
+               if (g==NULL) return NULL;
+               g->setSRID(SRID);
+               return g;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+
+       catch (...)
+       {
+               return NULL;
+       }
+}
+
+//polygons is an array of pointers to polygons
+Geometry *PostGIS2GEOS_multipolygon(POLYGON3D **polygons,int npolys, int SRID, bool is3d)
+{
+       try
+       {
+                       int t;
+                       vector<Geometry *> *subPolys=NULL;
+                       Geometry *g;
+
+                       subPolys=new vector<Geometry *>;
+
+                       for (t =0; t< npolys; t++)
+                       {
+                               subPolys->push_back(PostGIS2GEOS_polygon(polygons[t], SRID,is3d ));
+                       }
+                       g = geomFactory->createMultiPolygon(subPolys);
+#if GEOS_LAST_INTERFACE < 2
+                       delete subPolys;
+#endif
+
+                       if (g== NULL)
+                               return NULL;
+                       g->setSRID(SRID);
+                       return g;
+               }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+
+       catch (...)
+       {
+               return NULL;
+       }
+}
+
+//lines is an array of pointers to line3d
+Geometry *
+PostGIS2GEOS_multilinestring(const LINE3D **lines, int nlines, int SRID, bool is3d)
+{
+       try
+       {
+               int t;
+               vector<Geometry *> *subLines = new vector<Geometry *>;
+               Geometry *g;
+
+               for (t =0; t< nlines; t++)
+               {
+                       subLines->push_back(PostGIS2GEOS_linestring(lines[t],
+                                               SRID,is3d ));
+               }
+               g = geomFactory->createMultiLineString(subLines);
+#if GEOS_LAST_INTERFACE < 2
+               delete subLines;
+#endif
+               if (g==NULL) return NULL;
+               g->setSRID(SRID);
+               return g;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+       catch (...)
+       {
+               return NULL;
+       }
+}
+
+Geometry *PostGIS2GEOS_multipoint(POINT3D **points,int npoints, int SRID, bool is3d)
+{
+       try
+       {
+                       int t;
+                       vector<Geometry *> *subPoints =new vector<Geometry *>;
+                       Geometry *g;
+
+                       for (t =0; t< npoints; t++)
+                       {
+                               subPoints->push_back(PostGIS2GEOS_point(points[t], SRID,is3d ));
+                       }
+                       g = geomFactory->createMultiPoint(subPoints);
+#if GEOS_LAST_INTERFACE < 2
+                       delete subPoints;
+#endif
+                       if (g==NULL)
+                               return NULL;
+                       g->setSRID(SRID);
+                       return g;
+               }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+
+       catch (...)
+       {
+               return NULL;
+       }
+
+}
+
+
+Geometry *PostGIS2GEOS_polygon(POLYGON3D *polygon,int SRID, bool is3d)
+{
+       try
+       {
+               POINT3D *pts;
+               Coordinate c;
+               int  ring,t;
+               Geometry *g;
+               LinearRing *outerRing;
+               LinearRing *innerRing;
+               DefaultCoordinateSequence *cl;
+               int pointOffset =0; // first point that we're looking at.  a POLYGON3D has all its points smooshed together
+               vector<Geometry *> *innerRings=new vector<Geometry *>;
+
+
+               pts = (POINT3D *) ( (char *)&(polygon->npoints[polygon->nrings] )  );
+               pts = (POINT3D *) MAXALIGN(pts);
+
+                       // make outerRing
+                       cl = new DefaultCoordinateSequence(polygon->npoints[0]);
+                       if (is3d)
+                       {
+                               for(t=0;t<polygon->npoints[0];t++)
+                               {
+                                               c.x = pts[t].x;
+                                               c.y = pts[t].y;
+                                               c.z = pts[t].z;
+                                               cl->setAt( c ,t);
+                               }
+                       }
+                       else
+                       {
+                               for(t=0;t<polygon->npoints[0];t++)
+                               {
+                                               c.x = pts[t].x;
+                                               c.y = pts[t].y;
+                                               c.z = DoubleNotANumber;
+                                               cl->setAt( c ,t);
+                               }
+                       }
+                       outerRing = (LinearRing*) geomFactory->createLinearRing(cl);
+#if GEOS_LAST_INTERFACE < 2
+                       delete cl;
+#endif
+                       if (outerRing == NULL)
+                               return NULL;
+                       outerRing->setSRID(SRID);
+                       pointOffset = polygon->npoints[0];
+
+               for(ring =1; ring< polygon->nrings; ring++)
+               {
+                       cl = new DefaultCoordinateSequence(polygon->npoints[ring]);
+                       if (is3d)
+                       {
+                               for(t=0;t<polygon->npoints[ring];t++)
+                               {
+                                               c.x = pts[t+pointOffset].x;
+                                               c.y = pts[t+pointOffset].y;
+                                               c.z = pts[t+pointOffset].z;
+                                               cl->setAt( c ,t);
+                               }
+                       }
+                       else
+                       {
+                               for(t=0;t<polygon->npoints[ring];t++)
+                               {
+                                               c.x = pts[t+pointOffset].x;
+                                               c.y = pts[t+pointOffset].y;
+                                               c.z = DoubleNotANumber;
+                                               cl->setAt( c ,t);
+                               }
+                       }
+                       innerRing = (LinearRing *) geomFactory->createLinearRing(cl);
+#if GEOS_LAST_INTERFACE < 2
+                       delete cl;
+#endif
+                       if (innerRing == NULL)
+                       {
+                               delete outerRing;
+                               return NULL;
+                       }
+                       innerRing->setSRID(SRID);
+                       innerRings->push_back(innerRing);
+                       pointOffset += polygon->npoints[ring];
+               }
+
+               g = geomFactory->createPolygon(outerRing, innerRings);
+               if (g==NULL)
+                       return NULL;
+               g->setSRID(SRID);
+               return g;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+
+       catch (...)
+       {
+               return NULL;
+       }
+}
+
+//-----------------------------------------------------------
+// relate()-related functions
+//  return 0 = false, 1 = true, 2 = error occured
+//-----------------------------------------------------------
+
+char GEOSrelateDisjoint(Geometry *g1, Geometry*g2)
+{
+       try {
+               bool result;
+               result = g1->disjoint(g2);
+               return result;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return 2;
+       }
+       catch (...)
+       {
+               return 2;
+       }
+}
+
+char GEOSrelateTouches(Geometry *g1, Geometry*g2)
+{
+       try {
+               bool result;
+               result =  g1->touches(g2);
+               return result;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return 2;
+       }
+
+       catch (...)
+       {
+               return 2;
+       }
+}
+
+char GEOSrelateIntersects(Geometry *g1, Geometry*g2)
+{
+       try {
+               bool result;
+               result = g1->intersects(g2);
+               return result;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return 2;
+       }
+
+       catch (...)
+       {
+               return 2;
+       }
+}
+
+char GEOSrelateCrosses(Geometry *g1, Geometry*g2)
+{
+       try {
+               bool result;
+               result = g1->crosses(g2);
+               return result;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return 2;
+       }
+
+       catch (...)
+       {
+               return 2;
+       }
+}
+
+char GEOSrelateWithin(Geometry *g1, Geometry*g2)
+{
+       try {
+               bool result;
+               result = g1->within(g2);
+               return result;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return 2;
+       }
+
+       catch (...)
+       {
+               return 2;
+       }
+}
+
+// call g1->contains(g2)
+// returns 0 = false
+//         1 = true
+//         2 = error was trapped
+char GEOSrelateContains(Geometry *g1, Geometry*g2)
+{
+       try {
+               bool result;
+               result = g1->contains(g2);
+               return result;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return 2;
+       }
+
+       catch (...)
+       {
+               return 2;
+       }
+}
+
+char GEOSrelateOverlaps(Geometry *g1, Geometry*g2)
+{
+       try {
+               bool result;
+               result = g1->overlaps(g2);
+               return result;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return 2;
+       }
+
+       catch (...)
+       {
+               return 2;
+       }
+}
+
+
+//-------------------------------------------------------------------
+// low-level relate functions
+//------------------------------------------------------------------
+
+char GEOSrelatePattern(Geometry *g1, Geometry*g2,char *pat)
+{
+       try {
+               bool result;
+               string s = pat;
+               result = g1->relate(g2,pat);
+               return result;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return 2;
+       }
+
+       catch (...)
+       {
+               return 2;
+       }
+}
+
+char *GEOSrelate(Geometry *g1, Geometry*g2)
+{
+
+       try {
+
+               IntersectionMatrix *im = g1->relate(g2);
+
+               string s;
+               char *result;
+               if (im == NULL)
+                               return NULL;
+
+               s= im->toString();
+               result = (char*) malloc( s.length() + 1);
+               strcpy(result, s.c_str() );
+
+               return result;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+
+       catch (...)
+       {
+               return NULL;
+       }
+}
+
+
+
+//-----------------------------------------------------------------
+// isValid
+//-----------------------------------------------------------------
+
+
+char GEOSisvalid(Geometry *g1)
+{
+       try {
+               bool result;
+               result =g1->isValid();
+               return result;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return 2;
+       }
+
+       catch (...)
+       {
+               return 2;
+       }
+
+}
+
+
+//-----------------------------------------------------------------
+// general purpose
+//-----------------------------------------------------------------
+
+char GEOSequals(Geometry *g1, Geometry*g2)
+{
+       try {
+               bool result;
+               result = g1->equals(g2);
+               return result;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return 2;
+       }
+
+       catch (...)
+       {
+               return 2;
+       }
+}
+
+
+
+char *GEOSasText(Geometry *g1)
+{
+       try
+       {
+               string s = g1->toString();
+
+
+               char *result;
+               result = (char*) malloc( s.length() + 1);
+               strcpy(result, s.c_str() );
+               return result;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+
+       catch (...)
+       {
+               return NULL;
+       }
+}
+
+char GEOSisEmpty(Geometry *g1)
+{
+       try
+       {
+               return g1->isEmpty();
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return 2;
+       }
+
+       catch (...)
+       {
+               return 2;
+       }
+}
+
+char GEOSisSimple(Geometry *g1)
+{
+       try
+       {
+               return g1->isSimple();
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return 2;
+       }
+
+       catch (...)
+       {
+               return 2;
+       }
+}
+
+char GEOSisRing(Geometry *g1)
+{
+       try
+       {
+               return (( (LinearRing*)g1)->isRing());
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return 2;
+       }
+
+       catch (...)
+       {
+               return 2;
+       }
+}
+
+
+
+//free the result of this
+char *GEOSGeometryType(Geometry *g1)
+{
+       try
+       {
+               string s = g1->getGeometryType();
+
+
+               char *result;
+               result = (char*) malloc( s.length() + 1);
+               strcpy(result, s.c_str() );
+               return result;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+
+       catch (...)
+       {
+               return NULL;
+       }
+}
+
+// Return postgis geometry type index
+int GEOSGeometryTypeId(Geometry *g1)
+{
+       try
+       {
+               GeometryTypeId id = g1->getGeometryTypeId();
+               switch (id)
+               {
+                       case GEOS_POINT:
+                               return POINTTYPE;
+                       case GEOS_LINESTRING:
+                               return LINETYPE;
+                       case GEOS_POLYGON:
+                               return POLYGONTYPE;
+                       case GEOS_MULTIPOINT:
+                               return MULTIPOINTTYPE;
+                       case GEOS_MULTILINESTRING:
+                               return MULTILINETYPE;
+                       case GEOS_MULTIPOLYGON:
+                               return MULTIPOLYGONTYPE;
+                       case GEOS_GEOMETRYCOLLECTION:
+                               return COLLECTIONTYPE;
+                       default:
+                               return 0;
+               }
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return -1;
+       }
+
+       catch (...)
+       {
+               return -1;
+       }
+}
+
+
+
+
+//-------------------------------------------------------------------
+// GEOS functions that return geometries
+//-------------------------------------------------------------------
+
+Geometry *GEOSIntersection(Geometry *g1,Geometry *g2)
+{
+       try
+       {
+               Geometry *g3 = g1->intersection(g2);
+               return g3;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+
+       catch (...)
+       {
+               return NULL;
+       }
+}
+
+Geometry *GEOSBuffer(Geometry *g1,double width)
+{
+       try
+       {
+               Geometry *g3 = g1->buffer(width);
+               return g3;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+
+       catch (...)
+       {
+               return NULL;
+       }
+}
+
+Geometry *GEOSConvexHull(Geometry *g1)
+{
+       try
+       {
+               Geometry *g3 = g1->convexHull();
+               return g3;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+
+       catch (...)
+       {
+               return NULL;
+       }
+}
+
+Geometry *GEOSDifference(Geometry *g1,Geometry *g2)
+{
+       try
+       {
+               Geometry *g3 = g1->difference(g2);
+               return g3;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+
+       catch (...)
+       {
+               return NULL;
+       }
+}
+
+Geometry *GEOSBoundary(Geometry *g1)
+{
+       try
+       {
+               Geometry *g3 = g1->getBoundary();
+               return g3;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+
+       catch (...)
+       {
+               return NULL;
+       }
+}
+
+Geometry *GEOSSymDifference(Geometry *g1,Geometry *g2)
+{
+       try
+       {
+               Geometry *g3 = g1->symDifference(g2);
+               return g3;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+
+       catch (...)
+       {
+               return NULL;
+       }
+}
+
+Geometry *GEOSUnion(Geometry *g1,Geometry *g2)
+{
+       try
+       {
+               Geometry *g3 = g1->Union(g2);
+               return g3;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+       catch (...)
+       {
+               return NULL;
+       }
+}
+
+
+Geometry *GEOSpointonSurface(Geometry *g1)
+{
+       try
+       {
+               Geometry *g3 = g1->getInteriorPoint();
+               return g3;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+
+       catch (...)
+       {
+               return NULL;
+       }
+}
+
+
+
+
+
+//-------------------------------------------------------------------
+// memory management functions
+//------------------------------------------------------------------
+
+
+//BUG:: this leaks memory, but delete kills the PrecisionModel for ALL the geometries
+void GEOSdeleteGeometry(Geometry *a)
+{
+       try{
+               delete a;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               //return NULL;
+       }
+
+       catch(...)
+       {
+               // do nothing!
+       }
+}
+
+void GEOSdeleteChar(char *a)
+{
+       try{
+          free(a);
+       }
+       catch (GEOSException *ge) // ???
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               //return NULL;
+       }
+
+       catch(...)
+       {
+               // do nothing!
+       }
+}
+
+
+//-------------------------------------------------------------------
+//GEOS => POSTGIS conversions
+//-------------------------------------------------------------------
+
+
+// free the result when done!
+// g1 must be a point
+POINT3D  *GEOSGetCoordinate(Geometry *g1)
+{
+       try{
+               POINT3D         *result = (POINT3D*) malloc (sizeof(POINT3D));
+               const Coordinate *c =g1->getCoordinate();
+
+               result->x = c->x;
+               result->y = c->y;
+               result->z = c->z;
+               return result;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+
+       catch(...)
+       {
+               return NULL;
+       }
+
+}
+
+
+//must free the result when done
+// result is an array length g1->getNumCoordinates()
+POINT3D  *GEOSGetCoordinates(Geometry *g1)
+{
+       if ( g1->getGeometryTypeId() == GEOS_POLYGON )
+       {
+               return GEOSGetCoordinates_Polygon((Polygon *)g1);
+       }
+
+       try {
+               int numPoints = g1->getNumPoints();
+               POINT3D *result = (POINT3D*) malloc (sizeof(POINT3D) * numPoints );
+               int t;
+               CoordinateSequence *cl = g1->getCoordinates();
+               Coordinate              c;
+
+               for (t=0;t<numPoints;t++)
+               {
+                       c =cl->getAt(t);
+
+                       result[t].x = c.x;
+                       result[t].y = c.y;
+                       result[t].z = c.z;
+               }
+
+               delete cl;
+               return result;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+
+       catch(...)
+       {
+               return NULL;
+       }
+
+}
+
+// A somewhat optimized version for polygon types.
+POINT3D  *GEOSGetCoordinates_Polygon(Polygon *g1)
+{
+       try {
+               int t, r, outidx=0;
+               const CoordinateSequence *cl;
+               Coordinate c;
+               const LineString *lr;
+               int npts, nrings;
+               POINT3D *result;
+               
+               npts = g1->getNumPoints();
+               result = (POINT3D*) malloc (sizeof(POINT3D) * npts);
+               
+               // Exterior ring 
+               lr = g1->getExteriorRing();
+               cl = lr->getCoordinatesRO();
+               npts = lr->getNumPoints();
+               for (t=0; t<npts; t++)
+               {
+                       c = cl->getAt(t);
+
+                       result[outidx].x = c.x;
+                       result[outidx].y = c.y;
+                       result[outidx].z = c.z;
+                       outidx++;
+               }
+
+               // Interior rings
+               nrings = g1->getNumInteriorRing();
+               for (r=0; r<nrings; r++)
+               {
+                       lr = g1->getInteriorRingN(r);
+                       cl = lr->getCoordinatesRO();
+                       npts = lr->getNumPoints();
+                       for (t=0; t<npts; t++)
+                       {
+                               c = cl->getAt(t);
+                               result[outidx].x = c.x;
+                               result[outidx].y = c.y;
+                               result[outidx].z = c.z;
+                               outidx++;
+                       }
+               }
+
+               return result;
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+
+       catch(...)
+       {
+               return NULL;
+       }
+
+}
+
+
+
+
+int      GEOSGetNumCoordinate(Geometry *g1)
+{
+       try{
+               return g1->getNumPoints();
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return 0;
+       }
+
+       catch(...)
+       {
+               return 0;
+       }
+}
+
+int      GEOSGetNumInteriorRings(Geometry *g1)
+{
+       try{
+               Polygon *p = (Polygon *) g1;
+               return p->getNumInteriorRing();
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return 0;
+       }
+
+       catch(...)
+       {
+               return 0;
+       }
+}
+
+
+//only call on GCs (or multi*)
+int      GEOSGetNumGeometries(Geometry *g1)
+{
+       try{
+               GeometryCollection *gc = (GeometryCollection *) g1;
+               return gc->getNumGeometries();
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return 0;
+       }
+
+       catch(...)
+       {
+               return 0;
+       }
+}
+
+
+//call only on GEOMETRYCOLLECTION or MULTI*
+const Geometry *GEOSGetGeometryN(Geometry *g1, int n)
+{
+       try{
+               const GeometryCollection *gc = (GeometryCollection *) g1;
+               return gc->getGeometryN(n);
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+
+       catch(...)
+       {
+               return NULL;
+       }
+}
+
+
+//call only on polygon
+const Geometry *GEOSGetExteriorRing(Geometry *g1)
+{
+       try{
+               Polygon *p = (Polygon *) g1;
+               return p->getExteriorRing();
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return 0;
+       }
+
+       catch(...)
+       {
+               return 0;
+       }
+}
+
+//call only on polygon
+const Geometry *GEOSGetInteriorRingN(Geometry *g1,int n)
+{
+       try{
+               Polygon *p = (Polygon *) g1;
+               return p->getInteriorRingN(n);
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+
+       catch(...)
+       {
+               return NULL;
+       }
+}
+
+Geometry *GEOSGetCentroid(Geometry *g)
+{
+       try{
+               return g->getCentroid();
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return NULL;
+       }
+
+       catch(...)
+       {
+               return NULL;
+       }
+}
+
+
+int      GEOSGetSRID(Geometry *g1)
+{
+       try{
+               return g1->getSRID();
+       }
+       catch (GEOSException *ge)
+       {
+               NOTICE_MESSAGE((char *)ge->toString().c_str());
+               delete ge;
+               return 0;
+       }
+
+       catch(...)
+       {
+               return 0;
+       }
+}
+
+char *
+GEOSversion()
+{
+#if GEOS_LAST_INTERFACE < 2
+       /*
+        * GEOS upgrade needs postgis re-build, so this static
+        * assignment is not going to be a problem
+        */
+       char *res = strdup("1.0.0");
+#else
+       string version = geosversion();
+       char *res = strdup(version.c_str());
+#endif
+       return res;
+}
+
+char *
+GEOSjtsport()
+{
+#if GEOS_LAST_INTERFACE < 2
+       /*
+        * GEOS upgrade needs postgis re-build, so this static
+        * assignment is not going to be a problem
+        */
+       char *res = strdup("1.3");
+#else
+       string version = jtsport();
+       char *res = strdup(version.c_str());
+#endif
+       return res;
+}
+
+
diff --git a/hwgeom/postgis_gist_71.c b/hwgeom/postgis_gist_71.c
new file mode 100644 (file)
index 0000000..b2391b2
--- /dev/null
@@ -0,0 +1,599 @@
+
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2001-2003 Refractions Research Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ *
+ **********************************************************************
+ * $Log$
+ * Revision 1.1  2004/09/20 07:50:06  strk
+ * prepared to contain old internal representation code
+ *
+ * Revision 1.4  2004/04/28 22:26:02  pramsey
+ * Fixed spelling mistake in header text.
+ *
+ * Revision 1.3  2003/08/08 18:19:20  dblasby
+ * Conformance changes.
+ * Removed junk from postgis_debug.c and added the first run of the long
+ * transaction locking support.  (this will change - dont use it)
+ * conformance tests were corrected
+ * some dos cr/lf removed
+ * empty geometries i.e. GEOMETRYCOLLECT(EMPTY) added (with indexing support)
+ * pointN(<linestring>,1) now returns the first point (used to return 2nd)
+ *
+ * Revision 1.2  2003/07/01 18:30:55  pramsey
+ * Added CVS revision headers.
+ *
+ *
+ **********************************************************************/
+
+#include "postgres.h"
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "access/gist.h"
+#include "access/itup.h"
+#include "access/rtree.h"
+
+#include "fmgr.h"
+
+#include "postgis.h"
+#include "utils/elog.h"
+
+//Norman Vine found this problem for compiling under cygwin
+//  it defines BYTE_ORDER and LITTLE_ENDIAN
+
+#ifdef __CYGWIN__
+#include <sys/param.h>       // FOR ENDIAN DEFINES
+#endif
+
+#define SHOW_DIGS_DOUBLE 15
+#define MAX_DIGS_DOUBLE (SHOW_DIGS_DOUBLE + 6 + 1 + 3 +1)
+
+// #define DEBUG_GIST
+
+
+//for GIST index
+typedef char* (*BINARY_UNION)(char*, char*, int*);
+typedef float (*SIZE_BOX)(char*);
+typedef Datum (*RDF)(PG_FUNCTION_ARGS);
+
+GISTENTRY *ggeometry_compress(PG_FUNCTION_ARGS);
+GEOMETRYKEY *ggeometry_union(PG_FUNCTION_ARGS);
+GIST_SPLITVEC * ggeometry_picksplit(PG_FUNCTION_ARGS);
+bool ggeometry_consistent(PG_FUNCTION_ARGS);
+float * ggeometry_penalty(PG_FUNCTION_ARGS);
+bool * ggeometry_same(PG_FUNCTION_ARGS);
+
+char * ggeometry_binary_union(char *r1, char *r2, int *sizep);
+float size_geometrykey( char *pk );
+
+Datum ggeometry_inter(PG_FUNCTION_ARGS);
+
+/*
+** Common rtree-function (for all ops)
+*/
+char * rtree_union(bytea *entryvec, int *sizep, BINARY_UNION bu);
+float * rtree_penalty(GISTENTRY *origentry, GISTENTRY *newentry, float *result, BINARY_UNION bu, SIZE_BOX sb);
+GIST_SPLITVEC * rtree_picksplit(bytea *entryvec, GIST_SPLITVEC *v, int keylen, BINARY_UNION bu, RDF interop, SIZE_BOX sb);
+bool rtree_internal_consistent(BOX *key, BOX *query, StrategyNumber strategy);
+
+
+GISTENTRY *rtree_decompress(PG_FUNCTION_ARGS);
+
+
+//restriction in the GiST && operator
+
+Datum postgis_gist_sel(PG_FUNCTION_ARGS)
+{
+        PG_RETURN_FLOAT8(0.000005);
+}
+
+
+BOX    *convert_box3d_to_box(BOX3D *in)
+{
+               BOX     *out = palloc (sizeof (BOX) );
+
+               out->high.x = in->URT.x;
+               out->high.y = in->URT.y;
+
+               out->low.x = in->LLB.x;
+               out->low.y = in->LLB.y;
+
+       return out;
+}
+
+PG_FUNCTION_INFO_V1(ggeometry_compress);
+GISTENTRY *ggeometry_compress(PG_FUNCTION_ARGS)
+{
+       GISTENTRY *entry=(GISTENTRY*)PG_GETARG_POINTER(0);
+       GISTENTRY *retval;
+
+       if ( entry->leafkey) {
+               retval = palloc(sizeof(GISTENTRY));
+               if ( entry->pred ) {
+
+                       GEOMETRY *in;
+                       GEOMETRYKEY *r;
+                       BOX     *thebox;
+
+#ifdef DEBUG_GIST2
+       printf("GIST: ggeometry_compress called on geometry\n");
+#endif
+
+                       in = (GEOMETRY*)PG_DETOAST_DATUM(PointerGetDatum(entry->pred));
+
+       if (in->nobjs ==0)  // this is the EMPTY geometry
+       {
+                       //elog(NOTICE,"found an empty geometry");
+                       // dont bother adding this to the index
+                       PG_RETURN_POINTER(entry);
+       }
+
+                       r = (GEOMETRYKEY*)palloc( sizeof(GEOMETRYKEY) );
+                       r->size = sizeof(GEOMETRYKEY);
+                       r->SRID = in->SRID;
+                       thebox = convert_box3d_to_box(&in->bvol);
+                       memcpy( (void*)&(r->key), (void*)thebox, sizeof(BOX) );
+                       if ( (char*)in != entry->pred )
+                       {
+                               pfree( in );
+                               pfree(thebox);
+                       }
+
+                       gistentryinit(*retval, (char*)r, entry->rel, entry->page,
+                               entry->offset, sizeof(GEOMETRYKEY),FALSE);
+
+               } else {
+                       gistentryinit(*retval, NULL, entry->rel, entry->page,
+                               entry->offset, 0,FALSE);
+               }
+       } else {
+               retval = entry;
+       }
+       return( retval );
+}
+
+PG_FUNCTION_INFO_V1(ggeometry_consistent);
+bool ggeometry_consistent(PG_FUNCTION_ARGS)
+{
+    GISTENTRY *entry = (GISTENTRY*) PG_GETARG_POINTER(0);
+    GEOMETRY *query       = (GEOMETRY*)            PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+    StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
+    BOX                *thebox;
+    /*
+    ** if entry is not leaf, use gbox_internal_consistent,
+    ** else use gbox_leaf_consistent
+    */
+
+#ifdef DEBUG_GIST2
+       printf("GIST: ggeometry_consistent called\n");
+#endif
+
+    if ( ! (entry->pred && query) )
+       return FALSE;
+
+       thebox = convert_box3d_to_box( &(query->bvol) );
+
+       if(    ((GEOMETRYKEY *)(entry->pred))->SRID != query->SRID)
+       {
+               elog(ERROR,"Operation on two GEOMETRIES with different SRIDs (ggeometry_consistent)\n");
+               PG_RETURN_BOOL(FALSE);
+       }
+
+    PG_RETURN_BOOL(rtree_internal_consistent((BOX*)&( ((GEOMETRYKEY *)(entry->pred))->key ),
+               thebox, strategy));
+}
+
+
+PG_FUNCTION_INFO_V1(ggeometry_union);
+GEOMETRYKEY *ggeometry_union(PG_FUNCTION_ARGS)
+{
+       GEOMETRYKEY             *result;
+
+#ifdef DEBUG_GIST2
+       printf("GIST: ggeometry_union called\n");
+#endif
+
+       result = (GEOMETRYKEY*)
+               rtree_union(
+                       (bytea*) PG_GETARG_POINTER(0),
+                       (int*) PG_GETARG_POINTER(1),
+                       ggeometry_binary_union
+               );
+
+    return result;
+}
+
+
+PG_FUNCTION_INFO_V1(ggeometry_penalty);
+float *ggeometry_penalty(PG_FUNCTION_ARGS)
+{
+#ifdef DEBUG_GIST2
+       printf("GIST: ggeometry_penalty called\n");
+#endif
+
+    return rtree_penalty(
+       (GISTENTRY*) PG_GETARG_POINTER(0),
+       (GISTENTRY*) PG_GETARG_POINTER(1),
+       (float*) PG_GETARG_POINTER(2),
+       ggeometry_binary_union,
+       size_geometrykey
+    );
+}
+
+PG_FUNCTION_INFO_V1(ggeometry_picksplit);
+GIST_SPLITVEC *ggeometry_picksplit(PG_FUNCTION_ARGS)
+{
+#ifdef DEBUG_GIST2
+       printf("GIST: ggeometry_picksplit called\n");
+#endif
+
+
+    return rtree_picksplit(
+       (bytea*)PG_GETARG_POINTER(0),
+       (GIST_SPLITVEC*)PG_GETARG_POINTER(1),
+       sizeof(GEOMETRYKEY),
+       ggeometry_binary_union,
+       ggeometry_inter,
+       size_geometrykey
+    );
+}
+
+PG_FUNCTION_INFO_V1(ggeometry_same);
+bool *ggeometry_same(PG_FUNCTION_ARGS)
+{
+
+  GEOMETRYKEY *b1 = (GEOMETRYKEY*) PG_GETARG_POINTER(0);
+  GEOMETRYKEY *b2 = (GEOMETRYKEY*) PG_GETARG_POINTER(1);
+
+  bool *result = (bool*) PG_GETARG_POINTER(2);
+
+#ifdef DEBUG_GIST2
+       printf("GIST: ggeometry_same called\n");
+#endif
+
+
+  if ( b1 && b2 )
+       *result = DatumGetBool( DirectFunctionCall2( box_same,
+               PointerGetDatum(&(b1->key)),
+               PointerGetDatum(&(b2->key))) );
+  else
+       *result = ( b1==NULL && b2==NULL ) ? TRUE : FALSE;
+  return(result);
+}
+
+PG_FUNCTION_INFO_V1(ggeometry_inter);
+Datum ggeometry_inter(PG_FUNCTION_ARGS) {
+       GEOMETRYKEY *b1 = (GEOMETRYKEY*) PG_GETARG_POINTER(0);
+       GEOMETRYKEY*b2 = (GEOMETRYKEY*) PG_GETARG_POINTER(1);
+       char *interd;
+
+#ifdef DEBUG_GIST2
+       printf("GIST: ggeometry_inter called\n");
+#endif
+
+
+       interd = DatumGetPointer(DirectFunctionCall2(
+                       rt_box_inter,
+                       PointerGetDatum( &(b1->key) ),
+                       PointerGetDatum( &(b2->key) )) );
+
+       if (interd) {
+               GEOMETRYKEY *tmp = (GEOMETRYKEY*)palloc( sizeof(GEOMETRYKEY) );
+               tmp->size = sizeof(GEOMETRYKEY);
+
+               memcpy( (void*)&(tmp->key), (void*)interd, sizeof(BOX) );
+               tmp->SRID = b1->SRID;
+               pfree( interd );
+               PG_RETURN_POINTER( tmp );
+       } else
+               PG_RETURN_POINTER( NULL );
+}
+
+char *ggeometry_binary_union(char *r1, char *r2, int *sizep)
+{
+    GEOMETRYKEY *retval;
+
+#ifdef DEBUG_GIST2
+       printf("GIST: ggeometry_binary_union called\n");
+#endif
+
+    if ( ! (r1 && r2) ) {
+       if ( r1 ) {
+               retval = (GEOMETRYKEY*)palloc( sizeof(GEOMETRYKEY) );
+               memcpy( (void*)retval, (void*)r1, sizeof(GEOMETRYKEY) );
+               *sizep = sizeof(GEOMETRYKEY);
+       } else if ( r2 ) {
+               retval = (GEOMETRYKEY*)palloc( sizeof(GEOMETRYKEY) );
+               memcpy( (void*)retval, (void*)r2, sizeof(GEOMETRYKEY) );
+               *sizep = sizeof(GEOMETRYKEY);
+       } else {
+               *sizep = 0;
+               retval = NULL;
+       }
+    } else {
+       BOX *key = (BOX*)DatumGetPointer( DirectFunctionCall2(
+               rt_box_union,
+               PointerGetDatum( &(((GEOMETRYKEY*)r1)->key) ),
+               PointerGetDatum( &(((GEOMETRYKEY*)r2)->key) )) );
+       retval = (GEOMETRYKEY*)palloc( sizeof(GEOMETRYKEY) );
+       retval->SRID = ((GEOMETRYKEY *) r1)->SRID;
+       memcpy( (void*)&(retval->key), (void*)key, sizeof(BOX) );
+       pfree( key );
+       *sizep = retval->size = sizeof(GEOMETRYKEY);
+    }
+    return (char*)retval;
+}
+
+
+float size_geometrykey( char *pk ) {
+
+#ifdef DEBUG_GIST2
+       printf("GIST: size_geometrykey called\n");
+#endif
+
+    if ( pk ) {
+       float size;
+       DirectFunctionCall2( rt_box_size,
+               PointerGetDatum( &(((GEOMETRYKEY*)pk)->key) ),
+               PointerGetDatum( &size ) );
+       return size;
+    } else
+       return 0.0;
+}
+
+char *rtree_union(bytea *entryvec, int *sizep, BINARY_UNION bu)
+{
+    int numranges, i;
+    char *out, *tmp;
+
+#ifdef DEBUG_GIST2
+       printf("GIST: rtree_union called\n");
+#endif
+
+    numranges = (VARSIZE(entryvec) - VARHDRSZ)/sizeof(GISTENTRY);
+    tmp = (char *)(((GISTENTRY *)(VARDATA(entryvec)))[0]).pred;
+    out = NULL;
+
+    for (i = 1; i < numranges; i++) {
+       out = (*bu)(tmp, (char *)
+                                (((GISTENTRY *)(VARDATA(entryvec)))[i]).pred,
+                                sizep);
+       if (i > 1 && tmp) pfree(tmp);
+       tmp = out;
+    }
+
+    return(out);
+}
+
+float *rtree_penalty(GISTENTRY *origentry, GISTENTRY *newentry, float *result, BINARY_UNION bu, SIZE_BOX sb)
+{
+    char * ud;
+    float tmp1;
+    int sizep;
+
+#ifdef DEBUG_GIST2
+       printf("GIST: rtree_penalty called\n");
+#endif
+
+
+
+    ud = (*bu)( origentry->pred, newentry->pred, &sizep );
+    tmp1 = (*sb)( ud );
+    if (ud) pfree(ud);
+
+    *result = tmp1 - (*sb)( origentry->pred );
+    return(result);
+}
+
+/*
+** The GiST PickSplit method
+** We use Guttman's poly time split algorithm
+*/
+GIST_SPLITVEC *rtree_picksplit(bytea *entryvec, GIST_SPLITVEC *v, int keylen, BINARY_UNION bu, RDF interop, SIZE_BOX sb)
+{
+    OffsetNumber i, j;
+    char *datum_alpha, *datum_beta;
+    char *datum_l, *datum_r;
+    char *union_d, *union_dl, *union_dr;
+    char *inter_d;
+    bool firsttime;
+    float size_alpha, size_beta, size_union, size_inter;
+    float size_waste, waste;
+    float size_l, size_r;
+    int nbytes;
+    int sizep;
+    OffsetNumber seed_1 = 0, seed_2 = 0;
+    OffsetNumber *left, *right;
+    OffsetNumber maxoff;
+
+#ifdef DEBUG_GIST2
+       printf("GIST: rtree_picsplit called\n");
+#endif
+
+    maxoff = ((VARSIZE(entryvec) - VARHDRSZ)/sizeof(GISTENTRY)) - 2;
+    nbytes =  (maxoff + 2) * sizeof(OffsetNumber);
+    v->spl_left = (OffsetNumber *) palloc(nbytes);
+    v->spl_right = (OffsetNumber *) palloc(nbytes);
+
+    firsttime = true;
+    waste = 0.0;
+
+    for (i = FirstOffsetNumber; i < maxoff; i = OffsetNumberNext(i)) {
+       datum_alpha = (char *)(((GISTENTRY *)(VARDATA(entryvec)))[i].pred);
+       for (j = OffsetNumberNext(i); j <= maxoff; j = OffsetNumberNext(j)) {
+           datum_beta = (char *)(((GISTENTRY *)(VARDATA(entryvec)))[j].pred);
+
+           /* compute the wasted space by unioning these guys */
+           /* size_waste = size_union - size_inter; */
+           union_d = (*bu)( datum_alpha, datum_beta, &sizep );
+           if ( union_d ) {
+               size_union = (*sb)(union_d);
+               pfree(union_d);
+           } else
+               size_union = 0.0;
+
+           if ( datum_alpha && datum_beta ) {
+               inter_d = DatumGetPointer(DirectFunctionCall2(
+                       interop,
+                       PointerGetDatum( datum_alpha ),
+                       PointerGetDatum( datum_beta )) );
+               if ( inter_d ) {
+                       size_inter = (*sb)(inter_d);
+                       pfree(inter_d);
+               } else
+                       size_inter = 0.0;
+           } else
+               size_inter = 0.0;
+
+           size_waste = size_union - size_inter;
+
+           /*
+            *  are these a more promising split that what we've
+            *  already seen?
+            */
+
+           if (size_waste > waste || firsttime) {
+               waste = size_waste;
+               seed_1 = i;
+               seed_2 = j;
+               firsttime = false;
+           }
+       }
+    }
+
+    left = v->spl_left;
+    v->spl_nleft = 0;
+    right = v->spl_right;
+    v->spl_nright = 0;
+
+    if ( ((GISTENTRY *)(VARDATA(entryvec)))[seed_1].pred ) {
+       datum_l = (char*) palloc( keylen );
+       memcpy( (void*)datum_l, (void*)(((GISTENTRY *)(VARDATA(entryvec)))[seed_1].pred ), keylen );
+    } else
+       datum_l = NULL;
+    size_l  = (*sb)( datum_l );
+    if ( ((GISTENTRY *)(VARDATA(entryvec)))[seed_2].pred ) {
+       datum_r = (char*) palloc( keylen );
+       memcpy( (void*)datum_r, (void*)(((GISTENTRY *)(VARDATA(entryvec)))[seed_2].pred ), keylen );
+    } else
+       datum_r = NULL;
+    size_r  = (*sb)( datum_r );
+
+    /*
+     *  Now split up the regions between the two seeds.  An important
+     *  property of this split algorithm is that the split vector v
+     *  has the indices of items to be split in order in its left and
+     *  right vectors.  We exploit this property by doing a merge in
+     *  the code that actually splits the page.
+     *
+     *  For efficiency, we also place the new index tuple in this loop.
+     *  This is handled at the very end, when we have placed all the
+     *  existing tuples and i == maxoff + 1.
+     */
+
+    maxoff = OffsetNumberNext(maxoff);
+    for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
+
+       /*
+        *  If we've already decided where to place this item, just
+        *  put it on the right list.  Otherwise, we need to figure
+        *  out which page needs the least enlargement in order to
+        *  store the item.
+        */
+
+       if (i == seed_1) {
+           *left++ = i;
+           v->spl_nleft++;
+           continue;
+       } else if (i == seed_2) {
+           *right++ = i;
+           v->spl_nright++;
+           continue;
+       }
+
+       /* okay, which page needs least enlargement? */
+       datum_alpha = (char *)(((GISTENTRY *)(VARDATA(entryvec)))[i].pred);
+       union_dl = (*bu)( datum_l, datum_alpha, &sizep );
+       union_dr = (*bu)( datum_r, datum_alpha, &sizep );
+       size_alpha = (*sb)( union_dl );
+       size_beta  = (*sb)( union_dr );
+
+       /* pick which page to add it to */
+       if (size_alpha - size_l < size_beta - size_r) {
+           pfree(datum_l);
+           pfree(union_dr);
+           datum_l = union_dl;
+           size_l = size_alpha;
+           *left++ = i;
+           v->spl_nleft++;
+       } else {
+           pfree(datum_r);
+           pfree(union_dl);
+           datum_r = union_dr;
+           size_r = size_alpha;
+           *right++ = i;
+           v->spl_nright++;
+       }
+    }
+    *left = *right = FirstOffsetNumber;        /* sentinel value, see dosplit() */
+
+    v->spl_ldatum = datum_l;
+    v->spl_rdatum = datum_r;
+
+    return( v );
+}
+
+
+bool rtree_internal_consistent(BOX *key,
+                       BOX *query,
+                       StrategyNumber strategy)
+{
+    bool retval;
+
+#ifdef DEBUG_GIST2
+       printf("GIST: rtree_internal_consist called\n");
+#endif
+
+    switch(strategy) {
+    case RTLeftStrategyNumber:
+    case RTOverLeftStrategyNumber:
+      retval = DatumGetBool( DirectFunctionCall2( box_overleft, PointerGetDatum(key), PointerGetDatum(query) ) );
+      break;
+    case RTOverlapStrategyNumber:
+      retval = DatumGetBool( DirectFunctionCall2( box_overlap, PointerGetDatum(key), PointerGetDatum(query) ) );
+      break;
+    case RTOverRightStrategyNumber:
+    case RTRightStrategyNumber:
+      retval = DatumGetBool( DirectFunctionCall2( box_right, PointerGetDatum(key), PointerGetDatum(query) ) );
+      break;
+    case RTSameStrategyNumber:
+    case RTContainsStrategyNumber:
+      retval = DatumGetBool( DirectFunctionCall2( box_contain, PointerGetDatum(key), PointerGetDatum(query) ) );
+      break;
+    case RTContainedByStrategyNumber:
+      retval = DatumGetBool( DirectFunctionCall2( box_overlap, PointerGetDatum(key), PointerGetDatum(query) ) );
+      break;
+    default:
+      retval = FALSE;
+    }
+    return(retval);
+}
+
+PG_FUNCTION_INFO_V1(rtree_decompress);
+GISTENTRY *rtree_decompress(PG_FUNCTION_ARGS)
+{
+    return((GISTENTRY*)PG_GETARG_POINTER(0));
+}
+
+
diff --git a/hwgeom/postgis_gist_72.c b/hwgeom/postgis_gist_72.c
new file mode 100644 (file)
index 0000000..1d68bd1
--- /dev/null
@@ -0,0 +1,738 @@
+
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2001-2003 Refractions Research Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ *
+ **********************************************************************
+ * $Log$
+ * Revision 1.1  2004/09/20 07:50:06  strk
+ * prepared to contain old internal representation code
+ *
+ * Revision 1.19  2004/08/19 06:15:58  strk
+ * USE_VERSION gets 80 where it got 75
+ *
+ * Revision 1.18  2004/06/09 10:19:06  strk
+ * Moved changes needed for PG75 inside postgis_gist_72.c using #if switches.
+ *
+ * Revision 1.17  2004/06/03 08:19:20  strk
+ * yet another Infinite check used: finite() - which checks for NaN,-Inf,+Inf
+ *
+ * Revision 1.16  2004/06/03 08:13:11  strk
+ * Simplified INFINITY checks by use of isinf()
+ *
+ * Revision 1.15  2004/06/03 07:58:11  strk
+ * Infinite coordinate geoms omitted from index
+ *
+ * Revision 1.14  2004/06/02 23:54:09  strk
+ * Made equality checks the default in picksplit to catch also NaN results (INF geoms)
+ *
+ * Revision 1.13  2004/06/02 23:29:08  strk
+ * reverted Inf handling modification (conceptually bogus)
+ *
+ * Revision 1.12  2004/06/02 22:43:54  strk
+ * handled special case of Inf boxes as GiST keys in picksplit
+ *
+ * Revision 1.11  2004/04/28 22:26:02  pramsey
+ * Fixed spelling mistake in header text.
+ *
+ * Revision 1.10  2004/04/08 17:05:20  dblasby
+ * Somehow the memory leak changes I made got removed - I've re-added them.
+ *
+ * Revision 1.9  2004/04/08 17:00:27  dblasby
+ * Changed ggeometry_consistent to be aware of NULL queries.  Ie.
+ * select * from <table> where the_geom &&  null::geometry;
+ *
+ * This tends to happen when you're joining two tables using && and the table
+ * has NULLs in it.
+ *
+ * Revision 1.8  2004/03/05 18:16:47  strk
+ * Applied Mark Cave-Ayland patch
+ *
+ * Revision 1.7  2004/02/25 13:17:31  strk
+ * RTContainedBy and RTOverlap strategries implemented locally with a pgbox_overlap function
+ *
+ * Revision 1.6  2003/08/08 18:19:20  dblasby
+ * Conformance changes.
+ * Removed junk from postgis_debug.c and added the first run of the long
+ * transaction locking support.  (this will change - dont use it)
+ * conformance tests were corrected
+ * some dos cr/lf removed
+ * empty geometries i.e. GEOMETRYCOLLECT(EMPTY) added (with indexing support)
+ * pointN(<linestring>,1) now returns the first point (used to return 2nd)
+ *
+ * Revision 1.5  2003/07/01 18:30:55  pramsey
+ * Added CVS revision headers.
+ *
+ *
+ **********************************************************************
+ *
+ * GiST indexing functions for pgsql >= 7.2
+ *
+ **********************************************************************/
+
+#include "postgres.h"
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "access/gist.h"
+#include "access/itup.h"
+#include "access/rtree.h"
+
+#include "fmgr.h"
+
+#include "postgis.h"
+#include "utils/elog.h"
+
+#define SHOW_DIGS_DOUBLE 15
+#define MAX_DIGS_DOUBLE (SHOW_DIGS_DOUBLE + 6 + 1 + 3 +1)
+
+#define DEBUG_GIST
+
+//for GIST index
+typedef char* (*BINARY_UNION)(char*, char*, int*);
+typedef float (*SIZE_BOX)(char*);
+typedef Datum (*RDF)(PG_FUNCTION_ARGS);
+
+
+BOX *convert_box3d_to_box(BOX3D *in);
+Datum ggeometry_compress(PG_FUNCTION_ARGS);
+Datum ggeometry_consistent(PG_FUNCTION_ARGS);
+static bool rtree_internal_consistent(BOX *key, BOX *query,StrategyNumber strategy);
+static bool rtree_leaf_consistent(BOX *key, BOX *query,StrategyNumber strategy);
+Datum rtree_decompress(PG_FUNCTION_ARGS);
+Datum gbox_union(PG_FUNCTION_ARGS);
+Datum gbox_picksplit(PG_FUNCTION_ARGS);
+Datum gbox_penalty(PG_FUNCTION_ARGS);
+Datum gbox_same(PG_FUNCTION_ARGS);
+static float size_box(Datum box);
+extern void convert_box3d_to_box_p(BOX3D *in,BOX *out);
+
+
+int debug = 0;
+
+//puts result in pre-allocated "out"
+void convert_box3d_to_box_p(BOX3D *in,BOX *out)
+{
+
+               out->high.x = in->URT.x;
+               out->high.y = in->URT.y;
+
+               out->low.x = in->LLB.x;
+               out->low.y = in->LLB.y;
+}
+
+
+BOX    *convert_box3d_to_box(BOX3D *in)
+{
+               BOX     *out = palloc (sizeof (BOX) );
+
+               out->high.x = in->URT.x;
+               out->high.y = in->URT.y;
+
+               out->low.x = in->LLB.x;
+               out->low.y = in->LLB.y;
+
+       return out;
+}
+
+PG_FUNCTION_INFO_V1(ggeometry_compress);
+Datum ggeometry_compress(PG_FUNCTION_ARGS)
+{
+       GISTENTRY *entry=(GISTENTRY*)PG_GETARG_POINTER(0);
+       GISTENTRY *retval;
+
+#ifdef DEBUG_GIST
+       elog(NOTICE,"GIST: ggeometry_compress called on geometry\n");
+       fflush( stdout );
+#endif
+
+       if ( entry->leafkey)
+       {
+               retval = palloc(sizeof(GISTENTRY));
+               if ( DatumGetPointer(entry->key) != NULL )
+               {
+
+                       GEOMETRY *in;
+                       BOX     *r;
+
+
+                       in = (GEOMETRY*)PG_DETOAST_DATUM( entry->key );
+
+                       if (in->nobjs ==0)  // this is the EMPTY geometry
+                       {
+                               //elog(NOTICE,"found an empty geometry");
+                               // dont bother adding this to the index
+                               PG_RETURN_POINTER(entry);
+                       }
+
+                       if ( ! finite(in->bvol.URT.x) ||
+                               ! finite(in->bvol.URT.y) ||
+                               ! finite(in->bvol.LLB.x) ||
+                               ! finite(in->bvol.LLB.y) )
+                       {
+                               //elog(NOTICE, "found infinite geometry");
+                               PG_RETURN_POINTER(entry);
+                       }
+
+                       r = convert_box3d_to_box(&in->bvol);
+                       if ( in != (GEOMETRY*)DatumGetPointer(entry->key) )
+                       {
+                               pfree( in );
+                       }
+
+                       gistentryinit(*retval, PointerGetDatum(r),
+                               entry->rel, entry->page,
+                               entry->offset, sizeof(BOX),
+                               FALSE);
+
+               }
+               else
+               {
+                       gistentryinit(*retval, (Datum) 0, entry->rel,
+                               entry->page, entry->offset, 0,FALSE);
+               }
+       }
+       else
+       {
+               retval = entry;
+       }
+       PG_RETURN_POINTER(retval);
+}
+
+PG_FUNCTION_INFO_V1(ggeometry_consistent);
+Datum ggeometry_consistent(PG_FUNCTION_ARGS)
+{
+    GISTENTRY *entry = (GISTENTRY*) PG_GETARG_POINTER(0);
+    GEOMETRY *query ;
+    StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
+    BOX                thebox;
+       bool result;
+
+elog(NOTICE, "**** ggeometry_consistent call");
+
+   /*
+    ** if entry is not leaf, use rtree_internal_consistent,
+    ** else use rtree_leaf_consistent
+    */
+
+   if (   ( (Pointer *) PG_GETARG_DATUM(1) ) == NULL)
+    {
+               //elog(NOTICE,"ggeometry_consistent:: got null query!");
+               PG_RETURN_BOOL(false); // null query - this is screwy!
+       }
+
+       query = (GEOMETRY*) PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+
+
+#ifdef DEBUG_GIST
+       elog(NOTICE,"GIST: ggeometry_consistent called\n");
+       fflush( stdout );
+#endif
+
+       if ( ! (DatumGetPointer(entry->key) != NULL && query) )
+               PG_RETURN_BOOL(FALSE);
+
+       convert_box3d_to_box_p( &(query->bvol) , &thebox);
+
+       if (GIST_LEAF(entry))
+               result = rtree_leaf_consistent((BOX *) DatumGetPointer(entry->key), &thebox, strategy );
+       else
+               result = rtree_internal_consistent((BOX *) DatumGetPointer(entry->key), &thebox, strategy );
+
+       PG_FREE_IF_COPY(query, 1);
+       PG_RETURN_BOOL(result);
+}
+
+
+static bool
+rtree_internal_consistent(BOX *key,
+                       BOX *query,
+                       StrategyNumber strategy)
+{
+    bool retval;
+
+#ifdef DEBUG_GIST
+       elog(NOTICE,"GIST: rtree_internal_consist called\n");
+       fflush( stdout );
+#endif
+
+    switch(strategy) {
+    case RTLeftStrategyNumber:
+    case RTOverLeftStrategyNumber:
+      retval = DatumGetBool( DirectFunctionCall2( box_overleft, PointerGetDatum(key), PointerGetDatum(query) ) );
+      break;
+    case RTOverlapStrategyNumber:
+      retval = DatumGetBool( DirectFunctionCall2( box_overlap, PointerGetDatum(key), PointerGetDatum(query) ) );
+      break;
+    case RTOverRightStrategyNumber:
+    case RTRightStrategyNumber:
+      retval = DatumGetBool( DirectFunctionCall2( box_overright, PointerGetDatum(key), PointerGetDatum(query) ) );
+      break;
+    case RTSameStrategyNumber:
+    case RTContainsStrategyNumber:
+      retval = DatumGetBool( DirectFunctionCall2( box_contain, PointerGetDatum(key), PointerGetDatum(query) ) );
+      break;
+    case RTContainedByStrategyNumber:
+      retval = DatumGetBool( DirectFunctionCall2( box_overlap, PointerGetDatum(key), PointerGetDatum(query) ) );
+      break;
+    default:
+      retval = FALSE;
+    }
+    return(retval);
+}
+
+
+static bool
+rtree_leaf_consistent(BOX *key,
+                       BOX *query,
+                       StrategyNumber strategy)
+{
+    bool retval;
+
+#ifdef DEBUG_GIST
+       elog(NOTICE,"GIST: rtree_leaf_consist called\n");
+       fflush( stdout );
+#endif
+
+       switch (strategy)
+       {
+               case RTLeftStrategyNumber:
+                       retval = DatumGetBool(DirectFunctionCall2(box_left, PointerGetDatum(key), PointerGetDatum(query)));
+                       break;
+               case RTOverLeftStrategyNumber:
+                       retval = DatumGetBool(DirectFunctionCall2(box_overleft, PointerGetDatum(key), PointerGetDatum(query)));
+                       break;
+               case RTOverlapStrategyNumber:
+                       retval = DatumGetBool(DirectFunctionCall2(box_overlap, PointerGetDatum(key), PointerGetDatum(query)));
+                       break;
+               case RTOverRightStrategyNumber:
+                       retval = DatumGetBool(DirectFunctionCall2(box_overright, PointerGetDatum(key), PointerGetDatum(query)));
+                       break;
+               case RTRightStrategyNumber:
+                       retval = DatumGetBool(DirectFunctionCall2(box_right, PointerGetDatum(key), PointerGetDatum(query)));
+                       break;
+               case RTSameStrategyNumber:
+                       retval = DatumGetBool(DirectFunctionCall2(box_same, PointerGetDatum(key), PointerGetDatum(query)));
+                       break;
+               case RTContainsStrategyNumber:
+                       retval = DatumGetBool(DirectFunctionCall2(box_contain, PointerGetDatum(key), PointerGetDatum(query)));
+                       break;
+               case RTContainedByStrategyNumber:
+                       retval = DatumGetBool(DirectFunctionCall2(box_contained, PointerGetDatum(key), PointerGetDatum(query)));
+                       break;
+               default:
+                       retval = FALSE;
+       }
+       return (retval);
+}
+
+
+PG_FUNCTION_INFO_V1(rtree_decompress);
+Datum rtree_decompress(PG_FUNCTION_ARGS)
+{
+       elog(NOTICE, "rtree_decompress called");
+    PG_RETURN_POINTER(PG_GETARG_POINTER(0));
+}
+
+/*
+** The GiST Union method for boxes
+** returns the minimal bounding box that encloses all the entries in entryvec
+*/
+PG_FUNCTION_INFO_V1(gbox_union);
+Datum gbox_union(PG_FUNCTION_ARGS)
+{
+#if USE_VERSION < 80
+       bytea *entryvec = (bytea *) PG_GETARG_POINTER(0);
+#else
+       GistEntryVector    *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
+#endif
+       int                *sizep = (int *) PG_GETARG_POINTER(1);
+       int                     numranges,
+                               i;
+       BOX                *cur,
+                          *pageunion;
+
+#ifdef DEBUG_GIST
+       elog(NOTICE,"GIST: gbox_union called\n");
+       fflush( stdout );
+#endif
+
+#if USE_VERSION < 80
+       numranges = (VARSIZE(entryvec) - VARHDRSZ) / sizeof(GISTENTRY);
+       cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[0].key);
+#else
+       numranges = entryvec->n;
+       cur = DatumGetBoxP(entryvec->vector[0].key);
+#endif
+       pageunion = (BOX *) palloc(sizeof(BOX));
+       memcpy((void *) pageunion, (void *) cur, sizeof(BOX));
+
+       for (i = 1; i < numranges; i++)
+       {
+#if USE_VERSION < 80
+               cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[i].key);
+#else
+               cur = DatumGetBoxP(entryvec->vector[i].key);
+#endif
+               if (pageunion->high.x < cur->high.x)
+                       pageunion->high.x = cur->high.x;
+               if (pageunion->low.x > cur->low.x)
+                       pageunion->low.x = cur->low.x;
+               if (pageunion->high.y < cur->high.y)
+                       pageunion->high.y = cur->high.y;
+               if (pageunion->low.y > cur->low.y)
+                       pageunion->low.y = cur->low.y;
+       }
+       *sizep = sizeof(BOX);
+
+       PG_RETURN_POINTER(pageunion);
+}
+
+/*
+** The GiST Penalty method for boxes
+** As in the R-tree paper, we use change in area as our penalty metric
+*/
+PG_FUNCTION_INFO_V1(gbox_penalty);
+Datum gbox_penalty(PG_FUNCTION_ARGS)
+{
+       GISTENTRY  *origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
+       GISTENTRY  *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
+       float      *result = (float *) PG_GETARG_POINTER(2);
+       Datum           ud;
+       float           tmp1;
+
+#ifdef DEBUG_GIST
+       elog(NOTICE,"GIST: gbox_penalty called\n");
+       fflush( stdout );
+#endif
+
+       ud = DirectFunctionCall2(rt_box_union, origentry->key, newentry->key);
+       tmp1 = size_box(ud);
+       if (DatumGetPointer(ud) != NULL)
+               pfree(DatumGetPointer(ud));
+
+       *result = tmp1 - size_box(origentry->key);
+       PG_RETURN_POINTER(result);
+}
+
+typedef struct {
+       BOX     *key;
+       int     pos;
+} KBsort;
+
+static int
+compare_KB(const void* a, const void* b) {
+       BOX *abox = ((KBsort*)a)->key;
+       BOX *bbox = ((KBsort*)b)->key;
+       float sa = (abox->high.x - abox->low.x) * (abox->high.y - abox->low.y);
+       float sb = (bbox->high.x - bbox->low.x) * (bbox->high.y - bbox->low.y);
+
+       if ( sa==sb ) return 0;
+       return ( sa>sb ) ? 1 : -1;
+}
+
+/*
+** The GiST PickSplit method
+** New linear algorithm, see 'New Linear Node Splitting Algorithm for R-tree',
+** C.H.Ang and T.C.Tan
+*/
+PG_FUNCTION_INFO_V1(gbox_picksplit);
+Datum
+gbox_picksplit(PG_FUNCTION_ARGS)
+{
+#if USE_VERSION < 80
+       bytea *entryvec = (bytea *) PG_GETARG_POINTER(0);
+#else
+       GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
+#endif
+       GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+       OffsetNumber i;
+       OffsetNumber *listL,
+                          *listR,
+                          *listB,
+                          *listT;
+       BOX                *unionL,
+                          *unionR,
+                          *unionB,
+                          *unionT;
+       int                     posL,
+                               posR,
+                               posB,
+                               posT;
+       BOX                     pageunion;
+       BOX                *cur;
+       char            direction = ' ';
+       bool            allisequal = true;
+       OffsetNumber maxoff;
+       int                     nbytes;
+
+#ifdef DEBUG_GIST
+       elog(NOTICE,"GIST: gbox_picksplit called\n");
+       fflush( stdout );
+#endif
+
+       posL = posR = posB = posT = 0;
+#if USE_VERSION < 80
+       maxoff = ((VARSIZE(entryvec) - VARHDRSZ) / sizeof(GISTENTRY)) - 1;
+       cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[FirstOffsetNumber].key);
+#else
+       maxoff = entryvec->n - 1;
+       cur = DatumGetBoxP(entryvec->vector[FirstOffsetNumber].key);
+#endif
+
+       memcpy((void *) &pageunion, (void *) cur, sizeof(BOX));
+
+       /* find MBR */
+       for (i = OffsetNumberNext(FirstOffsetNumber); i <= maxoff; i = OffsetNumberNext(i))
+       {
+#if USE_VERSION < 80
+               cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[i].key);
+#else
+               cur = DatumGetBoxP(entryvec->vector[i].key);
+#endif
+               if ( allisequal == true &&  (
+                               pageunion.high.x != cur->high.x ||
+                               pageunion.high.y != cur->high.y ||
+                               pageunion.low.x != cur->low.x ||
+                               pageunion.low.y != cur->low.y
+                       ) )
+                       allisequal = false;
+
+               if (pageunion.high.x < cur->high.x)
+                       pageunion.high.x = cur->high.x;
+               if (pageunion.low.x > cur->low.x)
+                       pageunion.low.x = cur->low.x;
+               if (pageunion.high.y < cur->high.y)
+                       pageunion.high.y = cur->high.y;
+               if (pageunion.low.y > cur->low.y)
+                       pageunion.low.y = cur->low.y;
+       }
+
+       nbytes = (maxoff + 2) * sizeof(OffsetNumber);
+       listL = (OffsetNumber *) palloc(nbytes);
+       listR = (OffsetNumber *) palloc(nbytes);
+       unionL = (BOX *) palloc(sizeof(BOX));
+       unionR = (BOX *) palloc(sizeof(BOX));
+       if (allisequal)
+       {
+#if USE_VERSION < 80
+               cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[OffsetNumberNext(FirstOffsetNumber)].key);
+#else
+               cur = DatumGetBoxP(entryvec->vector[OffsetNumberNext(FirstOffsetNumber)].key);
+#endif
+               if (memcmp((void *) cur, (void *) &pageunion, sizeof(BOX)) == 0)
+               {
+                       v->spl_left = listL;
+                       v->spl_right = listR;
+                       v->spl_nleft = v->spl_nright = 0;
+                       memcpy((void *) unionL, (void *) &pageunion, sizeof(BOX));
+                       memcpy((void *) unionR, (void *) &pageunion, sizeof(BOX));
+
+                       for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
+                       {
+                               if (i <= (maxoff - FirstOffsetNumber + 1) / 2)
+                               {
+                                       v->spl_left[v->spl_nleft] = i;
+                                       v->spl_nleft++;
+                               }
+                               else
+                               {
+                                       v->spl_right[v->spl_nright] = i;
+                                       v->spl_nright++;
+                               }
+                       }
+                       v->spl_ldatum = BoxPGetDatum(unionL);
+                       v->spl_rdatum = BoxPGetDatum(unionR);
+
+                       PG_RETURN_POINTER(v);
+               }
+       }
+
+       listB = (OffsetNumber *) palloc(nbytes);
+       listT = (OffsetNumber *) palloc(nbytes);
+       unionB = (BOX *) palloc(sizeof(BOX));
+       unionT = (BOX *) palloc(sizeof(BOX));
+
+#define ADDLIST( list, unionD, pos, num ) do { \
+       if ( pos ) { \
+               if ( unionD->high.x < cur->high.x ) unionD->high.x      = cur->high.x; \
+               if ( unionD->low.x      > cur->low.x  ) unionD->low.x   = cur->low.x; \
+               if ( unionD->high.y < cur->high.y ) unionD->high.y      = cur->high.y; \
+               if ( unionD->low.y      > cur->low.y  ) unionD->low.y   = cur->low.y; \
+       } else { \
+                       memcpy( (void*)unionD, (void*) cur, sizeof( BOX ) );  \
+       } \
+       list[pos] = num; \
+       (pos)++; \
+} while(0)
+
+       for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
+       {
+#if USE_VERSION < 80
+               cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[i].key);
+#else
+               cur = DatumGetBoxP(entryvec->vector[i].key);
+#endif
+               if (cur->low.x - pageunion.low.x < pageunion.high.x - cur->high.x)
+                       ADDLIST(listL, unionL, posL,i);
+               else
+                       ADDLIST(listR, unionR, posR,i);
+
+               if (cur->low.y - pageunion.low.y < pageunion.high.y - cur->high.y)
+                       ADDLIST(listB, unionB, posB,i);
+               else
+                       ADDLIST(listT, unionT, posT,i);
+       }
+
+       /* bad disposition, sort by ascending and resplit */
+       if ( (posR==0 || posL==0) && (posT==0 || posB==0) ) {
+               KBsort *arr = (KBsort*)palloc( sizeof(KBsort) * maxoff );
+               posL = posR = posB = posT = 0;
+               for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
+#if USE_VERSION < 80
+                       arr[i-1].key = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[i].key);
+#else
+                       arr[i-1].key = DatumGetBoxP(entryvec->vector[i].key);
+#endif
+                       arr[i-1].pos = i;
+               }
+               qsort( arr, maxoff, sizeof(KBsort), compare_KB );
+               for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
+                       cur = arr[i-1].key;
+                       if (cur->low.x - pageunion.low.x < pageunion.high.x - cur->high.x)
+                               ADDLIST(listL, unionL, posL,arr[i-1].pos);
+                       else if ( cur->low.x - pageunion.low.x > pageunion.high.x - cur->high.x ) 
+                               ADDLIST(listR, unionR, posR,arr[i-1].pos);
+                       else
+                       {
+                               if ( posL>posR )
+                                       ADDLIST(listR, unionR, posR,arr[i-1].pos);
+                               else
+                                       ADDLIST(listL, unionL, posL,arr[i-1].pos);
+                       }
+
+                       if (cur->low.y - pageunion.low.y < pageunion.high.y - cur->high.y)
+                               ADDLIST(listB, unionB, posB,arr[i-1].pos);
+                       else if ( cur->low.y - pageunion.low.y > pageunion.high.y - cur->high.y ) 
+                               ADDLIST(listT, unionT, posT,arr[i-1].pos);
+                       else
+                       {
+                               if ( posB>posT )
+                                       ADDLIST(listT, unionT, posT,arr[i-1].pos);
+                               else
+                                       ADDLIST(listB, unionB, posB,arr[i-1].pos);
+                       }
+               }
+               pfree(arr);
+       }
+
+       /* which split more optimal? */
+       if (Max(posL, posR) < Max(posB, posT))
+               direction = 'x';
+       else if (Max(posL, posR) > Max(posB, posT))
+               direction = 'y';
+       else
+       {
+               Datum           interLR = DirectFunctionCall2(rt_box_inter,
+                                                                                                 BoxPGetDatum(unionL),
+                                                                                                 BoxPGetDatum(unionR));
+               Datum           interBT = DirectFunctionCall2(rt_box_inter,
+                                                                                                 BoxPGetDatum(unionB),
+                                                                                                 BoxPGetDatum(unionT));
+               float           sizeLR,
+                                       sizeBT;
+
+               sizeLR = size_box(interLR);
+               sizeBT = size_box(interBT);
+
+               if (sizeLR < sizeBT)
+                       direction = 'x';
+               else
+                       direction = 'y';
+       }
+
+       if (direction == 'x')
+       {
+               pfree(unionB);
+               pfree(listB);
+               pfree(unionT);
+               pfree(listT);
+
+               v->spl_left = listL;
+               v->spl_right = listR;
+               v->spl_nleft = posL;
+               v->spl_nright = posR;
+               v->spl_ldatum = BoxPGetDatum(unionL);
+               v->spl_rdatum = BoxPGetDatum(unionR);
+       }
+       else
+       {
+               pfree(unionR);
+               pfree(listR);
+               pfree(unionL);
+               pfree(listL);
+
+               v->spl_left = listB;
+               v->spl_right = listT;
+               v->spl_nleft = posB;
+               v->spl_nright = posT;
+               v->spl_ldatum = BoxPGetDatum(unionB);
+               v->spl_rdatum = BoxPGetDatum(unionT);
+       }
+
+       PG_RETURN_POINTER(v);
+}
+
+/*
+** Equality method
+*/
+PG_FUNCTION_INFO_V1(gbox_same);
+Datum gbox_same(PG_FUNCTION_ARGS)
+{
+       BOX                *b1 = (BOX *) PG_GETARG_POINTER(0);
+       BOX                *b2 = (BOX *) PG_GETARG_POINTER(1);
+       bool       *result = (bool *) PG_GETARG_POINTER(2);
+
+#ifdef DEBUG_GIST
+       elog(NOTICE,"GIST: gbox_same called\n");
+       fflush( stdout );
+#endif
+
+       if (b1 && b2)
+               *result = DatumGetBool(DirectFunctionCall2(box_same, PointerGetDatum(b1), PointerGetDatum(b2)));
+       else
+               *result = (b1 == NULL && b2 == NULL) ? TRUE : FALSE;
+       PG_RETURN_POINTER(result);
+}
+
+static float
+size_box(Datum box)
+{
+
+#ifdef DEBUG_GIST
+       elog(NOTICE,"GIST: size_box called\n");
+       fflush( stdout );
+#endif
+
+       if (DatumGetPointer(box) != NULL)
+       {
+               float           size;
+
+               DirectFunctionCall2(rt_box_size,
+                                                       box, PointerGetDatum(&size));
+               return size;
+       }
+       else
+               return 0.0;
+}
diff --git a/hwgeom/postgis_inout.c b/hwgeom/postgis_inout.c
new file mode 100644 (file)
index 0000000..d12ccdf
--- /dev/null
@@ -0,0 +1,4906 @@
+
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2001-2003 Refractions Research Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ *
+ **********************************************************************
+ * $Log$
+ * Revision 1.1  2004/09/20 07:50:06  strk
+ * prepared to contain old internal representation code
+ *
+ * Revision 1.45  2004/08/11 17:07:26  strk
+ * Fixed a bug in non-finite Z check
+ *
+ * Revision 1.44  2004/06/09 09:08:53  strk
+ * changed index/rindex to strchr/strrchr
+ *
+ * Revision 1.43  2004/06/08 15:18:12  strk
+ * Deleted prototype for isspace() in postgis.h
+ * and included <ctype.h> in postgis_inout.c,
+ * which is the only module calling isspace().
+ * This was needed to compile postgis against PG75(CVS).
+ *
+ * Revision 1.42  2004/06/03 09:45:57  strk
+ * infinite geoms handled in WKB parser
+ *
+ * Revision 1.41  2004/06/03 08:19:20  strk
+ * yet another Infinite check used: finite() - which checks for NaN,-Inf,+Inf
+ *
+ * Revision 1.40  2004/06/03 08:13:11  strk
+ * Simplified INFINITY checks by use of isinf()
+ *
+ * Revision 1.39  2004/06/03 07:57:29  strk
+ * wkt parser throws an error on Infinite coordinates
+ *
+ * Revision 1.38  2004/04/28 22:26:02  pramsey
+ * Fixed spelling mistake in header text.
+ *
+ * Revision 1.37  2004/04/27 23:47:39  dblasby
+ * minor 3d geometrycollection bug fix
+ *
+ * Revision 1.36  2004/01/13 22:14:25  pramsey
+ * Changed getint and getdouble used by WKB so that it plays nice with
+ * memory alignment (solaris issue).
+ *
+ * Revision 1.35  2003/12/12 18:00:15  strk
+ * reverted make_line patch, patched size_subobject instead - the reported bug was caused to their inconsistency
+ *
+ * Revision 1.34  2003/12/12 14:39:04  strk
+ * Fixed a bug in make_line allocating less memory then required
+ *
+ * Revision 1.33  2003/12/12 12:03:30  strk
+ * More debugging output, some code cleanup.
+ *
+ * Revision 1.32  2003/12/09 11:58:42  strk
+ * Final touch to wkb binary input function
+ *
+ * Revision 1.31  2003/12/09 11:13:41  strk
+ * WKB_recv: set StringInfo cursor to the end of StringInfo buf as required by postgres backend
+ *
+ * Revision 1.30  2003/12/08 17:57:36  strk
+ * Binary WKB input function built only when USE_VERSION > 73. Making some modifications based on reported failures
+ *
+ * Revision 1.29  2003/11/28 11:06:49  strk
+ * Added WKB_recv function for binary WKB input
+ *
+ * Revision 1.28  2003/10/06 18:09:08  dblasby
+ * Fixed typo in add_to_geometry().  With very poorly aligned sub-objects, it
+ * wouldnt allocate enough memory.  Fixed it so its pesimistic and will allocate
+ * enough memory.
+ *
+ * Revision 1.27  2003/08/22 17:40:11  dblasby
+ * fixed geometry_in('SRID=<int>{no ;}').
+ *
+ * Revision 1.26  2003/08/21 16:22:09  dblasby
+ * added patch from strk@freek.keybit.net  for PG_NARGS() not being in 7.2
+ *
+ * Revision 1.25  2003/08/08 18:19:20  dblasby
+ * Conformance changes.
+ * Removed junk from postgis_debug.c and added the first run of the long
+ * transaction locking support.  (this will change - dont use it)
+ * conformance tests were corrected
+ * some dos cr/lf removed
+ * empty geometries i.e. GEOMETRYCOLLECT(EMPTY) added (with indexing support)
+ * pointN(<linestring>,1) now returns the first point (used to return 2nd)
+ *
+ * Revision 1.24  2003/08/06 19:31:18  dblasby
+ * Added the WKB parser.  Added all the functions like
+ * PolyFromWKB(<WKB>,[<SRID>]).
+ *
+ * Added all the functions like PolyFromText(<WKT>,[<SRID>])
+ *
+ * Minor problem in GEOS library fixed.
+ *
+ * Revision 1.23  2003/07/25 17:08:37  pramsey
+ * Moved Cygwin endian define out of source files into postgis.h common
+ * header file.
+ *
+ * Revision 1.22  2003/07/08 18:35:54  dblasby
+ * changed asbinary_specify() so that it is more aware of TEXT being
+ * un-terminated.
+ *
+ * this is a modified patch from David Garnier <david.garnier@etudier-online.com>.
+ *
+ * Revision 1.21  2003/07/01 18:30:55  pramsey
+ * Added CVS revision headers.
+ *
+ *
+ **********************************************************************/
+
+#include "postgres.h"
+
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "access/gist.h"
+#include "access/itup.h"
+#include "access/rtree.h"
+
+
+#include "fmgr.h"
+
+
+#include "postgis.h"
+#if USE_VERSION > 73
+# include "lib/stringinfo.h" // for WKB binary input
+#endif
+#include "utils/elog.h"
+
+
+#define SHOW_DIGS_DOUBLE 16
+#define MAX_DIGS_DOUBLE (SHOW_DIGS_DOUBLE + 6 + 1 + 3 +1)
+
+
+// #define DEBUG_GIST
+//#define DEBUG_GIST2
+
+
+#define WKB3DOFFSET 0x80000000
+
+
+void swap_char(char    *a,char *b)
+{
+       char c;
+
+       c = *a;
+       *a=*b;
+       *b=c;
+}
+
+
+void   flip_endian_double(char         *d)
+{
+       swap_char(d+7, d);
+       swap_char(d+6, d+1);
+       swap_char(d+5, d+2);
+       swap_char(d+4, d+3);
+}
+
+void           flip_endian_int32(char          *i)
+{
+       swap_char (i+3,i);
+       swap_char (i+2,i+1);
+}
+
+
+
+//takes a point and sticks it at the end of a string
+void print_point(char *result, POINT3D *pt,bool is3d)
+{
+       char    temp[MAX_DIGS_DOUBLE*3 +6];
+
+
+       if ( (pt == NULL) || (result == NULL) )
+               return;
+
+       if (is3d)
+               sprintf(temp, "%.15g %.15g %.15g", pt->x,pt->y,pt->z);
+       else
+               sprintf(temp, "%.15g %.15g", pt->x,pt->y);
+
+       strcat(result,temp);
+
+}
+
+//result MUST BE null terminated (or result[0] = 0)
+void print_many_points(char *result, POINT3D *pt ,int npoints, bool is3d)
+{
+       int     u;
+
+       result += strlen(result);
+       if (is3d)
+       {
+               for (u=0;u<npoints;u++)
+               {
+                       if (u != 0)
+                       {
+                               result[0] = ',';
+                               result++;
+                       }
+                       result+=        sprintf(result,"%.15g %.15g %.15g", pt[u].x,pt[u].y,pt[u].z);
+               }
+       }
+       else
+       {
+               for (u=0;u<npoints;u++)
+               {
+                       if (u != 0)
+                       {
+                               result[0] = ',';
+                               result++;
+                       }
+
+                       result += sprintf(result, "%.15g %.15g",
+                                       pt[u].x, pt[u].y);
+               }
+       }
+}
+
+
+//swap two doubles
+void swap(double *d1, double *d2)
+{
+       double t;
+
+       t = *d1;
+       *d1 = *d2;
+       *d2 = t;
+}
+
+
+//returns how many points are in the first list in str
+//
+//  1. scan ahead looking for "("
+//  2. find "," until hit a ")"
+//  3. return number of points found
+//
+// NOTE: doesnt actually parse the points, so if the
+//       str contains an invalid geometry, this could give
+//        back the wrong answer.
+//
+// "(1 2 3, 4 5 6),(7 8, 9 10, 11 12 13)" => 2 (2nd list is not included)
+int    numb_points_in_list(char        *str)
+{
+       bool    keep_going;
+       int     points_found = 1; //no "," if only one point (and last point)
+                                       // # points = 1 + numb of ","
+
+       if ( (str == NULL) || (str[0] == 0) )
+       {
+               return 0;  //either null string or empty string
+       }
+
+       //look ahead for the "("
+
+       str = strchr(str,'(') ;
+
+       if ( (str == NULL) || (str[1] == 0) )  // str[0] = '(';
+       {
+               return 0;  //either didnt find "(" or its at the end of the string
+       }
+
+       keep_going = TRUE;
+       while (keep_going)
+       {
+               str=strpbrk(str,",)");  // look for a "," or ")"
+               keep_going = (str != NULL);
+               if (keep_going)  // found a , or )
+               {
+                       if (str[0] == ')')
+                       {
+                               //finished
+                               return points_found;
+                       }
+                       else    //str[0] = ","
+                       {
+                               points_found++;
+                               str++; //move 1 char forward
+                       }
+               }
+       }
+       return points_found; // technically it should return an error.
+}
+
+//Actually parse the points in a list
+//  "(1 2 3, 4 5 6),(7 8, 9 10, 11 12 13)" =>
+//                     points[0] = {1,2,3} and points[1] = {4,5,6}
+//             (2nd list is not parsed)
+//
+// parse at most max_points (points should already have enough space)
+// if any of the points are 3d, is3d is set to true, otherwise its untouched.
+//     2d points have z=0.0
+//
+// returns true if everything parses okay, otherwise false
+
+bool   parse_points_in_list(char       *str, POINT3D   *points, int32  max_points, bool *is3d)
+{
+       bool    keep_going;
+       int     numb_found= 0;
+       int     num_entities;
+
+       if ( (str == NULL) || (str[0] == 0)  || (max_points <0) || (points == NULL) )
+       {
+               return FALSE;  //either null string or empty string or other problem
+       }
+
+       if (max_points == 0)
+               return TRUE; //ask for nothing, get nothing
+
+       //look ahead for the "("
+
+       str = strchr(str,'(') ;
+
+       if ( (str == NULL) || (str[1] == 0) )  // str[0] = '(';
+       {
+               return FALSE;  //either didnt find "(" or its at the end of the string
+       }
+       str++;  //move forward one char
+
+       keep_going = TRUE;
+       while (keep_going)
+       {
+               //attempt to get the point
+               num_entities = sscanf(str,"%le %le %le", 
+                       &(points[numb_found].x),
+                       &(points[numb_found].y),
+                       &(points[numb_found].z));
+
+               if (num_entities !=3)
+               {
+                       if (num_entities !=2 )
+                       {
+                               elog(ERROR, "geom3d: parse_points_in_list() on invalid point");
+                               return FALSE; //error
+                       }
+                       else
+                       {
+                               points[numb_found].z = 0.0; //2d (only found x,y - set z =0.0)
+                       }
+               }
+               else
+               {
+                       *is3d = TRUE; //found 3 entites (x,y,z)
+               }
+
+               if ( ! finite(points[numb_found].x) ||
+                       ! finite(points[numb_found].y) )
+               {
+                       elog(ERROR, "infinite coordinate in geom");
+                       return FALSE;
+               }
+               numb_found++;
+
+               str=strpbrk(str,",)");  // look for a "," or ")"
+               if (str != NULL)
+                       str++;
+               keep_going =  ( (str != NULL) && (str[0] != ')' ) && (numb_found < max_points)  );
+       }
+       return TRUE;
+}
+
+
+//Actually parse the points in a list
+//  "(1 2 3, 4 5 6),(7 8, 9 10, 11 12 13)" =>
+//                     points[0] = {1,2,3} and points[1] = {4,5,6}
+//             (2nd list is not parsed)
+//
+// parse at most max_points (points should already have enough space)
+// if any of the points are 3d, is3d is set to true, otherwise its untouched.
+//     2d points have z=0.0
+//
+// returns true if everything parses okay, otherwise false
+//
+// THIS IS EXACTLY the same as parse_points_in_list(), but returns FALSE if
+// the number of points parsed is != max_points
+
+bool   parse_points_in_list_exact(char *str, POINT3D   *points, int32  max_points, bool *is3d)
+{
+       bool    keep_going;
+       int     numb_found= 0;
+
+       char    *end_of_double;
+
+       if ( (str == NULL) || (str[0] == 0)  || (max_points <0) || (points == NULL) )
+       {
+               return FALSE;  //either null string or empty string or other problem
+       }
+
+       if (max_points == 0)
+               return TRUE; //ask for nothing, get nothing
+
+       //look ahead for the "("
+
+       str = strchr(str,'(') ;
+
+       if ( (str == NULL) || (str[1] == 0) )  // str[0] = '(';
+       {
+               return FALSE;  //either didnt find "(" or its at the end of the string
+       }
+       str++;  //move forward one char
+
+       keep_going = TRUE;
+       while (keep_going)
+       {
+               //attempt to get the point
+
+                       //scanf is slow, so we use strtod()
+
+
+                       points[numb_found].x = strtod(str,&end_of_double);
+                       if (end_of_double == str)
+                       {
+                               return FALSE; //error occured (nothing parsed)
+                       }
+                       str = end_of_double;
+                       if ( ! finite(points[numb_found].x) )
+                       {
+                               elog(ERROR, "infinite coordinate in geom");
+                               return FALSE;
+                       }
+                       points[numb_found].y = strtod(str,&end_of_double);
+                       if (end_of_double == str)
+                       {
+                               return FALSE; //error occured (nothing parsed)
+                       }
+                       if ( ! finite(points[numb_found].y) )
+                       {
+                               elog(ERROR, "infinite coordinate in geom");
+                               return FALSE;
+                       }
+                       str = end_of_double;
+                       points[numb_found].z = strtod(str,&end_of_double); //will be zero if error occured
+                       if (!(end_of_double == str))
+                       {
+                               if ( ! finite(points[numb_found].z) )
+                               {
+                                       elog(ERROR, "infinite coordinate in geom");
+                                       return FALSE;
+                               }
+                               *is3d = TRUE; //found 3 entites (x,y,z)
+                       }
+                       else
+                       {
+                               points[numb_found].z = 0.0;
+                       }
+                       str = end_of_double;
+                       numb_found++;
+
+
+
+               str=strpbrk(str,",)");  // look for a "," or ")"
+               if (str != NULL)
+                       str++;
+               keep_going =  ( (str != NULL) && (str[0] != ')' ) && (numb_found < max_points)  );
+       }
+       return (numb_found == max_points);
+}
+
+
+
+
+// Find the number of sub lists in a list
+// ( (..),(..),(..) )  -> 3
+// ( ( (..),(..) ), ( (..) )) -> 2
+// ( ) -> 0
+// scan through the list, for every "(", depth (nesting) increases by 1
+//                               for every ")", depth (nesting) decreases by 1
+// if find a "(" at depth 1, then there is a sub list
+//
+// example:
+//      "(((..),(..)),((..)))"
+//depth  12333223332112333210
+//        +           +         increase here
+
+int    find_outer_list_length(char *str)
+{
+       int     current_depth = 0;
+       int     numb_lists = 0;
+
+
+       while ( (str != NULL) && (str[0] != 0) )
+       {
+               str=strpbrk(str,"()"); //look for "(" or ")"
+               if (str != NULL)
+               {
+                       if (str[0] == '(')
+                       {
+                               current_depth++;
+                               if (current_depth == 2)
+                                       numb_lists ++;
+                       }
+                       if (str[0] == ')')
+                       {
+                               current_depth--;
+                               if (current_depth == 0)
+                                       return numb_lists ;
+                       }
+                       str++;
+               }
+       }
+       return numb_lists ; // probably should give an error
+}
+
+
+// Find out how many points are in each sublist, put the result in the array npoints[]
+//  (for at most max_list sublists)
+//
+//  ( (L1),(L2),(L3) )  --> npoints[0] = points in L1,
+//                                 npoints[1] = points in L2,
+//                                 npoints[2] = points in L3
+//
+// We find these by, again, scanning through str looking for "(" and ")"
+// to determine the current depth.  We dont actually parse the points.
+
+bool   points_per_sublist( char *str, int32 *npoints, int32 max_lists)
+{
+       //scan through, noting depth and ","s
+
+       int     current_depth = 0;
+       int     current_list =-1 ;
+
+
+       while ( (str != NULL) && (str[0] != 0) )
+       {
+               str=strpbrk(str,"(),");  //find "(" or ")" or ","
+               if (str != NULL)
+               {
+                       if (str[0] == '(')
+                       {
+                               current_depth++;
+                               if (current_depth == 2)
+                               {
+                                       current_list ++;
+                                       if (current_list >=max_lists)
+                                               return TRUE;                    // too many sub lists found
+                                       npoints[current_list] = 1;
+                               }
+                               // might want to return an error if depth>2
+                       }
+                       if (str[0] == ')')
+                       {
+                               current_depth--;
+                               if (current_depth == 0)
+                                       return TRUE ;
+                       }
+                       if (str[0] == ',')
+                       {
+                               if (current_depth==2)
+                               {
+                                       npoints[current_list] ++;
+                               }
+                       }
+
+                       str++;
+               }
+       }
+       return TRUE ; // probably should give an error
+}
+
+
+
+
+//simple scan-forward to find the next "(" at the same level
+//  ( (), (),(), ),(...
+//                 + return this location
+char   *scan_to_same_level(char        *str)
+{
+
+       //scan forward in string looking for at "(" at the same level
+       // as the one its already pointing at
+
+               int     current_depth = 0;
+               bool  first_one=TRUE;
+
+
+       while ( (str != NULL) && (str[0] != 0) )
+       {
+               str=strpbrk(str,"()");
+               if (str != NULL)
+               {
+                       if (str[0] == '(')
+                       {
+                               if (!(first_one))
+                               {
+                                       if (current_depth == 0)
+                                               return str;
+                               }
+                               else
+                                       first_one = FALSE;  //ignore the first opening "("
+                               current_depth++;
+                       }
+                       if (str[0] == ')')
+                       {
+                               current_depth--;
+                       }
+
+                       str++;
+               }
+       }
+       return str ; // probably should give an error
+}
+
+
+/*
+ *  BOX3D_in - takes a string rep of BOX3D and returns internal rep
+ *
+ *  example:
+ *     "BOX3D(x1 y1 z1,x2 y2 z2)"
+ * or  "BOX3D(x1 y1,x2 y2)"   z1 and z2 = 0.0
+ *
+ *
+ */
+
+BOX3D  *parse_box3d(char *str)
+{
+
+       BOX3D      *bbox = (BOX3D *) palloc(sizeof(BOX3D));
+       bool            junk_bool;
+       bool       okay;
+       int             npoints;
+
+
+       //verify that there are exactly two points
+
+       //strip leading spaces
+       while (isspace((unsigned char) *str))
+               str++;
+//printf( "box3d_in gets '%s'\n",str);
+
+       if (strstr(str,"BOX3D") !=  str )
+       {
+                elog(ERROR,"BOX3D parser - doesnt start with BOX3D");
+                pfree(bbox);
+                return NULL;
+       }
+
+       if ((npoints = numb_points_in_list(str)) != 2)
+       {
+//printf("npoints in this bbox is %i, should be 2\n",npoints);
+                elog(ERROR,"BOX3D parser - number of points should be exactly 2");
+                pfree(bbox);
+                return NULL;
+       }
+
+       //want to parse two points, and dont care if they are 2d or 3d
+       okay =  parse_points_in_list(str, &(bbox->LLB), 2, &junk_bool);
+       if (okay == 0)
+       {
+               elog(ERROR,"box3d: couldnt parse: '%s'\n",str);
+               pfree(bbox);
+               return NULL;
+       }
+
+
+       //validate corners so LLB is mins of x,y,z and URT is maxs x,y,z
+       if (    bbox->LLB.x > bbox->URT.x)
+       {
+               swap ( &bbox->LLB.x , &bbox->URT.x ) ;
+       }
+       if (    bbox->LLB.y > bbox->URT.y)
+       {
+               swap ( &bbox->LLB.y , &bbox->URT.y );
+       }
+       if (    bbox->LLB.z > bbox->URT.z)
+       {
+               swap ( &bbox->LLB.z , &bbox->URT.z ) ;
+       }
+
+       return bbox;
+}
+
+PG_FUNCTION_INFO_V1(box3d_in);
+Datum box3d_in(PG_FUNCTION_ARGS)
+{
+       char       *str = PG_GETARG_CSTRING(0);
+       BOX3D      *bbox ;
+
+       bbox = parse_box3d(str);
+       if (bbox != NULL)
+               PG_RETURN_POINTER(bbox);
+       else
+               PG_RETURN_NULL();
+}
+
+/*
+ *  Takes an internal rep of a BOX3D and returns a string rep.
+ *
+ *  example:
+ *     "BOX3D(LLB.x LLB.y LLB.z, URT.x URT.y URT.z)"
+ */
+PG_FUNCTION_INFO_V1(box3d_out);
+Datum box3d_out(PG_FUNCTION_ARGS)
+{
+       BOX3D  *bbox = (BOX3D *) PG_GETARG_POINTER(0);
+       int size;
+       char    *result;
+
+       if (bbox == NULL)
+       {
+               result = palloc(5);
+               strcat(result,"NULL");
+               PG_RETURN_CSTRING(result);
+       }
+
+       size = MAX_DIGS_DOUBLE*6+5+2+4+5+1;
+       result = (char *) palloc(size); //double digits+ "BOX3D"+ "()" + commas +null
+       sprintf(result, "BOX3D(%.15g %.15g %.15g,%.15g %.15g %.15g)",
+                       bbox->LLB.x,bbox->LLB.y,bbox->LLB.z,
+                       bbox->URT.x,bbox->URT.y,bbox->URT.z);
+
+       PG_RETURN_CSTRING(result);
+}
+
+/***************************************************************
+ * these functions return the number of sub-objects inside a
+ * sub-portion of a string.
+ *
+ * LINESTRING(), POLYGON(), POINT() allways have 1 sub object
+ *
+ * MULTIPOLYGON() MULTILINESTRING() MULTIPOINT() will have a variable number of sub-parts
+ *           ex MULTIPOINT(1 1 1, 2 2 2,3 3 3,4 4 4) -->4 because there are 4 points in it
+ *
+ * GEOMETRYCOLLECTION() - need to look at each sub-object in the collection
+ ***************************************************************/
+
+//number of object inside a point
+int objects_inside_point(char *str)
+{
+       return 1; //trivial points always have 1 point in them
+}
+
+
+//number of object inside a line
+int objects_inside_line(char *str)
+{
+       return 1; //trivial lines always have 1 line in them
+}
+
+//number of object inside a polygon
+int objects_inside_polygon(char *str)
+{
+       return 1; //trivial polygon always have 1 polygon in them
+}
+
+
+//bit more complicated - need to find out how many points are in this entity
+int objects_inside_multipoint(char *str)
+{
+       return numb_points_in_list(str);
+}
+
+//bit more complicated - need to find out how many lines are in this entity
+int objects_inside_multiline(char *str)
+{
+       return find_outer_list_length(str);
+}
+
+
+//bit more complicated - need to find out how many polygons are in this entity
+int objects_inside_multipolygon(char *str)
+{
+       return find_outer_list_length(str);
+}
+
+
+
+//This one is more complicated, we have to look at each of the sub-object inside
+// the collection.
+//  result = sum of ( number of objects in each of the collection's sub-objects )
+//
+// EX:   'GEOMETRYCOLLECTION( POINT(1 2 3),
+//     POINT (4 5 6) ),
+//     LINESTRING(3 4 5,1 2 3,5 6 0),
+//     MULTILINESTRING((3 4 5,1 2 3,5 6 0)),
+//     MULTIPOINT(1 2 3),
+//     MULTILINESTRING((3 4 5,1 2 3,5 6 0),(1 2 0,4 5 0,6 7 0,99 99 0)),
+//     MULTIPOLYGON ((  (3 4 5,1 2 3,5 6),(1 2, 4 5, 6 7)  ), ( (11 11, 22 22, 33 33),(44 44, 55 55, 66 66) ) )'
+//
+//     # object = # of objs in POINT (1) + # of objs in POINT (1) +   # of objs in LINESTRING (1)
+//                     + # of objs in MULTILINESTRING (1) + # of objs in MULTIPOINT  (1)
+//                     + # of objs in MULTILINESTRING (1) + # of objs in  MULTIPOLYGON  (2)
+
+int objects_inside_collection(char *str)
+{
+       int             tally = 0;
+       int             sub_size = 0;
+
+       //skip past the "geometrycollection" at begining of string
+       str += 18;
+       if (strstr(str, "GEOMETRYCOLLECTION") != NULL)
+               return -1; // collection inside collection; bad boy!
+
+       //we scan to the first letter, then pass it to objects inside
+       // to find out how many sub-objects there are.  Then move to
+       // the next object
+
+
+       while ( str != NULL)
+       {
+               //scan for a letter  (all objs start with M, P, or L
+               str = strpbrk(str,"MPL"); //search for MULTI*, POLY*, POIN*
+               if (str != NULL)
+               {
+                       //object found
+                       sub_size = objects_inside(str); //how many in this object
+                       if (sub_size == -1)
+                               return -1;//relay error
+
+                       tally += sub_size;      // running total
+
+                       //need to move to next item
+                       // by scanning past any letters
+                       str = strchr(str,'(');  //need to move to the next sub-object
+               }
+       }
+       return tally;
+}
+
+
+// Find the # of sub-objects inside the geometry
+//     Just pass it off to the other functions
+int objects_inside(char *str)
+{
+
+       char            *parenth;
+       char            *loc;
+
+
+       parenth = strchr(str,'(');
+       if (parenth == NULL)
+               return -1; //invalid string
+
+       // look only at the begining of the string
+       // the order of these "if" are important!
+
+       if (  ((loc = strstr(str, "GEOMETRYCOLLECTION")) != NULL) && (loc < parenth) )
+               return objects_inside_collection(str);
+
+       // do multi before base types (MULTIPOINT has the string "POINT" in it)
+
+       if (((loc = strstr(str,"MULTIPOINT")) != NULL) && (loc < parenth) )
+               return objects_inside_multipoint(str);
+       if (((loc = strstr(str,"MULTILINESTRING") )!= NULL) && (loc < parenth) )
+               return objects_inside_multiline(str);
+       if (((loc = strstr(str,"MULTIPOLYGON")) != NULL) && (loc < parenth) )
+               return objects_inside_multipolygon(str);
+
+       //base types
+       if (((loc = strstr(str,"POINT")) != NULL) && (loc < parenth) )
+               return objects_inside_point(str);
+       if (((loc = strstr(str,"LINESTRING")) != NULL) && (loc < parenth) )
+               return objects_inside_line(str);
+       if (((loc = strstr(str,"POLYGON")) != NULL) && (loc < parenth) )
+               return objects_inside_polygon(str);
+
+       return -1; //invalid
+
+}
+
+
+/****************************************************************************
+ *  These functions actually parse the objects in the strings
+ *  They all have arguments:
+ *     obj_size[]  -- size (in bytes) of each the sub-objects
+ *    objs[][]    -- list of pointers to the actual objects
+ *    obj_types   -- type of the sub-obj (see postgis.h)
+ *    nobjs       -- max number of sub-obj
+ *    str              -- actual string to parse from (start at begining)
+ *    offset      -- which sub-object # are we dealing with
+ *    is3d     -- are any of the points in the object 3d?
+ *
+ *    basically, each of the functions will:
+ *             + parse str to find the information required
+ *             + allocate a new obj(s) and place it/them in objs to hold this info
+ *             + set the obj_size for this/these sub-object
+ *             + set the obj_type for this/these sub-object
+ *             + increase offset so the next objects will go in the correct place
+ *             + set is3d if there are any 3d points in the sub-object
+ *             + return FALSE if an error occured
+ ****************************************************************************/
+
+
+//parse a point - make a new POINT3D object
+bool parse_objects_inside_point(int32 *obj_size,char **objs, int32 *obj_types, int32 nobjs, char *str, int *offset, bool* is3d)
+{
+       bool    result;
+
+       if (*offset >= nobjs)
+               return FALSE;     //too many objs found
+
+       objs[*offset] = palloc (sizeof(POINT3D) );
+       memset(objs[*offset], 0, sizeof(POINT3D) ); // zero memory
+       obj_types[*offset] = POINTTYPE;
+       obj_size[*offset] = sizeof(POINT3D);
+
+       str = strchr(str,'(');
+       if (str == NULL)
+               return FALSE;  // parse error
+
+//printf("about to parse the point\n");
+
+       result = parse_points_in_list_exact(str, (POINT3D*) objs[*offset] , 1, is3d);
+//printf("     +point parsed\n");
+
+       *offset = *offset + 1;
+       return (result); // pass along any parse error
+}
+
+
+//parse a multipoint object - offset may increase by >1
+// multiple sub object may be created
+bool parse_objects_inside_multipoint(int32 *obj_size,char **objs, int32 *obj_types, int32 nobjs, char *str, int *offset, bool* is3d)
+{
+       bool    result;
+       int     npoints;
+       int     t;
+       POINT3D *pts;
+
+       // first find out how many points are in the multi-point
+       npoints = objects_inside_multipoint(str);
+
+       if (npoints <0)
+               return FALSE; // error in parsing (should have been caught before)
+
+       if (npoints ==0)
+               return TRUE;  //no points, no nothing
+
+       if (*offset >= nobjs)
+               return FALSE;     //too many objs found dont do anything, relay error
+
+
+       str = strchr(str,'(');
+       if (str == NULL)
+               return FALSE;
+
+               //allocate all the new objects
+       pts = palloc( sizeof(POINT3D) * npoints);
+       for (t=0;t<npoints; t++)
+       {
+               objs[*offset + t] = palloc( sizeof(POINT3D) );
+               memset(objs[*offset+t], 0, sizeof(POINT3D) ); // zero memory
+
+               obj_types[*offset+t] = POINTTYPE;
+               obj_size[*offset+t] = sizeof(POINT3D);
+       }
+
+       //actually do the parsing into a temporary list
+       result = parse_points_in_list_exact(str, pts , npoints, is3d);
+
+
+       if (!(result))
+       {
+               pfree(pts);
+               return FALSE; //relay error
+       }
+
+       for(t=0;t<npoints;t++)
+       {
+               memcpy(objs[*offset+t], &pts[t], sizeof(POINT3D) );
+       }
+
+       pfree(pts);
+       *offset = *offset + npoints;
+       return (result);
+}
+
+
+//parse a linestring
+bool parse_objects_inside_line(int32 *obj_size,char **objs, int32 *obj_types, int32 nobjs, char *str, int *offset, bool* is3d)
+{
+       bool    result;
+       int     num_points;
+       bool    add_point = FALSE; // only 1 point --> make it two on top of each other
+
+
+       if (*offset >= nobjs)
+               return FALSE;     //too many objs found
+
+       //how many points are in this linestring?
+       num_points = numb_points_in_list(str);
+
+       if (num_points == 0)
+               return FALSE;
+
+
+       if (num_points == 1)    //not really a line
+       {
+               num_points  = 2;
+               add_point = TRUE;
+       }
+
+
+
+                                                       // -1 because object already has 1 point in it
+               objs[*offset] = palloc (sizeof(LINE3D) + sizeof(POINT3D)*(num_points-1) );
+               memset(objs[*offset], 0, sizeof(LINE3D) + sizeof(POINT3D)*(num_points-1)  ); // zero memory
+
+       obj_types[*offset] = LINETYPE;
+       obj_size[*offset] = sizeof(LINE3D) + sizeof(POINT3D)*(num_points-1) ;
+
+       str = strchr(str,'(');
+       if (str == NULL)
+               return FALSE;
+
+       ( (LINE3D *) objs[*offset] )->npoints = num_points;
+
+       if (add_point)
+       {
+               result = parse_points_in_list_exact(str, &(( (LINE3D*) objs[*offset]) ->points[0]) , num_points-1, is3d);
+       }
+       else
+       {
+               result = parse_points_in_list_exact(str, &(( (LINE3D*) objs[*offset]) ->points[0]) , num_points, is3d);
+       }
+
+       if (add_point)
+       {
+               memcpy( &(( (LINE3D*) objs[*offset]) ->points[1])   , &(( (LINE3D*) objs[*offset]) ->points[0])  , sizeof(POINT3D) );
+       }
+
+       *offset = *offset + 1;
+       return (result);
+}
+
+
+
+//parse a multi-line.  For each sub-linestring, call the parse_objects_inside_line() function
+//     'MULTILINESTRING((3 4 5,1 2 3,5 6 0),(1 2 0,4 5 0,6 7 0,99 99 0))'
+// first scan to the second "(" - this is the begining of the first linestring.
+// scan it, then move to the second linestring
+bool parse_objects_inside_multiline(int32 *obj_size,char **objs, int32 *obj_types, int32 nobjs, char *str, int *offset, bool* is3d)
+{
+       bool    result;
+       int     num_lines;
+       int     t;
+
+
+               //how many lines are there?
+       num_lines = objects_inside_multiline(str);
+
+
+       if (num_lines <0)
+               return FALSE;
+
+       if (num_lines ==0)
+               return TRUE;    //can be nothing
+
+
+       if (*offset >= nobjs)
+               return FALSE;     //too many objs found
+
+       str= strchr(str,'('); //first one
+       if (str != NULL)
+               str++;
+       str= strchr(str,'('); //2nd one
+
+       if (str == NULL)
+               return FALSE;  // couldnt find it
+
+       for (t=0;t<num_lines; t++)
+       {
+                       //pass along the work - it will update offset for us
+               result = parse_objects_inside_line(obj_size,objs, obj_types,nobjs, str,offset,is3d);
+               if (!(result))
+                       return FALSE;//relay error
+               if (str != NULL)
+                       str++;  // go past the first '(' in the linestring
+               else
+                       return FALSE; //parse error
+               str = strchr(str,'('); // go to the beginning of the next linestring
+       }
+
+       return (TRUE);  //everything okay
+}
+
+//parse a polygon
+//     POLYGON (  (3 4 5,1 2 3,5 6),(1 2, 4 5, 6 7)  )'
+
+
+bool parse_objects_inside_polygon(int32 *obj_size,char **objs, int32 *obj_types, int32 nobjs, char *str, int *offset, bool* is3d)
+{
+       bool    result;
+       int     num_rings;
+       int32   *npoints;
+       int32   sum_points,points_offset;
+       int     t;
+       int     size;
+       POINT3D *pts;
+       POLYGON3D       *polygon;
+
+
+
+       if (*offset >= nobjs)
+               return FALSE;     //too many objs found
+
+               //find out how many rings there are
+       num_rings = find_outer_list_length(str);
+
+       if (num_rings <1)
+               return FALSE; // must have at least one ring
+
+               //allocate space for how many points in each ring
+       npoints = palloc( sizeof(int32) * num_rings);
+
+               //find out how many points are in each sub-list (ring)
+       result = points_per_sublist(str,npoints, num_rings);
+
+               //how many points are inside this polygon in total?
+       sum_points =0;
+       for(t=0;  t< num_rings ; t++)
+       {
+               sum_points += npoints[t];
+               if (npoints[t] <3)
+               {
+                       elog(ERROR,"polygon has ring with <3 points.");
+                       pfree(npoints);
+                       return FALSE;
+               }
+       }
+
+               //now we can allocate the structure
+
+
+                       // -1 because the POLYGON3D already has space for 1 ring with 1 point
+                       // 4 is for possible double-word alignment
+       size = sizeof(POLYGON3D) + sizeof(int32)*(num_rings-1) +4+ sizeof(POINT3D)*(sum_points-1)  ;
+       objs[*offset] = palloc (size);
+               memset(objs[*offset], 0, size  ); // zero memory
+
+       polygon = (POLYGON3D *) objs[*offset];
+       obj_types[*offset] = POLYGONTYPE;
+       obj_size[*offset] = size;
+
+       str = strchr(str,'(');
+       if (str == NULL)
+       {
+               pfree(npoints);
+               return FALSE;
+       }
+       str++;
+
+       polygon->nrings = num_rings;
+
+               pts = (POINT3D *)  &(polygon->npoints[num_rings]); //pts[] is just past the end of npoints[]
+
+                       // make sure its double-word aligned
+
+               pts = (POINT3D *) MAXALIGN(pts);
+
+
+//printf("POLYGON is at %p, size is %i, pts is at %p (%i)\n",objs[*offset],size, pts, ((void *) pts) - ((void *)objs[*offset]) );
+//printf("npoints[0] is at %p, num_rings=%i\n", &(((POLYGON3D *) objs[*offset])->npoints[0] ), num_rings );
+
+       points_offset = 0;
+       for(t=0;t<num_rings;t++)        //for each ring
+       {
+               //set num points in this obj
+               polygon->npoints[t] = npoints[t]; // set # pts in this ring
+
+                       //use exact because it will screw up otherwise
+//printf("about to parse the actual points in ring #%i with %i points:\n",t,npoints[t]);
+               result = parse_points_in_list_exact(str,
+                                       &pts[points_offset],
+                                       npoints[t], is3d);
+//printf("     +done parsing points\n");
+
+//printf("1st points in ring is: [%g,%g,%g]\n", pts[points_offset].x ,pts[points_offset].y ,pts[points_offset].z );
+
+               if (!(result))
+               {
+                       pfree(npoints);
+                       return FALSE;   //relay error
+               }
+
+               //first and last point are the same
+               if (!  (FPeq(pts[points_offset].x , pts[points_offset + npoints[t]-1].x  )
+                        &&FPeq(pts[points_offset].y , pts[points_offset + npoints[t]-1].y   )
+                        &&FPeq(pts[points_offset].z , pts[points_offset + npoints[t]-1].z   )    )   )
+               {
+                       elog(ERROR,"polygon has ring where first point != last point");
+                       pfree(npoints);
+                       return FALSE; //invalid poly
+               }
+
+
+               points_offset += npoints[t]; //where to stick points in the next ring
+
+               str = strchr(str,'('); // where is the next ring
+               if (str == NULL)
+               {
+                       pfree(npoints);
+                       return FALSE;  //relay parse error
+               }
+               str++;
+       }
+
+       pfree(npoints);
+       *offset = *offset + 1;
+       return (result);
+}
+
+
+//     MULTIPOLYGON ((  (3 4 5,1 2 3,5 6),(1 2, 4 5, 6 7)  ), ( (11 11, 22 22, 33 33),(44 44, 55 55, 66 66) ) )'
+//     pass off work to parse_objects_inside_polygon()
+
+bool parse_objects_inside_multipolygon(int32 *obj_size,char **objs, int32 *obj_types, int32 nobjs, char *str, int *offset, bool* is3d)
+{
+       bool    result;
+       int     num_polys;
+       int     t;
+
+       num_polys = objects_inside_multipolygon(str);  //how many polygons are inside me?
+
+//printf("parse_objects_inside_multipolygon('%s')\n",str);
+
+       if (num_polys <0)
+               return FALSE;
+
+       if (num_polys ==0)
+               return TRUE;    //ask for nothing, get nothing
+
+       if (*offset >= nobjs)
+               return FALSE;     //too many objs found
+
+       str= strchr(str,'('); //first one (begining of list of polys)
+       if (str != NULL)
+       {
+               str++;
+               str= strchr(str,'('); //2nd one (beginning of 1st poly)
+       }
+       if (str == NULL)
+               return FALSE;   //relay error
+
+       for (t=0;t<num_polys; t++)
+       {
+               if (str == NULL)
+                       return FALSE;   //relay error
+//printf("parse_inside_multipolygon: parsing a polygon #%i\n",t);
+               result = parse_objects_inside_polygon(obj_size,objs, obj_types,nobjs, str,offset,is3d);
+               if (!(result))
+                       return FALSE;   //relay error
+
+               //scan ahead to next "(" start of next polygon
+               str = scan_to_same_level(str);
+       }
+       return (TRUE);
+}
+
+
+// relay work to the above functions
+bool parse_objects(int32 *obj_size,char **objs,int32   *obj_types,int32 nobjs,char *str, int *offset, bool *is3d)
+{
+               char            *parenth;
+               char            *loc;
+
+
+//printf("parse_objects: parsing object offset=%i\n",*offset);
+
+       if (str == NULL)
+               return FALSE;
+
+       parenth = strchr(str,'(');
+       if  (parenth == NULL)
+               return FALSE; //invalid string
+
+
+       if (  ((loc = strstr(str, "GEOMETRYCOLLECTION")) != NULL) && (loc < parenth) )
+               return parse_objects_inside_collection(obj_size,objs, obj_types, nobjs, str, offset, is3d);
+
+
+       if (((loc = strstr(str,"MULTIPOINT")) != NULL) && (loc < parenth) )
+               return parse_objects_inside_multipoint(obj_size,objs, obj_types, nobjs, str, offset, is3d);
+
+
+       if (((loc = strstr(str,"MULTILINESTRING") )!= NULL) && (loc < parenth) )
+               return parse_objects_inside_multiline(obj_size,objs, obj_types, nobjs, str, offset, is3d);
+
+
+       if (((loc = strstr(str,"MULTIPOLYGON")) != NULL) && (loc < parenth) )
+               return parse_objects_inside_multipolygon(obj_size,objs, obj_types, nobjs, str, offset, is3d);
+
+       if (((loc = strstr(str,"POINT")) != NULL) && (loc < parenth) )
+               return parse_objects_inside_point(obj_size,objs, obj_types, nobjs, str, offset, is3d);
+
+
+       if (((loc = strstr(str,"LINESTRING")) != NULL) && (loc < parenth) )
+               return parse_objects_inside_line(obj_size,objs, obj_types, nobjs, str, offset, is3d);
+
+       if (((loc = strstr(str,"POLYGON")) != NULL) && (loc < parenth) )
+               return parse_objects_inside_polygon(obj_size,objs, obj_types, nobjs, str, offset, is3d);
+
+
+       return FALSE; //invalid
+
+}
+
+
+
+//look for each of the object inside a collection, and send the work to parse it along
+bool parse_objects_inside_collection(int32 *obj_size,char **objs, int32 *obj_types, int32 nobjs, char *str, int *offset, bool* is3d)
+{
+       bool    result = FALSE;
+
+       str += 18; //scan past "geometrycollection"
+       if (strstr(str, "GEOMETRYCOLLECTION") != NULL)
+               return FALSE; // collection inside collection; bad boy!
+
+       while ( str != NULL)
+       {
+               //scan for a letter
+               str = strpbrk(str,"MPL"); //search for MULTI*, POLY*, POIN*
+               if (str != NULL)
+               {
+                       //object found
+                       result = parse_objects(obj_size,objs,obj_types,nobjs,str, offset, is3d);
+
+                       if (result == FALSE)
+                               return FALSE;//relay error
+
+                       //prepare to move to next item
+                       // by scanning past any letters
+                       str = strchr(str,'(');
+               }
+       }
+       return result;
+}
+
+/**************************************************************
+ * Find the bounding box3d of a sub-object
+ **************************************************************/
+
+//simple box encloses a point
+BOX3D  *bbox_of_point(POINT3D *pt)
+{
+       BOX3D           *the_box = (BOX3D *) palloc (sizeof(BOX3D));
+
+       the_box->LLB.x = pt->x;
+       the_box->LLB.y = pt->y;
+       the_box->LLB.z = pt->z;
+
+       the_box->URT.x = pt->x;
+       the_box->URT.y = pt->y;
+       the_box->URT.z = pt->z;
+
+       return the_box;
+}
+
+// box encloses all the points in all the rings
+BOX3D  *bbox_of_polygon(POLYGON3D *polygon)
+{
+               BOX3D           *the_box ;
+               int             numb_points =0,i;
+               POINT3D *pts,*pt;
+
+       for (i=0; i<polygon->nrings; i++)
+       {
+               numb_points +=  polygon->npoints[i];
+       }
+
+       if (numb_points <1)
+               return NULL;
+
+       pts = (POINT3D *) ( (char *)&(polygon->npoints[polygon->nrings] )  );
+       pts = (POINT3D *) MAXALIGN(pts);
+
+       the_box = bbox_of_point(&pts[0]);
+
+       for (i=1; i<numb_points;i++)
+       {
+               pt = &pts[i];
+
+               if (pt->x < the_box->LLB.x)
+                       the_box->LLB.x = pt->x;
+
+               if (pt->y < the_box->LLB.y)
+                       the_box->LLB.y = pt->y;
+
+               if (pt->z < the_box->LLB.z)
+                       the_box->LLB.z = pt->z;
+
+               if (pt->x > the_box->URT.x)
+                       the_box->URT.x = pt->x;
+
+               if (pt->y > the_box->URT.y)
+                       the_box->URT.y = pt->y;
+
+               if (pt->z > the_box->URT.z)
+                       the_box->URT.z = pt->z;
+
+       }
+       return the_box;
+}
+
+//box encloses points in line
+BOX3D  *bbox_of_line(LINE3D *line)
+{
+       BOX3D           *the_box;
+       POINT3D *pt;
+       int i;
+
+       if (line->npoints <1)
+       {
+               return NULL;
+       }
+       the_box = bbox_of_point(&line->points[0]);
+
+       for (i=1;i< line->npoints; i++)
+       {
+               pt = &line->points[i];
+               if (pt->x < the_box->LLB.x)
+                       the_box->LLB.x = pt->x;
+
+               if (pt->y < the_box->LLB.y)
+                       the_box->LLB.y = pt->y;
+
+               if (pt->z < the_box->LLB.z)
+                       the_box->LLB.z = pt->z;
+
+               if (pt->x > the_box->URT.x)
+                       the_box->URT.x = pt->x;
+
+               if (pt->y > the_box->URT.y)
+                       the_box->URT.y = pt->y;
+
+               if (pt->z > the_box->URT.z)
+                       the_box->URT.z = pt->z;
+       }
+       return the_box;
+}
+
+//merge box a and b (expand b)
+//if b is null, new one is returned
+// otherwise b is returned
+BOX3D *union_box3d(BOX3D *a, BOX3D *b)
+{
+       if (a==NULL)
+               return NULL;
+       if (b==NULL)
+       {
+               b=(BOX3D*) palloc(sizeof(BOX3D));
+               memcpy(b,a,sizeof(BOX3D) );
+               return b;
+       }
+
+
+       if (a->LLB.x < b->LLB.x)
+               b->LLB.x = a->LLB.x;
+       if (a->LLB.y < b->LLB.y)
+               b->LLB.y = a->LLB.y;
+       if (a->LLB.z < b->LLB.z)
+               b->LLB.z = a->LLB.z;
+
+       if (a->URT.x > b->URT.x)
+               b->URT.x = a->URT.x;
+       if (a->URT.y > b->URT.y)
+               b->URT.y = a->URT.y;
+       if (a->URT.z > b->URT.z)
+               b->URT.z = a->URT.z;
+       return b;
+}
+
+BOX3D  *bbox_of_geometry(GEOMETRY *geom)
+{
+       int     i;
+       int32   *offsets;
+       char    *obj;
+       BOX3D   *result=NULL;
+       BOX3D   *a_box;
+
+//printf("bbox_of_geometry(%p)\n", geom);
+
+       if (geom->nobjs <1)
+               return NULL;    //bbox of 0 objs is 0
+
+       //where are the objects living?
+       offsets = (int32 *) ( ((char *) &(geom->objType[0] ))+ sizeof(int32) * geom->nobjs ) ;
+
+
+       //for each sub-object
+       for(i=0; i<geom->nobjs;i++)
+       {
+               obj = (char *) geom +offsets[i] ;
+
+
+               if (geom->objType[i] == POINTTYPE)
+               {
+//printf("box of a point\n");
+                       a_box = bbox_of_point(  (POINT3D *) obj);
+                       result= union_box3d(a_box  ,result);
+
+                       if (a_box != NULL)
+                               pfree (a_box);
+               }
+               if (geom->objType[i] == LINETYPE)
+               {
+//printf("box of a line, # points = %i\n",((LINE3D *) obj)->npoints );
+                       a_box = bbox_of_line(  (LINE3D *) obj);
+                       result = union_box3d(a_box  ,result);
+                       if (a_box != NULL)
+                               pfree (a_box);
+               }
+               if (geom->objType[i] == POLYGONTYPE)
+               {
+//printf("box of a polygon\n");
+                       a_box = bbox_of_polygon(  (POLYGON3D *) obj);
+                       result =union_box3d(a_box  ,result);
+                       if (a_box != NULL)
+                               pfree (a_box);
+               }
+       }
+       return result;
+}
+
+
+//given wkt and SRID, return a geometry
+//actually we cheat, postgres will convert the string to a geometry for us...
+PG_FUNCTION_INFO_V1(geometry_from_text);
+Datum geometry_from_text(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                      *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       int32                           SRID;
+
+       GEOMETRY        *result;
+
+       if ( ! PG_ARGISNULL(1) )
+               SRID = PG_GETARG_INT32(1);
+       else
+               SRID = -1;
+
+
+       result = (GEOMETRY *) palloc(geom1->size);
+       memcpy(result,geom1, geom1->size);
+
+       if (result != NULL)
+       {
+               result->SRID = SRID;
+       }
+       else
+       {
+               PG_RETURN_NULL();
+       }
+       PG_RETURN_POINTER(result);
+}
+
+
+
+// Parse a geometry object
+//
+// 1. Find information on how many sub-object there are
+// 2. create a group of sub-object and populate them
+// 3. make a large structure & populate with all the above info.
+//
+//
+PG_FUNCTION_INFO_V1(geometry_in);
+Datum geometry_in(PG_FUNCTION_ARGS)
+{
+               char       *str = PG_GETARG_CSTRING(0);
+               GEOMETRY        *geometry;
+               BOX3D           *the_bbox;
+               int32           nobjs;
+               char            **objs;
+               int32           *obj_types;
+               int32           *obj_size;
+               int             entire_size;
+               bool            is3d = FALSE;
+               bool            okay;
+               int             next_loc;
+               int32           *offsets;
+               int             t;
+               int             obj_parse_offset;
+               int             nitems;
+               int32           SRID;
+               double  scale,offx,offy;
+               BOX3D   *mybox;
+
+//printf("str=%s\n",str);
+
+
+
+               //trim white
+       while (isspace((unsigned char) *str))
+               str++;
+
+       //test to see if it starts with a SRID=
+
+       SRID = -1;
+       scale = offx = offy = 0;
+
+       if (strstr(str,"SRID="))
+       {
+               //its spatially located
+               nitems = sscanf(str,"SRID=%i;",&SRID);
+               if (nitems !=1 )
+               {
+                       //error
+                       elog(ERROR,"couldnt parse objects in GEOMETRY (SRID related)");
+                       PG_RETURN_NULL() ;
+               }
+               //delete first part of string
+               str = strchr(str,';');
+               if (str != NULL)
+                       str++;
+               else
+               {
+                       elog(ERROR,"couldnt parse objects in GEOMETRY (SRID related - no ';')");
+                       PG_RETURN_NULL() ;
+               }
+       }
+
+
+       if (strstr(str,"EMPTY"))
+       {
+               GEOMETRY *result=makeNullGeometry( SRID);
+               if (strstr(str,"MULTIPOLYGON"))
+                       result->type = MULTIPOLYGONTYPE;
+               if (strstr(str,"MULTILINESTRING"))
+                       result->type = MULTILINETYPE;
+               if (strstr(str,"MULTIPOINT"))
+                       result->type = MULTIPOINTTYPE;
+               PG_RETURN_POINTER( result );
+       }
+
+       if (strstr(str,"BOX3D") != NULL ) // bbox only
+       {
+
+
+               mybox = parse_box3d(str);
+
+               if (mybox == NULL)
+                       PG_RETURN_NULL() ;
+
+               geometry = (GEOMETRY *) palloc(sizeof(GEOMETRY));
+               geometry->size = sizeof(GEOMETRY);
+               geometry->type = BBOXONLYTYPE;
+               geometry->nobjs = -1;
+               geometry->SRID = SRID;
+               geometry->scale = 1.0;
+               geometry->offsetX = 0.0;
+               geometry->offsetY = 0.0;
+               memcpy(&(geometry->bvol),mybox, sizeof(BOX3D) );
+
+               pfree(mybox);
+               PG_RETURN_POINTER(geometry);
+       }
+
+
+       if ((str==NULL) || (strlen(str) == 0) )
+       {
+               elog(ERROR,"couldnt parse objects in GEOMETRY (null string)\n");
+               PG_RETURN_NULL() ;
+       }
+
+       // handle the 2 variants of MULTIPOINT - 'MULTIPOINT(0 0, 1 1)'::geometry and 'MULTIPOINT( (0 0), (1 1))'::geometry;
+
+               // we cheat - if its the 2nd variant, we replace the internal parethesis with spaces!
+                       if (strstr(str,"MULTIPOINT") != NULL )
+                       {
+                               //its a multipoint - replace any internal parenthesis with spaces
+                               char *first_paren;
+                               char *last_paren;
+                               char *current_paren;
+
+                               first_paren= strchr (str,'(');
+                               last_paren = strrchr(str,')');
+
+                               if  ( (first_paren == NULL) || (last_paren == NULL) || (first_paren >last_paren) )
+                               {
+                                               elog(ERROR,"couldnt parse objects in GEOMETRY (parenthesis related)\n");
+                                               PG_RETURN_NULL() ;
+                               }
+                               //now we can just got through the string
+                               for (current_paren = (first_paren+1); current_paren <(last_paren);current_paren++)
+                               {
+                                       if ( (current_paren[0] ==')') || (current_paren[0]=='(') )
+                                       {
+                                               current_paren[0] = ' ';
+                                       }
+                               }
+
+                       }
+
+
+//elog (NOTICE,"in:%s\n", str);
+
+//printf("geometry_in got string ''\n");
+
+       nobjs=  objects_inside(str);  //how many sub-objects
+       if (nobjs <=0)  //dont allow zero-object items
+       {
+               elog(ERROR,"couldnt parse objects in GEOMETRY\n");
+               PG_RETURN_NULL() ;
+       }
+
+//printf("geometry_in determins that there are %i sub-objects\n",nobjs);
+
+       //allocate enough space for info structures
+
+
+       objs = palloc(sizeof( char *) * nobjs);
+       memset(objs, 0, sizeof( char *) * nobjs);
+
+       obj_types = palloc(sizeof(int32) * nobjs);
+       memset(obj_types, 0, sizeof(int32) * nobjs);
+
+       obj_size  = palloc(sizeof(int32) * nobjs);
+       memset(obj_size, 0, sizeof(int32) * nobjs);
+
+       obj_parse_offset = 0; //start populating obj list at beginning
+
+
+//printf("     +about to parse the objects\n");
+
+
+       okay = parse_objects(obj_size,objs,obj_types,nobjs,str,&obj_parse_offset , &is3d);
+
+//printf("     +finished parsing object\n");
+
+       if (!(okay) || (obj_parse_offset != nobjs ) )
+       {
+               //free
+               for (t =0; t<nobjs; t++)
+               {
+                       if (objs[t] != NULL)
+                               pfree (objs[t]);
+               }
+               pfree(objs); pfree(obj_types); pfree(obj_size);
+               elog(ERROR,"couldnt parse object in GEOMETRY\n");
+               PG_RETURN_NULL();
+       }
+
+       entire_size = sizeof(GEOMETRY);
+
+       for (t =0; t<nobjs; t++)
+       {
+               //do they all have an obj_type?
+               if ( (obj_types[t] == 0) || (objs[t] == NULL) )
+               {
+                       okay = FALSE;
+               }
+               entire_size += obj_size[t];  //total space of the objects
+
+               //size seem reasonable?
+               if (obj_size[t] <1)
+               {
+                       okay = FALSE;
+               }
+
+       }
+       if (!(okay) )
+       {
+               for (t =0; t<nobjs; t++)
+               {
+                       if (objs[t] != NULL)
+                               pfree (objs[t]);
+               }
+               pfree(objs); pfree(obj_types); pfree(obj_size);
+               elog(ERROR,"couldnt parse object in GEOMETRY\n");
+               PG_RETURN_NULL();
+       }
+
+               //size = object size + space for object type & offset array and possible double-word boundary stuff
+       entire_size = entire_size + sizeof(int32) *nobjs*2 + nobjs*4;
+       geometry = (GEOMETRY *) palloc (entire_size);
+
+       geometry->size = entire_size;
+
+       //set collection type.  Need to differentiate between
+       //  geometrycollection and (say) multipoint
+
+       if (   strstr(str, "GEOMETRYCOLLECTION") != NULL  )
+               geometry->type = COLLECTIONTYPE;
+       else
+       {
+               if (   strstr(str, "MULTI") != NULL  )
+               {
+                       if (obj_types[0] == POINTTYPE)
+                               geometry->type = MULTIPOINTTYPE;
+                       if (obj_types[0] == LINETYPE)
+                               geometry->type = MULTILINETYPE;
+                       if (obj_types[0] == POLYGONTYPE)
+                               geometry->type = MULTIPOLYGONTYPE;
+               }
+               else
+               {
+                       geometry->type = obj_types[0]; //single object
+               }
+       }
+
+       geometry->is3d = is3d;    //any 3d points found anywhere?
+       geometry->nobjs = nobjs; // sub-objects
+
+
+       //copy in type and length info
+
+                       //where to put objects.  next_loc will point to (bytes forward) &objData[0]
+               next_loc = ( (char *) &(geometry->objType[0] ) - (char *) geometry);
+               next_loc += sizeof(int32) * 2* nobjs;
+
+               next_loc = MAXALIGN(next_loc);
+
+
+
+               //where is the offsets array
+               offsets = (int32 *) ( ((char *) &(geometry->objType[0] ))+ sizeof(int32) * nobjs ) ;
+
+
+//printf("structure is at %p, offsets is at %p, next_loc = %i\n",geometry, offsets, next_loc);
+
+       for (t=0; t<nobjs; t++) //for each object
+       {
+               geometry->objType[t] = obj_types[t];  //set its type
+
+               offsets[t] = next_loc;  //where is it
+//printf("copy %i bytes from %p to %p\n",      obj_size[t] ,  objs[t],(char *) geometry + next_loc);
+               memcpy( (char *) geometry + next_loc, objs[t], obj_size[t] );   //put sub-object into object
+               pfree(objs[t]); // free the original object (its redundant)
+
+               next_loc += obj_size[t];        //where does the next object go?
+
+               next_loc =  MAXALIGN(next_loc);
+
+
+       }
+
+       //free temporary structures
+       pfree(objs); pfree(obj_types); pfree(obj_size);
+
+
+       //calculate its bounding volume
+       the_bbox = bbox_of_geometry(geometry);
+
+       if (the_bbox != NULL)
+       {
+               memcpy( &geometry->bvol, the_bbox, sizeof(BOX3D) );
+               pfree(the_bbox);
+       }
+
+
+//printf("returning from geometry_in, nobjs = %i\n", nobjs);
+
+
+               geometry->SRID = SRID;
+               geometry->scale = scale;
+               geometry->offsetX = offx;
+               geometry->offsetY = offy;
+       PG_RETURN_POINTER(geometry);
+}
+
+PG_FUNCTION_INFO_V1(srid_geom);
+Datum srid_geom(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                   *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+       PG_RETURN_INT32(geom1->SRID);
+}
+
+
+//Take internal rep of geometry, output string in the form of
+//  'SRID=%i;<wkt>'  ie.  'SRID=5;POINT(1 1)'
+PG_FUNCTION_INFO_V1(geometry_out);
+Datum geometry_out(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                   *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               char                            *wkt;
+               char                            *result;
+               int                             len;
+
+               wkt = geometry_to_text(geom1);
+
+               len = strlen(wkt) + 6+ 25 + 1;
+               result = palloc(len);//extra space for SRID
+               memset(result, 0, len); //zero everything out
+
+               sprintf(result,"SRID=%i;%s",geom1->SRID,wkt);
+
+               pfree(wkt);
+
+               PG_RETURN_CSTRING(result);
+
+}
+
+//Take internal rep of geometry, output string
+PG_FUNCTION_INFO_V1(astext_geometry);
+Datum astext_geometry(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                   *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       char                       *wkt;
+       char                       *result;
+       int                     len;
+
+       wkt = geometry_to_text(geom1);
+
+                       //changed so it isnt null terminated (not needed with text)
+       len = strlen(wkt) + 4;
+
+       result= palloc(len);
+       *((int *) result) = len;
+
+       memcpy(result +4, wkt, len-4);
+
+       pfree(wkt);
+
+       PG_RETURN_CSTRING(result);
+}
+
+
+//Take internal rep of geometry, output string
+
+
+//takes a GEOMETRY and returns a WKT representation
+char *geometry_to_text(GEOMETRY  *geometry)
+{
+       char            *result;
+       int             t,u;
+       int32           *offsets;
+       char            *obj;
+       POINT3D *pts;
+       POLYGON3D       *polygon;
+       LINE3D  *line;
+       POINT3D *point;
+
+       int             pt_off,size;
+       bool            briefmode= TRUE; // ie. dont print out "POINT3D(..)", just print out the ".." part
+       bool            is3d;
+       bool            first_sub_obj = TRUE;
+       bool            multi_obj = FALSE;
+       int             mem_size,npts;
+
+
+       if (geometry->nobjs == 0)
+       {
+                       //empty geometry
+               result = (char*) palloc(30);
+
+               sprintf(result,"GEOMETRYCOLLECTION(EMPTY)");
+
+               if (geometry->type == MULTILINETYPE)
+                       sprintf(result,"MULTILINESTRING(EMPTY)");
+               if (geometry->type == MULTIPOINTTYPE)
+                       sprintf(result,"MULTIPOINT(EMPTY)");
+               if (geometry->type == MULTIPOLYGONTYPE)
+                       sprintf(result,"MULTIPOLYGON(EMPTY)");
+               return result;
+       }
+
+
+//printf("in geom_out(%p)\n",geometry);
+
+       size = 30;      //just enough to put in object type
+       result = (char *) palloc(30); mem_size= 30;       //try to limit number of repalloc()s
+
+       if (geometry->type == BBOXONLYTYPE)
+       {
+                       mem_size = MAX_DIGS_DOUBLE*6+5+2+4+5+1;
+                       pfree(result);
+                       result = (char *) palloc(mem_size); //double digits+ "BOX3D"+ "()" + commas +null
+                       sprintf(result, "BOX3D(%.15g %.15g %.15g,%.15g %.15g %.15g)",
+                                       geometry->bvol.LLB.x,geometry->bvol.LLB.y,geometry->bvol.LLB.z,
+                                       geometry->bvol.URT.x,geometry->bvol.URT.y,geometry->bvol.URT.z);
+               return result;
+       }
+
+
+       if (geometry->type == POINTTYPE)
+       {
+               multi_obj = FALSE;
+               sprintf(result, "POINT(" );
+       }
+       else if (geometry->type == LINETYPE)
+       {
+               multi_obj = FALSE;
+               sprintf(result, "LINESTRING" );
+       }
+       else if (geometry->type == POLYGONTYPE)
+       {
+               multi_obj = FALSE;
+               sprintf(result, "POLYGON" );
+       }
+       else if (geometry->type == MULTIPOINTTYPE)
+       {
+               //multiple sub-object need to be put into one object
+               multi_obj = TRUE;
+               sprintf(result, "MULTIPOINT(" );
+       }
+       else if (geometry->type == MULTILINETYPE)
+       {
+               //multiple sub-object need to be put into one object
+               multi_obj = TRUE;
+               sprintf(result, "MULTILINESTRING(" );
+       }
+       else if (geometry->type == MULTIPOLYGONTYPE)
+       {
+               //multiple sub-object need to be put into one object
+               multi_obj = TRUE;
+               sprintf(result, "MULTIPOLYGON(" );
+       }
+       else if (geometry->type == COLLECTIONTYPE)
+       {
+               sprintf(result, "GEOMETRYCOLLECTION(" );
+               briefmode = FALSE;
+               multi_obj = FALSE;
+       }
+
+               //where are the objects?
+       offsets = (int32 *) ( ((char *) &(geometry->objType[0] ))+ sizeof(int32) * geometry->nobjs ) ;
+
+       is3d = geometry->is3d;
+
+
+       for(t=0;t<geometry->nobjs; t++)  //for each object
+       {
+               obj = (char *) geometry +offsets[t] ;
+
+               if (geometry->objType[t] == 1)   //POINT
+               {
+                       point = (POINT3D *) obj;
+                       if (briefmode)  //dont need to put in "POINT("
+                       {
+                               size +=MAX_DIGS_DOUBLE*3 + 2 +3  ;
+                               result = repalloc(result, size );  //make memory bigger
+                               if (!(first_sub_obj))
+                               {
+                                       strcat(result,",");
+                               }
+                               else
+                               {
+                                       first_sub_obj = FALSE;
+                               }
+                               print_point(result, point,is3d);  //render point
+                               if (t == (geometry->nobjs-1))
+                                       strcat(result,")");     // need to close object?
+                       }
+                       else
+                       {
+                               size +=MAX_DIGS_DOUBLE*3 + 2 +3 +7  ;
+                               result = repalloc(result, size  );
+                               strcat(result, "POINT(");
+                               print_point(result, point,is3d);
+                               strcat(result, ")");
+                               if (t != (geometry->nobjs -1) )
+                                       strcat(result,",");
+                       }
+
+               }
+               if (geometry->objType[t] == 2)  //LINESTRING
+               {
+                       line = (LINE3D *) obj;
+                       if (briefmode)
+                       {
+                               size +=(MAX_DIGS_DOUBLE*3+5)*line->npoints +3;
+                               result = repalloc(result, size );
+#ifdef DEBUG_TOTEXT
+elog(NOTICE, "Added space for %d points, result size: %d", line->npoints, size);
+elog(NOTICE, "Strlen result(%x): %d", result, strlen(result));
+#endif
+
+                               if (!(first_sub_obj))
+                               {
+                                       strcat(result,",");
+                               }
+                               else
+                               {
+                                       first_sub_obj = FALSE;
+                               }
+
+                               strcat(result,"(");
+                               print_many_points(result, &line->points[0],line->npoints , is3d);
+                               strcat(result,")");
+                               if ((t == (geometry->nobjs-1)) && multi_obj )  //close object?
+                                       strcat(result,")");
+#ifdef DEBUG_TOTEXT
+elog(NOTICE, "RESULT: %s", result);
+#endif
+                       }
+                       else
+                       {
+                               size +=(MAX_DIGS_DOUBLE*3+5)*line->npoints +12+3;
+                               result = repalloc(result, size );
+                               strcat(result, "LINESTRING(");
+
+                               //optimized for speed
+
+                               print_many_points(result, &line->points[0],line->npoints , is3d);
+                               strcat(result,")");
+                               if (t != (geometry->nobjs -1) )
+                                       strcat(result,",");
+
+                       }
+               }
+               if (geometry->objType[t] == 3)          //POLYGON
+               {
+                               polygon = (POLYGON3D *) obj;
+                               if (!(briefmode))
+                               {
+                                       size += 7 + polygon->nrings *3+9;
+                                       result = repalloc(result, size );
+                                       strcat(result,"POLYGON");
+                               }
+                               else
+                               {
+                                       size += 7 + polygon->nrings *3;
+                                       result = repalloc(result, size );
+                               }
+
+                               //need to separate objects?
+                               if (!(first_sub_obj))
+                               {
+                                       strcat(result,",");
+                               }
+                               else
+                               {
+                                       first_sub_obj = FALSE;
+                               }
+
+
+                               strcat(result,"(");     //begin poly
+
+                               pt_off = 0;     //where is the first point in this ring?
+
+                                       //where are the points
+                               pts = (POINT3D *) ( (char *)&(polygon->npoints[polygon->nrings] )  );
+                               pts = (POINT3D *) MAXALIGN(pts);
+
+
+                               npts = 0;
+                               for (u=0; u< polygon->nrings ; u++)
+                                       npts += polygon->npoints[u];
+
+                               size += (MAX_DIGS_DOUBLE*3+3) * npts + 5* polygon->nrings;
+                               result = repalloc(result, size );
+
+                               for (u=0; u< polygon->nrings ; u++)  //for each ring
+                               {
+//printf("making ring #%i in obj, %i\n",u,t);
+                                       if (u!=0)
+                                               strcat(result,","); //separate rings
+
+                                       strcat(result,"(");     //begin ring
+                                       print_many_points(result, &pts[pt_off] ,polygon->npoints[u], is3d);
+
+                                       pt_off = pt_off + polygon->npoints[u]; //where is first point of next ring?
+                                       strcat(result,")");     //end ring
+                               }
+                               strcat(result,")"); //end poly
+                               if ((t == (geometry->nobjs-1)) && multi_obj )
+                                       strcat(result,")");
+                               if ((!(briefmode)) && (t != (geometry->nobjs -1) ))
+                                       strcat(result,",");
+               }
+               if (!(briefmode))
+               {
+                       first_sub_obj = TRUE;
+               }
+
+       }
+
+       if (!(briefmode))
+               strcat(result,")");
+       return(result);
+}
+
+
+/*****************************************************
+ * conversion routines
+ *****************************************************/
+
+
+
+
+
+//get bvol inside a geometry
+PG_FUNCTION_INFO_V1(get_bbox_of_geometry);
+Datum get_bbox_of_geometry(PG_FUNCTION_ARGS)
+{
+       GEOMETRY  *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+       BOX3D    *result;
+
+       if (geom->nobjs == 0)
+               PG_RETURN_NULL();
+
+       //make a copy of it
+
+       result = palloc ( sizeof(BOX3D) );
+       memcpy(result,&geom->bvol, sizeof(BOX3D) );
+
+
+       PG_RETURN_POINTER(result);
+}
+
+
+//make a fake geomery with just it a bvol
+PG_FUNCTION_INFO_V1(get_geometry_of_bbox);
+Datum get_geometry_of_bbox(PG_FUNCTION_ARGS)
+{
+       BOX3D  *bbox = (BOX3D *) PG_GETARG_POINTER(0);
+       GEOMETRY  *geom   ;
+
+       //make a copy of it
+
+       geom = palloc ( sizeof(GEOMETRY) );
+       geom->size = sizeof(GEOMETRY);
+       geom->type = BBOXONLYTYPE;
+       geom->nobjs = -1;
+       geom->SRID = -1;
+       geom->scale = 1.0;
+       geom->offsetX = 0.0;
+       geom->offsetY = 0.0;
+       memcpy(&(geom->bvol),bbox, sizeof(BOX3D) );
+
+
+       PG_RETURN_POINTER(geom);
+}
+
+
+
+
+//*****************************************************************
+//WKB does not do any padding or alignment!
+
+// in general these function take
+//     1. geometry item or list
+//     2. if the endian-ness should be changed (flipbytes)
+//     3. byte_order - 0=big endian (XDR) 1= little endian (NDR)
+//    4. use the 3d extention wkb (use3d)
+//
+// NOTE:
+//             A. mem - if null, function pallocs memory for result
+//                      - otherwise, use mem to dump the wkb into
+//             b. for lists, the # of sub-objects is also specified
+//
+//  and return
+//     1. size of the wkb structure ("int *size")
+//     2. the actual wkb structure
+//*****************************************************************
+
+//convert a POINT3D into wkb
+char   *wkb_point(POINT3D *pt,int32 *size, bool flipbytes, char byte_order, bool use3d)
+{
+       char            *result ;
+       uint32  type ;
+       char            *c_type = (char *) &type;
+
+       if (use3d)
+       {
+               *size = 29;
+               type = ((unsigned int) WKB3DOFFSET + ((unsigned int)1));
+       }
+       else
+       {
+               *size = 21;
+               type =  1;
+       }
+
+       result = palloc (*size);
+
+       if (flipbytes)
+               flip_endian_int32( (char *) &type);
+
+       result[0] = byte_order;
+       result[1] = c_type[0];
+       result[2] = c_type[1];
+       result[3] = c_type[2];
+       result[4] = c_type[3];
+
+       if (use3d)
+               memcpy(&result[5], pt, 24 ); //copy in point data
+       else
+               memcpy(&result[5], pt, 16 ); //copy in point data
+
+       if (flipbytes)
+       {
+               flip_endian_double((char *) &result[5]);                // x
+               flip_endian_double((char *) &result[5+8]);      // y
+               if (use3d)
+                       flip_endian_double((char *) &result[5+16]);     // z
+       }
+
+       return result;
+}
+
+//pt is a pointer to list of POINT3Ds
+char   *wkb_multipoint(POINT3D *pt,int32 numb_points,int32 *size, bool flipbytes, char byte_order,bool use3d)
+{
+       char *result;
+       uint32  type ;          // 3D/2D multipoint, following frank's spec
+       uint32  npoints = numb_points;
+       char            *c_type = (char *) &type;
+       char            *c_npoints = (char *) &npoints;
+       int             t;
+       int32           junk;
+       const int               sub_size_3d = 29;
+       const int               sub_size_2d = 21;
+
+
+
+
+       if (use3d)
+       {
+               *size = 9 + sub_size_3d*numb_points;
+               type = WKB3DOFFSET + 4;
+       }
+       else
+       {
+               *size = 9 + sub_size_2d*numb_points;
+               type =  4;
+       }
+
+
+       result = palloc (*size);
+       npoints = numb_points;
+
+       if (flipbytes)
+       {
+               flip_endian_int32((char *) &type);
+               flip_endian_int32((char *) &npoints);
+       }
+
+
+       result[0] = byte_order;
+       result[1] = c_type[0];
+       result[2] = c_type[1];
+       result[3] = c_type[2];
+       result[4] = c_type[3];
+
+       result[5] = c_npoints[0];
+       result[6] = c_npoints[1];
+       result[7] = c_npoints[2];
+       result[8] = c_npoints[3];
+
+       for (t=0; t<numb_points;t++)  //for each point
+       {
+               if (use3d)
+                       memcpy(&result[9+ t*sub_size_3d],
+                                       wkb_point(&pt[t],&junk,  flipbytes,  byte_order,use3d)
+                                               , sub_size_3d);
+               else
+                       memcpy(&result[9+ t*sub_size_2d],
+                                       wkb_point(&pt[t],&junk,  flipbytes,  byte_order,use3d)
+                                                       , sub_size_2d);
+       }
+
+       return result;
+
+}
+
+
+//if mem != null, it must have enough space for the wkb
+char   *wkb_line(LINE3D *line,int32 *size, bool flipbytes, char byte_order,bool use3d, char *mem)
+{
+       char *result;
+       uint32  type ;
+
+       char            *c_type = (char *) &type;
+       int             t;
+
+       uint32  npoints = line->npoints;
+       int             numb_points = line->npoints;
+       char            *c_npoints = (char *) &npoints;
+
+
+       if (use3d)
+       {
+               *size = 9 + 24*numb_points;
+               type = WKB3DOFFSET + 2;
+       }
+       else
+       {
+               *size = 9 + 16*numb_points;
+               type =  2;
+       }
+
+
+
+       if (flipbytes)
+       {
+               flip_endian_int32((char *) &type);
+               flip_endian_int32((char *) &npoints);
+       }
+
+       if (mem == NULL)
+               result = palloc (*size);
+       else
+               result = mem;
+
+       result[0] = byte_order;
+       result[1] = c_type[0];          //type
+       result[2] = c_type[1];
+       result[3] = c_type[2];
+       result[4] = c_type[3];
+
+       result[5] = c_npoints[0];       //npoints
+       result[6] = c_npoints[1];
+       result[7] = c_npoints[2];
+       result[8] = c_npoints[3];
+
+       if (use3d)
+       {
+               memcpy(&result[9],&line->points, line->npoints*24); // copy all the pt data in one chuck
+               if (flipbytes)
+               {
+                       for (t=0;t<line->npoints; t++)
+                       {
+                               flip_endian_double((char *) &result[9   + t*24]);       // x
+                               flip_endian_double((char *) &result[9+8 + t*24]);       // y
+                               flip_endian_double((char *) &result[9+16+ t*24]);       // z
+                       }
+               }
+       }
+       else
+       {
+               for (t=0; t<numb_points;t++)
+               {
+                       memcpy(&result[9+16*t],&((&line->points)[t]), 16);  //need to copy each pt individually
+                       if (flipbytes)
+                       {
+                               flip_endian_double((char *) &result[9   + t*16]);       // x
+                               flip_endian_double((char *) &result[9+8 + t*16]);       // y
+                       }
+               }
+       }
+       return result;
+}
+
+// expects an array of pointers to lines
+// calls wkb_line() to do most of the work
+char   *wkb_multiline(LINE3D **lines,int32 *size, int numb_lines, bool flipbytes, char byte_order,bool use3d)
+{
+       int     total_points = 0;
+       int     total_size=0;
+       int     size_part = 0;
+
+       int     t;
+       char    *result,*new_spot;
+
+
+       uint32  type ;
+       char            *c_type = (char *) &type;
+
+       uint32  nlines = numb_lines;
+       char            *c_nlines = (char *) &nlines;
+
+       if (use3d)
+       {
+               type = WKB3DOFFSET + 5;
+       }
+       else
+       {
+               type =  5;
+       }
+
+       if (flipbytes)
+       {
+               flip_endian_int32((char *) &type);
+               flip_endian_int32((char *) &nlines);
+       }
+
+       //how many points in the entire multiline()
+       for (t=0;t<numb_lines; t++)
+       {
+               total_points += lines[t]->npoints;
+       }
+
+
+       if (use3d)
+       {
+               total_size = 9+ 9*numb_lines + 24*total_points;
+       }
+       else
+       {
+               total_size = 9 + 9*numb_lines + 16*total_points;
+       }
+
+       *size = total_size;
+
+       result = palloc (total_size);
+
+
+
+       result[0] = byte_order;
+       result[1] = c_type[0];          //type
+       result[2] = c_type[1];
+       result[3] = c_type[2];
+       result[4] = c_type[3];
+
+       result[5] = c_nlines[0];        //num linestrings
+       result[6] = c_nlines[1];
+       result[7] = c_nlines[2];
+       result[8] = c_nlines[3];
+
+
+
+       new_spot = result + 9;//where to put the next linestring
+
+               for (t=0;t<numb_lines;t++)  //for each linestring
+               {
+                       wkb_line( lines[t], &size_part, flipbytes, byte_order, use3d, new_spot);
+                       new_spot += size_part; //move to next slot
+               }
+       return result;
+}
+
+
+//mem should be zero or hold enough space
+char   *wkb_polygon(POLYGON3D  *poly,int32 *size, bool flipbytes, char byte_order,bool use3d, char *mem)
+{
+       int     t, u,total_points =0;
+       uint32  type ;
+       char            *c_type = (char *) &type;
+       uint32  numRings,npoints;
+       char            *c_numRings = (char *) &numRings;
+       char            *c_npoints = (char *) &npoints;
+       int             offset;
+       POINT3D *pts;
+       int             point_offset;
+       char    *result;
+
+
+       //where the polygon's points are (double aligned)
+
+       pts = (POINT3D *) ( (char *)&(poly->npoints[poly->nrings] )  );
+       pts = (POINT3D *) MAXALIGN(pts);
+
+
+
+       numRings = poly->nrings;
+
+       //total points in all the rings
+       for (t=0; t< poly->nrings; t++)
+       {
+               total_points += poly->npoints[t];
+       }
+
+       if (use3d)
+       {
+               *size = 9 + 4*poly->nrings + 24*total_points;
+               type = WKB3DOFFSET + 3;
+       }
+       else
+       {
+               *size = 9 + 4*poly->nrings + 16*total_points;
+               type = 3;
+       }
+
+       if (mem == NULL)
+               result = palloc (*size);
+       else
+               result = mem;
+
+
+       if (flipbytes)
+       {
+               flip_endian_int32((char *) &type);
+               flip_endian_int32((char *) &numRings);
+       }
+
+
+
+       result[0] = byte_order;
+       result[1] = c_type[0];          //type
+       result[2] = c_type[1];
+       result[3] = c_type[2];
+       result[4] = c_type[3];
+
+       result[5] = c_numRings[0];      //npoints
+       result[6] = c_numRings[1];
+       result[7] = c_numRings[2];
+       result[8] = c_numRings[3];
+
+
+       if (use3d)
+       {
+               offset = 9; //where the actual linearring structure is going
+               point_offset = 0;
+               for (t=0;t<poly->nrings; t++) //for each ring
+               {
+                       npoints = poly->npoints[t];
+                       if (flipbytes)
+                               flip_endian_int32((char *) &npoints);
+                       result[offset]   = c_npoints[0];  //num points in this ring
+                       result[offset+1] = c_npoints[1];
+                       result[offset+2] = c_npoints[2];
+                       result[offset+3] = c_npoints[3];
+
+                       memcpy(&result[offset+4], &pts[point_offset], 24*poly->npoints[t]);
+                       if (flipbytes)
+                       {
+                               for (u=0;u<poly->npoints[t];u++)  //for each point
+                               {
+                                       flip_endian_double((char *) &result[offset+4+u*24]   );
+                                       flip_endian_double((char *) &result[offset+4+u*24+8] );
+                                       flip_endian_double((char *) &result[offset+4+u*24]+16);
+                               }
+                       }
+                       point_offset += poly->npoints[t];     // reset offsets
+                       offset +=  4 +  poly->npoints[t]* 24;
+               }
+       }
+       else
+       {
+               offset = 9; // where the 1st linearring goes
+               point_offset = 0;
+               for (t=0;t<poly->nrings; t++)
+               {
+                       npoints = poly->npoints[t];
+                       if (flipbytes)
+                               flip_endian_int32((char *) &npoints);
+                       result[offset]   = c_npoints[0];
+                       result[offset+1] = c_npoints[1];
+                       result[offset+2] = c_npoints[2];
+                       result[offset+3] = c_npoints[3];
+
+                               for (u=0;u<poly->npoints[t];u++)
+                               {
+                                       memcpy(&result[offset+4 + u*16], &pts[point_offset+u], 16);
+                                       if (flipbytes)
+                                       {
+                                               flip_endian_double((char *) &result[offset+4+u*16]   );
+                                               flip_endian_double((char *) &result[offset+4+u*16+8] );
+                                       }
+                               }
+
+                       point_offset += poly->npoints[t];
+                       offset +=  4 +  poly->npoints[t]* 16;
+               }
+
+       }
+       return result;
+}
+
+
+//expects a list of pointer to polygon3d
+char   *wkb_multipolygon(POLYGON3D     **polys,int numb_polys,int32 *size, bool flipbytes, char byte_order,bool use3d)
+{
+
+       int     t,u;
+       int     total_points = 0;
+       int     total_size=0;
+       int     size_part = 0;
+       int     total_rings = 0;
+       char    *result,*new_spot;
+       uint32  type;
+
+       char            *c_type = (char *) &type;
+
+       uint32  npolys = numb_polys;
+       char            *c_npolys = (char *) &npolys;
+
+       if (use3d)
+       {
+               type = WKB3DOFFSET + 6;
+       }
+       else
+       {
+               type =  6;
+       }
+
+
+       if (flipbytes)
+       {
+               flip_endian_int32( (char *) &type);
+               flip_endian_int32((char *) &npolys);
+       }
+
+
+
+
+       //find total # rings and total points
+       for (t=0;t<numb_polys; t++)
+       {
+               total_rings += polys[t]->nrings;
+               for (u=0;u<polys[t]->nrings; u++)
+               {
+                       total_points += polys[t]->npoints[u];
+               }
+       }
+
+
+       if (use3d)
+       {
+               total_size =9 +  9*numb_polys + 24*total_points + 4*total_rings;
+       }
+       else
+       {
+               total_size = 9 + 9*numb_polys + 16*total_points + 4*total_rings;
+       }
+
+       *size = total_size;
+
+       result = palloc (total_size);
+
+       result[0] = byte_order;
+       result[1] = c_type[0];          //type
+       result[2] = c_type[1];
+       result[3] = c_type[2];
+       result[4] = c_type[3];
+
+       result[5] = c_npolys[0];        //number of polygons
+       result[6] = c_npolys[1];
+       result[7] = c_npolys[2];
+       result[8] = c_npolys[3];
+
+
+
+       new_spot = result+9;  //where 1st polygon goes
+
+               for (t=0;t<numb_polys;t++)  //for each polygon
+               {
+                       wkb_polygon( polys[t], &size_part, flipbytes, byte_order, use3d, new_spot);
+                       new_spot +=  size_part; //more to next slot
+               }
+       return result;
+
+}
+
+
+//passes work off to the correct function
+// then makes a simple postgres type - the first 4 bytes is an int32 with the size of the structure
+// and the rest is the wkb info.
+//
+// 3d-ness is determined by looking at the is3d flag in the GEOMETRY structure
+
+char   *to_wkb(GEOMETRY *geom, bool flip_endian)
+{
+       char    *result, *sub_result;
+       int     size;
+
+       if (geom->type == COLLECTIONTYPE)
+       {
+               sub_result=     to_wkb_collection(geom,  flip_endian, &size);
+               size += 4; //size info
+               result = palloc (size );
+               memcpy(result+4, sub_result, size-4);
+               memcpy(result, &size, 4);
+               pfree(sub_result);
+               return result;
+       }
+       else
+       {
+               sub_result=     to_wkb_sub(geom,  flip_endian, &size);
+               size += 4; //size info
+               result = palloc (size );
+               memcpy(result+4, sub_result, size-4);
+               memcpy(result, &size, 4);
+               pfree(sub_result);
+               return result;
+       }
+
+}
+
+//make a wkb chunk out of a geometrycollection
+char *to_wkb_collection(GEOMETRY *geom, bool flip_endian, int32 *end_size)
+{
+       char    *result, **sub_result;
+       int     sub_size;
+       int     *sizes;
+       int     t;
+       int32           *offsets1;
+
+       int32           type;
+       int             total_size =0;
+       int             offset;
+       char            byte_order;
+       int             coll_type=7;
+       char            *c_type = (char *)&coll_type;
+       int             nobj = geom->nobjs;
+       char            *c_nobj = (char *) &nobj;
+
+//printf("making a wkb of a collections\n");
+
+
+       //determing byte order and if numbers need endian change
+       if (BYTE_ORDER == BIG_ENDIAN)
+       {
+               if (flip_endian)
+               {
+                       byte_order = 1;
+               }
+               else
+               {
+                       byte_order = 0;
+               }
+       }
+       else
+       {
+               if (flip_endian)
+               {
+                       byte_order = 0;
+               }
+               else
+               {
+                       byte_order = 1;
+               }
+       }
+
+       //for sub part of the geometry structure
+       offsets1 = (int32 *) ( ((char *) &(geom->objType[0] ))+ sizeof(int32) * geom->nobjs ) ;
+
+
+       // we make a list of smaller wkb chunks in sub_result[]
+       // and that wkb chunk's size in sizes[]
+
+       sub_result = NULL;
+       if (geom->nobjs >0)
+               sub_result = palloc( sizeof(char *) * geom->nobjs);
+       sizes = NULL;
+       if (geom->nobjs >0)
+               sizes = palloc( sizeof(int) * geom->nobjs);
+
+
+       for (t=0; t<geom->nobjs; t++)   //for each part of the collections, do the work in another function
+       {
+               type = geom->objType[t];
+
+               if (type == POINTTYPE)
+               {
+                       sub_result[t] = (  wkb_point((POINT3D *) ((char *) geom +offsets1[t] ) ,
+                                       &sub_size, flip_endian, byte_order, geom->is3d));
+                       sizes[t] = sub_size;
+                       total_size += sub_size;
+               }
+
+               if (type == LINETYPE)
+               {
+
+                       sub_result[t] = (  wkb_line((LINE3D *) ((char *) geom +offsets1[t] ) ,
+                                &sub_size, flip_endian, byte_order, geom->is3d, NULL));
+                       sizes[t] = sub_size;
+                       total_size += sub_size;
+               }
+               if (type == POLYGONTYPE)
+               {
+
+                       sub_result[t] = (  wkb_polygon((POLYGON3D *) ((char *) geom +offsets1[t] ) ,
+                                &sub_size, flip_endian, byte_order, geom->is3d, NULL));
+                       sizes[t] = sub_size;
+                       total_size += sub_size;
+               }
+       }
+
+       result = palloc( total_size +9); // need geometrycollection header
+
+       if (geom->is3d)
+               coll_type += WKB3DOFFSET;
+
+       if (flip_endian)
+       {
+               flip_endian_int32((char *) &coll_type);
+               flip_endian_int32((char *) &nobj);
+       }
+
+       //could use a memcpy, but...
+
+
+
+       result[0] = byte_order;
+       result[1] = c_type[0];          //type
+       result[2] = c_type[1];
+       result[3] = c_type[2];
+       result[4] = c_type[3];
+
+       result[5] = c_nobj[0];  //number of part to collection
+       result[6] = c_nobj[1];
+       result[7] = c_nobj[2];
+       result[8] = c_nobj[3];
+
+
+       offset =9;  //start after the geometrycollection header
+
+
+       for (t=0;t<geom->nobjs; t++)
+       {
+               memcpy(result+ offset, sub_result[t], sizes[t]);
+               pfree(sub_result[t]);
+               offset += sizes[t];
+       }
+
+               //free temp structures
+       if (sub_result != NULL)
+               pfree( sub_result);
+       if (sizes != NULL)
+               pfree( sizes);
+
+               //total size of the wkb
+       *end_size = total_size+9;
+
+
+
+       //decode_wkb(result, &junk);
+       return result;
+}
+
+
+//convert geometry to WKB, flipping endian if appropriate
+//
+//  This handles POINT, LINESTRING, POLYGON, MULTIPOINT,MULTILINESTRING, MULTIPOLYGON
+//
+// GEOMETRYCOLLECTION is not handled by this function see to_wkb_collection()
+//
+// flip_endian - request an endian swap (relative to current machine)
+// wkb_size    - return size of wkb
+
+char   *to_wkb_sub(GEOMETRY *geom, bool flip_endian, int32 *wkb_size)
+{
+
+       char    byte_order;
+       char    *result = NULL;
+       int             t;
+
+
+       int32           *offsets1;
+       LINE3D  **linelist;
+       POLYGON3D       **polylist;
+
+
+       //determine WKB byte order flag
+       if (BYTE_ORDER == BIG_ENDIAN)   //server is running on a big endian machine
+       {
+               if (flip_endian)
+               {
+                       byte_order = 1;  //as per WKB specification
+               }
+               else
+               {
+                       byte_order = 0;
+               }
+       }
+       else
+       {
+               if (flip_endian)
+               {
+                       byte_order = 0;
+               }
+               else
+               {
+                       byte_order = 1;
+               }
+       }
+
+       // for searching in the geom struct
+       offsets1 = (int32 *) ( ((char *) &(geom->objType[0] ))+ sizeof(int32) * geom->nobjs ) ;
+
+
+               // for each of the possible geometry types, we pass it off to a worker function
+
+       if (geom->type == POINTTYPE)
+       {
+
+               result = (  wkb_point((POINT3D *) ((char *) geom +offsets1[0] ) ,
+                                       wkb_size, flip_endian, byte_order, geom->is3d));
+       }
+
+       if (geom->type == MULTIPOINTTYPE)
+       {
+
+               result = (  wkb_multipoint((POINT3D *) ((char *) geom +offsets1[0] ) ,
+                               geom->nobjs,    wkb_size, flip_endian, byte_order, geom->is3d));
+       }
+
+       if (geom->type == LINETYPE)
+       {
+
+               result = (  wkb_line((LINE3D *) ((char *) geom +offsets1[0] ) ,
+                                wkb_size, flip_endian, byte_order, geom->is3d, NULL));
+       }
+
+
+       if (geom->type == MULTILINETYPE)
+       {
+               //make a list of lines
+               linelist = NULL;
+               if (geom->nobjs >0)
+                       linelist = palloc( sizeof(LINE3D *) * geom->nobjs);
+               for (t=0;t<geom->nobjs; t++)
+               {
+                       linelist[t] = (LINE3D *) ((char *) geom +offsets1[t] ) ;
+               }
+               result = (  wkb_multiline( linelist,
+                                wkb_size, geom->nobjs, flip_endian, byte_order, geom->is3d));
+       }
+
+       if (geom->type == POLYGONTYPE)
+       {
+
+               result = (  wkb_polygon((POLYGON3D *) ((char *) geom +offsets1[0] ) ,
+                                wkb_size, flip_endian, byte_order, geom->is3d, NULL));
+       }
+
+
+       if (geom->type == MULTIPOLYGONTYPE)
+       {
+               //make a list of polygons
+               polylist = NULL;
+               if (geom->nobjs >0)
+                       polylist = palloc( sizeof(POLYGON3D *) * geom->nobjs);
+               for (t=0;t<geom->nobjs; t++)
+               {
+                       polylist[t] = (POLYGON3D *) ((char *) geom +offsets1[t] ) ;
+               }
+               result = (  wkb_multipolygon( polylist,geom->nobjs,
+                                wkb_size,  flip_endian, byte_order, geom->is3d));
+       }
+
+       //decode_wkb(result, &junk);
+
+       return (result);
+}
+
+
+//convert binary geometry into OGIS well know binary format with NDR (little endian) formating
+// see http://www.opengis.org/techno/specs/99-049.rtf page 3-24 for specification
+//
+// 3d geometries are encode as in OGR by adding WKB3DOFFSET to the type.  Points are then 24bytes (X,Y,Z)
+// instead of 16 bytes (X,Y)
+//
+//dont do any flipping of endian   asbinary_simple(GEOMETRY)
+PG_FUNCTION_INFO_V1(asbinary_simple);
+Datum asbinary_simple(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                      *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+       PG_RETURN_POINTER(to_wkb(geom, FALSE));
+}
+
+
+//convert binary geometry into OGIS well know binary format with NDR (little endian) formating
+// see http://www.opengis.org/techno/specs/99-049.rtf page 3-24 for specification
+//
+// 3d geometries are encode as in OGR by adding WKB3DOFFSET to the type.  Points are then 24bytes (X,Y,Z)
+// instead of 16 bytes (X,Y)
+//
+//flip if required    asbinary_specify(GEOMETRY,'xdr') or asbinary_specify(GEOMETRY,'ndr')
+PG_FUNCTION_INFO_V1(asbinary_specify);
+Datum asbinary_specify(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                      *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+                text                           *type = PG_GETARG_TEXT_P(1);
+
+
+       if (VARSIZE(type) < 7)  // 4 (size header) + 3 (text length)
+       {
+               elog(ERROR,"asbinary(geometry, <type>) - type should be 'XDR' or 'NDR'.  type length is %i",VARSIZE(type) -4);
+               PG_RETURN_NULL();
+       }
+
+       if  ( ( strncmp(VARDATA(type) ,"xdr",3) == 0 ) || (strncmp(VARDATA(type) ,"XDR",3) == 0) )
+       {
+//printf("requested XDR\n");
+               if (BYTE_ORDER == BIG_ENDIAN)
+                       PG_RETURN_POINTER(to_wkb(geom, FALSE));
+               else
+                       PG_RETURN_POINTER(to_wkb(geom, TRUE));
+       }
+       else
+       {
+//printf("requested NDR\n");
+               if (BYTE_ORDER == LITTLE_ENDIAN)
+                       PG_RETURN_POINTER(to_wkb(geom, FALSE));
+               else
+                       PG_RETURN_POINTER(to_wkb(geom, TRUE));
+       }
+}
+
+//takes a text argument and parses it to make a geometry
+PG_FUNCTION_INFO_V1(geometry_text);
+Datum geometry_text(PG_FUNCTION_ARGS)
+{
+       char            *input = (char *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       char            *cstring;
+       void            *result;
+
+       if (*((int *) input) == 4)
+       {
+               //empty string
+               PG_RETURN_NULL();
+       }
+       cstring = palloc(*((int *) input));  // length+4
+
+       memcpy(cstring, &input[4], ( *((int *) input))-4);
+       cstring[( *((int *) input))-4] = 0; // nullterminate
+
+       result = DatumGetPointer(       DirectFunctionCall1(geometry_in,PointerGetDatum(cstring))) ;
+
+       PG_RETURN_POINTER (result);
+//NOTE: this could cause problem since input isnt null terminated - usually this will not
+//      cause a problem because the parse will not go further than the end of the string.
+//       FIXED.
+}
+
+
+
+//make a geometry with one obj in it (of size new_obj_size)
+// type should be POINTTYPE, LINETYPE or POLYGONTYPE
+// if you want to change the object's type to something else (ie GEOMETRYCOLLECTION), do
+// that after with geom->type = GEOMETRYCOLLECTION
+// this does  calculate the bvol
+//
+// sets the SRID and grid info to the given values
+//
+// do not call this with type = GEOMETRYCOLLECTION
+
+GEOMETRY       *make_oneobj_geometry(int sub_obj_size, char *sub_obj, int type, bool is3d,int SRID, double scale, double offx, double offy)
+{
+       int size = sizeof(GEOMETRY) + 4 + sub_obj_size ;
+       GEOMETRY        *result = palloc(size);
+       char            *sub_obj_loc;
+       BOX3D           *bbox;
+
+       result->size = size;
+       result->nobjs = 1;
+       result->type = type;
+       result->is3d = is3d;
+
+
+       result->SRID = SRID;
+       result->scale = scale;
+       result->offsetX = offx;
+       result->offsetY = offy;
+
+       result->objType[0] = type;
+       if (type == MULTIPOINTTYPE)
+               result->objType[0] = POINTTYPE;
+       if (type == MULTILINETYPE)
+               result->objType[0] = LINETYPE;
+       if (type == MULTIPOLYGONTYPE)
+               result->objType[0] = POLYGONTYPE;
+
+       if (type == COLLECTIONTYPE)
+       {
+               pfree(result);
+               return(NULL); //error
+       }
+
+       sub_obj_loc = (char *)  &result->objType[2];
+       sub_obj_loc  = (char *) MAXALIGN(sub_obj_loc);
+
+       result->objType[1] = sub_obj_loc  - (char *) result; //result->objType[1] is where objOffset is
+
+       //copy in the subobject
+       memcpy(sub_obj_loc , sub_obj, sub_obj_size);
+
+       bbox = bbox_of_geometry(result);
+       memcpy(&result->bvol,bbox, sizeof(BOX3D) ); //make bounding box
+
+       return result;
+}
+
+
+//find the size of the subobject and return it
+int    size_subobject (char *sub_obj, int type)
+{
+       if (type == POINTTYPE)
+       {
+               return (sizeof(POINT3D));
+       }
+       if (type == LINETYPE)
+       {
+               /* size of first point is included in struct LINE3D */
+               return(sizeof(LINE3D) +  sizeof(POINT3D) * ( ((LINE3D *)sub_obj)->npoints - 1 ));
+       }
+       if (type==POLYGONTYPE)
+       {
+               POLYGON3D       *poly = (POLYGON3D *) sub_obj;
+               int             t,points=0;
+
+               for (t=0;t<poly->nrings;t++)
+               {
+                       points += poly->npoints[t];
+               }
+               if  (   ( (long) ( &poly->npoints[poly->nrings] )) == (MAXALIGN(&poly->npoints[poly->nrings] ) ) )
+                       return (sizeof(POLYGON3D) + 4*(poly->nrings-1) +  sizeof(POINT3D)*(points-1) );  //no extra align
+               else
+                       return (sizeof(POLYGON3D) + 4*(poly->nrings-1) +  sizeof(POINT3D)*(points-1) +4 );
+       }
+
+       return (-1);//unknown sub-object type
+}
+
+
+//produce a new geometry, which is the old geometry with another object stuck in it
+// This will try to make the geometry's type is correct (move POINTTYPE to MULTIPOINTTYPE or
+//   change to GEOMETRYCOLLECTION)
+//
+// this does NOT calculate the bvol - you should set it with "bbox_of_geometry"
+//
+// type is the type of the subobject
+// do not call this as with type = GEOMETRYCOLLECTION
+//
+// doesnt change the is3d flag
+// doesnt change the SRID or other attributes
+
+GEOMETRY       *add_to_geometry(GEOMETRY *geom,int sub_obj_size, char *sub_obj, int type)
+{
+       int             size,t;
+       int             size_obj,next_offset;
+       GEOMETRY        *result;
+       int32           *old_offsets, *new_offsets;
+
+       //all the offsets could cause re-alignment problems, so need to deal with each one
+       size = geom->size +4*(geom->nobjs +1) /*byte align*/
+                       +sub_obj_size + 4 /*ObjType[]*/ +4 /*new offset*/;
+
+       result = (GEOMETRY *) palloc(size);
+       result->size = size;
+       result->is3d = geom->is3d;
+       result->SRID = geom->SRID;
+       result->offsetX = geom->offsetX;
+       result->offsetY = geom->offsetY;
+       result->scale = geom->scale;
+
+       //accidently sent in a single-entity type but gave it a multi-entity type
+       //  re-type it as single-entity
+       if (type == MULTIPOINTTYPE)
+               type = POINTTYPE;
+       if (type == MULTILINETYPE)
+               type = LINETYPE;
+       if (type == MULTIPOLYGONTYPE)
+               type = POLYGONTYPE;
+
+
+       // change to the simplest possible type that supports the type being added
+       if  (geom->type == POINTTYPE || geom->type == MULTIPOINTTYPE)
+       {
+               if (type == POINTTYPE)
+                       result->type  = MULTIPOINTTYPE;
+               else
+                       result->type  = COLLECTIONTYPE;
+       }
+       if  (geom->type == LINETYPE || geom->type == MULTILINETYPE)
+       {
+               if (type == LINETYPE)
+                       result->type  = MULTILINETYPE;
+               else
+                       result->type  = COLLECTIONTYPE;
+       }
+       if  (geom->type == POLYGONTYPE || geom->type == MULTIPOLYGONTYPE)
+       {
+               if (type == POLYGONTYPE)
+                       result->type  = MULTIPOLYGONTYPE;
+               else
+                       result->type  = COLLECTIONTYPE;
+       }
+       if (geom->type == COLLECTIONTYPE)
+               result->type = COLLECTIONTYPE;
+
+       // now result geometry's type and sub-object's type is okay
+       // we have to setup the geometry
+
+       result->nobjs = geom->nobjs+1;
+
+       for (t=0; t< geom->nobjs; t++)
+       {
+               result->objType[t] = geom->objType[t];
+       }
+
+//printf("about to copy geomes\n");
+//printf("result is at %p and is %i bytes long\n",result,result->size);
+//printf("geom is at %p and is %i bytes long\n",geom,geom->size);
+
+       old_offsets =&  geom->objType[geom->nobjs] ;
+       new_offsets =&  result->objType[result->nobjs] ;
+       next_offset = ( (char *) &new_offsets[result->nobjs] ) - ( (char *) result) ;
+       next_offset = MAXALIGN(next_offset);
+
+       //have to re-set the offsets and copy in the sub-object data
+       for (t=0; t< geom->nobjs; t++)
+       {
+               //where is this going to go?
+               new_offsets[t] = next_offset;
+
+               size_obj =      size_subobject ((char *) (((char *) geom) + old_offsets[t] ), geom->objType[t]);
+
+               next_offset += size_obj;
+               next_offset = MAXALIGN(next_offset); // make sure its aligned properly
+
+
+//printf("coping %i bytes from %p to %p\n", size_obj,( (char *) geom) + old_offsets[t],((char *) result)  + new_offsets[t]   );
+               memcpy( ((char *) result)  + new_offsets[t] , ( (char *) geom) + old_offsets[t], size_obj);
+//printf("copying done\n");
+
+       }
+
+//printf("copying in new object\n");
+
+       //now, put in the new data
+       result->objType[ result->nobjs -1 ] = type;
+       new_offsets[ result->nobjs -1 ] = next_offset;
+       memcpy(  ((char *) result)  + new_offsets[result->nobjs-1] ,sub_obj , sub_obj_size);
+
+//printf("returning\n");
+
+       return result;
+}
+
+
+
+//make a polygon obj
+// size is return in arg "size"
+POLYGON3D      *make_polygon(int nrings, int *pts_per_ring, POINT3D *pts, int npoints, int *size)
+{
+               POLYGON3D       *result;
+               int             t;
+               POINT3D *inside_poly_pts;
+
+
+       *size = sizeof(POLYGON3D) + 4 /*align*/
+                               + 4*(nrings-1)/*npoints struct*/
+                               + sizeof(POINT3D) *(npoints-1) /*points struct*/ ;
+
+       result= (POLYGON3D *) palloc (*size);
+       result->nrings = nrings;
+
+
+       for (t=0;t<nrings;t++)
+       {
+               result->npoints[t] = pts_per_ring[t];
+       }
+
+       inside_poly_pts = (POINT3D *) ( (char *)&(result->npoints[result->nrings] )  );
+       inside_poly_pts = (POINT3D *) MAXALIGN(inside_poly_pts);
+
+       memcpy(inside_poly_pts, pts, npoints *sizeof(POINT3D) );
+
+       return result;
+}
+
+void set_point( POINT3D *pt,double x, double y, double z)
+{
+       pt->x = x;
+       pt->y = y;
+       pt->z = z;
+}
+
+//make a line3d object
+// return size of structure in 'size'
+LINE3D *make_line(int  npoints, POINT3D        *pts, int       *size)
+{
+       LINE3D  *result;
+
+       *size = sizeof(LINE3D) + (npoints-1)*sizeof(POINT3D);
+
+       result= (LINE3D *) palloc (*size);
+
+       result->npoints = npoints;
+       memcpy( result->points, pts, npoints*sizeof(POINT3D) );
+
+       return result;
+}
+
+//given one byte, populate result with two byte representing
+// the hex number
+// ie deparse_hex( 255, mystr)
+//             -> mystr[0] = 'F' and mystr[1] = 'F'
+// no error checking done
+void deparse_hex(unsigned char str, unsigned char *result)
+{
+       int     input_high;
+       int  input_low;
+
+       input_high = (str>>4);
+       input_low = (str & 0x0F);
+
+       switch (input_high)
+       {
+               case 0:
+                       result[0] = '0';
+                       break;
+               case 1:
+                       result[0] = '1';
+                       break;
+               case 2:
+                       result[0] = '2';
+                       break;
+               case 3:
+                       result[0] = '3';
+                       break;
+               case 4:
+                       result[0] = '4';
+                       break;
+               case 5:
+                       result[0] = '5';
+                       break;
+               case 6:
+                       result[0] = '6';
+                       break;
+               case 7:
+                       result[0] = '7';
+                       break;
+               case 8:
+                       result[0] = '8';
+                       break;
+               case 9:
+                       result[0] = '9';
+                       break;
+               case 10:
+                       result[0] = 'A';
+                       break;
+               case 11:
+                       result[0] = 'B';
+                       break;
+               case 12:
+                       result[0] = 'C';
+                       break;
+               case 13:
+                       result[0] = 'D';
+                       break;
+               case 14:
+                       result[0] = 'E';
+                       break;
+               case 15:
+                       result[0] = 'F';
+                       break;
+       }
+
+       switch (input_low)
+       {
+               case 0:
+                       result[1] = '0';
+                       break;
+               case 1:
+                       result[1] = '1';
+                       break;
+               case 2:
+                       result[1] = '2';
+                       break;
+               case 3:
+                       result[1] = '3';
+                       break;
+               case 4:
+                       result[1] = '4';
+                       break;
+               case 5:
+                       result[1] = '5';
+                       break;
+               case 6:
+                       result[1] = '6';
+                       break;
+               case 7:
+                       result[1] = '7';
+                       break;
+               case 8:
+                       result[1] = '8';
+                       break;
+               case 9:
+                       result[1] = '9';
+                       break;
+               case 10:
+                       result[1] = 'A';
+                       break;
+               case 11:
+                       result[1] = 'B';
+                       break;
+               case 12:
+                       result[1] = 'C';
+                       break;
+               case 13:
+                       result[1] = 'D';
+                       break;
+               case 14:
+                       result[1] = 'E';
+                       break;
+               case 15:
+                       result[1] = 'F';
+                       break;
+       }
+}
+
+
+//given a string with at least 2 chars in it, convert them to
+// a byte value.  No error checking done!
+unsigned char  parse_hex(char *str)
+{
+       //do this a little brute force to make it faster
+
+       unsigned char           result_high = 0;
+       unsigned char           result_low = 0;
+
+       switch (str[0])
+       {
+               case '0' :
+                       result_high = 0;
+                       break;
+               case '1' :
+                       result_high = 1;
+                       break;
+               case '2' :
+                       result_high = 2;
+                       break;
+               case '3' :
+                       result_high = 3;
+                       break;
+               case '4' :
+                       result_high = 4;
+                       break;
+               case '5' :
+                       result_high = 5;
+                       break;
+               case '6' :
+                       result_high = 6;
+                       break;
+               case '7' :
+                       result_high = 7;
+                       break;
+               case '8' :
+                       result_high = 8;
+                       break;
+               case '9' :
+                       result_high = 9;
+                       break;
+               case 'A' :
+                       result_high = 10;
+                       break;
+               case 'B' :
+                       result_high = 11;
+                       break;
+               case 'C' :
+                       result_high = 12;
+                       break;
+               case 'D' :
+                       result_high = 13;
+                       break;
+               case 'E' :
+                       result_high = 14;
+                       break;
+               case 'F' :
+                       result_high = 15;
+                       break;
+       }
+       switch (str[1])
+       {
+               case '0' :
+                       result_low = 0;
+                       break;
+               case '1' :
+                       result_low = 1;
+                       break;
+               case '2' :
+                       result_low = 2;
+                       break;
+               case '3' :
+                       result_low = 3;
+                       break;
+               case '4' :
+                       result_low = 4;
+                       break;
+               case '5' :
+                       result_low = 5;
+                       break;
+               case '6' :
+                       result_low = 6;
+                       break;
+               case '7' :
+                       result_low = 7;
+                       break;
+               case '8' :
+                       result_low = 8;
+                       break;
+               case '9' :
+                       result_low = 9;
+                       break;
+               case 'A' :
+                       result_low = 10;
+                       break;
+               case 'B' :
+                       result_low = 11;
+                       break;
+               case 'C' :
+                       result_low = 12;
+                       break;
+               case 'D' :
+                       result_low = 13;
+                       break;
+               case 'E' :
+                       result_low = 14;
+                       break;
+               case 'F' :
+                       result_low = 15;
+                       break;
+       }
+       return (unsigned char) ((result_high<<4) + result_low);
+}
+
+
+// input is a string with hex chars in it.  Convert to binary and put in the result
+PG_FUNCTION_INFO_V1(WKB_in);
+Datum WKB_in(PG_FUNCTION_ARGS)
+{
+       char                    *str = PG_GETARG_CSTRING(0);
+       WellKnownBinary         *result;
+       int                     size;
+       int                     t;
+       int                     input_str_len;
+
+//printf("wkb_in called\n");
+
+       input_str_len = strlen(str);
+
+       if  ( ( ( (int)(input_str_len/2.0) ) *2.0) != input_str_len)
+       {
+               elog(ERROR,"WKB_in parser - should be even number of characters!");
+               PG_RETURN_NULL();
+       }
+
+       if (strspn(str,"0123456789ABCDEF") != strlen(str) )
+       {
+               elog(ERROR,"WKB_in parser - input contains bad characters.  Should only have '0123456789ABCDEF'!");
+               PG_RETURN_NULL();
+       }
+       size = (input_str_len/2) + 4;
+       result = (WellKnownBinary *) palloc(size);
+       result->size = size;
+
+       for (t=0;t<input_str_len/2;t++)
+       {
+               ((unsigned char *)result)[t+4] = parse_hex( &str[t*2]) ;
+       }
+       PG_RETURN_POINTER(result);
+}
+
+
+//given a WKB structure, convert it to Hex and put it in a string
+PG_FUNCTION_INFO_V1(WKB_out);
+Datum WKB_out(PG_FUNCTION_ARGS)
+{
+       WellKnownBinary               *WKB = (WellKnownBinary *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       char                                    *result;
+       int                                     size_result;
+       int                                     t;
+
+//printf("wkb_out called\n");
+
+       size_result = (WKB->size - 4) *2 +1; //+1 for null char
+       result = palloc (size_result);
+       result[size_result-1] = 0; //null terminate
+
+       for (t=0; t< (WKB->size -4); t++)
+       {
+               deparse_hex( ((unsigned char *) WKB)[4 + t], &result[t*2]);
+       }
+       PG_RETURN_CSTRING(result);
+}
+
+
+#if USE_VERSION > 73
+/*
+ * This function must advance the StringInfo.cursor pointer
+ * and leave it at the end of StringInfo.buf. If it fails
+ * to do so the backend will raise an exception with message:
+ * ERROR:  incorrect binary data format in bind parameter #
+ *
+ */
+PG_FUNCTION_INFO_V1(WKB_recv);
+Datum WKB_recv(PG_FUNCTION_ARGS)
+{
+       StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+        bytea *result;
+
+       //elog(NOTICE, "WKB_recv start");
+
+       /* Add VARLENA size info to make it a valid varlena object */
+       result = (bytea *)palloc(buf->len+VARHDRSZ);
+       VARATT_SIZEP(result) = buf->len+VARHDRSZ;
+       memcpy(VARATT_DATA(result), buf->data, buf->len);
+
+       /* Set cursor to the end of buffer (so the backend is happy) */
+       buf->cursor = buf->len;
+
+       //elog(NOTICE, "WKB_recv end (returning result)");
+        PG_RETURN_POINTER(result);
+}
+#endif
+
+PG_FUNCTION_INFO_V1(WKBtoBYTEA);
+Datum WKBtoBYTEA(PG_FUNCTION_ARGS)
+{
+               WellKnownBinary               *WKB = (WellKnownBinary *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               bytea                                     *result;
+
+               result = (bytea*) palloc(WKB->size);
+               memcpy(result,WKB, WKB->size);
+
+               PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(geometry2box);
+Datum geometry2box(PG_FUNCTION_ARGS)
+{
+        GEOMETRY *g = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+//elog(NOTICE,"geometry2box - ymax is %.15g",g->bvol.URT.y);
+
+       PG_RETURN_POINTER(convert_box3d_to_box(&g->bvol) );
+
+}
+
+//create_histogram2d(BOX3D, boxesPerSide)
+// returns a histgram with 0s in all the boxes.
+PG_FUNCTION_INFO_V1(create_histogram2d);
+Datum create_histogram2d(PG_FUNCTION_ARGS)
+{
+       BOX3D  *bbox = (BOX3D *) PG_GETARG_POINTER(0);
+       int32   boxesPerSide=  PG_GETARG_INT32(1);
+       HISTOGRAM2D             *histo;
+       int size,t;
+
+
+       if ( (boxesPerSide <1) || (boxesPerSide >50) )
+       {
+                       elog(ERROR,"create_histogram2d - boxesPerSide is too small or big.\n");
+                       PG_RETURN_NULL() ;
+       }
+
+
+       size =  sizeof(HISTOGRAM2D) +    (boxesPerSide*boxesPerSide-1)*4  ;
+
+       histo = (HISTOGRAM2D *) palloc (size);
+       histo->size = size;
+
+       histo->xmin = bbox->LLB.x;
+       histo->ymin = bbox->LLB.y;
+
+
+       histo->xmax = bbox->URT.x;
+       histo->ymax = bbox->URT.y;
+
+       histo->avgFeatureArea = 0;
+
+       histo->boxesPerSide = boxesPerSide;
+
+       for (t=0;t<boxesPerSide*boxesPerSide; t++)
+       {
+               histo->value[t] =0;
+       }
+
+       //elog(NOTICE,"create_histogram2d returning");
+
+       PG_RETURN_POINTER(histo);
+
+}
+
+
+//text form of HISTOGRAM2D is:
+// 'HISTOGRAM2D(xmin,ymin,xmax,ymax,boxesPerSide;value[0],value[1],...')
+//    note the ";" in the middle (for easy parsing)
+//  I dont expect anyone to actually create one by hand
+PG_FUNCTION_INFO_V1(histogram2d_in);
+Datum histogram2d_in(PG_FUNCTION_ARGS)
+{
+       char                    *str = PG_GETARG_CSTRING(0);
+       HISTOGRAM2D         *histo ;
+       int nitems;
+       double xmin,ymin,xmax,ymax;
+       int boxesPerSide;
+       double avgFeatureArea;
+       char *str2,*str3;
+       long datum;
+
+       int t;
+
+       while (isspace((unsigned char) *str))
+               str++;
+
+       if (strstr(str,"HISTOGRAM2D(") != str)
+       {
+                       elog(ERROR,"histogram2d parser - doesnt start with 'HISTOGRAM2D(\n");
+                       PG_RETURN_NULL() ;
+       }
+       if (strstr(str,";") == NULL)
+       {
+                       elog(ERROR,"histogram2d parser - doesnt have a ; in sring!\n");
+                       PG_RETURN_NULL() ;
+       }
+
+       nitems = sscanf(str,"HISTOGRAM2D(%lf,%lf,%lf,%lf,%i,%lf;",&xmin,&ymin,&xmax,&ymax,&boxesPerSide,&avgFeatureArea);
+
+       if (nitems != 6)
+       {
+                       elog(ERROR,"histogram2d parser - couldnt parse initial portion of histogram!\n");
+                       PG_RETURN_NULL() ;
+       }
+
+       if  ( (boxesPerSide > 50) || (boxesPerSide <1) )
+       {
+                       elog(ERROR,"histogram2d parser - boxesPerSide is too big or too small\n");
+                       PG_RETURN_NULL() ;
+       }
+
+       str2 = strstr(str,";");
+       str2++;
+
+       if (str2[0] ==0)
+       {
+                       elog(ERROR,"histogram2d parser - no histogram values\n");
+                       PG_RETURN_NULL() ;
+       }
+
+       histo = (HISTOGRAM2D *) palloc (sizeof(HISTOGRAM2D) +    (boxesPerSide*boxesPerSide-1)*4 );
+       histo->size = sizeof(HISTOGRAM2D) +    (boxesPerSide*boxesPerSide-1)*4  ;
+
+       for (t=0;t<boxesPerSide*boxesPerSide;t++)
+       {
+               datum = strtol(str2,&str3,10); // str2=start of int, str3=end of int, base 10
+               // str3 points to "," or ")"
+               if (str3[0] ==0)
+               {
+                       elog(ERROR,"histogram2d parser - histogram values prematurely ended!\n");
+                       PG_RETURN_NULL() ;
+               }
+               histo->value[t] = (unsigned int) datum;
+               str2= str3+1; //move past the "," or ")"
+       }
+       histo->xmin = xmin;
+       histo->xmax = xmax;
+       histo->ymin = ymin;
+       histo->ymax = ymax;
+       histo->avgFeatureArea = avgFeatureArea;
+       histo->boxesPerSide = boxesPerSide;
+
+       PG_RETURN_POINTER(histo);
+}
+
+
+
+//text version
+PG_FUNCTION_INFO_V1(histogram2d_out);
+Datum histogram2d_out(PG_FUNCTION_ARGS)
+{
+       //char *result;
+       //result = palloc(200);
+       //sprintf(result,"HISTOGRAM2D(0,0,100,100,2;11,22,33,44)");
+       //PG_RETURN_CSTRING(result);
+
+               HISTOGRAM2D   *histo = (HISTOGRAM2D *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               char    *result;
+               int t;
+               char    temp[100];
+               int size;
+
+               size = 26+6*MAX_DIGS_DOUBLE + histo->boxesPerSide*histo->boxesPerSide* (MAX_DIGS_DOUBLE+1);
+               result = palloc(size);
+
+               sprintf(result,"HISTOGRAM2D(%.15g,%.15g,%.15g,%.15g,%i,%.15g;",
+                                       histo->xmin,histo->ymin,histo->xmax,histo->ymax,histo->boxesPerSide,histo->avgFeatureArea );
+
+               //elog(NOTICE,"so far: %s",result);
+               //elog(NOTICE,"buffsize=%i, size=%i",size,histo->size);
+
+               for (t=0;t<histo->boxesPerSide*histo->boxesPerSide;t++)
+               {
+                       if (t!=((histo->boxesPerSide*histo->boxesPerSide)-1))
+                               sprintf(temp,"%u,", histo->value[t]);
+                       else
+                               sprintf(temp,"%u", histo->value[t]);
+                       strcat(result,temp);
+               }
+
+               strcat(result,")");
+               //elog(NOTICE,"about to return string (len=%i): -%s-",strlen(result),result);
+
+               PG_RETURN_CSTRING(result);
+
+}
+
+
+
+int getint(char *c)
+{
+       int i;
+       memcpy( &i, c, 4);
+       return i;
+//return *((int*)c);
+}
+
+double getdouble(char *c)
+{
+       double d;
+       memcpy( &d, c, 8);
+       return d;
+//     return *((double*)c);
+}
+
+//void flip_endian_double(char *dd);
+//void         flip_endian_int32(char  *ii);
+
+
+//select geometry(asbinary('POINT(1 2 3)','XDR'));
+//select geometry(asbinary('POINT(1 2)','XDR'));
+//select geometry(asbinary('POINT(1 2 3)','NDR'));
+//select geometry(asbinary('POINT(1 2)','NDR'));
+
+//select geometry(asbinary('LINESTRING(1 2, 3 4)','XDR'));
+//select geometry(asbinary('LINESTRING(1 2, 3 4)','NDR'));
+//select geometry(asbinary('LINESTRING(1 2 5 , 3 4 6)','XDR'));
+//select geometry(asbinary('LINESTRING(1 2 5 , 3 4 6)','NDR'));
+
+
+//select geometry(asbinary('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))','XDR'));
+//select geometry(asbinary('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))','NDR'));
+//select geometry(asbinary('POLYGON((0 0 0, 10 0, 10 10, 0 10, 0 0))','XDR'));
+//select geometry(asbinary('POLYGON((0 0 0, 10 0, 10 10, 0 10, 0 0))','NDR'));
+
+//select geometry(asbinary('POLYGON((5 5, 15 5, 15 7, 5 7, 5 5 ),(6 6,6.5 6, 6.5 6.5,6 6.5,6 6))','NDR'));
+//select geometry(asbinary('POLYGON((5 5, 15 5, 15 7, 5 7, 5 5 ),(6 6,6.5 6, 6.5 6.5,6 6.5,6 6))','XDR'));
+//select geometry(asbinary('POLYGON((5 5 0, 15 5, 15 7, 5 7, 5 5 ),(6 6,6.5 6, 6.5 6.5,6 6.5,6 6))','NDR'));
+//select geometry(asbinary('POLYGON((5 5 0, 15 5, 15 7, 5 7, 5 5 ),(6 6,6.5 6, 6.5 6.5,6 6.5,6 6))','XDR'));
+
+
+//select geometry(asbinary('MULTIPOINT(0 0, 10 0, 10 10, 0 10, 0 0)','NDR'));
+//select geometry(asbinary('MULTIPOINT(0 0, 10 0, 10 10, 0 10, 0 0 0)','NDR'));
+//select geometry(asbinary('MULTIPOINT(0 0, 10 0, 10 10, 0 10, 0 0)','XDR'));
+//select geometry(asbinary('MULTIPOINT(0 0, 10 0, 10 10, 0 10, 0 0 0)','NDR'));
+
+
+//select geometry(asbinary('MULTILINESTRING((5 5, 10 10),(1 1, 2 2) )','NDR'));
+//select geometry(asbinary('MULTILINESTRING((5 5, 10 10),(1 1, 2 2 0) )','NDR'));
+//select geometry(asbinary('MULTILINESTRING((5 5, 10 10),(1 1, 2 2) )','XDR'));
+//select geometry(asbinary('MULTILINESTRING((5 5, 10 10),(1 1, 2 2 0) )','XDR'));
+
+
+//select geometry(asbinary('MULTIPOLYGON(((5 5, 15 5, 15 7, 5 7, 5 5)),((1 1,1 2,2 2,1 2, 1 1)))','NDR'));
+//select geometry(asbinary('MULTIPOLYGON(((5 5, 15 5, 15 7, 5 7, 5 5)),((1 1,1 2,2 2,1 2, 1 1 0)))','NDR'));
+//select geometry(asbinary('MULTIPOLYGON(((5 5, 15 5, 15 7, 5 7, 5 5)),((1 1,1 2,2 2,1 2, 1 1)))','XDR'));
+//select geometry(asbinary('MULTIPOLYGON(((5 5, 15 5, 15 7, 5 7, 5 5)),((1 1,1 2,2 2,1 2, 1 1 0)))','XDR'));
+
+
+//select geometry(asbinary('GEOMETRYCOLLECTION(POINT(1 2),POINT(3 4))','NDR'));
+//select geometry(asbinary('GEOMETRYCOLLECTION(POINT(1 2 3),LINESTRING(1 2, 3 4),POLYGON((0 0, 10 0, 10 10, 0 10, 0 0)))','NDR'));
+// geometryfromWKB(<WKB>, [<SRID>] )
+PG_FUNCTION_INFO_V1(geometryfromWKB_SRID);
+Datum geometryfromWKB_SRID(PG_FUNCTION_ARGS)
+{
+               char   *wkb_input = (char *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               int    SRID = PG_GETARG_INT32(1);
+               char *wkb = &wkb_input[4];
+               int wkb_size = *((int *) wkb_input);
+               int bytes_read;
+               GEOMETRY *result;
+
+               if ( ! PG_ARGISNULL(1) )
+                       SRID = PG_GETARG_INT32(1);
+               else
+                       SRID = -1;
+
+//elog(NOTICE,"geometryfromWKB:: size = %i",wkb_size);
+
+       result = WKBtoGeometry(wkb,wkb_size,&bytes_read);
+
+       if (result == NULL)
+       {
+               PG_RETURN_NULL();
+       }
+       else
+       {
+               result->SRID = SRID;
+               PG_RETURN_POINTER(result);
+       }
+}
+
+PG_FUNCTION_INFO_V1(PointfromWKB_SRID);
+Datum PointfromWKB_SRID(PG_FUNCTION_ARGS)
+{
+               int SRID;
+               GEOMETRY *geom;
+
+               if ( ! PG_ARGISNULL(1) )
+                       SRID = PG_GETARG_INT32(1);
+               else
+                       SRID = -1;
+
+
+               geom = (GEOMETRY *) DatumGetPointer(
+                                       DirectFunctionCall2(geometryfromWKB_SRID,
+                                                       PG_GETARG_DATUM(0),Int32GetDatum(SRID)
+                                               ));
+               if (geom->type != POINTTYPE)
+               {
+                       elog(ERROR,"PointfromWKB:: WKB isnt POINT");
+               }
+               PG_RETURN_POINTER(geom);
+}
+
+PG_FUNCTION_INFO_V1(LinefromWKB_SRID);
+Datum LinefromWKB_SRID(PG_FUNCTION_ARGS)
+{
+               int SRID;
+               GEOMETRY *geom;
+
+               if ( ! PG_ARGISNULL(1) )
+                       SRID = PG_GETARG_INT32(1);
+               else
+                       SRID = -1;
+
+
+               geom = (GEOMETRY *) DatumGetPointer(
+                                       DirectFunctionCall2(geometryfromWKB_SRID,
+                                                       PG_GETARG_DATUM(0),Int32GetDatum(SRID)
+                                               ));
+               if (geom->type != LINETYPE)
+               {
+                       elog(ERROR,"LinefromWKB_SRID:: WKB isnt LINESTRING");
+               }
+               PG_RETURN_POINTER(geom);
+}
+
+PG_FUNCTION_INFO_V1(PolyfromWKB_SRID);
+Datum PolyfromWKB_SRID(PG_FUNCTION_ARGS)
+{
+               int SRID;
+               GEOMETRY *geom;
+
+               if ( ! PG_ARGISNULL(1) )
+                       SRID = PG_GETARG_INT32(1);
+               else
+                       SRID = -1;
+
+
+               geom = (GEOMETRY *) DatumGetPointer(
+                                       DirectFunctionCall2(geometryfromWKB_SRID,
+                                                       PG_GETARG_DATUM(0),Int32GetDatum(SRID)
+                                               ));
+               if (geom->type != POLYGONTYPE)
+               {
+                       elog(ERROR,"PolyfromWKB_SRID:: WKB isnt POLYGON");
+               }
+               PG_RETURN_POINTER(geom);
+}
+
+PG_FUNCTION_INFO_V1(MPointfromWKB_SRID);
+Datum MPointfromWKB_SRID(PG_FUNCTION_ARGS)
+{
+               int SRID;
+               GEOMETRY *geom;
+
+               if ( ! PG_ARGISNULL(1) )
+                       SRID = PG_GETARG_INT32(1);
+               else
+                       SRID = -1;
+
+
+               geom = (GEOMETRY *) DatumGetPointer(
+                                       DirectFunctionCall2(geometryfromWKB_SRID,
+                                                       PG_GETARG_DATUM(0),Int32GetDatum(SRID)
+                                               ));
+               if (geom->type != MULTIPOINTTYPE)
+               {
+                       elog(ERROR,"MPointfromWKB_SRID:: WKB isnt MULTIPOINT");
+               }
+               PG_RETURN_POINTER(geom);
+}
+
+PG_FUNCTION_INFO_V1(MLinefromWKB_SRID);
+Datum MLinefromWKB_SRID(PG_FUNCTION_ARGS)
+{
+               int SRID;
+               GEOMETRY *geom;
+
+               if ( ! PG_ARGISNULL(1) )
+                       SRID = PG_GETARG_INT32(1);
+               else
+                       SRID = -1;
+
+
+               geom = (GEOMETRY *) DatumGetPointer(
+                                       DirectFunctionCall2(geometryfromWKB_SRID,
+                                                       PG_GETARG_DATUM(0),Int32GetDatum(SRID)
+                                               ));
+               if (geom->type != MULTILINETYPE)
+               {
+                       elog(ERROR,"MLinefromWKB_SRID:: WKB isnt MULTILINESTRING");
+               }
+               PG_RETURN_POINTER(geom);
+}
+
+PG_FUNCTION_INFO_V1(MPolyfromWKB_SRID);
+Datum MPolyfromWKB_SRID(PG_FUNCTION_ARGS)
+{
+               int SRID;
+               GEOMETRY *geom;
+
+               if ( ! PG_ARGISNULL(1) )
+                       SRID = PG_GETARG_INT32(1);
+               else
+                       SRID = -1;
+
+
+               geom = (GEOMETRY *) DatumGetPointer(
+                                       DirectFunctionCall2(geometryfromWKB_SRID,
+                                                       PG_GETARG_DATUM(0),Int32GetDatum(SRID)
+                                               ));
+               if (geom->type != MULTIPOLYGONTYPE)
+               {
+                       elog(ERROR,"MPolyfromWKB_SRID:: WKB isnt MULTIPOLYGON");
+               }
+               PG_RETURN_POINTER(geom);
+}
+
+PG_FUNCTION_INFO_V1(GCfromWKB_SRID);
+Datum GCfromWKB_SRID(PG_FUNCTION_ARGS)
+{
+               int SRID;
+               GEOMETRY *geom;
+
+               if ( ! PG_ARGISNULL(1) )
+                       SRID = PG_GETARG_INT32(1);
+               else
+                       SRID = -1;
+
+
+               geom = (GEOMETRY *) DatumGetPointer(
+                                       DirectFunctionCall2(geometryfromWKB_SRID,
+                                                       PG_GETARG_DATUM(0),Int32GetDatum(SRID)
+                                               ));
+               if (geom->type != COLLECTIONTYPE)
+               {
+                       elog(ERROR,"MPolyfromWKB_SRID:: WKB isnt GEOMETRYCOLLECTION");
+               }
+               PG_RETURN_POINTER(geom);
+}
+
+
+
+
+//convert a WKB (that has length bytes)
+// to a GEOMETRY.  This function read bytes_read bytes during the operation
+//  This function will call itself "recursively" on GeometryCollections
+GEOMETRY *WKBtoGeometry(char *WKB, int length, int *bytes_read)
+{
+                       char myByteOrder;
+                       char wkbByteOrder;
+                       int  wkbType;
+                       char    is3d;
+
+*bytes_read = 0;
+if (length<5)
+       elog(ERROR,"WKB:: insufficient bytes in stream");
+
+
+if ( BYTE_ORDER == BIG_ENDIAN )
+       myByteOrder=0;
+else
+       myByteOrder=1;
+
+wkbByteOrder = *(WKB);
+
+if (!( (wkbByteOrder==0) || (wkbByteOrder==1) ))
+{
+       elog(ERROR,"WKB is not valid - endian code = %i", (int) wkbByteOrder);
+       return NULL;
+}
+
+WKB ++; // skip to next byte
+(*bytes_read)++;
+
+if (myByteOrder == wkbByteOrder)
+{
+       wkbType = getint(WKB);
+}
+else
+{
+       flip_endian_int32( WKB );
+       wkbType = getint(WKB);
+}
+WKB += 4;
+(*bytes_read) += 4;
+
+//elog(NOTICE,"my byte order is %i",myByteOrder);
+//elog(NOTICE,"WKB byte order is %i",wkbByteOrder);
+//elog(NOTICE,"WKB type is %i",wkbType);
+
+is3d = 0;
+
+switch (wkbType)
+{
+       case (0x80000000 +1):  //point 3d
+                       is3d = 1;
+       case 1:  //point 2d
+                       if ( (length-(*bytes_read)) < (16+is3d*8))
+                               elog(ERROR,"WKB:: insufficient bytes in stream");
+                       {
+                               POINT3D pt;
+                               if (myByteOrder == wkbByteOrder)
+                               {
+                                       pt.x = getdouble(WKB);
+                                       WKB+=8;
+                                       (*bytes_read)+=8;
+                                       pt.y = getdouble(WKB);
+                                       WKB+=8;
+                                       (*bytes_read)+=8;
+                                       if (is3d)
+                                       {
+                                               pt.z = getdouble(WKB);
+                                               WKB+=8;
+                                               (*bytes_read)+=8;
+                                       }
+                                       else
+                                       {
+                                               pt.z=0;
+                                       }
+                               }
+                               else
+                               {
+                                       flip_endian_double(WKB);
+                                       pt.x = getdouble(WKB);
+                                       WKB+=8;
+                                       (*bytes_read)+=8;
+                                       flip_endian_double(WKB);
+                                       pt.y = getdouble(WKB);
+                                       WKB+=8;
+                                       (*bytes_read)+=8;
+                                       if (is3d)
+                                       {
+                                               flip_endian_double(WKB);
+                                               pt.z = getdouble(WKB);
+                                               WKB+=8;
+                                               (*bytes_read)+=8;
+                                       }
+                                       else
+                                       {
+                                               pt.z =0;
+                                       }
+                               }
+                               if ( ! finite(pt.x) || ! finite(pt.y) || ! finite(pt.z) )
+                               {
+                                       elog(ERROR, "infinite coordinate in geom");
+                                       return NULL;
+                               }
+                               return make_oneobj_geometry(sizeof(POINT3D),
+                                                                                       (char *) &pt,
+                                                                                       POINTTYPE,  is3d, -1,1.0, 0.0, 0.0
+                                               );
+                       }
+
+
+                       break;
+       case (0x80000000 +2):  //line 3d
+                       is3d = 1;
+       case 2: //line 2d
+                       if ( (length-(*bytes_read)) < (4))
+                               elog(ERROR,"WKB:: insufficient bytes in stream");
+                               {
+                                               int npoints,t;
+                                               POINT3D *pts;
+                                               int size;
+                                               LINE3D  *line;
+                                               if (myByteOrder == wkbByteOrder)
+                                               {
+                                                       npoints= getint(WKB);
+                                                       WKB+=4;
+                                                       (*bytes_read)+=4;
+                                                       if ( (length-(*bytes_read)) < ((16+is3d*8))*npoints)
+                                                               elog(ERROR,"WKB:: insufficient bytes in stream");
+
+                                                       pts = palloc( npoints *sizeof(POINT3D));
+                                                       for (t=0;t<npoints;t++)
+                                                       {
+                                                               pts[t].x = getdouble(WKB);
+                                                               WKB+=8;
+                                                               (*bytes_read)+=8;
+                                                               pts[t].y = getdouble(WKB);
+                                                               WKB+=8;
+                                                               (*bytes_read)+=8;
+                                                               if (is3d)
+                                                               {
+                                                                       pts[t].z = getdouble(WKB);
+                                                                       WKB+=8;
+                                                                       (*bytes_read)+=8;
+                                                               }
+                                                               else
+                                                               {
+                                                                       pts[t].z =0;
+                                                               }
+                                                               if ( ! finite(pts[t].x) || ! finite(pts[t].y) || ! finite(pts[t].z) )
+                                                               {
+                                                                       elog(ERROR, "infinite coordinate in geom");
+                                                                       return NULL;
+                                                               }
+                                                       }
+                                                       //make_line(int  npoints, POINT3D    *pts, int   *size)
+                                                       line = make_line(npoints, pts,&size);
+                                                       return make_oneobj_geometry(size,
+                                                                                                               (char *) line,
+                                                                                                               LINETYPE,  is3d, -1,1.0, 0.0, 0.0
+                                                                                       );
+                                               }
+                                               else
+                                               {
+                                                       flip_endian_int32(WKB);
+                                                       npoints= getint(WKB);
+                                                       WKB+=4;
+                                                       (*bytes_read)+=4;
+                                                       if ( (length-(*bytes_read)) < ((16+is3d*8))*npoints)
+                                                               elog(ERROR,"WKB:: insufficient bytes in stream");
+                                                       pts = palloc( npoints *sizeof(POINT3D));
+                                                       for (t=0;t<npoints;t++)
+                                                       {
+                                                               flip_endian_double(WKB);
+                                                               pts[t].x = getdouble(WKB);
+                                                               WKB+=8;
+                                                               (*bytes_read)+=8;
+                                                               flip_endian_double(WKB);
+                                                               pts[t].y = getdouble(WKB);
+                                                               WKB+=8;
+                                                               (*bytes_read)+=8;
+                                                               if (is3d)
+                                                               {
+                                                                       flip_endian_double(WKB);
+                                                                       pts[t].z = getdouble(WKB);
+                                                                       WKB+=8;
+                                                                       (*bytes_read)+=8;
+                                                               }
+                                                               else
+                                                               {
+                                                                       pts[t].z =0;
+                                                               }
+                                                               if ( ! finite(pts[t].x) || ! finite(pts[t].y) || ! finite(pts[t].z) )
+                                                               {
+                                                                       elog(ERROR, "infinite coordinate in geom");
+                                                                       return NULL;
+                                                               }
+                                                       }
+                                                       //make_line(int  npoints, POINT3D    *pts, int   *size)
+                                                       line = make_line(npoints, pts,&size);
+                                                       return make_oneobj_geometry(size, (char *) line, LINETYPE,  is3d, -1,1.0, 0.0, 0.0);
+                                               }
+                                       }
+                       break;
+       case (0x80000000 +3):  //polygon 3d
+                       is3d =1;
+       case 3: //polygon
+               if ( (length-(*bytes_read)) < (4))
+                               elog(ERROR,"WKB:: insufficient bytes in stream");
+               {
+                               int nrings;
+                               POINT3D** list_list_points;
+                               int32  *points_per_ring;
+                               int t;
+                               int total_points =0;
+                               POLYGON3D *poly;
+                               int size;
+                               POINT3D *pts;
+                               int point_offset;
+
+                       if (myByteOrder == wkbByteOrder)
+                       {
+                               nrings= getint(WKB);
+                               WKB+=4;
+                               (*bytes_read)+=4;
+                               list_list_points = palloc( sizeof(POINT3D*) * nrings);
+                               points_per_ring  = palloc( sizeof(int32*) * nrings);
+                               for (t=0;t<nrings;t++)
+                               {
+                                       int bytes, numbPoints;
+
+                                       list_list_points[t] =
+                                                       wkb_linearring(WKB, is3d, 0, &numbPoints, &bytes,length-(*bytes_read));
+                                       points_per_ring[t] = numbPoints;
+                                       total_points += numbPoints;
+                                       WKB += bytes;
+                                       (*bytes_read)+=bytes;
+                               }
+
+                       }
+                       else
+                       {
+                               flip_endian_int32(WKB);
+                               nrings= getint(WKB);
+                               WKB+=4;
+                               (*bytes_read)+=4;
+                               list_list_points = palloc( sizeof(POINT3D*) * nrings);
+                               points_per_ring  = palloc( sizeof(int32*) * nrings);
+                               for (t=0;t<nrings;t++)
+                               {
+                                       int bytes, numbPoints;
+
+                                       list_list_points[t] =
+                                                       wkb_linearring(WKB, is3d, 1, &numbPoints, &bytes,length-(*bytes_read));
+                                       points_per_ring[t] = numbPoints;
+                                       total_points += numbPoints;
+                                       WKB += bytes;
+                                       (*bytes_read)+=bytes;
+                               }
+                       }
+                       //compact point arrays
+                       pts = palloc ( sizeof(POINT3D) * total_points);
+                       point_offset  = 0;
+                       for (t=0;t<nrings;t++)
+                       {
+                               memcpy( &pts[point_offset],list_list_points[t], sizeof(POINT3D)*points_per_ring[t]);
+                               point_offset += points_per_ring[t];
+                       }
+                       poly = make_polygon( nrings, points_per_ring, pts, total_points, &size);
+                       return make_oneobj_geometry(size,
+                                                                               (char *) poly,
+                                                                               POLYGONTYPE,  is3d, -1,1.0, 0.0, 0.0
+                                       );
+               }
+
+                       break;
+       case (0x80000000 +4):  //multipoint 3d
+       case 4: //multipoint
+                               {
+                                       int ngeoms,t;
+                                       GEOMETRY *so_far,*so_far2;
+                                       GEOMETRY *one_obj;
+
+                                       int mybytes_read;
+                                       int other_bytes_read;
+
+                                       if (myByteOrder != wkbByteOrder)
+                                       {
+                                               flip_endian_int32(WKB);
+                                       }
+                                       ngeoms= getint(WKB);
+                                       WKB+=4;
+                                       (*bytes_read)+=4;
+                                       if (ngeoms ==0)
+                                       {
+                                               GEOMETRY *result= makeNullGeometry(-1);
+                                               result->type = MULTIPOINTTYPE;
+                                               return result;
+                                       }
+
+                                               mybytes_read = *bytes_read;
+                                               so_far = WKBtoGeometry(WKB, length - mybytes_read, &other_bytes_read);
+                                               (*bytes_read) =  mybytes_read + other_bytes_read;
+                                               WKB += other_bytes_read;
+                                               so_far->type = MULTIPOINTTYPE;
+
+                                               if (ngeoms ==1)
+                                                       return so_far;
+
+                                       for (t=1;t<ngeoms;t++) // already done the first
+                                       {
+                                               mybytes_read = *bytes_read;
+                                               one_obj = WKBtoGeometry(WKB, length - mybytes_read, &other_bytes_read);
+                                               (*bytes_read) =  mybytes_read + other_bytes_read;
+                                               WKB += other_bytes_read;
+                                               so_far2 =
+                                                       (GEOMETRY *) DatumGetPointer(
+                                                                               DirectFunctionCall2(collector,
+                                                                                               PointerGetDatum(so_far),PointerGetDatum(one_obj)
+                                                                                       )
+                                                                       );
+                                               pfree(one_obj);
+                                               pfree(so_far);
+                                               so_far = so_far2;
+                                       }
+                                       return so_far;
+                               }
+       case (0x80000000 +5):  //multiline 3d
+       case 5: //multiline
+                       {
+                                       int ngeoms,t;
+                                       GEOMETRY *so_far,*so_far2;
+                                       GEOMETRY *one_obj;
+
+                                       int mybytes_read;
+                                       int other_bytes_read;
+
+                                       if (myByteOrder != wkbByteOrder)
+                                       {
+                                               flip_endian_int32(WKB);
+                                       }
+                                       ngeoms= getint(WKB);
+                                       WKB+=4;
+                                       (*bytes_read)+=4;
+                                       if (ngeoms ==0)
+                                       {
+                                               GEOMETRY *result= makeNullGeometry(-1);
+                                               result->type = MULTILINETYPE;
+                                               return result;
+                                       }
+
+                                               mybytes_read = *bytes_read;
+                                               so_far = WKBtoGeometry(WKB, length - mybytes_read, &other_bytes_read);
+                                               (*bytes_read) =  mybytes_read + other_bytes_read;
+                                               WKB += other_bytes_read;
+                                               so_far->type = MULTILINETYPE;
+
+                                               if (ngeoms ==1)
+                                                       return so_far;
+
+                                       for (t=1;t<ngeoms;t++) // already done the first
+                                       {
+                                               mybytes_read = *bytes_read;
+                                               one_obj = WKBtoGeometry(WKB, length - mybytes_read, &other_bytes_read);
+                                               (*bytes_read) =  mybytes_read + other_bytes_read;
+                                               WKB += other_bytes_read;
+                                               so_far2 =
+                                                       (GEOMETRY *) DatumGetPointer(
+                                                                               DirectFunctionCall2(collector,
+                                                                                               PointerGetDatum(so_far),PointerGetDatum(one_obj)
+                                                                                       )
+                                                                       );
+                                               pfree(one_obj);
+                                               pfree(so_far);
+                                               so_far = so_far2;
+                                       }
+                                       return so_far;
+                               }
+       case (0x80000000 +6):  //multipolygon 3d
+       case 6: //multipolygon
+                       {
+                               int ngeoms,t;
+                               GEOMETRY *so_far,*so_far2;
+                               GEOMETRY *one_obj;
+
+                               int mybytes_read;
+                               int other_bytes_read;
+
+                               if (myByteOrder != wkbByteOrder)
+                               {
+                                       flip_endian_int32(WKB);
+                               }
+                               ngeoms= getint(WKB);
+                               WKB+=4;
+                               (*bytes_read)+=4;
+                               if (ngeoms ==0)
+                               {
+                                       GEOMETRY *result= makeNullGeometry(-1);
+                                       result->type = MULTIPOLYGONTYPE;
+                                       return result;
+                               }
+
+                                       mybytes_read = *bytes_read;
+                                       so_far = WKBtoGeometry(WKB, length - mybytes_read, &other_bytes_read);
+                                       (*bytes_read) =  mybytes_read + other_bytes_read;
+                                       WKB += other_bytes_read;
+                                       so_far->type = MULTIPOLYGONTYPE;
+
+                                       if (ngeoms ==1)
+                                               return so_far;
+
+                               for (t=1;t<ngeoms;t++) // already done the first
+                               {
+                                       mybytes_read = *bytes_read;
+                                       one_obj = WKBtoGeometry(WKB, length - mybytes_read, &other_bytes_read);
+                                       (*bytes_read) =  mybytes_read + other_bytes_read;
+                                       WKB += other_bytes_read;
+                                       so_far2 =
+                                               (GEOMETRY *) DatumGetPointer(
+                                                                       DirectFunctionCall2(collector,
+                                                                                       PointerGetDatum(so_far),PointerGetDatum(one_obj)
+                                                                               )
+                                                               );
+                                       pfree(one_obj);
+                                       pfree(so_far);
+                                       so_far = so_far2;
+                               }
+                               return so_far;
+                       }
+       case (0x80000000 +7):  //geometry collection 3d
+       case 7: //geometry collection
+                       {
+                               int ngeoms,t;
+                               GEOMETRY *so_far,*so_far2;
+                               GEOMETRY *one_obj;
+
+                               int mybytes_read;
+                               int other_bytes_read;
+
+                               if (myByteOrder != wkbByteOrder)
+                               {
+                                       flip_endian_int32(WKB);
+                               }
+                               ngeoms= getint(WKB);
+                               WKB+=4;
+                               (*bytes_read)+=4;
+                               if (ngeoms ==0)
+                               {
+                                       GEOMETRY *result= makeNullGeometry(-1);
+                                       return result;
+                               }
+
+                                       mybytes_read = *bytes_read;
+                                       so_far = WKBtoGeometry(WKB, length - mybytes_read, &other_bytes_read);
+                                       (*bytes_read) =  mybytes_read + other_bytes_read;
+                                       WKB += other_bytes_read;
+                                       so_far->type = COLLECTIONTYPE;
+
+                                       if (ngeoms ==1)
+                                               return so_far;
+
+                               for (t=1;t<ngeoms;t++) // already done the first
+                               {
+                                       mybytes_read = *bytes_read;
+                                       one_obj = WKBtoGeometry(WKB, length - mybytes_read, &other_bytes_read);
+                                       (*bytes_read) =  mybytes_read + other_bytes_read;
+                                       WKB += other_bytes_read;
+                                       so_far2 =
+                                               (GEOMETRY *) DatumGetPointer(
+                                                                       DirectFunctionCall2(collector,
+                                                                                       PointerGetDatum(so_far),PointerGetDatum(one_obj)
+                                                                               )
+                                                               );
+                                       pfree(one_obj);
+                                       pfree(so_far);
+                                       so_far = so_far2;
+                               }
+                               return so_far;
+                       }
+       default:
+               elog(ERROR,"unknown WKB type: %i",wkbType);
+               return NULL;
+}
+return NULL;
+}
+
+// take some wkt (and if its 3d or needs endianflip) and return as list of points
+//  also return the number of points and its size in bytes
+//
+POINT3D *wkb_linearring(char *WKB,char is3d, char flip_endian, int *numbPoints, int *bytes,int bytes_in_stream)
+{
+       int t;
+
+       if (bytes_in_stream < 4)
+               elog(ERROR,"WKB:: insufficient bytes in stream");
+
+       if (flip_endian)
+       {
+               POINT3D *pts;
+               int npoints;
+
+               flip_endian_int32(WKB);
+               npoints= getint(WKB);
+               WKB+=4;
+               if ( (bytes_in_stream-4) < ((16+is3d*8))*npoints)
+                                       elog(ERROR,"WKB:: insufficient bytes in stream");
+               pts = palloc( npoints *sizeof(POINT3D));
+               for (t=0;t<npoints;t++)
+               {
+                       flip_endian_double(WKB);
+                       pts[t].x = getdouble(WKB);
+                       WKB+=8;
+                       flip_endian_double(WKB);
+                       pts[t].y = getdouble(WKB);
+                       WKB+=8;
+                       if (is3d)
+                       {
+                               flip_endian_double(WKB);
+                               pts[t].z = getdouble(WKB);
+                               WKB+=8;
+                       }
+                       else
+                       {
+                               pts[t].z =0;
+                       }
+                       if ( ! finite(pts[t].x) || ! finite(pts[t].y) || ! finite(pts[t].z) )
+                       {
+                               elog(ERROR, "infinite coordinate in geom");
+                               return NULL;
+                       }
+               }
+               *numbPoints = npoints;
+               if (is3d)
+               {
+                       *bytes = 4+8*3*npoints;
+               }
+               else
+               {
+                       *bytes = 4+8*2*npoints;
+               }
+               return pts;
+       }
+       else
+       {
+               POINT3D *pts;
+               int npoints;
+
+               npoints= getint(WKB);
+               WKB+=4;
+               if ( (bytes_in_stream-4) < ((16+is3d*8))*npoints)
+                                       elog(ERROR,"WKB:: insufficient bytes in stream");
+               pts = palloc( npoints *sizeof(POINT3D));
+               for (t=0;t<npoints;t++)
+               {
+                       pts[t].x = getdouble(WKB);
+                       WKB+=8;
+                       pts[t].y = getdouble(WKB);
+                       WKB+=8;
+                       if (is3d)
+                       {
+                               pts[t].z = getdouble(WKB);
+                               WKB+=8;
+                       }
+                       else
+                       {
+                               pts[t].z =0;
+                       }
+               }
+               *numbPoints = npoints;
+               if (is3d)
+               {
+                       *bytes = 4+8*3*npoints;
+               }
+               else
+               {
+                       *bytes = 4+8*2*npoints;
+               }
+               return pts;
+       }
+}
+
+
+
+PG_FUNCTION_INFO_V1(geometry_from_text_poly);
+Datum geometry_from_text_poly(PG_FUNCTION_ARGS)
+{
+               int SRID;
+               GEOMETRY *geom;
+
+               if ( ! PG_ARGISNULL(1) )
+                       SRID = PG_GETARG_INT32(1);
+               else
+                       SRID = -1;
+
+
+               geom = (GEOMETRY *) DatumGetPointer(
+                                       DirectFunctionCall2(geometry_from_text,
+                                                       PG_GETARG_DATUM(0),Int32GetDatum(SRID)
+                                               ));
+               if (geom->type != POLYGONTYPE)
+               {
+                       elog(ERROR,"geometry_from_text_poly:: WKT isnt POLYGON");
+               }
+               PG_RETURN_POINTER(geom);
+}
+
+PG_FUNCTION_INFO_V1(geometry_from_text_line);
+Datum geometry_from_text_line(PG_FUNCTION_ARGS)
+{
+               int SRID;
+               GEOMETRY *geom;
+
+               if ( ! PG_ARGISNULL(1) )
+                       SRID = PG_GETARG_INT32(1);
+               else
+                       SRID = -1;
+
+
+               geom = (GEOMETRY *) DatumGetPointer(
+                                       DirectFunctionCall2(geometry_from_text,
+                                                       PG_GETARG_DATUM(0),Int32GetDatum(SRID)
+                                               ));
+               if (geom->type != LINETYPE)
+               {
+                       elog(ERROR,"geometry_from_text_line:: WKT isnt LINESTRING");
+               }
+               PG_RETURN_POINTER(geom);
+}
+
+
+PG_FUNCTION_INFO_V1(geometry_from_text_point);
+Datum geometry_from_text_point(PG_FUNCTION_ARGS)
+{
+               int SRID;
+               GEOMETRY *geom;
+
+               if ( ! PG_ARGISNULL(1) )
+                       SRID = PG_GETARG_INT32(1);
+               else
+                       SRID = -1;
+
+
+               geom = (GEOMETRY *) DatumGetPointer(
+                                       DirectFunctionCall2(geometry_from_text,
+                                                       PG_GETARG_DATUM(0),Int32GetDatum(SRID)
+                                               ));
+               if (geom->type != POINTTYPE)
+               {
+                       elog(ERROR,"geometry_from_text_point:: WKT isnt POINT");
+               }
+               PG_RETURN_POINTER(geom);
+}
+
+PG_FUNCTION_INFO_V1(geometry_from_text_mpoint);
+Datum geometry_from_text_mpoint(PG_FUNCTION_ARGS)
+{
+               int SRID;
+               GEOMETRY *geom;
+
+               if ( ! PG_ARGISNULL(1) )
+                       SRID = PG_GETARG_INT32(1);
+               else
+                       SRID = -1;
+
+
+               geom = (GEOMETRY *) DatumGetPointer(
+                                       DirectFunctionCall2(geometry_from_text,
+                                                       PG_GETARG_DATUM(0),Int32GetDatum(SRID)
+                                               ));
+               if (geom->type != MULTIPOINTTYPE)
+               {
+                       elog(ERROR,"geometry_from_text_mpoint:: WKT isnt MULTIPOINT");
+               }
+               PG_RETURN_POINTER(geom);
+}
+
+PG_FUNCTION_INFO_V1(geometry_from_text_mline);
+Datum geometry_from_text_mline(PG_FUNCTION_ARGS)
+{
+               int SRID;
+               GEOMETRY *geom;
+
+               if ( ! PG_ARGISNULL(1) )
+                       SRID = PG_GETARG_INT32(1);
+               else
+                       SRID = -1;
+
+
+               geom = (GEOMETRY *) DatumGetPointer(
+                                       DirectFunctionCall2(geometry_from_text,
+                                                       PG_GETARG_DATUM(0),Int32GetDatum(SRID)
+                                               ));
+               if (geom->type != MULTILINETYPE)
+               {
+                       elog(ERROR,"geometry_from_text_mline:: WKT isnt MULTILINESTRING");
+               }
+               PG_RETURN_POINTER(geom);
+}
+
+PG_FUNCTION_INFO_V1(geometry_from_text_mpoly);
+Datum geometry_from_text_mpoly(PG_FUNCTION_ARGS)
+{
+               int SRID;
+               GEOMETRY *geom;
+
+               if ( ! PG_ARGISNULL(1) )
+                       SRID = PG_GETARG_INT32(1);
+               else
+                       SRID = -1;
+
+
+               geom = (GEOMETRY *) DatumGetPointer(
+                                       DirectFunctionCall2(geometry_from_text,
+                                                       PG_GETARG_DATUM(0),Int32GetDatum(SRID)
+                                               ));
+               if (geom->type != MULTIPOLYGONTYPE)
+               {
+                       elog(ERROR,"geometry_from_text_mpoly:: WKT isnt MULTIPOLYGON");
+               }
+               PG_RETURN_POINTER(geom);
+}
+
+PG_FUNCTION_INFO_V1(geometry_from_text_gc);
+Datum geometry_from_text_gc(PG_FUNCTION_ARGS)
+{
+               int SRID;
+               GEOMETRY *geom;
+
+               if ( ! PG_ARGISNULL(1) )
+                       SRID = PG_GETARG_INT32(1);
+               else
+                       SRID = -1;
+
+
+               geom = (GEOMETRY *) DatumGetPointer(
+                                       DirectFunctionCall2(geometry_from_text,
+                                                       PG_GETARG_DATUM(0),Int32GetDatum(SRID)
+                                               ));
+               if (geom->type != COLLECTIONTYPE)
+               {
+                       elog(ERROR,"geometry_from_text_gc:: WKT isnt GEOMETRYCOLLECTION");
+               }
+               PG_RETURN_POINTER(geom);
+}
+
+
+//returns a GEOMETRYCOLLECTION(EMPTY)
+// bbox of this object is BOX3D(0 0 0, 0 0 0)
+//   but should be treated as NULL
+GEOMETRY *makeNullGeometry(int SRID)
+{
+               int size = sizeof(GEOMETRY);
+               GEOMETRY        *result = palloc(size);
+
+
+               memset(result,0, size ); // init to 0s
+
+               result->size = size;
+               result->nobjs = 0;
+               result->type = COLLECTIONTYPE;
+               result->is3d = false;
+
+
+               result->SRID = SRID;
+               result->scale = 1.0;
+               result->offsetX = 0;
+               result->offsetY = 0;
+
+               memset(&result->bvol,0, sizeof(BOX3D) );  //make bbox :: BOX3D(0 0 0, 0 0 0)
+
+       return result;
+}
+
+
diff --git a/hwgeom/postgis_ops.c b/hwgeom/postgis_ops.c
new file mode 100644 (file)
index 0000000..188cad3
--- /dev/null
@@ -0,0 +1,1123 @@
+
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2001-2003 Refractions Research Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ * 
+ **********************************************************************
+ * $Log$
+ * Revision 1.1  2004/09/20 07:50:06  strk
+ * prepared to contain old internal representation code
+ *
+ * Revision 1.13  2004/04/28 22:26:02  pramsey
+ * Fixed spelling mistake in header text.
+ *
+ * Revision 1.12  2003/11/20 15:34:02  strk
+ * expected in-transaction memory release for btree operators
+ *
+ * Revision 1.11  2003/11/19 15:26:57  strk
+ * Added geometry_le, geometry_ge, geometry_cmp functions,
+ * modified geometry_lt, geometry_gt, geometry_eq to be consistent.
+ *
+ * Revision 1.10  2003/07/25 17:08:37  pramsey
+ * Moved Cygwin endian define out of source files into postgis.h common
+ * header file.
+ *
+ * Revision 1.9  2003/07/01 18:30:55  pramsey
+ * Added CVS revision headers.
+ *
+ *
+ **********************************************************************/
+
+#include "postgres.h"
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "access/gist.h"
+#include "access/itup.h"
+#include "access/rtree.h"
+
+#include "fmgr.h"
+
+#include "postgis.h"
+#include "utils/elog.h"
+
+#define SHOW_DIGS_DOUBLE 15
+#define MAX_DIGS_DOUBLE (SHOW_DIGS_DOUBLE + 6 + 1 + 3 +1)
+
+
+//given a bvol, make a "fake" geometry that contains it (for indexing)
+GEOMETRY *make_bvol_geometry(BOX3D *box)
+{
+       return (GEOMETRY *) DatumGetPointer(DirectFunctionCall1(get_geometry_of_bbox,PointerGetDatum(box) ) );
+}
+
+//   ***********************************
+//          GEOMETRY indexing (operator) fns
+//     NOTE:
+//             These work on the bbox of the geometry, not the 
+//             individual components (except same)
+//   ***********************************
+
+
+PG_FUNCTION_INFO_V1(box3d_same);
+Datum box3d_same(PG_FUNCTION_ARGS)
+{
+       BOX3D              *box1 = (BOX3D *)  PG_GETARG_POINTER(0);
+       BOX3D              *box2 = (BOX3D *) PG_GETARG_POINTER(1);
+
+       bool            result;
+
+       result = (         FPeq(box1->LLB.x     , box2->LLB.x) &&
+                                  FPeq(box1->LLB.y     , box2->LLB.y) &&
+                                  FPeq(box1->URT.x     , box2->URT.x) &&
+                                  FPeq(box1->URT.y     , box2->URT.y)
+                               );
+       PG_RETURN_BOOL(result);
+}
+
+
+
+
+PG_FUNCTION_INFO_V1(geometry_overleft);
+Datum geometry_overleft(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                   *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                   *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       bool    result;
+
+//printf("in geometry_overleft\n");
+//print_box(&geom1->bvol);
+//print_box(&geom2->bvol);
+
+       if (geom1->SRID != geom2->SRID)
+       {
+               elog(ERROR,"Operation on two GEOMETRIES with different SRIDs\n");
+               PG_RETURN_NULL();
+       }
+
+       result = FPle(geom1->bvol.URT.x, geom2->bvol.URT.x);
+
+       PG_RETURN_BOOL(result);
+}
+
+PG_FUNCTION_INFO_V1(geometry_left);
+Datum geometry_left(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                   *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                   *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       bool    result;
+
+//printf("in geometry_left\n");
+
+       if (geom1->SRID != geom2->SRID)
+       {
+               elog(ERROR,"Operation on two GEOMETRIES with different SRIDs\n");
+               PG_RETURN_NULL();
+       }
+
+
+       result = FPlt(geom1->bvol.URT.x, geom2->bvol.LLB.x);
+
+
+       PG_RETURN_BOOL(result);
+}
+
+
+PG_FUNCTION_INFO_V1(geometry_right);
+Datum geometry_right(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                   *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                   *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       bool    result;
+//printf("in geometry_right\n");
+
+       if (geom1->SRID != geom2->SRID)
+       {
+               elog(ERROR,"Operation on two GEOMETRIES with different SRIDs\n");
+               PG_RETURN_NULL();
+       }
+
+
+       result = FPgt(geom1->bvol.LLB.x, geom2->bvol.URT.x);
+       
+       PG_RETURN_BOOL(result);
+}
+
+PG_FUNCTION_INFO_V1(geometry_overright);
+Datum geometry_overright(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                   *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                   *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       bool    result;
+//printf("in geometry_overright\n");
+
+       if (geom1->SRID != geom2->SRID)
+       {
+               elog(ERROR,"Operation on two GEOMETRIES with different SRIDs\n");
+               PG_RETURN_NULL();
+       }
+
+
+       result = FPge(geom1->bvol.LLB.x, geom2->bvol.LLB.x);
+       
+
+
+       PG_RETURN_BOOL(result);
+}
+
+PG_FUNCTION_INFO_V1(geometry_contained);
+Datum geometry_contained(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                   *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                   *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       bool    result;
+
+       if (geom1->SRID != geom2->SRID)
+       {
+               elog(ERROR,"Operation on two GEOMETRIES with different SRIDs\n");
+               PG_RETURN_NULL();
+       }
+
+
+//printf("in geometry_contained\n");
+
+
+       result = FPle(geom1->bvol.URT.x, geom2->bvol.URT.x) &&
+                                  FPge(geom1->bvol.LLB.x, geom2->bvol.LLB.x) &&
+                                  FPle(geom1->bvol.URT.y, geom2->bvol.URT.y) &&
+                                  FPge(geom1->bvol.LLB.y, geom2->bvol.LLB.y);
+       
+//printf("returning geometry_contained\n");
+       PG_RETURN_BOOL(result);
+}
+
+PG_FUNCTION_INFO_V1(geometry_contain);
+Datum geometry_contain(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                   *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                   *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       bool    result;
+//printf("in geometry_contain\n");
+
+       if (geom1->SRID != geom2->SRID)
+       {
+               elog(ERROR,"Operation on two GEOMETRIES with different SRIDs\n");
+               PG_RETURN_NULL();
+       }
+
+
+       result = FPge(geom1->bvol.URT.x, geom2->bvol.URT.x) &&
+                                  FPle(geom1->bvol.LLB.x, geom2->bvol.LLB.x) &&
+                                  FPge(geom1->bvol.URT.y, geom2->bvol.URT.y) &&
+                                  FPle(geom1->bvol.LLB.y, geom2->bvol.LLB.y);
+       
+
+
+       PG_RETURN_BOOL(result);
+}
+
+bool box3d_ov(BOX3D *box1, BOX3D *box2)
+{
+       bool    result;
+
+       result = 
+
+       /*overlap in x*/
+                ((    FPge(box1->URT.x, box2->URT.x) &&
+                        FPle(box1->LLB.x, box2->URT.x)) ||
+                       (FPge(box2->URT.x, box1->URT.x) &&
+                        FPle(box2->LLB.x, box1->URT.x)))
+       &&
+       /*overlap in y*/
+       ((FPge(box1->URT.y, box2->URT.y) &&
+         FPle(box1->LLB.y, box2->URT.y)) ||
+        (FPge(box2->URT.y, box1->URT.y) &&
+         FPle(box2->LLB.y, box1->URT.y)));
+
+//printf("box3d_ov about to return %i\n",(int) result);        
+       return result;
+}
+
+
+PG_FUNCTION_INFO_V1(geometry_overlap);
+Datum geometry_overlap(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                   *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                   *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+//printf("in geometry_overlap\n");
+
+       if (geom1->SRID != geom2->SRID)
+       {
+               elog(ERROR,"Operation on two GEOMETRIES with different SRIDs\n");
+               PG_RETURN_NULL();
+       }
+
+
+       PG_RETURN_BOOL(box3d_ov(&(geom1->bvol), &(geom2->bvol)));
+}
+
+
+bool is_same_point(POINT3D     *p1, POINT3D    *p2)
+{
+       return ( FPeq(p1->x, p2->x ) && FPeq(p1->y, p2->y ) &&FPeq(p1->z, p2->z ) );
+}
+
+bool is_same_line(LINE3D       *l1, LINE3D     *l2)
+{
+       int i;
+               //lines are directed, so all the points should be the same
+
+       if (l1->npoints != l2->npoints)
+               return FALSE; //simple case, not the same # of points
+
+       for (i=0;i<l2->npoints; i++)
+       {
+               if (!( is_same_point( &l1->points[i], &l2->points[i] ) ) )
+               {
+                       return FALSE;
+               }
+       }
+       return TRUE;
+}
+
+
+/***********************************************************
+ *
+ * Comparision function for use in Binary Tree searches
+ * (ORDER BY, GROUP BY, DISTINCT)
+ *
+ ***********************************************************/
+
+
+PG_FUNCTION_INFO_V1(geometry_lt);
+Datum geometry_lt(PG_FUNCTION_ARGS)
+{
+       GEOMETRY *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       //elog(NOTICE, "geometry_lt called");
+
+       if (geom1->SRID != geom2->SRID)
+       {
+               elog(ERROR,
+                       "Operation on two GEOMETRIES with different SRIDs\n");
+               PG_RETURN_NULL();
+       }
+
+       if  ( ! FPeq(geom1->bvol.LLB.x , geom2->bvol.LLB.x) ) {
+               if  (geom1->bvol.LLB.x < geom2->bvol.LLB.x)
+                       PG_RETURN_BOOL(TRUE);
+       }
+
+       if  ( ! FPeq(geom1->bvol.LLB.y , geom2->bvol.LLB.y) ) {
+               if  (geom1->bvol.LLB.y < geom2->bvol.LLB.y)
+                       PG_RETURN_BOOL(TRUE);
+       }
+
+       if  ( ! FPeq(geom1->bvol.LLB.z , geom2->bvol.LLB.z) ) {
+               if  (geom1->bvol.LLB.z < geom2->bvol.LLB.z)
+                       PG_RETURN_BOOL(TRUE);
+       }
+
+       if  ( ! FPeq(geom1->bvol.URT.x , geom2->bvol.URT.x) ) {
+               if  (geom1->bvol.URT.x < geom2->bvol.URT.x)
+                       PG_RETURN_BOOL(TRUE);
+       }
+
+       if  ( ! FPeq(geom1->bvol.URT.y , geom2->bvol.URT.y) ) {
+               if  (geom1->bvol.URT.y < geom2->bvol.URT.y)
+                       PG_RETURN_BOOL(TRUE);
+       }
+
+       if  ( ! FPeq(geom1->bvol.URT.z , geom2->bvol.URT.z) ) {
+               if  (geom1->bvol.URT.z < geom2->bvol.URT.z)
+                       PG_RETURN_BOOL(TRUE);
+       }
+
+       PG_RETURN_BOOL(FALSE);
+}
+
+PG_FUNCTION_INFO_V1(geometry_le);
+Datum geometry_le(PG_FUNCTION_ARGS)
+{
+       GEOMETRY *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       //elog(NOTICE, "geometry_le called");
+
+       if (geom1->SRID != geom2->SRID)
+       {
+               if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+               if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+               elog(ERROR,
+                       "Operation on two GEOMETRIES with different SRIDs\n");
+               PG_RETURN_NULL();
+       }
+
+       if  ( ! FPeq(geom1->bvol.LLB.x , geom2->bvol.LLB.x) ) {
+               if  (geom1->bvol.LLB.x < geom2->bvol.LLB.x)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_BOOL(TRUE);
+               }
+               if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+               if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+               PG_RETURN_BOOL(FALSE);
+       }
+
+       if  ( ! FPeq(geom1->bvol.LLB.y , geom2->bvol.LLB.y) ) {
+               if  (geom1->bvol.LLB.y < geom2->bvol.LLB.y)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_BOOL(TRUE);
+               }
+               if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+               if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+               PG_RETURN_BOOL(FALSE);
+       }
+
+       if  ( ! FPeq(geom1->bvol.LLB.z , geom2->bvol.LLB.z) ) {
+               if  (geom1->bvol.LLB.z < geom2->bvol.LLB.z)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_BOOL(TRUE);
+               }
+               if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+               if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+               PG_RETURN_BOOL(FALSE);
+       }
+
+       if  ( ! FPeq(geom1->bvol.URT.x , geom2->bvol.URT.x) ) {
+               if  (geom1->bvol.URT.x < geom2->bvol.URT.x)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_BOOL(TRUE);
+               }
+               if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+               if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+               PG_RETURN_BOOL(FALSE);
+       }
+
+       if  ( ! FPeq(geom1->bvol.URT.y , geom2->bvol.URT.y) ) {
+               if  (geom1->bvol.URT.y < geom2->bvol.URT.y)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_BOOL(TRUE);
+               }
+               if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+               if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+               PG_RETURN_BOOL(FALSE);
+       }
+
+       if  ( ! FPeq(geom1->bvol.URT.z , geom2->bvol.URT.z) ) {
+               if  (geom1->bvol.URT.z < geom2->bvol.URT.z)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_BOOL(TRUE);
+               }
+               if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+               if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+               PG_RETURN_BOOL(FALSE);
+       }
+
+       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+
+       PG_RETURN_BOOL(TRUE);
+}
+
+PG_FUNCTION_INFO_V1(geometry_eq);
+Datum geometry_eq(PG_FUNCTION_ARGS)
+{
+       GEOMETRY *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       //elog(NOTICE, "geometry_eq called");
+
+       if (geom1->SRID != geom2->SRID)
+       {
+               if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+               if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+               elog(ERROR,
+                       "Operation on two GEOMETRIES with different SRIDs\n");
+               PG_RETURN_NULL();
+       }
+
+       if  ( ! FPeq(geom1->bvol.LLB.x , geom2->bvol.LLB.x) ) 
+       {
+               if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+               if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+               PG_RETURN_BOOL(FALSE);
+       }
+
+       if  ( ! FPeq(geom1->bvol.LLB.y , geom2->bvol.LLB.y) ) 
+       {
+               if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+               if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+               PG_RETURN_BOOL(FALSE);
+       }
+
+       if  ( ! FPeq(geom1->bvol.LLB.z , geom2->bvol.LLB.z) ) 
+       {
+               if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+               if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+               PG_RETURN_BOOL(FALSE);
+       }
+
+       if  ( ! FPeq(geom1->bvol.URT.x , geom2->bvol.URT.x) ) 
+       {
+               if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+               if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+               PG_RETURN_BOOL(FALSE);
+       }
+
+       if  ( ! FPeq(geom1->bvol.URT.y , geom2->bvol.URT.y) )
+       {
+               if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+               if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+               PG_RETURN_BOOL(FALSE);
+       }
+
+       if  ( ! FPeq(geom1->bvol.URT.z , geom2->bvol.URT.z) ) 
+       {
+               if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+               if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+               PG_RETURN_BOOL(FALSE);
+       }
+
+       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+
+       PG_RETURN_BOOL(TRUE);
+}
+
+PG_FUNCTION_INFO_V1(geometry_ge);
+Datum geometry_ge(PG_FUNCTION_ARGS)
+{
+       GEOMETRY *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       //elog(NOTICE, "geometry_ge called");
+
+       if (geom1->SRID != geom2->SRID)
+       {
+               if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+               if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+               elog(ERROR,
+                       "Operation on two GEOMETRIES with different SRIDs\n");
+               PG_RETURN_NULL();
+       }
+
+       if  ( ! FPeq(geom1->bvol.LLB.x , geom2->bvol.LLB.x) ) {
+               if  (geom1->bvol.LLB.x > geom2->bvol.LLB.x)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_BOOL(TRUE);
+               }
+               PG_RETURN_BOOL(FALSE);
+       }
+
+       if  ( ! FPeq(geom1->bvol.LLB.y , geom2->bvol.LLB.y) ) {
+               if  (geom1->bvol.LLB.y > geom2->bvol.LLB.y)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_BOOL(TRUE);
+               }
+               PG_RETURN_BOOL(FALSE);
+       }
+
+       if  ( ! FPeq(geom1->bvol.LLB.z , geom2->bvol.LLB.z) ) {
+               if  (geom1->bvol.LLB.z > geom2->bvol.LLB.z)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_BOOL(TRUE);
+               }
+               PG_RETURN_BOOL(FALSE);
+       }
+
+       if  ( ! FPeq(geom1->bvol.URT.x , geom2->bvol.URT.x) ) {
+               if  (geom1->bvol.URT.x > geom2->bvol.URT.x)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_BOOL(TRUE);
+               }
+               PG_RETURN_BOOL(FALSE);
+       }
+
+       if  ( ! FPeq(geom1->bvol.URT.y , geom2->bvol.URT.y) ) {
+               if  (geom1->bvol.URT.y > geom2->bvol.URT.y)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_BOOL(TRUE);
+               }
+               PG_RETURN_BOOL(FALSE);
+       }
+
+       if  ( ! FPeq(geom1->bvol.URT.z , geom2->bvol.URT.z) ) {
+               if  (geom1->bvol.URT.z > geom2->bvol.URT.z)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_BOOL(TRUE);
+               }
+               PG_RETURN_BOOL(FALSE);
+       }
+
+       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+
+       PG_RETURN_BOOL(TRUE);
+}
+
+PG_FUNCTION_INFO_V1(geometry_gt);
+Datum geometry_gt(PG_FUNCTION_ARGS)
+{
+       GEOMETRY *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       //elog(NOTICE, "geometry_gt called");
+
+       if (geom1->SRID != geom2->SRID)
+       {
+               if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 )
+                       pfree(geom1);
+               if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 )
+                       pfree(geom2);
+               elog(ERROR,
+                       "Operation on two GEOMETRIES with different SRIDs\n");
+               PG_RETURN_NULL();
+       }
+
+       if  ( ! FPeq(geom1->bvol.LLB.x , geom2->bvol.LLB.x) ) {
+               if  (geom1->bvol.LLB.x > geom2->bvol.LLB.x)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_BOOL(TRUE);
+               }
+       }
+
+       if  ( ! FPeq(geom1->bvol.LLB.y , geom2->bvol.LLB.y) ) {
+               if  (geom1->bvol.LLB.y > geom2->bvol.LLB.y)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_BOOL(TRUE);
+               }
+       }
+
+       if  ( ! FPeq(geom1->bvol.LLB.z , geom2->bvol.LLB.z) ) {
+               if  (geom1->bvol.LLB.z > geom2->bvol.LLB.z)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_BOOL(TRUE);
+               }
+       }
+
+       if  ( ! FPeq(geom1->bvol.URT.x , geom2->bvol.URT.x) ) {
+               if  (geom1->bvol.URT.x > geom2->bvol.URT.x)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_BOOL(TRUE);
+               }
+       }
+
+       if  ( ! FPeq(geom1->bvol.URT.y , geom2->bvol.URT.y) ) {
+               if  (geom1->bvol.URT.y > geom2->bvol.URT.y)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_BOOL(TRUE);
+               }
+       }
+
+       if  ( ! FPeq(geom1->bvol.URT.z , geom2->bvol.URT.z) ) {
+               if  (geom1->bvol.URT.z > geom2->bvol.URT.z)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_BOOL(TRUE);
+               }
+       }
+
+       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+
+       PG_RETURN_BOOL(FALSE);
+}
+
+PG_FUNCTION_INFO_V1(geometry_cmp);
+Datum geometry_cmp(PG_FUNCTION_ARGS)
+{
+       GEOMETRY *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       //elog(NOTICE, "geometry_cmp called");
+
+       if (geom1->SRID != geom2->SRID)
+       {
+               if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 )
+                       pfree(geom1);
+               if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 )
+                       pfree(geom2);
+               elog(ERROR,
+                       "Operation on two GEOMETRIES with different SRIDs\n");
+               PG_RETURN_NULL();
+       }
+
+       if  ( ! FPeq(geom1->bvol.LLB.x , geom2->bvol.LLB.x) ) {
+               if  (geom1->bvol.LLB.x < geom2->bvol.LLB.x)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_INT32(-1);
+               }
+               if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+               if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+               PG_RETURN_INT32(1);
+       }
+
+       if  ( ! FPeq(geom1->bvol.LLB.y , geom2->bvol.LLB.y) ) {
+               if  (geom1->bvol.LLB.y < geom2->bvol.LLB.y)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_INT32(-1);
+               }
+               if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+               if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+               PG_RETURN_INT32(1);
+       }
+
+       if  ( ! FPeq(geom1->bvol.LLB.z , geom2->bvol.LLB.z) ) {
+               if  (geom1->bvol.LLB.z < geom2->bvol.LLB.z)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_INT32(-1);
+               }
+               if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+               if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+               PG_RETURN_INT32(1);
+       }
+
+       if  ( ! FPeq(geom1->bvol.URT.x , geom2->bvol.URT.x) ) {
+               if  (geom1->bvol.URT.x < geom2->bvol.URT.x)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_INT32(-1);
+               }
+               if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+               if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+               PG_RETURN_INT32(1);
+       }
+
+       if  ( ! FPeq(geom1->bvol.URT.y , geom2->bvol.URT.y) ) {
+               if  (geom1->bvol.URT.y < geom2->bvol.URT.y)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_INT32(-1);
+               }
+               if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+               if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+               PG_RETURN_INT32(1);
+       }
+
+       if  ( ! FPeq(geom1->bvol.URT.z , geom2->bvol.URT.z) ) {
+               if  (geom1->bvol.URT.z < geom2->bvol.URT.z)
+               {
+                       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+                       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+                       PG_RETURN_INT32(-1);
+               }
+               if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+               if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+               PG_RETURN_INT32(1);
+       }
+
+       if ( (Pointer *)PG_GETARG_DATUM(0) != (Pointer *)geom1 ) pfree(geom1);
+       if ( (Pointer *)PG_GETARG_DATUM(1) != (Pointer *)geom2 ) pfree(geom2);
+
+       PG_RETURN_INT32(0);
+}
+
+
+//order of points in a ring IS important
+//order of rings is also important
+bool is_same_polygon(POLYGON3D *poly1, POLYGON3D       *poly2)
+{
+       int     i;
+       int     numb_points;
+       POINT3D *pts1, *pts2;
+
+
+       if (poly1->nrings != poly2->nrings)
+               return FALSE;
+       
+       numb_points = 0;
+       for (i=0; i< poly1->nrings; i++)
+       {
+               if (poly1->npoints[i] != poly2->npoints[i])
+                       return FALSE;
+               numb_points += poly1->npoints[i];
+       }
+
+       pts1 = (POINT3D *) ( (char *)&(poly1->npoints[poly1->nrings] )  );
+       pts1 = (POINT3D *) MAXALIGN(pts1);
+       
+
+       pts2 = (POINT3D *) ( (char *)&(poly2->npoints[poly2->nrings] )  );
+       pts2 = (POINT3D *) MAXALIGN(pts2);
+
+       for (i=0; i< numb_points; i++)
+       {
+               if (!( is_same_point( &pts1[i], &pts2[i] ) ) )
+               {
+                       return FALSE;
+               }
+
+       }
+       return TRUE;
+}
+
+
+
+//FIXED: 
+// geom1 same as geom2
+//  iff  
+//     + have same type
+//     + have same # objects
+//     + have same bvol
+//     + each object in geom1 has a corresponding object in geom2 (see above)
+PG_FUNCTION_INFO_V1(geometry_same);
+Datum geometry_same(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                   *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                   *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+       bool                            result;
+       int                             i,j;
+       bool                            *already_hit;
+       int32                           type1,type2;
+       int32                           *offsets1,*offsets2;
+       char                            *o1,*o2;
+
+       if (geom1->SRID != geom2->SRID)
+       {
+               elog(ERROR,"Operation on two GEOMETRIES with different SRIDs\n");
+               PG_RETURN_NULL();
+       }
+
+
+//printf("in geometry_same\n");
+
+       //This was removed because this doesnt actually refer to the geometry, but how it is represented
+       // to the user
+       //easy tests
+       //if (geom1->type != geom2-> type)
+       //      PG_RETURN_BOOL(FALSE);
+
+       
+
+       if (geom1->nobjs != geom2->nobjs)
+               PG_RETURN_BOOL(FALSE);  
+
+       result = DatumGetBool(DirectFunctionCall2(box3d_same,
+                                                               PointerGetDatum(&geom1->bvol), 
+                                                               PointerGetDatum(&geom2->bvol)   )     );
+
+       if (result == FALSE)
+               PG_RETURN_BOOL(FALSE);
+
+       if (geom1->nobjs<1)
+               return FALSE; // no objects (probably a BOXONLYTYPE) and bvols dont match
+
+//     printf("        +have to do it the hard way");
+
+       already_hit = (bool *) palloc (geom2->nobjs * sizeof(bool) );
+       memset(already_hit, 0, geom2->nobjs * sizeof(bool) ); //all false
+       
+
+       offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
+       offsets2 = (int32 *) ( ((char *) &(geom2->objType[0] ))+ sizeof(int32) * geom2->nobjs ) ;
+
+
+       //now have to do a scan of each object
+
+       for (j=0; j< geom1->nobjs; j++)         //for each object in geom1
+       {
+               o1 = (char *) geom1 +offsets1[j] ;  
+               type1=  geom1->objType[j];
+
+               for(i=0;i<geom1->nobjs; i++)            //for each object in geom2
+               {
+                       o2 = (char *) geom2 +offsets2[i] ;  
+                       type2=  geom2->objType[j];
+                       
+                       if (  (type1 == type2) && (!(already_hit[i])) )
+                       {
+                               //see if they match
+                               if (type1 == POINTTYPE)
+                               {
+                                       if (is_same_point((POINT3D *) o1,(POINT3D *) o2))
+                                       {
+                                               already_hit[i] = TRUE;
+                                               break;
+                                       } 
+                               }
+                               if (type1 == LINETYPE)
+                               {
+                                       if (is_same_line((LINE3D *) o1,(LINE3D *) o2))
+                                       {
+                                               already_hit[i] = TRUE;
+                                               break;
+                                       } 
+
+                               }
+                               if (type2 == POLYGONTYPE)
+                               {
+                                       if (is_same_polygon((POLYGON3D *) o1,(POLYGON3D *) o2))
+                                       {
+                                               already_hit[i] = TRUE;
+                                               break;
+                                       } 
+                               }       
+                       }
+               }
+               if (i == geom1->nobjs)
+                       PG_RETURN_BOOL(FALSE);   // couldnt find match
+       }
+       PG_RETURN_BOOL(TRUE);
+}
+
+/*******************************************************
+ *Box3d routines
+ *******************************************************/
+
+
+
+/*             box_overlap             -               does box1 overlap box2?
+ */
+PG_FUNCTION_INFO_V1(box3d_overlap);
+Datum box3d_overlap(PG_FUNCTION_ARGS)
+{
+       BOX3D              *box1 = (BOX3D *) PG_GETARG_POINTER(0);
+       BOX3D              *box2 = (BOX3D *) PG_GETARG_POINTER(1);
+
+       PG_RETURN_BOOL(box3d_ov(box1, box2));
+}
+
+/*             box_overleft    -               is the right edge of box1 to the left of
+ *                                                             the right edge of box2?
+ *
+ *             This is "less than or equal" for the end of a time range,
+ *             when time ranges are stored as rectangles.
+ */
+PG_FUNCTION_INFO_V1(box3d_overleft);
+Datum box3d_overleft(PG_FUNCTION_ARGS)
+{
+       BOX3D              *box1 = (BOX3D *) PG_GETARG_POINTER(0);
+       BOX3D              *box2 = (BOX3D *) PG_GETARG_POINTER(1);
+
+       bool    result;
+
+       result = FPle(box1->URT.x, box2->URT.x);
+
+//printf("box3d_overleft about to return %i\n",(int) result);  
+
+       PG_RETURN_BOOL(result);
+}
+
+/*             box_right               -               is box1 strictly right of box2?
+ */
+PG_FUNCTION_INFO_V1(box3d_right);
+Datum box3d_right(PG_FUNCTION_ARGS)
+{
+       BOX3D              *box1 = (BOX3D *) PG_GETARG_POINTER(0);
+       BOX3D              *box2 = (BOX3D *) PG_GETARG_POINTER(1);
+
+       bool    result;
+
+       result = FPgt(box1->LLB.x, box2->URT.x);
+//printf("box3d_rightabout to return %i\n",(int) result);      
+
+       PG_RETURN_BOOL(result);
+}
+
+/*             box_contain             -               does box1 contain box2?
+ */
+PG_FUNCTION_INFO_V1(box3d_contain);
+Datum box3d_contain(PG_FUNCTION_ARGS)
+{
+       BOX3D              *box1 = (BOX3D *) PG_GETARG_POINTER(0);
+       BOX3D              *box2 = (BOX3D *) PG_GETARG_POINTER(1);
+
+       bool result;
+
+       result = FPge(box1->URT.x, box2->URT.x) &&
+                                  FPle(box1->LLB.x, box2->LLB.x) &&
+                                  FPge(box1->URT.y, box2->URT.y) &&
+                                  FPle(box1->LLB.y, box2->LLB.y);
+       //printf("box3d_contain about to return %i\n",(int) result);    
+
+
+       PG_RETURN_BOOL(result);
+}
+
+
+/******************************************************
+ * RTREE index requires these 3 functions
+ ******************************************************/
+
+PG_FUNCTION_INFO_V1(geometry_union);
+Datum geometry_union(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                   *geom1 =  (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                   *geom2 =  (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+       GEOMETRY                   *result = (GEOMETRY *) palloc(sizeof(GEOMETRY) );
+       BOX3D                           *n;
+
+       if (geom1->SRID != geom2->SRID)
+       {
+               elog(ERROR,"Operation on two GEOMETRIES with different SRIDs\n");
+               PG_RETURN_NULL();
+       }
+
+
+       result->size = sizeof(GEOMETRY);
+       result->type = BBOXONLYTYPE;
+       result->nobjs = -1;
+       result->SRID = geom1->SRID;
+       result->scale = geom1->scale;
+       result->offsetX = geom1->offsetX;
+       result->offsetY = geom1->offsetY;
+
+
+       n = &result->bvol;
+
+       //cheat - just change the bbox
+
+        n->URT.x = max(geom1->bvol.URT.x, geom2->bvol.URT.x);
+        n->URT.y = max(geom1->bvol.URT.y, geom2->bvol.URT.y);
+        n->LLB.x = min(geom1->bvol.LLB.x, geom2->bvol.LLB.x);
+        n->LLB.y = min(geom1->bvol.LLB.y, geom2->bvol.LLB.y);
+
+       
+        n->URT.z = max(geom1->bvol.URT.z, geom2->bvol.URT.z);
+        n->LLB.z = min(geom1->bvol.LLB.z, geom2->bvol.LLB.z);
+
+         PG_FREE_IF_COPY(geom1, 0);
+        PG_FREE_IF_COPY(geom2, 1);
+
+       PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(geometry_inter);
+Datum geometry_inter(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                   *geom1 =  (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       GEOMETRY                   *geom2 =  (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+       GEOMETRY                   *result = (GEOMETRY *) palloc(sizeof(GEOMETRY) );
+
+       if (geom1->SRID != geom2->SRID)
+       {
+               elog(ERROR,"Operation on two GEOMETRIES with different SRIDs\n");
+               PG_RETURN_NULL();
+       }
+
+       result->size = sizeof(GEOMETRY);
+       result->type = BBOXONLYTYPE;
+       result->nobjs = -1;
+       result->SRID = geom1->SRID;
+       result->scale = geom1->scale;
+       result->offsetX = geom1->offsetX;
+       result->offsetY = geom1->offsetY;
+
+       result->bvol.URT.x = min(geom1->bvol.URT.x, geom2->bvol.URT.x);
+       result->bvol.URT.y = min(geom1->bvol.URT.y, geom2->bvol.URT.y);
+       result->bvol.URT.z = min(geom1->bvol.URT.z, geom2->bvol.URT.z);
+
+       result->bvol.LLB.x = max(geom1->bvol.LLB.x, geom2->bvol.LLB.x);
+       result->bvol.LLB.y = max(geom1->bvol.LLB.y, geom2->bvol.LLB.y);
+       result->bvol.LLB.z = max(geom1->bvol.LLB.z, geom2->bvol.LLB.z);
+
+    if (result->bvol.URT.x < result->bvol.LLB.x || result->bvol.URT.y < result->bvol.LLB.y)
+    {
+               pfree(result);
+        /* Indicate "no intersection" by returning NULL pointer */
+        result = NULL;
+    }
+       PG_FREE_IF_COPY(geom1, 0);
+    PG_FREE_IF_COPY(geom2, 1);
+
+       PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(geometry_size);
+Datum geometry_size(PG_FUNCTION_ARGS)
+{
+       Pointer          aptr = PG_GETARG_POINTER(0);
+
+    float              *size = (float *) PG_GETARG_POINTER(1);
+    GEOMETRY           *a;
+       float                   xdim,ydim;
+
+    if (aptr == NULL)
+    {
+        *size = 0.0;
+               //printf("      + aprt==null return in 0.0\n");
+        PG_RETURN_VOID();
+    }
+
+       a = (GEOMETRY *)   PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+    if (a->bvol.URT.x <= a->bvol.LLB.x ||
+        a->bvol.URT.y <= a->bvol.LLB.y)
+        *size = 0.0;
+    else
+    {
+               xdim = (a->bvol.URT.x - a->bvol.LLB.x);
+        ydim = (a->bvol.URT.y - a->bvol.LLB.y);
+
+        *size = (float) (xdim * ydim);
+    }
+
+       //printf("      + about to return (and free) with %e\n",*size);
+
+    /* Avoid leaking memory when handed toasted input. */
+       PG_FREE_IF_COPY(a, 0);
+       
+       PG_RETURN_VOID();
+}
diff --git a/hwgeom/postgis_proj.c b/hwgeom/postgis_proj.c
new file mode 100644 (file)
index 0000000..9d09440
--- /dev/null
@@ -0,0 +1,646 @@
+
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2001-2003 Refractions Research Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ *
+ **********************************************************************
+ * $Log$
+ * Revision 1.1  2004/09/20 07:50:06  strk
+ * prepared to contain old internal representation code
+ *
+ * Revision 1.14  2004/09/16 15:50:59  mleslie
+ * Added the distance_sphere function to calculate the distance between two points
+ * on an earth-sized sphere using an algorithm implemented by Bruno Wolff III.
+ * Added the postgresql loader function.
+ *
+ * Revision 1.13  2004/04/28 22:26:02  pramsey
+ * Fixed spelling mistake in header text.
+ *
+ * Revision 1.12  2004/02/06 00:42:25  dblasby
+ * moved forward declarations from postgis.h to postgis_proj.c
+ *
+ * Revision 1.11  2004/02/05 20:31:48  dblasby
+ * Optimized the curvature method (doesnt have to calculate e2)
+ *
+ * Revision 1.10  2004/02/05 20:21:14  dblasby
+ * Added 'curvature method' for cases where the original algorithm breaks down.
+ *
+ * Revision 1.9  2004/02/04 02:53:20  dblasby
+ * applied patricia tozer's patch (distance function was taking acos of something
+ * just slightly outside [-1,1]).
+ *
+ * Revision 1.8  2003/12/04 18:58:35  dblasby
+ * changed david skae to skea
+ *
+ * Revision 1.7  2003/07/01 18:30:55  pramsey
+ * Added CVS revision headers.
+ *
+ *
+ **********************************************************************/
+
+#include "postgres.h"
+
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "access/gist.h"
+#include "access/itup.h"
+#include "access/rtree.h"
+
+
+#include "fmgr.h"
+
+
+#include "postgis.h"
+#include "utils/elog.h"
+
+#define SHOW_DIGS_DOUBLE 15
+#define MAX_DIGS_DOUBLE (SHOW_DIGS_DOUBLE + 6 + 1 + 3 +1)
+
+
+//     distance from -126 49  to -126 49.011096139863 in 'SPHEROID["GRS_1980",6378137,298.257222101]' is 1234.000
+
+
+double distance_sphere_method(double lat1, double long1,double lat2,double long2, SPHEROID *sphere);
+double distance_ellipse_calculation(double lat1, double long1,
+                                       double lat2, double long2,
+                                       SPHEROID *sphere);
+
+//use the WKT definition of an ellipsoid
+// ie. SPHEROID["name",A,rf] or SPHEROID("name",A,rf)
+//       SPHEROID["GRS_1980",6378137,298.257222101]
+// wkt says you can use "(" or "["
+
+PG_FUNCTION_INFO_V1(ellipsoid_in);
+Datum ellipsoid_in(PG_FUNCTION_ARGS)
+{
+       char               *str = PG_GETARG_CSTRING(0);
+       SPHEROID           *sphere = (SPHEROID *) palloc(sizeof(SPHEROID));
+       int                nitems;
+       double     rf;
+
+
+       memset(sphere,0, sizeof(SPHEROID));
+
+       if (strstr(str,"SPHEROID") !=  str )
+       {
+                elog(ERROR,"SPHEROID parser - doesnt start with SPHEROID");
+                pfree(sphere);
+                PG_RETURN_NULL();
+       }
+
+       nitems = sscanf(str,"SPHEROID[\"%19[^\"]\",%lf,%lf]",sphere->name,&sphere->a,&rf);
+
+       if ( nitems==0)
+               nitems = sscanf(str,"SPHEROID(\"%19[^\"]\",%lf,%lf)",sphere->name,&sphere->a,&rf);
+
+       if (nitems != 3)
+       {
+                elog(ERROR,"SPHEROID parser - couldnt parse the spheroid");
+                pfree(sphere);
+                PG_RETURN_NULL();
+       }
+
+       sphere->f = 1.0/rf;
+       sphere->b = sphere->a - (1.0/rf)*sphere->a;
+       sphere->e_sq = ((sphere->a*sphere->a) - (sphere->b*sphere->b) )/ (sphere->a*sphere->a);
+       sphere->e = sqrt(sphere->e_sq);
+
+       PG_RETURN_POINTER(sphere);
+
+}
+
+
+
+PG_FUNCTION_INFO_V1(ellipsoid_out);
+Datum ellipsoid_out(PG_FUNCTION_ARGS)
+{
+       SPHEROID  *sphere = (SPHEROID *) PG_GETARG_POINTER(0);
+       char    *result;
+
+       result = palloc(MAX_DIGS_DOUBLE + MAX_DIGS_DOUBLE + 20 + 9 + 2);
+
+       sprintf(result,"SPHEROID(\"%s\",%.15g,%.15g)", sphere->name,sphere->a, 1.0/sphere->f);
+
+       PG_RETURN_CSTRING(result);
+}
+
+//support function for distance calc
+       //code is taken from David Skea
+       //Geographic Data BC, Province of British Columbia, Canada.
+       // Thanks to GDBC and David Skea for allowing this to be
+       // put in PostGIS.
+double deltaLongitude(double azimuth, double sigma, double tsm,SPHEROID *sphere)
+{
+       // compute the expansion C
+       double das,C;
+       double ctsm,DL;
+
+       das = cos(azimuth)*cos(azimuth);
+       C = sphere->f/16.0 * das * (4.0 + sphere->f * (4.0 - 3.0 * das));
+       // compute the difference in longitude
+
+       ctsm = cos(tsm);
+       DL = ctsm + C * cos(sigma) * (-1.0 + 2.0 * ctsm*ctsm);
+       DL = sigma + C * sin(sigma) * DL;
+       return (1.0 - C) * sphere->f * sin(azimuth) * DL;
+}
+
+
+//support function for distance calc
+       //code is taken from David Skea
+       //Geographic Data BC, Province of British Columbia, Canada.
+       // Thanks to GDBC and David Skea for allowing this to be
+       // put in PostGIS.
+double mu2(double azimuth,SPHEROID *sphere)
+{
+       double    e2;
+
+       e2 = sqrt(sphere->a*sphere->a-sphere->b*sphere->b)/sphere->b;
+       return cos(azimuth)*cos(azimuth) * e2*e2;
+}
+
+
+//support function for distance calc
+       //code is taken from David Skea
+       //Geographic Data BC, Province of British Columbia, Canada.
+       // Thanks to GDBC and David Skea for allowing this to be
+       // put in PostGIS.
+double bigA(double u2)
+{
+       return 1.0 + u2/256.0 * (64.0 + u2 * (-12.0 + 5.0 * u2));
+}
+
+
+//support function for distance calc
+       //code is taken from David Skea
+       //Geographic Data BC, Province of British Columbia, Canada.
+       // Thanks to GDBC and David Skea for allowing this to be
+       // put in PostGIS.
+double bigB(double u2)
+{
+       return u2/512.0 * (128.0 + u2 * (-64.0 + 37.0 * u2));
+}
+
+
+
+double distance_ellipse(double lat1, double long1,
+                                       double lat2, double long2,
+                                       SPHEROID *sphere)
+{
+               double result;
+
+           if ( (lat1==lat2) && (long1 == long2) )
+               {
+                       return 0.0; // same point, therefore zero distance
+               }
+
+               result = distance_ellipse_calculation(lat1,long1,lat2,long2,sphere);
+//             result2 =  distance_sphere_method(lat1, long1,lat2,long2, sphere);
+
+//elog(NOTICE,"delta = %lf, skae says: %.15lf,2 circle says: %.15lf",(result2-result),result,result2);
+//elog(NOTICE,"2 circle says: %.15lf",result2);
+
+               if (result != result)  // NaN check (x==x for all x except NaN by IEEE definition)
+               {
+                       result =  distance_sphere_method(lat1, long1,lat2,long2, sphere);
+               }
+
+               return result;
+}
+
+//given 2 lat/longs and ellipse, find the distance
+// note original r = 1st, s=2nd location
+double distance_ellipse_calculation(double lat1, double long1,
+                                       double lat2, double long2,
+                                       SPHEROID *sphere)
+{
+       //code is taken from David Skea
+       //Geographic Data BC, Province of British Columbia, Canada.
+       // Thanks to GDBC and David Skea for allowing this to be
+       // put in PostGIS.
+
+       double L1,L2,sinU1,sinU2,cosU1,cosU2;
+       double dl,dl1,dl2,dl3,cosdl1,sindl1;
+       double cosSigma,sigma,azimuthEQ,tsm;
+       double u2,A,B;
+       double dsigma;
+
+       double TEMP;
+
+       int iterations;
+
+
+       L1 = atan((1.0 - sphere->f ) * tan( lat1) );
+       L2 = atan((1.0 - sphere->f ) * tan( lat2) );
+       sinU1 = sin(L1);
+       sinU2 = sin(L2);
+       cosU1 = cos(L1);
+       cosU2 = cos(L2);
+
+       dl = long2- long1;
+       dl1 = dl;
+       cosdl1 = cos(dl);
+       sindl1 = sin(dl);
+       iterations = 0;
+       do {
+               cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosdl1;
+               sigma = acos(cosSigma);
+               azimuthEQ = asin((cosU1 * cosU2 * sindl1)/sin(sigma));
+
+                       // patch from patrica tozer to handle minor mathematical stability problem
+               TEMP = cosSigma - (2.0 * sinU1 * sinU2)/(cos(azimuthEQ)*cos(azimuthEQ));
+               if(TEMP > 1)
+               {
+                          TEMP = 1;
+               }
+               else if(TEMP < -1)
+               {
+                          TEMP = -1;
+               }
+        tsm = acos(TEMP);
+
+
+               //tsm = acos(cosSigma - (2.0 * sinU1 * sinU2)/(cos(azimuthEQ)*cos(azimuthEQ)));
+               dl2 = deltaLongitude(azimuthEQ, sigma, tsm,sphere);
+               dl3 = dl1 - (dl + dl2);
+               dl1 = dl + dl2;
+               cosdl1 = cos(dl1);
+               sindl1 = sin(dl1);
+               iterations++;
+       } while ( (iterations<999) && (fabs(dl3) > 1.0e-032));
+
+          // compute expansions A and B
+       u2 = mu2(azimuthEQ,sphere);
+       A = bigA(u2);
+       B = bigB(u2);
+
+       // compute length of geodesic
+       dsigma = B * sin(sigma) * (cos(tsm) + (B*cosSigma*(-1.0 + 2.0 * (cos(tsm)*cos(tsm))))/4.0);
+       return sphere->b * (A * (sigma - dsigma));
+}
+
+
+double length2d_ellipse_linestring(LINE3D      *line, SPHEROID         *sphere)
+{
+
+       int     i;
+       POINT3D *frm,*to;
+       double  dist = 0.0;
+
+       if (line->npoints <2)
+               return 0.0;     //must have >1 point to make sense
+
+       frm = &line->points[0];
+
+       for (i=1; i<line->npoints;i++)
+       {
+               to = &line->points[i];
+
+               dist +=  distance_ellipse(frm->y*M_PI/180.0 , frm->x*M_PI/180.0,
+                                               to->y*M_PI/180.0 , to->x*M_PI/180.0,
+                                               sphere);
+
+               frm = to;
+       }
+       return dist;
+}
+
+double length3d_ellipse_linestring(LINE3D      *line, SPHEROID         *sphere)
+{
+       int     i;
+       POINT3D *frm,*to;
+       double  dist = 0.0;
+       double  dist_ellipse;
+
+       if (line->npoints <2)
+               return 0.0;     //must have >1 point to make sense
+
+       frm = &line->points[0];
+
+       for (i=1; i<line->npoints;i++)
+       {
+               to = &line->points[i];
+
+               dist_ellipse =  distance_ellipse(frm->y*M_PI/180.0 , frm->x*M_PI/180.0,
+                                               to->y*M_PI/180.0 , to->x*M_PI/180.0,
+                                               sphere);
+
+               dist += sqrt(dist_ellipse*dist_ellipse + (frm->z*frm->z) );
+
+               frm = to;
+       }
+       return dist;
+
+
+}
+
+
+// length_ellipsoid(GEOMETRY, SPHEROID)
+// find the "length of a geometry"
+// length2d(point) = 0
+// length2d(line) = length of line
+// length2d(polygon) = 0
+// uses ellipsoidal math to find the distance
+//// x's are longitude, and y's are latitude - both in decimal degrees
+
+PG_FUNCTION_INFO_V1(length_ellipsoid);
+Datum length_ellipsoid(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                      *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               SPHEROID                *sphere = (SPHEROID *) PG_GETARG_POINTER(1);
+
+       int32                           *offsets1;
+       char                            *o1;
+       int32                           type1,j;
+       LINE3D                  *line;
+       double                  dist = 0.0;
+
+       offsets1 = (int32 *) ( ((char *) &(geom->objType[0] ))+ sizeof(int32) * geom->nobjs ) ;
+
+
+       //now have to do a scan of each object
+
+       for (j=0; j< geom->nobjs; j++)          //for each object in geom1
+       {
+               o1 = (char *) geom +offsets1[j] ;
+               type1=  geom->objType[j];
+               if (type1 == LINETYPE)  //LINESTRING
+               {
+                       line = (LINE3D *) o1;
+                       dist += length2d_ellipse_linestring(line,sphere);
+               }
+       }
+       PG_RETURN_FLOAT8(dist);
+
+
+
+}
+
+// length3d_ellipsoid(GEOMETRY, SPHEROID)
+// find the "length of a geometry"
+// length3d(point) = 0
+// length3d(line) = length of line
+// length3d(polygon) = 0
+// uses ellipsoidal math to find the distance on the XY plane, then
+//  uses simple Pythagoras theorm to find the 3d distance on each segment
+// x's are longitude, and y's are latitude - both in decimal degrees
+
+PG_FUNCTION_INFO_V1(length3d_ellipsoid);
+Datum length3d_ellipsoid(PG_FUNCTION_ARGS)
+{
+               GEOMETRY                      *geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               SPHEROID                *sphere = (SPHEROID *) PG_GETARG_POINTER(1);
+
+       int32                           *offsets1;
+       char                            *o1;
+       int32                           type1,j;
+       LINE3D                  *line;
+       double                  dist = 0.0;
+
+       offsets1 = (int32 *) ( ((char *) &(geom->objType[0] ))+ sizeof(int32) * geom->nobjs ) ;
+
+
+       //now have to do a scan of each object
+
+       for (j=0; j< geom->nobjs; j++)          //for each object in geom1
+       {
+               o1 = (char *) geom +offsets1[j] ;
+               type1=  geom->objType[j];
+               if (type1 == LINETYPE)  //LINESTRING
+               {
+                       line = (LINE3D *) o1;
+                       dist += length3d_ellipse_linestring(line,sphere);
+               }
+       }
+       PG_RETURN_FLOAT8(dist);
+}
+
+/*
+ * This algorithm was taken from the geo_distance function of the 
+ * earthdistance package contributed by Bruno Wolff III.
+ * It was altered to accept GEOMETRY objects and return results in
+ * meters.
+ */
+PG_FUNCTION_INFO_V1(distance_sphere);
+Datum distance_sphere(PG_FUNCTION_ARGS)
+{
+               const double EARTH_RADIUS = 6370986.884258304;
+               const double TWO_PI = 2.0 * M_PI;
+               GEOMETRY                *geom1 = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               GEOMETRY                *geom2 = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+               POINT3D                 *pt1, *pt2;
+               int32                   *offsets1;
+               int32                   *offsets2;
+               char                    *o;
+
+               double                  long1, lat1, long2, lat2;
+               double                  longdiff;
+               double                  sino;
+
+       if (geom1->SRID != geom2->SRID)
+       {
+               elog(ERROR, "optimistic_overlap:Operation on two GEOMETRIES with differenc SRIDs\n");
+               PG_RETURN_NULL();
+       }
+
+       if (geom1->type != POINTTYPE)
+       {
+               elog(ERROR, "optimistic_overlap: first arg isnt a point\n");
+               PG_RETURN_NULL();
+       }
+
+       if (geom2->type != POINTTYPE)
+       {
+               elog(ERROR, "optimistic_overlap: second arg isnt a point\n");
+               PG_RETURN_NULL();
+       }
+
+       offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs );
+       offsets2 = (int32 *) ( ((char *) &(geom2->objType[0] ))+ sizeof(int32) * geom2->nobjs );
+       o = (char *) geom1 + offsets1[0];
+       pt1 = (POINT3D *) o;
+
+       o = (char *) geom2 + offsets2[0];
+       pt2 = (POINT3D *) o;
+
+       /*
+        * Start geo_distance code.  Longitude is degrees west of
+        * Greenwich, and thus is negative from what normal things
+        * will supply the function.
+        */
+        long1 = -1 * (pt1->x / 360.0) * TWO_PI;
+        lat1 = (pt1->y / 360.0) * TWO_PI;
+
+       long2 = -1 * (pt2->x / 360.0) * TWO_PI;
+       lat2 = (pt2->y / 360.0) * TWO_PI;
+
+        /* compute difference in longitudes - want < 180 degrees */
+        longdiff = fabs(long1 - long2);
+        if (longdiff > M_PI)
+                longdiff = TWO_PI - longdiff;
+
+        sino = sqrt(sin(fabs(lat1 - lat2) / 2.) * sin(fabs(lat1 - lat2) / 2.) +
+                cos(lat1) * cos(lat2) * sin(longdiff / 2.) * sin(longdiff / 2.));
+        if (sino > 1.)
+                sino = 1.;
+        PG_RETURN_FLOAT8(2. * EARTH_RADIUS * asin(sino));
+
+/*     PG_RETURN_FLOAT8(distance_sphere_method(pt1->y*M_PI/180.0 ,
+                                               pt1->x*M_PI/180.0 ,
+                                               pt2->y*M_PI/180.0 ,
+                                               pt2->x*M_PI/180.0 ,
+                                               sphere));
+*/
+
+}
+
+
+//distance (geometry,geometry, sphere)
+// -geometrys MUST be points
+PG_FUNCTION_INFO_V1(distance_ellipsoid);
+Datum distance_ellipsoid(PG_FUNCTION_ARGS)
+{
+               SPHEROID                *sphere = (SPHEROID *) PG_GETARG_POINTER(2);
+               GEOMETRY                      *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+               GEOMETRY                      *geom2 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
+
+               POINT3D *pt1,*pt2;
+               int32                           *offsets1;
+               int32                           *offsets2;
+               char                            *o;
+
+
+       if (geom1->SRID != geom2->SRID)
+       {
+               elog(ERROR,"optimistic_overlap:Operation on two GEOMETRIES with different SRIDs\n");
+               PG_RETURN_NULL();
+       }
+
+       if (geom1->type != POINTTYPE)
+       {
+               elog(ERROR,"optimistic_overlap: first arg isnt a point\n");
+               PG_RETURN_NULL();
+       }
+       if (geom2->type != POINTTYPE)
+       {
+               elog(ERROR,"optimistic_overlap: second arg isnt a point\n");
+               PG_RETURN_NULL();
+       }
+
+       offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
+       offsets2 = (int32 *) ( ((char *) &(geom2->objType[0] ))+ sizeof(int32) * geom2->nobjs ) ;
+       o = (char *) geom1 +offsets1[0] ;
+       pt1 = (POINT3D *) o;
+
+       o = (char *) geom2 +offsets2[0] ;
+       pt2 = (POINT3D *) o;
+
+       PG_RETURN_FLOAT8(distance_ellipse(pt1->y*M_PI/180.0 ,pt1->x*M_PI/180.0 ,
+                                                         pt2->y*M_PI/180.0 ,pt2->x*M_PI/180.0 , sphere) );
+
+//double       distance_ellipse(double lat1, double long1,
+//                                     double lat2, double long2,
+//                                     SPHEROID *sphere)
+
+
+}
+
+
+/*
+ *  For some lat/long points, the above method doesnt calculate the distance very well.
+ *  Typically this is for two lat/long points that are very very close together (<10cm).
+ *  This gets worse closer to the equator.
+ *
+ *   This method works very well for very close together points, not so well if they're
+ *   far away (>1km).
+ *
+ *  METHOD:
+ *    We create two circles (with Radius R and Radius S) and use these to calculate the distance.
+ *
+ *    The first (R) is basically a (north-south) line of longitude.
+ *    Its radius is approximated by looking at the ellipse. Near the equator R = 'a' (earth's major axis)
+ *    near the pole R = 'b' (earth's minor axis).
+ *
+ *    The second (S) is basically a (east-west) line of lattitude.
+ *    Its radius runs from 'a' (major axis) at the equator, and near 0 at the poles.
+ *
+ *
+ *                North pole
+ *                *
+ *               *
+ *              *\--S--
+ *             *  R   +
+ *            *    \  +
+ *           *     A\ +
+ *          * ------ \         Equator/centre of earth
+ *           *
+ *            *
+ *             *
+ *              *
+ *               *
+ *                *
+ *                South pole
+ *  (side view of earth)
+ *
+ *   Angle A is lat1
+ *   R is the distance from the centre of the earth to the lat1/long1 point on the surface
+ *   of the Earth.
+ *   S is the circle-of-lattitude.  Its calculated from the right triangle defined by
+ *      the angle (90-A), and the hypothenus R.
+ *
+ *
+ *
+ *   Once R and S have been calculated, the actual distance between the two points can be
+ *   calculated.
+ *
+ *   We dissolve the vector from lat1,long1 to lat2,long2 into its X and Y components (called DeltaX,DeltaY).
+ *   The actual distance that these angle-based measurements represent is taken from the two
+ *   circles we just calculated; R (for deltaY) and S (for deltaX).
+ *
+ *    (if deltaX is 1 degrees, then that distance represents 1/360 of a circle of radius S.)
+ *
+ *
+ *  Parts taken from PROJ4 - geodetic_to_geocentric() (for calculating Rn)
+ *
+ *  remember that lat1/long1/lat2/long2 are comming in a *RADIANS* not degrees.
+ *
+ * By Patricia Tozer and Dave Blasby
+ *
+ *  This is also called the "curvature method".
+ */
+
+double distance_sphere_method(double lat1, double long1,double lat2,double long2, SPHEROID *sphere)
+{
+                       double R,S,X,Y,deltaX,deltaY;
+
+                       double  distance        = 0.0;
+                       double  sin_lat         = sin(lat1);
+                       double  sin2_lat        = sin_lat * sin_lat;
+
+                       double  Geocent_a       = sphere->a;
+                       double  Geocent_e2      = sphere->e_sq;
+
+                       R       = Geocent_a / (sqrt(1.0e0 - Geocent_e2 * sin2_lat));
+                       S       = R * sin(M_PI/2.0-lat1) ; // 90 - lat1, but in radians
+
+                       deltaX = long2 - long1;  //in rads
+                       deltaY = lat2 - lat1;    // in rads
+
+                       X = deltaX/(2.0*M_PI) * 2 * M_PI * S;  // think: a % of 2*pi*S
+                       Y = deltaY /(2.0*M_PI) * 2 * M_PI * R;
+
+                       distance = sqrt((X * X + Y * Y));
+
+                       return distance;
+}
diff --git a/hwgeom/postgis_svg.c b/hwgeom/postgis_svg.c
new file mode 100644 (file)
index 0000000..cb14bfc
--- /dev/null
@@ -0,0 +1,352 @@
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2001-2003 Refractions Research Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of hte GNU General Public Licence. See the COPYING file.
+ *
+ **********************************************************************
+ *
+ * SVG output routines.
+ * Originally written by: Klaus Förster <klaus@svg.cc>
+ * Patches from: Olivier Courtin <pnine@free.fr>
+ *
+ **********************************************************************
+ * $Log$
+ * Revision 1.1  2004/09/20 07:50:06  strk
+ * prepared to contain old internal representation code
+ *
+ * Revision 1.4  2004/09/13 14:26:27  strk
+ * indentation fix
+ *
+ * Revision 1.3  2004/09/10 16:16:31  pramsey
+ * Added Log tag to header.
+ *
+ *
+ **********************************************************************/
+
+
+#include "postgres.h"
+#include "postgis.h"
+
+Datum assvg_geometry(PG_FUNCTION_ARGS);
+char *geometry_to_svg(GEOMETRY  *geometry, int svgrel, int precision);
+void print_svg_coords(char *result, POINT3D *pt, int precision);
+void print_svg_circle(char *result, POINT3D *pt, int precision);
+void print_svg_path_abs(char *result, POINT3D *pt, int npoints, int precision);
+void print_svg_path_rel(char *result, POINT3D *pt, int npoints, int precision);
+
+#define SHOW_DIGS_DOUBLE 15
+#define MAX_DIGS_DOUBLE (SHOW_DIGS_DOUBLE + 6 + 1 + 3 +1)
+
+/**
+ * SVG features
+ */
+PG_FUNCTION_INFO_V1(assvg_geometry);
+Datum assvg_geometry(PG_FUNCTION_ARGS)
+{
+       GEOMETRY *geom1;
+       char *wkt;
+       char *result;
+       int len;
+       int32 svgrel=0;
+       int32 precision=15;
+
+       if ( PG_ARGISNULL(0) ) PG_RETURN_NULL();
+
+       geom1 = (GEOMETRY *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+       // check for relative path notation
+       if ( PG_NARGS() > 1 && ! PG_ARGISNULL(1) )
+                       svgrel = PG_GETARG_INT32(1);
+
+       if ( PG_NARGS() > 2 && ! PG_ARGISNULL(2) )
+               precision = PG_GETARG_INT32(2);
+               
+       wkt = geometry_to_svg(geom1, svgrel, precision);
+
+       len = strlen(wkt) + 5;
+
+       result= palloc(len);
+       *((int *) result) = len;
+
+       memcpy(result +4, wkt, len-4);
+
+       pfree(wkt);
+
+       PG_RETURN_CSTRING(result);
+}
+
+
+//takes a GEOMETRY and returns a SVG representation
+char *geometry_to_svg(GEOMETRY  *geometry, int svgrel, int precision)
+{
+       char            *result;
+       int             t,u;
+       int32           *offsets;
+       char            *obj;
+       POINT3D *pts;
+       POLYGON3D       *polygon;
+       LINE3D  *line;
+       POINT3D *point;
+
+       int             pt_off,size;
+       bool            first_sub_obj = TRUE;
+       int             npts;
+
+       //elog(NOTICE, "precision is %d", precision);
+       size = 30;      //just enough to put in object type
+
+       // TODO BBox, from where is it called?...
+       if (geometry->type == BBOXONLYTYPE)
+       {
+               if (svgrel == 1)
+               {
+                       // 5 double digits+ "Mhvhz"+ spaces +null
+                       size = MAX_DIGS_DOUBLE*5+5+6+1;
+                       result = (char *) palloc(size); 
+
+                       sprintf(result, "M %.*g %.*g h%.*g v%.*g h%.*g z",
+                               precision, 
+                               geometry->bvol.LLB.x,
+                               precision, 
+                               geometry->bvol.URT.y*-1,
+                               precision, 
+                               (geometry->bvol.URT.x 
+                                - geometry->bvol.LLB.x),
+                               precision, 
+                               (geometry->bvol.URT.y 
+                                - geometry->bvol.LLB.y),
+                               precision, 
+                               (geometry->bvol.URT.x 
+                                - geometry->bvol.LLB.x)*-1);
+               }
+               else
+               {
+                       size = MAX_DIGS_DOUBLE*4+3+1;
+                       result = (char *) palloc(size); 
+                       // 4 double digits + 3 spaces +null
+
+                       sprintf(result, "%.*g %.*g %.*g %.*g",
+                               precision,
+                               geometry->bvol.LLB.x,
+                               precision,
+                               geometry->bvol.URT.y*-1,
+                               precision,
+                               (geometry->bvol.URT.x - 
+geometry->bvol.LLB.x),
+                               precision, 
+                               (geometry->bvol.URT.y - 
+geometry->bvol.LLB.y)
+                               );
+               }
+               return result;
+       }
+
+       if (geometry->type == COLLECTIONTYPE)
+       {
+               result = (char *)palloc(64); 
+               sprintf(result, "GEOMETRYCOLLECTION not yet supported");
+               return result;
+       }
+
+       //where are the objects?
+       offsets = (int32 *) ( ((char *) &(geometry->objType[0] ))
+                       + sizeof(int32) * geometry->nobjs ) ;
+
+       result = palloc(size);
+       result[0] = '\0';
+       for(t=0;t<geometry->nobjs; t++)  //for each object
+       {
+               obj = (char *) geometry +offsets[t] ;
+
+               if (geometry->objType[t] == 1)   //POINT
+               {
+                       point = (POINT3D *) obj;
+                       size +=MAX_DIGS_DOUBLE*3 + 2 +10  ;
+                       //make memory bigger
+                       result = repalloc(result, size );
+
+                       if (!(first_sub_obj))
+                       {       
+                               // next circle ...
+                               strcat(result,",");
+                       }
+                       else
+                       {
+                               first_sub_obj = FALSE;
+                       }
+                       if (svgrel == 1)
+                       {  
+                               //render circle
+                               print_svg_coords(result, point, precision);
+                       }
+                       else
+                       {  
+                               //render circle
+                               print_svg_circle(result, point, precision);
+                       }
+
+               }
+               if (geometry->objType[t] == 2)  //LINESTRING
+               {
+                       line = (LINE3D *) obj;
+
+                       size +=(MAX_DIGS_DOUBLE*3+5)*line->npoints +12+3;
+                       result = repalloc(result, size );
+
+                       // start path with moveto
+                       strcat(result, "M ");
+
+                       if (svgrel == 1)
+                               print_svg_path_rel(
+                                               result,
+                                               &line->points[0],
+                                               line->npoints, 
+                                               precision
+                                               );
+                       else
+                               print_svg_path_abs(
+                                               result,
+                                               &line->points[0],
+                                               line->npoints,
+                                               precision
+                                               );
+
+                       strcat(result," ");
+               }
+               if (geometry->objType[t] == 3)  //POLYGON
+               {
+                       polygon = (POLYGON3D *) obj;
+                       pt_off = 0;     //where is the first point in this ring?
+
+                       //where are the points
+                       pts = (POINT3D *)
+                               ((char *)&(polygon->npoints[polygon->nrings]));
+                       pts = (POINT3D *) MAXALIGN(pts);
+
+                       npts = 0;
+                       for (u=0; u< polygon->nrings ; u++)
+                               npts += polygon->npoints[u];
+
+                       size += (MAX_DIGS_DOUBLE*3+3) 
+                               * npts + 5* polygon->nrings;
+                       result = repalloc(result, size );
+
+                       for (u=0; u< polygon->nrings ; u++)  //for each ring
+                       {
+                               strcat(result,"M ");    //begin ring
+                               if (svgrel == 1)
+                                       print_svg_path_rel(result, 
+                                                       &pts[pt_off] ,
+                                                       polygon->npoints[u],
+                                                       precision);
+                               else
+                                       print_svg_path_abs(result,
+                                                       &pts[pt_off],
+                                                       polygon->npoints[u],
+                                                       precision);
+                               
+                               //where is first point of next ring?
+                               pt_off = pt_off + polygon->npoints[u]; 
+                               strcat(result," ");     //end ring
+                       }
+               }
+       }
+       return(result);
+}
+
+
+void print_svg_coords(char *result, POINT3D *pt, int precision)
+{
+       char    temp[MAX_DIGS_DOUBLE*3 +12];
+
+       if ( (pt == NULL) || (result == NULL) )
+               return;
+
+       sprintf(temp, "x=\"%.*g\" y=\"%.*g\"", 
+                       precision, pt->x, 
+                       precision, pt->y*-1);
+       strcat(result,temp);
+}
+
+
+void print_svg_circle(char *result, POINT3D *pt, int precision)
+{
+       char    temp[MAX_DIGS_DOUBLE*3 +12];
+
+       if ( (pt == NULL) || (result == NULL) )
+               return;
+
+       sprintf(temp, "cx=\"%.*g\" cy=\"%.*g\"", 
+                       precision, pt->x, 
+                       precision, pt->y*-1);
+       strcat(result,temp);
+}
+
+
+void print_svg_path_abs(char *result, POINT3D *pt ,int npoints, int
+precision){
+       int     u;
+
+       result += strlen(result);
+       for (u=0;u<npoints;u++)
+       {
+               if (u != 0)
+               {
+                       result[0] = ' ';
+                       result++;
+               }
+               result+= sprintf(result,"%.*g %.*g", 
+                               precision, pt[u].x, 
+                               precision, pt[u].y*-1);
+       }
+}
+
+
+void print_svg_path_rel(char *result, POINT3D *pt ,int npoints, int
+precision){
+       int     u;
+
+       result += strlen(result);
+       for (u=0;u<npoints;u++)
+       {
+               if (u == 0)
+               {
+                       result+= sprintf(result,"%.*g %.*g l", 
+                                       precision, pt[u].x, 
+                                       precision, pt[u].y*-1);
+               }
+               else
+               {
+                       result+= sprintf(result," %.*g %.*g", 
+                                       precision, (pt[u].x-pt[u-1].x), 
+                                       precision, (pt[u].y-pt[u-1].y)*-1);
+               }
+       }
+}
+
+
+/**********************************************************************
+ * $Log$
+ * Revision 1.1  2004/09/20 07:50:06  strk
+ * prepared to contain old internal representation code
+ *
+ * Revision 1.4  2004/09/13 14:26:27  strk
+ * indentation fix
+ *
+ * Revision 1.3  2004/09/10 16:16:31  pramsey
+ * Added Log tag to header.
+ *
+ * Revision 1.2  2004/09/10 13:25:36  strk
+ * fixed a memory fault
+ *
+ * Revision 1.1  2004/09/10 12:49:29  strk
+ * Included SVG output function, modified to have precision expressed
+ * in terms of significant digits.
+ *
+ **********************************************************************/
+
diff --git a/hwgeom/postgis_transform.c b/hwgeom/postgis_transform.c
new file mode 100644 (file)
index 0000000..98d96aa
--- /dev/null
@@ -0,0 +1,513 @@
+
+/**********************************************************************
+ * $Id$
+ *
+ * PostGIS - Spatial Types for PostgreSQL
+ * http://postgis.refractions.net
+ * Copyright 2001-2003 Refractions Research Inc.
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU General Public Licence. See the COPYING file.
+ *
+ **********************************************************************
+ * $Log$
+ * Revision 1.1  2004/09/20 07:50:06  strk
+ * prepared to contain old internal representation code
+ *
+ * Revision 1.20  2004/08/10 21:09:59  strk
+ * changed proj version extractor to support pre 4.4.8 releases
+ *
+ * Revision 1.19  2004/07/28 16:10:59  strk
+ * Changed all version functions to return text.
+ * Renamed postgis_scripts_version() to postgis_scripts_installed()
+ * Added postgis_scripts_released().
+ * Added postgis_full_version().
+ *
+ * Revision 1.18  2004/07/23 21:24:33  strk
+ * Added postgis_proj_version()
+ *
+ * Revision 1.17  2004/07/22 16:20:10  strk
+ * Added postgis_lib_version() and postgis_geos_version()
+ *
+ * Revision 1.16  2004/04/28 22:26:02  pramsey
+ * Fixed spelling mistake in header text.
+ *
+ * Revision 1.15  2004/01/14 01:52:53  pramsey
+ * Fix solaris alignment problem in transformations.
+ *
+ * Revision 1.14  2003/09/16 20:27:12  dblasby
+ * added ability to delete geometries.
+ *
+ * Revision 1.13  2003/07/01 18:30:55  pramsey
+ * Added CVS revision headers.
+ *
+ *
+ **********************************************************************/
+
+#include "postgres.h"
+
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "access/gist.h"
+#include "access/itup.h"
+#include "access/rtree.h"
+
+
+#include "fmgr.h"
+
+
+#include "postgis.h"
+#include "utils/elog.h"
+
+
+
+#define SHOW_DIGS_DOUBLE 15
+#define MAX_DIGS_DOUBLE (SHOW_DIGS_DOUBLE + 6 + 1 + 3 +1)
+
+
+// if USE_PROJECTION undefined, we get a do-nothing transform() function
+#ifdef USE_PROJ
+
+#include "projects.h"
+
+
+PJ *make_project(char *str1);
+void to_rad(POINT3D *pts, int num_points);
+void to_dec(POINT3D *pts, int num_points);
+
+int pj_transform_nodatum( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset,
+                  double *x, double *y, double *z );
+
+//this is *exactly* the same as PROJ.4's pj_transform(), but it doesnt do the
+// datum shift.
+int pj_transform_nodatum( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset,
+                  double *x, double *y, double *z )
+
+{
+    long      i;
+    //int       need_datum_shift;
+
+    pj_errno = 0;
+
+    if( point_offset == 0 )
+        point_offset = 1;
+
+    if( !srcdefn->is_latlong )
+    {
+        for( i = 0; i < point_count; i++ )
+        {
+            XY         projected_loc;
+            LP        geodetic_loc;
+
+            projected_loc.u = x[point_offset*i];
+            projected_loc.v = y[point_offset*i];
+
+            geodetic_loc = pj_inv( projected_loc, srcdefn );
+            if( pj_errno != 0 )
+                return pj_errno;
+
+            x[point_offset*i] = geodetic_loc.u;
+            y[point_offset*i] = geodetic_loc.v;
+        }
+    }
+
+    if( !dstdefn->is_latlong )
+    {
+        for( i = 0; i < point_count; i++ )
+        {
+            XY         projected_loc;
+            LP        geodetic_loc;
+
+            geodetic_loc.u = x[point_offset*i];
+            geodetic_loc.v = y[point_offset*i];
+
+            projected_loc = pj_fwd( geodetic_loc, dstdefn );
+            if( pj_errno != 0 )
+                return pj_errno;
+
+            x[point_offset*i] = projected_loc.u;
+            y[point_offset*i] = projected_loc.v;
+        }
+    }
+
+    return 0;
+}
+
+
+
+// convert decimal degress to radians
+void to_rad(POINT3D *pts, int num_points)
+{
+       int t;
+       for(t=0;t<num_points;t++)
+       {
+               pts[t].x *= PI/180.0;
+               pts[t].y *= PI/180.0;
+       }
+}
+
+// convert radians to decimal degress
+void to_dec(POINT3D *pts, int num_points)
+{
+       int t;
+       for(t=0;t<num_points;t++)
+       {
+               pts[t].x *= 180.0/PI;
+               pts[t].y *= 180.0/PI;
+       }
+
+}
+
+       //given a string, make a PJ object
+PJ *make_project(char *str1)
+{
+       int t;
+       char    *params[1024];  //one for each parameter
+       char  *loc;
+       char    *str;
+       PJ *result;
+
+
+       if (str1 == NULL)
+               return NULL;
+
+       if (strlen(str1) ==0)
+               return NULL;
+
+       str = palloc(1+strlen(str1) );
+       strcpy(str,str1);
+
+       //first we split the string into a bunch of smaller strings, based on the " " separator
+
+       params[0] = str; //1st param, we'll null terminate at the " " soon
+
+       loc = str;
+       t =1;
+       while  ((loc != NULL) && (*loc != 0) )
+       {
+               loc = strchr( loc,' ');
+               if (loc != NULL)
+               {
+                       *loc = 0; // null terminate
+                       params[t] = loc +1;
+                       loc++; // next char
+                       t++; //next param
+               }
+       }
+
+       if (!(result= pj_init ( t , params)))
+       {
+               pfree(str);
+               return NULL;
+       }
+       pfree(str);
+       return result;
+}
+
+
+//tranform_geom( GEOMETRY, TEXT (input proj4), TEXT (output proj4), INT (output srid)
+// tmpPts - if there is a nadgrid error (-38), we re-try the transform on a copy of points.  The transformed points
+//          are in an indeterminate state after the -38 error is thrown.
+PG_FUNCTION_INFO_V1(transform_geom);
+Datum transform_geom(PG_FUNCTION_ARGS)
+{
+       GEOMETRY                   *geom ;
+       GEOMETRY                   *result;
+       PJ                         *input_pj,*output_pj;
+
+       char                            *o1;
+       int32                           *offsets1;
+       int                             j,type1,gtype,i,poly_points;
+
+       POLYGON3D                       *poly;
+       LINE3D                  *line;
+       POINT3D                 *pt,*poly_pts;
+
+       char                            *input_proj4, *output_proj4;
+
+       BOX3D                           *bbox;
+       POINT3D                 *tmpPts;
+
+
+
+       text       *input_proj4_text;
+       text       *output_proj4_text;
+       int32      result_srid ;
+
+
+       geom = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       input_proj4_text  = (PG_GETARG_TEXT_P(1));
+       output_proj4_text = (PG_GETARG_TEXT_P(2));
+       result_srid   = PG_GETARG_INT32(3);
+
+
+       input_proj4 = (char *) palloc(input_proj4_text->vl_len +1-4);
+      memcpy(input_proj4,input_proj4_text->vl_dat, input_proj4_text->vl_len-4);
+       input_proj4[input_proj4_text->vl_len-4] = 0; //null terminate
+
+
+       output_proj4 = (char *) palloc(output_proj4_text->vl_len +1-4);
+      memcpy(output_proj4,output_proj4_text->vl_dat, output_proj4_text->vl_len-4);
+       output_proj4[output_proj4_text->vl_len-4] = 0; //null terminate
+
+       if (geom->SRID == -1)
+       {
+               pfree(input_proj4); pfree(output_proj4);
+               elog(ERROR,"tranform: source SRID = -1");
+               PG_RETURN_NULL();                       // no srid, cannot convert
+       }
+
+       if (result_srid   == -1)
+       {
+               pfree(input_proj4); pfree(output_proj4);
+               elog(ERROR,"tranform: destination SRID = -1");
+               PG_RETURN_NULL();                       // no srid, cannot convert
+       }
+
+       //make input and output projection objects
+       input_pj = make_project(input_proj4);
+       if ( (input_pj == NULL) || pj_errno)
+       {
+               pfree(input_proj4); pfree(output_proj4);
+               elog(ERROR,"tranform: couldnt parse proj4 input string");
+               PG_RETURN_NULL();
+       }
+
+       output_pj = make_project(output_proj4);
+       if ((output_pj == NULL)|| pj_errno)
+       {
+               pfree(input_proj4); pfree(output_proj4);
+               pj_free(input_pj);
+               elog(ERROR,"tranform: couldnt parse proj4 output string");
+
+               PG_RETURN_NULL();
+       }
+       //great, now we have a geometry, and input/output PJ* structs.  Excellent.
+
+       //copy the geometry structure - we're only going to change the points, not the structures
+       result = (GEOMETRY *) palloc (geom->size);
+       memcpy(result,geom, geom->size);
+
+       gtype = result->type;
+
+       // allow transformations of the BOX3D type - the loop below won't be entered since for a BOX3D
+       // type result->nobjs will be -1 so we check for it here
+       if (gtype == BBOXONLYTYPE)
+       {
+               bbox = &(result->bvol);
+               pt = (POINT3D *)bbox;
+
+               if (input_pj->is_latlong)
+                       to_rad(pt,2);
+
+               tmpPts = palloc(sizeof(POINT3D)*2);
+               memcpy(tmpPts, pt, sizeof(POINT3D)*2);
+
+               pj_transform(input_pj,output_pj, 2,3, &pt->x,&pt->y, &pt->z);
+
+               if (pj_errno)
+               {
+                       if (pj_errno == -38)  //2nd chance
+                       {
+                               //couldnt do nadshift - do it without the datum
+                               memcpy(pt,tmpPts, sizeof(POINT3D)*2);
+                               pj_transform_nodatum(input_pj,output_pj, 2 ,3, &pt->x,&pt->y, &pt->z);
+                       }
+
+                       if (pj_errno)
+                       {
+                               pfree(input_proj4); pfree(output_proj4);
+                               pj_free(input_pj); pj_free(output_pj);
+                               elog(ERROR,"transform: couldnt project bbox point: %i (%s)",pj_errno,pj_strerrno(pj_errno));
+                               PG_RETURN_NULL();
+                       }
+
+               }
+               pfree(tmpPts);
+               if (output_pj->is_latlong)
+                       to_dec(pt,2);
+
+       }else{
+
+               //handle each sub-geometry
+               offsets1 = (int32 *) ( ((char *) &(result->objType[0] ))+ sizeof(int32) * result->nobjs ) ;
+               for (j=0; j< result->nobjs; j++)                //for each object in geom1
+               {
+                       o1 = (char *) result +offsets1[j] ;
+                       type1=  result->objType[j];
+
+                       if (type1 == POINTTYPE) //point
+                       {
+                               pt = (POINT3D *) o1;
+                               if (input_pj->is_latlong)
+                                       to_rad(pt,1);
+
+                               tmpPts = palloc(sizeof(POINT3D) );
+                               memcpy(tmpPts,pt, sizeof(POINT3D));
+
+                               pj_transform(input_pj,output_pj, 1,3, &pt->x,&pt->y, &pt->z);
+                               if (pj_errno)
+                               {
+                                       if (pj_errno == -38)  //2nd chance
+                                       {
+                                               //couldnt do nadshift - do it without the datum
+                                               memcpy(pt,tmpPts, sizeof(POINT3D));
+                                               pj_transform_nodatum(input_pj,output_pj, 1,3, &pt->x,&pt->y, &pt->z);
+                                       }
+
+                                       if (pj_errno)
+                                       {
+                                               pfree(input_proj4); pfree(output_proj4);
+                                               pj_free(input_pj); pj_free(output_pj);
+                                               elog(ERROR,"transform: couldnt project point: %i (%s)",pj_errno,pj_strerrno(pj_errno));
+                                               PG_RETURN_NULL();
+                                       }
+
+                               }
+                               pfree(tmpPts);
+                               if (output_pj->is_latlong)
+                                       to_dec(pt,1);
+                       }
+                       if (type1 == LINETYPE)  //line
+                       {
+                               line = (LINE3D *) o1;
+                               if (input_pj->is_latlong)
+                                       to_rad(&line->points[0],line->npoints);
+
+                               tmpPts = palloc(sizeof(POINT3D)*line->npoints );
+                               memcpy(tmpPts,&line->points[0], sizeof(POINT3D)*line->npoints);
+
+                               pj_transform(input_pj,output_pj, line->npoints ,3,
+                                                       &line->points[0].x,&line->points[0].y, &line->points[0].z);
+                               if (pj_errno)
+                               {
+                                       if (pj_errno == -38)  //2nd chance
+                                       {
+                                               //couldnt do nadshift - do it without the datum
+                                               memcpy(&line->points[0],tmpPts, sizeof(POINT3D)*line->npoints);
+                                               pj_transform_nodatum(input_pj,output_pj, line->npoints ,3,
+                                                       &line->points[0].x,&line->points[0].y, &line->points[0].z);
+                                       }
+
+                                       if (pj_errno)
+                                       {
+
+                                               pfree(input_proj4); pfree(output_proj4);
+                                               pj_free(input_pj); pj_free(output_pj);
+                                               elog(ERROR,"transform: couldnt project line");
+                                               PG_RETURN_NULL();
+                                       }
+                               }
+                               pfree(tmpPts);
+                               if (output_pj->is_latlong)
+                                       to_dec(&line->points[0],line->npoints);
+                       }
+                       if (type1 == POLYGONTYPE)       //POLYGON
+                       {
+                               poly = (POLYGON3D *) o1;
+                               poly_points = 0;
+
+                               for (i=0; i<poly->nrings;i++)
+                               {
+                                       poly_points  += poly->npoints[i];
+                               }
+                               poly_pts = (POINT3D *) ( (char *)&(poly->npoints[poly->nrings] )  );
+                               poly_pts = (POINT3D *) MAXALIGN(poly_pts);
+
+                               if (input_pj->is_latlong)
+                                       to_rad(poly_pts , poly_points);
+
+                               tmpPts = palloc(sizeof(POINT3D)* poly_points );
+                               memcpy(tmpPts,&poly_pts[0].x, sizeof(POINT3D)* poly_points);
+
+
+
+                               pj_transform(input_pj,output_pj, poly_points,3,
+                                                       &poly_pts[0].x,&poly_pts[0].y, &poly_pts[0].z);
+                               if (pj_errno)
+                               {
+                                       if (pj_errno == -38)  //2nd chance
+                                       {
+                                               //couldnt do nadshift - do it without the datum
+                                               memcpy(&poly_pts[0].x,tmpPts, sizeof(POINT3D)*poly_points);
+                                               pj_transform_nodatum(input_pj,output_pj, poly_points,3,
+                                                       &poly_pts[0].x,&poly_pts[0].y, &poly_pts[0].z);
+                                       }
+
+                                       if (pj_errno)
+                                       {
+
+                                               pfree(input_proj4); pfree(output_proj4);
+                                               pj_free(input_pj); pj_free(output_pj);
+                                               elog(ERROR,"transform: couldnt project polygon");
+                                               PG_RETURN_NULL();
+                                       }
+                               }
+                               pfree(tmpPts);
+                               if (output_pj->is_latlong)
+                                       to_dec(poly_pts , poly_points);
+                       }
+               }
+
+       }
+
+       // clean up
+       pj_free(input_pj);
+       pj_free(output_pj);
+       pfree(input_proj4); pfree(output_proj4);
+
+       // Generate the bounding box if necessary
+       if (gtype != BBOXONLYTYPE)
+       {
+               bbox = bbox_of_geometry(result);
+               memcpy(&result->bvol,bbox, sizeof(BOX3D) ); //make bounding box
+       }
+
+       result->SRID = result_srid;
+
+       PG_RETURN_POINTER(result); // new geometry
+}
+
+PG_FUNCTION_INFO_V1(postgis_proj_version);
+Datum postgis_proj_version(PG_FUNCTION_ARGS)
+{
+       //const char *ver = pj_get_release();
+       const char *ver = pj_release;
+       text *result;
+       result = (text *) palloc(VARHDRSZ  + strlen(ver));
+       VARATT_SIZEP(result) = VARHDRSZ + strlen(ver) ;
+       memcpy(VARDATA(result), ver, strlen(ver));
+       PG_RETURN_POINTER(result);
+}
+
+
+#else // ! defined USE_PROJ
+
+       // return the original geometry
+PG_FUNCTION_INFO_V1(transform_geom);
+Datum transform_geom(PG_FUNCTION_ARGS)
+{
+
+       elog(ERROR,"PostGIS transform() called, but support not compiled in.  Modify your makefile to add proj support, remake and re-install");
+
+       //GEOMETRY                 *geom1 = (GEOMETRY *)  PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+       //GEOMETRY                 *result;
+
+       //result = (GEOMETRY *) palloc (geom1->size);
+       //memcpy(result,geom1, geom1->size);
+       ///elog(NOTICE,"PostGIS transform
+       //PG_RETURN_POINTER(result);
+       PG_RETURN_NULL();
+}
+
+PG_FUNCTION_INFO_V1(postgis_proj_version);
+Datum postgis_proj_version(PG_FUNCTION_ARGS)
+{
+       PG_RETURN_NULL();
+}
+#endif