--- /dev/null
+#---------------------------------------------------------------
+# 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
+
--- /dev/null
+
+/**********************************************************************
+ * $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))
+
+
--- /dev/null
+-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+--
+-- $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;
--- /dev/null
+/**********************************************************************
+ * $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;
+ ***********************************************************************/
--- /dev/null
+
+/**********************************************************************
+ * $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);
+}
--- /dev/null
+
+/**********************************************************************
+ * $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) );
+}
+
+
--- /dev/null
+
+/**********************************************************************
+ * $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
+
+
--- /dev/null
+
+/**********************************************************************
+ * $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
+}
--- /dev/null
+/**********************************************************************
+ * $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
+
--- /dev/null
+// 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;
+}
+
+
--- /dev/null
+
+/**********************************************************************
+ * $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));
+}
+
+
--- /dev/null
+
+/**********************************************************************
+ * $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;
+}
--- /dev/null
+
+/**********************************************************************
+ * $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;
+}
+
+
--- /dev/null
+
+/**********************************************************************
+ * $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();
+}
--- /dev/null
+
+/**********************************************************************
+ * $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;
+}
--- /dev/null
+/**********************************************************************
+ * $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.
+ *
+ **********************************************************************/
+
--- /dev/null
+
+/**********************************************************************
+ * $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